diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 517ed3a58f..492540e60d 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -449,9 +449,18 @@ bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, const auto& cbufs = maxwell3d->state.shader_stages[stage]; const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; - channel_state->storage_buffers[stage][ssbo_index] = + StorageBufferBindingInfo& slot = channel_state->storage_buffers[stage][ssbo_index]; + const StorageBufferBindingInfo binding = StorageBufferBinding(ssbo_addr, cbuf_index, is_written, descriptor_count); - return channel_state->storage_buffers[stage][ssbo_index].gpu_addr != 0; + if (slot.gpu_addr != binding.gpu_addr || slot.size != binding.size || + slot.descriptor_count != binding.descriptor_count) { + slot.gpu_addr = binding.gpu_addr; + slot.size = binding.size; + slot.descriptor_count = binding.descriptor_count; + slot.mapping_generation = 0; + slot.segments.clear(); + } + return slot.gpu_addr != 0; } template @@ -525,8 +534,17 @@ void BufferCache

::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, const auto& cbufs = launch_desc.const_buffer_config; const GPUVAddr ssbo_addr = cbufs[cbuf_index].Address() + cbuf_offset; - channel_state->compute_storage_buffers[ssbo_index] = + StorageBufferBindingInfo& slot = channel_state->compute_storage_buffers[ssbo_index]; + const StorageBufferBindingInfo binding = StorageBufferBinding(ssbo_addr, cbuf_index, is_written, descriptor_count); + if (slot.gpu_addr != binding.gpu_addr || slot.size != binding.size || + slot.descriptor_count != binding.descriptor_count) { + slot.gpu_addr = binding.gpu_addr; + slot.size = binding.size; + slot.descriptor_count = binding.descriptor_count; + slot.mapping_generation = 0; + slot.segments.clear(); + } } template @@ -1002,7 +1020,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 template void BufferCache

