mirror of https://github.com/stella-emu/stella.git
Merge branch 'master' into feature/filesystem
This commit is contained in:
commit
944f20a011
|
@ -72,62 +72,71 @@ FrameLayout FrameLayoutDetector::detectedLayout(bool detectPal60, bool detectNts
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Multiply each hue's count with its NTSC and PAL stats and aggregate results
|
// Init the layout based on scanline analysis.
|
||||||
// If NTSC/PAL results differ significantly, overrule frame result
|
FrameLayout layout = myPalFrameSum > myNtscFrameSum ? FrameLayout::pal : FrameLayout::ntsc;
|
||||||
FrameLayout layout = myPalFrames > myNtscFrames ? FrameLayout::pal : FrameLayout::ntsc;
|
|
||||||
|
|
||||||
constexpr std::array<double, NUM_HUES> ntscColorFactor{
|
if(detectPal60 || detectNtsc50)
|
||||||
0.00000, 0.05683, 0.06220, 0.05505, 0.06162, 0.02874, 0.03532, 0.03716,
|
{
|
||||||
0.15568, 0.06471, 0.02886, 0.03224, 0.06903, 0.11478, 0.02632, 0.01675
|
// Multiply each hue's count with its NTSC and PAL stats and aggregate results
|
||||||
}; // ignore black = 0x00!
|
// If NTSC/PAL results differ significantly, overrule frame result
|
||||||
constexpr std::array<double, NUM_HUES> palColorFactor{
|
constexpr std::array<double, NUM_HUES> ntscColorFactor{
|
||||||
0.00000, 0.00450, 0.09962, 0.07603, 0.06978, 0.13023, 0.09638, 0.02268,
|
0.00000, 0.05683, 0.06220, 0.05505, 0.06162, 0.02874, 0.03532, 0.03716,
|
||||||
0.02871, 0.04700, 0.02950, 0.11974, 0.03474, 0.08025, 0.00642, 0.00167
|
0.15568, 0.06471, 0.02886, 0.03224, 0.06903, 0.11478, 0.02632, 0.01675
|
||||||
}; // ignore black = 0x00!
|
}; // ignore black = 0x00!
|
||||||
// Calculation weights and params (optimum based on sampled ROMs optimized for PAL-60)
|
constexpr std::array<double, NUM_HUES> palColorFactor{
|
||||||
constexpr double POWER_FACTOR = 0.17; // Level the color counts (large values become less relevant)
|
0.00000, 0.00450, 0.09962, 0.07603, 0.06978, 0.13023, 0.09638, 0.02268,
|
||||||
constexpr uInt32 SUM_DIV = 20; // Skip too small counts
|
0.02871, 0.04700, 0.02950, 0.11974, 0.03474, 0.08025, 0.00642, 0.00167
|
||||||
constexpr uInt32 MIN_VALID = 3; // Minimum number of different hues with significant counts
|
}; // ignore black = 0x00!
|
||||||
constexpr double SUM_FACTOR = 2.0; // Minimum sum difference which triggers a layout change
|
// Calculation weights and params (optimum based on sampled ROMs, optimized for PAL-60)
|
||||||
|
constexpr double POWER_FACTOR = 0.17; // Level the color counts (large values become less relevant)
|
||||||
|
constexpr uInt32 SUM_DIV = 20; // Skip too small counts (could be removed)
|
||||||
|
constexpr uInt32 MIN_VALID = 3; // Minimum number of different hues with significant counts
|
||||||
|
constexpr double OVERRULE_FACTOR = 2.0; // Minimum sum difference which triggers a layout change
|
||||||
|
|
||||||
double ntscSum{0}, palSum{0};
|
double ntscColSum{ 0 }, paCollSum{ 0 };
|
||||||
std::array<double, NUM_HUES> hueSum{0};
|
std::array<double, NUM_HUES> hueSum{ 0 };
|
||||||
double totalHueSum = 0;
|
double totalHueSum = 0;
|
||||||
uInt32 validHues = 0;
|
uInt32 validHues = 0;
|
||||||
|
|
||||||
// Aggregate hues
|
// Aggregate hues
|
||||||
for(int hue = 0; hue < NUM_HUES; ++hue)
|
for(int hue = 0; hue < NUM_HUES; ++hue)
|
||||||
{
|
|
||||||
for(int lum = 0; lum < NUM_LUMS; ++lum)
|
|
||||||
if(hue || lum) // skip 0x00
|
|
||||||
hueSum[hue] += myColorCount[hue * NUM_LUMS + lum];
|
|
||||||
hueSum[hue] = std::pow(hueSum[hue], POWER_FACTOR);
|
|
||||||
totalHueSum += hueSum[hue];
|
|
||||||
}
|
|
||||||
// Calculate hue sums
|
|
||||||
for(int hue = 0; hue < NUM_HUES; ++hue)
|
|
||||||
{
|
|
||||||
if(hueSum[hue] > totalHueSum / SUM_DIV)
|
|
||||||
validHues++;
|
|
||||||
ntscSum += hueSum[hue] * ntscColorFactor[hue];
|
|
||||||
palSum += hueSum[hue] * palColorFactor[hue];
|
|
||||||
}
|
|
||||||
// Correct layout if enough valid hues and significant sum difference
|
|
||||||
// TODO: Use fractional scanline counts for intermediate values around 285 scanlines, e.g.
|
|
||||||
// Desert Falcon, Dumbo's Flying Circus, Dungeon, Firefox, Millipede, Popeye, RS Basketball, Star Trek, Stunt Cycle
|
|
||||||
if(validHues >= MIN_VALID)
|
|
||||||
{
|
|
||||||
if(detectPal60 && layout == FrameLayout::ntsc && ntscSum * SUM_FACTOR < palSum)
|
|
||||||
{
|
{
|
||||||
layout = FrameLayout::pal60;
|
for(int lum = 0; lum < NUM_LUMS; ++lum)
|
||||||
Logger::debug("Changed layout from NTSC into PAL-60");
|
if(hue || lum) // skip 0x00
|
||||||
|
hueSum[hue] += myColorCount[hue * NUM_LUMS + lum];
|
||||||
|
hueSum[hue] = std::pow(hueSum[hue], POWER_FACTOR);
|
||||||
|
totalHueSum += hueSum[hue];
|
||||||
}
|
}
|
||||||
// Note: three false positives (Berzerk, Canyon Bomber, Jawbreaker) for NTSC-50 after
|
// Calculate hue sums
|
||||||
// optimizing for PAL-60
|
for(int hue = 0; hue < NUM_HUES; ++hue)
|
||||||
else if(detectNtsc50 && layout == FrameLayout::pal && palSum * SUM_FACTOR < ntscSum)
|
|
||||||
{
|
{
|
||||||
layout = FrameLayout::ntsc50;
|
if(hueSum[hue] > totalHueSum / SUM_DIV)
|
||||||
Logger::debug("Changed layout from PAL into NTSC-50");
|
validHues++;
|
||||||
|
ntscColSum += hueSum[hue] * ntscColorFactor[hue];
|
||||||
|
paCollSum += hueSum[hue] * palColorFactor[hue];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correct the layout if there are enough valid hues and a significant color sum difference.
|
||||||
|
// The required difference depends on the significance of the scanline analyis.
|
||||||
|
if(validHues >= MIN_VALID)
|
||||||
|
{
|
||||||
|
// Use frame analysis results to scale color overrule factor from 1.0 .. OVERRULE_FACTOR
|
||||||
|
const double overRuleFactor = 1.0 + (OVERRULE_FACTOR - 1.0) * 2
|
||||||
|
* (std::max(myNtscFrameSum, myPalFrameSum) / (myNtscFrameSum + myPalFrameSum) - 0.5); // 1.0 .. OVERRULE_FACTOR
|
||||||
|
|
||||||
|
//cerr << overRuleFactor << " * PAL:" << paCollSum << "/NTSC:" << ntscColSum << endl;
|
||||||
|
if(detectPal60 && layout == FrameLayout::ntsc && ntscColSum * overRuleFactor < paCollSum)
|
||||||
|
{
|
||||||
|
layout = FrameLayout::pal60;
|
||||||
|
Logger::debug("TV format changed from NTSC to PAL-60");
|
||||||
|
}
|
||||||
|
// Note: Three false positives (Adventure, Berzerk, Canyon Bomber) for NTSC-50 after
|
||||||
|
// optimizing for PAL-60
|
||||||
|
else if(detectNtsc50 && layout == FrameLayout::pal && paCollSum * overRuleFactor < ntscColSum)
|
||||||
|
{
|
||||||
|
layout = FrameLayout::ntsc50;
|
||||||
|
Logger::debug("TV format changed from PAL to NTSC-50");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return layout;
|
return layout;
|
||||||
|
@ -143,7 +152,7 @@ FrameLayoutDetector::FrameLayoutDetector()
|
||||||
void FrameLayoutDetector::onReset()
|
void FrameLayoutDetector::onReset()
|
||||||
{
|
{
|
||||||
myState = State::waitForVsyncStart;
|
myState = State::waitForVsyncStart;
|
||||||
myNtscFrames = myPalFrames = 0;
|
myNtscFrameSum = myPalFrameSum = 0;
|
||||||
myLinesWaitingForVsyncToStart = 0;
|
myLinesWaitingForVsyncToStart = 0;
|
||||||
myColorCount.fill(0);
|
myColorCount.fill(0);
|
||||||
myIsRendering = true;
|
myIsRendering = true;
|
||||||
|
@ -223,37 +232,14 @@ void FrameLayoutDetector::finalizeFrame()
|
||||||
|
|
||||||
if (myTotalFrames <= Metrics::initialGarbageFrames) return;
|
if (myTotalFrames <= Metrics::initialGarbageFrames) return;
|
||||||
|
|
||||||
// Calculate the delta between scanline count and the sweet spot for the respective
|
// Calculate how close a frame is to PAL and NTSC based on scanlines. An odd scanline count
|
||||||
// frame layouts
|
// results into a penalty of 0.5 for PAL. The result is between 0.0 (<=262 scanlines) and
|
||||||
const uInt32
|
// 1.0 (>=312) and added to PAL and (inverted) NTSC sums.
|
||||||
deltaNTSC = abs(static_cast<Int32>(myCurrentFrameFinalLines) - static_cast<Int32>(frameLinesNTSC)),
|
constexpr double ODD_PENALTY = 0.5; // guessed value :)
|
||||||
deltaPAL = abs(static_cast<Int32>(myCurrentFrameFinalLines) - static_cast<Int32>(frameLinesPAL));
|
const double palFrame = BSPF::clamp(((myCurrentFrameFinalLines % 2) ? ODD_PENALTY : 1.0)
|
||||||
|
* static_cast<double>(myCurrentFrameFinalLines - frameLinesNTSC)
|
||||||
// Does the scanline count fall into one of our tolerance windows? -> use it
|
/ static_cast<double>(frameLinesPAL - frameLinesNTSC), 0.0, 1.0);
|
||||||
if (std::min(deltaNTSC, deltaPAL) <= Metrics::tvModeDetectionTolerance)
|
myPalFrameSum += palFrame;
|
||||||
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
|
myNtscFrameSum += 1.0 - palFrame;
|
||||||
else if (
|
//cerr << myCurrentFrameFinalLines << ", " << palFrame << ", " << myPalFrameSum << ", " << myNtscFrameSum << endl;
|
||||||
// If scanline count is odd and lies between the PAL and NTSC windows we assume
|
|
||||||
// it is NTSC (it would cause color loss on PAL CRTs)
|
|
||||||
(myCurrentFrameFinalLines < frameLinesPAL) &&
|
|
||||||
(myCurrentFrameFinalLines > frameLinesNTSC) &&
|
|
||||||
(myCurrentFrameFinalLines % 2)
|
|
||||||
)
|
|
||||||
layout(FrameLayout::ntsc);
|
|
||||||
else
|
|
||||||
// Take the nearest layout if all else fails
|
|
||||||
layout(deltaNTSC <= deltaPAL ? FrameLayout::ntsc : FrameLayout::pal);
|
|
||||||
|
|
||||||
switch (layout()) {
|
|
||||||
case FrameLayout::ntsc:
|
|
||||||
++myNtscFrames;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FrameLayout::pal:
|
|
||||||
++myPalFrames;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw runtime_error("cannot happen");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,9 +95,6 @@ class FrameLayoutDetector: public AbstractFrameManager
|
||||||
// (exceeding ideal frame height)
|
// (exceeding ideal frame height)
|
||||||
waitForVsync = 100,
|
waitForVsync = 100,
|
||||||
|
|
||||||
// tolerance window around ideal frame size for TV mode detection
|
|
||||||
tvModeDetectionTolerance = 20,
|
|
||||||
|
|
||||||
// these frames will not be considered for detection
|
// these frames will not be considered for detection
|
||||||
initialGarbageFrames = TIAConstants::initialGarbageFrames
|
initialGarbageFrames = TIAConstants::initialGarbageFrames
|
||||||
};
|
};
|
||||||
|
@ -121,10 +118,8 @@ class FrameLayoutDetector: public AbstractFrameManager
|
||||||
*/
|
*/
|
||||||
State myState{State::waitForVsyncStart};
|
State myState{State::waitForVsyncStart};
|
||||||
|
|
||||||
/**
|
// The aggregated likelynesses of respective two frame layouts.
|
||||||
* The total number of frames detected as the respective frame layout.
|
double myNtscFrameSum{0}, myPalFrameSum{0};
|
||||||
*/
|
|
||||||
uInt32 myNtscFrames{0}, myPalFrames{0};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We count the number of scanlines we spend waiting for vsync to be
|
* We count the number of scanlines we spend waiting for vsync to be
|
||||||
|
@ -139,7 +134,6 @@ class FrameLayoutDetector: public AbstractFrameManager
|
||||||
*/
|
*/
|
||||||
static constexpr int NUM_HUES = 16;
|
static constexpr int NUM_HUES = 16;
|
||||||
static constexpr int NUM_LUMS = 8;
|
static constexpr int NUM_LUMS = 8;
|
||||||
|
|
||||||
std::array<uInt64, NUM_HUES * NUM_LUMS> myColorCount{0};
|
std::array<uInt64, NUM_HUES * NUM_LUMS> myColorCount{0};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in New Issue