mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
LSFG: let the generated frame wait for a display slot; unhide FSR on renderer=auto
Two bugs of mine, both found on-device with everything else working. ★ The zero-timeout acquire disabled frame generation entirely. Forcing FIFO fixed the MAILBOX discard, and the display rate still equalled the real rate. Under FIFO the presentation engine returns an image at a vblank, so at steady state nothing is ever free INSTANTLY — vkAcquireNextImageKHR with a zero timeout returns VK_NOT_READY every frame, the loop breaks, and every interpolated frame is dropped. Silently, because a dropped generated frame is a legitimate outcome and nothing logs it. The reasoning behind the zero was that an interpolated frame is a bonus not worth stalling for. That is backwards: presenting two frames per rendered frame MEANS waiting for the second display slot. Waiting is the mechanism, not the cost. Now a 50ms bound — six vblanks at 120Hz, so it expires only when something is genuinely wrong, while still keeping a lost surface from wedging the GS thread the way an unbounded wait would. ★ The in-game FSR row was gated on renderer == "vulkan", and the default is "auto". "auto" resolves to Vulkan on Android, so the row was hidden from anyone who had not explicitly pinned the renderer — which is nearly everyone, and was the reporter. Gated on the two backends that genuinely cannot run it instead.
This commit is contained in:
@@ -1208,8 +1208,22 @@ namespace GSLsfg
|
||||
// serialised on a vblank per generated frame and ate the very time the feature
|
||||
// exists to spend. VK_NOT_READY/VK_TIMEOUT leave the semaphore unsignalled, so
|
||||
// s_acquire_sems[i] stays clean for the next frame.
|
||||
const VkResult acq = vkAcquireNextImageKHR(s_device, swap_chain->GetSwapChain(), 0,
|
||||
s_acquire_sems[i], VK_NULL_HANDLE, &image_index);
|
||||
// ★ Bounded, but NOT zero. A zero timeout looks right — an interpolated frame is
|
||||
// a bonus, so why stall for one — and it silently disables the entire feature:
|
||||
// under FIFO the presentation engine hands an image back at a vblank, so at
|
||||
// steady state nothing is EVER free instantly, every acquire returns
|
||||
// VK_NOT_READY, and every generated frame is dropped. Observed exactly that on
|
||||
// an Adreno 740: LSFG active, FIFO confirmed, display rate still equal to the
|
||||
// real rate. Waiting for a display slot IS the mechanism here — presenting two
|
||||
// frames per rendered frame means waiting for the second slot.
|
||||
//
|
||||
// The bound is what keeps a lost surface from wedging the GS thread the way an
|
||||
// infinite wait would (see VKSwapChain::AcquireNextImage's own timeout, and the
|
||||
// background/rotate/fold case it documents). Generous next to a refresh interval
|
||||
// — 6 vblanks at 120Hz — so it only expires when something is actually wrong.
|
||||
static constexpr u64 kGeneratedAcquireTimeoutNs = 50ull * 1000 * 1000;
|
||||
const VkResult acq = vkAcquireNextImageKHR(s_device, swap_chain->GetSwapChain(),
|
||||
kGeneratedAcquireTimeoutNs, s_acquire_sems[i], VK_NULL_HANDLE, &image_index);
|
||||
if (acq != VK_SUCCESS && acq != VK_SUBOPTIMAL_KHR)
|
||||
break; // nothing free, out of date, or lost — still present the real frame
|
||||
|
||||
|
||||
@@ -823,7 +823,10 @@ private fun GraphicsPane(state: EmulationMenuUiState, viewModel: EmulationMenuVi
|
||||
// is: the two rows above choose how big the frame is RENDERED, and this chooses how it
|
||||
// gets to the screen. In full settings it lives under Display Effects next to CAS, which
|
||||
// is the wrong shelf for finding it while you are looking at the framerate.
|
||||
if (settings.renderer == "vulkan") {
|
||||
// "auto" is the DEFAULT and resolves to Vulkan on Android, so gating on the literal string
|
||||
// "vulkan" hid this row from almost everyone — which is exactly what happened. Only OpenGL
|
||||
// and software genuinely cannot run it.
|
||||
if (settings.renderer != "opengl" && settings.renderer != "software") {
|
||||
val fsr1On = settings.upscaler == com.armsx2.config.Settings.UPSCALER_FSR1
|
||||
MenuSwitchRow(
|
||||
str("renderer.fsr1.label"),
|
||||
|
||||
Reference in New Issue
Block a user