[Scummvm-git-logs] scummvm master -> ff3d0e83c2e47278098e313dff8f6f03497e8982
athrxx
athrxx at scummvm.org
Fri May 28 18:27:56 UTC 2021
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
d099ed5047 SCUMM: (FM-TOWNS) - make scrolling more smooth for fast platforms
ff3d0e83c2 GRAPHICS: fix Edge scaler glitch (bug no. 12553)
Commit: d099ed5047a023e0479e4ef5da8c04b9fc6e8aae
https://github.com/scummvm/scummvm/commit/d099ed5047a023e0479e4ef5da8c04b9fc6e8aae
Author: athrxx (athrxx at scummvm.org)
Date: 2021-05-28T20:26:28+02:00
Commit Message:
SCUMM: (FM-TOWNS) - make scrolling more smooth for fast platforms
The engine now measures whether it can perform one screen update within a 60Hz tick. Unfortunately the calls to OSystem::updateScreen() may take very long, depending on the backend and the filter setting. In this case the engine will start to catch up to the current frame. It should still look fine, unless the platform is way too slow for the selected filter setting (with the wrong settings it is not too difficult to achieve OSystem::updateScreen() durations of over 100ms).
Changed paths:
engines/scumm/gfx.h
engines/scumm/gfx_towns.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h
index b2ed6bd1a2..90b9fca96f 100644
--- a/engines/scumm/gfx.h
+++ b/engines/scumm/gfx.h
@@ -469,6 +469,11 @@ public:
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
// Helper class for FM-Towns output (required for specific hardware effects like switching graphics layers on and off).
class TownsScreen {
+public:
+ enum {
+ kDirtyRectsMax = 20,
+ kFullRedraw = (kDirtyRectsMax + 1)
+ };
public:
TownsScreen(OSystem *system);
~TownsScreen();
diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp
index 2d80b5cd04..184f0ba92b 100644
--- a/engines/scumm/gfx_towns.cpp
+++ b/engines/scumm/gfx_towns.cpp
@@ -185,7 +185,19 @@ void ScummEngine::towns_updateGfx() {
if (!_townsScreen)
return;
+ // Determine whether the smooth scrolling is likely to fall behind and needs to catch up (becoming more sloppy than smooth). It depends
+ // monstly on the hardware and the filter settings. Calls to _system->updateScreen() can be very expensive with the "wrong" filter setting.
+ // We simply check whether the average screen update duration would fit into a 60 Hz tick. If catchup mode is triggered once, it stays on
+ // permanently. Otherwise the scrolling can become very jerky when the engine keeps jumping between the settings (usually triggering it shortly
+ // after the start of a scrolling, resulting in a very visible jerk, and then falling back to non-catchup after the scrolling is done).
uint32 cur = _system->getMillis();
+ if (!_refreshNeedCatchUp) {
+ int dur = 0;
+ for (int i = 0; i < ARRAYSIZE(_refreshDuration); ++i)
+ dur += _refreshDuration[i];
+ _refreshNeedCatchUp = (dur / ARRAYSIZE(_refreshDuration)) > (1000 / 60);
+ }
+
while (_scrollTimer <= cur) {
if (!_scrollTimer)
_scrollTimer = cur;
@@ -194,6 +206,8 @@ void ScummEngine::towns_updateGfx() {
if (_townsScreen->isScrolling(0))
_scrollDeltaAdjust++;
_scrollRequest = 0;
+ if (!_refreshNeedCatchUp)
+ break;
}
_townsScreen->update();
@@ -315,9 +329,6 @@ const uint8 ScummEngine::_townsLayer2Mask[] = {
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-#define FMTOWNS_DIRTY_RECTS_MAX 20
-#define FMTOWNS_FULL_REDRAW (FMTOWNS_DIRTY_RECTS_MAX + 1)
-
TownsScreen::TownsScreen(OSystem *system) : _system(system), _width(0), _height(0), _pitch(0), _pixelFormat(system->getScreenFormat()), _scrollOffset(0), _scrollRemainder(0), _numDirtyRects(0) {
memset(&_layers[0], 0, sizeof(TownsScreenLayer));
memset(&_layers[1], 0, sizeof(TownsScreenLayer));
@@ -388,7 +399,7 @@ void TownsScreen::clearLayer(int layer) {
memset(l->pixels, 0, l->pitch * l->height);
_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
- _numDirtyRects = FMTOWNS_FULL_REDRAW;
+ _numDirtyRects = kFullRedraw;
}
@@ -431,10 +442,10 @@ uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) const {
}
void TownsScreen::addDirtyRect(int x, int y, int w, int h) {
- if (w <= 0 || h <= 0 || _numDirtyRects > FMTOWNS_DIRTY_RECTS_MAX)
+ if (w <= 0 || h <= 0 || _numDirtyRects > kDirtyRectsMax)
return;
- if (_numDirtyRects == FMTOWNS_DIRTY_RECTS_MAX) {
+ if (_numDirtyRects == kDirtyRectsMax) {
// full redraw
_dirtyRects.clear();
_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
@@ -501,7 +512,7 @@ void TownsScreen::toggleLayers(int flags) {
_dirtyRects.clear();
_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
- _numDirtyRects = FMTOWNS_FULL_REDRAW;
+ _numDirtyRects = kFullRedraw;
Graphics::Surface *s = _system->lockScreen();
assert(s);
@@ -524,7 +535,7 @@ void TownsScreen::scrollLayers(int flags, int offset) {
_dirtyRects.clear();
_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1));
- _numDirtyRects = FMTOWNS_FULL_REDRAW;
+ _numDirtyRects = kFullRedraw;
for (int i = 0; i < 2; ++i) {
if (!(flags & (1 << i)))
@@ -704,9 +715,6 @@ template void TownsScreen::updateScreenBuffer<uint16>();
template void TownsScreen::updateScreenBuffer<uint8>();
#endif
-#undef FMTOWNS_DIRTY_RECTS_MAX
-#undef FMTOWNS_FULL_REDRAW
-
} // End of namespace Scumm
#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index bdbec74b2a..b5f2818569 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -284,6 +284,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_townsScreen = 0;
_scrollRequest = _scrollDeltaAdjust = 0;
_scrollDestOffset = _scrollTimer = 0;
+ _refreshNeedCatchUp = false;
+ memset(_refreshDuration, 0, sizeof(_refreshDuration));
+ _refreshArrayPos = 0;
#ifdef USE_RGB_COLOR
_cjkFont = 0;
#endif
@@ -2264,10 +2267,9 @@ Common::Error ScummEngine::go() {
// before the main loop continues. We try to imitate that behaviour here to avoid glitches, but without making it
// overly complicated...
if (_scrollDeltaAdjust) {
- int adj = MIN<int>(_scrollDeltaAdjust * 4 / 3 - _scrollDeltaAdjust, delta * 4 / 3 - delta);
- delta += adj;
+ delta = MAX<int>(0, delta - _scrollDeltaAdjust) + (MIN<int>(_scrollDeltaAdjust, delta) << 2) / 3;
+ _scrollDeltaAdjust = 0;
}
- _scrollDeltaAdjust = 0;
#endif
if (delta < 1) // Ensure we don't get into an endless loop
delta = 1; // by not decreasing sleepers.
@@ -2327,10 +2329,21 @@ void ScummEngine::waitForTimer(int msec_delay) {
_sound->updateCD(); // Loop CD Audio if needed
parseEvents();
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ uint32 screenUpdateTimerStart = _system->getMillis();
towns_updateGfx();
+#endif
_system->updateScreen();
+ uint32 cur = _system->getMillis();
- if (_system->getMillis() >= start_time + msec_delay)
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ // These measurements are used to determine whether the FM-Towns smooth scrolling is likely to fall behind and need to catch
+ // up (becoming more sloppy than smooth). Calls to _system->updateScreen() can require way longer than a 60Hz tick, depending
+ // on the hardware and the filter setting. In fact, these calls can take way over 100ms for some unfortunate configs.
+ _refreshDuration[_refreshArrayPos] = (int)(cur - screenUpdateTimerStart);
+ _refreshArrayPos = (_refreshArrayPos + 1) % ARRAYSIZE(_refreshDuration);
+#endif
+ if (cur >= start_time + msec_delay)
break;
_system->delayMillis(10);
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 6f2eac4e69..a63d008762 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1375,6 +1375,9 @@ protected:
int _numCyclRects;
int _scrollRequest;
int _scrollDeltaAdjust;
+ int _refreshDuration[20];
+ int _refreshArrayPos;
+ bool _refreshNeedCatchUp;
uint32 _scrollTimer;
uint32 _scrollDestOffset;
uint16 _scrollFeedStrips[3];
Commit: ff3d0e83c2e47278098e313dff8f6f03497e8982
https://github.com/scummvm/scummvm/commit/ff3d0e83c2e47278098e313dff8f6f03497e8982
Author: athrxx (athrxx at scummvm.org)
Date: 2021-05-28T20:26:28+02:00
Commit Message:
GRAPHICS: fix Edge scaler glitch (bug no. 12553)
Force the creation of a new screen buffer for the scaler after leaving the overlay. The whole "old source" code looks a bit unfinished. Anyway, this fix should only kick in for one particular scaler and one particular situation and one particular backend. So I guess it is safe enough...
Changed paths:
backends/graphics/surfacesdl/surfacesdl-graphics.cpp
backends/graphics/surfacesdl/surfacesdl-graphics.h
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index b6d09a97fc..0e64337a77 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -138,7 +138,8 @@ SurfaceSdlGraphicsManager::SurfaceSdlGraphicsManager(SdlEventSource *sdlEventSou
_enableFocusRectDebugCode(false), _enableFocusRect(false), _focusRect(),
#endif
_transactionMode(kTransactionNone),
- _scalerPlugins(ScalerMan.getPlugins()) {
+ _scalerPlugins(ScalerMan.getPlugins()),
+ _needRestoreAfterOverlay(false) {
// allocate palette storage
_currentPalette = (SDL_Color *)calloc(sizeof(SDL_Color), 256);
@@ -1148,12 +1149,24 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
}
int oldScaleFactor;
+
if (!_overlayVisible) {
+ if (_needRestoreAfterOverlay) {
+ // This is needed for the Edge scaler which seems to be the only scaler to use the "_useOldSrc" feature.
+ // Otherwise the screen will not be properly restored after removing the overlay. We need to trigger a
+ // regeneration of SourceScaler::_bufferedOutput. The call to _scalerPlugin->setFactor() down below could
+ // do that in theory, but it won't unless the factor actually changes (which it doesn't). Now, the code
+ // in SourceScaler::setSource() looks a bit fishy, e. g. the *src argument isn't even used. But otherwise
+ // it does what we want here at least...
+ _scalerPlugin->setSource(0, _tmpscreen->pitch, _videoMode.screenWidth, _videoMode.screenHeight, _maxExtraPixels);
+ }
+
origSurf = _screen;
srcSurf = _tmpscreen;
width = _videoMode.screenWidth;
height = _videoMode.screenHeight;
oldScaleFactor = scale1 = _videoMode.scaleFactor;
+ _needRestoreAfterOverlay = false;
} else {
origSurf = _overlayscreen;
srcSurf = _tmpscreen2;
@@ -1161,6 +1174,7 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
height = _videoMode.overlayHeight;
scale1 = 1;
oldScaleFactor = _scalerPlugin->setFactor(1);
+ _needRestoreAfterOverlay = _useOldSrc;
}
// Add the area covered by the mouse cursor to the list of dirty rects if
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index 43083aa08f..c572840132 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -443,6 +443,14 @@ private:
return Common::Point(x * getOverlayWidth() / getWidth(),
y * getOverlayHeight() / getHeight());
}
+
+ /**
+ * Special case for scalers that use the useOldSrc feature (currently
+ * only the Edge scalers). The variable is checked after closing the
+ * overlay, so that the creation of a new output buffer for the scaler
+ * can be triggered.
+ */
+ bool _needRestoreAfterOverlay;
};
#endif
More information about the Scummvm-git-logs
mailing list