Add adrenotools direct mapping support

This commit is contained in:
Billy Laws 2022-12-11 14:33:08 +00:00
parent 8846a85d3a
commit 3ecaedd71e
6 changed files with 110 additions and 14 deletions

View File

@ -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<const char *> 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 = 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<PFN_vkGetInstanceProcAddr>(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),

View File

@ -3,6 +3,7 @@
#pragma once
#include <adrenotools/driver.h>
#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'

View File

@ -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<u8> 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)};
}
}

View File

@ -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<u8> {
vk::raii::Buffer vkBuffer;
vk::raii::DeviceMemory vkMemory;
ImportedBuffer(span<u8> 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<u8> cpuMapping);
};
}

View File

@ -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;
}
}
}

View File

@ -4,6 +4,7 @@
#pragma once
#include <vulkan/vulkan_raii.hpp>
#include <adrenotools/driver.h>
#include <common.h>
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<u32>::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