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;