[Scummvm-tracker] [ScummVM :: Bugs] #15978: ASYLUM: Sanitarium always blits fullscreen rectangles

ScummVM :: Bugs trac at scummvm.org
Fri Aug 21 09:52:48 UTC 2026


#15978: ASYLUM: Sanitarium always blits fullscreen rectangles
------------------------+-----------------------------
Reporter:  mikrosk      |       Owner:  (none)
    Type:  defect       |      Status:  new
Priority:  normal       |   Component:  Engine: Asylum
 Version:               |  Resolution:
Keywords:  performance  |        Game:  Sanitarium
------------------------+-----------------------------
Comment (by alexbevi):

 The original game rebuilds the complete scene back buffer on every render
 tick. Something like the following could be used to preserve that
 composition and priority behavior while comparing it with the previously
 presented frame and copying only the bounding rectangle of changed pixels.


 {{{#!diff
 diff --git a/engines/asylum/system/screen.cpp
 b/engines/asylum/system/screen.cpp
 index 8a943e2ef842..61fbdb9dae01 100644
 --- a/engines/asylum/system/screen.cpp
 +++ b/engines/asylum/system/screen.cpp
 @@ -41,9 +41,11 @@ int g_debugDrawRects;

  #define TRANSPARENCY_TABLE_SIZE (256 * 256)

 -Screen::Screen(AsylumEngine *vm) : _vm(vm) ,
 -       _useColorKey(false), _transTableCount(0), _transTable(nullptr),
 _transTableBuffer(nullptr) {
 +Screen::Screen(AsylumEngine *vm) : _vm(vm),
 +       _presentedBufferValid(false), _useColorKey(false),
 _transTableCount(0),
 +       _transTable(nullptr), _transTableBuffer(nullptr) {
         _backBuffer.create(640, 480,
 Graphics::PixelFormat::createFormatCLUT8());
 +       _presentedBuffer.create(640, 480,
 Graphics::PixelFormat::createFormatCLUT8());

         _flag = -1;
         _clipRect = Common::Rect(0, 0, 640, 480);
 @@ -61,6 +63,7 @@ Screen::Screen(AsylumEngine *vm) : _vm(vm) ,

  Screen::~Screen() {
         _backBuffer.free();
 +       _presentedBuffer.free();

         clearTransTables();
  }
 @@ -196,8 +199,9 @@ void Screen::clear() {
         copyBackBufferToScreen();
  }

 -void Screen::drawWideScreenBars(int16 barSize) const {
 +void Screen::drawWideScreenBars(int16 barSize) {
         if (barSize > 0) {
 +               invalidateScreen();
                 _vm->_system->fillScreen(Common::Rect(0, 0, 640, barSize),
 0);
                 _vm->_system->fillScreen(Common::Rect(0, 480 - barSize,
 640, 480), 0);
         }
 @@ -208,7 +212,55 @@ void Screen::fillRect(int16 x, int16 y, int16 width,
 int16 height, uint32 color)
  }

  void Screen::copyBackBufferToScreen() {
 -       _vm->_system->copyRectToScreen((byte *)_backBuffer.getPixels(),
 _backBuffer.w, 0, 0, _backBuffer.w, _backBuffer.h);
 +       // The original RenderScene (0x40DD30) rebuilds the complete back
 buffer before
 +       // PresentBackBuffer (0x436120). Preserve that ordering, but avoid
 uploading pixels
 +       // which have not changed since the previous presentation.
 +       Common::Rect dirtyRect;
 +
 +       if (!_presentedBufferValid) {
 +               dirtyRect = Common::Rect(_backBuffer.w, _backBuffer.h);
 +       } else {
 +               for (int16 y = 0; y < _backBuffer.h; ++y) {
 +                       const byte *current = (const byte
 *)_backBuffer.getBasePtr(0, y);
 +                       const byte *presented = (const byte
 *)_presentedBuffer.getBasePtr(0, y);
 +
 +                       if (!memcmp(current, presented, _backBuffer.w))
 +                               continue;
 +
 +                       int16 left = 0;
 +                       while (current[left] == presented[left])
 +                               ++left;
 +
 +                       int16 right = _backBuffer.w;
 +                       while (right > left && current[right - 1] ==
 presented[right - 1])
 +                               --right;
 +
 +                       Common::Rect rowRect(left, y, right, y + 1);
 +                       if (dirtyRect.isEmpty())
 +                               dirtyRect = rowRect;
 +                       else
 +                               dirtyRect.extend(rowRect);
 +               }
 +       }
 +
 +       if (dirtyRect.isEmpty()) {
 +               debugC(3, kDebugLevelScene,
 "[Screen::copyBackBufferToScreen] Skipping unchanged frame");
 +               return;
 +       }
 +
 +       debugC(3, kDebugLevelScene,
 +              "[Screen::copyBackBufferToScreen] Presenting changed
 rectangle (%d, %d)-(%d, %d)",
 +              dirtyRect.left, dirtyRect.top, dirtyRect.right,
 dirtyRect.bottom);
 +
 +       _vm->_system->copyRectToScreen((const byte
 *)_backBuffer.getBasePtr(dirtyRect.left, dirtyRect.top),
 +                                     _backBuffer.pitch, dirtyRect.left,
 dirtyRect.top,
 +                                     dirtyRect.width(),
 dirtyRect.height());
 +       _presentedBuffer.copyRectToSurface(_backBuffer, dirtyRect.left,
 dirtyRect.top, dirtyRect);
 +       _presentedBufferValid = true;
 +}
 +
 +void Screen::invalidateScreen() {
 +       _presentedBufferValid = false;
  }

  void Screen::clip(Common::Rect *source, Common::Rect *destination, int32
 flags) const {
 diff --git a/engines/asylum/system/screen.h
 b/engines/asylum/system/screen.h
 index 0165cd313a56..651c753e22b7 100644
 --- a/engines/asylum/system/screen.h
 +++ b/engines/asylum/system/screen.h
 @@ -95,9 +95,11 @@ class Screen {
         // Misc
         void clear();
         void clearDefaultColor() { memset(_mainPalette, 0, 3);
 setupPalette(NULL, 0, 0); }
 -       void drawWideScreenBars(int16 barSize) const;
 +       void drawWideScreenBars(int16 barSize);
         void fillRect(int16 x, int16 y, int16 x2, int16 y2, uint32 color);
         void copyBackBufferToScreen();
 +       // Force the next back buffer presentation to refresh the complete
 game surface.
 +       void invalidateScreen();
         void setFlag(int16 val) { _flag = (val < -1) ? -1 : val; }
         int16 getFlag() { return _flag; }

 @@ -153,6 +155,8 @@ class Screen {
         AsylumEngine *_vm;

         Graphics::Surface _backBuffer;
 +       Graphics::Surface _presentedBuffer;
 +       bool _presentedBufferValid;
         Common::Rect _clipRect;
         Common::Array<GraphicQueueItem> _queueItems;

 diff --git a/engines/asylum/views/video.cpp
 b/engines/asylum/views/video.cpp
 index 0d206b2edaa9..2c3b747d7247 100644
 --- a/engines/asylum/views/video.cpp
 +++ b/engines/asylum/views/video.cpp
 @@ -110,6 +110,7 @@ bool VideoPlayer::handleEvent(const AsylumEvent &evt)
 {

                                 if (_vm->checkGameVersion("Steam")) {
                                         Graphics::Surface *st =
 getScreen()->getSurface()->convertTo(g_system->getScreenFormat(),
 _subtitlePalette);
 +                                       getScreen()->invalidateScreen();
                                         g_system->copyRectToScreen((const
 byte *)st->getBasePtr(0, 400), st->pitch, 0, 400, 640, 80);
                                         st->free();
                                         delete st;
 @@ -209,6 +210,7 @@ void VideoPlayer::play(const Common::Path &filename,
 bool showSubtitles) {
                                 continue;

                         if (_vm->checkGameVersion("Steam") ||
 _vm->isAltDemo()) {
 +                               getScreen()->invalidateScreen();
                                 g_system->copyRectToScreen((const byte
 *)frame->getPixels(), frame->pitch, x, y, frame->w, frame->h);
                         } else {
                                 if (_decoder->hasDirtyPalette())
 }}}
-- 
Ticket URL: <https://bugs.scummvm.org/ticket/15978#comment:3>
ScummVM :: Bugs <https://bugs.scummvm.org>
ScummVM


More information about the Scummvm-tracker mailing list