diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index c786207f3..52b72577e 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -69,6 +69,11 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo if (device.IsExtConditionalRendering()) { 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 u64 optimized_size = size; @@ -158,6 +163,20 @@ Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_) if (runtime.device.HasDebuggingToolAttached()) { 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) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index cab09649c..69c58dbee 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -34,6 +34,13 @@ public: 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 { return tracker.IsUsed(offset, size); } @@ -60,6 +67,7 @@ private: const Device* device{}; vk::Buffer buffer; + VkDeviceAddress device_address{}; // NVNbufferAddress - 64-bit GPU address for global memory std::vector views; VideoCommon::UsageTracker tracker; bool is_null{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index c69f496da..f1010914b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -350,6 +350,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), .support_derivative_control = true, .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), + .support_buffer_device_address = device.IsKhrBufferDeviceAddressSupported(), .support_native_ndc = device.IsExtDepthClipControlSupported(), .support_scaled_attributes = !device.MustEmulateScaledFormats(), .support_multi_viewport = device.SupportsMultiViewport(), diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index f05ff2372..08e05ca57 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -745,8 +745,25 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr; 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 = { - .flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT, + .flags = vma_flags, .physicalDevice = physical, .device = *logical, .preferredLargeHeapBlockSize = 0, @@ -755,7 +772,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR .pHeapSizeLimit = nullptr, .pVulkanFunctions = &functions, .instance = instance, - .vulkanApiVersion = VK_API_VERSION_1_1, + .vulkanApiVersion = VK_API_VERSION_1_2, .pTypeExternalMemoryHandleTypes = nullptr, }; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 03b19a084..57a3928fa 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -31,7 +31,8 @@ VK_DEFINE_HANDLE(VmaAllocator) #define FOR_EACH_VK_FEATURE_1_2(FEATURE) \ FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \ 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) \ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ @@ -554,6 +555,16 @@ public: 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. bool IsExtVertexInputDynamicStateSupported() const { return extensions.vertex_input_dynamic_state; @@ -595,6 +606,11 @@ public: 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. u32 SupportedSpirvVersion() const { if (instance_version >= VK_API_VERSION_1_3) { diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index d6ac53148..09121386b 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -203,6 +203,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkFreeDescriptorSets); X(vkFreeMemory); X(vkGetBufferMemoryRequirements2); + X(vkGetBufferDeviceAddress); // For NVN-style global memory (NVNbufferAddress) X(vkGetDeviceQueue); X(vkGetEventStatus); X(vkGetFenceStatus); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 757f3c8af..9bdce5802 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -302,6 +302,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkFreeDescriptorSets vkFreeDescriptorSets{}; PFN_vkFreeMemory vkFreeMemory{}; PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2{}; + PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress{}; // For NVN-style global memory (NVNbufferAddress) PFN_vkGetDeviceQueue vkGetDeviceQueue{}; PFN_vkGetEventStatus vkGetEventStatus{}; PFN_vkGetFenceStatus vkGetFenceStatus{}; @@ -1041,6 +1042,15 @@ public: return dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, 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 {