<div dir="ltr">Hi,<div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"></div></div><div><br></div><div><div>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.<br><br>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: <a href="https://github.com/scummvm/scummvm/blob/master/gui/gui-manager.cpp#L541" target="_blank">https://github.com/scummvm/scummvm/blob/master/gui/gui-manager.cpp#L541</a>.<br><br>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().<br><br>However, what about vertical refresh synchronization? (vsync) It can seriously hurt performance; what often happens is that some game does:</div><div><br>- Engine::run()<br>- audio stuff<br>- "AI" stuff</div><div>- animation<br>- copyRectToScreen()</div><div>- animation<br>- copyRectToSreen()<br>- updateScreen()</div><div>- move the player<br>- copyRectToScreen()<br>- updateScreen()<br>- ...<br>- end of Engine::run()<br><br>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).</div><div><br></div><div>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.</div><div><br></div><div>This can be solved with <a href="https://en.wikipedia.org/wiki/Multiple_buffering#Triple_buffering" target="_blank">triple buffering</a> 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 <a class="gmail_plusreply" id="m_-7309601318334409700plusReplyChip-2">+</a> vsync.</div><div><br></div><div>So I've been wondering, is there a way to solve this? I can think of:</div><div><br></div><div>- inspect every engine and remove unnecessary updateScreen() calls (easy to break something, requires a lot of testing)<br></div><div>- 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)</div><div>- 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)</div><div>- 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)</div><div><br></div><div>What do you think? I'm open to any opinions or ideas, brainstorming is fun (and more suited for mailing list instead of discord).</div><div><br></div><div>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)</div><div><br></div><div>Cheers,</div><div>Miro</div></div></div>