Merge pull request #2686 from booto/field-timing
VI: derive field timing from VI registers
This commit is contained in:
commit
ecbb83fa0f
|
@ -15,6 +15,7 @@
|
|||
#include "Core/HW/GPFifo.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
|
||||
|
@ -169,7 +170,7 @@ FifoPlayer::FifoPlayer() :
|
|||
void FifoPlayer::WriteFrame(const FifoFrameInfo &frame, const AnalyzedFrameInfo &info)
|
||||
{
|
||||
// Core timing information
|
||||
m_CyclesPerFrame = SystemTimers::GetTicksPerSecond() / 60;
|
||||
m_CyclesPerFrame = SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate;
|
||||
m_ElapsedCycles = 0;
|
||||
m_FrameFifoSize = frame.fifoDataSize;
|
||||
|
||||
|
|
|
@ -581,11 +581,11 @@ int GetTicksToNextSIPoll()
|
|||
return SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate / 2;
|
||||
|
||||
if (!g_Poll.Y && g_Poll.X)
|
||||
return VideoInterface::GetTicksPerLine() * g_Poll.X;
|
||||
return 2 * VideoInterface::GetTicksPerHalfLine() * g_Poll.X;
|
||||
else if (!g_Poll.Y)
|
||||
return SystemTimers::GetTicksPerSecond() / 60;
|
||||
|
||||
return std::min(VideoInterface::GetTicksPerFrame() / g_Poll.Y, VideoInterface::GetTicksPerLine() * g_Poll.X);
|
||||
return std::min(VideoInterface::GetTicksPerField() / g_Poll.Y, 2 * VideoInterface::GetTicksPerHalfLine() * g_Poll.X);
|
||||
}
|
||||
|
||||
} // end of namespace SerialInterface
|
||||
|
|
|
@ -126,7 +126,7 @@ static void IPC_HLE_UpdateCallback(u64 userdata, int cyclesLate)
|
|||
static void VICallback(u64 userdata, int cyclesLate)
|
||||
{
|
||||
VideoInterface::Update();
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine() - cyclesLate, et_VI);
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine() - cyclesLate, et_VI);
|
||||
}
|
||||
|
||||
static void SICallback(u64 userdata, int cyclesLate)
|
||||
|
@ -185,7 +185,7 @@ static void PatchEngineCallback(u64 userdata, int cyclesLate)
|
|||
{
|
||||
// Patch mem and run the Action Replay
|
||||
PatchEngine::ApplyFramePatches();
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame() - cyclesLate, et_PatchEngine);
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField() - cyclesLate, et_PatchEngine);
|
||||
}
|
||||
|
||||
static void ThrottleCallback(u64 last_time, int cyclesLate)
|
||||
|
@ -259,16 +259,16 @@ void Init()
|
|||
et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);
|
||||
et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback);
|
||||
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI);
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine(), et_VI);
|
||||
CoreTiming::ScheduleEvent(0, et_DSP);
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI);
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_SI);
|
||||
CoreTiming::ScheduleEvent(AUDIO_DMA_PERIOD, et_AudioDMA);
|
||||
CoreTiming::ScheduleEvent(0, et_Throttle, Common::Timer::GetTimeMs());
|
||||
if (SConfig::GetInstance().bCPUThread && SConfig::GetInstance().bSyncGPU)
|
||||
CoreTiming::ScheduleEvent(0, et_CP);
|
||||
s_last_sync_gpu_tick = CoreTiming::GetTicks();
|
||||
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_PatchEngine);
|
||||
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine);
|
||||
|
||||
if (SConfig::GetInstance().bWii)
|
||||
CoreTiming::ScheduleEvent(IPC_HLE_PERIOD, et_IPC_HLE);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace VideoInterface
|
||||
{
|
||||
|
@ -36,8 +37,6 @@ static UVIFBInfoRegister m_XFBInfoTop;
|
|||
static UVIFBInfoRegister m_XFBInfoBottom;
|
||||
static UVIFBInfoRegister m_3DFBInfoTop; // Start making your stereoscopic demos! :p
|
||||
static UVIFBInfoRegister m_3DFBInfoBottom;
|
||||
static u16 m_VBeamPos = 0; // 0: Inactive
|
||||
static u16 m_HBeamPos = 0; // 0: Inactive
|
||||
static UVIInterruptRegister m_InterruptRegister[4];
|
||||
static UVILatchRegister m_LatchRegister[2];
|
||||
static PictureConfigurationRegister m_PictureConfiguration;
|
||||
|
@ -53,11 +52,23 @@ static UVIBorderBlankRegister m_BorderHBlank;
|
|||
|
||||
u32 TargetRefreshRate = 0;
|
||||
|
||||
static u32 TicksPerFrame = 0;
|
||||
static u32 s_lineCount = 0;
|
||||
static u32 s_upperFieldBegin = 0;
|
||||
static u32 s_lowerFieldBegin = 0;
|
||||
static int fields = 1;
|
||||
static u32 s_clock_freqs[2] =
|
||||
{
|
||||
27000000UL,
|
||||
54000000UL,
|
||||
};
|
||||
|
||||
static u64 s_ticks_last_line_start; // number of ticks when the current full scanline started
|
||||
static u32 s_half_line_count; // number of halflines that have occurred for this full frame
|
||||
|
||||
|
||||
static FieldType s_current_field;
|
||||
|
||||
// below indexes are 1-based
|
||||
static u32 s_even_field_first_hl; // index first halfline of the even field
|
||||
static u32 s_odd_field_first_hl; // index first halfline of the odd field
|
||||
static u32 s_even_field_last_hl; // index last halfline of the even field
|
||||
static u32 s_odd_field_last_hl; // index last halfline of the odd field
|
||||
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
|
@ -73,8 +84,6 @@ void DoState(PointerWrap &p)
|
|||
p.Do(m_XFBInfoBottom);
|
||||
p.Do(m_3DFBInfoTop);
|
||||
p.Do(m_3DFBInfoBottom);
|
||||
p.Do(m_VBeamPos);
|
||||
p.Do(m_HBeamPos);
|
||||
p.DoArray(m_InterruptRegister, 4);
|
||||
p.DoArray(m_LatchRegister, 2);
|
||||
p.Do(m_PictureConfiguration);
|
||||
|
@ -86,16 +95,20 @@ void DoState(PointerWrap &p)
|
|||
p.Do(m_FBWidth);
|
||||
p.Do(m_BorderHBlank);
|
||||
p.Do(TargetRefreshRate);
|
||||
p.Do(TicksPerFrame);
|
||||
p.Do(s_lineCount);
|
||||
p.Do(s_upperFieldBegin);
|
||||
p.Do(s_lowerFieldBegin);
|
||||
p.Do(s_ticks_last_line_start);
|
||||
p.Do(s_half_line_count);
|
||||
p.Do(s_current_field);
|
||||
p.Do(s_even_field_first_hl);
|
||||
p.Do(s_odd_field_first_hl);
|
||||
p.Do(s_even_field_last_hl);
|
||||
p.Do(s_odd_field_last_hl);
|
||||
}
|
||||
|
||||
// Executed after Init, before game boot
|
||||
void Preset(bool _bNTSC)
|
||||
{
|
||||
m_VerticalTimingRegister.EQU = 6;
|
||||
m_VerticalTimingRegister.ACV = 0;
|
||||
|
||||
m_DisplayControlRegister.ENB = 1;
|
||||
m_DisplayControlRegister.FMT = _bNTSC ? 0 : 1;
|
||||
|
@ -133,59 +146,22 @@ void Preset(bool _bNTSC)
|
|||
m_PictureConfiguration.STD = 40;
|
||||
m_PictureConfiguration.WPL = 40;
|
||||
|
||||
m_HBeamPos = -1; // NTSC-U N64 VC games check for a non-zero HBeamPos
|
||||
m_VBeamPos = 0; // RG4JC0 checks for a zero VBeamPos
|
||||
|
||||
// 54MHz, capable of progressive scan
|
||||
m_Clock = SConfig::GetInstance().bNTSC;
|
||||
|
||||
// Say component cable is plugged
|
||||
m_DTVStatus.component_plugged = SConfig::GetInstance().bProgressive;
|
||||
|
||||
s_ticks_last_line_start = 0;
|
||||
s_half_line_count = 1;
|
||||
s_current_field = FIELD_ODD;
|
||||
|
||||
UpdateParameters();
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
m_VerticalTimingRegister.Hex = 0;
|
||||
m_DisplayControlRegister.Hex = 0;
|
||||
m_HTiming0.Hex = 0;
|
||||
m_HTiming1.Hex = 0;
|
||||
m_VBlankTimingOdd.Hex = 0;
|
||||
m_VBlankTimingEven.Hex = 0;
|
||||
m_BurstBlankingOdd.Hex = 0;
|
||||
m_BurstBlankingEven.Hex = 0;
|
||||
m_XFBInfoTop.Hex = 0;
|
||||
m_XFBInfoBottom.Hex = 0;
|
||||
m_3DFBInfoTop.Hex = 0;
|
||||
m_3DFBInfoBottom.Hex = 0;
|
||||
m_VBeamPos = 0;
|
||||
m_HBeamPos = 0;
|
||||
m_PictureConfiguration.Hex = 0;
|
||||
m_HorizontalScaling.Hex = 0;
|
||||
m_UnkAARegister = 0;
|
||||
m_Clock = 0;
|
||||
m_DTVStatus.Hex = 0;
|
||||
m_FBWidth.Hex = 0;
|
||||
m_BorderHBlank.Hex = 0;
|
||||
memset(&m_FilterCoefTables, 0, sizeof(m_FilterCoefTables));
|
||||
|
||||
fields = 1;
|
||||
|
||||
m_DTVStatus.ntsc_j = SConfig::GetInstance().bForceNTSCJ;
|
||||
|
||||
for (UVIInterruptRegister& reg : m_InterruptRegister)
|
||||
{
|
||||
reg.Hex = 0;
|
||||
}
|
||||
|
||||
for (UVILatchRegister& reg : m_LatchRegister)
|
||||
{
|
||||
reg.Hex = 0;
|
||||
}
|
||||
|
||||
m_DisplayControlRegister.Hex = 0;
|
||||
UpdateParameters();
|
||||
Preset(true);
|
||||
}
|
||||
|
||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||
|
@ -251,6 +227,32 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
);
|
||||
}
|
||||
|
||||
struct {
|
||||
u32 addr;
|
||||
u16* ptr;
|
||||
} update_params_on_read_vars[] = {
|
||||
{ VI_VERTICAL_TIMING, &m_VerticalTimingRegister.Hex },
|
||||
{ VI_HORIZONTAL_TIMING_0_HI, &m_HTiming0.Hi },
|
||||
{ VI_HORIZONTAL_TIMING_0_LO, &m_HTiming0.Lo },
|
||||
{ VI_VBLANK_TIMING_ODD_HI, &m_VBlankTimingOdd.Hi },
|
||||
{ VI_VBLANK_TIMING_ODD_LO, &m_VBlankTimingOdd.Lo },
|
||||
{ VI_VBLANK_TIMING_EVEN_HI, &m_VBlankTimingEven.Hi },
|
||||
{ VI_VBLANK_TIMING_EVEN_LO, &m_VBlankTimingEven.Lo },
|
||||
{ VI_CLOCK, &m_Clock },
|
||||
};
|
||||
|
||||
// Declare all the MMIOs that update timing params.
|
||||
for (auto& mapped_var : update_params_on_read_vars)
|
||||
{
|
||||
mmio->Register(base | mapped_var.addr,
|
||||
MMIO::DirectRead<u16>(mapped_var.ptr),
|
||||
MMIO::ComplexWrite<u16>([mapped_var](u32, u16 val) {
|
||||
*mapped_var.ptr = val;
|
||||
UpdateParameters();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// XFB related MMIOs that require special handling on writes.
|
||||
mmio->Register(base | VI_FB_LEFT_TOP_HI,
|
||||
MMIO::DirectRead<u16>(&m_XFBInfoTop.Hi),
|
||||
|
@ -283,13 +285,17 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
|
||||
// MMIOs with unimplemented writes that trigger warnings.
|
||||
mmio->Register(base | VI_VERTICAL_BEAM_POSITION,
|
||||
MMIO::DirectRead<u16>(&m_VBeamPos),
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
return (s_half_line_count + 1) / 2;
|
||||
}),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
WARN_LOG(VIDEOINTERFACE, "Changing vertical beam position to 0x%04x - not documented or implemented yet", val);
|
||||
})
|
||||
);
|
||||
mmio->Register(base | VI_HORIZONTAL_BEAM_POSITION,
|
||||
MMIO::DirectRead<u16>(&m_HBeamPos),
|
||||
MMIO::ComplexRead<u16>([](u32) {
|
||||
return static_cast<u16>(m_HTiming0.HLW * (CoreTiming::GetTicks() - s_ticks_last_line_start) / GetTicksPerHalfLine());
|
||||
}),
|
||||
MMIO::ComplexWrite<u16>([](u32, u16 val) {
|
||||
WARN_LOG(VIDEOINTERFACE, "Changing horizontal beam position to 0x%04x - not documented or implemented yet", val);
|
||||
})
|
||||
|
@ -436,6 +442,27 @@ u32 GetXFBAddressBottom()
|
|||
return m_XFBInfoBottom.FBB;
|
||||
}
|
||||
|
||||
static u32 GetHalfLinesPerEvenField()
|
||||
{
|
||||
return (3 * m_VerticalTimingRegister.EQU + m_VBlankTimingEven.PRB + 2 * m_VerticalTimingRegister.ACV + m_VBlankTimingEven.PSB);
|
||||
}
|
||||
|
||||
static u32 GetHalfLinesPerOddField()
|
||||
{
|
||||
return (3 * m_VerticalTimingRegister.EQU + m_VBlankTimingOdd.PRB + 2 * m_VerticalTimingRegister.ACV + m_VBlankTimingOdd.PSB);
|
||||
}
|
||||
|
||||
|
||||
static u32 GetTicksPerEvenField()
|
||||
{
|
||||
return GetTicksPerHalfLine() * GetHalfLinesPerEvenField();
|
||||
}
|
||||
|
||||
static u32 GetTicksPerOddField()
|
||||
{
|
||||
return GetTicksPerHalfLine() * GetHalfLinesPerOddField();
|
||||
}
|
||||
|
||||
float GetAspectRatio(bool wide)
|
||||
{
|
||||
u32 multiplier = static_cast<u32>(m_PictureConfiguration.STD / m_PictureConfiguration.WPL);
|
||||
|
@ -489,106 +516,80 @@ float GetAspectRatio(bool wide)
|
|||
|
||||
void UpdateParameters()
|
||||
{
|
||||
fields = m_DisplayControlRegister.NIN ? 2 : 1;
|
||||
s_even_field_first_hl = 1;
|
||||
s_odd_field_first_hl = s_even_field_first_hl + GetHalfLinesPerEvenField();
|
||||
s_even_field_last_hl = s_odd_field_first_hl - 1;
|
||||
s_odd_field_last_hl = s_odd_field_first_hl + GetHalfLinesPerOddField() - 1;
|
||||
|
||||
switch (m_DisplayControlRegister.FMT)
|
||||
{
|
||||
case 0: // NTSC
|
||||
TargetRefreshRate = NTSC_FIELD_RATE;
|
||||
TicksPerFrame = SystemTimers::GetTicksPerSecond() / NTSC_FIELD_RATE;
|
||||
s_lineCount = NTSC_LINE_COUNT;
|
||||
s_upperFieldBegin = NTSC_UPPER_BEGIN;
|
||||
s_lowerFieldBegin = NTSC_LOWER_BEGIN;
|
||||
break;
|
||||
|
||||
case 2: // PAL-M
|
||||
TargetRefreshRate = NTSC_FIELD_RATE;
|
||||
TicksPerFrame = SystemTimers::GetTicksPerSecond() / NTSC_FIELD_RATE;
|
||||
s_lineCount = PAL_LINE_COUNT;
|
||||
s_upperFieldBegin = PAL_UPPER_BEGIN;
|
||||
s_lowerFieldBegin = PAL_LOWER_BEGIN;
|
||||
break;
|
||||
|
||||
case 1: // PAL
|
||||
TargetRefreshRate = PAL_FIELD_RATE;
|
||||
TicksPerFrame = SystemTimers::GetTicksPerSecond() / PAL_FIELD_RATE;
|
||||
s_lineCount = PAL_LINE_COUNT;
|
||||
s_upperFieldBegin = PAL_UPPER_BEGIN;
|
||||
s_lowerFieldBegin = PAL_LOWER_BEGIN;
|
||||
break;
|
||||
|
||||
case 3: // Debug
|
||||
PanicAlert("Debug video mode not implemented");
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("Unknown Video Format - CVideoInterface");
|
||||
break;
|
||||
}
|
||||
TargetRefreshRate = lround(2.0 * SystemTimers::GetTicksPerSecond() / (GetTicksPerEvenField() + GetTicksPerOddField()));
|
||||
}
|
||||
|
||||
unsigned int GetTicksPerLine()
|
||||
u32 GetTicksPerHalfLine()
|
||||
{
|
||||
if (s_lineCount == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TicksPerFrame / (s_lineCount / (2 / fields)) ;
|
||||
}
|
||||
return 2 * SystemTimers::GetTicksPerSecond() / s_clock_freqs[m_Clock] * m_HTiming0.HLW;
|
||||
}
|
||||
|
||||
unsigned int GetTicksPerFrame()
|
||||
|
||||
u32 GetTicksPerField()
|
||||
{
|
||||
return TicksPerFrame;
|
||||
return GetTicksPerEvenField();
|
||||
}
|
||||
|
||||
static void BeginField(FieldType field)
|
||||
{
|
||||
// TODO: the stride and height shouldn't depend on whether the FieldType is
|
||||
// "progressive". We're actually telling the video backend to draw unspecified
|
||||
// junk. Due to the way XFB copies work, the unspecified junk is usually
|
||||
// the contents of the other field, so it looks okay in most cases, but we
|
||||
// shouldn't depend on that; a good example of where our output is wrong is
|
||||
// the title screen teaser videos in Metroid Prime.
|
||||
//
|
||||
// What should actually happen is that we should pass on the correct width,
|
||||
// stride, and height to the video backend, and it should deinterlace the
|
||||
// output when appropriate.
|
||||
u32 fbStride = m_PictureConfiguration.STD * (field == FIELD_PROGRESSIVE ? 16 : 8);
|
||||
bool interlaced_xfb = ((m_PictureConfiguration.STD / m_PictureConfiguration.WPL)==2);
|
||||
u32 fbStride = m_PictureConfiguration.STD * 16;
|
||||
u32 fbWidth = m_PictureConfiguration.WPL * 16;
|
||||
u32 fbHeight = m_VerticalTimingRegister.ACV * (field == FIELD_PROGRESSIVE ? 1 : 2);
|
||||
u32 fbHeight = m_VerticalTimingRegister.ACV;
|
||||
|
||||
u32 xfbAddr;
|
||||
|
||||
// Only the top field is valid in progressive mode.
|
||||
if (field == FieldType::FIELD_PROGRESSIVE)
|
||||
{
|
||||
xfbAddr = GetXFBAddressTop();
|
||||
if (interlaced_xfb && g_ActiveConfig.bForceProgressive) {
|
||||
// Strictly speaking, in interlaced mode, we're only supposed to read
|
||||
// half of the lines of the XFB, and use that to display a field; the
|
||||
// other lines are unspecified junk. However, in practice, we can
|
||||
// almost always double the vertical resolution of the output by
|
||||
// forcing progressive output: there's usually useful data in the
|
||||
// other field. One notable exception: the title screen teaser
|
||||
// videos in Metroid Prime don't render correctly using this hack.
|
||||
fbStride /= 2;
|
||||
fbHeight *= 2;
|
||||
if (m_VBlankTimingOdd.PRB < m_VBlankTimingEven.PRB)
|
||||
{
|
||||
xfbAddr = GetXFBAddressTop();
|
||||
}
|
||||
else
|
||||
{
|
||||
xfbAddr = GetXFBAddressBottom();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are displaying an interlaced field, we convert it to a progressive field
|
||||
// by simply using the whole XFB, which 99% of the time contains a whole progressive
|
||||
// frame.
|
||||
|
||||
// All known NTSC games use the top/odd field as the upper field but
|
||||
// PAL games are known to whimsically arrange the fields in either order.
|
||||
// So to work out which field is pointing to the top of the progressive XFB we check
|
||||
// which field has the lower PRB value in the VBlank Timing Registers.
|
||||
|
||||
if(m_VBlankTimingOdd.PRB < m_VBlankTimingEven.PRB)
|
||||
if (field == FieldType::FIELD_EVEN)
|
||||
{
|
||||
xfbAddr = GetXFBAddressTop();
|
||||
}
|
||||
else
|
||||
{
|
||||
xfbAddr = GetXFBAddressBottom();
|
||||
}
|
||||
}
|
||||
|
||||
static const char* const fieldTypeNames[] = { "Progressive", "Upper", "Lower" };
|
||||
static const char* const fieldTypeNames[] = { "Odd", "Even" };
|
||||
|
||||
DEBUG_LOG(VIDEOINTERFACE,
|
||||
"(VI->BeginField): Address: %.08X | WPL %u | STD %u | ACV %u | Field %s",
|
||||
xfbAddr, m_PictureConfiguration.WPL, m_PictureConfiguration.STD,
|
||||
m_VerticalTimingRegister.ACV, fieldTypeNames[field]);
|
||||
static const UVIVBlankTimingRegister *vert_timing[] = {
|
||||
&m_VBlankTimingOdd,
|
||||
&m_VBlankTimingEven,
|
||||
};
|
||||
|
||||
WARN_LOG(VIDEOINTERFACE,
|
||||
"(VI->BeginField): Address: %.08X | WPL %u | STD %u | EQ %u | PRB %u | ACV %u | PSB %u | Field %s",
|
||||
xfbAddr, m_PictureConfiguration.WPL, m_PictureConfiguration.STD, m_VerticalTimingRegister.EQU,
|
||||
vert_timing[field]->PRB, m_VerticalTimingRegister.ACV, vert_timing[field]->PSB, fieldTypeNames[field]);
|
||||
|
||||
WARN_LOG(VIDEOINTERFACE,
|
||||
"HorizScaling: %04x | fbwidth %d | %u | %u",
|
||||
m_HorizontalScaling.Hex, m_FBWidth.Hex, GetTicksPerEvenField(), GetTicksPerOddField());
|
||||
|
||||
if (xfbAddr)
|
||||
g_video_backend->Video_BeginField(xfbAddr, fbWidth, fbStride, fbHeight);
|
||||
|
@ -604,44 +605,41 @@ static void EndField()
|
|||
// Run when: When a frame is scanned (progressive/interlace)
|
||||
void Update()
|
||||
{
|
||||
if (m_DisplayControlRegister.NIN)
|
||||
if (s_half_line_count == s_even_field_first_hl)
|
||||
{
|
||||
// Progressive
|
||||
if (m_VBeamPos == 1)
|
||||
BeginField(FIELD_PROGRESSIVE);
|
||||
BeginField(FIELD_EVEN);
|
||||
}
|
||||
else if (m_VBeamPos == s_upperFieldBegin)
|
||||
else if (s_half_line_count == s_odd_field_first_hl)
|
||||
{
|
||||
// Interlace Upper
|
||||
BeginField(FIELD_UPPER);
|
||||
BeginField(FIELD_ODD);
|
||||
}
|
||||
else if (m_VBeamPos == s_lowerFieldBegin)
|
||||
else if (s_half_line_count == s_even_field_last_hl)
|
||||
{
|
||||
// Interlace Lower
|
||||
BeginField(FIELD_LOWER);
|
||||
}
|
||||
|
||||
if (m_VBeamPos == s_upperFieldBegin + m_VerticalTimingRegister.ACV)
|
||||
{
|
||||
// Interlace Upper.
|
||||
EndField();
|
||||
}
|
||||
else if (m_VBeamPos == s_lowerFieldBegin + m_VerticalTimingRegister.ACV)
|
||||
else if (s_half_line_count == s_odd_field_last_hl)
|
||||
{
|
||||
// Interlace Lower
|
||||
EndField();
|
||||
}
|
||||
|
||||
if (++m_VBeamPos > s_lineCount * fields)
|
||||
m_VBeamPos = 1;
|
||||
|
||||
for (UVIInterruptRegister& reg : m_InterruptRegister)
|
||||
{
|
||||
if (m_VBeamPos == reg.VCT)
|
||||
if (s_half_line_count == 2 * reg.VCT)
|
||||
{
|
||||
reg.IR_INT = 1;
|
||||
}
|
||||
}
|
||||
|
||||
s_half_line_count++;
|
||||
|
||||
if (s_half_line_count > s_odd_field_last_hl) {
|
||||
s_half_line_count = 1;
|
||||
}
|
||||
|
||||
if (s_half_line_count & 1) {
|
||||
s_ticks_last_line_start = CoreTiming::GetTicks();
|
||||
}
|
||||
|
||||
UpdateInterrupts();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,27 +11,6 @@ namespace MMIO { class Mapping; }
|
|||
|
||||
namespace VideoInterface
|
||||
{
|
||||
// NTSC is 60 FPS, right?
|
||||
// Wrong, it's about 59.94 FPS. The NTSC engineers had to slightly lower
|
||||
// the field rate from 60 FPS when they added color to the standard.
|
||||
// This was done to prevent analog interference between the video and
|
||||
// audio signals. PAL has no similar reduction; it is exactly 50 FPS.
|
||||
//#define NTSC_FIELD_RATE (60.0f / 1.001f)
|
||||
#define NTSC_FIELD_RATE 60
|
||||
#define NTSC_LINE_COUNT 525
|
||||
// These line numbers indicate the beginning of the "active video" in a frame.
|
||||
// An NTSC frame has the lower field first followed by the upper field.
|
||||
// TODO: Is this true for PAL-M? Is this true for PAL60?
|
||||
#define NTSC_LOWER_BEGIN 21
|
||||
#define NTSC_UPPER_BEGIN 283
|
||||
|
||||
//#define PAL_FIELD_RATE 50.0f
|
||||
#define PAL_FIELD_RATE 50
|
||||
#define PAL_LINE_COUNT 625
|
||||
// These line numbers indicate the beginning of the "active video" in a frame.
|
||||
// A PAL frame has the upper field first followed by the lower field.
|
||||
#define PAL_UPPER_BEGIN 23
|
||||
#define PAL_LOWER_BEGIN 336
|
||||
|
||||
// VI Internal Hardware Addresses
|
||||
enum
|
||||
|
@ -351,8 +330,8 @@ union UVIHorizontalStepping
|
|||
// Change values pertaining to video mode
|
||||
void UpdateParameters();
|
||||
|
||||
unsigned int GetTicksPerLine();
|
||||
unsigned int GetTicksPerFrame();
|
||||
u32 GetTicksPerHalfLine();
|
||||
u32 GetTicksPerField();
|
||||
|
||||
//For VI Scaling and Aspect Ratio Correction
|
||||
float GetAspectRatio(bool);
|
||||
|
|
|
@ -66,7 +66,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||
static std::thread g_save_thread;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
static const u32 STATE_VERSION = 45; // Last changed in PR 2846
|
||||
static const u32 STATE_VERSION = 46; // Last changed in PR 2686
|
||||
|
||||
// Maps savestate versions to Dolphin versions.
|
||||
// Versions after 42 don't need to be added to this list,
|
||||
|
|
|
@ -15,9 +15,8 @@ namespace MMIO { class Mapping; }
|
|||
|
||||
enum FieldType
|
||||
{
|
||||
FIELD_PROGRESSIVE = 0,
|
||||
FIELD_UPPER,
|
||||
FIELD_LOWER
|
||||
FIELD_ODD = 0,
|
||||
FIELD_EVEN = 1,
|
||||
};
|
||||
|
||||
enum EFBAccessType
|
||||
|
|
|
@ -99,6 +99,7 @@ void VideoConfig::Load(const std::string& ini_file)
|
|||
IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
|
||||
hacks->Get("EFBAccessEnable", &bEFBAccessEnable, true);
|
||||
hacks->Get("BBoxEnable", &bBBoxEnable, false);
|
||||
hacks->Get("ForceProgressive", &bForceProgressive, true);
|
||||
hacks->Get("EFBToTextureEnable", &bSkipEFBCopyToRam, true);
|
||||
hacks->Get("EFBScaledCopy", &bCopyEFBScaled, true);
|
||||
hacks->Get("EFBEmulateFormatChanges", &bEFBEmulateFormatChanges, false);
|
||||
|
@ -204,6 +205,7 @@ void VideoConfig::GameIniLoad()
|
|||
|
||||
CHECK_SETTING("Video_Hacks", "EFBAccessEnable", bEFBAccessEnable);
|
||||
CHECK_SETTING("Video_Hacks", "BBoxEnable", bBBoxEnable);
|
||||
CHECK_SETTING("Video_Hacks", "ForceProgressive", bForceProgressive);
|
||||
CHECK_SETTING("Video_Hacks", "EFBToTextureEnable", bSkipEFBCopyToRam);
|
||||
CHECK_SETTING("Video_Hacks", "EFBScaledCopy", bCopyEFBScaled);
|
||||
CHECK_SETTING("Video_Hacks", "EFBEmulateFormatChanges", bEFBEmulateFormatChanges);
|
||||
|
@ -293,6 +295,7 @@ void VideoConfig::Save(const std::string& ini_file)
|
|||
IniFile::Section* hacks = iniFile.GetOrCreateSection("Hacks");
|
||||
hacks->Set("EFBAccessEnable", bEFBAccessEnable);
|
||||
hacks->Set("BBoxEnable", bBBoxEnable);
|
||||
hacks->Set("ForceProgressive", bForceProgressive);
|
||||
hacks->Set("EFBToTextureEnable", bSkipEFBCopyToRam);
|
||||
hacks->Set("EFBScaledCopy", bCopyEFBScaled);
|
||||
hacks->Set("EFBEmulateFormatChanges", bEFBEmulateFormatChanges);
|
||||
|
|
|
@ -112,6 +112,7 @@ struct VideoConfig final
|
|||
bool bEFBAccessEnable;
|
||||
bool bPerfQueriesEnable;
|
||||
bool bBBoxEnable;
|
||||
bool bForceProgressive;
|
||||
|
||||
bool bEFBEmulateFormatChanges;
|
||||
bool bSkipEFBCopyToRam;
|
||||
|
|
Loading…
Reference in New Issue