BACKENDS: ATARI: Final screen resolution changes

- seamless resolution/palette switching (at least in theory, it still
  isn't artifact-free, no clue why)

- bugfixes (shake offset, some s_screenSurf corner cases)

- simplification the triple-buffering code
This commit is contained in:
Miro Kropacek
2024-11-21 11:31:25 +01:00
parent 10665fb723
commit feef4e1bf1
5 changed files with 392 additions and 185 deletions
+111 -173
View File
@@ -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<OSystem::TransactionError>(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());
+6 -12
View File
@@ -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,
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "atari-pendingscreenchanges.h"
#include <cassert>
#include <mint/falcon.h>
#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);
}
}
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef BACKENDS_GRAPHICS_ATARI_PENDINGSCREENCHANGES_H
#define BACKENDS_GRAPHICS_ATARI_PENDINGSCREENCHANGES_H
#include <utility>
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<int, bool>& aspectRatioCorrectionYOffset() const {
return _aspectRatioCorrectionYOffset;
}
const std::pair<bool, bool>& screenOffsets() const {
return _setScreenOffsets;
}
const std::pair<bool, bool>& 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;
// <value, set> ... std::optional would be so much better!
std::pair<int, bool> _aspectRatioCorrectionYOffset;
std::pair<bool, bool> _setScreenOffsets;
std::pair<bool, bool> _shrinkVidelVisibleArea;
};
#endif // ATARI-PENDINGSCREENCHANGES_H
+1
View File
@@ -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