[scummvm-devel] OSystem::updateScreen() issues
Miro Kropáček
miro.kropacek at gmail.com
Fri Mar 31 11:58:08 UTC 2023
Hi,
perhaps you remember, I'm adapting ScummVM for low-end hardware, like
really low-end (tens of MHz, 8bpp rendering). However at the same time I'd
like to present ScummVM games in a visually pleasing way, most importantly
without tearing, sudden jumps in performance or without audio artefacts.
It's not easy but I'm getting there.
However there is one function which is IMHO often misused -
OSystem::updateScreen(). Ideally, this function should serve as some kind
of final good-bye to the current game tick. And indeed, for the GUI it
works like that:
https://github.com/scummvm/scummvm/blob/master/gui/gui-manager.cpp#L541.
For game engines it is different, its description even explicitly says so:
it can be called many times during the game tick and it is up to the
backend to handle the situation. This mostly works, one can cache dirty
rectangles and copy them from the back buffer to screen in updateScreen().
However, what about vertical refresh synchronization? (vsync) It can
seriously hurt performance; what often happens is that some game does:
- Engine::run()
- audio stuff
- "AI" stuff
- animation
- copyRectToScreen()
- animation
- copyRectToSreen()
- updateScreen()
- move the player
- copyRectToScreen()
- updateScreen()
- ...
- end of Engine::run()
Technically speaking, you may argue that sure, the function is supposed to
"render the current content of the screen framebuffer to the display" so
whatever triggered that call, the engine has a good reason to show it and
therefore the backend should render (and vsync) to follow this wish.
However this works best in environments when SDL_RenderPresent +
SDL_RENDERER_PRESENTVSYNC is not blocking (which often isn't on modern
systems but on mine is).
Imagine the whole Engine::run() can finish in 60 FPS (one frame). If
Engine::run() reaches its end at, say, 3/4 of the frame and calls
updateScreen(), we're good and get a nice, smooth, 60 FPS animation.
However, if some engine follows the example given above, the whole game
will crawl because every updateScreen() will halt the engine for one whole
frame, basically making the game 3x as slow.
This can be solved with triple buffering
<https://en.wikipedia.org/wiki/Multiple_buffering#Triple_buffering> and
indeed, it helps. But it raises another performance hit - to keep both
buffers in sync, one has to copy dirty rectangles not only from the current
frame but also from the previous one. So that requires either some smart
merge strategy (so we e.g. don't update the screen twice in case of two
consecutive fullscreen updates but there are also more complex scenarios to
handle) or just blitting the whole buffer to screen in each frame. Whatever
you do, it is always slower than using the plain single buffering + dirty
rectangles + vsync.
So I've been wondering, is there a way to solve this? I can think of:
- inspect every engine and remove unnecessary updateScreen() calls (easy to
break something, requires a lot of testing)
- introduce a new function, something like flushScreen() which would be
added at the end of every Engine::run() (shouldn't break anything but
creates confusion)
- rewrite updateScreen() as updateScreen(bool flush = false) _and_ put
updateScreen(true) at the end of every Engine::run() (maybe a good
compromise; every backend can choose whether it want to adhere to it or
just doesn't care)
- do nothing and let the backend handle it (there was one idea to queue
updateScreen() calls up to some point (say 3/4 of the frame) but this
requires precise and platform-dependant timing so we e.g. don't wait one
frame solely for updateScreen() calls and then spend the next frame just
blitting one, crippling the performance again... so not easy)
What do you think? I'm open to any opinions or ideas, brainstorming is fun
(and more suited for mailing list instead of discord).
Oh and as a bonus, we'd get a nice FPS counter! (which is impossible to
implement in backends now because of the reasons above)
Cheers,
Miro
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.scummvm.org/pipermail/scummvm-devel/attachments/20230331/ef521502/attachment.htm>
More information about the scummvm-devel
mailing list