Better TV emulation. Seems to fix problem in 50 Hz PAL games where frames go backwards.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3811 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Nolan Check 2009-07-15 22:20:59 +00:00
parent a73dd21ee9
commit fdc2b69143
7 changed files with 146 additions and 60 deletions

View File

@ -25,6 +25,7 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo
Video_Prepare = 0;
Video_SendFifoData = 0;
Video_BeginField = 0;
Video_EndField = 0;
Video_EnterLoop = 0;
Video_ExitLoop = 0;
Video_Screenshot = 0;
@ -37,6 +38,8 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo
(LoadSymbol("Video_SendFifoData"));
Video_BeginField = reinterpret_cast<TVideo_BeginField>
(LoadSymbol("Video_BeginField"));
Video_EndField = reinterpret_cast<TVideo_EndField>
(LoadSymbol("Video_EndField"));
Video_Screenshot = reinterpret_cast<TVideo_Screenshot>
(LoadSymbol("Video_Screenshot"));
Video_EnterLoop = reinterpret_cast<TVideo_EnterLoop>
@ -51,6 +54,7 @@ PluginVideo::PluginVideo(const char *_Filename) : CPlugin(_Filename), validVideo
if ((Video_Prepare != 0) &&
(Video_SendFifoData != 0) &&
(Video_BeginField != 0) &&
(Video_EndField != 0) &&
(Video_EnterLoop != 0) &&
(Video_ExitLoop != 0) &&
(Video_Screenshot != 0) &&

View File

@ -26,6 +26,7 @@ namespace Common {
typedef void (__cdecl* TVideo_Prepare)();
typedef void (__cdecl* TVideo_SendFifoData)(u8*,u32);
typedef void (__cdecl* TVideo_BeginField)(u32, FieldType, u32, u32);
typedef void (__cdecl* TVideo_EndField)();
typedef bool (__cdecl* TVideo_Screenshot)(const char* filename);
typedef void (__cdecl* TVideo_EnterLoop)();
typedef void (__cdecl* TVideo_ExitLoop)();
@ -44,6 +45,7 @@ public:
TVideo_EnterLoop Video_EnterLoop;
TVideo_ExitLoop Video_ExitLoop;
TVideo_BeginField Video_BeginField;
TVideo_EndField Video_EndField;
TVideo_AccessEFB Video_AccessEFB;
TVideo_AddMessage Video_AddMessage;

View File

@ -334,12 +334,15 @@ static UVIBorderBlankRegister m_BorderHBlank;
static u32 TicksPerFrame = 0;
static u32 LineCount = 0;
static u32 LinesPerField = 0;
static u32 NextXFBRender = 0;
int TargetRefreshRate = 0;
static u32 s_lineCount = 0;
static u32 s_upperFieldBegin = 0;
static u32 s_upperFieldEnd = 0;
static u32 s_lowerFieldBegin = 0;
static u32 s_lowerFieldEnd = 0;
double TargetRefreshRate = 0.0;
double ActualRefreshRate = 0.0;
s64 SyncTicksProgress = 0;
float ActualRefreshRate = 0.0;
void DoState(PointerWrap &p)
{
@ -369,15 +372,21 @@ void DoState(PointerWrap &p)
p.Do(m_BorderHBlank);
p.Do(TicksPerFrame);
p.Do(LineCount);
p.Do(LinesPerField);
p.Do(NextXFBRender);
p.Do(s_lineCount);
p.Do(s_upperFieldBegin);
p.Do(s_upperFieldEnd);
p.Do(s_lowerFieldBegin);
p.Do(s_lowerFieldEnd);
}
void PreInit(bool _bNTSC)
{
TicksPerFrame = 0;
LineCount = 0;
s_lineCount = 0;
s_upperFieldBegin = 0;
s_upperFieldEnd = 0;
s_lowerFieldBegin = 0;
s_lowerFieldEnd = 0;
m_VerticalTimingRegister.EQU = 6;
@ -437,7 +446,11 @@ void Init()
m_DisplayControlRegister.Hex = 0;
NextXFBRender = 1;
s_lineCount = 0;
s_upperFieldBegin = 0;
s_upperFieldEnd = 0;
s_lowerFieldBegin = 0;
s_lowerFieldEnd = 0;
}
void Read8(u8& _uReturnValue, const u32 _iAddress)
@ -1005,23 +1018,62 @@ u32 GetXFBAddressBottom()
}
// 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.
const double NTSC_FIELD_RATE = 60.0 / 1.001;
const u32 NTSC_LINE_COUNT = 525;
// An NTSC frame has the lower field first followed by the upper field.
// TODO: Is this true for PAL-M? Is this true for EURGB60?
const u32 NTSC_LOWER_BEGIN = 21;
const u32 NTSC_LOWER_END = 263;
const u32 NTSC_UPPER_BEGIN = 283;
const u32 NTSC_UPPER_END = 525;
const double PAL_FIELD_RATE = 50.0;
const u32 PAL_LINE_COUNT = 625;
// A PAL frame has the upper field first followed by the lower field.
const u32 PAL_UPPER_BEGIN = 23; // TODO: Actually 23.5!
const u32 PAL_UPPER_END = 310;
const u32 PAL_LOWER_BEGIN = 336;
const u32 PAL_LOWER_END = 623; // TODO: Actually 623.5!
// Screenshot and screen message
void UpdateTiming()
{
switch (m_DisplayControlRegister.FMT)
{
case 0: // NTSC
case 2: // MPAL
TicksPerFrame = SystemTimers::GetTicksPerSecond() / 30;
LineCount = m_DisplayControlRegister.NIN ? 263 : 525;
LinesPerField = 263;
case 2: // PAL-M
TicksPerFrame = (u32)(SystemTimers::GetTicksPerSecond() / (NTSC_FIELD_RATE / 2.0));
s_lineCount = m_DisplayControlRegister.NIN ? (NTSC_LINE_COUNT+1)/2 : NTSC_LINE_COUNT;
// TODO: The game may have some control over these parameters (not that it's useful).
s_upperFieldBegin = NTSC_UPPER_BEGIN;
s_upperFieldEnd = NTSC_UPPER_END;
s_lowerFieldBegin = NTSC_LOWER_BEGIN;
s_lowerFieldEnd = NTSC_LOWER_END;
break;
case 1: // PAL
TicksPerFrame = SystemTimers::GetTicksPerSecond() / 25;
LineCount = m_DisplayControlRegister.NIN ? 313 : 625;
LinesPerField = 313;
TicksPerFrame = (u32)(SystemTimers::GetTicksPerSecond() / (PAL_FIELD_RATE / 2.0));
s_lineCount = m_DisplayControlRegister.NIN ? (PAL_LINE_COUNT+1)/2 : PAL_LINE_COUNT;
s_upperFieldBegin = PAL_UPPER_BEGIN;
s_upperFieldEnd = PAL_UPPER_END;
s_lowerFieldBegin = PAL_LOWER_BEGIN;
s_lowerFieldEnd = PAL_LOWER_END;
break;
case 3: // Debug
@ -1031,17 +1083,18 @@ void UpdateTiming()
default:
PanicAlert("Unknown Video Format - CVideoInterface");
break;
}
}
int getTicksPerLine() {
if (LineCount == 0)
if (s_lineCount == 0)
return 100000;
return TicksPerFrame / LineCount;
return TicksPerFrame / s_lineCount;
}
static void BeginField(u32 xfbAddr, FieldType field)
static void BeginField(FieldType field)
{
static const char* const fieldTypeNames[] = { "Progressive", "Upper", "Lower" };
DEBUG_LOG(VIDEOINTERFACE, "(VI->BeginField): addr: %.08X | FieldSteps %u | FbSteps %u | ACV %u | Field %s",
@ -1052,6 +1105,12 @@ static void BeginField(u32 xfbAddr, FieldType field)
u32 fbWidth = m_HorizontalStepping.FieldSteps * 16;
u32 fbHeight = (m_HorizontalStepping.FbSteps / m_HorizontalStepping.FieldSteps) * m_VerticalTimingRegister.ACV;
// TODO: Are the "Bottom Field" and "Top Field" registers actually more
// like "First Field" and "Second Field" registers? There's an important
// difference because NTSC and PAL have opposite field orders.
u32 xfbAddr = (field == FIELD_LOWER) ? GetXFBAddressBottom() : GetXFBAddressTop();
Common::PluginVideo* video = CPluginManager::GetInstance().GetVideo();
if (xfbAddr && video->IsValid())
{
@ -1059,6 +1118,15 @@ static void BeginField(u32 xfbAddr, FieldType field)
}
}
static void EndField()
{
Common::PluginVideo* video = CPluginManager::GetInstance().GetVideo();
if (video->IsValid())
{
video->Video_EndField();
}
}
// Purpose 1: Send VI interrupt for every screen refresh
// Purpose 2: Execute XFB copy in homebrew games
@ -1067,7 +1135,7 @@ void Update()
{
// Update the target refresh rate
TargetRefreshRate = (m_DisplayControlRegister.FMT == 0 || m_DisplayControlRegister.FMT == 2)
? 60 : 50;
? NTSC_FIELD_RATE : PAL_FIELD_RATE;
// Calculate actual refresh rate
static u64 LastTick = 0;
@ -1083,36 +1151,12 @@ void Update()
// rather than 50 and 60)
// TODO : Feed the FPS estimate into Iulius' framelimiter.
ActualRefreshRate = ((float)SyncTicksProgress / (float)TicksPerFrame) * 2.0;
ActualRefreshRate = ((double)SyncTicksProgress / (double)TicksPerFrame) * 2.0;
LastTick = CoreTiming::GetTicks();
SyncTicksProgress = 0;
}
m_VBeamPos++;
if (m_VBeamPos > LineCount)
{
m_VBeamPos = 1;
}
if (m_VBeamPos == NextXFBRender)
{
if (NextXFBRender == 1)
{
NextXFBRender = LinesPerField;
// TODO: proper VI regs typedef and logic for XFB to work.
// eg. Animal Crossing gc have smth in TFBL.XOF bitfield.
// "XOF - Horizontal Offset of the left-most pixel within the first word of the fetched picture."
u32 xfbAddr = GetXFBAddressTop();
BeginField(xfbAddr, m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_UPPER);
}
else
{
NextXFBRender = 1;
// Previously checked m_XFBInfoTop.POFF then used m_XFBInfoBottom.FBB, try reverting if there are problems
u32 xfbAddr = GetXFBAddressBottom();
BeginField(xfbAddr, m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_LOWER);
}
}
// TODO: What's the correct behavior for progressive mode?
for (int i = 0; i < 4; ++i)
{
@ -1122,6 +1166,22 @@ void Update()
UpdateInterrupts();
}
}
if (m_VBeamPos == s_upperFieldBegin)
BeginField(m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_UPPER);
if (m_VBeamPos == s_upperFieldEnd)
EndField();
if (m_VBeamPos == s_lowerFieldBegin)
BeginField(m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_LOWER);
if (m_VBeamPos == s_lowerFieldEnd)
EndField();
m_VBeamPos++;
if (m_VBeamPos > s_lineCount)
m_VBeamPos = 1;
}
} // namespace

