Merge pull request #3472 from phire/TVs_dont_have_pixels
Rework the aspect ratio calculation (Fixes 240p mode)
This commit is contained in:
commit
39971ec039
|
@ -472,55 +472,90 @@ static u32 GetTicksPerOddField()
|
||||||
return GetTicksPerHalfLine() * GetHalfLinesPerOddField();
|
return GetTicksPerHalfLine() * GetHalfLinesPerOddField();
|
||||||
}
|
}
|
||||||
|
|
||||||
float GetAspectRatio(bool wide)
|
// Get the aspect ratio of VI's active area.
|
||||||
|
float GetAspectRatio()
|
||||||
{
|
{
|
||||||
u32 multiplier = static_cast<u32>(m_PictureConfiguration.STD / m_PictureConfiguration.WPL);
|
// The picture of a PAL/NTSC TV signal is defined to have a 4:3 aspect ratio,
|
||||||
int height = (multiplier * m_VerticalTimingRegister.ACV);
|
// but it's only 4:3 if the picture fill the entire active area.
|
||||||
int width = ((2 * m_HTiming0.HLW) - (m_HTiming0.HLW - m_HTiming1.HBS640)
|
// All games configure VideoInterface to add padding in both the horizontal and vertical
|
||||||
- m_HTiming1.HBE640);
|
// directions and most games also do a slight horizontal scale.
|
||||||
float pixelAR;
|
// This means that XFB never fills the entire active area and is therefor almost never 4:3
|
||||||
if (m_DisplayControlRegister.FMT == 1)
|
|
||||||
|
// To work out the correct aspect ratio of the XFB, we need to know how VideoInterface's
|
||||||
|
// currently configured active area compares to the active area of a stock PAL or NTSC
|
||||||
|
// signal (which would be 4:3)
|
||||||
|
|
||||||
|
// This function only deals with standard aspect ratios. For widescreen aspect ratios,
|
||||||
|
// multiply the result by 1.33333..
|
||||||
|
|
||||||
|
// 1. Get our active area in BT.601 samples (more or less pixels)
|
||||||
|
int active_lines = m_VerticalTimingRegister.ACV;
|
||||||
|
int active_width_samples = (m_HTiming0.HLW + m_HTiming1.HBS640 - m_HTiming1.HBE640);
|
||||||
|
|
||||||
|
// 2. TVs are analog and don't have pixels. So we convert to seconds.
|
||||||
|
float tick_length = (1.0f / SystemTimers::GetTicksPerSecond());
|
||||||
|
float vertical_period = tick_length * GetTicksPerField();
|
||||||
|
float horizontal_period= tick_length * GetTicksPerHalfLine() * 2;
|
||||||
|
float vertical_active_area = active_lines * horizontal_period;
|
||||||
|
float horizontal_active_area = tick_length * GetTicksPerSample() * active_width_samples;
|
||||||
|
|
||||||
|
// We are approximating the horizontal/vertical flyback transformers that control the
|
||||||
|
// position of the election beam on the screen. Our flyback transformers create a
|
||||||
|
// perfect Sawtooth wave, with a smooth rise and a fall that takes zero time.
|
||||||
|
// For more accurate emulation of video signals out of the 525 or 625 line standards,
|
||||||
|
// it might be necessary to emulate a less precise flyback transformer with more flaws.
|
||||||
|
// But those modes aren't officially supported by TVs anyway and could behave differently
|
||||||
|
// on different TVs.
|
||||||
|
|
||||||
|
// 3. Calculate the ratio of active time to total time for VI's active area
|
||||||
|
float vertical_active_ratio = vertical_active_area / vertical_period;
|
||||||
|
float horizontal_active_ratio = horizontal_active_area / horizontal_period;
|
||||||
|
|
||||||
|
// 4. And then scale the ratios to typical PAL/NTSC signals.
|
||||||
|
// NOTE: With the exception of selecting between PAL-M and NTSC color encoding on Brazilian
|
||||||
|
// GameCubes, the FMT field doesn't actually do anything on real hardware. But
|
||||||
|
// Nintendo's SDK always sets it appropriately to match the number of lines.
|
||||||
|
if (m_DisplayControlRegister.FMT == 1) // 625 line TV (PAL)
|
||||||
{
|
{
|
||||||
//PAL active frame is 702*576
|
// PAL defines the horizontal active area as 52us of the 64us line.
|
||||||
//In square pixels, 1024*576 is 16:9, and 768*576 is 4:3
|
// BT.470-6 defines the blanking period as 12.0us +0.0 -0.3 [table on page 5]
|
||||||
//Therefore a 16:9 TV would have a "pixel" aspect ratio of 1024/702
|
horizontal_active_ratio *= 64.0f / 52.0f;
|
||||||
//Similarly a 4:3 TV would have a ratio of 768/702
|
// PAL defines the vertical active area as 576 of 625 lines.
|
||||||
if (wide)
|
vertical_active_ratio *= 625.0f / 576.0f;
|
||||||
{
|
// TODO: Should PAL60 games go through the 625 or 525 line codepath?
|
||||||
pixelAR = 1024.0f / 702.0f;
|
// The resulting aspect ratio is close, but not identical.
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pixelAR = 768.0f / 702.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else // 525 line TV (NTSC or PAL-M)
|
||||||
|
{
|
||||||
|
// The NTSC standard doesn't define it's active area very well.
|
||||||
|
// The line is 63.55555..us long, which is derived from 1.001 / (30 * 525)
|
||||||
|
// but the blanking area is defined with a large amount of slack in the SMPTE 170M-2004
|
||||||
|
// standard, 10.7us +0.3 -0.2 [derived from table on page 9]
|
||||||
|
// The BT.470-6 standard provides a different number of 10.9us +/- 0.2 [table on page 5]
|
||||||
|
// This results in an active area between 52.5555us and 53.05555us
|
||||||
|
// Lots of different numbers float around the Internet including:
|
||||||
|
// * 52.655555.. us -- http://web.archive.org/web/20140218044518/http://lipas.uwasa.fi/~f76998/video/conversion/
|
||||||
|
// * 52.66 us -- http://www.ni.com/white-paper/4750/en/
|
||||||
|
// * 52.6 us -- http://web.mit.edu/6.111/www/f2008/handouts/L12.pdf
|
||||||
|
//
|
||||||
|
// None of these website provide primary sources for their numbers, back in the days of
|
||||||
|
// analog, TV signal timings were not that precise to start with and it never got standardized
|
||||||
|
// during the move to digital.
|
||||||
|
// We are just going to use 52.655555.. as most other numbers on the Internet appear to be a
|
||||||
|
// simplification of it. 53.655555.. is a blanking period of 10.9us, matching the BT.470-6 standard
|
||||||
|
// and within tolerance of the SMPTE 170M-2004 standard.
|
||||||
|
horizontal_active_ratio *= 63.555555f / 52.655555f;
|
||||||
|
// Even 486 active lines isn't completely agreed upon.
|
||||||
|
// Depending on how you count the two half lines you could get 485 or 484
|
||||||
|
vertical_active_ratio *= 525.0f / 486.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Calculate the final ratio and scale to 4:3
|
||||||
|
float ratio = horizontal_active_ratio / vertical_active_ratio;
|
||||||
|
if (std::isnormal(ratio)) // Check we have a sane ratio and haven't propagated any infs/nans/zeros
|
||||||
|
return ratio * (4.0f / 3.0f); // Scale to 4:3
|
||||||
else
|
else
|
||||||
{
|
return (4.0f / 3.0f); // VI isn't initialized correctly, just return 4:3 instead
|
||||||
//NTSC active frame is 710.85*486
|
|
||||||
//In square pixels, 864*486 is 16:9, and 648*486 is 4:3
|
|
||||||
//Therefore a 16:9 TV would have a "pixel" aspect ratio of 864/710.85
|
|
||||||
//Similarly a 4:3 TV would have a ratio of 648/710.85
|
|
||||||
if (wide)
|
|
||||||
{
|
|
||||||
pixelAR = 864.0f / 710.85f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pixelAR = 648.0f / 710.85f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (width == 0 || height == 0)
|
|
||||||
{
|
|
||||||
if (wide)
|
|
||||||
{
|
|
||||||
return 16.0f / 9.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 4.0f / 3.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ((float)width / (float)height) * pixelAR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function updates:
|
// This function updates:
|
||||||
|
@ -582,9 +617,14 @@ void UpdateParameters()
|
||||||
TargetRefreshRate = lround(2.0 * SystemTimers::GetTicksPerSecond() / (GetTicksPerEvenField() + GetTicksPerOddField()));
|
TargetRefreshRate = lround(2.0 * SystemTimers::GetTicksPerSecond() / (GetTicksPerEvenField() + GetTicksPerOddField()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetTicksPerSample()
|
||||||
|
{
|
||||||
|
return 2 * SystemTimers::GetTicksPerSecond() / s_clock_freqs[m_Clock];
|
||||||
|
}
|
||||||
|
|
||||||
u32 GetTicksPerHalfLine()
|
u32 GetTicksPerHalfLine()
|
||||||
{
|
{
|
||||||
return 2 * SystemTimers::GetTicksPerSecond() / s_clock_freqs[m_Clock] * m_HTiming0.HLW;
|
return GetTicksPerSample() * m_HTiming0.HLW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -330,9 +330,11 @@ union UVIHorizontalStepping
|
||||||
// Change values pertaining to video mode
|
// Change values pertaining to video mode
|
||||||
void UpdateParameters();
|
void UpdateParameters();
|
||||||
|
|
||||||
|
u32 GetTicksPerSample();
|
||||||
u32 GetTicksPerHalfLine();
|
u32 GetTicksPerHalfLine();
|
||||||
u32 GetTicksPerField();
|
u32 GetTicksPerField();
|
||||||
|
|
||||||
//For VI Scaling and Aspect Ratio Correction
|
// Get the aspect ratio of VI's active area.
|
||||||
float GetAspectRatio(bool);
|
// This function only deals with standard aspect ratios. For widescreen aspect ratios, multiply the result by 1.33333..
|
||||||
|
float GetAspectRatio();
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ unsigned int Renderer::efb_scale_numeratorY = 1;
|
||||||
unsigned int Renderer::efb_scale_denominatorX = 1;
|
unsigned int Renderer::efb_scale_denominatorX = 1;
|
||||||
unsigned int Renderer::efb_scale_denominatorY = 1;
|
unsigned int Renderer::efb_scale_denominatorY = 1;
|
||||||
|
|
||||||
|
static float AspectToWidescreen(float aspect) { return aspect * ((16.0f / 9.0f) / (4.0f / 3.0f)); }
|
||||||
|
|
||||||
Renderer::Renderer()
|
Renderer::Renderer()
|
||||||
: frame_data()
|
: frame_data()
|
||||||
|
@ -427,7 +428,9 @@ void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
|
||||||
// Don't know if there is a better place for this code so there isn't a 1 frame delay
|
// Don't know if there is a better place for this code so there isn't a 1 frame delay
|
||||||
if (g_ActiveConfig.bWidescreenHack)
|
if (g_ActiveConfig.bWidescreenHack)
|
||||||
{
|
{
|
||||||
float source_aspect = VideoInterface::GetAspectRatio(Core::g_aspect_wide);
|
float source_aspect = VideoInterface::GetAspectRatio();
|
||||||
|
if (Core::g_aspect_wide)
|
||||||
|
source_aspect = AspectToWidescreen(source_aspect);
|
||||||
float target_aspect;
|
float target_aspect;
|
||||||
|
|
||||||
switch (g_ActiveConfig.iAspectRatio)
|
switch (g_ActiveConfig.iAspectRatio)
|
||||||
|
@ -436,10 +439,10 @@ void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
|
||||||
target_aspect = WinWidth / WinHeight;
|
target_aspect = WinWidth / WinHeight;
|
||||||
break;
|
break;
|
||||||
case ASPECT_ANALOG:
|
case ASPECT_ANALOG:
|
||||||
target_aspect = VideoInterface::GetAspectRatio(false);
|
target_aspect = VideoInterface::GetAspectRatio();
|
||||||
break;
|
break;
|
||||||
case ASPECT_ANALOG_WIDE:
|
case ASPECT_ANALOG_WIDE:
|
||||||
target_aspect = VideoInterface::GetAspectRatio(true);
|
target_aspect = AspectToWidescreen(VideoInterface::GetAspectRatio());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// ASPECT_AUTO
|
// ASPECT_AUTO
|
||||||
|
@ -472,17 +475,13 @@ void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
|
||||||
|
|
||||||
// The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio
|
// The rendering window aspect ratio as a proportion of the 4:3 or 16:9 ratio
|
||||||
float Ratio;
|
float Ratio;
|
||||||
switch (g_ActiveConfig.iAspectRatio)
|
if (g_ActiveConfig.iAspectRatio == ASPECT_ANALOG_WIDE || (g_ActiveConfig.iAspectRatio != ASPECT_ANALOG && Core::g_aspect_wide))
|
||||||
{
|
{
|
||||||
case ASPECT_ANALOG_WIDE:
|
Ratio = (WinWidth / WinHeight) / AspectToWidescreen(VideoInterface::GetAspectRatio());
|
||||||
Ratio = (WinWidth / WinHeight) / VideoInterface::GetAspectRatio(true);
|
}
|
||||||
break;
|
else
|
||||||
case ASPECT_ANALOG:
|
{
|
||||||
Ratio = (WinWidth / WinHeight) / VideoInterface::GetAspectRatio(false);
|
Ratio = (WinWidth / WinHeight) / VideoInterface::GetAspectRatio();
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Ratio = (WinWidth / WinHeight) / VideoInterface::GetAspectRatio(Core::g_aspect_wide);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH)
|
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH)
|
||||||
|
@ -508,18 +507,7 @@ void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height)
|
||||||
// ------------------
|
// ------------------
|
||||||
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH && g_ActiveConfig.bCrop)
|
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH && g_ActiveConfig.bCrop)
|
||||||
{
|
{
|
||||||
switch (g_ActiveConfig.iAspectRatio)
|
Ratio = (4.0f / 3.0f) / VideoInterface::GetAspectRatio();
|
||||||
{
|
|
||||||
case ASPECT_ANALOG_WIDE:
|
|
||||||
Ratio = (16.0f / 9.0f) / VideoInterface::GetAspectRatio(true);
|
|
||||||
break;
|
|
||||||
case ASPECT_ANALOG:
|
|
||||||
Ratio = (4.0f / 3.0f) / VideoInterface::GetAspectRatio(false);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Ratio = (!Core::g_aspect_wide ? (4.0f / 3.0f) : (16.0f / 9.0f)) / VideoInterface::GetAspectRatio(Core::g_aspect_wide);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Ratio <= 1.0f)
|
if (Ratio <= 1.0f)
|
||||||
{
|
{
|
||||||
Ratio = 1.0f / Ratio;
|
Ratio = 1.0f / Ratio;
|
||||||
|
|
Loading…
Reference in New Issue