From 3ecaedd71ec53ef47a20e485b84ef9fe7e89f71b Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sun, 11 Dec 2022 14:33:08 +0000 Subject: [PATCH] Add adrenotools direct mapping support --- app/src/main/cpp/skyline/gpu.cpp | 38 ++++++++++++++----- app/src/main/cpp/skyline/gpu.h | 3 ++ .../main/cpp/skyline/gpu/memory_manager.cpp | 32 +++++++++++++++- app/src/main/cpp/skyline/gpu/memory_manager.h | 33 +++++++++++++++- .../main/cpp/skyline/gpu/trait_manager.cpp | 13 ++++++- app/src/main/cpp/skyline/gpu/trait_manager.h | 5 ++- 6 files changed, 110 insertions(+), 14 deletions(-) diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index 95db3e23..11a54e3a 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -219,7 +219,8 @@ namespace skyline::gpu { static vk::raii::Device CreateDevice(const vk::raii::Context &context, const vk::raii::PhysicalDevice &physicalDevice, decltype(vk::DeviceQueueCreateInfo::queueCount) &vkQueueFamilyIndex, - TraitManager &traits) { + TraitManager &traits, + adrenotools_gpu_mapping *mapping) { auto deviceFeatures2{physicalDevice.getFeatures2< vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceCustomBorderColorFeaturesEXT, @@ -274,7 +275,7 @@ namespace skyline::gpu { vk::PhysicalDeviceSubgroupProperties>()}; traits = TraitManager{deviceFeatures2, enabledFeatures2, deviceExtensions, enabledExtensions, deviceProperties2, physicalDevice}; - traits.ApplyDriverPatches(context); + traits.ApplyDriverPatches(context, mapping); std::vector pEnabledExtensions; pEnabledExtensions.reserve(enabledExtensions.size()); @@ -335,19 +336,20 @@ namespace skyline::gpu { }); } - static PFN_vkGetInstanceProcAddr LoadVulkanDriver(const DeviceState &state) { + static PFN_vkGetInstanceProcAddr LoadVulkanDriver(const DeviceState &state, adrenotools_gpu_mapping *mapping) { void *libvulkanHandle{}; // If the user has selected a custom driver, try to load it if (!(*state.settings->gpuDriver).empty()) { libvulkanHandle = adrenotools_open_libvulkan( RTLD_NOW, - ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_CUSTOM, + ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_CUSTOM | ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT, nullptr, // We require Android 10 so don't need to supply state.os->nativeLibraryPath.c_str(), (state.os->privateAppFilesPath + "gpu_drivers/" + *state.settings->gpuDriver + "/").c_str(), (*state.settings->gpuDriverLibraryName).c_str(), - (state.os->publicAppFilesPath + "gpu/vk_file_redirect/").c_str() + (state.os->publicAppFilesPath + "gpu/vk_file_redirect/").c_str(), + mapping ); if (!libvulkanHandle) { @@ -356,19 +358,37 @@ namespace skyline::gpu { } } - if (!libvulkanHandle) - libvulkanHandle = dlopen("libvulkan.so", RTLD_NOW); + if (!libvulkanHandle) { + libvulkanHandle = adrenotools_open_libvulkan( + RTLD_NOW, + ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT, + nullptr, // We require Android 10 so don't need to supply + state.os->nativeLibraryPath.c_str(), + nullptr, + nullptr, + (state.os->publicAppFilesPath + "gpu/vk_file_redirect/").c_str(), + mapping + ); + + if (!libvulkanHandle) { + char *error = dlerror(); + Logger::Warn("Failed to load builtin Vulkan driver: {}", error ? error : ""); + } + + if (!libvulkanHandle) + libvulkanHandle = dlopen("libvulkan.so", RTLD_NOW); + } return reinterpret_cast(dlsym(libvulkanHandle, "vkGetInstanceProcAddr")); } GPU::GPU(const DeviceState &state) : state(state), - vkContext(LoadVulkanDriver(state)), + vkContext(LoadVulkanDriver(state, &adrenotoolsImportMapping)), vkInstance(CreateInstance(state, vkContext)), vkDebugReportCallback(CreateDebugReportCallback(this, vkInstance)), vkPhysicalDevice(CreatePhysicalDevice(vkInstance)), - vkDevice(CreateDevice(vkContext, vkPhysicalDevice, vkQueueFamilyIndex, traits)), + vkDevice(CreateDevice(vkContext, vkPhysicalDevice, vkQueueFamilyIndex, traits, &adrenotoolsImportMapping)), vkQueue(vkDevice, vkQueueFamilyIndex, 0), memory(*this), scheduler(state, *this), diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h index cf2a3c32..240a69b6 100644 --- a/app/src/main/cpp/skyline/gpu.h +++ b/app/src/main/cpp/skyline/gpu.h @@ -3,6 +3,7 @@ #pragma once +#include #include "gpu/trait_manager.h" #include "gpu/memory_manager.h" #include "gpu/command_scheduler.h" @@ -31,8 +32,10 @@ namespace skyline::gpu { const DeviceState &state; // We access the device state inside Texture (and Buffers) for setting up NCE memory tracking friend Texture; friend Buffer; + friend BufferManager; public: + adrenotools_gpu_mapping adrenotoolsImportMapping{}; //!< Persistent struct to store active adrenotools mapping import info vk::raii::Context vkContext; vk::raii::Instance vkInstance; vk::raii::DebugReportCallbackEXT vkDebugReportCallback; //!< An RAII Vulkan debug report manager which calls into 'GPU::DebugCallback' diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.cpp b/app/src/main/cpp/skyline/gpu/memory_manager.cpp index 0e23762d..389cd713 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/memory_manager.cpp @@ -33,7 +33,7 @@ namespace skyline::gpu::memory { return pointer; } - MemoryManager::MemoryManager(const GPU &pGpu) : gpu(pGpu) { + MemoryManager::MemoryManager(GPU &pGpu) : gpu{pGpu} { auto instanceDispatcher{gpu.vkInstance.getDispatcher()}; auto deviceDispatcher{gpu.vkDevice.getDispatcher()}; VmaVulkanFunctions vulkanFunctions{ @@ -143,4 +143,34 @@ namespace skyline::gpu::memory { return Image(vmaAllocator, image, allocation); } + + ImportedBuffer MemoryManager::ImportBuffer(span cpuMapping) { + if (!gpu.traits.supportsAdrenoDirectMemoryImport) + throw exception("Cannot import host buffers without adrenotools import support!"); + + if (!adrenotools_import_user_mem(&gpu.adrenotoolsImportMapping, cpuMapping.data(), cpuMapping.size())) + throw exception("Failed to import user memory"); + + auto buffer{gpu.vkDevice.createBuffer(vk::BufferCreateInfo{ + .size = cpuMapping.size(), + .usage = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eUniformTexelBuffer | vk::BufferUsageFlagBits::eStorageTexelBuffer | vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndirectBuffer | vk::BufferUsageFlagBits::eTransformFeedbackBufferEXT, + .sharingMode = vk::SharingMode::eExclusive + })}; + + auto memory{gpu.vkDevice.allocateMemory(vk::MemoryAllocateInfo{ + .allocationSize = cpuMapping.size(), + .memoryTypeIndex = gpu.traits.hostVisibleCoherentCachedMemoryType, + })}; + + if (!adrenotools_validate_gpu_mapping(&gpu.adrenotoolsImportMapping)) + throw exception("Failed to validate GPU mapping"); + + gpu.vkDevice.bindBufferMemory2({vk::BindBufferMemoryInfo{ + .buffer = *buffer, + .memory = *memory, + .memoryOffset = 0 + }}); + + return ImportedBuffer{cpuMapping, std::move(buffer), std::move(memory)}; + } } diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.h b/app/src/main/cpp/skyline/gpu/memory_manager.h index c4682397..a96eb3c0 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.h +++ b/app/src/main/cpp/skyline/gpu/memory_manager.h @@ -44,6 +44,30 @@ namespace skyline::gpu::memory { using Buffer::Buffer; }; + /** + * @brief A buffer that directly owns it's own memory + */ + struct ImportedBuffer : public span { + vk::raii::Buffer vkBuffer; + vk::raii::DeviceMemory vkMemory; + + ImportedBuffer(span data, vk::raii::Buffer vkBuffer, vk::raii::DeviceMemory vkMemory) + : vkBuffer{std::move(vkBuffer)}, + vkMemory{std::move(vkMemory)}, + span{data} {} + + ImportedBuffer(const ImportedBuffer &) = delete; + + ImportedBuffer(ImportedBuffer &&other) + : vkBuffer{std::move(other.vkBuffer)}, + vkMemory{std::move(other.vkMemory)}, + span{other} {} + + ImportedBuffer &operator=(const ImportedBuffer &) = delete; + + ImportedBuffer &operator=(ImportedBuffer &&) = default; + }; + /** * @brief A Vulkan image which VMA allocates and manages the backing memory for * @note Any images created with VMA_ALLOCATION_CREATE_MAPPED_BIT must not be utilized with this since it'll unconditionally unmap when a pointer is present which is illegal when an image was created with that flag as unmapping will be automatically performed on image deletion @@ -94,11 +118,11 @@ namespace skyline::gpu::memory { */ class MemoryManager { private: - const GPU &gpu; + GPU &gpu; VmaAllocator vmaAllocator{VK_NULL_HANDLE}; public: - MemoryManager(const GPU &gpu); + MemoryManager(GPU &gpu); ~MemoryManager(); @@ -121,5 +145,10 @@ namespace skyline::gpu::memory { * @brief Creates an image which is allocated and deallocated using RAII and is optimal for being mapped on the CPU */ Image AllocateMappedImage(const vk::ImageCreateInfo &createInfo); + + /** + * @brief Maps the input CPU mapped region into a new buffer + */ + ImportedBuffer ImportBuffer(span cpuMapping); }; } diff --git a/app/src/main/cpp/skyline/gpu/trait_manager.cpp b/app/src/main/cpp/skyline/gpu/trait_manager.cpp index 3e93a6cb..bd96881f 100644 --- a/app/src/main/cpp/skyline/gpu/trait_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/trait_manager.cpp @@ -204,6 +204,12 @@ namespace skyline::gpu { bcnSupport[4] = isFormatSupported(vk::Format::eBc5UnormBlock) && isFormatSupported(vk::Format::eBc5SnormBlock); bcnSupport[5] = isFormatSupported(vk::Format::eBc6HSfloatBlock) && isFormatSupported(vk::Format::eBc6HUfloatBlock); bcnSupport[6] = isFormatSupported(vk::Format::eBc7UnormBlock) && isFormatSupported(vk::Format::eBc7SrgbBlock); + + auto memoryProps{physicalDevice.getMemoryProperties2()}; + constexpr auto ReqMemFlags{vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostCached}; + for (u32 i{}; i < memoryProps.memoryProperties.memoryTypeCount; i++) + if ((memoryProps.memoryProperties.memoryTypes[i].propertyFlags & ReqMemFlags) == ReqMemFlags) + hostVisibleCoherentCachedMemoryType = i; } std::string TraitManager::Summary() { @@ -269,7 +275,7 @@ namespace skyline::gpu { ); } - void TraitManager::ApplyDriverPatches(const vk::raii::Context &context) { + void TraitManager::ApplyDriverPatches(const vk::raii::Context &context, adrenotools_gpu_mapping *mapping) { // Create an instance without validation layers in order to get pointers to the functions we need to patch from the driver vk::ApplicationInfo applicationInfo{ .apiVersion = VK_API_VERSION_1_0, @@ -294,5 +300,10 @@ namespace skyline::gpu { Logger::Info("BCeNabler skipped, blob BCN support is present"); bcnSupport.set(); } + + if (adrenotools_validate_gpu_mapping(mapping)) { + Logger::Info("Applied GPU memory import patch"); + supportsAdrenoDirectMemoryImport = true; + } } } diff --git a/app/src/main/cpp/skyline/gpu/trait_manager.h b/app/src/main/cpp/skyline/gpu/trait_manager.h index 31e9b247..945764f1 100644 --- a/app/src/main/cpp/skyline/gpu/trait_manager.h +++ b/app/src/main/cpp/skyline/gpu/trait_manager.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include namespace skyline::gpu { @@ -49,8 +50,10 @@ namespace skyline::gpu { bool supportsExtendedDynamicState{}; //!< If the device supports the 'VK_EXT_extended_dynamic_state' Vulkan extension bool supportsNullDescriptor{}; //!< If the device supports the null descriptor feature in the 'VK_EXT_robustness2' Vulkan extension u32 subgroupSize{}; //!< Size of a subgroup on the host GPU + u32 hostVisibleCoherentCachedMemoryType{std::numeric_limits::max()}; std::bitset<7> bcnSupport{}; //!< Bitmask of BCn texture formats supported, it is ordered as BC1, BC2, BC3, BC4, BC5, BC6H and BC7 + bool supportsAdrenoDirectMemoryImport{}; /** * @brief Manages a list of any vendor/device-specific errata in the host GPU @@ -111,7 +114,7 @@ namespace skyline::gpu { /** * @brief Applies driver specific binary patches to the driver (e.g. BCeNabler) */ - void ApplyDriverPatches(const vk::raii::Context &context); + void ApplyDriverPatches(const vk::raii::Context &context, adrenotools_gpu_mapping *mapping); /** * @return A summary of all the GPU traits as a human-readable string