mirror of https://github.com/stella-emu/stella.git
enhanced and optimized auto-phosphor
This commit is contained in:
parent
274490128f
commit
0aeffe0392
|
@ -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 |
|
@ -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 <always|auto|byrom></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 <byrom|always|autoon|auto></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>
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
colorloss = !colorloss;
|
||||
if(myTIA->enableColorLoss(colorloss))
|
||||
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);
|
||||
}
|
||||
else
|
||||
else {
|
||||
myOSystem.frameBuffer().showTextMessage(
|
||||
"PAL color-loss not available in non PAL modes");
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
// ... and apply potential setting changes to the TIA surface
|
||||
// Apply potential setting changes to the TIA surface
|
||||
myOSystem.frameBuffer().tiaSurface().updateSurfaceSettings();
|
||||
}
|
||||
ostringstream ss;
|
||||
|
||||
ss << "Interpolation " << (enabled ? "enabled" : "disabled");
|
||||
|
@ -623,9 +627,13 @@ void Console::changeSpeed(int direction)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Console::togglePhosphor()
|
||||
void Console::togglePhosphor(bool toggle)
|
||||
{
|
||||
const bool enable = !myOSystem.frameBuffer().tiaSurface().phosphorEnabled();
|
||||
bool enable = myOSystem.frameBuffer().tiaSurface().phosphorEnabled();
|
||||
|
||||
if(toggle)
|
||||
{
|
||||
enable = !enable;
|
||||
if(!enable)
|
||||
myProperties.set(PropType::Display_Phosphor, "NO");
|
||||
else
|
||||
|
@ -633,8 +641,8 @@ void Console::togglePhosphor()
|
|||
myOSystem.frameBuffer().tiaSurface().enablePhosphor(enable);
|
||||
|
||||
// disable auto-phosphor
|
||||
if(myTIA->autoPhosphorEnabled())
|
||||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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");
|
||||
|
||||
if(phosphor == "always")
|
||||
switch(phosphorMode)
|
||||
{
|
||||
p_blend = myOSystem.settings().getInt("tv.phosblend");
|
||||
case PhosphorHandler::Always:
|
||||
enable = true;
|
||||
}
|
||||
else if(phosphor == "auto")
|
||||
{
|
||||
p_blend = myOSystem.settings().getInt("tv.phosblend");
|
||||
p_blend = myOSystem.settings().getInt(PhosphorHandler::SETTING_BLEND);
|
||||
myOSystem.console().tia().enableAutoPhosphor(false);
|
||||
break;
|
||||
|
||||
case PhosphorHandler::Auto_on:
|
||||
case PhosphorHandler::Auto:
|
||||
enable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_blend = BSPF::stoi(myOSystem.console().properties().get(PropType::Display_PPBlend));
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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())
|
||||
// Only use new position if missile/ball are enabled
|
||||
if(myMissile0.isOn())
|
||||
myPosM0[y][myFlickerFrame] = myMissile0.getPosition();
|
||||
//else
|
||||
// myPosM0[y][myFlickerFrame] = myPosM0[y][otherFrame];
|
||||
//if(myMissile1.isOn())
|
||||
if(myMissile1.isOn())
|
||||
myPosM1[y][myFlickerFrame] = myMissile1.getPosition();
|
||||
//else
|
||||
// myPosM1[y][myFlickerFrame] = myPosM1[y][otherFrame];
|
||||
//if(myBall.isOn())
|
||||
if(myBall.isOn())
|
||||
myPosBL[y][myFlickerFrame] = myBall.getPosition();
|
||||
//else
|
||||
// myPosBL[y][myFlickerFrame] = myPosBL[y][otherFrame];
|
||||
|
||||
//cerr << int(myPlayer0.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
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
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,8 +896,8 @@ void VideoAudioDialog::saveConfig()
|
|||
NTSCFilter::saveConfig(settings);
|
||||
|
||||
// TV phosphor mode & blend
|
||||
settings.setValue("tv.phosphor", myTVPhosphor->getSelectedTag());
|
||||
settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off"
|
||||
settings.setValue(PhosphorHandler::SETTING_MODE, myTVPhosphor->getSelectedTag());
|
||||
settings.setValue(PhosphorHandler::SETTING_BLEND, myTVPhosLevel->getValueLabel() == "Off"
|
||||
? "0" : myTVPhosLevel->getValueLabel());
|
||||
|
||||
// TV scanline intensity & mask
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue