gpu & pmem: add direct guest memory resource

This commit is contained in:
DH 2026-01-05 06:18:04 +03:00
parent db2e1207cf
commit 62465b9001
11 changed files with 364 additions and 143 deletions

View file

@ -13,7 +13,7 @@ struct File;
} // namespace orbis
namespace orbis::pmem {
ErrorCode initialize(std::uint64_t size);
ErrorCode initialize(rx::Mappable mappable, std::uint64_t size);
void destroy();
std::pair<rx::AddressRange, ErrorCode>
allocate(std::uint64_t addressHint, std::uint64_t size,

View file

@ -33,14 +33,28 @@ struct PhysicalMemoryAllocation {
bool operator==(const PhysicalMemoryAllocation &) const = default;
};
using MappableMemoryResource =
kernel::MappableResource<decltype([](std::size_t size) {
return rx::Mappable::CreateMemory(size);
})>;
struct PhysicalMemoryResource
: kernel::AllocableResource<PhysicalMemoryAllocation, orbis::kallocator,
kernel::ExternalResource> {
std::size_t size;
rx::Mappable mappable;
using PhysicalMemoryResource =
kernel::AllocableResource<PhysicalMemoryAllocation, orbis::kallocator,
MappableMemoryResource>;
std::errc create(rx::Mappable mappable, std::size_t size) {
if (size == 0 || !mappable) {
return std::errc::invalid_argument;
}
if (auto errc =
BaseResource::create(rx::AddressRange::fromBeginSize(0, size));
errc != std::errc{}) {
return errc;
}
this->size = size;
this->mappable = std::move(mappable);
return {};
}
};
static auto g_pmemInstance = orbis::createGlobalObject<
kernel::LockableKernelObject<PhysicalMemoryResource>>();
@ -76,12 +90,12 @@ struct PhysicalMemory : orbis::IoDevice {
static auto g_phyMemory = orbis::createGlobalObject<PhysicalMemory>();
orbis::ErrorCode orbis::pmem::initialize(std::uint64_t size) {
orbis::ErrorCode orbis::pmem::initialize(rx::Mappable mappable,
std::uint64_t size) {
std::lock_guard lock(*g_pmemInstance);
rx::println("pmem: {:x}", size);
return toErrorCode(
g_pmemInstance->create(rx::AddressRange::fromBeginSize(0, size)));
return toErrorCode(g_pmemInstance->create(std::move(mappable), size));
}
void orbis::pmem::destroy() {

View file

@ -63,19 +63,10 @@ makeDisplayEvent(DisplayEvent id, std::uint16_t unk0 = 0,
return result;
}
static vk::Context createVkContext(Device *device) {
static vk::Context createVkContext(Device *device, std::size_t dmemSize) {
std::vector<const char *> optionalLayers;
bool enableValidation = rx::g_config.validateGpu;
for (std::size_t process = 0; process < 6; ++process) {
auto range = rx::AddressRange::fromBeginSize(
0x40'0000 + 0x100'0000'0000 * process, 0x100'0000'0000 - 0x40'0000);
if (auto errc = rx::mem::reserve(range); errc != std::errc{}) {
rx::die("failed to reserve userspace memory: {} {:x}-{:x}", (int)errc,
range.beginAddress(), range.endAddress());
}
}
auto createWindow = [=] {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
device->window = glfwCreateWindow(1920, 1080, "RPCSX", nullptr, nullptr);
@ -142,17 +133,22 @@ static vk::Context createVkContext(Device *device) {
result.createDevice(device->surface, rx::g_config.gpuIndex,
{
// VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME,
// VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
// VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME,
// VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,
// VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
// VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
#ifdef _WIN32
VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
#else
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
#endif
VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME,
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_EXT_SHADER_OBJECT_EXTENSION_NAME,
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
// VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME,
// VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
// VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME,
// VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,
},
{
VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME,
@ -180,18 +176,70 @@ static vk::Context createVkContext(Device *device) {
getTotalMemorySize(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
vk::getHostVisibleMemory().initHostVisible(
std::min(hostVisibleMemoryTotalSize / 2, 1ul * 1024 * 1024 * 1024));
vk::getDirectMemory().initHostDirect(dmemSize);
vk::getHostVisibleMemory().initHostVisible(512 * 1024 *
1024); // FIXME: reduce RAM usage
vk::getDeviceLocalMemory().initDeviceLocal(
std::min(localMemoryTotalSize / 4, 4ul * 1024 * 1024 * 1024));
vk::context = &device->vkContext;
return result;
}
const auto kCachePageSize = 0x100'0000'0000 / rx::mem::pageSize;
Device::Device() : vkContext(createVkContext(this)) {
Device::Device(std::size_t dmemSize)
: vkContext(createVkContext(this, dmemSize)) {
for (auto &pipe : graphicsPipes) {
pipe.device = this;
}
for (auto &cachePage : cachePages) {
cachePage = static_cast<std::atomic<std::uint8_t> *>(
orbis::kalloc(kCachePageSize, 1));
std::memset(cachePage, 0, kCachePageSize);
}
commandPipe.device = this;
commandPipe.ring = {
.base = std::data(cmdRing),
.size = std::size(cmdRing),
.rptr = std::data(cmdRing),
.wptr = std::data(cmdRing),
};
for (auto &pipe : computePipes) {
pipe.device = this;
}
for (int i = 0; i < kGfxPipeCount; ++i) {
graphicsPipes[i].setDeQueue(
Ring{
.base = mainGfxRings[i],
.size = std::size(mainGfxRings[i]),
.rptr = mainGfxRings[i],
.wptr = mainGfxRings[i],
},
0);
}
}
Device::~Device() {
vkDeviceWaitIdle(vk::context->device);
if (debugMessenger != VK_NULL_HANDLE) {
vk::DestroyDebugUtilsMessengerEXT(vk::context->instance, debugMessenger,
vk::context->allocator);
}
for (auto &cachePage : cachePages) {
orbis::kfree(cachePage, kCachePageSize);
}
}
void Device::initialize() {
vk::context = &vkContext;
if (!shader::spv::validate(g_rdna_semantic_spirv)) {
shader::spv::dump(g_rdna_semantic_spirv, true);
rx::die("builtin semantic validation failed");
@ -209,16 +257,6 @@ Device::Device() : vkContext(createVkContext(this)) {
rx::die("failed to deserialize builtin semantics\n");
}
for (auto &pipe : graphicsPipes) {
pipe.device = this;
}
for (auto &cachePage : cachePages) {
cachePage = static_cast<std::atomic<std::uint8_t> *>(
orbis::kalloc(kCachePageSize, 1));
std::memset(cachePage, 0, kCachePageSize);
}
cacheUpdateThread = std::jthread([this](const std::stop_token &stopToken) {
auto &sched = graphicsPipes[0].scheduler;
std::uint32_t prevIdleValue = 0;
@ -262,42 +300,6 @@ Device::Device() : vkContext(createVkContext(this)) {
}
}
});
commandPipe.device = this;
commandPipe.ring = {
.base = std::data(cmdRing),
.size = std::size(cmdRing),
.rptr = std::data(cmdRing),
.wptr = std::data(cmdRing),
};
for (auto &pipe : computePipes) {
pipe.device = this;
}
for (int i = 0; i < kGfxPipeCount; ++i) {
graphicsPipes[i].setDeQueue(
Ring{
.base = mainGfxRings[i],
.size = std::size(mainGfxRings[i]),
.rptr = mainGfxRings[i],
.wptr = mainGfxRings[i],
},
0);
}
}
Device::~Device() {
vkDeviceWaitIdle(vk::context->device);
if (debugMessenger != VK_NULL_HANDLE) {
vk::DestroyDebugUtilsMessengerEXT(vk::context->instance, debugMessenger,
vk::context->allocator);
}
for (auto &cachePage : cachePages) {
orbis::kfree(cachePage, kCachePageSize);
}
}
void Device::start() {
@ -984,10 +986,10 @@ void Device::mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,
auto memory = amdgpu::RemoteMemory{process.vmId};
auto vmemAddress = memory.getVirtualAddress(virtualRange.beginAddress());
auto errc = orbis::pmem::map(vmemAddress,
rx::AddressRange::fromBeginSize(
physicalOffset, virtualRange.size()),
orbis::vmem::toGpuProtection(prot));
auto errc = orbis::pmem::map(
vmemAddress,
rx::AddressRange::fromBeginSize(physicalOffset, virtualRange.size()),
orbis::vmem::toGpuProtection(prot));
if (errc != orbis::ErrorCode{}) {
rx::die("failed to map process {} memory, address {:x}-{:x}, type {}, "
"offset {:x}, prot {}, error {}",

View file

@ -103,9 +103,10 @@ struct Device : rx::RcBase, DeviceContext {
std::uint32_t mainGfxRings[kGfxPipeCount][0x4000 / sizeof(std::uint32_t)];
std::uint32_t cmdRing[0x4000 / sizeof(std::uint32_t)];
Device();
Device(std::size_t dmemSize);
~Device();
void initialize();
void start();
Cache::Tag getCacheTag(int vmId, Scheduler &scheduler) {

View file

@ -21,9 +21,9 @@ DeviceCtl &DeviceCtl::operator=(const DeviceCtl &) = default;
DeviceCtl::~DeviceCtl() = default;
DeviceCtl DeviceCtl::createDevice() {
DeviceCtl DeviceCtl::createDevice(std::uint64_t dmemSize) {
DeviceCtl result;
result.mDevice = orbis::knew<Device>();
result.mDevice = orbis::knew<Device>(dmemSize);
return result;
}
@ -229,7 +229,10 @@ void DeviceCtl::submitComputeQueue(std::uint32_t meId, std::uint32_t pipeId,
pipe.submit(queueId, offset);
}
void DeviceCtl::start() { mDevice->start(); }
void DeviceCtl::start() {
mDevice->initialize();
mDevice->start();
}
void DeviceCtl::waitForIdle() { mDevice->waitForIdle(); }
void amdgpu::mapMemory(std::uint32_t pid, rx::AddressRange virtualRange,

View file

@ -25,7 +25,7 @@ public:
DeviceCtl &operator=(const DeviceCtl &);
~DeviceCtl();
static DeviceCtl createDevice();
static DeviceCtl createDevice(std::uint64_t dmemSize);
DeviceContext &getContext();
rx::Ref<rx::RcBase> getOpaque();

View file

@ -1,7 +1,10 @@
#pragma once
#include "rx/MemoryTable.hpp"
#include "rx/die.hpp"
#include <rx/FileLock.hpp>
#include <rx/MemoryTable.hpp>
#include <rx/die.hpp>
#include <rx/format.hpp>
#include <rx/print.hpp>
#include <cstddef>
#include <cstdint>
@ -157,6 +160,12 @@ public:
DeviceMemory(DeviceMemory &&other) noexcept { *this = std::move(other); }
DeviceMemory() = default;
#ifdef _WIN32
using NativeHandle = void *;
#else
using NativeHandle = int;
#endif
~DeviceMemory() {
if (mDeviceMemory != nullptr) {
vkFreeMemory(context->device, mDeviceMemory, context->allocator);
@ -176,9 +185,20 @@ public:
[[nodiscard]] unsigned getMemoryTypeIndex() const { return mMemoryTypeIndex; }
static DeviceMemory AllocateFromType(std::size_t size,
unsigned memoryTypeIndex) {
unsigned memoryTypeIndex,
bool withExportSupport = false) {
VkExportMemoryAllocateInfo exportInfo = {
.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
#ifdef _WIN32
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT,
#else
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
#endif
};
VkMemoryAllocateFlagsInfo flags{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
.pNext = withExportSupport ? &exportInfo : nullptr,
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT,
};
@ -198,16 +218,20 @@ public:
}
static DeviceMemory Allocate(std::size_t size, unsigned memoryTypeBits,
VkMemoryPropertyFlags properties) {
VkMemoryPropertyFlags properties,
bool withExportSupport = false) {
return AllocateFromType(
size, context->findPhysicalMemoryTypeIndex(memoryTypeBits, properties));
size, context->findPhysicalMemoryTypeIndex(memoryTypeBits, properties),
withExportSupport);
}
static DeviceMemory Allocate(VkMemoryRequirements requirements,
VkMemoryPropertyFlags properties) {
VkMemoryPropertyFlags properties,
bool withExportSupport = false) {
return AllocateFromType(requirements.size,
context->findPhysicalMemoryTypeIndex(
requirements.memoryTypeBits, properties));
requirements.memoryTypeBits, properties),
withExportSupport);
}
static DeviceMemory CreateExternalFd(int fd, std::size_t size,
@ -315,6 +339,33 @@ public:
}
void unmap() { vkUnmapMemory(context->device, mDeviceMemory); }
VkResult getNativeHandle(NativeHandle &handle) const {
#ifdef _WIN32
VkMemoryGetWin32HandleInfoKHR info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR,
.memory = mDeviceMemory,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR,
};
auto vkGetMemoryWin32HandleKHR =
(PFN_vkGetMemoryWin32HandleKHR)vkGetDeviceProcAddr(
context->device, "vkGetMemoryWin32HandleKHR");
return vkGetMemoryWin32HandleKHR(context->device, &info, &handle);
#else
VkMemoryGetFdInfoKHR info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
.memory = mDeviceMemory,
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
};
auto vkGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR)vkGetDeviceProcAddr(
context->device, "vkGetMemoryFdKHR");
return vkGetMemoryFdKHR(context->device, &info, &handle);
#endif
}
};
struct DeviceMemoryRef {
@ -331,7 +382,7 @@ class MemoryResource {
DeviceMemory mMemory;
char *mData = nullptr;
rx::MemoryAreaTable<> table;
// const char *debugName = "<unknown>";
const char *debugName = "<unknown>";
std::mutex mMtx;
@ -339,6 +390,8 @@ public:
MemoryResource() = default;
~MemoryResource() { clear(); }
using NativeHandle = DeviceMemory::NativeHandle;
void clear() {
if (mMemory.getHandle() != nullptr && mData != nullptr) {
vkUnmapMemory(context->device, mMemory.getHandle());
@ -356,7 +409,7 @@ public:
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
mMemory = DeviceMemory::CreateExternalFd(fd, size, properties);
table.map(rx::AddressRange::fromBeginSize(0, size));
// debugName = "fd-direct";
debugName = "fd-direct";
}
void initFromHost(void *data, std::size_t size) {
@ -365,7 +418,16 @@ public:
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
mMemory = DeviceMemory::CreateExternalHostMemory(data, size, properties);
table.map(rx::AddressRange::fromBeginSize(0, size));
// debugName = "direct";
debugName = "imported-direct";
}
void initHostDirect(std::size_t size) {
assert(mMemory.getHandle() == nullptr);
auto properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
mMemory = DeviceMemory::Allocate(size, ~0, properties, true);
debugName = "direct";
}
void initHostVisible(std::size_t size) {
@ -381,7 +443,7 @@ public:
mMemory = std::move(memory);
table.map(rx::AddressRange::fromBeginSize(0, size));
mData = reinterpret_cast<char *>(data);
// debugName = "host";
debugName = "host";
}
void initDeviceLocal(std::size_t size) {
@ -390,13 +452,14 @@ public:
mMemory = DeviceMemory::Allocate(size, ~0, properties);
table.map(rx::AddressRange::fromBeginSize(0, size));
// debugName = "local";
debugName = "local";
}
DeviceMemoryRef allocate(VkMemoryRequirements requirements) {
if ((requirements.memoryTypeBits & (1 << mMemory.getMemoryTypeIndex())) ==
0) {
std::abort();
rx::die("unexpected requirements for {} memory, {}", debugName,
requirements);
}
std::lock_guard lock(mMtx);
@ -415,26 +478,23 @@ public:
continue;
}
// if (debugName == std::string_view{"local"}) {
// std::printf("memory: allocation %s memory %lx-%lx\n", debugName,
// offset,
// offset + requirements.size);
// }
table.unmap(offset, offset + requirements.size);
return {.deviceMemory = mMemory.getHandle(),
.offset = offset,
.size = requirements.size,
.data = mData,
.allocator = this,
.release = [](DeviceMemoryRef &memoryRef) {
return {
.deviceMemory = mMemory.getHandle(),
.offset = offset,
.size = requirements.size,
.data = mData,
.allocator = this,
.release =
[](DeviceMemoryRef &memoryRef) {
auto self =
reinterpret_cast<MemoryResource *>(memoryRef.allocator);
self->deallocate(memoryRef);
}};
},
};
}
std::abort();
return {};
}
void deallocate(DeviceMemoryRef memory) {
@ -445,13 +505,16 @@ public:
void dump() {
std::lock_guard lock(mMtx);
rx::ScopedFileLock errLock(stderr);
rx::println(stderr, "{} resource\n", debugName);
for (auto elem : table) {
std::fprintf(stderr, "%zu - %zu\n", elem.beginAddress, elem.endAddress);
rx::println(stderr, " {:#x} - {:#x}\n", elem.beginAddress,
elem.endAddress);
}
}
DeviceMemoryRef getFromOffset(std::uint64_t offset, std::size_t size) {
return {mMemory.getHandle(), offset, size, nullptr, nullptr, nullptr};
VkResult getNativeHandle(NativeHandle &handle) const {
return mMemory.getNativeHandle(handle);
}
explicit operator bool() const { return mMemory.getHandle() != nullptr; }
@ -479,8 +542,11 @@ public:
static Semaphore Create(std::uint64_t initialValue = 0) {
VkSemaphoreTypeCreateInfo typeCreateInfo = {
VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, nullptr,
VK_SEMAPHORE_TYPE_TIMELINE, initialValue};
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = nullptr,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = initialValue,
};
VkSemaphoreCreateInfo createInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
&typeCreateInfo, 0};
@ -492,19 +558,25 @@ public:
}
VkResult wait(std::uint64_t value, uint64_t timeout) const {
VkSemaphoreWaitInfo waitInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
nullptr,
VK_SEMAPHORE_WAIT_ANY_BIT,
1,
&mSemaphore,
&value};
VkSemaphoreWaitInfo waitInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
.pNext = nullptr,
.flags = VK_SEMAPHORE_WAIT_ANY_BIT,
.semaphoreCount = 1,
.pSemaphores = &mSemaphore,
.pValues = &value,
};
return vkWaitSemaphores(context->device, &waitInfo, timeout);
}
void signal(std::uint64_t value) {
VkSemaphoreSignalInfo signalInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
nullptr, mSemaphore, value};
VkSemaphoreSignalInfo signalInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
.pNext = nullptr,
.semaphore = mSemaphore,
.value = value,
};
VK_VERIFY(vkSignalSemaphore(context->device, &signalInfo));
}
@ -543,11 +615,17 @@ public:
static BinSemaphore Create() {
VkSemaphoreTypeCreateInfo typeCreateInfo = {
VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, nullptr,
VK_SEMAPHORE_TYPE_BINARY, 0};
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.pNext = nullptr,
.semaphoreType = VK_SEMAPHORE_TYPE_BINARY,
.initialValue = 0,
};
VkSemaphoreCreateInfo createInfo = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
&typeCreateInfo, 0};
VkSemaphoreCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &typeCreateInfo,
.flags = 0,
};
BinSemaphore result;
VK_VERIFY(vkCreateSemaphore(context->device, &createInfo, nullptr,
@ -581,8 +659,11 @@ public:
}
static Fence Create() {
VkFenceCreateInfo fenceCreateInfo = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
nullptr, 0};
VkFenceCreateInfo fenceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
};
Fence result;
VK_VERIFY(vkCreateFence(context->device, &fenceCreateInfo, nullptr,
&result.mFence));
@ -1027,6 +1108,7 @@ public:
vk::MemoryResource &getHostVisibleMemory();
vk::MemoryResource &getDeviceLocalMemory();
vk::MemoryResource &getDirectMemory();
VkResult CreateShadersEXT(VkDevice device, uint32_t createInfoCount,
const VkShaderCreateInfoEXT *pCreateInfos,

View file

@ -9,6 +9,7 @@
vk::Context *vk::context;
static vk::MemoryResource g_hostVisibleMemory;
static vk::MemoryResource g_deviceLocalMemory;
static vk::MemoryResource g_directMemory;
void vk::verifyFailed(VkResult result, const char *message) {
std::fprintf(stderr, "vk verification failed: %s\n", message);
@ -782,6 +783,7 @@ vk::Context::findPhysicalMemoryTypeIndex(std::uint32_t typeBits,
vk::MemoryResource &vk::getHostVisibleMemory() { return g_hostVisibleMemory; }
vk::MemoryResource &vk::getDeviceLocalMemory() { return g_deviceLocalMemory; }
vk::MemoryResource &vk::getDirectMemory() { return g_directMemory; }
static auto importDeviceVkProc(VkDevice device, const char *name) {
auto result = vkGetDeviceProcAddr(device, name);

View file

@ -16,6 +16,7 @@
#include "orbis/vmem.hpp"
#include "rx/Config.hpp"
#include "rx/FileLock.hpp"
#include "rx/Mappable.hpp"
#include "rx/die.hpp"
#include "rx/format.hpp"
#include "rx/mem.hpp"
@ -23,6 +24,7 @@
#include "rx/watchdog.hpp"
#include "thread.hpp"
#include "vfs.hpp"
#include "vk.hpp"
#include "xbyak/xbyak.h"
#include <bit>
#include <optional>
@ -1034,16 +1036,84 @@ int main(int argc, const char *argv[]) {
rx::println(stderr, "RPCSX v{}", rx::getVersion().toString());
setupSigHandlers();
// FIXME: determine mode by reading elf file
orbis::constructAllGlobals();
setupSigHandlers();
rx::startWatchdog();
orbis::allocatePid();
auto initProcess = orbis::createProcess(nullptr, asRoot ? 1 : 10);
orbis::vmem::initialize(initProcess);
auto pmemSize = 9ull * 1024 * 1024 * 1024;
orbis::g_context->gpuDevice =
amdgpu::DeviceCtl::createDevice(pmemSize).getOpaque();
orbis::g_context->deviceEventEmitter = orbis::knew<orbis::EventEmitter>();
// FIXME: determine mode by reading elf file
orbis::pmem::initialize(10ull * 1024 * 1024 * 1024);
orbis::dmem::initialize();
orbis::fmem::initialize(2ull * 1024 * 1024 * 1024);
vk::DeviceMemory::NativeHandle handle;
VK_VERIFY(vk::getDirectMemory().getNativeHandle(handle));
auto mappable = rx::Mappable::CreateFromNativeHandle(handle);
rx::AddressRange importedVkMemory;
if (mappable.map(rx::AddressRange::fromBeginSize(orbis::kMinAddress,
orbis::vmem::kPageSize),
0, rx::mem::Protection::R,
orbis::vmem::kPageSize) != std::errc{}) {
rx::println(stderr, "warning: failed to use Vulkan exported memory, "
"switching to imported memory");
vk::getDirectMemory().free();
auto [cpuMappable, errc] = rx::Mappable::CreateMemory(pmemSize);
rx::dieIf(errc != std::errc{},
"failed to allocate physical memory, errc {}", errc);
mappable = std::move(cpuMappable);
auto [addr, mapErrc] = mappable.map(
pmemSize, 0, rx::mem::Protection::R | rx::mem::Protection::W);
rx::dieIf(mapErrc != std::errc{}, "failed to map physical memory, errc {}",
mapErrc);
vk::getDirectMemory().initFromHost(addr, pmemSize);
importedVkMemory = rx::AddressRange::fromBeginSize(
std::bit_cast<std::uintptr_t>(addr), pmemSize);
} else {
rx::mem::release(rx::AddressRange::fromBeginSize(orbis::kMinAddress,
orbis::vmem::kPageSize),
orbis::vmem::kPageSize);
}
if (auto errc = orbis::pmem::initialize(std::move(mappable), pmemSize);
errc != orbis::ErrorCode{}) {
rx::die("pmem initialization failed, {}", errc);
}
if (auto errc = orbis::dmem::initialize(); errc != orbis::ErrorCode{}) {
rx::die("dmem initialization failed, {}", errc);
}
if (auto errc = orbis::fmem::initialize(2ull * 1024 * 1024 * 1024);
errc != orbis::ErrorCode{}) {
rx::die("fmem initialization failed, {}", errc);
}
if (::fork() != 0) {
rx::attachGpuProcess(::getpid());
pthread_setname_np(pthread_self(), "rpcsx-gpu");
int logFd =
::open("log-gpu.txt", O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
dup2(logFd, 1);
dup2(logFd, 2);
::close(logFd);
amdgpu::DeviceCtl{orbis::g_context->gpuDevice}.start();
return 0;
}
rx::attachProcess(::getpid());
if (importedVkMemory.isValid()) {
rx::mem::release(importedVkMemory, 0);
}
rx::startWatchdog();
vfs::initialize();
std::vector<std::string> guestArgv(argv + argIndex, argv + argc);
@ -1055,11 +1125,6 @@ int main(int argc, const char *argv[]) {
rx::thread::initialize();
// vm::printHostStats();
orbis::allocatePid();
auto initProcess = orbis::createProcess(nullptr, asRoot ? 1 : 10);
orbis::vmem::initialize(initProcess);
// pthread_setname_np(pthread_self(), "10.MAINTHREAD");
int status = 0;

View file

@ -43,6 +43,9 @@ public:
rx::EnumBitSet<mem::Protection> protection,
std::size_t alignment);
std::pair<void *, std::errc> map(std::size_t size, std::size_t offset,
rx::EnumBitSet<mem::Protection> protection);
[[nodiscard]] NativeHandle release() {
return std::exchange(m_handle, kInvalidHandle);
}

View file

@ -169,6 +169,55 @@ std::errc rx::Mappable::map(rx::AddressRange virtualRange, std::size_t offset,
return {};
}
std::pair<void *, std::errc>
rx::Mappable::map(std::size_t size, std::size_t offset,
rx::EnumBitSet<mem::Protection> protection) {
#ifdef _WIN32
static const DWORD protTable[] = {
PAGE_NOACCESS, // 0
PAGE_READONLY, // R
PAGE_EXECUTE_READWRITE, // W
PAGE_EXECUTE_READWRITE, // RW
PAGE_EXECUTE, // X
PAGE_EXECUTE_READWRITE, // XR
PAGE_EXECUTE_READWRITE, // XW
PAGE_EXECUTE_READWRITE, // XRW
};
auto prot = protTable[(protection & (mem::Protection::R | mem::Protection::W |
mem::Protection::X))
.toUnderlying()];
auto result = MapViewOfFile3((HANDLE)m_handle, nullptr, nullptr, offset, size,
0, prot, nullptr, 0);
if (!result) {
return {{}, std::errc::invalid_argument};
}
return {};
#else
int prot = 0;
if (protection & mem::Protection::R) {
prot |= PROT_READ;
}
if (protection & mem::Protection::W) {
prot |= PROT_READ | PROT_WRITE;
}
if (protection & mem::Protection::X) {
prot |= PROT_EXEC;
}
auto result = ::mmap(nullptr, size, prot, MAP_SHARED, m_handle, offset);
if (result == MAP_FAILED) {
return {{}, std::errc{errno}};
}
#endif
return {result, {}};
}
void rx::Mappable::destroy() {
#ifdef _WIN32
CloseHandle((HANDLE)m_handle);