Skip to content

Released async shader can permanently inhibit frame elision #108

Description

@hkf57

Description

One Path of Building instance can enter a severe, focus-gated GPU/UI stutter state on Windows. The displayed frame remains pixel-identical, but a focused 3840x1600 window repeatedly redraws and saturates the ANGLE/D3D11 path.

This was reproduced live with PoB 2.66.2 and SimpleGraphic 2.5-3b1a346 on an RTX 3080 Ti.

Measured behavior

State PoB GPU 3D PoB CPU
affected instance, focused, maximized (3856x1568) 80.5% avg / 89.7% max 89.4% avg
affected instance, unfocused no measurable PoB GPU engine 4.0% avg
affected instance, minimized no measurable PoB GPU engine negligible
affected instance, focused, windowed (1296x759) 1.26% avg 24.9% avg
affected instance, near-fullscreen windowed (3800x1500) 79.4% avg 99.4% avg
affected instance, focused at temporary 60 Hz 81.8% avg 83.6% avg
second PoB instance, same executable/runtime, focused/maximized 2.57% avg 33.1% avg

Two consecutive captures of the affected window were byte-for-byte identical. Changing PoB tabs did not reduce load while focused. The workload stops when the window loses focus and scales strongly with framebuffer size.

The NVIDIA per-application profile correctly targets the exact executable and disables G-SYNC for it. DWM remained light during the bad sample, and there were no display-driver resets or relevant application errors. Both PoB instances had the same RTSS hook loaded, so the overlay is not the primary discriminator.

Confirmed renderer state

SimpleGraphic's built-in Layers diagnostic showed:

  • 122 out of 122 frames drawn
  • Elide identical frames enabled
  • Elision inhibited continuously true

This rules out a changing command-buffer hash as the immediate trigger. A first candidate that stabilized raw command padding also passed the Windows build but made no performance difference in a portable A/B test.

Root cause

r_shaderHnd_c::~r_shaderHnd_c() aborts an asynchronous texture load when its shader's reference count reaches zero. Cancellation can leave the texture in a non-DONE state (INIT or PENDING_UPLOAD), and PurgeShaders() retains non-DONE shader slots.

r_renderer_c::PumpShaders() currently scans every retained slot. It therefore treats an already released, unusable shader as an active incomplete texture and sets inhibitElision on every frame. EndFrame() then clears lastFrameHash, forcing the full draw queue to replay continuously while PoB is focused.

Proposed fix

Only active shader slots (refCount > 0) should inhibit frame elision while their textures load. Released shaders cannot be used for rendering and must not globally disable elision.

PR #109 contains the one-line correction. The Windows CI build passed, and portable A/B validation on the reproducing build reduced focused/maximized GPU 3D load from 80.7% average to 6.27% while clearing the renderer's inhibition flag (22 out of 1020 frames drawn).

Environment

  • Windows build 26100
  • NVIDIA RTX 3080 Ti, driver 610.47
  • Active display: 3840x1600 at 144 Hz
  • PoB 2.66.2
  • SimpleGraphic 2.5-3b1a346
  • Loaded path: SimpleGraphic -> GLFW 3.4 -> EGL/GLES (ANGLE) -> D3D11/DXGI

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions