Skip to content

Commit

Permalink
Rework aspect ratio calculations.
Browse files Browse the repository at this point in the history
They are now based on signal timings rather than pixels, as it
didn't make a lot of sense to do things with pixels.

Now handles all 240i/240p/480i/480p modes without any special
casing.

Despite the diffrent equaions, this should result in the exact same aspect
ratio as the previous code.
  • Loading branch information
phire committed Jan 9, 2016
1 parent 9c0dae4 commit c49a82b
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 24 deletions.
107 changes: 84 additions & 23 deletions Source/Core/Core/HW/VideoInterface.cpp
Expand Up @@ -472,34 +472,90 @@ static u32 GetTicksPerOddField()
return GetTicksPerHalfLine() * GetHalfLinesPerOddField();
}

// Get the aspect ratio of VI's active area.
float GetAspectRatio()
{
u32 multiplier = static_cast<u32>(m_PictureConfiguration.STD / m_PictureConfiguration.WPL);
int height = (multiplier * m_VerticalTimingRegister.ACV);
int width = ((2 * m_HTiming0.HLW) - (m_HTiming0.HLW - m_HTiming1.HBS640)
- m_HTiming1.HBE640);
float pixelAR;
if (m_DisplayControlRegister.FMT == 1)
// The picture of a PAL/NTSC TV signal is defined to have a 4:3 aspect ratio,
// but it's only 4:3 if the picture fill the entire active area.
// All games configure VideoInterface to add padding in both the horizontal and vertical
// directions and most games also do a slight horizontal scale.
// This means that XFB never fills the entire active area and is therefor almost never 4:3

// 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
//In square pixels, 1024*576 is 16:9, and 768*576 is 4:3
//Therefore a 16:9 TV would have a "pixel" aspect ratio of 1024/702
//Similarly a 4:3 TV would have a ratio of 768/702
pixelAR = 768.0f / 702.0f;
// PAL defines the horizontal active area as 52us of the 64us line.
// BT.470-6 defines the blanking period as 12.0us +0.0 -0.3 [table on page 5]
horizontal_active_ratio *= 64.0f / 52.0f;
// PAL defines the vertical active area as 576 of 625 lines.
vertical_active_ratio *= 625.0f / 576.0f;
// TODO: Should PAL60 games go through the 625 or 525 line codepath?
// The resulting aspect ratio is close, but not identical.
}
else
{
//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
pixelAR = 648.0f / 710.85f;
}
if (width == 0 || height == 0)
else // 525 line TV (NTSC or PAL-M)
{
return 4.0f / 3.0f;
// 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;
}
return ((float)width / (float)height) * pixelAR;

// 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
return (4.0f / 3.0f); // VI isn't initialized correctly, just return 4:3 instead
}

// This function updates:
Expand Down Expand Up @@ -561,9 +617,14 @@ void UpdateParameters()
TargetRefreshRate = lround(2.0 * SystemTimers::GetTicksPerSecond() / (GetTicksPerEvenField() + GetTicksPerOddField()));
}

u32 GetTicksPerSample()
{
return 2 * SystemTimers::GetTicksPerSecond() / s_clock_freqs[m_Clock];
}

u32 GetTicksPerHalfLine()
{
return 2 * SystemTimers::GetTicksPerSecond() / s_clock_freqs[m_Clock] * m_HTiming0.HLW;
return GetTicksPerSample() * m_HTiming0.HLW;
}


Expand Down
4 changes: 3 additions & 1 deletion Source/Core/Core/HW/VideoInterface.h
Expand Up @@ -330,9 +330,11 @@ union UVIHorizontalStepping
// Change values pertaining to video mode
void UpdateParameters();

u32 GetTicksPerSample();
u32 GetTicksPerHalfLine();
u32 GetTicksPerField();

//For VI Scaling and Aspect Ratio Correction
// Get the aspect ratio of VI's active area.
// This function only deals with standard aspect ratios. For widescreen aspect ratios, multiply the result by 1.33333..
float GetAspectRatio();
}

0 comments on commit c49a82b

Please sign in to comment.