View File

@ -54,8 +54,8 @@ namespace VideoInterface
void Update();
// urgh, ugly externs.
extern float ActualRefreshRate;
extern int TargetRefreshRate;
extern double ActualRefreshRate;
extern double TargetRefreshRate;
extern s64 SyncTicksProgress;
// UpdateInterrupts: check if we have to generate a new VI Interrupt

View File

@ -117,14 +117,23 @@ EXPORT void CALL Video_SendFifoData(u8* _uData, u32 len);
// __________________________________________________________________________________________________
// Function: Video_BeginField
// Purpose: When a vertical blank occurs in the VI emulator, this function tells the video plugin
// what the parameters of the upcoming field are. The video plugin should make sure the
// previous field is on the player's display before returning.
// input: vi parameters of the next field
// Purpose: When a field begins in the VI emulator, this function tells the video plugin what the
// parameters of the upcoming field are. The video plugin should make sure the previous
// field is on the player's display before returning.
// input: vi parameters of the upcoming field
// output: none
//
EXPORT void CALL Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight);
// __________________________________________________________________________________________________
// Function: Video_EndField
// Purpose: When a field ends in the VI emulator, this function notifies the video plugin. The video
// has permission to swap the field to the player's display.
// input: none
// output: none
//
EXPORT void CALL Video_EndField();
// __________________________________________________________________________________________________
// Function: Video_AccessEFB
// input: type of access (r/w, z/color, ...), x coord, y coord

View File

@ -285,6 +285,10 @@ void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
D3D::BeginFrame();*/
}
void Video_EndField()
{
}
void Video_AddMessage(const char* pstr, u32 milliseconds)
{
Renderer::AddMessage(pstr,milliseconds);

View File

@ -495,21 +495,28 @@ void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
{
if (s_PluginInitialized)
{
if (s_swapRequested)
{
if (g_VideoInitialize.bUseDualCore)
s_swapResponseEvent.MsgWait();
else
VideoFifo_CheckSwapRequest();
}
s_beginFieldArgs.xfbAddr = xfbAddr;
s_beginFieldArgs.field = field;
s_beginFieldArgs.fbWidth = fbWidth;
s_beginFieldArgs.fbHeight = fbHeight;
Common::AtomicStoreRelease(s_swapRequested, TRUE);
}
}
// Run from the CPU thread (from VideoInterface.cpp)
void Video_EndField()
{
Common::AtomicStoreRelease(s_swapRequested, TRUE);
}
static volatile struct
{
EFBAccessType type;