BACKEND: ATARI: Screen shaking and some fixes

- surface setup for optimized 4-bit C2P routine wasn't properly detected

- STFA pretends to support Falcon sampling frequencies on TT leading to
  suboptimal sample mixing

- delayMillis() should check also for other events (fixes Future Wars)
  but avoid doing it for SCI as its MIDI timer would call itself in a
  recursive loop

- SuperVidel doesn't need to use VsetScreen() in VBL anymore

- Wetlands, Teen Agent, Shivers and Private Eye need non-aligned
  surface  widths

- However Wetlands and Private Eye use setCursorPalette, see
  https://bugs.scummvm.org/ticket/14524

- Added warning for Phantasmagoria's 630x450, nothing can be done there
  as the game also requires non-aligned surfaces and at the same time
  the buffer has to be aligned on 16 bytes.

- BDF scaling disabled by default
This commit is contained in:
Miro Kropacek 2023-07-09 10:03:03 +02:00
parent f85fde7707
commit d9a5de0fe6
8 changed files with 273 additions and 143 deletions

View File

@ -80,6 +80,10 @@ private:
return [](void *ptr) { Mfree((uintptr)ptr & 0x00FFFFFF); };
}
Common::Rect alignRect(int x, int y, int w, int h) const override {
return Common::Rect(x, y, x + w, y + h);
}
static long hasSvRamBoosted() {
register long ret __asm__ ("d0") = 0;

View File

@ -64,28 +64,25 @@ public:
}
private:
void copyRectToSurface(Graphics::Surface &dstSurface,
const Graphics::Surface &srcSurface, int destX, int destY,
void copyRectToSurface(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
int destX, int destY,
const Common::Rect &subRect) const override {
// 'pChunkyEnd' is a delicate parameter: the c2p routine compares it to the address register
// used for pixel reading; two common mistakes:
// 1. (subRect.left, subRect.bottom) = beginning of the next line *including the offset*
// 2. (subRect.right, subRect.bottom) = even worse, end of the *next* line, not current one
const byte *pChunky = (const byte *)srcSurface.getBasePtr(subRect.left, subRect.top);
const byte *pChunkyEnd = (const byte *)srcSurface.getBasePtr(subRect.right, subRect.bottom-1);
const byte *pChunky = (const byte *)srcSurface.getBasePtr(subRect.left, subRect.top);
const byte *pChunkyEnd = (const byte *)srcSurface.getBasePtr(subRect.right, subRect.bottom-1);
const uint32 bitsPerPixel = dstSurface.format.isCLUT8() || dstSurface.format == PIXELFORMAT_RGB332 ? 8 : 4;
const uint32 screenPitch = dstSurface.pitch * bitsPerPixel/8;
byte *pScreen = (byte *)dstSurface.getPixels() + destY * dstSurface.pitch + destX * dstBitsPerPixel/8;
byte *pScreen = (byte *)dstSurface.getPixels() + destY * screenPitch + destX * bitsPerPixel/8;
if (bitsPerPixel == 8) {
if (dstBitsPerPixel == 8) {
if (srcSurface.pitch == subRect.width()) {
if (srcSurface.pitch == dstSurface.pitch) {
asm_c2p1x1_8(pChunky, pChunkyEnd, pScreen);
return;
} else if (srcSurface.pitch == dstSurface.pitch/2) {
asm_c2p1x1_8_tt(pChunky, pChunkyEnd, pScreen, screenPitch);
asm_c2p1x1_8_tt(pChunky, pChunkyEnd, pScreen, dstSurface.pitch);
return;
}
}
@ -95,10 +92,9 @@ private:
subRect.width(),
srcSurface.pitch,
pScreen,
screenPitch);
dstSurface.pitch);
} else {
// compare unmodified dst pitch
if (srcSurface.pitch == subRect.width() && srcSurface.pitch == dstSurface.pitch) {
if (srcSurface.pitch == subRect.width() && srcSurface.pitch/2 == dstSurface.pitch) {
asm_c2p1x1_4(pChunky, pChunkyEnd, pScreen);
return;
}
@ -108,23 +104,15 @@ private:
subRect.width(),
srcSurface.pitch,
pScreen,
screenPitch);
dstSurface.pitch);
}
}
void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
int destX, int destY, const Common::Rect &subRect, uint32 key,
void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
int destX, int destY,
const Common::Rect &subRect, uint32 key,
const Graphics::Surface &bgSurface, const byte srcPalette[256*3]) const override {
Common::Rect backgroundRect(destX, destY, destX + subRect.width(), destY + subRect.height());
// ensure that background's left and right lie on a 16px boundary and double the width if needed
backgroundRect.moveTo(backgroundRect.left & 0xfff0, backgroundRect.top);
const int deltaX = destX - backgroundRect.left;
backgroundRect.right = (backgroundRect.right + deltaX + 15) & 0xfff0;
if (backgroundRect.right > bgSurface.w)
backgroundRect.right = bgSurface.w;
const Common::Rect backgroundRect = alignRect(destX, destY, subRect.width(), subRect.height());
static Graphics::Surface cachedSurface;
@ -141,14 +129,17 @@ private:
cachedSurface.copyRectToSurface(bgSurface, 0, 0, backgroundRect);
// copy cursor
convertRectToSurfaceWithKey(cachedSurface, srcSurface, deltaX, 0, subRect, key, srcPalette);
convertRectToSurfaceWithKey(cachedSurface, srcSurface, destX - backgroundRect.left, 0, subRect, key, srcPalette);
copyRectToSurface(
dstSurface,
cachedSurface,
dstSurface, dstBitsPerPixel, cachedSurface,
backgroundRect.left, backgroundRect.top,
Common::Rect(cachedSurface.w, cachedSurface.h));
}
Common::Rect alignRect(int x, int y, int w, int h) const override {
return Common::Rect(x & 0xfff0, y, (x + w + 15) & 0xfff0, y + h);
}
};
#endif

