[scummvm-devel] OSystem::updateScreen() issues

Eugene Sandulenko sev at scummvm.org
Fri Mar 31 17:33:15 UTC 2023


On Fri, 31 Mar 2023 at 13:58, Miro Kropáček via scummvm-devel <
scummvm-devel at lists.scummvm.org> wrote:

> 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().
>
Correct, this is the intended use of it.

However, what about vertical refresh synchronization? (vsync) It can
> seriously hurt performance; what often happens is that some game does:
>
We do not do  VSync and never intended because it is plainly impossible in
most engines.

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.
>
ScummVM has no games with 60 fps animation. The closest things so far are
the 3d engines, but even those are not FPS-greedy.


> 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:
>
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?

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.

- inspect every engine and remove unnecessary updateScreen() calls (easy to
> break something, requires a lot of testing)
>
No way. We do not have all engine authors around.


> - 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)
>
No, it is unclear when to call it in the engines. Their architecture has
never been fps-bounded.


> - 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)
>
Not easily possible.


> - 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)
>
Yes, this is the way if your platform is FPS-locked.

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.

We encourage the engine authors to call it as much as possible for the best
user experience, aka smooth movements, including mouse movements.

Now, if your backend is FPS-locked, e.g. has VSync. You have to deal with
it in the backend.

Two approaches:

   -   Skip some updateScreen() calls when they come too frequently for you.
        The disadvantage for you is that something could
*theoretically* happen,
   that there will be no more calls for, say, more that 1/60s, and you start
   to drop frames
   - 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.

But touching 100 engines for the sake of a single platform defies one of
the main ScummVM principles: hardware abstraction.


Eugene
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.scummvm.org/pipermail/scummvm-devel/attachments/20230331/94235268/attachment-0001.htm>


More information about the scummvm-devel mailing list