From f229172d3b4ed9feed6da391ca7ae60606e5180b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 29 Aug 2026 01:37:56 -0400 Subject: [PATCH] Some cleanups on the vertex buffer path --- src/video_core/buffer_cache/buffer_base.h | 20 ------ src/video_core/buffer_cache/buffer_cache.h | 68 +++++++------------ .../buffer_cache/buffer_cache_base.h | 6 +- .../renderer_opengl/gl_buffer_cache.cpp | 16 ----- .../renderer_opengl/gl_buffer_cache.h | 2 - .../renderer_vulkan/vk_buffer_cache.cpp | 23 ------- .../renderer_vulkan/vk_buffer_cache.h | 2 - .../renderer_vulkan/vk_graphics_pipeline.cpp | 10 +-- .../renderer_vulkan/vk_rasterizer.cpp | 10 ++- .../vulkan_common/vulkan_device.cpp | 5 ++ src/video_core/vulkan_common/vulkan_device.h | 29 +++++++- 11 files changed, 74 insertions(+), 117 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index d7eb21612f..a5d7774dc3 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -20,8 +20,6 @@ namespace VideoCommon { enum class BufferFlagBits { Picked = 1 << 0, - CachedWrites = 1 << 1, - PreemtiveDownload = 1 << 2, }; DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits) @@ -58,15 +56,6 @@ public: flags |= BufferFlagBits::Picked; } - void MarkPreemtiveDownload() noexcept { - flags |= BufferFlagBits::PreemtiveDownload; - } - - /// Unmark buffer as picked - void Unpick() noexcept { - flags &= ~BufferFlagBits::Picked; - } - /// Increases the likeliness of this being a stream buffer void IncreaseStreamScore(int score) noexcept { stream_score += score; @@ -87,15 +76,6 @@ public: return True(flags & BufferFlagBits::Picked); } - /// Returns true when the buffer has pending cached writes - [[nodiscard]] bool HasCachedWrites() const noexcept { - return True(flags & BufferFlagBits::CachedWrites); - } - - bool IsPreemtiveDownload() const noexcept { - return True(flags & BufferFlagBits::PreemtiveDownload); - } - /// Returns the base CPU address of the buffer [[nodiscard]] VAddr CpuAddr() const noexcept { return cpu_addr; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index e40aad1fb5..979b2aefe5 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -121,25 +122,6 @@ void BufferCache

::WriteMemory(DAddr device_addr, u64 size) { memory_tracker.MarkRegionAsCpuModified(device_addr, size); } -template -void BufferCache

::CachedWriteMemory(DAddr device_addr, u64 size) { - const bool is_dirty = IsRegionRegistered(device_addr, size); - if (!is_dirty) { - return; - } - DAddr aligned_start = Common::AlignDown(device_addr, DEVICE_PAGESIZE); - DAddr aligned_end = Common::AlignUp(device_addr + size, DEVICE_PAGESIZE); - if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) { - WriteMemory(device_addr, size); - return; - } - - tmp_buffer.resize_destructive(size); - device_memory.ReadBlockUnsafe(device_addr, tmp_buffer.data(), size); - - InlineMemoryImplementation(device_addr, size, tmp_buffer); -} - template bool BufferCache

::OnCPUWrite(DAddr device_addr, u64 size) { const bool is_dirty = IsRegionRegistered(device_addr, size); @@ -422,7 +404,7 @@ void BufferCache

::UnbindGraphicsStorageBuffers(size_t stage) { } template -bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, +void BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written) { const bool already_enabled = ((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0; @@ -433,7 +415,7 @@ bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, LOG_WARNING(HW_GPU, "Skipping graphics storage buffer {} due to driver limit {}", ssbo_index, max_bindings); - return false; + return; } } } @@ -449,7 +431,6 @@ bool BufferCache

::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset; channel_state->storage_buffers[stage][ssbo_index] = StorageBufferBinding(ssbo_addr, cbuf_index, is_written); - return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID); } template @@ -762,16 +743,6 @@ void BufferCache

::BindHostIndexBuffer() { } } -template -void BufferCache

::BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, - u32 stride) { - if constexpr (IS_OPENGL) { - runtime.BindVertexBuffer(index, buffer, offset, size, stride); - } else { - runtime.BindVertexBuffer(index, buffer.Handle(), offset, size, stride); - } -} - template Binding& BufferCache

