From 50e59cf09d4341dded94815e3eb6796627dd3bd8 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 26 Aug 2026 04:06:25 -0400 Subject: [PATCH] Another intent on write/read on the fly --- src/video_core/buffer_cache/buffer_cache.h | 65 +++++++++++++++++++ .../buffer_cache/buffer_cache_base.h | 8 +++ .../renderer_vulkan/vk_buffer_cache.h | 23 +++++++ src/video_core/vulkan_common/vulkan_device.h | 6 +- .../vulkan_common/vulkan_memory_allocator.cpp | 41 +++++++++++- .../vulkan_common/vulkan_memory_allocator.h | 10 +++ 6 files changed, 150 insertions(+), 3 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 63efeebdcf..61a064f1a2 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1166,6 +1166,20 @@ void BufferCache

::BindHostGraphicsStorageBuffers(size_t stage) { Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; + + if constexpr (USE_UNIFIED_MEMORY) { + const auto window = TryResolveUnifiedRange(binding.device_addr, size); + if (window && runtime.IsUnifiedStorageRange(size, window->offset)) { + if (is_written) { + memory_tracker.MarkRegionAsCpuModified(binding.device_addr, size); + } + runtime.BindStorageBuffer(runtime.UnifiedWindowBuffer(window->window), + runtime.UnifiedWindowAddress(window->window), + static_cast(window->offset), size, is_written); + return; + } + } + SynchronizeBuffer(buffer, binding.device_addr, size); const u32 offset = buffer.Offset(binding.device_addr); @@ -1942,6 +1956,57 @@ bool BufferCache

::ResolveUnifiedWindows( } } +template +std::optional::UnifiedWindowRange> +BufferCache

::TryResolveUnifiedRange([[maybe_unused]] DAddr device_addr, + [[maybe_unused]] u64 size) { + if constexpr (USE_UNIFIED_MEMORY) { + if (size == 0 || !runtime.IsUnifiedMemoryBindable()) { + return std::nullopt; + } + const u64 window_size = runtime.UnifiedMemoryWindowSize(); + if (window_size == 0) { + return std::nullopt; + } + const u8* const first = device_memory.GetPointer(device_addr); + if (first == nullptr) { + return std::nullopt; + } + const u64 phys_offset = static_cast(first - device_memory.GetPhysicalBase()); + const u64 unified_base = runtime.UnifiedMemoryBase(); + if (phys_offset < unified_base) { + return std::nullopt; + } + const u64 relative = phys_offset - unified_base; + const u64 unified_size = runtime.UnifiedMemorySize(); + if (relative >= unified_size || unified_size - relative < size) { + return std::nullopt; + } + const u64 local_offset = relative % window_size; + if (window_size - local_offset < size) { + return std::nullopt; + } + if (memory_tracker.IsRegionGpuModified(device_addr, size) || + IsRegionGpuModified(device_addr, size)) { + return std::nullopt; + } + u64 walked = Core::DEVICE_PAGESIZE - (device_addr & Core::DEVICE_PAGEMASK); + while (walked < size) { + const u8* const next = device_memory.GetPointer(device_addr + walked); + if (next != first + walked) { + return std::nullopt; + } + walked += Core::DEVICE_PAGESIZE; + } + return UnifiedWindowRange{ + .window = static_cast(relative / window_size), + .offset = local_offset, + }; + } else { + return std::nullopt; + } +} + template bool BufferCache

