enhanced and optimized auto-phosphor

This commit is contained in:
thrust26 2024-01-25 12:24:18 +01:00
parent 274490128f
commit 0aeffe0392
18 changed files with 256 additions and 182 deletions

View File

@ -22,7 +22,7 @@
* Added option to start random ROM.
* Added option for automatic phosphor.
* Added automatically enabled phosphor modes.
* Enhanced Game Properties dialog for multigame ROMs.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1310,7 +1310,12 @@
<td>Cmd + 3</td>
</tr>
<tr>
<td><i>Increase</i> 'phosphor' enabling mode</td>
<td>Select <i>previous</i> 'phosphor' enabling mode</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td>Select <i>next</i> 'phosphor' enabling mode</td>
<td>Ctrl-Alt + P</td>
<td>Ctrl-Cmd + P</td>
</tr>
@ -1356,7 +1361,7 @@
</tr>
<tr>
<td colspan="3"><center><font size="-1">
Items marked as (*) will also switch to 'Custom' mode</font></center></td>
Items marked as (*) will also switch to 'Custom' TV effects mode</font></center></td>
</tr>
</table>
@ -2926,11 +2931,13 @@
</tr>
<tr>
<td><pre>-tv.phosphor &lt;always|auto|byrom&gt;</pre></td>
<td>Determine how phosphor mode is enabled. If 'always' or 'auto', then
the ROM properties entry is ignored, and phosphor mode is either always
turned on or automatic. Otherwise, the ROM properties determine whether
phosphor mode is used for each ROM.
<td><pre>-tv.phosphor &lt;byrom|always|autoon|auto&gt;</pre></td>
<td>Determine how phosphor mode is enabled. If 'byrom', then the ROM
properties determine whether phosphor mode is used for each ROM.
Else the ROM properties entry is ignored. If 'always', then the
phosphor mode is always enabled. If 'autoon', then the phosphor
mode is enabled automatically. If 'auto', then the phosphor mode
is enabled/disabled on automatically.
</td>
</tr>
@ -3826,8 +3833,8 @@
<tr><td>TV mode</td><td>Disable TV effects, or select TV preset</td><td>-tv.filter</td></tr>
<tr><td>Adjustable sliders</td><td>Set specific attribute in 'Custom' TV mode</td><td>-tv.sharpness<br/>-tv.resolution, etc.</td></tr>
<tr><td>Phosphor</td><td>Select mode for enabling phosphor</td><td>-tv.phosphor</td></tr>
<tr><td>Blend (phosphor)</td><td>Blend level to use in phosphor modes for all ROMs and automatic
(needs to be manually adjusted for your particular hardware)</td><td>-tv.phosblend</td></tr>
<tr><td>Blend (phosphor)</td><td>Blend level to use in phosphor modes for all ROMs and automatic ones
(level needs to be manually adjusted for your particular hardware)</td><td>-tv.phosblend</td></tr>
<tr><td>(Scanlines) Intensity</td><td>Sets scanlines black-level intensity.</br>
Note: No scanlines in 1x mode snapshots</td><td>-tv.scanlines</td></tr>
<tr><td>(Scanlines) Mask</td><td>Sets the scanlines mask.</br>

View File

