mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-12 14:09:28 +00:00
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:
parent
f85fde7707
commit
d9a5de0fe6
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -54,6 +54,7 @@ private:
|
||||
bool _videoInitialized = false;
|
||||
bool _timerInitialized = false;
|
||||
bool _useNullMixer = false;
|
||||
bool _inTimer = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user