Some sanity on the vertex stream

This commit is contained in:
CamilleLaVey
2026-08-29 16:36:05 -04:00
parent d254d24dc2
commit 844cbfaa19
2 changed files with 66 additions and 28 deletions
+62 -28
View File
@@ -1272,58 +1272,92 @@ u64 BufferCache<P>::DrawMaxIndex() {
return cached_max_index; return cached_max_index;
} }
template <class P>
u64 BufferCache<P>::StreamAttributeExtent(u32 index) {
using VertexAttribute = typename Maxwell::VertexAttribute;
if (stream_extents_valid) {
return stream_extents[index];
}
stream_extents_valid = true;
stream_extents.fill(0);
for (size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
const auto& attribute = maxwell3d->regs.vertex_attrib_format[i];
if (attribute.constant != 0 || attribute.size == VertexAttribute::Size::Invalid) {
continue;
}
const u32 buffer = attribute.buffer.Value();
if (buffer >= NUM_VERTEX_BUFFERS) {
continue;
}
const u64 end = static_cast<u64>(attribute.offset.Value()) +
static_cast<u64>(attribute.SizeInBytes());
stream_extents[buffer] = (std::max)(stream_extents[buffer], end);
}
return stream_extents[index];
}
template <class P> template <class P>
u64 BufferCache<P>::DrawVertexBound(u32 index, bool is_indexed) { u64 BufferCache<P>::DrawVertexBound(u32 index, bool is_indexed) {
const auto& array = maxwell3d->regs.vertex_streams[index]; const auto& array = maxwell3d->regs.vertex_streams[index];
const u64 stride = static_cast<u64>(array.stride); if (array.enable == 0) {
if (array.enable == 0 || stride == 0) {
return 0; return 0;
} }
const u64 extent = StreamAttributeExtent(index);
if (extent == 0) {
return 0;
}
const u64 stride = static_cast<u64>(array.stride);
if (stride == 0) {
return extent;
}
const auto& draw_state = maxwell3d->draw_manager.draw_state; const auto& draw_state = maxwell3d->draw_manager.draw_state;
u64 elements = 0;
if (maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index)) { if (maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index)) {
if (draw_instance_count == 0) { if (draw_instance_count == 0) {
return 0; return 0;
} }
const u64 base_instance = static_cast<u64>(draw_state.base_instance); const u64 base_instance = static_cast<u64>(draw_state.base_instance);
u64 elements = base_instance + 1; elements = base_instance + 1;
if (array.frequency != 0) { if (array.frequency != 0) {
elements = (base_instance + static_cast<u64>(draw_instance_count) - 1) / elements = (base_instance + static_cast<u64>(draw_instance_count) - 1) /
static_cast<u64>(array.frequency) + static_cast<u64>(array.frequency) +
1; 1;
} }
return (elements + 1) * stride; } else if (!is_indexed) {
} elements = static_cast<u64>(draw_state.vertex_buffer.first) +
if (!is_indexed) { static_cast<u64>(draw_state.vertex_buffer.count);
const u64 elements = static_cast<u64>(draw_state.vertex_buffer.first) + } else {
static_cast<u64>(draw_state.vertex_buffer.count); const auto format = draw_state.index_buffer.format;
return (elements + 1) * stride; u64 max_index = 0xFF;
} if (format == Maxwell::IndexFormat::UnsignedShort) {
const auto format = draw_state.index_buffer.format; max_index = 0xFFFF;
u64 max_index = 0xFF; } else if (format != Maxwell::IndexFormat::UnsignedByte) {
if (format == Maxwell::IndexFormat::UnsignedShort) { const auto& limit = maxwell3d->regs.vertex_stream_limits[index];
max_index = 0xFFFF; const GPUVAddr gpu_addr_begin = array.Address();
} else if (format != Maxwell::IndexFormat::UnsignedByte) { const GPUVAddr gpu_addr_end = limit.Address() + 1;
const auto& limit = maxwell3d->regs.vertex_stream_limits[index]; if (gpu_addr_end <= gpu_addr_begin) {
const GPUVAddr gpu_addr_begin = array.Address(); return 0;
const GPUVAddr gpu_addr_end = limit.Address() + 1; }
if (gpu_addr_end <= gpu_addr_begin) { const bool walks = gpu_addr_end - gpu_addr_begin >= IMPLAUSIBLE_VERTEX_SIZE ||
return 0; !gpu_memory->IsWithinGPUAddressRange(gpu_addr_end);
if (!walks) {
return 0;
}
max_index = DrawMaxIndex();
} }
const bool walks = gpu_addr_end - gpu_addr_begin >= IMPLAUSIBLE_VERTEX_SIZE || elements = static_cast<u64>(draw_state.base_index) + max_index + 1;
!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end);
if (!walks) {
return 0;
}
max_index = DrawMaxIndex();
} }
const u64 elements = static_cast<u64>(draw_state.base_index) + max_index + 1; if (elements == 0) {
return (elements + 1) * stride; return extent;
}
return (elements - 1) * stride + extent;
} }
template <class P> template <class P>
void BufferCache<P>::UpdateVertexBuffers(bool is_indexed) { void BufferCache<P>::UpdateVertexBuffers(bool is_indexed) {
auto& flags = maxwell3d->dirty.flags; auto& flags = maxwell3d->dirty.flags;
max_index_scanned = false; max_index_scanned = false;
stream_extents_valid = false;
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
const u64 bound = DrawVertexBound(index, is_indexed); const u64 bound = DrawVertexBound(index, is_indexed);
if (bound <= last_draw_bounds[index]) { if (bound <= last_draw_bounds[index]) {
@@ -399,6 +399,8 @@ private:
[[nodiscard]] u64 DrawMaxIndex(); [[nodiscard]] u64 DrawMaxIndex();
[[nodiscard]] u64 StreamAttributeExtent(u32 index);
void UpdateDrawIndirect(); void UpdateDrawIndirect();
void UpdateUniformBuffers(size_t stage); void UpdateUniformBuffers(size_t stage);
@@ -494,6 +496,8 @@ private:
Common::ScratchBuffer<u32> index_scan_buffer; Common::ScratchBuffer<u32> index_scan_buffer;
u64 cached_max_index = 0; u64 cached_max_index = 0;
bool max_index_scanned = false; bool max_index_scanned = false;
std::array<u64, NUM_VERTEX_BUFFERS> stream_extents{};
bool stream_extents_valid = false;
u32 last_index_count = 0; u32 last_index_count = 0;