mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-27 05:32:45 +00:00
BACKENDS: ATARI: Implement aspect ratio correction
This commit is contained in:
parent
fc3995f351
commit
b510eff336
@ -78,7 +78,6 @@ static void VblHandler() {
|
||||
: (2 * MAX_HZ_SHAKE * bitsPerPixel / 8) / 2 - bitsPerPixel;
|
||||
}
|
||||
|
||||
|
||||
union { byte c[4]; uintptr p; } sptr;
|
||||
sptr.p = p;
|
||||
|
||||
@ -122,6 +121,34 @@ static uint32 UninstallVblHandler() {
|
||||
return uninstalled;
|
||||
}
|
||||
|
||||
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.
|
||||
Vsync();
|
||||
|
||||
if (hasSuperVidel()) {
|
||||
const int vOffset = ((480 - 400) / 2) * 2; // *2 because of half-lines
|
||||
|
||||
// VDB = VBE = VDB + paddding/2
|
||||
*((volatile uint16*)0xFFFF82A8) = *((volatile uint16*)0xFFFF82A6) = *((volatile uint16*)0xFFFF82A8) + vOffset;
|
||||
// VDE = VBB = VDE - padding/2
|
||||
*((volatile uint16*)0xFFFF82AA) = *((volatile uint16*)0xFFFF82A4) = *((volatile uint16*)0xFFFF82AA) - vOffset;
|
||||
} else {
|
||||
// 31500/60.1 = 524 raster lines
|
||||
// vft = 524 * 2 + 1 = 1049 half-lines
|
||||
// 480 visible lines = 960 half-lines
|
||||
// 1049 - 960 = 89 half-lines reserved for borders
|
||||
// we want 400 visible lines = 800 half-lines
|
||||
// vft = 800 + 89 = 889 half-lines in total ~ 70.1 Hz vertical frequency
|
||||
int16 vft = *((volatile int16*)0xFFFF82A2);
|
||||
int16 vss = *((volatile int16*)0xFFFF82AC); // vss = vft - vss_sync
|
||||
vss -= vft; // -vss_sync
|
||||
*((volatile int16*)0xFFFF82A2) = 889;
|
||||
*((volatile int16*)0xFFFF82AC) = 889 + vss;
|
||||
}
|
||||
}
|
||||
|
||||
static int s_oldRez = -1;
|
||||
static int s_oldMode = -1;
|
||||
static void *s_oldPhysbase = nullptr;
|
||||
@ -134,6 +161,8 @@ void AtariGraphicsShutdown() {
|
||||
} else if (s_oldMode != -1) {
|
||||
// prevent setting video base address just on the VDB line
|
||||
Vsync();
|
||||
if (hasSuperVidel())
|
||||
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
|
||||
VsetMode(s_oldMode);
|
||||
VsetScreen(SCR_NOCHANGE, s_oldPhysbase, SCR_NOCHANGE, SCR_NOCHANGE);
|
||||
}
|
||||
@ -227,7 +256,7 @@ bool AtariGraphicsManager::hasFeature(OSystem::Feature f) const {
|
||||
switch (f) {
|
||||
case OSystem::Feature::kFeatureAspectRatioCorrection:
|
||||
//debug("hasFeature(kFeatureAspectRatioCorrection): %d", !_vgaMonitor);
|
||||
return !_tt && !_vgaMonitor;
|
||||
return !_tt;
|
||||
case OSystem::Feature::kFeatureCursorPalette:
|
||||
// FIXME: pretend to have cursor palette at all times, this function
|
||||
// can get (and it is) called any time, before and after showOverlay()
|
||||
@ -249,7 +278,7 @@ void AtariGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
|
||||
_aspectRatioCorrection = enable;
|
||||
break;
|
||||
default:
|
||||
[[fallthrough]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,7 +603,7 @@ void AtariGraphicsManager::updateScreen() {
|
||||
_pendingScreenChange = kPendingScreenChangeNone;
|
||||
|
||||
if (_oldAspectRatioCorrection != _aspectRatioCorrection) {
|
||||
if (!isOverlayVisible()) {
|
||||
if (!isOverlayVisible() && _currentState.height == 200) {
|
||||
if (!_vgaMonitor) {
|
||||
short mode = VsetMode(VM_INQUIRE);
|
||||
if (_aspectRatioCorrection) {
|
||||
@ -587,15 +616,82 @@ void AtariGraphicsManager::updateScreen() {
|
||||
mode |= PAL;
|
||||
}
|
||||
VsetMode(mode);
|
||||
} else if (hasSuperVidel()) {
|
||||
// TODO: reduce to 200 scan lines?
|
||||
} else if (!_tt) {
|
||||
// TODO: increase vertical frequency?
|
||||
} else if (hasSuperVidel() || !_tt) {
|
||||
if (_aspectRatioCorrection) {
|
||||
for (int screenId : { FRONT_BUFFER, BACK_BUFFER1, BACK_BUFFER2 }) {
|
||||
Screen *screen = _screen[screenId];
|
||||
Graphics::Surface *offsettedSurf = screen->offsettedSurf;
|
||||
|
||||
// erase old screen
|
||||
offsettedSurf->fillRect(Common::Rect(offsettedSurf->w, offsettedSurf->h), 0);
|
||||
|
||||
// setup new screen
|
||||
screen->oldScreenSurfaceWidth = screen->surf.w;
|
||||
screen->oldScreenSurfaceHeight = screen->surf.h;
|
||||
screen->oldScreenSurfacePitch = screen->surf.pitch;
|
||||
screen->oldOffsettedSurfaceWidth = offsettedSurf->w;
|
||||
screen->oldOffsettedSurfaceHeight = offsettedSurf->h;
|
||||
|
||||
screen->surf.w = 320 + 2 * MAX_HZ_SHAKE;
|
||||
screen->surf.h = 200 + 2 * MAX_V_SHAKE;
|
||||
screen->surf.pitch = screen->surf.w;
|
||||
|
||||
offsettedSurf->init(
|
||||
320, 200, screen->surf.pitch,
|
||||
screen->surf.getBasePtr((screen->surf.w - 320) / 2, (screen->surf.h - 200) / 2),
|
||||
screen->surf.format);
|
||||
|
||||
screen->addDirtyRect(*lockScreen(), Common::Rect(offsettedSurf->w, offsettedSurf->h), _currentState.mode == GraphicsMode::DirectRendering);
|
||||
}
|
||||
|
||||
Supexec(shrinkVidelVisibleArea);
|
||||
} else {
|
||||
for (int screenId : { FRONT_BUFFER, BACK_BUFFER1, BACK_BUFFER2 }) {
|
||||
Screen *screen = _screen[screenId];
|
||||
Graphics::Surface *offsettedSurf = screen->offsettedSurf;
|
||||
|
||||
assert(screen->oldScreenSurfaceWidth != -1);
|
||||
assert(screen->oldScreenSurfaceHeight != -1);
|
||||
assert(screen->oldScreenSurfacePitch != -1);
|
||||
assert(screen->oldOffsettedSurfaceWidth != -1);
|
||||
assert(screen->oldOffsettedSurfaceHeight != -1);
|
||||
|
||||
// erase old screen
|
||||
offsettedSurf->fillRect(Common::Rect(offsettedSurf->w, offsettedSurf->h), 0);
|
||||
|
||||
// setup new screen
|
||||
screen->surf.w = screen->oldScreenSurfaceWidth;
|
||||
screen->surf.h = screen->oldScreenSurfaceHeight;
|
||||
screen->surf.pitch = screen->oldScreenSurfacePitch;
|
||||
|
||||
offsettedSurf->init(
|
||||
screen->oldOffsettedSurfaceWidth, screen->oldOffsettedSurfaceHeight, screen->surf.pitch,
|
||||
screen->surf.getBasePtr(
|
||||
(screen->surf.w - screen->oldOffsettedSurfaceWidth) / 2,
|
||||
(screen->surf.h - screen->oldOffsettedSurfaceHeight) / 2),
|
||||
screen->surf.format);
|
||||
|
||||
screen->oldScreenSurfaceWidth = -1;
|
||||
screen->oldScreenSurfaceHeight = -1;
|
||||
screen->oldScreenSurfacePitch = -1;
|
||||
screen->oldOffsettedSurfaceWidth = -1;
|
||||
screen->oldOffsettedSurfaceHeight = -1;
|
||||
|
||||
screen->addDirtyRect(*lockScreen(), Common::Rect(offsettedSurf->w, offsettedSurf->h), _currentState.mode == GraphicsMode::DirectRendering);
|
||||
}
|
||||
|
||||
if (hasSuperVidel())
|
||||
VsetMode(SVEXT | SVEXT_BASERES(0) | COL80 | BPS8C); // resync to proper 640x480
|
||||
VsetMode(_workScreen->mode);
|
||||
}
|
||||
} else {
|
||||
// TODO: some tricks with TT's 480 lines?
|
||||
}
|
||||
|
||||
_oldAspectRatioCorrection = _aspectRatioCorrection;
|
||||
|
||||
_pendingScreenChange |= kPendingScreenChangeScreen;
|
||||
updateScreen();
|
||||
} else {
|
||||
// ignore new value in overlay
|
||||
_aspectRatioCorrection = _oldAspectRatioCorrection;
|
||||
|
@ -294,6 +294,12 @@ private:
|
||||
int mode = -1;
|
||||
Graphics::Surface *const offsettedSurf = &_offsettedSurf;
|
||||
|
||||
int oldScreenSurfaceWidth = -1;
|
||||
int oldScreenSurfaceHeight = -1;
|
||||
int oldScreenSurfacePitch = -1;
|
||||
int oldOffsettedSurfaceWidth = -1;
|
||||
int oldOffsettedSurfaceHeight = -1;
|
||||
|
||||
private:
|
||||
static constexpr size_t ALIGN = 16; // 16 bytes
|
||||
|
||||
|
@ -213,6 +213,39 @@ means that if the SuperVidel is detected, it does the following:
|
||||
and makes a *huge* difference for 640x480 fullscreen updates.
|
||||
|
||||
|
||||
Aspect ratio correction
|
||||
-----------------------
|
||||
|
||||
Please refer to the official documentation about its usage. Normally ScummVM
|
||||
implements this functionality using yet another fullscreen transformation of
|
||||
320x200 surface into a 320x240 one (there is even a selection of algorithms
|
||||
for this task, varying in performance and quality).
|
||||
|
||||
Naturally, this would pose a terrible performance anchor on our backend so some
|
||||
cheating has been used:
|
||||
|
||||
- on RGB, the vertical refresh rate frequency is set to 60 Hz, creating an
|
||||
illusion of creating non-square pixels. Works best on CRT monitors.
|
||||
|
||||
- on VGA, the vertical refresh rate frequency is set to 70 Hz, with more or
|
||||
less the same effect as on RGB. Works best on CRT monitors.
|
||||
|
||||
- on SuperVidel, video output is modified in such way that the DVI/HDMI monitor
|
||||
receives a 320x200 image and if properly set/supported, it will automatically
|
||||
stretch the image to 320x240 (this is usually a setting called "picture
|
||||
expansion" or "picture stretch" -- make sure it isn't set to something like
|
||||
"1:1" or "dot by dot")
|
||||
|
||||
Yes, it's a hack. :) Owners of a CRT monitor can achieve the same effect by the
|
||||
analog knobs -- stretch and move the 320x200 picture unless black borders are
|
||||
no longer visible. This hack provides a more elegant and per-game
|
||||
functionality.
|
||||
|
||||
Realtime aspect ratio correction (CTRL+ALT+a) should be used with caution in
|
||||
Direct rendering mode because there's no way to refresh the screen. So if you
|
||||
change the setting and there isn't any game screen update coming, screen will
|
||||
stay black.
|
||||
|
||||
Audio mixing
|
||||
------------
|
||||
|
||||
@ -295,8 +328,9 @@ music (and therefore avoiding the expensive synthesis emulation) but beware, it
|
||||
doesn't affect CD (*.wav) playback at all! Same applies for speech and sfx.
|
||||
|
||||
The least amount of cycles is spent when:
|
||||
- "No music" (or keep it default and choose a native MIDI device) is set in the
|
||||
GUI options; this prevents MIDI sythesis of any kind
|
||||
- "No music" as "Preferred device": this prevents MIDI sythesis of any kind
|
||||
- "Subtitles" as "Text and speech": this prevents any sampled speech to be
|
||||
mixed
|
||||
- all external audio files are deleted (typically *.wav); that way the mixer
|
||||
wont have anything to mix. However beware, this is not allowed in every game!
|
||||
|
||||
@ -336,8 +370,6 @@ restricts features but also improves performance:
|
||||
Known issues
|
||||
------------
|
||||
|
||||
- aspect ratio correction works on RGB only (yet)
|
||||
|
||||
- adding a game in TOS and loading it in FreeMiNT (and vice versa) generates
|
||||
incompatible paths. Either use only one system or edit scummvm.ini and set
|
||||
there only relative paths (mintlib bug/limitation).
|
||||
@ -349,6 +381,9 @@ Known issues
|
||||
- horizontal screen shaking doesn't work on TT because TT Shifter doesn't
|
||||
support fine scrolling. However it is "emulated" via vertical shaking.
|
||||
|
||||
- aspect ratio correction has no effect on TT because is not possible to alter
|
||||
its vertical screen refresh frequency.
|
||||
|
||||
- tooltips in overlay are sometimes drawn with corrupted background.
|
||||
|
||||
- the talkie version of MI1 needs to be merged from two sources: first generate
|
||||
@ -373,11 +408,15 @@ Known issues
|
||||
- Engine GUI (for save/load/etc) does not support 8-bit screens
|
||||
- https://wiki.scummvm.org/index.php?title=Hugo
|
||||
|
||||
- Indy4 (the adventure) may have a bug in the screen when you K.O. the bouncer.
|
||||
I was able to get a freeze when he fell to the ground but currently I am
|
||||
unable to reproduce it. It may be related to the intensive mouse clicking
|
||||
during that scene so feel free to use keypad for the fight and report whether
|
||||
it has improved the situation.
|
||||
|
||||
Future plans
|
||||
------------
|
||||
|
||||
- aspect ratio correction for TT/VGA/SuperVidel
|
||||
|
||||
- unified file paths in scummvm.ini
|
||||
|
||||
- DSP-based sample mixer (WAV, FLAC, MP2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user