From 580b86904b35e3bcd52b613fe36f41cbf94934c0 Mon Sep 17 00:00:00 2001 From: thrust26 Date: Wed, 24 Jan 2024 11:24:21 +0100 Subject: [PATCH] added auto-phosphor (resolves #1009) --- Changes.txt | 2 + docs/graphics/options_video_tv.png | Bin 4141 -> 4109 bytes docs/index.html | 19 ++-- src/common/PKeyboardHandler.cxx | 2 + src/common/PhosphorHandler.cxx | 23 ++--- src/common/PhosphorHandler.hxx | 1 + src/common/jsonDefinitions.hxx | 2 + src/emucore/Console.cxx | 77 ++++++++++++++-- src/emucore/Console.hxx | 5 + src/emucore/Event.hxx | 3 +- src/emucore/EventHandler.cxx | 19 ++++ src/emucore/EventHandler.hxx | 2 +- src/emucore/FrameBuffer.cxx | 12 ++- src/emucore/GlobalKeyHandler.cxx | 1 + src/emucore/GlobalKeyHandler.hxx | 1 + src/emucore/ProfilingRunner.cxx | 5 +- src/emucore/Settings.cxx | 5 +- src/emucore/tia/TIA.cxx | 143 ++++++++++++++++++++++++++++- src/emucore/tia/TIA.hxx | 44 ++++++++- src/gui/GameInfoDialog.cxx | 16 ++-- src/gui/VideoAudioDialog.cxx | 18 ++-- src/gui/VideoAudioDialog.hxx | 2 +- 22 files changed, 350 insertions(+), 52 deletions(-) diff --git a/Changes.txt b/Changes.txt index 0b3081d9c..9fa07beaf 100644 --- a/Changes.txt +++ b/Changes.txt @@ -22,6 +22,8 @@ * Added option to start random ROM. + * Added option for automatic phosphor. + * Enhanced Game Properties dialog for multigame ROMs. * Added 2nd UI theme and hotkey for toggling UI theme. diff --git a/docs/graphics/options_video_tv.png b/docs/graphics/options_video_tv.png index 4cdb07c82300692536b7ab5a8951b4abfc5c8e22..a2a4fe98a6ee73cbf20ad42a3140fae7bd3c8a53 100644 GIT binary patch delta 3998 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 delta 4023 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 Q@L$JaY+$Bep^J$47x?VIEC2ui diff --git a/docs/index.html b/docs/index.html index d5f9978d7..b61627da9 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1309,6 +1309,11 @@ Alt + 3 Cmd + 3 + + Increase 'phosphor' enabling mode + Ctrl-Alt + P + Ctrl-Cmd + P + Toggle 'phosphor' mode Alt + P @@ -2921,11 +2926,11 @@ -
-tv.phosphor <always|byrom>
- Determine how phosphor mode is enabled. If 'always', then the - ROM properties entry is ignored, and phosphor mode is always turned - on. Otherwise, the ROM properties determine whether phosphor mode - is used for each ROM. +
-tv.phosphor <always|auto|byrom>
+ 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. @@ -3820,8 +3825,8 @@ ItemBrief descriptionFor more information,
see Command Line TV modeDisable TV effects, or select TV preset-tv.filter Adjustable slidersSet specific attribute in 'Custom' TV mode-tv.sharpness
-tv.resolution, etc. - Phosphor for all ROMsEnable phosphor mode for all ROMs-tv.phosphor - Blend (phosphor)Blend level to use in phosphor mode for all ROMs + PhosphorSelect mode for enabling phosphor-tv.phosphor + Blend (phosphor)Blend level to use in phosphor modes for all ROMs and automatic (needs to be manually adjusted for your particular hardware)-tv.phosblend (Scanlines) IntensitySets scanlines black-level intensity.
Note: No scanlines in 1x mode snapshots-tv.scanlines diff --git a/src/common/PKeyboardHandler.cxx b/src/common/PKeyboardHandler.cxx index 74e10c0e1..b75d76fa0 100644 --- a/src/common/PKeyboardHandler.cxx +++ b/src/common/PKeyboardHandler.cxx @@ -660,6 +660,8 @@ 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 }, { Event::PreviousScanlineMask, KBDK_6, KBDM_SHIFT | MOD3 }, diff --git a/src/common/PhosphorHandler.cxx b/src/common/PhosphorHandler.cxx index db31feb38..9b16c33f7 100644 --- a/src/common/PhosphorHandler.cxx +++ b/src/common/PhosphorHandler.cxx @@ -24,23 +24,24 @@ bool PhosphorHandler::initialize(bool enable, int blend) return false; myUsePhosphor = enable; - if(blend >= 0 && blend <= 100) - myPhosphorPercent = blend / 100.F; - - // Used to calculate an averaged color for the 'phosphor' effect - const auto getPhosphor = [&] (const uInt8 c1, uInt8 c2) -> uInt8 { - // Use maximum of current and decayed previous values - c2 = static_cast(c2 * myPhosphorPercent); - if(c1 > c2) return c1; // raise (assumed immediate) - else return c2; // decay - }; // Precalculate the average colors for the 'phosphor' effect - if(myUsePhosphor) + if((myUsePhosphor && blend != -1 && blend / 100.F != myPhosphorPercent) || !myLUTInitialized) { + if(blend >= 0 && blend <= 100) + myPhosphorPercent = blend / 100.F; + + // Used to calculate an averaged color for the 'phosphor' effect + const auto getPhosphor = [&] (const uInt8 c1, uInt8 c2) -> uInt8 { + // Use maximum of current and decayed previous values + c2 = static_cast(c2 * myPhosphorPercent); + if(c1 > c2) return c1; // raise (assumed immediate) + else return c2; // decay + }; for(int c = 255; c >= 0; --c) for(int p = 255; p >= 0; --p) ourPhosphorLUT[c][p] = getPhosphor(static_cast(c), static_cast(p)); + myLUTInitialized = true; } return true; } diff --git a/src/common/PhosphorHandler.hxx b/src/common/PhosphorHandler.hxx index 7492c28d3..fbe3feb17 100644 --- a/src/common/PhosphorHandler.hxx +++ b/src/common/PhosphorHandler.hxx @@ -58,6 +58,7 @@ class PhosphorHandler // Amount to blend when using phosphor effect float myPhosphorPercent{0.50F}; + bool myLUTInitialized{false}; // Precalculated averaged phosphor colors using PhosphorLUT = BSPF::array2D; diff --git a/src/common/jsonDefinitions.hxx b/src/common/jsonDefinitions.hxx index 5be679942..e4c98991b 100644 --- a/src/common/jsonDefinitions.hxx +++ b/src/common/jsonDefinitions.hxx @@ -360,6 +360,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Event::Type, { {Event::PhosphorDecrease, "PhosphorDecrease"}, {Event::PhosphorIncrease, "PhosphorIncrease"}, {Event::TogglePhosphor, "TogglePhosphor"}, + {Event::PhosphorModeDecrease, "PhosphorModeDecrease"}, + {Event::PhosphorModeIncrease, "PhosphorModeIncrease"}, {Event::ToggleDeveloperSet, "ToggleDeveloperSet"}, {Event::ToggleInter, "ToggleInter"}, {Event::JitterSenseDecrease, "JitterSenseDecrease"}, diff --git a/src/emucore/Console.cxx b/src/emucore/Console.cxx index a28657aae..ecc3e2d50 100644 --- a/src/emucore/Console.cxx +++ b/src/emucore/Console.cxx @@ -123,7 +123,17 @@ Console::Console(OSystem& osystem, unique_ptr& cart, // Create subsystems for the console my6502 = make_unique(myOSystem.settings()); myRiot = make_unique(*this, myOSystem.settings()); - myTIA = make_unique(*this, [this]() { return timing(); }, myOSystem.settings()); + + const TIA::onPhosphorCallback callback = [&frameBuffer = this->myOSystem.frameBuffer()](bool enable) + { + frameBuffer.tiaSurface().enablePhosphor(enable); +#if DEBUG_BUILD + ostringstream msg; + msg << "Phosphor effect automatically " << (enable ? "enabled" : "disabled"); + frameBuffer.showTextMessage(msg.str()); +#endif + }; + myTIA = make_unique(*this, [this]() { return timing(); }, myOSystem.settings(), callback); myFrameManager = make_unique(); mySwitches = make_unique(myEvent, myProperties, myOSystem.settings()); @@ -615,18 +625,65 @@ void Console::changeSpeed(int direction) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Console::togglePhosphor() { - if(myOSystem.frameBuffer().tiaSurface().phosphorEnabled()) - { + const bool enable = !myOSystem.frameBuffer().tiaSurface().phosphorEnabled(); + if(!enable) myProperties.set(PropType::Display_Phosphor, "NO"); - myOSystem.frameBuffer().tiaSurface().enablePhosphor(false); - myOSystem.frameBuffer().showTextMessage("Phosphor effect disabled"); - } else - { myProperties.set(PropType::Display_Phosphor, "YES"); - myOSystem.frameBuffer().tiaSurface().enablePhosphor(true); - myOSystem.frameBuffer().showTextMessage("Phosphor effect enabled"); + myOSystem.frameBuffer().tiaSurface().enablePhosphor(enable); + + // disable auto-phosphor + if(myTIA->autoPhosphorEnabled()) + myTIA->enableAutoPhosphor(false); + + ostringstream msg; + msg << "Phosphor effect " << (enable ? "enabled" : "disabled"); + myOSystem.frameBuffer().showTextMessage(msg.str()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Console::cyclePhosphorMode(int direction) +{ + static constexpr std::array MESSAGES = { + "by ROM", "always on", "auto-enabled" + }; + static constexpr std::array 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; + + if(direction) + { + mode = BSPF::clampw(mode + direction, 0, 2); + if(mode == 0) + { + myOSystem.frameBuffer().tiaSurface().enablePhosphor( + myProperties.get(PropType::Display_Phosphor) == "YES", + BSPF::stoi(myProperties.get(PropType::Display_PPBlend))); + myTIA->enableAutoPhosphor(false); + } + 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]); } + ostringstream msg; + msg << "Phosphor mode " << MESSAGES[mode]; + myOSystem.frameBuffer().showTextMessage(msg.str()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -693,7 +750,7 @@ void Console::initializeAudio() .updateAudioQueueExtraFragments(myAudioSettings.bufferSize()) .updateAudioQueueHeadroom(myAudioSettings.headroom()) .updateSpeedFactor(myOSystem.settings().getBool("turbo") - ? 20.0F + ? 50.0F : myOSystem.settings().getFloat("speed")); createAudioQueue(); diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index 6784805af..7ab8af4d8 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -252,6 +252,11 @@ class Console : public Serializable, public ConsoleIO */ void togglePhosphor(); + /** + Toggles auto-phosphor. + */ + void cyclePhosphorMode(int direction = +1); + /** Change the "Display.PPBlend" variable. diff --git a/src/emucore/Event.hxx b/src/emucore/Event.hxx index bd8573443..18d8ef939 100644 --- a/src/emucore/Event.hxx +++ b/src/emucore/Event.hxx @@ -115,7 +115,8 @@ class Event PreviousAttribute, NextAttribute, DecreaseAttribute, IncreaseAttribute, ScanlinesDecrease, ScanlinesIncrease, PreviousScanlineMask, NextScanlineMask, - PhosphorDecrease, PhosphorIncrease, TogglePhosphor, ToggleInter, + PhosphorDecrease, PhosphorIncrease, TogglePhosphor, + PhosphorModeDecrease, PhosphorModeIncrease, ToggleInter, ToggleDeveloperSet, JitterRecDecrease, JitterRecIncrease, JitterSenseDecrease, JitterSenseIncrease, ToggleJitter, diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 7a32e9478..d1198a2f7 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -738,6 +738,22 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated) } return; + case Event::PhosphorModeDecrease: + if(pressed && !repeated) + { + myOSystem.console().cyclePhosphorMode(-1); + myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::PHOSPHOR_MODE); + } + return; + + case Event::PhosphorModeIncrease: + if(pressed && !repeated) + { + myOSystem.console().cyclePhosphorMode(+1); + myGlobalKeyHandler->setSetting(GlobalKeyHandler::Setting::PHOSPHOR_MODE); + } + return; + case Event::ScanlinesDecrease: if(pressed) { @@ -3004,6 +3020,8 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { { { Event::IncreaseAttribute, "Increase selected 'Custom' attribute" }, // Other TV effects { Event::TogglePhosphor, "Toggle 'phosphor' effect" }, + { Event::PhosphorModeDecrease, "Decrease 'phosphor' enabling mode" }, + { Event::PhosphorModeIncrease, "Increase 'phosphor' enabling mode" }, { Event::PhosphorDecrease, "Decrease 'phosphor' blend" }, { Event::PhosphorIncrease, "Increase 'phosphor' blend" }, { Event::ScanlinesDecrease, "Decrease scanlines" }, @@ -3181,6 +3199,7 @@ const Event::EventSet EventHandler::AudioVideoEvents = { Event::PreviousVideoMode, Event::NextVideoMode, Event::PreviousAttribute, Event::NextAttribute, Event::DecreaseAttribute, Event::IncreaseAttribute, Event::PhosphorDecrease, Event::PhosphorIncrease, Event::TogglePhosphor, + Event::PhosphorModeDecrease, Event::PhosphorModeIncrease, Event::ScanlinesDecrease, Event::ScanlinesIncrease, Event::PreviousScanlineMask, Event::NextScanlineMask, Event::ToggleInter, diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index c99dafb89..34beeb36b 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -540,7 +540,7 @@ class EventHandler #else REFRESH_SIZE = 0, #endif - EMUL_ACTIONLIST_SIZE = 233 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE, + EMUL_ACTIONLIST_SIZE = 235 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE, MENU_ACTIONLIST_SIZE = 20 ; diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index 3bbf1f434..95e27f0cd 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -328,12 +328,20 @@ FBInitStatus FrameBuffer::createDisplay(string_view title, BufferType type, // 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"); - if(myOSystem.settings().getString("tv.phosphor") == "always") + myOSystem.console().tia().enableAutoPhosphor(phosphor == "auto"); + + 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)); @@ -719,7 +727,7 @@ void FrameBuffer::drawFrameStats(float framesPerSecond) << "fps @ " << std::fixed << std::setprecision(0) << 100 * (myOSystem.settings().getBool("turbo") - ? 20.0F + ? 50.0F : myOSystem.settings().getFloat("speed")) << "% speed"; diff --git a/src/emucore/GlobalKeyHandler.cxx b/src/emucore/GlobalKeyHandler.cxx index 6c6874192..67355c43f 100644 --- a/src/emucore/GlobalKeyHandler.cxx +++ b/src/emucore/GlobalKeyHandler.cxx @@ -390,6 +390,7 @@ GlobalKeyHandler::SettingData GlobalKeyHandler::getSettingData(const Setting set {Setting::NTSC_BLEEDING, {true, std::bind(&TIASurface::changeNTSCAdjustable, &myOSystem.frameBuffer().tiaSurface(), static_cast(NTSCFilter::Adjustables::BLEEDING), _1)}}, // Other TV effects adjustables + {Setting::PHOSPHOR_MODE, {true, std::bind(&Console::cyclePhosphorMode, &myOSystem.console(), _1)}}, {Setting::PHOSPHOR, {true, std::bind(&Console::changePhosphor, &myOSystem.console(), _1)}}, {Setting::SCANLINES, {true, std::bind(&TIASurface::changeScanlineIntensity, &myOSystem.frameBuffer().tiaSurface(), _1)}}, {Setting::SCANLINE_MASK, {false, std::bind(&TIASurface::cycleScanlineMask, &myOSystem.frameBuffer().tiaSurface(), _1)}}, diff --git a/src/emucore/GlobalKeyHandler.hxx b/src/emucore/GlobalKeyHandler.hxx index aef8c3b85..61ba7f91c 100644 --- a/src/emucore/GlobalKeyHandler.hxx +++ b/src/emucore/GlobalKeyHandler.hxx @@ -69,6 +69,7 @@ class GlobalKeyHandler NTSC_FRINGING, NTSC_BLEEDING, // Other TV effects adjustables + PHOSPHOR_MODE, PHOSPHOR, SCANLINES, SCANLINE_MASK, diff --git a/src/emucore/ProfilingRunner.cxx b/src/emucore/ProfilingRunner.cxx index 0c7229295..0adc1723f 100644 --- a/src/emucore/ProfilingRunner.cxx +++ b/src/emucore/ProfilingRunner.cxx @@ -125,7 +125,10 @@ bool ProfilingRunner::runOne(const ProfilingRun& run) M6502 cpu(mySettings); M6532 riot(consoleIO, mySettings); - TIA tia(consoleIO, []() { return ConsoleTiming::ntsc; }, mySettings); + + const TIA::onPhosphorCallback callback = [] (bool enable) {}; + + TIA tia(consoleIO, []() { return ConsoleTiming::ntsc; }, mySettings, callback); System system(rng, cpu, riot, tia, *cartridge); consoleIO.myLeftControl = make_unique(Controller::Jack::Left, event, system); diff --git a/src/emucore/Settings.cxx b/src/emucore/Settings.cxx index 6b10dc991..0aeba5ed9 100644 --- a/src/emucore/Settings.cxx +++ b/src/emucore/Settings.cxx @@ -362,7 +362,7 @@ void Settings::validate() if(s != "bgopry") setValue("tia.dbgcolors", "roygpb"); s = getString("tv.phosphor"); - if(s != "always" && s != "byrom") setValue("tv.phosphor", "byrom"); + if(s != "always" && s != "byrom" && s != "auto") setValue("tv.phosphor", "byrom"); i = getInt("tv.phosblend"); if(i < 0 || i > 100) setValue("tv.phosblend", "50"); @@ -576,7 +576,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 When to use phosphor mode\n" + << " -tv.phosphor When to use phosphor mode\n" + << " byrom\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" diff --git a/src/emucore/tia/TIA.cxx b/src/emucore/tia/TIA.cxx index 78dcba927..795e10e0b 100644 --- a/src/emucore/tia/TIA.cxx +++ b/src/emucore/tia/TIA.cxx @@ -63,10 +63,11 @@ static constexpr uInt8 resxLateHblankThreshold = TIAConstants::H_CYCLES - 3; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TIA::TIA(ConsoleIO& console, const ConsoleTimingProvider& timingProvider, - Settings& settings) + Settings& settings, const onPhosphorCallback callback) : myConsole{console}, myTimingProvider{timingProvider}, mySettings{settings}, + myPhosphorCallback{callback}, myPlayfield{~CollisionMask::playfield & 0x7FFF}, myMissile0{~CollisionMask::missile0 & 0x7FFF}, myMissile1{~CollisionMask::missile1 & 0x7FFF}, @@ -186,6 +187,12 @@ void TIA::initialize() myFrontBuffer.fill(0); myFramebuffer.fill(0); + 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)); + applyDeveloperSettings(); // Must be done last, after all other items have reset @@ -193,6 +200,8 @@ void TIA::initialize() setFixedColorPalette(mySettings.getString("tia.dbgcolors")); enableFixedColors( mySettings.getBool(devSettings ? "dev.debugcolors" : "plr.debugcolors")); + myAutoPhosphorEnabled = mySettings.getString("tv.phosphor") == "auto"; + myAutoPhosphorActive = false; #ifdef DEBUGGER_SUPPORT createAccessArrays(); @@ -1396,6 +1405,83 @@ void TIA::onFrameComplete() myFrontBufferScanlines = scanlinesLastFrame(); + if(myAutoPhosphorEnabled) + { + 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; + static constexpr int PHOSPHOR_FRAMES = 8; + + int diffCount[FLICKER_FRAMES - 1]; + + //cerr << missingScanlines << ", " << myFrontBufferScanlines << " | "; + //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) + { + int delta; + delta = std::abs(myPosP0[y][myFlickerFrame] - myPosP0[y][otherFrame]); + if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA) + ++diffCount[frame]; + delta = std::abs(myPosP1[y][myFlickerFrame] - myPosP1[y][otherFrame]); + if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA) + ++diffCount[frame]; + delta = std::abs(myPosM0[y][myFlickerFrame] - myPosM0[y][otherFrame]); + if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA) + ++diffCount[frame]; + delta = std::abs(myPosM1[y][myFlickerFrame] - myPosM1[y][otherFrame]); + if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA) + ++diffCount[frame]; + delta = std::abs(myPosBL[y][myFlickerFrame] - myPosBL[y][otherFrame]); + if(delta >= MIN_FLICKER_DELTA && delta <= MAX_FLICKER_DELTA) + ++diffCount[frame]; + } + } + //cerr << ": "; + //for(int i = 0; i < FLICKER_FRAMES - 1; ++i) + // cerr << diffCount[i] << ", "; + if(diffCount[0] > MIN_DIFF && + (diffCount[0] > diffCount[1] * 1.1 || + diffCount[0] > diffCount[2] * 1.2 || + diffCount[0] > diffCount[3] * 1.3)) + { + if(myFlickerCount < PHOSPHOR_FRAMES) + { + myFlickerCount += 2; // enabled phosphor twice as fast + if(myFlickerCount >= PHOSPHOR_FRAMES && !myAutoPhosphorActive) + { + myAutoPhosphorActive = true; + myPhosphorCallback(true); + } + } + } + else if(myFlickerCount) + { + if(--myFlickerCount == 0 && myAutoPhosphorActive) + { + myAutoPhosphorActive = false; + myPhosphorCallback(false); + } + } + //cerr << "|" << myFlickerCount; + //if(myAutoPhosphorActive) + // cerr << " *** ON ***\n"; + //else + // cerr << " off\n"; + + if(--myFlickerFrame < 0) + myFlickerFrame = FLICKER_FRAMES - 1; + } + ++myFramesSinceLastRender; } @@ -1552,7 +1638,51 @@ FORCE_INLINE void TIA::nextLine() myBall.nextLine(); myPlayfield.nextLine(); - if (myFrameManager->isRendering() && myFrameManager->getY() == 0) flushLineCache(); + if(myFrameManager->isRendering()) + { + if(myFrameManager->getY() == 0) + flushLineCache(); + 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) + // + // - unneccassary phosphor: + // - Gas Hog: before game starts (odd invisible 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()) << " "; + } + } mySystem->m6502().clearHaltRequest(); } @@ -1574,6 +1704,15 @@ void TIA::cloneLastLine() std::copy_n(myBackBuffer.begin() + (y - 1) * TIAConstants::H_PIXEL, TIAConstants::H_PIXEL, myBackBuffer.begin() + y * TIAConstants::H_PIXEL); + + if(myAutoPhosphorEnabled) + { + myPosP0[y][myFlickerFrame] = myPosP0[y - 1][myFlickerFrame]; + myPosP1[y][myFlickerFrame] = myPosP1[y - 1][myFlickerFrame]; + myPosM0[y][myFlickerFrame] = myPosM0[y - 1][myFlickerFrame]; + myPosM1[y][myFlickerFrame] = myPosM1[y - 1][myFlickerFrame]; + myPosBL[y][myFlickerFrame] = myPosBL[y - 1][myFlickerFrame]; + } } } diff --git a/src/emucore/tia/TIA.hxx b/src/emucore/tia/TIA.hxx index cb23147c8..811c80c64 100644 --- a/src/emucore/tia/TIA.hxx +++ b/src/emucore/tia/TIA.hxx @@ -109,6 +109,8 @@ class TIA : public Device friend class TIADebug; friend class RiotDebug; + using onPhosphorCallback = std::function; + /** Create a new TIA for the specified console @@ -116,7 +118,7 @@ class TIA : public Device @param settings The settings object for this TIA device */ TIA(ConsoleIO& console, const ConsoleTimingProvider& timingProvider, - Settings& settings); + Settings& settings, const onPhosphorCallback callback); ~TIA() override = default; public: @@ -277,6 +279,8 @@ class TIA : public Device Enables/disables color-loss for PAL modes only. @param enabled Whether to enable or disable PAL color-loss mode + + @return True if color-loss got enabled */ bool enableColorLoss(bool enabled); @@ -294,6 +298,32 @@ class TIA : public Device */ bool colorLossActive() const { return myColorLossActive; } + /** + Enables/disables auto-phosphor. + + @param enabled Whether to enable or disable auto-phosphor mode + */ + void enableAutoPhosphor(bool enabled) + { + myAutoPhosphorEnabled = enabled; + if(!enabled) + 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. @@ -965,7 +995,17 @@ class TIA : public Device bool myColorLossEnabled{false}; bool myColorLossActive{false}; - std::array myColorCounts; + /** + * 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; + ObjectPos myPosP0, myPosP1, myPosM0, myPosM1, myPosBL; + int myFlickerFrame{0}; + int myFlickerCount{0}; + onPhosphorCallback myPhosphorCallback; #ifdef DEBUGGER_SUPPORT /** diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx index 7f2637ff2..e1bd070b7 100644 --- a/src/gui/GameInfoDialog.cxx +++ b/src/gui/GameInfoDialog.cxx @@ -160,7 +160,7 @@ void GameInfoDialog::addEmulationTab() // Phosphor ypos += lineHeight + VGAP; myPhosphor = new CheckboxWidget(myTab, _font, HBORDER, ypos + 1, - "Phosphor (enabled for all ROMs)", kPhosphorChanged); + "Phosphor (auto-enabled for all ROMs)", kPhosphorChanged); myPhosphor->setToolTip(Event::TogglePhosphor); wid.push_back(myPhosphor); @@ -770,7 +770,6 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props) } } myTypeDetected->setLabel(bsDetected); - updateMultiCart(); // Start bank VarList::push_back(items, "Auto", "AUTO"); @@ -806,10 +805,13 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props) // 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 bool usePhosphor = props.get(PropType::Display_Phosphor) == "YES"; myPhosphor->setState(usePhosphor); if (alwaysPhosphor) - myPhosphor->setLabel("Phosphor (enabled for all ROMs)"); + myPhosphor->setLabel("Phosphor (enabled for all ROMs"); + else if (autoPhosphor) + myPhosphor->setLabel("Phosphor (auto-enabled for all ROMs)"); else myPhosphor->setLabel("Phosphor"); @@ -823,6 +825,8 @@ void GameInfoDialog::loadEmulationProperties(const Properties& props) myVCenter->setValueUnit(vcenter ? "px" : ""); mySound->setState(props.get(PropType::Cart_Sound) == "STEREO"); + + updateMultiCart(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1191,9 +1195,9 @@ void GameInfoDialog::updateMultiCart() myFormat->setEnabled(!isMulti); // if phosphor is always enabled, disable game specific phosphor settings - const bool alwaysPhosphor = isMulti || instance().settings().getString("tv.phosphor") == "always"; - myPhosphor->setEnabled(!alwaysPhosphor); - myPPBlend->setEnabled(!alwaysPhosphor && myPhosphor->getState()); + const bool globalPhosphor = isMulti || instance().settings().getString("tv.phosphor") != "byrom"; + myPhosphor->setEnabled(!globalPhosphor); + myPPBlend->setEnabled(!globalPhosphor && myPhosphor->getState()); myVCenter->setEnabled(!isMulti); // if stereo is always enabled, disable game specific stereo setting diff --git a/src/gui/VideoAudioDialog.cxx b/src/gui/VideoAudioDialog.cxx index ef4926403..50b74ad6a 100644 --- a/src/gui/VideoAudioDialog.cxx +++ b/src/gui/VideoAudioDialog.cxx @@ -377,11 +377,17 @@ void VideoAudioDialog::addTVEffectsTab() CREATE_CUSTOM_SLIDERS(Bleed, "Bleeding ", 0) ypos += VGAP * 3; - xpos = HBORDER; // TV Phosphor effect - myTVPhosphor = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Phosphor for all ROMs", kPhosphorChanged); + items.clear(); + VarList::push_back(items, "by ROM", "byrom"); + VarList::push_back(items, "always", "always"); + VarList::push_back(items, "auto", "auto"); + myTVPhosphor = new PopUpWidget(myTab, _font, xpos, ypos, + _font.getStringWidth("by ROM"), lineHeight, + items, "Phosphor ", 0, kPhosphorChanged); + myTVPhosphor->setToolTip(Event::PhosphorModeDecrease, Event::PhosphorModeIncrease); wid.push_back(myTVPhosphor); ypos += lineHeight + VGAP / 2; @@ -754,7 +760,7 @@ void VideoAudioDialog::loadConfig() loadTVAdjustables(NTSCFilter::Preset::CUSTOM); // TV phosphor mode & blend - myTVPhosphor->setState(settings.getString("tv.phosphor") == "always"); + myTVPhosphor->setSelected(settings.getString("tv.phosphor"), "byrom"); myTVPhosLevel->setValue(settings.getInt("tv.phosblend")); handlePhosphorChange(); @@ -889,7 +895,7 @@ void VideoAudioDialog::saveConfig() NTSCFilter::saveConfig(settings); // TV phosphor mode & blend - settings.setValue("tv.phosphor", myTVPhosphor->getState() ? "always" : "byrom"); + settings.setValue("tv.phosphor", myTVPhosphor->getSelectedTag()); settings.setValue("tv.phosblend", myTVPhosLevel->getValueLabel() == "Off" ? "0" : myTVPhosLevel->getValueLabel()); @@ -1029,7 +1035,7 @@ void VideoAudioDialog::setDefaults() myTVMode->setSelected("0", "0"); // TV phosphor mode & blend - myTVPhosphor->setState(false); + myTVPhosphor->setSelected("byrom"); myTVPhosLevel->setValue(50); // TV scanline intensity & mask @@ -1209,7 +1215,7 @@ void VideoAudioDialog::handleOverscanChange() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void VideoAudioDialog::handlePhosphorChange() { - myTVPhosLevel->setEnabled(myTVPhosphor->getState()); + myTVPhosLevel->setEnabled(myTVPhosphor->getSelectedTag() != "byrom"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/gui/VideoAudioDialog.hxx b/src/gui/VideoAudioDialog.hxx index 9f391f610..1b349e220 100644 --- a/src/gui/VideoAudioDialog.hxx +++ b/src/gui/VideoAudioDialog.hxx @@ -96,7 +96,7 @@ class VideoAudioDialog : public Dialog SliderWidget* myTVBleed{nullptr}; // TV phosphor effect - CheckboxWidget* myTVPhosphor{nullptr}; + PopUpWidget* myTVPhosphor{nullptr}; SliderWidget* myTVPhosLevel{nullptr}; // TV scanline intensity and interpolation