::BindHostGraphicsStorageBuffers(size_t stage) { - boost::container::small_vector segment_sizes; + boost::container::small_vector segment_sizes; bool uses_mapping{}; ForEachEnabledBit(channel_state->enabled_storage_buffers[stage], [&](u32 index) { const StorageBufferBindingInfo& binding = channel_state->storage_buffers[stage][index]; @@ -1163,7 +1181,7 @@ void BufferCache

::BindHostComputeUniformBuffers() { template void BufferCache

::BindHostComputeStorageBuffers() { - boost::container::small_vector segment_sizes; + boost::container::small_vector segment_sizes; bool uses_mapping{}; ForEachEnabledBit(channel_state->enabled_compute_storage_buffers, [&](u32 index) { const StorageBufferBindingInfo& binding = channel_state->compute_storage_buffers[index]; @@ -1469,10 +1487,12 @@ void BufferCache

::UpdateComputeStorageBuffers() { template void BufferCache

::UpdateStorageBuffer(StorageBufferBindingInfo& binding) { - binding.segments.clear(); - if (binding.gpu_addr == 0 || binding.size == 0) { return;} + if (binding.gpu_addr == 0 || binding.size == 0) { + binding.segments.clear(); + return; + } if (binding.descriptor_count == 1) { - // for safety gotta preserve the legacy path on possible hosts without storage-buffer descriptor indexing. + binding.segments.clear(); const std::optional device_addr = gpu_memory->GpuToCpuAddress(binding.gpu_addr); if (device_addr) { binding.segments.push_back(Binding{ @@ -1484,30 +1504,32 @@ void BufferCache

::UpdateStorageBuffer(StorageBufferBindingInfo& binding) { return; } - const auto ranges = gpu_memory->GetSubmappedRange(binding.gpu_addr, binding.size); - const size_t mapped_size = - std::accumulate(ranges.begin(), ranges.end(), size_t{}, [](size_t total, const auto& range) { return total + range.second; }); - if (mapped_size != binding.size) { - LOG_ERROR(HW_GPU, "Storage buffer range {:#x}+{:#x} is not fully mapped", binding.gpu_addr, binding.size); - return; - } - if (ranges.size() > binding.descriptor_count) { - LOG_ERROR(HW_GPU, "Storage buffer range {:#x}+{:#x} has {} physical segments, exceeding host capacity {}", - binding.gpu_addr, binding.size, ranges.size(), binding.descriptor_count); - return; - } - for (const auto& [gpu_addr, size] : ranges) { - const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr); - if (!device_addr || size > (std::numeric_limits::max)()) { - binding.segments.clear(); - return; + const u64 generation = gpu_memory->MappingGeneration(); + if (binding.mapping_generation != generation || binding.segments.empty()) { + binding.mapping_generation = generation; + binding.segments.clear(); + const auto ranges = gpu_memory->GetSubmappedRange(binding.gpu_addr, binding.size); + for (const auto& [gpu_addr, size] : ranges) { + if (binding.segments.size() >= binding.descriptor_count) { + break; + } + const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr); + if (!device_addr) { + break; + } + u32 segment_size = (std::numeric_limits::max)(); + if (size < static_cast(segment_size)) { + segment_size = static_cast(size); + } + binding.segments.push_back(Binding{ + .device_addr = *device_addr, + .size = segment_size, + .buffer_id = BufferId{}, + }); } - const u32 segment_size = static_cast(size); - binding.segments.push_back(Binding{ - .device_addr = *device_addr, - .size = segment_size, - .buffer_id = FindBuffer(*device_addr, segment_size), - }); + } + for (Binding& segment : binding.segments) { + segment.buffer_id = FindBuffer(segment.device_addr, segment.size); } } diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 62bfc2f1ab..3f42405c5b 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -54,7 +54,8 @@ constexpr u32 NUM_VERTEX_BUFFERS = 32; constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; -constexpr u32 NUM_STORAGE_BUFFERS = 16; +constexpr u32 NUM_STORAGE_BUFFERS = 32; +constexpr u32 NUM_STORAGE_BUFFER_SEGMENTS = 8; constexpr u32 NUM_TEXTURE_BUFFERS = 32; constexpr u32 NUM_STAGES = 5; @@ -90,11 +91,10 @@ struct TextureBufferBinding : Binding { }; struct StorageBufferBindingInfo { - // another good one: guest SSBO is a virtual interval and may span discontiguous device-memory ranges. - // exact case of missing character frames (high sample lane) GPUVAddr gpu_addr{}; u32 size{}; u32 descriptor_count{1}; + u64 mapping_generation{}; boost::container::small_vector segments; }; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index b9b136f6b8..19a7d8d254 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -176,12 +176,14 @@ void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) } GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, DAddr dev_addr, std::size_t size, PTEKind kind, bool is_big_pages) { + mapping_generation.fetch_add(1, std::memory_order_release); if (is_big_pages) return BigPageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped); return PageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped); } GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) { + mapping_generation.fetch_add(1, std::memory_order_release); if (is_big_pages) return BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved); return PageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved); @@ -191,6 +193,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { if (size == 0) { return; } + mapping_generation.fetch_add(1, std::memory_order_release); GetSubmappedRangeImpl(gpu_addr, size, page_stash); for (const auto& [map_addr, map_size] : page_stash) { diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index f9fddd177a..82f0f1cddd 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -145,6 +145,10 @@ public: return gpu_addr < address_space_size; } + u64 MappingGeneration() const noexcept { + return mapping_generation.load(std::memory_order_acquire); + } + PTEKind GetPageKind(GPUVAddr gpu_addr) const; size_t GetMemoryLayoutSize(GPUVAddr gpu_addr, @@ -197,6 +201,8 @@ private: VideoCore::RasterizerInterface* rasterizer = nullptr; + std::atomic mapping_generation{1}; + enum class EntryType : u64 { Free = 0, Reserved = 1, diff --git a/src/video_core/renderer_vulkan/vk_descriptor_buffer.h b/src/video_core/renderer_vulkan/vk_descriptor_buffer.h index 6f96bcc326..5c1bd3a486 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_buffer.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_buffer.h @@ -18,7 +18,7 @@ class Scheduler; class DescriptorBufferRing final { static constexpr size_t FRAMES_IN_FLIGHT = 8; - static constexpr VkDeviceSize TILER_FRAME_SIZE = 2 * 1024 * 1024; + static constexpr VkDeviceSize TILER_FRAME_SIZE = 8 * 1024 * 1024; static constexpr VkDeviceSize DESKTOP_FRAME_SIZE = 4 * 1024 * 1024; public: diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 6d11d9a444..7df7bba61e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -315,7 +315,7 @@ GraphicsPipeline::GraphicsPipeline( try { MakePipeline(render_pass); } catch (const vk::Exception& exception) { - LOG_CRITICAL(Render_Vulkan, "Graphics pipeline build failed: {}", exception.what()); + LOG_DEBUG(Render_Vulkan, "Graphics pipeline build failed: {}", exception.what()); std::scoped_lock lock{build_mutex}; is_built = true; build_condvar.notify_one(); @@ -581,7 +581,6 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, const DescriptorBufferRing::Allocation alloc{ descriptor_buffer_ring.Allocate(scheduler, descriptor_buffer_layout.size)}; if (!alloc.host) { - LOG_DEBUG(Render_Vulkan, "Failed to reserve descriptor memory, skipping draw"); return false; } WriteDescriptorBuffer(device, descriptor_buffer_layout, entries, alloc.host); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 2e759eb781..45d5ab2c73 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -62,11 +62,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -// SPIR-V descriptor arrays require a fixed pipeline-layout count. -// Exploration ceiling; buffer-cache telemetry records the actual physical-range demand. -// Keep this modest because every mapped SSBO binds the full fixed array on each update. -const u32 MAX_MAPPED_STORAGE_BUFFER_DESCRIPTORS = - (std::max)(6u, static_cast(Settings::values.debug_knobs.GetValue())); +constexpr u32 MAX_MAPPED_STORAGE_BUFFER_DESCRIPTORS = 8; constexpr u32 CACHE_VERSION = 19; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index 9f30e65dbd..b530734748 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h @@ -40,7 +40,7 @@ class UpdateDescriptorQueue final { static constexpr size_t FRAMES_IN_FLIGHT = 8; public: - static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x80000; + static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x40000; static constexpr size_t COMPUTE_FRAME_PAYLOAD_SIZE = 0x20000; explicit UpdateDescriptorQueue(const Device& device_, size_t frame_payload_size_,