::VertexBufferSlot(u32 index) { ASSERT(index < NUM_VERTEX_BUFFERS); @@ -1251,9 +1222,14 @@ void BufferCache

::UpdateIndexBuffer() { const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress(); const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress(); const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); - const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); - const u32 draw_size = (index_buffer_ref.count + index_buffer_ref.first) * u32(index_buffer_ref.FormatSizeInBytes()); - const u32 size = (std::min)(address_size, draw_size); + u64 address_size = 0; + if (gpu_addr_end > gpu_addr_begin) { + address_size = (std::min)(gpu_addr_end - gpu_addr_begin, + u64{(std::numeric_limits::max)()}); + } + const u64 draw_size = (u64{index_buffer_ref.count} + u64{index_buffer_ref.first}) * + u64{index_buffer_ref.FormatSizeInBytes()}; + const u32 size = static_cast((std::min)(address_size, draw_size)); if (size == 0 || !device_addr) { channel_state->index_buffer = NULL_BINDING; return; @@ -1288,15 +1264,22 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { const GPUVAddr gpu_addr_begin = array.Address(); const GPUVAddr gpu_addr_end = limit.Address() + 1; const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin); - const u32 address_size = static_cast(gpu_addr_end - gpu_addr_begin); - u32 size = address_size; // TODO: Analyze stride and number of vertices - if (array.enable == 0 || size == 0 || !device_addr) { + if (array.enable == 0 || !device_addr || gpu_addr_end <= gpu_addr_begin) { channel_state->vertex_buffers[index] = NULL_BINDING; UpdateVertexBufferSlot(index, NULL_BINDING); return; } - if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) { - size = static_cast(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); + // TODO: Analyze stride and number of vertices + u64 address_size = (std::min)(gpu_addr_end - gpu_addr_begin, + u64{(std::numeric_limits::max)()}); + if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || address_size >= 64_MiB) { + address_size = gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size); + } + const u32 size = static_cast(address_size); + if (size == 0) { + channel_state->vertex_buffers[index] = NULL_BINDING; + UpdateVertexBufferSlot(index, NULL_BINDING); + return; } const BufferId buffer_id = FindBuffer(*device_addr, size); const Binding binding{ @@ -1578,9 +1561,10 @@ template BufferId BufferCache

::CreateBuffer(DAddr device_addr, u32 wanted_size) { DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE); device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE); - wanted_size = static_cast(device_addr_end - device_addr); + constexpr u64 max_buffer_size = u64{(std::numeric_limits::max)()}; + wanted_size = static_cast((std::min)(device_addr_end - device_addr, max_buffer_size)); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); - const u32 size = static_cast(overlap.end - overlap.begin); + const u32 size = static_cast((std::min)(overlap.end - overlap.begin, max_buffer_size)); const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size); auto& new_buffer = slot_buffers[new_buffer_id]; const size_t size_bytes = new_buffer.SizeBytes(); diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 0a521192c6..112c81bff5 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -217,8 +217,6 @@ public: void WriteMemory(DAddr device_addr, u64 size); - void CachedWriteMemory(DAddr device_addr, u64 size); - bool OnCPUWrite(DAddr device_addr, u64 size); void DownloadMemory(DAddr device_addr, u64 size); @@ -248,7 +246,7 @@ public: void UnbindGraphicsStorageBuffers(size_t stage); - bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, + void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written); void UnbindGraphicsTextureBuffers(size_t stage); @@ -376,8 +374,6 @@ private: void BindHostTransformFeedbackBuffers(); - void BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); - void BindHostComputeUniformBuffers(); void BindHostComputeStorageBuffers(); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index daff57bd00..ada65ae756 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -226,22 +226,6 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { } } -void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, - u32 stride) { - if (index >= max_attributes) { - return; - } - if (has_unified_vertex_buffers) { - buffer.MakeResident(GL_READ_ONLY); - glBindVertexBuffer(index, 0, 0, static_cast(stride)); - glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index, - buffer.HostGpuAddr() + offset, static_cast(size)); - } else { - glBindVertexBuffer(index, buffer.Handle(), static_cast(offset), - static_cast(stride)); - } -} - void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { // TODO: Should HostBindings provide the correct runtime types to avoid these transforms? std::array buffer_handles; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 09fa3a59af..e23dc33f74 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -99,8 +99,6 @@ public: void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); - void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); - void BindVertexBuffers(VideoCommon::HostBindings& bindings); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index a734bd049c..168083d23c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -585,29 +585,6 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir } } -void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) { - if (index >= device.GetMaxVertexInputBindings()) { - return; - } - if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { - const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; - const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; - const VkDeviceSize vk_stride = stride; - cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride); - }); - } else { - if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) { - ReserveNullBuffer(); - buffer = *null_buffer; - offset = 0; - } - scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffer(index, buffer, offset); - }); - } -} - void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { boost::container::static_vector buffer_handles(bindings.buffers.size()); for (u32 i = 0; i < bindings.buffers.size(); ++i) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 9b3dd50eaa..d23ebe2357 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -138,8 +138,6 @@ public: void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); - void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); - void BindVertexBuffers(VideoCommon::HostBindings& bindings); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8b5b0bc9c5..b2c6cffa82 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -694,9 +694,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { const size_t num_vertex_arrays = (std::min)( Maxwell::NumVertexArrays, static_cast(device.GetMaxVertexInputBindings())); for (size_t index = 0; index < num_vertex_arrays; ++index) { - const bool instanced = key.state.binding_divisors[index] != 0; - const auto rate = - instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; + const bool instanced = ((key.state.enabled_divisors >> index) & 1) != 0; + auto rate = VK_VERTEX_INPUT_RATE_VERTEX; + if (instanced) { + rate = VK_VERTEX_INPUT_RATE_INSTANCE; + } vertex_bindings.push_back({ .binding = static_cast(index), .stride = key.state.vertex_strides[index], @@ -705,7 +707,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (instanced) { vertex_binding_divisors.push_back({ .binding = static_cast(index), - .divisor = key.state.binding_divisors[index], + .divisor = device.GetVertexAttribDivisor(key.state.binding_divisors[index]), }); } } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 3b0fea2fec..f0a045f1a8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1917,13 +1917,19 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) for (u32 binding = 0; binding < max_bindings; ++binding) { const auto& input_binding{regs.vertex_streams[binding]}; const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; + auto input_rate = VK_VERTEX_INPUT_RATE_VERTEX; + u32 divisor = 1; + if (is_instanced) { + input_rate = VK_VERTEX_INPUT_RATE_INSTANCE; + divisor = device.GetVertexAttribDivisor(input_binding.frequency); + } bindings.push_back({ .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT, .pNext = nullptr, .binding = binding, .stride = input_binding.stride, - .inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX, - .divisor = is_instanced ? input_binding.frequency : 1, + .inputRate = input_rate, + .divisor = divisor, }); } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index fb3a9c4e7a..0e1896faa6 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1219,6 +1219,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; SetNext(next, properties.transform_feedback); } + if (extensions.vertex_attribute_divisor) { + properties.vertex_attribute_divisor.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT; + SetNext(next, properties.vertex_attribute_divisor); + } if (extensions.maintenance5) { properties.maintenance5.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index da82c7afb8..b29fafd838 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -70,6 +70,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \ FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \ FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \ + FEATURE(EXT, VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \ FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \ FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \ @@ -92,7 +93,6 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \ EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ - EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \ EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ @@ -690,6 +690,32 @@ FN_MAX_LIMIT_LIST return features.host_query_reset.hostQueryReset != VK_FALSE; } + u32 GetMaxVertexAttribDivisor() const { + const u32 reported = properties.vertex_attribute_divisor.maxVertexAttribDivisor; + if (reported == 0) { + return 1; + } + return reported; + } + + bool IsVertexAttributeInstanceRateZeroDivisorSupported() const { + return features.vertex_attribute_divisor.vertexAttributeInstanceRateZeroDivisor == VK_TRUE; + } + + u32 GetVertexAttribDivisor(u32 frequency) const { + const u32 max_divisor = GetMaxVertexAttribDivisor(); + if (frequency == 0) { + if (IsVertexAttributeInstanceRateZeroDivisorSupported()) { + return 0; + } + return max_divisor; + } + if (frequency > max_divisor) { + return max_divisor; + } + return frequency; + } + /// Returns true if the device supports VK_EXT_transform_feedback. bool IsExtTransformFeedbackSupported() const { return extensions.transform_feedback; @@ -1190,6 +1216,7 @@ private: VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{}; VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{}; VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{}; + VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{}; VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{}; VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{}; VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};