forked from dolphin-emu/dolphin
1. This should fix Issue 1625 (Bizarre Auto Frame Limit)
Now the frame limiter yields on CPU thread, not as before on GPU thread mistakenly 2. Fixed clear of VI interrupts I guess VI interrupts are not used at all, because they were never cleared before 3. Made GPU thread 0% processor usage when paused whatever your active config is. I tried the event approach but somehow the thread resume latency is excessively long (Who can tell me why?) git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4790 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
@@ -78,7 +78,7 @@ void Timer::Update()
|
||||
// -------------------------------------
|
||||
|
||||
// Get the number of milliseconds since the last Update()
|
||||
s64 Timer::GetTimeDifference()
|
||||
u64 Timer::GetTimeDifference()
|
||||
{
|
||||
return(timeGetTime() - m_LastTime);
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ public:
|
||||
void Update();
|
||||
|
||||
// The time difference is always returned in milliseconds, regardless of alternative internal representation
|
||||
s64 GetTimeDifference();
|
||||
u64 GetTimeDifference();
|
||||
void AddTimeDifference();
|
||||
void WindBackStartingTime(u64 WindBack);
|
||||
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#endif
|
||||
|
||||
#include "Setup.h" // Common
|
||||
#include "Atomic.h"
|
||||
#include "Thread.h"
|
||||
#include "Timer.h"
|
||||
#include "Common.h"
|
||||
@@ -64,11 +65,9 @@
|
||||
namespace Core
|
||||
{
|
||||
|
||||
|
||||
// Declarations and definitions
|
||||
Common::Timer Timer;
|
||||
u32 frames = 0;
|
||||
|
||||
volatile u32 DrawnFrame = 0;
|
||||
|
||||
// Function forwarding
|
||||
//void Callback_VideoRequestWindowSize(int _iWidth, int _iHeight, BOOL _bFullscreen);
|
||||
@@ -588,101 +587,28 @@ void ScreenShot()
|
||||
ScreenShot(GenerateScreenshotName());
|
||||
}
|
||||
|
||||
// --- Callbacks for plugins / engine ---
|
||||
|
||||
|
||||
// Callback_VideoLog
|
||||
// WARNING - THIS IS EXECUTED FROM VIDEO THREAD
|
||||
void Callback_VideoLog(const TCHAR *_szMessage, int _bDoBreak)
|
||||
// Apply Frame Limit and Display FPS info
|
||||
// This should only be called from VI
|
||||
void FrameThrottle()
|
||||
{
|
||||
INFO_LOG(VIDEO, _szMessage);
|
||||
}
|
||||
u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1) ? SConfig::GetInstance().m_Framelimit * 5
|
||||
: VideoInterface::TargetRefreshRate;
|
||||
u32 frames = Common::AtomicLoad(DrawnFrame);
|
||||
|
||||
// reports if a frame should be skipped or not
|
||||
// depending on the framelimit set
|
||||
bool report_slow(int skipped)
|
||||
{
|
||||
u32 targetfps = SConfig::GetInstance().m_Framelimit * 5;
|
||||
double wait_frametime;
|
||||
|
||||
if (targetfps < 5)
|
||||
wait_frametime = (1000.0 / VideoInterface::TargetRefreshRate);
|
||||
else
|
||||
wait_frametime = (1000.0 / targetfps);
|
||||
|
||||
bool fps_slow;
|
||||
|
||||
if (Timer.GetTimeDifference() < wait_frametime * (frames + skipped))
|
||||
fps_slow=false;
|
||||
else
|
||||
fps_slow=true;
|
||||
|
||||
if (targetfps == 5)
|
||||
fps_slow=true;
|
||||
|
||||
return fps_slow;
|
||||
}
|
||||
|
||||
// Callback_VideoCopiedToXFB
|
||||
// WARNING - THIS IS EXECUTED FROM VIDEO THREAD
|
||||
// We do not write to anything outside this function here
|
||||
void Callback_VideoCopiedToXFB(bool video_update)
|
||||
{
|
||||
if(!video_update)
|
||||
Frame::FrameUpdate();
|
||||
|
||||
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
||||
//count FPS and VPS
|
||||
static u32 videoupd = 0;
|
||||
static u32 no_framelimit = 0;
|
||||
|
||||
|
||||
if (video_update)
|
||||
videoupd++;
|
||||
else
|
||||
frames++;
|
||||
|
||||
if (no_framelimit>0)
|
||||
no_framelimit--;
|
||||
|
||||
// Custom frame limiter
|
||||
// --------------------
|
||||
u32 targetfps = SConfig::GetInstance().m_Framelimit * 5;
|
||||
|
||||
if (targetfps > 5)
|
||||
// When frame limit is NOT off
|
||||
if (frames && SConfig::GetInstance().m_Framelimit != 1)
|
||||
{
|
||||
double wait_frametime = (1000.0 / targetfps);
|
||||
|
||||
if (Timer.GetTimeDifference() >= wait_frametime * frames)
|
||||
no_framelimit = (u32)Timer.GetTimeDifference();
|
||||
|
||||
while (Timer.GetTimeDifference() < wait_frametime * frames)
|
||||
{
|
||||
if (no_framelimit == 0)
|
||||
Common::SleepCurrentThread(1);
|
||||
}
|
||||
}
|
||||
else if (targetfps < 5)
|
||||
{
|
||||
double wait_frametime = (1000.0 / VideoInterface::TargetRefreshRate);
|
||||
|
||||
if (Timer.GetTimeDifference() >= wait_frametime * frames)
|
||||
no_framelimit = (u32)Timer.GetTimeDifference();
|
||||
|
||||
while (Timer.GetTimeDifference() < wait_frametime * videoupd)
|
||||
{
|
||||
// TODO : This is wrong, the sleep shouldn't be there but rather in cputhread
|
||||
// as it's not based on the fps but on the refresh rate...
|
||||
if (no_framelimit == 0)
|
||||
Common::SleepCurrentThread(1);
|
||||
}
|
||||
u32 frametime = frames * 1000 / TargetFPS;
|
||||
while (Timer.GetTimeDifference() < frametime)
|
||||
//Common::YieldCPU();
|
||||
Common::SleepCurrentThread(1);
|
||||
}
|
||||
|
||||
if (Timer.GetTimeDifference() >= 1000)
|
||||
// Update info per second
|
||||
u32 ElapseTime = (u32)Timer.GetTimeDifference();
|
||||
if (ElapseTime >= 1000)
|
||||
{
|
||||
// Time passed
|
||||
float t = (float)(Timer.GetTimeDifference()) / 1000.f;
|
||||
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
||||
// Use extended or summary information. The summary information does not print the ticks data,
|
||||
// that's more of a debugging interest, it can always be optional of course if someone is interested.
|
||||
@@ -691,8 +617,8 @@ void Callback_VideoCopiedToXFB(bool video_update)
|
||||
u64 newTicks = CoreTiming::GetTicks();
|
||||
u64 newIdleTicks = CoreTiming::GetIdleTicks();
|
||||
|
||||
s64 diff = (newTicks - ticks) / 1000000;
|
||||
s64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
||||
u64 diff = (newTicks - ticks) / 1000000;
|
||||
u64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
||||
|
||||
ticks = newTicks;
|
||||
idleTicks = newIdleTicks;
|
||||
@@ -700,13 +626,9 @@ void Callback_VideoCopiedToXFB(bool video_update)
|
||||
float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100;
|
||||
#endif
|
||||
|
||||
float FPS = (float)frames / t;
|
||||
// for some reasons "VideoInterface::ActualRefreshRate" gives some odd results :(
|
||||
float VPS = (float)videoupd / t;
|
||||
u32 FPS = frames * 1000 / ElapseTime;
|
||||
|
||||
int TargetVPS = (int)(VideoInterface::TargetRefreshRate + 0.5);
|
||||
|
||||
float Speed = ((VPS > 0.0f ? VPS : VideoInterface::ActualRefreshRate) / TargetVPS) * 100.0f;
|
||||
float Speed = (float)FPS / (float)TargetFPS * 100.0f;
|
||||
|
||||
// Settings are shown the same for both extended and summary info
|
||||
std::string SSettings = StringFromFormat(" | Core: %s %s",
|
||||
@@ -726,8 +648,7 @@ void Callback_VideoCopiedToXFB(bool video_update)
|
||||
_CoreParameter.bCPUThread ? "DC" : "SC");
|
||||
|
||||
#ifdef EXTENDED_INFO
|
||||
std::string SFPS = StringFromFormat("FPS: %4.1f - VPS: %i/%i (%3.0f%%)",
|
||||
FPS, VPS > 0 ? (int)VPS : (int)VideoInterface::ActualRefreshRate, TargetVPS, Speed);
|
||||
std::string SFPS = StringFromFormat("FPS: %i/%i (%3.0f%%)", FPS, TargetFPS, Speed);
|
||||
SFPS += StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)",
|
||||
_CoreParameter.bSkipIdle ? "~" : "",
|
||||
(int)(diff),
|
||||
@@ -738,8 +659,7 @@ void Callback_VideoCopiedToXFB(bool video_update)
|
||||
TicksPercentage);
|
||||
|
||||
#else // Summary information
|
||||
std::string SFPS = StringFromFormat("FPS: %4.1f - VPS: %i/%i (%3.0f%%)",
|
||||
FPS, VPS > 0 ? (int)VPS : (int)VideoInterface::ActualRefreshRate, TargetVPS, Speed);
|
||||
std::string SFPS = StringFromFormat("FPS: %i/%i (%3.0f%%)", FPS, TargetFPS, Speed);
|
||||
#endif
|
||||
|
||||
// This is our final "frame counter" string
|
||||
@@ -752,12 +672,39 @@ void Callback_VideoCopiedToXFB(bool video_update)
|
||||
Host_UpdateStatusBar(SMessage.c_str());
|
||||
|
||||
// Reset frame counter
|
||||
frames = 0;
|
||||
videoupd = 0;
|
||||
Timer.Update();
|
||||
Common::AtomicStore(DrawnFrame, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Executed from GPU thread
|
||||
// reports if a frame should be skipped or not
|
||||
// depending on the framelimit set
|
||||
bool report_slow(int skipped)
|
||||
{
|
||||
u32 TargetFPS = (SConfig::GetInstance().m_Framelimit > 1) ? SConfig::GetInstance().m_Framelimit * 5
|
||||
: VideoInterface::TargetRefreshRate;
|
||||
u32 frames = Common::AtomicLoad(DrawnFrame);
|
||||
bool fps_slow = (Timer.GetTimeDifference() < (frames + skipped) * 1000 / TargetFPS) ? false : true;
|
||||
|
||||
return fps_slow;
|
||||
}
|
||||
|
||||
// --- Callbacks for plugins / engine ---
|
||||
|
||||
// Callback_VideoLog
|
||||
// WARNING - THIS IS EXECUTED FROM VIDEO THREAD
|
||||
void Callback_VideoLog(const TCHAR *_szMessage, int _bDoBreak)
|
||||
{
|
||||
INFO_LOG(VIDEO, _szMessage);
|
||||
}
|
||||
|
||||
// Should be called from GPU thread when a frame is drawn
|
||||
void Callback_VideoCopiedToXFB(bool video_update)
|
||||
{
|
||||
Common::AtomicIncrement(DrawnFrame);
|
||||
Frame::FrameUpdate();
|
||||
}
|
||||
|
||||
// Callback_DSPLog
|
||||
// WARNING - THIS MAY BE EXECUTED FROM DSP THREAD
|
||||
@@ -835,7 +782,8 @@ void Callback_WiimoteLog(const TCHAR* _szMessage, int _v)
|
||||
}
|
||||
|
||||
// TODO: Get rid of at some point
|
||||
const SCoreStartupParameter& GetStartupParameter() {
|
||||
const SCoreStartupParameter& GetStartupParameter()
|
||||
{
|
||||
return SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
}
|
||||
|
||||
|
@@ -74,6 +74,7 @@ namespace Core
|
||||
void StopTrace();
|
||||
|
||||
bool report_slow(int skipped);
|
||||
void FrameThrottle();
|
||||
|
||||
// -----------------------------------------
|
||||
#ifdef RERECORDING
|
||||
|
@@ -19,8 +19,7 @@
|
||||
#include "ChunkFile.h"
|
||||
|
||||
#include "../PowerPC/PowerPC.h"
|
||||
|
||||
#include "../Core.h" // <- for Core::GetStartupParameter().bUseDualCore
|
||||
#include "../Core.h"
|
||||
#include "ProcessorInterface.h"
|
||||
#include "VideoInterface.h"
|
||||
#include "Memmap.h"
|
||||
@@ -28,7 +27,6 @@
|
||||
#include "../CoreTiming.h"
|
||||
#include "../HW/SystemTimers.h"
|
||||
#include "StringUtil.h"
|
||||
#include "Timer.h"
|
||||
|
||||
namespace VideoInterface
|
||||
{
|
||||
@@ -53,7 +51,7 @@ enum
|
||||
VI_FB_LEFT_TOP_LO = 0x1e,
|
||||
VI_FB_RIGHT_TOP_HI = 0x20, // FB_RIGHT_TOP is only used in 3D mode
|
||||
VI_FB_RIGHT_TOP_LO = 0x22,
|
||||
VI_FB_LEFT_BOTTOM_HI = 0x24, // FB_LEFT_TOP is second half of XFB info
|
||||
VI_FB_LEFT_BOTTOM_HI = 0x24, // FB_LEFT_BOTTOM is second half of XFB info
|
||||
VI_FB_LEFT_BOTTOM_LO = 0x26,
|
||||
VI_FB_RIGHT_BOTTOM_HI = 0x28, // FB_RIGHT_BOTTOM is only used in 3D mode
|
||||
VI_FB_RIGHT_BOTTOM_LO = 0x2a,
|
||||
@@ -268,6 +266,7 @@ union UVIFilterCoefTable3
|
||||
unsigned : 2;
|
||||
};
|
||||
};
|
||||
|
||||
// Used for tables 3-6
|
||||
union UVIFilterCoefTable4
|
||||
{
|
||||
@@ -281,6 +280,7 @@ union UVIFilterCoefTable4
|
||||
unsigned Tap3 : 8;
|
||||
};
|
||||
};
|
||||
|
||||
struct SVIFilterCoefTables
|
||||
{
|
||||
UVIFilterCoefTable3 Tables02[3];
|
||||
@@ -331,16 +331,13 @@ static UVIBorderBlankRegister m_BorderHBlank;
|
||||
// 0xcc002076 - 0xcc00207f is full of 0x00FF: unknown
|
||||
// 0xcc002080 - 0xcc002100 even more unknown
|
||||
|
||||
u32 TargetRefreshRate = 0;
|
||||
|
||||
static u32 TicksPerFrame = 0;
|
||||
static u32 s_lineCount = 0;
|
||||
static u32 s_upperFieldBegin = 0;
|
||||
static u32 s_lowerFieldBegin = 0;
|
||||
|
||||
float TargetRefreshRate = 0.0;
|
||||
float ActualRefreshRate = 0.0;
|
||||
s64 SyncTicksProgress = 0;
|
||||
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
p.Do(m_VerticalTimingRegister);
|
||||
@@ -367,7 +364,7 @@ void DoState(PointerWrap &p)
|
||||
p.Do(m_DTVStatus);
|
||||
p.Do(m_FBWidth);
|
||||
p.Do(m_BorderHBlank);
|
||||
|
||||
p.Do(TargetRefreshRate);
|
||||
p.Do(TicksPerFrame);
|
||||
p.Do(s_lineCount);
|
||||
p.Do(s_upperFieldBegin);
|
||||
@@ -376,11 +373,6 @@ void DoState(PointerWrap &p)
|
||||
|
||||
void PreInit(bool _bNTSC)
|
||||
{
|
||||
TicksPerFrame = 0;
|
||||
s_lineCount = 0;
|
||||
s_upperFieldBegin = 0;
|
||||
s_lowerFieldBegin = 0;
|
||||
|
||||
m_VerticalTimingRegister.EQU = 6;
|
||||
|
||||
m_DisplayControlRegister.ENB = 1;
|
||||
@@ -439,9 +431,7 @@ void Init()
|
||||
|
||||
m_DisplayControlRegister.Hex = 0;
|
||||
|
||||
s_lineCount = 0;
|
||||
s_upperFieldBegin = 0;
|
||||
s_lowerFieldBegin = 0;
|
||||
UpdateTiming();
|
||||
}
|
||||
|
||||
void Read8(u8& _uReturnValue, const u32 _iAddress)
|
||||
@@ -711,9 +701,12 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
||||
{
|
||||
// shuffle2 clear all data, reset to default vals, and enter idle mode
|
||||
m_DisplayControlRegister.RST = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
m_InterruptRegister[i].Hex = 0;
|
||||
UpdateInterrupts();
|
||||
}
|
||||
|
||||
UpdateTiming();
|
||||
UpdateTiming();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -802,38 +795,38 @@ void Write16(const u16 _iValue, const u32 _iAddress)
|
||||
// RETRACE STUFF ...
|
||||
case VI_PRERETRACE_HI:
|
||||
m_InterruptRegister[0].Hi = _iValue;
|
||||
m_InterruptRegister[0].IR_INT = 0;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_PRERETRACE_LO:
|
||||
m_InterruptRegister[0].Lo = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
|
||||
case VI_POSTRETRACE_HI:
|
||||
m_InterruptRegister[1].Hi = _iValue;
|
||||
m_InterruptRegister[1].IR_INT = 0;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_POSTRETRACE_LO:
|
||||
m_InterruptRegister[1].Lo = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_INTERRUPT_2_HI:
|
||||
m_InterruptRegister[2].Hi = _iValue;
|
||||
m_InterruptRegister[2].IR_INT = 0;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_DISPLAY_INTERRUPT_2_LO:
|
||||
m_InterruptRegister[2].Lo = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_INTERRUPT_3_HI:
|
||||
m_InterruptRegister[3].Hi = _iValue;
|
||||
m_InterruptRegister[3].IR_INT = 0;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
case VI_DISPLAY_INTERRUPT_3_LO:
|
||||
m_InterruptRegister[3].Lo = _iValue;
|
||||
UpdateInterrupts();
|
||||
break;
|
||||
|
||||
case VI_DISPLAY_LATCH_0_HI:
|
||||
@@ -988,46 +981,22 @@ u32 GetXFBAddressBottom()
|
||||
return m_XFBInfoBottom.FBB;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
// 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 EURGB60?
|
||||
const u32 NTSC_LOWER_BEGIN = 21;
|
||||
const u32 NTSC_UPPER_BEGIN = 283;
|
||||
|
||||
const double PAL_FIELD_RATE = 50.0;
|
||||
const u32 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.
|
||||
const u32 PAL_UPPER_BEGIN = 23; // TODO: Actually 23.5!
|
||||
const u32 PAL_LOWER_BEGIN = 336;
|
||||
|
||||
// Screenshot and screen message
|
||||
void UpdateTiming()
|
||||
{
|
||||
switch (m_DisplayControlRegister.FMT)
|
||||
{
|
||||
|
||||
case 0: // NTSC
|
||||
case 2: // PAL-M
|
||||
|
||||
TicksPerFrame = (u32)(SystemTimers::GetTicksPerSecond() / (NTSC_FIELD_RATE / 2.0));
|
||||
TargetRefreshRate = NTSC_FIELD_RATE;
|
||||
TicksPerFrame = SystemTimers::GetTicksPerSecond() / (NTSC_FIELD_RATE / 2);
|
||||
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_lowerFieldBegin = NTSC_LOWER_BEGIN;
|
||||
break;
|
||||
|
||||
case 1: // PAL
|
||||
|
||||
TicksPerFrame = (u32)(SystemTimers::GetTicksPerSecond() / (PAL_FIELD_RATE / 2.0));
|
||||
TargetRefreshRate = PAL_FIELD_RATE;
|
||||
TicksPerFrame = SystemTimers::GetTicksPerSecond() / (PAL_FIELD_RATE / 2);
|
||||
s_lineCount = m_DisplayControlRegister.NIN ? (PAL_LINE_COUNT+1)/2 : PAL_LINE_COUNT;
|
||||
s_upperFieldBegin = PAL_UPPER_BEGIN;
|
||||
s_lowerFieldBegin = PAL_LOWER_BEGIN;
|
||||
@@ -1040,7 +1009,6 @@ void UpdateTiming()
|
||||
default:
|
||||
PanicAlert("Unknown Video Format - CVideoInterface");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1056,7 +1024,6 @@ int GetTicksPerFrame()
|
||||
return TicksPerFrame;
|
||||
}
|
||||
|
||||
|
||||
static void BeginField(FieldType field)
|
||||
{
|
||||
u32 fbWidth = m_HorizontalStepping.FieldSteps * 16;
|
||||
@@ -1088,45 +1055,24 @@ static void EndField()
|
||||
}
|
||||
|
||||
|
||||
// Purpose 1: Send VI interrupt for every screen refresh
|
||||
// Purpose 2: Execute XFB copy in homebrew games
|
||||
// Run when: This is run 15700 times per second on full speed
|
||||
// Purpose: Send VI interrupt when triggered
|
||||
// Run when: When every line is scaned
|
||||
void Update()
|
||||
{
|
||||
// Update the target refresh rate
|
||||
TargetRefreshRate = (float)((m_DisplayControlRegister.FMT == 0 || m_DisplayControlRegister.FMT == 2)
|
||||
? NTSC_FIELD_RATE : PAL_FIELD_RATE);
|
||||
|
||||
// Calculate actual refresh rate
|
||||
static u64 LastTick = 0;
|
||||
static s64 UpdateCheck = timeGetTime() + 1000, TickProgress = 0;
|
||||
s64 curTime = timeGetTime();
|
||||
if (UpdateCheck < (int)curTime)
|
||||
{
|
||||
UpdateCheck = curTime + 1000;
|
||||
TickProgress = CoreTiming::GetTicks() - LastTick;
|
||||
// Calculated CPU-GPU synced ticks for the dual core mode too
|
||||
// NOTICE_LOG(VIDEO, "Removed: %s Mhz", ThS(SyncTicksProgress / 1000000, false).c_str());
|
||||
SyncTicksProgress += TickProgress;
|
||||
// Multipled by two because of the way TicksPerFrame is calculated (divided by 25 and 30
|
||||
// rather than 50 and 60)
|
||||
|
||||
ActualRefreshRate = (float)(((double)SyncTicksProgress / (double)TicksPerFrame) * 2.0);
|
||||
LastTick = CoreTiming::GetTicks();
|
||||
SyncTicksProgress = 0;
|
||||
}
|
||||
|
||||
// TODO: What's the correct behavior for progressive mode?
|
||||
|
||||
if (m_VBeamPos == s_upperFieldBegin + m_VerticalTimingRegister.ACV)
|
||||
EndField();
|
||||
|
||||
if (m_VBeamPos == s_lowerFieldBegin + m_VerticalTimingRegister.ACV)
|
||||
else if (m_VBeamPos == s_lowerFieldBegin + m_VerticalTimingRegister.ACV)
|
||||
EndField();
|
||||
|
||||
m_VBeamPos++;
|
||||
if (m_VBeamPos > s_lineCount)
|
||||
|
||||
if (++m_VBeamPos > s_lineCount)
|
||||
{
|
||||
m_VBeamPos = 1;
|
||||
// Apply frame throttle wheneven a full screen scan finishes
|
||||
Core::FrameThrottle();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
@@ -1135,11 +1081,12 @@ void Update()
|
||||
}
|
||||
UpdateInterrupts();
|
||||
|
||||
|
||||
if (m_VBeamPos == s_upperFieldBegin)
|
||||
BeginField(m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_UPPER);
|
||||
|
||||
if (m_VBeamPos == s_lowerFieldBegin)
|
||||
else if (m_VBeamPos == s_lowerFieldBegin)
|
||||
BeginField(m_DisplayControlRegister.NIN ? FIELD_PROGRESSIVE : FIELD_LOWER);
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@@ -22,6 +22,31 @@ class PointerWrap;
|
||||
|
||||
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 EURGB60?
|
||||
#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 // TODO: Actually 23.5!
|
||||
#define PAL_LOWER_BEGIN 336
|
||||
|
||||
// urgh, ugly externs.
|
||||
extern u32 TargetRefreshRate;
|
||||
|
||||
// For BS2 HLE
|
||||
void PreInit(bool _bNTSC);
|
||||
void SetRegionReg(char _region);
|
||||
@@ -43,11 +68,6 @@ namespace VideoInterface
|
||||
// Update and draw framebuffer(s)
|
||||
void Update();
|
||||
|
||||
// urgh, ugly externs.
|
||||
extern float ActualRefreshRate;
|
||||
extern float TargetRefreshRate;
|
||||
extern s64 SyncTicksProgress;
|
||||
|
||||
// UpdateInterrupts: check if we have to generate a new VI Interrupt
|
||||
void UpdateInterrupts();
|
||||
|
||||
|
@@ -32,7 +32,7 @@ u32 g_autoFirstKey = 0, g_autoSecondKey = 0;
|
||||
bool g_bFirstKey = true;
|
||||
PlayMode g_playMode = MODE_NONE;
|
||||
|
||||
int g_framesToSkip = 0, g_frameSkipCounter = 0;
|
||||
unsigned int g_framesToSkip = 0, g_frameSkipCounter = 0;
|
||||
|
||||
int g_numPads = 0;
|
||||
ControllerState *g_padStates;
|
||||
@@ -70,14 +70,13 @@ void FrameUpdate() {
|
||||
EndPlayInput();
|
||||
}
|
||||
|
||||
|
||||
g_bPolled = false;
|
||||
}
|
||||
|
||||
void SetFrameSkipping(unsigned int framesToSkip) {
|
||||
cs_frameSkip.Enter();
|
||||
|
||||
g_framesToSkip = (int)framesToSkip;
|
||||
g_framesToSkip = framesToSkip;
|
||||
g_frameSkipCounter = 0;
|
||||
|
||||
// Don't forget to re-enable rendering in case it wasn't...
|
||||
|
@@ -51,7 +51,9 @@ extern bool g_bFrameStep, g_bAutoFire, g_bFirstKey, g_bPolled;
|
||||
extern u32 g_autoFirstKey, g_autoSecondKey;
|
||||
extern PlayMode g_playMode;
|
||||
|
||||
extern int g_framesToSkip, g_frameSkipCounter, g_numPads;
|
||||
extern unsigned int g_framesToSkip, g_frameSkipCounter;
|
||||
|
||||
extern int g_numPads;
|
||||
extern ControllerState *g_padStates;
|
||||
extern FILE *g_recordfd;
|
||||
extern std::string g_recordFile;
|
||||
|
@@ -383,7 +383,6 @@ void Write16(const u16 _Value, const u32 _Address)
|
||||
// Touching that game is a no-go so I don't want to take the risk :p
|
||||
while (fifo.bFF_GPReadEnable && ((!fifo.bFF_BPEnable && fifo.CPReadWriteDistance) || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint)))
|
||||
{
|
||||
Fifo_RunLoop();
|
||||
s_fifoIdleEvent.MsgWait();
|
||||
}
|
||||
}
|
||||
@@ -414,8 +413,6 @@ void Write16(const u16 _Value, const u32 _Address)
|
||||
Common::AtomicStore(fifo.bFF_Breakpoint, 0);
|
||||
}
|
||||
|
||||
Fifo_RunLoop();
|
||||
|
||||
DEBUG_LOG(COMMANDPROCESSOR,"\t write to CTRL_REGISTER : %04x", _Value);
|
||||
DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | LINK %s | BP %s || Init %s | OvF %s | UndF %s"
|
||||
, fifo.bFF_GPReadEnable ? "ON" : "OFF"
|
||||
@@ -572,7 +569,6 @@ void WaitForFrameFinish()
|
||||
{
|
||||
while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && ((!fifo.bFF_BPEnable && fifo.CPReadWriteDistance) || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint)));
|
||||
{
|
||||
Fifo_RunLoop();
|
||||
s_fifoIdleEvent.MsgWait();
|
||||
}
|
||||
|
||||
@@ -594,7 +590,6 @@ void STACKALIGN GatherPipeBursted()
|
||||
fifo.CPWritePointer += GATHER_PIPE_SIZE;
|
||||
|
||||
Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE);
|
||||
Fifo_RunLoop();
|
||||
|
||||
// High watermark overflow handling (hacked way)
|
||||
if (fifo.CPReadWriteDistance > fifo.CPHiWatermark)
|
||||
@@ -619,7 +614,6 @@ void STACKALIGN GatherPipeBursted()
|
||||
// Wait for GPU to catch up
|
||||
while (fifo.CPReadWriteDistance > fifo.CPLoWatermark && fifo.bFF_GPReadEnable && (!fifo.bFF_BPEnable || (fifo.bFF_BPEnable && !fifo.bFF_Breakpoint)))
|
||||
{
|
||||
Fifo_RunLoop();
|
||||
s_fifoIdleEvent.MsgWait();
|
||||
}
|
||||
}
|
||||
@@ -725,7 +719,6 @@ void UpdateFifoRegister()
|
||||
dist = (wp - fifo.CPBase) + ((fifo.CPEnd + GATHER_PIPE_SIZE) - rp);
|
||||
|
||||
Common::AtomicStore(fifo.CPReadWriteDistance, dist);
|
||||
Fifo_RunLoop();
|
||||
|
||||
if (!g_VideoInitialize.bOnThread)
|
||||
CatchUpGPU();
|
||||
|
@@ -34,6 +34,7 @@ extern u8* g_pVideoData;
|
||||
namespace
|
||||
{
|
||||
static volatile bool fifoStateRun = false;
|
||||
static volatile bool EmuRunning = false;
|
||||
static u8 *videoBuffer;
|
||||
static Common::Event fifo_run_event;
|
||||
// STATE_TO_SAVE
|
||||
@@ -100,9 +101,11 @@ void Fifo_ExitLoopNonBlocking()
|
||||
fifo_run_event.Set();
|
||||
}
|
||||
|
||||
void Fifo_RunLoop()
|
||||
void Fifo_RunLoop(bool run)
|
||||
{
|
||||
fifo_run_event.Set();
|
||||
EmuRunning = run;
|
||||
if (run)
|
||||
fifo_run_event.Set();
|
||||
}
|
||||
|
||||
// Description: Fifo_EnterLoop() sends data through this function.
|
||||
@@ -210,9 +213,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
|
||||
}
|
||||
|
||||
CommandProcessor::SetFifoIdleFromVideoPlugin();
|
||||
// "VideoFifo_CheckEFBAccess()" & "VideoFifo_CheckSwapRequest()" should be
|
||||
// moved to a more suitable place than inside function "Fifo_EnterLoop()"
|
||||
if (g_ActiveConfig.bEFBAccessEnable || g_ActiveConfig.bUseXFB)
|
||||
if (EmuRunning)
|
||||
Common::YieldCPU();
|
||||
else
|
||||
fifo_run_event.MsgWait();
|
||||
|
@@ -36,7 +36,7 @@ void Fifo_SendFifoData(u8* _uData, u32 len);
|
||||
void Fifo_EnterLoop(const SVideoInitialize &video_initialize);
|
||||
void Fifo_ExitLoop();
|
||||
void Fifo_ExitLoopNonBlocking();
|
||||
void Fifo_RunLoop();
|
||||
void Fifo_RunLoop(bool run);
|
||||
|
||||
void Fifo_DoState(PointerWrap &f);
|
||||
|
||||
|
@@ -535,6 +535,7 @@ void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRect
|
||||
DEBUGGER_PAUSE_LOG_AT(NEXT_XFB_CMD,false,{printf("RenderToXFB - disabled");});
|
||||
return;
|
||||
}
|
||||
|
||||
Renderer::ResetAPIState();
|
||||
// Set the backbuffer as the rendering target
|
||||
D3D::dev->SetDepthStencilSurface(NULL);
|
||||
@@ -544,7 +545,6 @@ void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRect
|
||||
EFBTextureToD3DBackBuffer(sourceRc);
|
||||
D3D::EndFrame();
|
||||
|
||||
|
||||
DEBUGGER_LOG_AT((NEXT_XFB_CMD|NEXT_EFB_CMD|NEXT_FRAME),
|
||||
{printf("StretchRect, EFB->XFB\n");});
|
||||
DEBUGGER_PAUSE_LOG_AT(
|
||||
@@ -553,6 +553,7 @@ void Renderer::RenderToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRect
|
||||
xfbAddr, fbWidth, fbHeight,
|
||||
sourceRc.left, sourceRc.top, sourceRc.right, sourceRc.bottom);}
|
||||
);
|
||||
|
||||
Swap(0,FIELD_PROGRESSIVE,0,0); // we used to swap the buffer here, now we will wait
|
||||
// until the XFB pointer is updated by VI
|
||||
D3D::BeginFrame();
|
||||
@@ -959,9 +960,9 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
|
||||
// Make any new configuration settings active.
|
||||
UpdateActiveConfig();
|
||||
|
||||
//TODO: Resize backbuffer if window size has changed. This code crashes, debug it.
|
||||
g_VideoInitialize.pCopiedToXFB(false);
|
||||
|
||||
//TODO: Resize backbuffer if window size has changed. This code crashes, debug it.
|
||||
CheckForResize();
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
@@ -312,6 +312,7 @@ void DoState(unsigned char **ptr, int mode) {
|
||||
|
||||
void EmuStateChange(PLUGIN_EMUSTATE newState)
|
||||
{
|
||||
Fifo_RunLoop((newState == PLUGIN_EMUSTATE_PLAY) ? true : false);
|
||||
}
|
||||
|
||||
void Video_EnterLoop()
|
||||
@@ -333,7 +334,7 @@ void Video_SetRendering(bool bEnabled) {
|
||||
// Run from the graphics thread
|
||||
void VideoFifo_CheckSwapRequest()
|
||||
{
|
||||
// CPU swap, not finished, seems to be working fine for dual-core for now
|
||||
// swap unimplemented
|
||||
return;
|
||||
|
||||
if (s_swapRequested)
|
||||
@@ -351,12 +352,15 @@ void VideoFifo_CheckSwapRequest()
|
||||
// Run from the graphics thread
|
||||
void VideoFifo_CheckSwapRequestAt(u32 xfbAddr, u32 fbWidth, u32 fbHeight)
|
||||
{
|
||||
// CPU swap unimplemented
|
||||
// swap unimplemented
|
||||
}
|
||||
|
||||
// Run from the CPU thread (from VideoInterface.cpp)
|
||||
void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
|
||||
{
|
||||
// swap unimplemented
|
||||
return;
|
||||
|
||||
s_beginFieldArgs.xfbAddr = xfbAddr;
|
||||
s_beginFieldArgs.field = field;
|
||||
s_beginFieldArgs.fbWidth = fbWidth;
|
||||
@@ -365,14 +369,11 @@ void Video_BeginField(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight)
|
||||
|
||||
if (s_initialized)
|
||||
{
|
||||
|
||||
// Make sure previous swap request has made it to the screen
|
||||
if (g_VideoInitialize.bOnThread)
|
||||
{
|
||||
// It seems to be working fine in this way for now without using AtomicLoadAcquire
|
||||
// ector, please check here
|
||||
//while (Common::AtomicLoadAcquire(s_swapRequested))
|
||||
// Common::YieldCPU();
|
||||
//Common::YieldCPU();
|
||||
}
|
||||
else
|
||||
VideoFifo_CheckSwapRequest();
|
||||
|
@@ -597,6 +597,9 @@ void Renderer::SetBlendMode(bool forceUpdate)
|
||||
|
||||
u32 Renderer::AccessEFB(EFBAccessType type, int x, int y)
|
||||
{
|
||||
if(!g_ActiveConfig.bEFBAccessEnable)
|
||||
return 0;
|
||||
|
||||
// Get the rectangular target region covered by the EFB pixel.
|
||||
EFBRectangle efbPixelRc;
|
||||
efbPixelRc.left = x;
|
||||
|
@@ -379,6 +379,7 @@ void DoState(unsigned char **ptr, int mode) {
|
||||
|
||||
void EmuStateChange(PLUGIN_EMUSTATE newState)
|
||||
{
|
||||
Fifo_RunLoop((newState == PLUGIN_EMUSTATE_PLAY) ? true : false);
|
||||
}
|
||||
|
||||
// This is called after Video_Initialize() from the Core
|
||||
@@ -494,9 +495,6 @@ void VideoFifo_CheckSwapRequest()
|
||||
g_VideoInitialize.pCopiedToXFB(false);
|
||||
}
|
||||
|
||||
// TODO : This just updates the frame counter, so we may change this func's name as well
|
||||
g_VideoInitialize.pCopiedToXFB(true);
|
||||
|
||||
Common::AtomicStoreRelease(s_swapRequested, FALSE);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user