diff --git a/backends/graphics/atari/atari-graphics.cpp b/backends/graphics/atari/atari-graphics.cpp index c1f104b612c..491898a2492 100644 --- a/backends/graphics/atari/atari-graphics.cpp +++ b/backends/graphics/atari/atari-graphics.cpp @@ -50,13 +50,11 @@ static const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::cr static const Graphics::PixelFormat PIXELFORMAT_RGB332 = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0); static const Graphics::PixelFormat PIXELFORMAT_RGB121 = Graphics::PixelFormat(1, 1, 2, 1, 0, 3, 1, 0, 0); -static bool s_shrinkVidelVisibleArea; - static void shrinkVidelVisibleArea() { // Active VGA screen area consists of 960 half-lines, i.e. 480 raster lines. // In case of 320x240, the number is still 480 but data is fetched // only for 240 lines so it doesn't make a difference to us. - +#ifdef SCREEN_ACTIVE if (hasSuperVidel()) { const int vOffset = ((480 - 400) / 2) * 2; // *2 because of half-lines @@ -77,33 +75,42 @@ static void shrinkVidelVisibleArea() { *((volatile int16*)0xFFFF82A2) = 889; *((volatile int16*)0xFFFF82AC) = 889 + vss; } +#endif } static bool s_tt; static int s_shakeXOffset; static int s_shakeYOffset; static int s_aspectRatioCorrectionYOffset; +static bool s_shrinkVidelVisibleArea; +static bool s_setScreenOffsets; static Graphics::Surface *s_screenSurf; static void VblHandler() { - if (s_screenSurf) { + // for easier querying + static Graphics::Surface *surf; + + if (s_screenSurf) + surf = s_screenSurf; + + if (s_screenSurf || s_setScreenOffsets) { #ifdef SCREEN_ACTIVE - uintptr p = (uintptr)s_screenSurf->getBasePtr(0, MAX_V_SHAKE + s_shakeYOffset + s_aspectRatioCorrectionYOffset); + uintptr p = (unsigned long)surf->getBasePtr(0, MAX_V_SHAKE + s_shakeYOffset + s_aspectRatioCorrectionYOffset); if (!s_tt) { - const int bitsPerPixel = (s_screenSurf->format == PIXELFORMAT_RGB121 ? 4 : 8); + const int bitsPerPixel = (surf->format == PIXELFORMAT_RGB121 ? 4 : 8); - s_shakeXOffset = -s_shakeXOffset; + int shakeXOffset = -s_shakeXOffset; - if (s_shakeXOffset >= 0) { + if (shakeXOffset >= 0) { p += MAX_HZ_SHAKE; - *((volatile char *)0xFFFF8265) = s_shakeXOffset; + *((volatile char *)0xFFFF8265) = shakeXOffset; } else { - *((volatile char *)0xFFFF8265) = MAX_HZ_SHAKE + s_shakeXOffset; + *((volatile char *)0xFFFF8265) = MAX_HZ_SHAKE + shakeXOffset; } // subtract 4 or 8 words if scrolling - *((volatile short *)0xFFFF820E) = s_shakeXOffset == 0 + *((volatile short *)0xFFFF820E) = shakeXOffset == 0 ? (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2 : (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2 - bitsPerPixel; } @@ -116,10 +123,12 @@ static void VblHandler() { *((volatile byte *)0xFFFF820D) = sptr.c[3]; #endif s_screenSurf = nullptr; + s_setScreenOffsets = false; } if (s_shrinkVidelVisibleArea) { - shrinkVidelVisibleArea(); + if (!s_tt) + shrinkVidelVisibleArea(); s_shrinkVidelVisibleArea = false; } } @@ -189,7 +198,8 @@ void AtariGraphicsShutdown() { } } -AtariGraphicsManager::AtariGraphicsManager() { +AtariGraphicsManager::AtariGraphicsManager() + : _pendingScreenChanges(this) { atari_debug("AtariGraphicsManager()"); enum { @@ -316,14 +326,20 @@ void AtariGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) { if (!hasFeature(f)) return; - // flags must be set to _currentState and _pendingScreenChange here - + // flags must be set to _pendingScreenChanges here + switch (f) { case OSystem::Feature::kFeatureAspectRatioCorrection: //atari_debug("setFeatureState(kFeatureAspectRatioCorrection): %d", enable); if (_aspectRatioCorrection != enable) { _aspectRatioCorrection = enable; - _pendingScreenChange |= kPendingAspectRatioCorrection; + + if (!_overlayVisible) { + _pendingScreenChanges.queueAspectRatioCorrection(); + + if (!_pendingState.inTransaction) + updateScreen(); + } } break; default: @@ -350,6 +366,9 @@ bool AtariGraphicsManager::setGraphicsMode(int mode, uint flags) { _pendingState.mode = (GraphicsMode)mode; + if (!_pendingState.inTransaction) + return endGFXTransaction() == OSystem::kTransactionSuccess; + // this doesn't seem to be checked anywhere return true; } @@ -360,25 +379,32 @@ void AtariGraphicsManager::initSize(uint width, uint height, const Graphics::Pix _pendingState.width = width; _pendingState.height = height; _pendingState.format = format ? *format : PIXELFORMAT_CLUT8; + + if (!_pendingState.inTransaction) + endGFXTransaction(); } void AtariGraphicsManager::beginGFXTransaction() { atari_debug("beginGFXTransaction"); _pendingState = GraphicsState(); - _pendingScreenChange &= ~kPendingTransaction; + _pendingState.inTransaction = true; + _pendingScreenChanges.clearTransaction(); } OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() { atari_debug("endGFXTransaction"); + _pendingState.inTransaction = false; + int error = OSystem::TransactionError::kTransactionSuccess; + bool hasPendingGraphicsMode = false; // no need to have a global flag if (_pendingState.mode != GraphicsMode::Unknown) { if (_pendingState.mode < GraphicsMode::DirectRendering || _pendingState.mode > GraphicsMode::TripleBuffering) { error |= OSystem::TransactionError::kTransactionModeSwitchFailed; } else if (_currentState.mode != _pendingState.mode) { - _pendingScreenChange |= kPendingScreenAddress; + hasPendingGraphicsMode = true; } } @@ -388,8 +414,8 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() { } else if (_pendingState.width % 16 != 0 && !hasSuperVidel()) { atari_warning("Requested width not divisible by 16, please report"); error |= OSystem::TransactionError::kTransactionSizeChangeFailed; - } else if (_currentState.width != _pendingState.width || _currentState.height != _pendingState.height) { - _pendingScreenChange |= kPendingVideoMode; + } else if (_overlayVisible || _currentState.width != _pendingState.width || _currentState.height != _pendingState.height) { + _pendingScreenChanges.queueVideoMode(); } } @@ -399,35 +425,35 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() { if (error != OSystem::TransactionError::kTransactionSuccess) { atari_warning("endGFXTransaction failed: %02x", (int)error); - _pendingScreenChange &= ~kPendingTransaction; + _pendingScreenChanges.clearTransaction(); return static_cast(error); } - if (_pendingScreenChange & kPendingScreenAddress) + if (hasPendingGraphicsMode) _currentState.mode = _pendingState.mode; - if ((_pendingScreenChange & kPendingVideoMode) - || (_overlayVisible && _pendingState.width > 0 && _pendingState.height > 0 && _pendingState.format == PIXELFORMAT_CLUT8)) { + if (_pendingScreenChanges.videoMode()) { _currentState.width = _pendingState.width; _currentState.height = _pendingState.height; _currentState.format = _pendingState.format; if (_overlayVisible) { - // that's it, really. updateScreen() will take care of everything. _checkUnalignedPitch = true; _ignoreHideOverlay = false; _overlayVisible = false; // if being in the overlay, reset everything (same as hideOverlay() does) - _pendingScreenChange |= kPendingAll; + // s_screenSurf will be set below + _pendingScreenChanges.queueAll(); } } else if (_overlayVisible) { // don't exit overlay unless there is real video mode to be set _ignoreHideOverlay = true; - _pendingScreenChange &= ~kPendingTransaction; + _pendingState = GraphicsState(); + _pendingScreenChanges.clearTransaction(); return OSystem::kTransactionSuccess; } - if (_pendingScreenChange & (kPendingScreenAddress | kPendingVideoMode)) { + if (_pendingScreenChanges.videoMode() || hasPendingGraphicsMode) { _chunkySurface.init(_currentState.width, _currentState.height, _currentState.width, _chunkySurface.getPixels(), _currentState.format); @@ -435,11 +461,14 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() { _screen[BACK_BUFFER1]->reset(_currentState.width, _currentState.height, 8, true); _screen[BACK_BUFFER2]->reset(_currentState.width, _currentState.height, 8, true); _workScreen = _screen[_currentState.mode <= GraphicsMode::SingleBuffering ? FRONT_BUFFER : BACK_BUFFER1]; + _pendingScreenChanges.setScreenSurface(&_screen[FRONT_BUFFER]->surf); _palette.clear(); - _pendingScreenChange |= kPendingPalette; + _pendingScreenChanges.queuePalette(); } + _pendingState = GraphicsState(); + // apply new screen changes updateScreen(); @@ -467,7 +496,7 @@ void AtariGraphicsManager::setPalette(const byte *colors, uint start, uint num) } } - _pendingScreenChange |= kPendingPalette; + _pendingScreenChanges.queuePalette(); } void AtariGraphicsManager::grabPalette(byte *colors, uint start, uint num) const { @@ -622,29 +651,34 @@ void AtariGraphicsManager::updateScreen() { _workScreen->clearDirtyRects(); + if (_overlayPending) { + atari_debug("Forcing overlay pending state"); + // must be done here because first updateScreen() is not called from showOverlay() + _pendingScreenChanges.queueAll(); + _overlayPending = false; + } + +#ifdef SCREEN_ACTIVE + // this assume that the screen surface is not going to be used yet + _pendingScreenChanges.applyBeforeVblLock(); +#endif + + set_sysvar_to_short(vblsem, 0); // lock vbl + if (screenUpdated && !isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) { // Triple buffer: // - alternate BACK_BUFFER1 and BACK_BUFFER2 - // - check if FRONT_BUFFER has been displayed for at least one frame - // - display the most recent buffer (BACK_BUFFER2 in our case) - // - alternate BACK_BUFFER2 and FRONT_BUFFER (only if BACK_BUFFER2 - // has been updated) + // - present BACK_BUFFER1 (as BACK_BUFFER2) + // - check if BACK_BUFFER2 has been displayed, if so, switch + // BACK_BUFFER2 and FRONT_BUFFER and make previous BACK_BUFFER2 work screen - set_sysvar_to_short(vblsem, 0); // lock vbl - - static long old_vbclock = get_sysvar(_vbclock); - long curr_vbclock = get_sysvar(_vbclock); - - if (old_vbclock != curr_vbclock) { - // at least one vbl has passed since setting new video base - // guard BACK_BUFFER2 from overwriting while presented + if (s_screenSurf == nullptr) { + // BACK_BUFFER2 has been set; guard it from overwriting while presented Screen *tmp = _screen[BACK_BUFFER2]; _screen[BACK_BUFFER2] = _screen[FRONT_BUFFER]; _screen[FRONT_BUFFER] = tmp; - - old_vbclock = curr_vbclock; } // swap back buffers @@ -653,137 +687,28 @@ void AtariGraphicsManager::updateScreen() { _screen[BACK_BUFFER2] = tmp; // queue BACK_BUFFER2 with the most recent frame content - s_screenSurf = &_screen[BACK_BUFFER2]->surf; - - set_sysvar_to_short(vblsem, 1); // unlock vbl - + _pendingScreenChanges.setScreenSurface(&_screen[BACK_BUFFER2]->surf); _workScreen = _screen[BACK_BUFFER1]; - // BACK_BUFFER2: now contains finished frame - // FRONT_BUFFER is displayed and still contains previously finished frame - } - - const int oldPendingScreenChange = _pendingScreenChange; - const bool oldAspectRatioCorrection = _aspectRatioCorrection; - if (_overlayPending) { - atari_debug("Forcing overlay pending state"); - _aspectRatioCorrection = false; - _pendingScreenChange = kPendingAll; - } - - bool doShrinkVidelVisibleArea = false; - bool doSuperVidelReset = false; - if (_pendingScreenChange & kPendingAspectRatioCorrection) { - assert(_workScreen->mode != -1); - - if (_aspectRatioCorrection && _currentState.height == 200 && !isOverlayVisible()) { - // apply machine-specific aspect ratio correction - if (!_vgaMonitor) { - _workScreen->mode &= ~PAL; - // 60 Hz - _workScreen->mode |= NTSC; - _pendingScreenChange |= kPendingVideoMode; - } else { - Screen *screen = _screen[FRONT_BUFFER]; - s_aspectRatioCorrectionYOffset = (screen->surf.h - 2*MAX_V_SHAKE - screen->offsettedSurf->h) / 2; - _pendingScreenChange |= kPendingShakeScreen; - - if (_pendingScreenChange & kPendingVideoMode) - doShrinkVidelVisibleArea = true; - else - s_shrinkVidelVisibleArea = true; - } - } else { - // reset back to default mode - if (!_vgaMonitor) { - _workScreen->mode &= ~NTSC; - // 50 Hz - _workScreen->mode |= PAL; - _pendingScreenChange |= kPendingVideoMode; - } else { - s_aspectRatioCorrectionYOffset = 0; - s_shrinkVidelVisibleArea = false; - - if (hasSuperVidel()) - doSuperVidelReset = true; - _pendingScreenChange |= kPendingVideoMode; - } - } - - _pendingScreenChange &= ~kPendingAspectRatioCorrection; } #ifdef SCREEN_ACTIVE - if (_pendingScreenChange & kPendingVideoMode) { - if (_workScreen->rez != -1) { - // unfortunately this reinitializes VDI, too - Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez); - - // strictly speaking, this is necessary only if kScreenAddress is set but makes code easier - static uint16 black[256]; - // Vsync(); // done by Setscreen() above - EsetPalette(0, isOverlayVisible() ? 16 : 256, black); - } else if (_workScreen->mode != -1) { - // VsetMode() must be called first: it resets all hz/v, scrolling and line width registers - // so even if kScreenAddress wasn't scheduled, we have to set new s_screenSurf to refresh them - static _RGB black[256]; - VsetRGB(0, 256, black); - // Vsync(); // done by (either) VsetMode() below - - if (doSuperVidelReset) { - VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480 - doSuperVidelReset = false; - } - - atari_debug("VsetMode: %04x", _workScreen->mode); - VsetMode(_workScreen->mode); - } - - // due to implied Vsync() above - assert(s_screenSurf == nullptr); - - // refresh Videl register settings - s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf; - s_shrinkVidelVisibleArea = doShrinkVidelVisibleArea; - - // keep kVideoMode for resetting the palette later - _pendingScreenChange &= ~(kPendingScreenAddress | kPendingShakeScreen); - } - - if (_pendingScreenChange & kPendingScreenAddress) { - // takes effect in the nearest VBL interrupt but we always wait for Vsync() in this case - Vsync(); - assert(s_screenSurf == nullptr); - - s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf; - _pendingScreenChange &= ~kPendingScreenAddress; - } - - if (_pendingScreenChange & kPendingShakeScreen) { - // takes effect in the nearest VBL interrupt - if (!s_screenSurf) - s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf; - _pendingScreenChange &= ~kPendingShakeScreen; - } - - if (_pendingScreenChange & (kPendingVideoMode | kPendingPalette)) { - if (!_tt) { - // takes effect in the nearest VBL interrupt - VsetRGB(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->falcon); - } else { - // takes effect immediatelly (it's possible that Vsync() hasn't been called: that's expected, - // don't cripple framerate only for a palette change) - EsetPalette(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->tt); - } - _pendingScreenChange &= ~(kPendingVideoMode | kPendingPalette); - } + _pendingScreenChanges.applyAfterVblLock(); #endif - if (_overlayPending) { - _aspectRatioCorrection = oldAspectRatioCorrection; - _pendingScreenChange = oldPendingScreenChange; - _overlayPending = false; + if (_pendingScreenChanges.screenSurface()) { + s_screenSurf = _pendingScreenChanges.screenSurface(); + _pendingScreenChanges.setScreenSurface(nullptr); } + if (_pendingScreenChanges.aspectRatioCorrectionYOffset().second) + s_aspectRatioCorrectionYOffset = _pendingScreenChanges.aspectRatioCorrectionYOffset().first; + if (_pendingScreenChanges.screenOffsets().second) + s_setScreenOffsets = _pendingScreenChanges.screenOffsets().first; + if (_pendingScreenChanges.shrinkVidelVisibleArea().second) + s_shrinkVidelVisibleArea = _pendingScreenChanges.shrinkVidelVisibleArea().first; + + set_sysvar_to_short(vblsem, 1); // unlock vbl + //atari_debug("end of updateScreen"); } @@ -798,7 +723,7 @@ void AtariGraphicsManager::setShakePos(int shakeXOffset, int shakeYOffset) { s_shakeYOffset = shakeYOffset; } - _pendingScreenChange |= kPendingShakeScreen; + _pendingScreenChanges.queueShakeScreen(); } void AtariGraphicsManager::showOverlay(bool inGUI) { @@ -813,13 +738,17 @@ void AtariGraphicsManager::showOverlay(bool inGUI) { _oldWorkScreen = _workScreen; _workScreen = _screen[OVERLAY_BUFFER]; + _pendingScreenChanges.setScreenSurface(&_workScreen->surf); // do not cache dirtyRects and oldCursorRect _workScreen->reset(getOverlayWidth(), getOverlayHeight(), getBitsPerPixel(getOverlayFormat()), false); _overlayVisible = true; - assert(_pendingScreenChange == kPendingNone); + if (!_pendingScreenChanges.empty()) { + warning("showOverlay: _pendingScreenChanges is %02x", _pendingScreenChanges.get()); + } + //_pendingScreenChanges.queueAll(); // must be called in updateScreen() ... _overlayPending = true; updateScreen(); } @@ -837,13 +766,18 @@ void AtariGraphicsManager::hideOverlay() { return; } + // BACK_BUFFER2 is intentional: regardless of the state before calling showOverlay(), + // this always contains the next desired frame buffer to show + _pendingScreenChanges.setScreenSurface(&_screen[_currentState.mode == GraphicsMode::TripleBuffering ? BACK_BUFFER2 : FRONT_BUFFER]->surf); _workScreen = _oldWorkScreen; _oldWorkScreen = nullptr; _overlayVisible = false; - assert(_pendingScreenChange == kPendingNone); - _pendingScreenChange = kPendingAll; + if (!_pendingScreenChanges.empty()) { + warning("hideOverlay: _pendingScreenChanges is %02x", _pendingScreenChanges.get()); + } + _pendingScreenChanges.queueAll(); updateScreen(); } @@ -1046,9 +980,12 @@ bool AtariGraphicsManager::notifyEvent(const Common::Event &event) { case kActionToggleAspectRatioCorrection: if (hasFeature(OSystem::Feature::kFeatureAspectRatioCorrection)) { _aspectRatioCorrection = !_aspectRatioCorrection; - _pendingScreenChange |= kPendingAspectRatioCorrection; - updateScreen(); + if (!_overlayVisible) { + _pendingScreenChanges.queueAspectRatioCorrection(); + + updateScreen(); + } return true; } break; @@ -1084,6 +1021,7 @@ void AtariGraphicsManager::allocateSurfaces() { // overlay is the default screen upon start _workScreen = _screen[OVERLAY_BUFFER] = new Screen(this, getOverlayWidth(), getOverlayHeight(), getOverlayFormat(), &_overlayPalette); _workScreen->reset(getOverlayWidth(), getOverlayHeight(), getBitsPerPixel(getOverlayFormat()), true); + _pendingScreenChanges.setScreenSurface(&_workScreen->surf); _chunkySurface.create(getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8); _overlaySurface.create(getOverlayWidth(), getOverlayHeight(), getOverlayFormat()); diff --git a/backends/graphics/atari/atari-graphics.h b/backends/graphics/atari/atari-graphics.h index 8fc77943337..0a8a843d5b3 100644 --- a/backends/graphics/atari/atari-graphics.h +++ b/backends/graphics/atari/atari-graphics.h @@ -31,6 +31,7 @@ #include "graphics/surface.h" #include "atari-cursor.h" +#include "atari-pendingscreenchanges.h" #include "atari-screen.h" #define MAX_HZ_SHAKE 16 // Falcon only @@ -38,6 +39,7 @@ class AtariGraphicsManager : public GraphicsManager, Common::EventObserver { friend class Cursor; + friend class PendingScreenChanges; friend class Screen; public: @@ -178,12 +180,14 @@ private: struct GraphicsState { GraphicsState() - : mode(GraphicsMode::Unknown) + : inTransaction(false) + , mode(GraphicsMode::Unknown) , width(0) , height(0) , format(Graphics::PixelFormat()) { } + bool inTransaction; GraphicsMode mode; int width; int height; @@ -195,17 +199,7 @@ private: // feature flags bool _aspectRatioCorrection = false; - enum PendingScreenChange { - kPendingNone = 0, - kPendingVideoMode = 1<<0, - kPendingScreenAddress = 1<<1, - kPendingAspectRatioCorrection = 1<<2, - kPendingPalette = 1<<3, - kPendingShakeScreen = 1<<4, - kPendingTransaction = kPendingVideoMode | kPendingScreenAddress | kPendingAspectRatioCorrection, - kPendingAll = kPendingTransaction | kPendingPalette | kPendingShakeScreen - }; - int _pendingScreenChange = kPendingNone; + PendingScreenChanges _pendingScreenChanges; enum { FRONT_BUFFER, diff --git a/backends/graphics/atari/atari-pendingscreenchanges.cpp b/backends/graphics/atari/atari-pendingscreenchanges.cpp new file mode 100644 index 00000000000..e0e05c093da --- /dev/null +++ b/backends/graphics/atari/atari-pendingscreenchanges.cpp @@ -0,0 +1,158 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "atari-pendingscreenchanges.h" + +#include +#include + +#include "backends/platform/atari/atari-debug.h" +#include "graphics/surface.h" + +#include "atari-graphics.h" +#include "atari-graphics-superblitter.h" + +void PendingScreenChanges::queueAll() { + _changes |= kAll; + + if (_manager->_tt) + _changes &= ~kAspectRatioCorrection; +} + +/* + * VsetRGB() - stores the palette in a buffer and applyBeforeVblLock in nearest VBL + * EsetPalette() - immediatelly applies the palette + * (V)SetScreen() - immediatelly sets physbase/logbase but explicitly calls Vsync() for resolution changes + * VsetMode() - explicitly calls Vsync() + */ + +void PendingScreenChanges::applyBeforeVblLock() { + _resetSuperVidel = false; + _switchToBlackPalette = (_changes & kVideoMode); + + _aspectRatioCorrectionYOffset.second = false; + _setScreenOffsets.second = false; + _shrinkVidelVisibleArea.second = false; + + if (_changes & kAspectRatioCorrection) { + processAspectRatioCorrection(); + _changes &= ~kAspectRatioCorrection; + } + + _switchToBlackPalette |= _resetSuperVidel; + + if (_changes & kVideoMode) { + processVideoMode(); + // don't reset kVideoMode yet + } +} + +void PendingScreenChanges::applyAfterVblLock() { + // VBL doesn't process new palette nor screen address updates + + if (_changes & kShakeScreen) { + _setScreenOffsets = std::make_pair(true, true); + _changes &= ~kShakeScreen; + } + + // restore current (kVideoMode) or set new (kPalette) palette + if (_changes & (kVideoMode | kPalette)) { + if (_switchToBlackPalette || (_changes & kPalette)) { + if (_manager->_tt) { + if (_changes & kPalette) + Vsync(); + EsetPalette(0, _manager->isOverlayVisible() ? _manager->getOverlayPaletteSize() : 256, _manager->_workScreen->palette->tt); + } else { + VsetRGB(0, _manager->isOverlayVisible() ? _manager->getOverlayPaletteSize() : 256, _manager->_workScreen->palette->falcon); + if (_changes & kPalette) + Vsync(); + } + } + + _changes &= ~(kVideoMode | kPalette); + } + + assert(_changes == kNone); +} + +void PendingScreenChanges::processAspectRatioCorrection() { + assert(!_manager->_tt); + + if (_manager->_aspectRatioCorrection && _manager->_currentState.height == 200 && !_manager->isOverlayVisible()) { + // apply machine-specific aspect ratio correction + if (!_manager->_vgaMonitor) { + _manager->_workScreen->mode &= ~PAL; + // 60 Hz + _manager->_workScreen->mode |= NTSC; + _changes |= kVideoMode; + } else { + _aspectRatioCorrectionYOffset = + std::make_pair((_manager->_workScreen->surf.h - 2*MAX_V_SHAKE - _manager->_workScreen->offsettedSurf->h) / 2, true); + _shrinkVidelVisibleArea = std::make_pair(true, true); + } + } else { + // reset back to default mode + if (!_manager->_vgaMonitor) { + _manager->_workScreen->mode &= ~NTSC; + // 50 Hz + _manager->_workScreen->mode |= PAL; + _changes |= kVideoMode; + } else { + _aspectRatioCorrectionYOffset = std::make_pair(0, true); + _shrinkVidelVisibleArea = std::make_pair(false, true); + + if (hasSuperVidel()) + _resetSuperVidel = true; + + // kPendingVideoMode will reset the shrunken Videl area + _changes |= kVideoMode; + } + } + + // for VsetMode() and/or _aspectRatioCorrectionYOffset + _setScreenOffsets = std::make_pair(true, true); +} + +void PendingScreenChanges::processVideoMode() { + // changing video mode implies an additional Vsync(): there's no way to change resolution + // and set new screen address (and/or shake offsets etc) in one go + if (_manager->_workScreen->rez != -1) { + if (_switchToBlackPalette) { + static uint16 black[256]; + EsetPalette(0, _manager->isOverlayVisible() ? _manager->getOverlayPaletteSize() : 256, black); + } + + // unfortunately this reinitializes VDI, too + Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _manager->_workScreen->rez); + } else if (_manager->_workScreen->mode != -1) { + if (_switchToBlackPalette) { + static _RGB black[256]; + VsetRGB(0, _manager->isOverlayVisible() ? _manager->getOverlayPaletteSize() : 256, black); + } + + // VsetMode() must be called first: it resets all hz/v, scrolling and line width registers + if (_resetSuperVidel) + VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480 + + atari_debug("VsetMode: %04x", _manager->_workScreen->mode); + VsetMode(_manager->_workScreen->mode); + } +} diff --git a/backends/graphics/atari/atari-pendingscreenchanges.h b/backends/graphics/atari/atari-pendingscreenchanges.h new file mode 100644 index 00000000000..227814a28e3 --- /dev/null +++ b/backends/graphics/atari/atari-pendingscreenchanges.h @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef BACKENDS_GRAPHICS_ATARI_PENDINGSCREENCHANGES_H +#define BACKENDS_GRAPHICS_ATARI_PENDINGSCREENCHANGES_H + +#include + +class AtariGraphicsManager; +namespace Graphics { +class Surface; +} + +class PendingScreenChanges { +public: + PendingScreenChanges(AtariGraphicsManager *manager) + : _manager(manager) { + } + + void clearTransaction() { + _changes &= ~kTransaction; + } + + void setScreenSurface(Graphics::Surface *surface) { + _surface = surface; + } + + void queueVideoMode() { + _changes |= kVideoMode; + } + void queueAspectRatioCorrection() { + _changes |= kAspectRatioCorrection; + } + void queuePalette() { + _changes |= kPalette; + } + void queueShakeScreen() { + _changes |= kShakeScreen; + } + void queueAll(); + + int get() const { + return _changes; + } + + bool videoMode() const { + return _changes & kVideoMode; + } + bool empty() const { + return _changes == kNone; + } + + Graphics::Surface *screenSurface() const { + return _surface; + } + const std::pair& aspectRatioCorrectionYOffset() const { + return _aspectRatioCorrectionYOffset; + } + const std::pair& screenOffsets() const { + return _setScreenOffsets; + } + const std::pair& shrinkVidelVisibleArea() const { + return _shrinkVidelVisibleArea; + } + + void applyBeforeVblLock(); + void applyAfterVblLock(); + +private: + void processAspectRatioCorrection(); + void processVideoMode(); + + enum Change { + kNone = 0, + kVideoMode = 1<<0, + kAspectRatioCorrection = 1<<1, + kPalette = 1<<2, + kShakeScreen = 1<<3, + kTransaction = kVideoMode | kAspectRatioCorrection, + kAll = kTransaction | kPalette | kShakeScreen + }; + int _changes = kNone; + + AtariGraphicsManager *_manager; + + Graphics::Surface *_surface = nullptr; + + bool _resetSuperVidel; + bool _switchToBlackPalette; + + // ... std::optional would be so much better! + std::pair _aspectRatioCorrectionYOffset; + std::pair _setScreenOffsets; + std::pair _shrinkVidelVisibleArea; + +}; + +#endif // ATARI-PENDINGSCREENCHANGES_H diff --git a/backends/module.mk b/backends/module.mk index 8be83686573..8f0a51138f4 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -370,6 +370,7 @@ MODULE_OBJS += \ graphics/atari/atari-cursor.o \ graphics/atari/atari-graphics.o \ graphics/atari/atari-graphics-asm.o \ + graphics/atari/atari-pendingscreenchanges.o \ graphics/atari/atari-screen.o \ mixer/atari/atari-mixer.o endif