<div dir="ltr"><div dir="ltr">On Fri, 31 Mar 2023 at 13:58, Miro Kropáček via scummvm-devel <<a href="mailto:scummvm-devel@lists.scummvm.org">scummvm-devel@lists.scummvm.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div>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></div></div></div></blockquote><div>Correct, this is the intended use of it.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div>However, what about vertical refresh synchronization? (vsync) It can seriously hurt performance; what often happens is that some game does:</div></div></div></blockquote><div><div>We do not do  VSync and never intended because it is plainly impossible in most engines.</div><br class="gmail-Apple-interchange-newline"></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><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></div></blockquote><div>ScummVM has no games with 60 fps animation. The closest things so far are the 3d engines, but even those are not FPS-greedy.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><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_2325165600841644346m_-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></div></blockquote><div>Do we need to solve this? What engines/games bring you the issues? Are you sure it is caused by the updateScreen() calls and not something else?</div><div><br></div><div>Many calls to the backend will only update the mouse because there were no game screen or overlay changes, and for that, you have the dirty rectangles implemented in your backend.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div>- inspect every engine and remove unnecessary updateScreen() calls (easy to break something, requires a lot of testing)<br></div></div></div></blockquote><div>No way. We do not have all engine authors around.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div></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></div></blockquote><div>No, it is unclear when to call it in the engines. Their architecture has never been fps-bounded.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><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></div></blockquote><div>Not easily possible.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><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></div></blockquote><div>Yes, this is the way if your platform is FPS-locked.</div><div><br></div><div>OSystem::updateScreen() is intended to signal to the backend that the game screen is in a drawable state. E.g. that there are no half-drawn things.</div><div><br></div><div>We encourage the engine authors to call it as much as possible for the best user experience, aka smooth movements, including mouse movements.</div><div><br></div><div>Now, if your backend is FPS-locked, e.g. has VSync. You have to deal with it in the backend.</div><div><br></div><div>Two approaches:</div><div><ul><li>  Skip some updateScreen() calls when they come too frequently for you.<br>     The disadvantage for you is that something could <i>theoretically</i> happen, that there will be no more calls for, say, more that 1/60s, and you start to drop frames</li><li>Run a timer in your engine, which will fire every 60s, and draw whatever you keep in your framebuffer at the moment. Ignore all updateScreen() calls.</li></ul><div>But touching 100 engines for the sake of a single platform defies one of the main ScummVM principles: hardware abstraction.</div></div><div><br></div><div><br></div><div>Eugene</div></div></div>