::TryUnifiedDownloadMemory([[maybe_unused]] Buffer& buffer, [[maybe_unused]] std::span copies) { diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index b7ed422d19..6eb3daf3aa 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -465,6 +466,13 @@ private: bool TryUnifiedDownloadMemory(Buffer& buffer, std::span copies); + struct UnifiedWindowRange { + size_t window; + u64 offset; + }; + + std::optional TryResolveUnifiedRange(DAddr device_addr, u64 size); + using UnifiedWindowGroups = boost::container::small_vector, 4>; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index d49325e51d..9e391e6bb2 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -246,6 +246,29 @@ public: BindBuffer(buffer, offset, size); } + void BindStorageBuffer(VkBuffer buffer, VkDeviceAddress address, u32 offset, u32 size, + [[maybe_unused]] bool is_written) { + guest_descriptor_queue.AddBuffer(buffer, address, offset, size); + } + + [[nodiscard]] bool IsUnifiedMemoryBindable() const noexcept { + return unified_memory != nullptr && unified_memory->IsValid() && + unified_memory->IsBindable(); + } + + [[nodiscard]] VkBuffer UnifiedWindowBuffer(size_t index) const noexcept { + return unified_memory->GetWindowBuffer(index); + } + + [[nodiscard]] VkDeviceAddress UnifiedWindowAddress(size_t index) const noexcept { + return unified_memory->GetWindowAddress(index); + } + + [[nodiscard]] bool IsUnifiedStorageRange(u32 size, u64 offset) const { + return size <= device.GetMaxStorageBufferRange() && + (offset % device.GetStorageBufferAlignment()) == 0; + } + void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, VideoCore::Surface::PixelFormat format) { guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format), diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cdd683f016..1fd939cfc6 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -350,7 +350,11 @@ public: return properties.properties.limits.minUniformBufferOffsetAlignment; } - /// Returns storage alignment requirement. + /// Returns max storage buffer range requirement. + u32 GetMaxStorageBufferRange() const { + return properties.properties.limits.maxStorageBufferRange; + } + VkDeviceSize GetStorageBufferAlignment() const { return properties.properties.limits.minStorageBufferOffsetAlignment; } diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 0209834045..82a09b9ef2 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -360,6 +360,8 @@ bool HostMemoryImport::ImportHardwareBuffers( const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties; window_size = hardware_buffer_window; base_offset = hardware_buffer_base; + + const auto import_all = [&](VkBufferUsageFlags usage, bool want_address) { for (size_t i = 0; i < hardware_buffers.size(); ++i) { const size_t offset = hardware_buffer_base + i * hardware_buffer_window; if (offset >= size) { @@ -390,7 +392,7 @@ bool HostMemoryImport::ImportHardwareBuffers( .pNext = &external_info, .flags = 0, .size = window_len, - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .usage = usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -422,9 +424,19 @@ bool HostMemoryImport::ImportHardwareBuffers( .image = VK_NULL_HANDLE, .buffer = new_buffer, }; + const VkMemoryAllocateFlagsInfo flags_info{ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, + .pNext = &dedicated_info, + .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + .deviceMask = 0, + }; + const void *alloc_next = &dedicated_info; + if (want_address) { + alloc_next = &flags_info; + } const VkMemoryAllocateInfo alloc_info{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = &dedicated_info, + .pNext = alloc_next, .allocationSize = ahb_props.allocationSize, .memoryTypeIndex = *type_index, }; @@ -437,12 +449,37 @@ bool HostMemoryImport::ImportHardwareBuffers( logical.DestroyBufferRaw(new_buffer); break; } + VkDeviceAddress address = 0; + if (want_address) { + address = logical.GetBufferDeviceAddress(new_buffer); + } windows.push_back(Window{ .memory = std::move(memory), .buffer = new_buffer, + .address = address, }); imported_size += static_cast(window_len); } + return !windows.empty(); + }; + + constexpr VkBufferUsageFlags TransferUsage = + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + VkBufferUsageFlags shader_usage = TransferUsage | + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; + const bool want_address = device.IsBufferDeviceAddressSupported(); + if (want_address) { + shader_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + } + + bindable = import_all(shader_usage, want_address); + if (!bindable) { + imported_size = 0; + import_all(TransferUsage, false); + } if (windows.empty()) { window_size = 0; base_offset = 0; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index bbcb7288df..d397242a5f 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -122,14 +122,23 @@ namespace Vulkan { return windows[index].buffer; } + [[nodiscard]] VkDeviceAddress GetWindowAddress(size_t index) const noexcept { + return windows[index].address; + } + [[nodiscard]] size_t GetWindowCount() const noexcept { return windows.size(); } + [[nodiscard]] bool IsBindable() const noexcept { + return bindable; + } + private: struct Window { vk::DeviceMemory memory; VkBuffer buffer{}; + VkDeviceAddress address{}; }; bool ImportHostPointer(void *base, size_t size); @@ -144,6 +153,7 @@ namespace Vulkan { size_t imported_size{}; size_t base_offset{}; bool foreign_ownership{}; + bool bindable{}; }; /// Memory allocator container.