Repository navigation
Make the F2 UI usable on ultrawide screens - #20
Conversation
The D3D11 handler copied and uploaded the whole CEF view on every paint, through a deferred context. At 5120x1440 that is about 30 MB per paint, and paints pile up in each game frame when the game runs below the UI's 60 fps, so the menu barely rendered and ignored clicks, worst outdoors. CEF paints are now merged into a CPU copy of the view, and the render thread uploads only the repainted area with UpdateSubresource, at most once per frame. The view is sized after the swap chain's back buffer instead of the render target bound when the UI is drawn, which was 512x512 for a moment while loading and made CEF lay out and repaint the whole UI twice. The cursor is scaled by the screen height instead of the width, so it no longer grows on wide screens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With only the upload fixed, the UI still showed up half drawn outdoors at
5120x1440. cef_debug.log shows CEF dropping UI frames ("Client rejects
proposal to capture frame") right when the UI changes: CEF rasterizes on
the GPU and copies each frame back to memory for OnPaint, and that work
waits behind the game's frames when the game keeps the GPU busy.
CEF now starts with disable-gpu and disable-gpu-compositing, so the UI
(plain HTML and CSS) is drawn on the CPU straight into the memory OnPaint
reads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Force67
left a comment
There was a problem hiding this comment.
- Out-of-bounds read when the view shrinks: a dirty rect pending at the old size survives the resize (
UploadDirtyPixelsskips while pixel and texture sizes differ), then the size-change branch inOnPaintunions it with(0, 0, newW, newH). The box ends up larger thanm_pixelsand the texture, soUpdateSubresourcereads past the vector. Setm_hasDirtyPixels = falsebeforeAddDirtyRectin that branch, and maybe clamp the box inUploadDirtyPixelstoo. disable-gpu/disable-gpu-compositinghit everyOverlayAppuser. Software raster of full repaints at 5120x1440 costs CPU, so please also check 1080p on a weaker CPU. Opt-in per app might be safer.- The comment saying the back buffer matches the window client area isn't always true (the ini resolution can differ, mouse coords come from
ScreenToClient). Using the swap chain is still better, just soften the comment. m_pixelLockis held during a full 30 MBUpdateSubresource, which stallsOnPaint. Fine for now.GetRenderTargetSize()no longer reads the render target, rename it.- Comments are long, the ones in
OverlayApp.cppand on the class could be a lot shorter.
- When the view shrinks, drop the dirty area of the old size before marking the new view dirty, and clamp the uploaded box to the view, so UpdateSubresource never reads past the pixel copy. - OverlayApp no longer adds disable-gpu and disable-gpu-compositing. An app that wants CPU drawing adds them in its own OnBeforeCommandLineProcessing. - GetRenderTargetSize() is now UpdateViewSize(), since it reads the swap chain and updates the view size. - Shorter comments, and no claim that the back buffer always matches the window's client area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@jotagalindo do you happen to know what exactly fixed the stutter (low fps)? If it's the "disable" flags, then yes it's worth keeping Dirty rects are a good fix too (#3 can be closed once this is merged) |
|
@miredirex both helped, in different ways:
I didn't measure FPS for each change separately; what I checked was that the menu showed up complete and responded to clicks. I can measure it with and without the flags if that helps. |
jotagalindo
left a comment
There was a problem hiding this comment.
@Force67 Thanks for the review. Everything is addressed in 15210c2:
- Out-of-bounds read when the view shrinks: on a size change, the dirty area of the old size is dropped before the new view is marked dirty, and the box in
UploadDirtyPixelsis also always clamped to the view. - Back buffer comment: softened; it no longer says it always matches the window's client area.
GetRenderTargetSize()is nowUpdateViewSize().- Comments: shortened, including the one on the class. The one in
OverlayApp.cppis gone. m_pixelLockduringUpdateSubresource: left as is, as you said.
I will write more in next comments.
|
@miredirex About the measurements, I made another build in my TiltedEvolution fork to measure. It has this commit plus two things that are only there for testing: a counter of the UI frames CEF hands to the game each second, and a file that, when present, removes the flags. That way both modes use the same client and the same server (release).
With these numbers, drawing on the CPU costs little even with 4 threads, and less than on the GPU, probably (i think) because on the GPU CEF has to copy every frame back to memory. So the flags are what fix the stutter. Dirty rects lower the cost of uploading the view, but on their own they aren't enough. |
|
@Force67 @miredirex with these numbers, how would you prefer the flags? I think we have two options
The measurements answer the CPU cost question. Not forcing it on everyone who uses |
I don't think this matters too much, really. Whatever is simpler code wise
That's totally fine too |
|
@miredirex thanks! Then I'll go with option A, in TiltedUI the flags are opt-in as @Force67 suggested and TiltedEvolution turns them on with its own subclass. I've already reopened tiltedphoques/TiltedEvolution#945 for that, and its submodule is already prepared. |
The view upload fix now lives in TiltedUI (tiltedphoques/TiltedUI#20), so the client goes back to OverlayRenderHandlerD3D11 and only opts in to CPU drawing with CpuRenderedOverlayApp. TiltedUI points to the head of that PR, which ends up in TiltedUI's master when the PR is merged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Make the F2 UI usable on ultrawide screens TiltedUI's D3D11 overlay handler copied and uploaded the whole CEF view on every paint, through a deferred context. At 5120x1440 that is about 30 MB per paint, and paints pile up in each game frame when the game runs below the UI's 60 fps, so the menu barely rendered and ignored clicks, worst outdoors. Details in tiltedphoques/TiltedUI#20 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Make the F2 UI usable on ultrawide screens TiltedUI's D3D11 overlay handler copied and uploaded the whole CEF view on every paint, through a deferred context. At 5120x1440 that is about 30 MB per paint, and paints pile up in each game frame when the game runs below the UI's 60 fps, so the menu barely rendered and ignored clicks, worst outdoors. Details in tiltedphoques/TiltedUI#20 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit ed46728)
Hi!
This comes from tiltedphoques/TiltedEvolution#945.
I play on a 5120x1440 monitor and the F2 menu was almost impossible to use: it showed up in pieces and clicks didn't respond, especially outdoors. At 1920x1080 it worked perfectly and used AI to make this fix, Claude specifically. I tested it myself in game.
What was happening
OverlayRenderHandlerD3D11uploaded the whole view to the GPU on every CEF paint. At this resolution that is about 30 MB each time, and with low FPS several paints pile up per frame.cef_debug.logshowsClient rejects proposal to capture frame.What changes
OverlayRenderHandlerD3D11: uploads to the GPU only the area that changed, at most once per frame. It keeps its name and interface, so TiltedEvolution only needs the submodule bump.disable-gpuanddisable-gpu-compositing(inOverlayApp): the UI is simple HTML and CSS, so drawing it on the CPU is cheap. This applies to anything that usesOverlayApp.Testing: the same code, as part of tiltedphoques/TiltedEvolution#945, was built on
windows-2022and tested in game at 5120x1440 indoors and outdoors, on a 2080 Super. This TiltedUI version builds with TiltedEvolution onwindows-2022(build);Before and after (5120x1440, outdoors in Whiterun)
Before (current
dev): the menu shows up half drawn, parts of it come and go, and the cursor is huge.f2-ultrawide-before-fix.mp4
After (this PR): the connect dialog and the settings show up complete right away, and the cursor has its normal size.
f2-ultrawide-after-fix.mp4
Thanks a lot for everything, and all feedback is welcome!