From c3204e25fbb1ca3effbf854809ed27b2ca6544c0 Mon Sep 17 00:00:00 2001 From: Davey Hughes Date: Fri, 23 Oct 2026 07:53:00 -0700 Subject: [PATCH] video_core: Only suppress command lists that issue geometry (#2467) GuessCmdRenderProperties() seeds vp_height and paddr from the current register state and overwrites them only if the scanned list writes those registers itself. It already records whether each value actually came from the list, but ShouldAllowCmdQueueTrigger() ignored those flags, so a list that merely sets state was classified using the previous pass's viewport. That is how a colour buffer rebind could be mistaken for a repeat render of the top screen and dropped. Track whether the list contains a draw trigger and require it before suppressing anything. A list that issues no geometry produces no pixels, so blocking it can never save work; it can only discard state that later lists depend on. The condition is strictly narrower than before, so this cannot suppress a list that was previously allowed. Measured on Ocarina of Time 3D with 3D enabled, the disabler still blocks 4141 lists per run versus 4441 before, retaining its effect. On Luigi's Mansion 2, one of the titles the RIGHT_EYE_DISABLE hack already disallows, blocking drops from 4601 to zero, which suggests those entries exist because of exactly this misclassification. Also guard both places where a scanned list address is turned into a pointer. CommandList::Reset() only reinterpret_casts, so an unmapped address would be dereferenced on the next read. This matters more now that the scan no longer stops as soon as vp_height and paddr are known and can therefore follow more nested command buffer jumps. --- src/video_core/pica/pica_core.cpp | 21 +++++++++++++++------ src/video_core/pica/pica_core.h | 2 ++ src/video_core/right_eye_disabler.cpp | 4 ++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/video_core/pica/pica_core.cpp b/src/video_core/pica/pica_core.cpp index 97a3a646b..3ae1b28b9 100644 --- a/src/video_core/pica/pica_core.cpp +++ b/src/video_core/pica/pica_core.cpp @@ -1182,10 +1182,6 @@ void PicaCore::LoadVertices(bool is_indexed) { } PicaCore::RenderPropertiesGuess PicaCore::GuessCmdRenderProperties(PAddr list, u32 size) { - // Initialize command list tracking. - const u8* head = memory.GetPhysicalPointer(list); - cmd_list.Reset(list, head, size); - constexpr size_t max_iterations = 0x100; RenderPropertiesGuess find_info{}; @@ -1193,6 +1189,13 @@ PicaCore::RenderPropertiesGuess PicaCore::GuessCmdRenderProperties(PAddr list, u find_info.vp_height = regs.internal.rasterizer.viewport_size_y.Value(); find_info.paddr = regs.internal.framebuffer.framebuffer.color_buffer_address.Value() * 8; + // Initialize command list tracking. + const u8* head = memory.GetPhysicalPointer(list); + if (head == nullptr) { + return find_info; + } + cmd_list.Reset(list, head, size); + auto process_write = [this, &find_info](u32 cmd_id, u32 value) { switch (cmd_id) { case PICA_REG_INDEX(rasterizer.viewport_size_y): @@ -1203,6 +1206,10 @@ PicaCore::RenderPropertiesGuess PicaCore::GuessCmdRenderProperties(PAddr list, u find_info.paddr = value * 8; find_info.paddr_found = true; break; + case PICA_REG_INDEX(pipeline.trigger_draw): + case PICA_REG_INDEX(pipeline.trigger_draw_indexed): + find_info.has_draw = true; + break; [[unlikely]] case PICA_REG_INDEX(pipeline.command_buffer.trigger[0]): [[unlikely]] case PICA_REG_INDEX(pipeline.command_buffer.trigger[1]): { const u32 index = @@ -1210,13 +1217,15 @@ PicaCore::RenderPropertiesGuess PicaCore::GuessCmdRenderProperties(PAddr list, u const PAddr addr = regs.internal.pipeline.command_buffer.GetPhysicalAddress(index); const u32 size = regs.internal.pipeline.command_buffer.GetSize(index); const u8* head = memory.GetPhysicalPointer(addr); - cmd_list.Reset(addr, head, size); + if (head != nullptr) { + cmd_list.Reset(addr, head, size); + } break; } default: break; } - return find_info.vp_heigh_found && find_info.paddr_found; + return find_info.vp_heigh_found && find_info.paddr_found && find_info.has_draw; }; size_t iterations = 0; diff --git a/src/video_core/pica/pica_core.h b/src/video_core/pica/pica_core.h index 8136d0c7a..a3c9cb14d 100644 --- a/src/video_core/pica/pica_core.h +++ b/src/video_core/pica/pica_core.h @@ -398,6 +398,8 @@ public: PAddr paddr; bool vp_heigh_found = false; bool paddr_found = false; + // Whether the scanned list issues any geometry. + bool has_draw = false; }; RenderPropertiesGuess GuessCmdRenderProperties(PAddr list, u32 size); diff --git a/src/video_core/right_eye_disabler.cpp b/src/video_core/right_eye_disabler.cpp index 9106f6644..a30f9d297 100644 --- a/src/video_core/right_eye_disabler.cpp +++ b/src/video_core/right_eye_disabler.cpp @@ -1,4 +1,4 @@ -// Copyright 2024 Azahar Emulator Project +// Copyright 2024-2026 Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the misc/licenses/gplv2.txt file included. @@ -21,7 +21,7 @@ bool RightEyeDisabler::ShouldAllowCmdQueueTrigger(PAddr addr, u32 size) { cmd_queue_trigger_happened = true; auto guess = gpu.impl->pica.GuessCmdRenderProperties(addr, size); - if (guess.vp_height == top_screen_size && !top_screen_blocked) { + if (guess.has_draw && guess.vp_height == top_screen_size && !top_screen_blocked) { if (top_screen_buf == 0) { top_screen_buf = guess.paddr; }