aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/engines
diff options
context:
space:
mode:
authorSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 11:41:26 -0500
committerSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 14:53:47 -0500
commit427b9abfdc4898f1aeef978ee6edbdeb336db280 (patch)
tree209aca8236268723c63f9879150d93f2fd6f426e /src/video_core/engines
parent8556e27015ded665ca9a5522071cdeeec20a1575 (diff)
video_core: continue a split instance loop at its own instance index
Writing any register outside the instance loop's own methods now draws the waiting instances first, so the batch sees the base instance it was begun with. When a title writes another register in the middle of a loop, such as a per-instance constant buffer, the loop is drawn in several batches, and each later batch started again at global_base_instance_index with one extra instance, so it used the instance IDs of the first records. Count the instances already drawn for the current loop and start the next batch after them, without the extra first-instance draw. A loop that is not split is drawn exactly as before. Follow-up to 2ad0830d2f and eb99da6072. Assisted-by: Claude Sonnet 5.5 (implementation and commit message)
Diffstat (limited to 'src/video_core/engines')
-rw-r--r--src/video_core/engines/draw_manager.cpp16
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.h5
3 files changed, 20 insertions, 5 deletions
diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp
index 038e5a2e69..974a9a6e9a 100644
--- a/src/video_core/engines/draw_manager.cpp
+++ b/src/video_core/engines/draw_manager.cpp
@@ -72,11 +72,19 @@ void Maxwell3D::DrawManager::Clear(Maxwell3D& maxwell3d, u32 layer_count) {
}
}
-void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d) {
+void Maxwell3D::DrawManager::DrawDeferred(Maxwell3D& maxwell3d, bool loop_continues) {
if (draw_state.draw_mode != DrawMode::Instance || draw_state.instance_count == 0) {
+ if (!loop_continues) {
+ draw_state.drawn_instances = 0;
+ }
return;
}
- DrawEnd(maxwell3d, draw_state.instance_count + 1, true);
+ // The first draw of a batch also covers the loop's first instance; a batch that continues a
+ // loop split by a register write draws only the instances after the ones already drawn.
+ const bool continued = draw_state.drawn_instances != 0;
+ DrawEnd(maxwell3d, draw_state.instance_count + (continued ? 0 : 1), true);
+ draw_state.drawn_instances =
+ loop_continues ? draw_state.drawn_instances + draw_state.instance_count : 0;
draw_state.instance_count = 0;
}
@@ -121,6 +129,7 @@ void Maxwell3D::DrawManager::DrawArrayInstanced(Maxwell3D& maxwell3d, Maxwell3D:
draw_state.vertex_buffer.count = vertex_count;
if (!subsequent) {
draw_state.instance_count = 1;
+ draw_state.drawn_instances = 0;
}
draw_state.base_instance = draw_state.instance_count - 1;
draw_state.draw_mode = DrawMode::Instance;
@@ -220,6 +229,9 @@ void Maxwell3D::DrawManager::DrawEnd(Maxwell3D& maxwell3d, u32 instance_count, b
[[fallthrough]];
case DrawMode::General:
draw_state.base_instance = maxwell3d.regs.global_base_instance_index;
+ if (draw_state.draw_mode == DrawMode::Instance && draw_state.drawn_instances != 0) {
+ draw_state.base_instance += draw_state.drawn_instances + 1;
+ }
draw_state.base_index = maxwell3d.regs.global_base_vertex_index;
if (draw_state.draw_indexed) {
draw_state.index_buffer = maxwell3d.regs.index_buffer;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 9731f54584..4ef7d42979 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -342,7 +342,7 @@ void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
// the state they were begun with: NVN's draw macros reset the base instance (and its copy in
// c[0][0x644]) right after the loop, before the macro returns.
if (draw_manager.HasDeferredDraw() && !DrawManager::IsInstanceLoopMethod(method)) [[unlikely]] {
- draw_manager.DrawDeferred(*this);
+ draw_manager.DrawDeferred(*this, true);
}
regs.reg_array[method] = argument;
for (auto const& table : dirty.tables)
@@ -537,7 +537,7 @@ void Maxwell3D::CallMultiMethod(Core::System& system, u32 method, const u32* bas
}
// As in ProcessDirtyRegisters: the paths below write without it.
if (draw_manager.HasDeferredDraw() && !DrawManager::IsInstanceLoopMethod(method)) [[unlikely]] {
- draw_manager.DrawDeferred(*this);
+ draw_manager.DrawDeferred(*this, true);
}
switch (method) {
case MAXWELL3D_REG_INDEX(const_buffer.buffer):
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index af0e37978e..c5d1eb3230 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -3069,6 +3069,8 @@ public:
Maxwell3D::Regs::IndexBuffer index_buffer;
u32 base_instance{};
u32 instance_count{};
+ /// Deferred instances already drawn by flushes in the middle of the current loop.
+ u32 drawn_instances{};
std::vector<u8> inline_index_draw_indexes;
};
struct DrawTextureState {
@@ -3095,7 +3097,8 @@ public:
};
void ProcessMethodCall(Maxwell3D& maxwell3d, u32 method, u32 argument);
void Clear(Maxwell3D& maxwell3d, u32 layer_count);
- void DrawDeferred(Maxwell3D& maxwell3d);
+ /// Draws the waiting instances. loop_continues is set when the loop goes on afterwards.
+ void DrawDeferred(Maxwell3D& maxwell3d, bool loop_continues = false);
/// Instances after the first wait to be drawn together (DrawDeferred).
[[nodiscard]] bool HasDeferredDraw() const noexcept {
return draw_state.draw_mode == DrawMode::Instance && draw_state.instance_count != 0;