View File

@ -42,27 +42,51 @@
#define SCREEN_ACTIVE
#define MAX_HZ_SHAKE 16 // Falcon only
#define MAX_V_SHAKE 16
bool g_unalignedPitch = false;
// this is how screenptr should have been handled in TOS...
#undef screenptr
static volatile uintptr screenptr;
static void VblHandler() {
if (screenptr) {
#ifdef SCREEN_ACTIVE
if (hasSuperVidel()) {
// SuperVidel's XBIOS seems to switch to Super mode with BS8C...
VsetScreen(SCR_NOCHANGE, screenptr, SCR_NOCHANGE, SCR_NOCHANGE);
} else {
union { byte c[4]; uintptr p; } sptr;
sptr.p = screenptr;
static const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::createFormatCLUT8();
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);
*((volatile byte *)0xFFFF8201) = sptr.c[1];
*((volatile byte *)0xFFFF8203) = sptr.c[2];
*((volatile byte *)0xFFFF820D) = sptr.c[3];
static bool s_tt;
static int s_shakeXOffset;
static int s_shakeYOffset;
static Graphics::Surface *s_screenSurf;
static void VblHandler() {
if (s_screenSurf) {
#ifdef SCREEN_ACTIVE
const int bitsPerPixel = (s_screenSurf->format == PIXELFORMAT_RGB121 ? 4 : 8);
uintptr p = (uintptr)s_screenSurf->getBasePtr(0, MAX_V_SHAKE + s_shakeYOffset);
if (!s_tt) {
s_shakeXOffset = -s_shakeXOffset;
if (s_shakeXOffset >= 0) {
p += MAX_HZ_SHAKE;
*((volatile char *)0xFFFF8265) = s_shakeXOffset;
} else {
*((volatile char *)0xFFFF8265) = MAX_HZ_SHAKE + s_shakeXOffset;
}
// subtract 4 or 8 words if scrolling
*((volatile short *)0xFFFF820E) = s_shakeXOffset == 0
? (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2
: (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2 - bitsPerPixel;
}
union { byte c[4]; uintptr p; } sptr;
sptr.p = p;
*((volatile byte *)0xFFFF8201) = sptr.c[1];
*((volatile byte *)0xFFFF8203) = sptr.c[2];
*((volatile byte *)0xFFFF820D) = sptr.c[3];
#endif
screenptr = 0;
s_screenSurf = nullptr;
}
}
@ -132,10 +156,14 @@ AtariGraphicsManager::AtariGraphicsManager() {
vdo >>= 16;
_tt = (vdo == VDO_TT);
s_tt = _tt;
if (!_tt)
_vgaMonitor = VgetMonitor() == MON_VGA;
// no BDF scaling please
ConfMan.registerDefault("gui_disable_fixed_font_scaling", true);
// make the standard GUI renderer default (!DISABLE_FANCY_THEMES implies anti-aliased rendering in ThemeEngine.cpp)
// (and without DISABLE_FANCY_THEMES we can't use 640x480 themes)
const char *standardThemeEngineName = GUI::ThemeEngine::findModeConfigName(GUI::ThemeEngine::kGfxStandard);
@ -148,7 +176,6 @@ AtariGraphicsManager::AtariGraphicsManager() {
#ifndef DISABLE_FANCY_THEMES
// make "themes" the default theme path
ConfMan.registerDefault("themepath", "themes");
if (!ConfMan.hasKey("themepath"))
ConfMan.set("themepath", "themes");
#endif
@ -276,6 +303,11 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
if (_pendingState.width > getMaximumScreenWidth() || _pendingState.height > getMaximumScreenHeight())
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
if (_pendingState.width % 16 != 0 && !hasSuperVidel()) {
warning("Requested width not divisible by 16, please report");
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
}
if (error != OSystem::TransactionError::kTransactionSuccess) {
warning("endGFXTransaction failed: %02x", (int)error);
// all our errors are fatal but engine.cpp takes only this one seriously
@ -289,10 +321,12 @@ OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
_screen[FRONT_BUFFER]->reset(_pendingState.width, _pendingState.height, 8);
_screen[BACK_BUFFER1]->reset(_pendingState.width, _pendingState.height, 8);
_screen[BACK_BUFFER2]->reset(_pendingState.width, _pendingState.height, 8);
screenptr = 0;
_workScreen = _screen[_pendingState.mode <= GraphicsMode::SingleBuffering ? FRONT_BUFFER : BACK_BUFFER1];
s_screenSurf = nullptr;
s_shakeXOffset = 0;
s_shakeYOffset = 0;
// in case of resolution change from GUI
if (_oldWorkScreen)
_oldWorkScreen = _workScreen;
@ -364,17 +398,21 @@ void AtariGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, i
//debug("copyRectToScreen: %d, %d, %d(%d), %d", x, y, w, pitch, h);
Graphics::Surface &dstSurface = *lockScreen();
const Common::Rect rect = Common::Rect(x, y, x + w, y + h);
dstSurface.copyRectToSurface(buf, pitch, x, y, w, h);
const Common::Rect rect = alignRect(x, y, w, h);
_workScreen->addDirtyRect(dstSurface, rect);
if (_currentState.mode == GraphicsMode::TripleBuffering) {
_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
}
// no need to align so far...
dstSurface.copyRectToSurface(buf, pitch, x, y, w, h);
if (_currentState.mode == GraphicsMode::DirectRendering) {
// TODO: c2p with 16pix align
updateScreen();
} else if (_currentState.mode == GraphicsMode::TripleBuffering) {
_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
}
}
@ -395,16 +433,16 @@ void AtariGraphicsManager::unlockScreen() {
//debug("unlockScreen: %d x %d", _workScreen->surf.w, _workScreen->surf.h);
const Graphics::Surface &dstSurface = *lockScreen();
const Common::Rect rect = Common::Rect(dstSurface.w, dstSurface.h);
const Common::Rect rect = alignRect(0, 0, dstSurface.w, dstSurface.h);
_workScreen->addDirtyRect(dstSurface, rect);
if (_currentState.mode == GraphicsMode::DirectRendering)
updateScreen();
else if (_currentState.mode == GraphicsMode::TripleBuffering) {
if (_currentState.mode == GraphicsMode::TripleBuffering) {
_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
}
updateScreen();
}
void AtariGraphicsManager::fillScreen(uint32 col) {
@ -427,10 +465,16 @@ void AtariGraphicsManager::updateScreen() {
// FIXME: Some engines are too bound to linear surfaces that it is very
// hard to repair them. So instead of polluting the engine with
// Surface::init() & delete[] Surface::getPixels() just use this hack.
Common::String engineId = activeDomain->getValOrDefault("engineid");
if (engineId == "parallaction"
const Common::String engineId = activeDomain->getValOrDefault("engineid");
const Common::String gameId = activeDomain->getValOrDefault("gameid");
if (engineId == "hypno"
|| engineId == "mohawk"
|| engineId == "parallaction"
|| engineId == "private"
|| (engineId == "sci"
&& (gameId == "phantasmagoria" || gameId == "shivers"))
|| engineId == "sherlock"
|| engineId == "teenagent"
|| engineId == "tsage") {
g_unalignedPitch = true;
}
@ -446,29 +490,40 @@ void AtariGraphicsManager::updateScreen() {
if (isOverlayVisible()) {
assert(_workScreen == _screen[OVERLAY_BUFFER]);
screenUpdated = updateScreenInternal(_overlaySurface);
screenUpdated = updateScreenInternal<false>(_overlaySurface);
} else {
switch (_currentState.mode) {
case GraphicsMode::DirectRendering:
assert(_workScreen == _screen[FRONT_BUFFER]);
screenUpdated = updateScreenInternal(Graphics::Surface());
screenUpdated = updateScreenInternal<true>(Graphics::Surface());
break;
case GraphicsMode::SingleBuffering:
assert(_workScreen == _screen[FRONT_BUFFER]);
screenUpdated = updateScreenInternal(_chunkySurface);
screenUpdated = updateScreenInternal<false>(_chunkySurface);
break;
case GraphicsMode::TripleBuffering:
assert(_workScreen == _screen[BACK_BUFFER1]);
screenUpdated = updateScreenInternal(_chunkySurface);
screenUpdated = updateScreenInternal<false>(_chunkySurface);
break;
}
}
_workScreen->clearDirtyRects();
#ifdef SCREEN_ACTIVE
// first change video mode so we can modify video regs later
if (_pendingScreenChange & kPendingScreenChangeMode) {
if (_workScreen->rez != -1) {
// unfortunately this reinitializes VDI, too
Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
} else if (_workScreen->mode != -1) {
VsetMode(_workScreen->mode);
}
}
if (_pendingScreenChange & kPendingScreenChangeScreen) {
// can't call (V)SetScreen without Vsync()
screenptr = (uintptr)(isOverlayVisible() ? _workScreen->surf.getPixels() : _screen[FRONT_BUFFER]->surf.getPixels());
// calling (V)SetScreen without Vsync() is dangerous (at least on Falcon)
s_screenSurf = isOverlayVisible() ? &_screen[OVERLAY_BUFFER]->surf : &_screen[FRONT_BUFFER]->surf;
} else if (screenUpdated && !isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
// Triple buffer:
// - alternate BACK_BUFFER1 and BACK_BUFFER2
@ -498,7 +553,7 @@ void AtariGraphicsManager::updateScreen() {
_screen[BACK_BUFFER2] = tmp;
// queue BACK_BUFFER2 with the most recent frame content
screenptr = (uintptr)_screen[BACK_BUFFER2]->surf.getPixels();
s_screenSurf = &_screen[BACK_BUFFER2]->surf;
set_sysvar_to_short(vblsem, 1); // unlock vbl
@ -507,19 +562,6 @@ void AtariGraphicsManager::updateScreen() {
// FRONT_BUFFER is displayed and still contains previously finished frame
}
#ifdef SCREEN_ACTIVE
if (_pendingScreenChange & kPendingScreenChangeMode) {
// Avoid changing video registers in the middle of rendering...
Vsync();
if (_workScreen->rez != -1) {
// unfortunately this reinitializes VDI, too
Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
} else if (_workScreen->mode != -1) {
VsetMode(_workScreen->mode);
}
}
if (_pendingScreenChange & kPendingScreenChangePalette) {
if (_tt)
EsetPalette(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->tt);
@ -562,7 +604,18 @@ void AtariGraphicsManager::updateScreen() {
}
void AtariGraphicsManager::setShakePos(int shakeXOffset, int shakeYOffset) {
debug("setShakePos: %d, %d", shakeXOffset, shakeYOffset);
//debug("setShakePos: %d, %d", shakeXOffset, shakeYOffset);
if (_tt) {
// as TT can't horizontally shake anything, do it at least vertically
s_shakeYOffset = (shakeYOffset == 0 && shakeXOffset != 0) ? shakeXOffset : shakeYOffset;
} else {
s_shakeXOffset = shakeXOffset;
s_shakeYOffset = shakeYOffset;
}
_pendingScreenChange |= kPendingScreenChangeScreen;
updateScreen();
}
void AtariGraphicsManager::showOverlay(bool inGUI) {
@ -585,7 +638,7 @@ void AtariGraphicsManager::showOverlay(bool inGUI) {
_workScreen = _screen[OVERLAY_BUFFER];
// do not cache dirtyRects and oldCursorRect
int bitsPerPixel = getOverlayFormat().isCLUT8() || getOverlayFormat() == PIXELFORMAT_RGB332 ? 8 : 4;
const int bitsPerPixel = getBitsPerPixel(getOverlayFormat());
_workScreen->reset(getOverlayWidth(), getOverlayHeight(), bitsPerPixel);
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
@ -615,6 +668,14 @@ void AtariGraphicsManager::hideOverlay() {
updateScreen();
}
Graphics::PixelFormat AtariGraphicsManager::getOverlayFormat() const {
#ifndef DISABLE_FANCY_THEMES
return _tt ? PIXELFORMAT_RGB121 : PIXELFORMAT_RGB332;
#else
return PIXELFORMAT_RGB121;
#endif
}
void AtariGraphicsManager::clearOverlay() {
debug("clearOverlay");
@ -625,15 +686,15 @@ void AtariGraphicsManager::clearOverlay() {
const Graphics::Surface &sourceSurface =
_currentState.mode == GraphicsMode::DirectRendering ? *_screen[FRONT_BUFFER]->offsettedSurf : _chunkySurface;
bool upscale = _overlaySurface.w / sourceSurface.w >= 2 && _overlaySurface.h / sourceSurface.h >= 2;
const bool upscale = _overlaySurface.w / sourceSurface.w >= 2 && _overlaySurface.h / sourceSurface.h >= 2;
int w = upscale ? sourceSurface.w * 2 : sourceSurface.w;
int h = upscale ? sourceSurface.h * 2 : sourceSurface.h;
const int w = upscale ? sourceSurface.w * 2 : sourceSurface.w;
const int h = upscale ? sourceSurface.h * 2 : sourceSurface.h;
int hzOffset = (_overlaySurface.w - w) / 2;
int vOffset = (_overlaySurface.h - h) / 2;
const int hzOffset = (_overlaySurface.w - w) / 2;
const int vOffset = (_overlaySurface.h - h) / 2;
int pitch = hzOffset * 2 + (upscale ? _overlaySurface.pitch : 0);
const int pitch = hzOffset * 2 + (upscale ? _overlaySurface.pitch : 0);
// Transpose from game palette to RGB332/RGB121 (overlay palette)
const byte *src = (const byte*)sourceSurface.getPixels();
@ -641,11 +702,11 @@ void AtariGraphicsManager::clearOverlay() {
// for TT: 8/4/0 + (xLoss - 4) + xShift
static const int rShift = (_tt ? (8 - 4) : 0)
+ _overlaySurface.format.rLoss - _overlaySurface.format.rShift;
+ _overlaySurface.format.rLoss - _overlaySurface.format.rShift;
static const int gShift = (_tt ? (4 - 4) : 0)
+ _overlaySurface.format.gLoss - _overlaySurface.format.gShift;
+ _overlaySurface.format.gLoss - _overlaySurface.format.gShift;
static const int bShift = (_tt ? (0 - 4) : 0)
+ _overlaySurface.format.bLoss - _overlaySurface.format.bShift;
+ _overlaySurface.format.bLoss - _overlaySurface.format.bShift;
static const int rMask = _overlaySurface.format.rMax() << _overlaySurface.format.rShift;
static const int gMask = _overlaySurface.format.gMax() << _overlaySurface.format.gShift;
@ -717,8 +778,12 @@ void AtariGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
void AtariGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
//debug("copyRectToOverlay: %d, %d, %d(%d), %d", x, y, w, pitch, h);
_overlaySurface.copyRectToSurface(buf, pitch, x, y, w, h);
_screen[OVERLAY_BUFFER]->addDirtyRect(_overlaySurface, Common::Rect(x, y, x + w, y + h));
Graphics::Surface *dstSurface = lockScreen();
const Common::Rect rect = alignRect(x, y, w, h);
_workScreen->addDirtyRect(*dstSurface, rect);
dstSurface->copyRectToSurface(buf, pitch, x, y, w, h);
}
bool AtariGraphicsManager::showMouse(bool visible) {
@ -868,8 +933,13 @@ void AtariGraphicsManager::convertRectToSurfaceWithKey(Graphics::Surface &dstSur
}
}
int AtariGraphicsManager::getBitsPerPixel(const Graphics::PixelFormat &format) const {
return format == PIXELFORMAT_RGB121 ? 4 : 8;
}
template <bool directRendering> // hopefully compiler optimizes all the branching out
bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurface) {
//debug("updateScreenInternal: %d", (int)dirtyRects.size());
//debug("updateScreenInternal");
const DirtyRects &dirtyRects = _workScreen->dirtyRects;
Graphics::Surface *dstSurface = _workScreen->offsettedSurf;
@ -878,12 +948,8 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
bool &cursorVisibilityChanged = _workScreen->cursorVisibilityChanged;
Common::Rect &oldCursorRect = _workScreen->oldCursorRect;
const bool &fullRedraw = _workScreen->fullRedraw;
#ifndef DISABLE_FANCY_THEMES
const bool directRendering = !isOverlayVisible() && _currentState.mode == GraphicsMode::DirectRendering;
#else
// hopefully compiler optimizes all the branching out
const bool directRendering = false;
#endif
const int dstBitsPerPixel = getBitsPerPixel(dstSurface->format);
bool updated = false;
@ -906,7 +972,7 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
restoreCursor = !it->contains(oldCursorRect);
if (!directRendering) {
copyRectToSurface(*dstSurface, srcSurface, it->left, it->top, *it);
copyRectToSurface(*dstSurface, dstBitsPerPixel, srcSurface, it->left, it->top, *it);
updated = true;
} else if (!oldCursorRect.isEmpty()) {
const Common::Rect intersectingRect = it->findIntersectingRect(oldCursorRect);
@ -932,12 +998,12 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
oldCursorRect.right = (oldCursorRect.right + 15) & 0xfff0;
copyRectToSurface(
*dstSurface, srcSurface,
*dstSurface, dstBitsPerPixel, srcSurface,
oldCursorRect.left, oldCursorRect.top,
oldCursorRect);
} else {
copyRectToSurface(
*dstSurface, cachedCursorSurface,
*dstSurface, dstBitsPerPixel, cachedCursorSurface,
oldCursorRect.left, oldCursorRect.top,
Common::Rect(oldCursorRect.width(), oldCursorRect.height()));
}
@ -962,7 +1028,7 @@ bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurf
}
copyRectToSurfaceWithKey(
*dstSurface, _cursor.surface,
*dstSurface, dstBitsPerPixel, _cursor.surface,
_cursor.dstRect.left, _cursor.dstRect.top,
_cursor.srcRect,
_cursor.keycolor,
@ -985,7 +1051,12 @@ AtariGraphicsManager::Screen::Screen(AtariGraphicsManager *manager, int width, i
palette = palette_;
surf.init(width, height, width * format.bytesPerPixel, nullptr, format);
width += (_manager->_tt ? 0 : 2 * MAX_HZ_SHAKE);
height += 2 * MAX_V_SHAKE;
const int bitsPerPixel = _manager->getBitsPerPixel(format);
surf.init(width, height, width * bitsPerPixel / 8, nullptr, format);
void *pixelsUnaligned = allocFunc(sizeof(uintptr) + (surf.h * surf.pitch) + ALIGN - 1);
if (!pixelsUnaligned) {
@ -999,7 +1070,7 @@ AtariGraphicsManager::Screen::Screen(AtariGraphicsManager *manager, int width, i
memset(surf.getPixels(), 0, surf.h * surf.pitch);
_offsettedSurf.init(surf.w, surf.h, surf.pitch, surf.getPixels(), surf.format);
_offsettedSurf.init(surf.w, surf.h, surf.pitch, surf.getBasePtr(_manager->_tt ? 0 : MAX_HZ_SHAKE, MAX_V_SHAKE), surf.format);
}
AtariGraphicsManager::Screen::~Screen() {
@ -1018,18 +1089,18 @@ void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel
mode = -1;
// erase old screen
surf.fillRect(Common::Rect(surf.w, surf.h), 0);
_offsettedSurf.fillRect(Common::Rect(_offsettedSurf.w, _offsettedSurf.h), 0);
if (_manager->_tt) {
if (width <= 320 && height <= 240) {
surf.w = 320;
surf.h = 240;
surf.pitch = 2*surf.w;
surf.h = 240 + 2 * MAX_V_SHAKE;
surf.pitch = 2 * surf.w * bitsPerPixel / 8;
rez = kRezValueTTLow;
} else {
surf.w = 640;
surf.h = 480;
surf.pitch = surf.w;
surf.h = 480 + 2 * MAX_V_SHAKE;
surf.pitch = surf.w * bitsPerPixel / 8;
rez = kRezValueTTMid;
}
} else {
@ -1048,7 +1119,7 @@ void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel
mode |= COL80;
}
} else {
mode |= TV | BPS8;
mode |= TV | (bitsPerPixel == 4 ? BPS4 : BPS8);
if (width <= 320 && height <= 200) {
surf.w = 320;
@ -1069,20 +1140,21 @@ void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel
}
}
surf.pitch = surf.w;
surf.w += 2 * MAX_HZ_SHAKE;
surf.h += 2 * MAX_V_SHAKE;
surf.pitch = surf.w * bitsPerPixel / 8;
}
_offsettedSurf.init(width, height, surf.pitch, surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2), surf.format);
_offsettedSurf.init(
width, height, surf.pitch,
surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2),
surf.format);
}
void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurface, Common::Rect rect) {
void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect) {
if (fullRedraw)
return;
// align on 16px (i.e. 16 bytes -> optimize for C2P, MOVE16 or just 16-byte cache lines)
rect.left &= 0xfff0;
rect.right = (rect.right + 15) & 0xfff0;
if ((rect.width() == srcSurface.w && rect.height() == srcSurface.h)
|| dirtyRects.size() == 128) { // 320x200 can hold at most 250 16x16 rectangles
//debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
@ -1094,7 +1166,7 @@ void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurf
return;
}
dirtyRects.emplace(std::move(rect));
dirtyRects.insert(rect);
}
void AtariGraphicsManager::Cursor::update(const Graphics::Surface &screen, bool isModified) {

View File

@ -79,13 +79,7 @@ public:
void showOverlay(bool inGUI) override;
void hideOverlay() override;
bool isOverlayVisible() const override { return _overlayVisible; }
Graphics::PixelFormat getOverlayFormat() const override {
#ifndef DISABLE_FANCY_THEMES
return _tt ? PIXELFORMAT_RGB121 : PIXELFORMAT_RGB332;
#else
return PIXELFORMAT_RGB121;
#endif
}
Graphics::PixelFormat getOverlayFormat() const override;
void clearOverlay() override;
void grabOverlay(Graphics::Surface &surface) const override;
void copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) override;
@ -105,10 +99,6 @@ public:
Common::Keymap *getKeymap() const;
protected:
const Graphics::PixelFormat PIXELFORMAT_CLUT8 = Graphics::PixelFormat::createFormatCLUT8();
const Graphics::PixelFormat PIXELFORMAT_RGB332 = Graphics::PixelFormat(1, 3, 3, 2, 0, 5, 2, 0, 0);
const Graphics::PixelFormat PIXELFORMAT_RGB121 = Graphics::PixelFormat(1, 1, 2, 1, 0, 3, 1, 0, 0);
typedef void* (*AtariMemAlloc)(size_t bytes);
typedef void (*AtariMemFree)(void *ptr);
@ -157,8 +147,11 @@ private:
int16 getMaximumScreenHeight() const { return 480; }
int16 getMaximumScreenWidth() const { return _tt ? 320 : (_vgaMonitor ? 640 : 640*1.2); }
template <bool directRendering>
bool updateScreenInternal(const Graphics::Surface &srcSurface);
inline int getBitsPerPixel(const Graphics::PixelFormat &format) const;
virtual AtariMemAlloc getStRamAllocFunc() const {
return [](size_t bytes) { return (void*)Mxalloc(bytes, MX_STRAM); };
}
@ -166,17 +159,20 @@ private:
return [](void *ptr) { Mfree(ptr); };
}
virtual void copyRectToSurface(Graphics::Surface &dstSurface,
const Graphics::Surface &srcSurface, int destX, int destY,
virtual void copyRectToSurface(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
int destX, int destY,
const Common::Rect &subRect) const {
dstSurface.copyRectToSurface(srcSurface, destX, destY, subRect);
}
virtual void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
int destX, int destY, const Common::Rect &subRect, uint32 key,
virtual void copyRectToSurfaceWithKey(Graphics::Surface &dstSurface, int dstBitsPerPixel, const Graphics::Surface &srcSurface,
int destX, int destY,
const Common::Rect &subRect, uint32 key,
const Graphics::Surface &bgSurface, const byte srcPalette[256*3]) const {
convertRectToSurfaceWithKey(dstSurface, srcSurface, destX, destY, subRect, key, srcPalette);
}
virtual Common::Rect alignRect(int x, int y, int w, int h) const = 0;
void cursorPositionChanged() {
if (_overlayVisible) {
_screen[OVERLAY_BUFFER]->cursorPositionChanged = true;
@ -259,7 +255,8 @@ private:
~Screen();
void reset(int width, int height, int bitsPerPixel);
void addDirtyRect(const Graphics::Surface &srcSurface, Common::Rect rect);
// must be called before any rectangle drawing
void addDirtyRect(const Graphics::Surface &srcSurface, const Common::Rect &rect);
void clearDirtyRects() {
dirtyRects.clear();

View File

@ -66,7 +66,7 @@ AtariMixerManager::~AtariMixerManager() {
}
void AtariMixerManager::init() {
long cookie;
long cookie, stfa = 0;
bool useDevconnectReturnValue = Getcookie(C__SND, &cookie) == C_FOUND && (cookie & SND_EXT) != 0;
int clk;
@ -75,9 +75,62 @@ void AtariMixerManager::init() {
error("Sound system is locked");
// try XBIOS APIs which do not set SND_EXT in _SND
useDevconnectReturnValue |= (Getcookie(C_STFA, &cookie) == C_FOUND); // STFA
useDevconnectReturnValue |= (Getcookie(C_STFA, &stfa) == C_FOUND); // STFA
useDevconnectReturnValue |= (Getcookie(C_McSn, &cookie) == C_FOUND); // X-SOUND, MacSound
bool forceSoundCmd = false;
if (stfa) {
// see http://removers.free.fr/softs/stfa.php#STFA
struct STFA_control {
uint16 sound_enable;
uint16 sound_control;
uint16 sound_output;
uint32 sound_start;
uint32 sound_current;
uint32 sound_end;
uint16 version;
uint32 old_vbl;
uint32 old_timerA;
uint32 old_mfp_status;
uint32 stfa_vbl;
uint32 drivers_list;
uint32 play_stop;
uint16 timer_a_setting;
uint32 set_frequency;
uint16 frequency_treshold;
uint32 custom_freq_table;
int16 stfa_on_off;
uint32 new_drivers_list;
uint32 old_bit_2_of_cookie_snd;
uint32 it;
} __attribute__((packed));
STFA_control *stfaControl = (STFA_control *)stfa;
if (stfaControl->version < 0x0200) {
error("Your STFA version is too old, please upgrade to at least 2.00");
}
if (stfaControl->stfa_on_off == -1) {
// emulating 16-bit playback, force TT frequencies
enum {
MCH_ST = 0,
MCH_STE,
MCH_TT,
MCH_FALCON,
MCH_CLONE,
MCH_ARANYM
};
long mch = MCH_ST<<16;
Getcookie(C__MCH, &mch);
mch >>= 16;
if (mch == MCH_TT) {
debug("Forcing STE/TT compatible frequency");
forceSoundCmd = true;
}
}
}
// reset connection matrix (and other settings)
Sndstatus(SND_RESET);
@ -118,7 +171,7 @@ void AtariMixerManager::init() {
}
// first try to use Devconnect() with a Falcon prescaler
if (Devconnect(DMAPLAY, DAC, CLK25M, clk, NO_SHAKE) != 0) {
if (forceSoundCmd || Devconnect(DMAPLAY, DAC, CLK25M, clk, NO_SHAKE) != 0) {
// the return value is broken on Falcon
if (useDevconnectReturnValue) {
if (Devconnect(DMAPLAY, DAC, CLK25M, CLKOLD, NO_SHAKE) == 0) {

View File

@ -291,7 +291,9 @@ uint32 OSystem_Atari::getMillis(bool skipRecord) {
void OSystem_Atari::delayMillis(uint msecs) {
const uint32 threshold = getMillis() + msecs;
while (getMillis() < threshold);
while (getMillis() < threshold) {
update();
}
}
void OSystem_Atari::getTimeAndDate(TimeDate &td, bool skipRecord) const {
@ -391,7 +393,14 @@ Common::String OSystem_Atari::getDefaultConfigFileName() {
}
void OSystem_Atari::update() {
((DefaultTimerManager *)_timerManager)->checkTimers();
// FIXME: SCI MIDI calls delayMillis() from a timer leading to an infitite recursion loop here
const Common::ConfigManager::Domain *activeDomain = ConfMan.getActiveDomain();
if (!activeDomain || activeDomain->getValOrDefault("engineid") != "sci" || !_inTimer) {
_inTimer = true;
((DefaultTimerManager *)_timerManager)->checkTimers();
_inTimer = false;
}
if (_useNullMixer)
((NullMixerManager *)_mixerManager)->update();
else

View File

@ -54,6 +54,7 @@ private:
bool _videoInitialized = false;
bool _timerInitialized = false;
bool _useNullMixer = false;
bool _inTimer = false;
};
#endif

View File

@ -262,10 +262,10 @@ or "FM_medium_quality=true" into scummvm.ini if you want to experiment with a
better quality synthesis, otherwise the lowest quality will be used (applies
for MAME OPL only).
On the TT, in 95% of cases it makes sense to use ScummVM only if you own a
On the TT, in most cases it makes sense to use ScummVM only if you own a
native MIDI synthesizer (like mt32-pi: https://github.com/dwhinham/mt32-pi).
MIDI emulation is out of question and STFA is usually slow to mix samples, too
=> stick with games with MIDI sounds or at least don't install/enable STFA.
MIDI emulation is out of question and STFA takes a good chunk of CPU time for
downsampling to 8-bit resolution which could be utilized elsewhere.
CD music slows everything down
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -343,6 +343,9 @@ Known issues
fact that TT offers only 320x480 in 256 colours. Possibly fixable by a Timer
B interrupt.
- horizontal screen shaking doesn't work on TT because TT Shifter doesn't
support fine scrolling.
- tooltips in overlay are sometimes drawn with corrupted background.
- the talkie version of MI1 needs to be merged from two sources: first generate