EDR-7560 - Throttle rendering when the window is occluded, not just minimized - #34
Open
Schogol wants to merge 1 commit into
Open
EDR-7560 - Throttle rendering when the window is occluded, not just minimized#34Schogol wants to merge 1 commit into
Schogol wants to merge 1 commit into
Conversation
The WINDOW_HIDDEN throttle (TriDevice::Throttle: 20ms sleep + ShouldSkipFrame,
which renders ~1 frame in 50) is only engaged for a minimized window
(WM_SIZE / SIZE_MINIMIZED). A window that is fully *covered* by another window is
not minimized, so it falls through to the weaker WINDOW_OUT_OF_FOCUS path (10ms
sleep, no frame-skip) and keeps rendering the full scene for a screen nobody can
see.
Measured in-game with a rendered space-view on my machine (NVIDIA GeForce RTX
2080, 60 Hz display, V-Sync on), with the window fully covered by another window:
- DX11: ~64 fps, ~2.3 ms/frame of active rendering
- DX12: ~72 fps, ~3.0 ms/frame
A focused, visible client renders at 60 fps (V-Sync-capped). So with V-Sync on at
60 Hz the client spends MORE GPU on rendering while the window is not visible than
while it is - a covered window bypasses the V-Sync cap and renders above it, so
the throttle is effectively backwards for covered windows. A minimized window, by
contrast, correctly drops to ~1 rendered frame/sec (~0.5 ms/frame).
DXGI cannot supply the missing signal: DXGI_STATUS_OCCLUDED is not reported for
flip-model swapchains (DXGI_SWAP_EFFECT_FLIP_DISCARD, mandatory on D3D12) under
DWM composition - both Present() and Present(0, DXGI_PRESENT_TEST) return S_OK
while the window is fully covered or minimized. Occlusion is therefore detected
from window geometry.
Tr2MainWindow now polls occlusion at 4 Hz (WM_TIMER) and drives the existing
WINDOW_HIDDEN throttle:
- IsWindowOccluded() samples 5 points on the window (centre + 4 inset corners)
and, at each, finds the topmost opaque top-level window; the window is hidden
iff every point is covered, or it is minimized. See-through overlays
(WS_EX_LAYERED / _TRANSPARENT / _TOOLWINDOW, e.g. the Alt+Tab switcher) are
skipped so they are not counted as occluders.
- Focus-gated: a focused window is on top and cannot be occluded, so it skips
the scan and pays only a HasFocus() check; the EnumWindows scan runs only on
a backgrounded client.
- Started from window creation, so a client launched straight into the
background is throttled immediately.
Result: a covered window now takes the same WINDOW_HIDDEN path as a minimized
one, dropping active rendering to ~1 frame/sec while hidden, with no change when
focused or visible-but-unfocused.
Author
|
MacOS already has occlusion-detection in Tr2MainWindow_MacOS.mm |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The WINDOW_HIDDEN throttle (TriDevice::Throttle: 20ms sleep + ShouldSkipFrame, which renders ~1 frame in 50) is only engaged for a minimized window (WM_SIZE / SIZE_MINIMIZED). A window that is fully covered by another window is not minimized, so it falls through to the weaker WINDOW_OUT_OF_FOCUS path (10ms sleep, no frame-skip) and keeps rendering the full scene for a screen nobody can see.
Measured in-game with a rendered space-view on my machine (NVIDIA GeForce RTX 2080, 60 Hz display, V-Sync on), with the window fully covered by another window:
A focused, visible client renders at 60 fps (V-Sync-capped). So with V-Sync on at 60 Hz the client spends more GPU on rendering while the window is not visible than while it is. A covered window bypasses the V-Sync cap and renders above it, so the throttle is effectively backwards for covered windows. A minimized window (only possible in Window mode), by contrast, correctly drops to ~1 rendered frame/sec (~0.5 ms/frame).
DXGI cannot supply the missing signal: DXGI_STATUS_OCCLUDED is not reported for flip-model swapchains (DXGI_SWAP_EFFECT_FLIP_DISCARD, mandatory on D3D12) under DWM composition - both Present() and Present(0, DXGI_PRESENT_TEST) return S_OK while the window is fully covered or minimized. Occlusion is therefore detected from window geometry.
Tr2MainWindow now polls occlusion at 4 Hz (WM_TIMER) and drives the existing WINDOW_HIDDEN throttle:
Result: a covered window now takes the same WINDOW_HIDDEN path as a minimized one, dropping active rendering to ~1 frame/sec while hidden, with no change when focused or visible-but-unfocused.
Disclaimer:
This issue was discovered by me on my local machine and I used Claude to investigate the cause and help me create this fix. I was able to build it fine against Python 3.12 but unable to test on a live client.
I did create a TrinityAL probe which confirmed the occlusion-detection to be working as expected.
This fix should be most noticeable by people running multiple clients on the same machine and lower the overall GPU usage.