From 02fef4418df4d1acfaf3a2683e3fff959d4106ea Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 29 Aug 2026 02:53:00 -0400 Subject: [PATCH] Add instance draw counts to vertex buffers --- src/video_core/buffer_cache/buffer_cache.h | 35 +++++++++++++++++-- .../buffer_cache/buffer_cache_base.h | 8 +++++ .../renderer_opengl/gl_rasterizer.cpp | 2 ++ .../renderer_vulkan/vk_rasterizer.cpp | 2 ++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 979b2aefe5..cd50970b7a 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1244,6 +1244,20 @@ void BufferCache

::UpdateIndexBuffer() { template void BufferCache

::UpdateVertexBuffers() { auto& flags = maxwell3d->dirty.flags; + const u32 base_instance = maxwell3d->draw_manager.draw_state.base_instance; + if (draw_instance_count != last_draw_instance_count || + base_instance != last_draw_base_instance) { + last_draw_instance_count = draw_instance_count; + last_draw_base_instance = base_instance; + const auto& instances = maxwell3d->regs.vertex_stream_instances; + for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { + if (!instances.IsInstancingEnabled(index)) { + continue; + } + flags[Dirty::VertexBuffer0 + index] = true; + flags[Dirty::VertexBuffers] = true; + } + } if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) { return; } @@ -1270,9 +1284,24 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { return; } // 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) { + constexpr u64 implausible_size = 64_MiB; + u64 address_size = gpu_addr_end - gpu_addr_begin; + if (address_size > u64{(std::numeric_limits::max)()}) { + address_size = implausible_size; + } + const bool is_instanced = maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index); + const u64 stride = static_cast(array.stride); + if (is_instanced && stride != 0 && draw_instance_count != 0) { + const u64 base_instance = static_cast(maxwell3d->draw_manager.draw_state.base_instance); + u64 elements = base_instance + 1; + if (array.frequency != 0) { + elements = (base_instance + static_cast(draw_instance_count) - 1) / + static_cast(array.frequency) + + 1; + } + address_size = (std::min)(address_size, (elements + 1) * stride); + } + if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || address_size >= implausible_size) { address_size = gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size); } const u32 size = static_cast(address_size); diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index facfe0d333..b3f0eb83c9 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -307,6 +307,10 @@ public: current_draw_indirect = current_draw_indirect_; } + void SetDrawInstanceCount(u32 draw_instance_count_) { + draw_instance_count = draw_instance_count_; + } + [[nodiscard]] std::pair GetDrawIndirectCount(); [[nodiscard]] std::pair GetDrawIndirectBuffer(); @@ -480,6 +484,10 @@ private: const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{}; + u32 draw_instance_count = 0; + u32 last_draw_instance_count = 0; + u32 last_draw_base_instance = 0; + u32 last_index_count = 0; u32 enabled_vertex_buffers_mask = 0; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1d77d28c46..0e2dc63a56 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -259,6 +259,7 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) { } void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { + buffer_cache.SetDrawInstanceCount(instance_count); PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) { const auto& draw_state = maxwell3d->draw_manager.draw_state; const GLuint base_instance = GLuint(draw_state.base_instance); @@ -304,6 +305,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) { void RasterizerOpenGL::DrawIndirect() { const auto& params = maxwell3d->draw_manager.indirect_state; buffer_cache.SetDrawIndirect(¶ms); + buffer_cache.SetDrawInstanceCount(0); PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) { if (params.is_byte_count) { const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f0a045f1a8..4f766d6104 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -260,6 +260,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { } void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { + buffer_cache.SetDrawInstanceCount(instance_count); PrepareDraw(is_indexed, [this, is_indexed, instance_count] { const auto& draw_state = maxwell3d->draw_manager.draw_state; const u32 num_instances{instance_count}; @@ -295,6 +296,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) { void RasterizerVulkan::DrawIndirect() { const auto& params = maxwell3d->draw_manager.indirect_state; buffer_cache.SetDrawIndirect(¶ms); + buffer_cache.SetDrawInstanceCount(0); PrepareDraw(params.is_indexed, [this, ¶ms] { const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer(); const auto& buffer = indirect_buffer.first;