@ -660,7 +660,6 @@ PhysicalKeyboardHandler::DefaultCommonMapping = {
{ Event::PhosphorDecrease, KBDK_4, KBDM_SHIFT | MOD3 },
{ Event::PhosphorIncrease, KBDK_4, MOD3 },
{ Event::TogglePhosphor, KBDK_P, MOD3 },
{ Event::PhosphorModeDecrease, KBDK_P, KBDM_SHIFT | KBDM_CTRL | MOD3 },
{ Event::PhosphorModeIncrease, KBDK_P, KBDM_CTRL | MOD3 },
{ Event::ScanlinesDecrease, KBDK_5, KBDM_SHIFT | MOD3 },
{ Event::ScanlinesIncrease, KBDK_5, MOD3 },

View File

@ -46,5 +46,30 @@ bool PhosphorHandler::initialize(bool enable, int blend)
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhosphorHandler::PhosphorMode PhosphorHandler::toPhosphorMode(string_view name)
{
if(name == VALUE_ALWAYS)
return PhosphorMode::Always;
if(name == VALUE_AUTO_ON)
return PhosphorMode::Auto_on;
if(name == VALUE_AUTO)
return PhosphorMode::Auto;
return PhosphorMode::ByRom;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string_view PhosphorHandler::toPhosphorName(PhosphorMode type)
{
static constexpr std::array<string_view, PhosphorMode::NumTypes> SETTING_NAMES = {
VALUE_BYROM, VALUE_ALWAYS, VALUE_AUTO_ON, VALUE_AUTO
};
return SETTING_NAMES[type];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhosphorHandler::PhosphorLUT PhosphorHandler::ourPhosphorLUT;

View File

@ -24,12 +24,34 @@
class PhosphorHandler
{
public:
// Phosphor settings names
static constexpr string_view SETTING_MODE = "tv.phosphor";
static constexpr string_view SETTING_BLEND = "tv.phosblend";
// Setting values of phosphor modes
static constexpr string_view VALUE_BYROM = "byrom";
static constexpr string_view VALUE_ALWAYS = "always";
static constexpr string_view VALUE_AUTO_ON = "autoon";
static constexpr string_view VALUE_AUTO = "auto";
enum PhosphorMode {
ByRom,
Always,
Auto_on,
Auto,
NumTypes
};
static constexpr string_view DEFAULT_BLEND = "50"; // align with myPhosphorPercent!
PhosphorHandler() = default;
bool initialize(bool enable, int blend);
bool phosphorEnabled() const { return myUsePhosphor; }
static PhosphorMode toPhosphorMode(string_view name);
static string_view toPhosphorName(PhosphorMode type);
/**
Used to calculate an averaged color pixel for the 'phosphor' effect.

View File

@ -534,19 +534,21 @@ void Console::setFormat(uInt32 format, bool force)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::toggleColorLoss(bool toggle)
{
const bool colorloss = !myTIA->colorLossEnabled();
if(myTIA->enableColorLoss(colorloss))
bool colorloss = myTIA->colorLossEnabled();
if(toggle)
{
myOSystem.settings().setValue(
myOSystem.settings().getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss", colorloss);
const string message = string("PAL color-loss ") +
(colorloss ? "enabled" : "disabled");
myOSystem.frameBuffer().showTextMessage(message);
colorloss = !colorloss;
if(myTIA->enableColorLoss(colorloss))
myOSystem.settings().setValue(
myOSystem.settings().getBool("dev.settings") ? "dev.colorloss" : "plr.colorloss", colorloss);
else {
myOSystem.frameBuffer().showTextMessage(
"PAL color-loss not available in non PAL modes");
return;
}
}
else
myOSystem.frameBuffer().showTextMessage(
"PAL color-loss not available in non PAL modes");
const string message = string("PAL color-loss ") + (colorloss ? "enabled" : "disabled");
myOSystem.frameBuffer().showTextMessage(message);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -563,12 +565,14 @@ void Console::toggleInter(bool toggle)
bool enabled = myOSystem.settings().getBool("tia.inter");
if(toggle)
{
enabled = !enabled;
myOSystem.settings().setValue("tia.inter", enabled);
myOSystem.settings().setValue("tia.inter", enabled);
// ... and apply potential setting changes to the TIA surface
myOSystem.frameBuffer().tiaSurface().updateSurfaceSettings();
// Apply potential setting changes to the TIA surface
myOSystem.frameBuffer().tiaSurface().updateSurfaceSettings();
}
ostringstream ss;
ss << "Interpolation " << (enabled ? "enabled" : "disabled");
@ -623,18 +627,22 @@ void Console::changeSpeed(int direction)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::togglePhosphor()
void Console::togglePhosphor(bool toggle)
{
const bool enable = !myOSystem.frameBuffer().tiaSurface().phosphorEnabled();
if(!enable)
myProperties.set(PropType::Display_Phosphor, "NO");
else
myProperties.set(PropType::Display_Phosphor, "YES");
myOSystem.frameBuffer().tiaSurface().enablePhosphor(enable);
bool enable = myOSystem.frameBuffer().tiaSurface().phosphorEnabled();
// disable auto-phosphor
if(myTIA->autoPhosphorEnabled())
if(toggle)
{
enable = !enable;
if(!enable)
myProperties.set(PropType::Display_Phosphor, "NO");
else
myProperties.set(PropType::Display_Phosphor, "YES");
myOSystem.frameBuffer().tiaSurface().enablePhosphor(enable);
// disable auto-phosphor
myTIA->enableAutoPhosphor(false);
}
ostringstream msg;
msg << "Phosphor effect " << (enable ? "enabled" : "disabled");
@ -644,42 +652,40 @@ void Console::togglePhosphor()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::cyclePhosphorMode(int direction)
{
static constexpr std::array<string_view, 3> MESSAGES = {
"by ROM", "always on", "auto-enabled"
static constexpr std::array<string_view, PhosphorHandler::NumTypes> MESSAGES = {
"by ROM", "always on", "auto-enabled", "auto-enabled/disabled"
};
static constexpr std::array<string_view, 3> VALUE = {
"byrom", "always", "auto"
};
const string value = myOSystem.settings().getString("tv.phosphor");
int mode;
for(mode = 2; mode > 0; --mode)
if(value == VALUE[mode])
break;
PhosphorHandler::PhosphorMode mode =
PhosphorHandler::toPhosphorMode(myOSystem.settings().getString(PhosphorHandler::SETTING_MODE));
if(direction)
{
mode = BSPF::clampw(mode + direction, 0, 2);
if(mode == 0)
mode = static_cast<PhosphorHandler::PhosphorMode>
(BSPF::clampw(mode + direction, 0, static_cast<int>(PhosphorHandler::NumTypes - 1)));
switch(mode)
{
myOSystem.frameBuffer().tiaSurface().enablePhosphor(
myProperties.get(PropType::Display_Phosphor) == "YES",
BSPF::stoi(myProperties.get(PropType::Display_PPBlend)));
myTIA->enableAutoPhosphor(false);
case PhosphorHandler::Always:
myOSystem.frameBuffer().tiaSurface().enablePhosphor(
true, myOSystem.settings().getInt(PhosphorHandler::SETTING_BLEND));
myTIA->enableAutoPhosphor(false);
break;
case PhosphorHandler::Auto_on:
case PhosphorHandler::Auto:
myOSystem.frameBuffer().tiaSurface().enablePhosphor(
false, myOSystem.settings().getInt(PhosphorHandler::SETTING_BLEND));
myTIA->enableAutoPhosphor(true, mode == PhosphorHandler::Auto_on);
break;
default: // PhosphorHandler::ByRom
myOSystem.frameBuffer().tiaSurface().enablePhosphor(
myProperties.get(PropType::Display_Phosphor) == "YES",
BSPF::stoi(myProperties.get(PropType::Display_PPBlend)));
myTIA->enableAutoPhosphor(false);
break;
}
else if(mode == 1)
{
myOSystem.frameBuffer().tiaSurface().enablePhosphor(
true, myOSystem.settings().getInt("tv.phosblend"));
myTIA->enableAutoPhosphor(false);
}
else
{
myOSystem.frameBuffer().tiaSurface().enablePhosphor(
false, myOSystem.settings().getInt("tv.phosblend"));
myTIA->enableAutoPhosphor(true);
}
myOSystem.settings().setValue("tv.phosphor", VALUE[mode]);
myOSystem.settings().setValue(PhosphorHandler::SETTING_MODE,
PhosphorHandler::toPhosphorName(mode));
}
ostringstream msg;
msg << "Phosphor mode " << MESSAGES[mode];
@ -1217,16 +1223,17 @@ void Console::changePaddleAxesRange(int direction)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Console::toggleAutoFire(bool toggle)
{
const bool enabled = myOSystem.settings().getBool("autofire");
bool enabled = myOSystem.settings().getBool("autofire");
if(toggle)
{
myOSystem.settings().setValue("autofire", !enabled);
Controller::setAutoFire(!enabled);
enabled = !enabled;
myOSystem.settings().setValue("autofire", enabled);
Controller::setAutoFire(enabled);
}
ostringstream ss;
ss << "Autofire " << (!enabled ? "enabled" : "disabled");
ss << "Autofire " << (enabled ? "enabled" : "disabled");
myOSystem.frameBuffer().showTextMessage(ss.str());
}

View File

@ -214,7 +214,7 @@ class Console : public Serializable, public ConsoleIO
public:
/**
Toggle between NTSC/PAL/SECAM (and variants) display format.
Switch between NTSC/PAL/SECAM (and variants) display format.
@param direction +1 indicates increase, -1 indicates decrease.
*/
@ -250,7 +250,7 @@ class Console : public Serializable, public ConsoleIO
/**
Toggles phosphor effect.
*/
void togglePhosphor();
void togglePhosphor(bool toggle = true);
/**
Toggles auto-phosphor.

View File

@ -134,7 +134,7 @@ void EventHandler::initialize()
// Default phosphor blend
Properties::setDefault(PropType::Display_PPBlend,
myOSystem.settings().getString("tv.phosblend"));
myOSystem.settings().getString(PhosphorHandler::SETTING_BLEND));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -326,26 +326,31 @@ FBInitStatus FrameBuffer::createDisplay(string_view title, BufferType type,
if(myOSystem.eventHandler().inTIAMode())
{
// Phosphor mode can be enabled either globally or per-ROM
int p_blend = 0;
bool enable = false;
const string phosphor = myOSystem.settings().getString("tv.phosphor");
int p_blend;
bool enable;
const int phosphorMode = PhosphorHandler::toPhosphorMode(
myOSystem.settings().getString(PhosphorHandler::SETTING_MODE));
myOSystem.console().tia().enableAutoPhosphor(phosphor == "auto");
switch(phosphorMode)
{
case PhosphorHandler::Always:
enable = true;
p_blend = myOSystem.settings().getInt(PhosphorHandler::SETTING_BLEND);
myOSystem.console().tia().enableAutoPhosphor(false);
break;
if(phosphor == "always")
{
p_blend = myOSystem.settings().getInt("tv.phosblend");
enable = true;
}
else if(phosphor == "auto")
{
p_blend = myOSystem.settings().getInt("tv.phosblend");
enable = false;
}
else
{
p_blend = BSPF::stoi(myOSystem.console().properties().get(PropType::Display_PPBlend));
enable = myOSystem.console().properties().get(PropType::Display_Phosphor) == "YES";
case PhosphorHandler::Auto_on:
case PhosphorHandler::Auto:
enable = false;
p_blend = myOSystem.settings().getInt(PhosphorHandler::SETTING_BLEND);
myOSystem.console().tia().enableAutoPhosphor(true, phosphorMode == PhosphorHandler::Auto_on);
break;
default: // PhosphorHandler::ByRom
enable = myOSystem.console().properties().get(PropType::Display_Phosphor) == "YES";
p_blend = BSPF::stoi(myOSystem.console().properties().get(PropType::Display_PPBlend));
myOSystem.console().tia().enableAutoPhosphor(false);
break;
}
myTIASurface->enablePhosphor(enable, p_blend);
}

View File

@ -99,8 +99,8 @@ Settings::Settings()
setPermanent("pal.gamma", "0.0");
// TV filtering options
setPermanent("tv.filter", "0");
setPermanent("tv.phosphor", "byrom");
setPermanent("tv.phosblend", "50");
setPermanent(PhosphorHandler::SETTING_MODE, PhosphorHandler::VALUE_BYROM);
setPermanent(PhosphorHandler::SETTING_BLEND, PhosphorHandler::DEFAULT_BLEND);
setPermanent("tv.scanlines", "0");
setPermanent("tv.scanmask", TIASurface::SETTING_STANDARD);
// TV options when using 'custom' mode
@ -361,11 +361,12 @@ void Settings::validate()
sort(s.begin(), s.end());
if(s != "bgopry") setValue("tia.dbgcolors", "roygpb");
s = getString("tv.phosphor");
if(s != "always" && s != "byrom" && s != "auto") setValue("tv.phosphor", "byrom");
if(PhosphorHandler::toPhosphorMode(getString(PhosphorHandler::SETTING_MODE)) == PhosphorHandler::ByRom)
setValue(PhosphorHandler::SETTING_MODE, PhosphorHandler::VALUE_BYROM);
i = getInt("tv.phosblend");
if(i < 0 || i > 100) setValue("tv.phosblend", "50");
i = getInt(PhosphorHandler::SETTING_BLEND);
if(i < 0 || i > 100)
setValue(PhosphorHandler::SETTING_BLEND, PhosphorHandler::DEFAULT_BLEND);
s = getString("tv.scanmask");
if(s != TIASurface::SETTING_STANDARD
@ -576,8 +577,8 @@ void Settings::usage()
<< " -tia.correct_aspect <1|0> Enable aspect ratio correct scaling\n\n"
<< " -tv.filter <0-5> Set TV effects off (0) or to specified mode\n"
<< " (1-5)\n"
<< " -tv.phosphor <always|auto|> When to use phosphor mode\n"
<< " byrom\n"
<< " -tv.phosphor <byrom|always|> When to use phosphor mode\n"
<< " autoon|auto\n"
<< " -tv.phosblend <0-100> Set default blend level in phosphor mode\n"
<< " -tv.scanlines <0-100> Set scanline intensity to percentage\n"
<< " (0 disables completely)\n"

View File

@ -23,6 +23,7 @@
#include "TIAConstants.hxx"
#include "AudioQueue.hxx"
#include "DispatchResult.hxx"
#include "PhosphorHandler.hxx"
#include "Base.hxx"
enum CollisionMask: uInt32 {
@ -187,11 +188,14 @@ void TIA::initialize()
myFrontBuffer.fill(0);
myFramebuffer.fill(0);
// Prepare variables for auto-phosphor
memset(&myPosP0, 0, sizeof(ObjectPos));
memset(&myPosP1, 0, sizeof(ObjectPos));
memset(&myPosM0, 0, sizeof(ObjectPos));
memset(&myPosM1, 0, sizeof(ObjectPos));
memset(&myPosBL, 0, sizeof(ObjectPos));
memset(&myPatPF, 0, sizeof(ObjectGfx));
myFrameEnd = 0;
applyDeveloperSettings();
@ -200,8 +204,12 @@ void TIA::initialize()
setFixedColorPalette(mySettings.getString("tia.dbgcolors"));
enableFixedColors(
mySettings.getBool(devSettings ? "dev.debugcolors" : "plr.debugcolors"));
myAutoPhosphorEnabled = mySettings.getString("tv.phosphor") == "auto";
// Auto-phosphor settings:
const string mode = mySettings.getString(PhosphorHandler::SETTING_MODE);
myAutoPhosphorAutoOn = mode == PhosphorHandler::VALUE_AUTO_ON;
myAutoPhosphorEnabled = myAutoPhosphorAutoOn || mode == PhosphorHandler::VALUE_AUTO;
myAutoPhosphorActive = false;
myFlickerCount = 0;
#ifdef DEBUGGER_SUPPORT
createAccessArrays();
@ -1407,6 +1415,9 @@ void TIA::onFrameComplete()
if(myAutoPhosphorEnabled)
{
// Calculate difference to previous frames (with some margin).
// If difference to latest frame is larger than to older frames, and this happens for
// multiple frames, enabled phosphor mode.
static constexpr int MIN_FLICKER_DELTA = 6;
static constexpr int MAX_FLICKER_DELTA = TIAConstants::H_PIXEL - MIN_FLICKER_DELTA;
static constexpr int MIN_DIFF = 4;
@ -1414,37 +1425,34 @@ void TIA::onFrameComplete()
int diffCount[FLICKER_FRAMES - 1];
//cerr << missingScanlines << ", " << myFrontBufferScanlines << " | ";
//cerr << missingScanlines << ", " << myFrameEnd << " | ";
//cerr << myFlickerFrame << ": ";
for(int frame = 0; frame < FLICKER_FRAMES - 1; ++frame)
{
int otherFrame = (myFlickerFrame + frame + 1) % FLICKER_FRAMES;
//cerr << otherFrame << " ";
// TODO:
// - differentiate fast movement and flicker
// - movement is directional
// - flicker goes back and forth
// - ignore disabled objects
diffCount[frame] = 0;
for(uInt32 y = 0; y < myFrontBufferScanlines; ++y)
const int otherFrame = (myFlickerFrame + frame + 1) % FLICKER_FRAMES;
int count = 0;
for(uInt32 y = 0; y <= myFrameEnd; ++y)
{
int delta;
delta = std::abs(myPosP0[y][myFlickerFrame] - myPosP0[y][otherFrame]);
if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA)
++diffCount[frame];
++count;
delta = std::abs(myPosP1[y][myFlickerFrame] - myPosP1[y][otherFrame]);
if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA)
++diffCount[frame];
++count;
delta = std::abs(myPosM0[y][myFlickerFrame] - myPosM0[y][otherFrame]);
if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA)
++diffCount[frame];
++count;
delta = std::abs(myPosM1[y][myFlickerFrame] - myPosM1[y][otherFrame]);
if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA)
++diffCount[frame];
++count;
delta = std::abs(myPosBL[y][myFlickerFrame] - myPosBL[y][otherFrame]);
if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA)
++diffCount[frame];
++count;
if(myPatPF[y][myFlickerFrame] != myPatPF[y][otherFrame])
++count;
}
diffCount[frame] = count;
}
//cerr << ": ";
//for(int i = 0; i < FLICKER_FRAMES - 1; ++i)
@ -1461,6 +1469,9 @@ void TIA::onFrameComplete()
{
myAutoPhosphorActive = true;
myPhosphorCallback(true);
// If auto-on, disable phosphor automatic (phosphor stays enabled)
if(myAutoPhosphorAutoOn)
myAutoPhosphorEnabled = false;
}
}
}
@ -1481,7 +1492,6 @@ void TIA::onFrameComplete()
if(--myFlickerFrame < 0)
myFlickerFrame = FLICKER_FRAMES - 1;
}
++myFramesSinceLastRender;
}
@ -1619,8 +1629,10 @@ void TIA::applyRsync()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FORCE_INLINE void TIA::nextLine()
{
bool cloned = false;
if (myLinesSinceChange >= 2) {
cloneLastLine();
cloned = true;
}
myHctr = 0;
@ -1642,48 +1654,43 @@ FORCE_INLINE void TIA::nextLine()
{
if(myFrameManager->getY() == 0)
flushLineCache();
// Save positions of objects for auto-phosphor
if(myAutoPhosphorEnabled)
{
// Test ROMs:
// - missing phosphor:
// x Princess Rescue: Lives display (~same x-position, different y-position)
// - QB: flicker sprite for multi color (same position, different shape and color)
// - Star Castle Arcade: vector font flicker (same position, different shape)
// - Omega Race: no phosphor enabled (flickers every 2nd frame)
// - Riddle of the Sphinx: shots (too small to be detected)
// - Missile Command: explosions
// - Yars' Revenge: shield, neutral zone (PF flicker)
// x Yars' Revenge: shield, neutral zone (PF flicker)
//
// - unneccassary phosphor:
// - Gas Hog: before game starts (odd invisible position changes)
// - Gas Hog: before game starts (odd invisible sprite position changes)
// x Turmoil: M1 rockets (gap between RESM1 and HMOVE?)
// x Fathom: seaweed (many sprites moving vertically)
// x FourPlay: game start (???)
// x Freeway: always (too many sprites?)
const uInt32 y = myFrameManager->getY();
//const int otherFrame = myFlickerFrame ^ 1;
//cerr << y << " ";
//if(myPlayer0.getGRPOld() != 0)
myPosP0[y][myFlickerFrame] = myPlayer0.getPosition();
//if(myPlayer1.getGRPOld() != 0)
myPosP1[y][myFlickerFrame] = myPlayer1.getPosition();
//if(myMissile0.isOn())
myPosM0[y][myFlickerFrame] = myMissile0.getPosition();
//else
// myPosM0[y][myFlickerFrame] = myPosM0[y][otherFrame];
//if(myMissile1.isOn())
myPosM1[y][myFlickerFrame] = myMissile1.getPosition();
//else
// myPosM1[y][myFlickerFrame] = myPosM1[y][otherFrame];
//if(myBall.isOn())
myPosBL[y][myFlickerFrame] = myBall.getPosition();
//else
// myPosBL[y][myFlickerFrame] = myPosBL[y][otherFrame];
//cerr << int(myPlayer0.getPosition()) << " ";
myPosP0[y][myFlickerFrame] = myPlayer0.getPosition();
myPosP1[y][myFlickerFrame] = myPlayer1.getPosition();
// Only use new position if missile/ball are enabled
if(myMissile0.isOn())
myPosM0[y][myFlickerFrame] = myMissile0.getPosition();
if(myMissile1.isOn())
myPosM1[y][myFlickerFrame] = myMissile1.getPosition();
if(myBall.isOn())
myPosBL[y][myFlickerFrame] = myBall.getPosition();
// Note: code checks only right side of playfield
myPatPF[y][myFlickerFrame] = (uInt32(registerValue(PF0))) << 16
| (uInt32(registerValue(PF1))) << 8 | uInt32(registerValue(PF2));
// Define end of frame for faster auto-phosphor calculation
if(!cloned)
myFrameEnd = y;
}
}
mySystem->m6502().clearHaltRequest();
}
@ -1705,6 +1712,7 @@ void TIA::cloneLastLine()
std::copy_n(myBackBuffer.begin() + (y - 1) * TIAConstants::H_PIXEL,
TIAConstants::H_PIXEL, myBackBuffer.begin() + y * TIAConstants::H_PIXEL);
// Save positions of objects for auto-phosphor
if(myAutoPhosphorEnabled)
{
myPosP0[y][myFlickerFrame] = myPosP0[y - 1][myFlickerFrame];
@ -1712,6 +1720,7 @@ void TIA::cloneLastLine()
myPosM0[y][myFlickerFrame] = myPosM0[y - 1][myFlickerFrame];
myPosM1[y][myFlickerFrame] = myPosM1[y - 1][myFlickerFrame];
myPosBL[y][myFlickerFrame] = myPosBL[y - 1][myFlickerFrame];
myPatPF[y][myFlickerFrame] = myPatPF[y - 1][myFlickerFrame];
}
}
}

View File

@ -301,29 +301,16 @@ class TIA : public Device
/**
Enables/disables auto-phosphor.
@param enabled Whether to enable or disable auto-phosphor mode
@param enabled Whether to use auto-phosphor mode
@param autoOn Whether to only ENABLE phosphor mode
*/
void enableAutoPhosphor(bool enabled)
void enableAutoPhosphor(bool enabled, bool autoOn = false)
{
myAutoPhosphorEnabled = enabled;
if(!enabled)
myAutoPhosphorActive = false;
myAutoPhosphorAutoOn = autoOn;
myAutoPhosphorActive = false;
}
/**
Answers whether auto-phosphor is enabled.
@return Auto-phosphor is enabled
*/
bool autoPhosphorEnabled() const { return myAutoPhosphorEnabled; }
/**
Answers whether auto-phosphor is active.
@return Auto-phosphor is acitve
*/
bool autoPhosphorActive() const { return myAutoPhosphorActive; }
/**
Answers the current color clock we've gotten to on this scanline.
@ -998,13 +985,17 @@ class TIA : public Device
/**
* Auto-phosphor detection variables.
*/
bool myAutoPhosphorEnabled{true};
bool myAutoPhosphorActive{true};
static constexpr int FLICKER_FRAMES = 1 + 4; // compare current frame with previous 4 frames
using ObjectPos = BSPF::array2D<uInt8, TIAConstants::frameBufferHeight, FLICKER_FRAMES>;
using ObjectGfx = BSPF::array2D<uInt32, TIAConstants::frameBufferHeight, FLICKER_FRAMES>;
bool myAutoPhosphorEnabled{false};
bool myAutoPhosphorAutoOn{false};
bool myAutoPhosphorActive{false};
ObjectPos myPosP0, myPosP1, myPosM0, myPosM1, myPosBL;
int myFlickerFrame{0};
int myFlickerCount{0};
ObjectGfx myPatPF;
int myFlickerFrame{0}, myFlickerCount{0};
uInt32 myFrameEnd{0};
onPhosphorCallback myPhosphorCallback;
#ifdef DEBUGGER_SUPPORT

View File

@ -160,7 +160,7 @@ void GameInfoDialog::addEmulationTab()
// Phosphor
ypos += lineHeight + VGAP;
myPhosphor = new CheckboxWidget(myTab, _font, HBORDER, ypos + 1,
"Phosphor (auto-enabled for all ROMs)", kPhosphorChanged);
"Phosphor (auto-enabled/disabled for all ROMs)", kPhosphorChanged);
myPhosphor->setToolTip(Event::TogglePhosphor);
wid.push_back(myPhosphor);
@ -804,13 +804,14 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props)
myFormatDetected->setLabel("");
// if phosphor is always enabled, disable game specific phosphor settings
const bool alwaysPhosphor = instance().settings().getString("tv.phosphor") == "always";
const bool autoPhosphor = instance().settings().getString("tv.phosphor") == "auto";
const string mode = instance().settings().getString(PhosphorHandler::SETTING_MODE);
const bool usePhosphor = props.get(PropType::Display_Phosphor) == "YES";
myPhosphor->setState(usePhosphor);
if (alwaysPhosphor)
if (mode == PhosphorHandler::VALUE_ALWAYS)
myPhosphor->setLabel("Phosphor (enabled for all ROMs");
else if (autoPhosphor)
else if (mode == PhosphorHandler::VALUE_AUTO)
myPhosphor->setLabel("Phosphor (auto-enabled/disabled for all ROMs)");
else if (mode == PhosphorHandler::VALUE_AUTO_ON)
myPhosphor->setLabel("Phosphor (auto-enabled for all ROMs)");
else
myPhosphor->setLabel("Phosphor");
@ -1195,7 +1196,8 @@ void GameInfoDialog::updateMultiCart()
myFormat->setEnabled(!isMulti);
// if phosphor is always enabled, disable game specific phosphor settings
const bool globalPhosphor = isMulti || instance().settings().getString("tv.phosphor") != "byrom";
const bool globalPhosphor = isMulti
|| instance().settings().getString(PhosphorHandler::SETTING_MODE) != PhosphorHandler::VALUE_BYROM;
myPhosphor->setEnabled(!globalPhosphor);
myPPBlend->setEnabled(!globalPhosphor && myPhosphor->getState());

View File

@ -235,7 +235,7 @@ void StellaSettingsDialog::loadConfig()
myTVScanIntense->setValue(valueToLevel(settings.getInt("tv.scanlines")));
// TV phosphor blend
myTVPhosLevel->setValue(valueToLevel(settings.getInt("tv.phosblend")));
myTVPhosLevel->setValue(valueToLevel(settings.getInt(PhosphorHandler::SETTING_BLEND)));
// TV overscan
myTVOverscan->setValue(settings.getInt("tia.fs_overscan"));
@ -274,10 +274,10 @@ void StellaSettingsDialog::saveConfig()
myTVMode->getSelectedTag().toString());
// TV phosphor mode
instance().settings().setValue("tv.phosphor",
myTVPhosLevel->getValue() > 0 ? "always" : "byrom");
instance().settings().setValue(PhosphorHandler::SETTING_MODE,
myTVPhosLevel->getValue() > 0 ? PhosphorHandler::VALUE_ALWAYS : PhosphorHandler::VALUE_BYROM);
// TV phosphor blend
instance().settings().setValue("tv.phosblend",
instance().settings().setValue(PhosphorHandler::SETTING_BLEND,
levelToValue(myTVPhosLevel->getValue()));
// TV scanline intensity and interpolation

View File

@ -381,11 +381,12 @@ void VideoAudioDialog::addTVEffectsTab()
// TV Phosphor effect
items.clear();
VarList::push_back(items, "by ROM", "byrom");
VarList::push_back(items, "always", "always");
VarList::push_back(items, "auto", "auto");
VarList::push_back(items, "by ROM", PhosphorHandler::VALUE_BYROM);
VarList::push_back(items, "always", PhosphorHandler::VALUE_ALWAYS);
VarList::push_back(items, "auto on", PhosphorHandler::VALUE_AUTO_ON);
VarList::push_back(items, "auto on/off", PhosphorHandler::VALUE_AUTO);
myTVPhosphor = new PopUpWidget(myTab, _font, xpos, ypos,
_font.getStringWidth("by ROM"), lineHeight,
_font.getStringWidth("auto on/off"), lineHeight,
items, "Phosphor ", 0, kPhosphorChanged);
myTVPhosphor->setToolTip(Event::PhosphorModeDecrease, Event::PhosphorModeIncrease);
wid.push_back(myTVPhosphor);
@ -760,8 +761,8 @@ void VideoAudioDialog::loadConfig()
loadTVAdjustables(NTSCFilter::Preset::CUSTOM);
// TV phosphor mode & blend
myTVPhosphor->setSelected(settings.getString("tv.phosphor"), "byrom");
myTVPhosLevel->setValue(settings.getInt("tv.phosblend"));
myTVPhosphor->setSelected(settings.getString(PhosphorHandler::SETTING_MODE), PhosphorHandler::VALUE_BYROM);
myTVPhosLevel->setValue(settings.getInt(PhosphorHandler::SETTING_BLEND));
handlePhosphorChange();
// TV scanline intensity & mask
@ -895,9 +896,9 @@ void VideoAudioDialog::saveConfig()
NTSCFilter::saveConfig(settings);
// TV phosphor mode & blend
settings.setValue("tv.phosphor", myTVPhosphor->getSelectedTag());
settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off"
? "0" : myTVPhosLevel->getValueLabel());
settings.setValue(PhosphorHandler::SETTING_MODE, myTVPhosphor->getSelectedTag());
settings.setValue(PhosphorHandler::SETTING_BLEND, myTVPhosLevel->getValueLabel() == "Off"
? "0" : myTVPhosLevel->getValueLabel());
// TV scanline intensity & mask
settings.setValue("tv.scanlines", myTVScanIntense->getValueLabel());
@ -1035,7 +1036,7 @@ void VideoAudioDialog::setDefaults()
myTVMode->setSelected("0", "0");
// TV phosphor mode & blend
myTVPhosphor->setSelected("byrom");
myTVPhosphor->setSelected(PhosphorHandler::VALUE_BYROM);
myTVPhosLevel->setValue(50);
// TV scanline intensity & mask
@ -1215,7 +1216,7 @@ void VideoAudioDialog::handleOverscanChange()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VideoAudioDialog::handlePhosphorChange()
{
myTVPhosLevel->setEnabled(myTVPhosphor->getSelectedTag() != "byrom");
myTVPhosLevel->setEnabled(myTVPhosphor->getSelectedTag() != PhosphorHandler::VALUE_BYROM);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -314,7 +314,7 @@ string Widget::getToolTip(const Common::Point& pos) const
const string hotkey2 = instance().eventHandler().keyHandler().getMappingDesc(
_toolTipEvent2, _toolTipMode);
if(hotkey2 != EmptyString)
if(hotkey != EmptyString && hotkey2 != EmptyString)
{
// Merge hotkeys if they only differ by "-Shift"
const string mod = "-Shift";
@ -333,6 +333,8 @@ string Widget::getToolTip(const Common::Point& pos) const
else
hotkey += ", " + hotkey2;
}
else
hotkey += hotkey2;
if(hotkey == EmptyString)
return _toolTipText;

View File

@ -98,6 +98,7 @@
<CharacterSet>MultiByte</CharacterSet>
<PlatformToolset>v143</PlatformToolset>
<EnableASAN>false</EnableASAN>
<WholeProgramOptimization>PGOptimize</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release-Sanitize|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
@ -486,6 +487,8 @@
<CompileAs>CompileAsCpp</CompileAs>
<AssemblerOutput>NoListing</AssemblerOutput>
<AssemblerListingLocation>$(IntDir)asm\windows\%(RelativeDir)</AssemblerListingLocation>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>Default</InlineFunctionExpansion>
</ClCompile>
<Link>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>