From d254d24dc24efd661f8ffc57df4bdb277d82e4ba Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 29 Aug 2026 14:20:05 -0400 Subject: [PATCH] Another take for indexing guards --- src/video_core/buffer_cache/buffer_cache.h | 61 +++++++++++++++---- .../buffer_cache/buffer_cache_base.h | 8 ++- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 970f635313..91c5b56213 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1242,7 +1242,38 @@ void BufferCache

::UpdateIndexBuffer() { } template -u64 BufferCache

::DrawVertexBound(u32 index, bool is_indexed) const { +u64 BufferCache

::DrawMaxIndex() { + if (max_index_scanned) { + return cached_max_index; + } + max_index_scanned = true; + cached_max_index = 0; + const auto& index_buffer_ref = maxwell3d->draw_manager.draw_state.index_buffer; + const u32 count = index_buffer_ref.count; + if (count == 0) { + return 0; + } + index_scan_buffer.resize_destructive(count); + gpu_memory->ReadBlockUnsafe(index_buffer_ref.IndexStart(), index_scan_buffer.data(), + size_t{count} * sizeof(u32)); + u32 restart_index = (std::numeric_limits::max)(); + if (maxwell3d->regs.primitive_restart.enabled != 0) { + restart_index = maxwell3d->regs.primitive_restart.index; + } + u32 max_index = 0; + for (u32 i = 0; i < count; ++i) { + const u32 value = index_scan_buffer[i]; + if (value == restart_index) { + continue; + } + max_index = (std::max)(max_index, value); + } + cached_max_index = max_index; + return cached_max_index; +} + +template +u64 BufferCache

::DrawVertexBound(u32 index, bool is_indexed) { const auto& array = maxwell3d->regs.vertex_streams[index]; const u64 stride = static_cast(array.stride); if (array.enable == 0 || stride == 0) { @@ -1267,16 +1298,23 @@ u64 BufferCache

::DrawVertexBound(u32 index, bool is_indexed) const { static_cast(draw_state.vertex_buffer.count); return (elements + 1) * stride; } - u64 max_index = 0; - switch (draw_state.index_buffer.format) { - case Maxwell::IndexFormat::UnsignedByte: - max_index = 0xFF; - break; - case Maxwell::IndexFormat::UnsignedShort: + const auto format = draw_state.index_buffer.format; + u64 max_index = 0xFF; + if (format == Maxwell::IndexFormat::UnsignedShort) { max_index = 0xFFFF; - break; - default: - return 0; + } else if (format != Maxwell::IndexFormat::UnsignedByte) { + const auto& limit = maxwell3d->regs.vertex_stream_limits[index]; + const GPUVAddr gpu_addr_begin = array.Address(); + const GPUVAddr gpu_addr_end = limit.Address() + 1; + if (gpu_addr_end <= gpu_addr_begin) { + return 0; + } + const bool walks = gpu_addr_end - gpu_addr_begin >= IMPLAUSIBLE_VERTEX_SIZE || + !gpu_memory->IsWithinGPUAddressRange(gpu_addr_end); + if (!walks) { + return 0; + } + max_index = DrawMaxIndex(); } const u64 elements = static_cast(draw_state.base_index) + max_index + 1; return (elements + 1) * stride; @@ -1285,6 +1323,7 @@ u64 BufferCache

::DrawVertexBound(u32 index, bool is_indexed) const { template void BufferCache

::UpdateVertexBuffers(bool is_indexed) { auto& flags = maxwell3d->dirty.flags; + max_index_scanned = false; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { const u64 bound = DrawVertexBound(index, is_indexed); if (bound <= last_draw_bounds[index]) { @@ -1319,7 +1358,7 @@ void BufferCache

::UpdateVertexBuffer(u32 index, bool is_indexed) { return; } // TODO: Analyze stride and number of vertices - constexpr u64 implausible_size = 64_MiB; + constexpr u64 implausible_size = IMPLAUSIBLE_VERTEX_SIZE; u64 address_size = gpu_addr_end - gpu_addr_begin; if (address_size > u64{(std::numeric_limits::max)()}) { address_size = implausible_size; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index ef05e7b778..a9d171354f 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -51,6 +51,7 @@ constexpr u32 NUM_VERTEX_BUFFERS = 16; #else constexpr u32 NUM_VERTEX_BUFFERS = 32; #endif +constexpr u64 IMPLAUSIBLE_VERTEX_SIZE = 64_MiB; constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; @@ -394,7 +395,9 @@ private: void UpdateVertexBuffer(u32 index, bool is_indexed); - [[nodiscard]] u64 DrawVertexBound(u32 index, bool is_indexed) const; + [[nodiscard]] u64 DrawVertexBound(u32 index, bool is_indexed); + + [[nodiscard]] u64 DrawMaxIndex(); void UpdateDrawIndirect(); @@ -488,6 +491,9 @@ private: u32 draw_instance_count = 0; std::array last_draw_bounds{}; + Common::ScratchBuffer index_scan_buffer; + u64 cached_max_index = 0; + bool max_index_scanned = false; u32 last_index_count = 0;