mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-05 02:48:29 -04:00
feat(vulkan): add VK_KHR_buffer_device_address support infrastructure
- Add vkGetBufferDeviceAddress function pointer loading - Add GetBufferAddress method to Device wrapper with null checks - Add buffer device address feature detection and AMD GPU disable - Add VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT to buffer creation - Store and expose device address in Buffer class - Update VMA allocator with BUFFER_DEVICE_ADDRESS_BIT flag - Add IsRectangularLinesSupported/IsSmoothLinesSupported checks - Disable buffer_device_address on AMD GPUs for stability
This commit is contained in:
@@ -69,6 +69,11 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
|
|||||||
if (device.IsExtConditionalRendering()) {
|
if (device.IsExtConditionalRendering()) {
|
||||||
flags |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
flags |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT;
|
||||||
}
|
}
|
||||||
|
// Enable buffer device address for NVN-style global memory emulation (NVNbufferAddress)
|
||||||
|
// This allows shaders to use 64-bit buffer addresses directly, matching Nintendo's NVN API
|
||||||
|
if (device.IsKhrBufferDeviceAddressSupported()) {
|
||||||
|
flags |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
// Optimize buffer size based on VRAM usage mode
|
// Optimize buffer size based on VRAM usage mode
|
||||||
u64 optimized_size = size;
|
u64 optimized_size = size;
|
||||||
@@ -158,6 +163,20 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
|
|||||||
if (runtime.device.HasDebuggingToolAttached()) {
|
if (runtime.device.HasDebuggingToolAttached()) {
|
||||||
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
|
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
|
||||||
}
|
}
|
||||||
|
// Obtain 64-bit GPU address for NVN-style global memory emulation
|
||||||
|
// This is the equivalent of nvnBufferGetAddress() from Nintendo's NVN API
|
||||||
|
if (device->IsKhrBufferDeviceAddressSupported()) {
|
||||||
|
// Safety check: ensure buffer handle is valid before querying address
|
||||||
|
const VkBuffer buffer_handle = *buffer;
|
||||||
|
if (buffer_handle != VK_NULL_HANDLE) {
|
||||||
|
const VkBufferDeviceAddressInfo address_info{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.buffer = buffer_handle,
|
||||||
|
};
|
||||||
|
device_address = device->GetLogical().GetBufferAddress(address_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
|
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ public:
|
|||||||
return *buffer;
|
return *buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the 64-bit GPU address of this buffer (NVNbufferAddress equivalent)
|
||||||
|
/// This is the Vulkan equivalent of nvnBufferGetAddress() from the Nintendo NVN API
|
||||||
|
/// Used for global memory emulation where shaders need direct 64-bit buffer addresses
|
||||||
|
[[nodiscard]] VkDeviceAddress GetDeviceAddress() const noexcept {
|
||||||
|
return device_address;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
|
[[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept {
|
||||||
return tracker.IsUsed(offset, size);
|
return tracker.IsUsed(offset, size);
|
||||||
}
|
}
|
||||||
@@ -60,6 +67,7 @@ private:
|
|||||||
|
|
||||||
const Device* device{};
|
const Device* device{};
|
||||||
vk::Buffer buffer;
|
vk::Buffer buffer;
|
||||||
|
VkDeviceAddress device_address{}; // NVNbufferAddress - 64-bit GPU address for global memory
|
||||||
std::vector<BufferView> views;
|
std::vector<BufferView> views;
|
||||||
VideoCommon::UsageTracker tracker;
|
VideoCommon::UsageTracker tracker;
|
||||||
bool is_null{};
|
bool is_null{};
|
||||||
|
|||||||
@@ -350,6 +350,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
|
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
|
||||||
.support_derivative_control = true,
|
.support_derivative_control = true,
|
||||||
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
|
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
|
||||||
|
.support_buffer_device_address = device.IsKhrBufferDeviceAddressSupported(),
|
||||||
.support_native_ndc = device.IsExtDepthClipControlSupported(),
|
.support_native_ndc = device.IsExtDepthClipControlSupported(),
|
||||||
.support_scaled_attributes = !device.MustEmulateScaledFormats(),
|
.support_scaled_attributes = !device.MustEmulateScaledFormats(),
|
||||||
.support_multi_viewport = device.SupportsMultiViewport(),
|
.support_multi_viewport = device.SupportsMultiViewport(),
|
||||||
|
|||||||
@@ -745,8 +745,25 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr;
|
functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr;
|
||||||
functions.vkGetDeviceProcAddr = dld.vkGetDeviceProcAddr;
|
functions.vkGetDeviceProcAddr = dld.vkGetDeviceProcAddr;
|
||||||
|
|
||||||
|
// Note: Buffer device address can cause device loss on some AMD GPUs
|
||||||
|
// Disable the feature on AMD to improve stability
|
||||||
|
const VkDriverId current_driver_id = properties.driver.driverID;
|
||||||
|
const bool is_amd_gpu = current_driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
|
||||||
|
current_driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
|
||||||
|
current_driver_id == VK_DRIVER_ID_MESA_RADV;
|
||||||
|
if (is_amd_gpu && features.buffer_device_address.bufferDeviceAddress) {
|
||||||
|
LOG_INFO(Render_Vulkan, "VK_KHR_buffer_device_address disabled on AMD GPU for stability");
|
||||||
|
features.buffer_device_address.bufferDeviceAddress = VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VmaAllocatorCreateFlags vma_flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
|
||||||
|
if (features.buffer_device_address.bufferDeviceAddress) {
|
||||||
|
vma_flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
|
||||||
|
LOG_INFO(Render_Vulkan, "VK_KHR_buffer_device_address enabled for global memory emulation");
|
||||||
|
}
|
||||||
|
|
||||||
const VmaAllocatorCreateInfo allocator_info = {
|
const VmaAllocatorCreateInfo allocator_info = {
|
||||||
.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT,
|
.flags = vma_flags,
|
||||||
.physicalDevice = physical,
|
.physicalDevice = physical,
|
||||||
.device = *logical,
|
.device = *logical,
|
||||||
.preferredLargeHeapBlockSize = 0,
|
.preferredLargeHeapBlockSize = 0,
|
||||||
@@ -755,7 +772,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
|||||||
.pHeapSizeLimit = nullptr,
|
.pHeapSizeLimit = nullptr,
|
||||||
.pVulkanFunctions = &functions,
|
.pVulkanFunctions = &functions,
|
||||||
.instance = instance,
|
.instance = instance,
|
||||||
.vulkanApiVersion = VK_API_VERSION_1_1,
|
.vulkanApiVersion = VK_API_VERSION_1_2,
|
||||||
.pTypeExternalMemoryHandleTypes = nullptr,
|
.pTypeExternalMemoryHandleTypes = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
#define FOR_EACH_VK_FEATURE_1_2(FEATURE) \
|
#define FOR_EACH_VK_FEATURE_1_2(FEATURE) \
|
||||||
FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \
|
FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \
|
||||||
FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \
|
FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \
|
||||||
FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore)
|
FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) \
|
||||||
|
FEATURE(KHR, BufferDeviceAddress, BUFFER_DEVICE_ADDRESS, buffer_device_address)
|
||||||
|
|
||||||
#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \
|
#define FOR_EACH_VK_FEATURE_1_3(FEATURE) \
|
||||||
FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
|
FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
|
||||||
@@ -554,6 +555,16 @@ public:
|
|||||||
return extensions.line_rasterization;
|
return extensions.line_rasterization;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if rectangularLines line rasterization mode is supported.
|
||||||
|
bool IsRectangularLinesSupported() const {
|
||||||
|
return extensions.line_rasterization && features.line_rasterization.rectangularLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if smoothLines line rasterization mode is supported.
|
||||||
|
bool IsSmoothLinesSupported() const {
|
||||||
|
return extensions.line_rasterization && features.line_rasterization.smoothLines;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
|
/// Returns true if the device supports VK_EXT_vertex_input_dynamic_state.
|
||||||
bool IsExtVertexInputDynamicStateSupported() const {
|
bool IsExtVertexInputDynamicStateSupported() const {
|
||||||
return extensions.vertex_input_dynamic_state;
|
return extensions.vertex_input_dynamic_state;
|
||||||
@@ -595,6 +606,11 @@ public:
|
|||||||
return extensions.fragment_shading_rate;
|
return extensions.fragment_shading_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if VK_KHR_buffer_device_address is supported.
|
||||||
|
bool IsKhrBufferDeviceAddressSupported() const {
|
||||||
|
return features.buffer_device_address.bufferDeviceAddress;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the minimum supported version of SPIR-V.
|
/// Returns the minimum supported version of SPIR-V.
|
||||||
u32 SupportedSpirvVersion() const {
|
u32 SupportedSpirvVersion() const {
|
||||||
if (instance_version >= VK_API_VERSION_1_3) {
|
if (instance_version >= VK_API_VERSION_1_3) {
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
|||||||
X(vkFreeDescriptorSets);
|
X(vkFreeDescriptorSets);
|
||||||
X(vkFreeMemory);
|
X(vkFreeMemory);
|
||||||
X(vkGetBufferMemoryRequirements2);
|
X(vkGetBufferMemoryRequirements2);
|
||||||
|
X(vkGetBufferDeviceAddress); // For NVN-style global memory (NVNbufferAddress)
|
||||||
X(vkGetDeviceQueue);
|
X(vkGetDeviceQueue);
|
||||||
X(vkGetEventStatus);
|
X(vkGetEventStatus);
|
||||||
X(vkGetFenceStatus);
|
X(vkGetFenceStatus);
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ struct DeviceDispatch : InstanceDispatch {
|
|||||||
PFN_vkFreeDescriptorSets vkFreeDescriptorSets{};
|
PFN_vkFreeDescriptorSets vkFreeDescriptorSets{};
|
||||||
PFN_vkFreeMemory vkFreeMemory{};
|
PFN_vkFreeMemory vkFreeMemory{};
|
||||||
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2{};
|
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2{};
|
||||||
|
PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress{}; // For NVN-style global memory (NVNbufferAddress)
|
||||||
PFN_vkGetDeviceQueue vkGetDeviceQueue{};
|
PFN_vkGetDeviceQueue vkGetDeviceQueue{};
|
||||||
PFN_vkGetEventStatus vkGetEventStatus{};
|
PFN_vkGetEventStatus vkGetEventStatus{};
|
||||||
PFN_vkGetFenceStatus vkGetFenceStatus{};
|
PFN_vkGetFenceStatus vkGetFenceStatus{};
|
||||||
@@ -1041,6 +1042,15 @@ public:
|
|||||||
return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
|
return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
|
||||||
flags);
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the 64-bit GPU address of a buffer (NVNbufferAddress equivalent)
|
||||||
|
/// This is used for global memory emulation, matching Nintendo's nvnBufferGetAddress()
|
||||||
|
VkDeviceAddress GetBufferAddress(const VkBufferDeviceAddressInfo& info) const noexcept {
|
||||||
|
if (dld->vkGetBufferDeviceAddress) {
|
||||||
|
return dld->vkGetBufferDeviceAddress(handle, &info);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PhysicalDevice {
|
class PhysicalDevice {
|
||||||
|
|||||||
Reference in New Issue
Block a user