Attempt to calculate actual refresh rate (i.e. a CPU-GPU synced Mhz), no real success. Anybody have any ideas?
Is there no indication from the game when the screen refresh should occur? No, not what I could find, we currently calculate the refresh rate and m_VBeamPos from the CPU ticks progress. That works perfectly if the CPU and GPU is perfectly synced as in the single core and no-idle skipping mode. So I guess it's possible that the game doesn't indicate when the screen should be refreshed, but rather that the hardware calculate that from the CPU ticks progress. That leaves us with a problem in the dual core and idle skipping modes to calculate a CPU-GPU synced CPU ticks progress. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3447 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
3295ec38eb
commit
5c04af50a4
|
@ -92,14 +92,16 @@ void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample
|
||||||
|
|
||||||
// Write Other Audio
|
// Write Other Audio
|
||||||
if (!m_throttle)
|
if (!m_throttle)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// The auto throttle function. This loop will put a ceiling on the CPU MHz.
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
/* This is only needed for non-AX sound, currently directly
|
/* This is only needed for non-AX sound, currently directly
|
||||||
streamed and DTK sound. For AX we call SoundStream::Update in
|
streamed and DTK sound. For AX we call SoundStream::Update in
|
||||||
AXTask() for example. */
|
AXTask() for example. */
|
||||||
while (m_queueSize > queue_maxlength / 2) {
|
while (m_queueSize > queue_maxlength / 2)
|
||||||
|
{
|
||||||
// Urgh.
|
// Urgh.
|
||||||
if (g_dspInitialize.pEmulatorState) {
|
if (g_dspInitialize.pEmulatorState) {
|
||||||
if (*g_dspInitialize.pEmulatorState != 0)
|
if (*g_dspInitialize.pEmulatorState != 0)
|
||||||
|
@ -108,7 +110,8 @@ void CMixer::PushSamples(short *samples, int num_stereo_samples, int core_sample
|
||||||
soundStream->Update();
|
soundStream->Update();
|
||||||
Common::SleepCurrentThread(0);
|
Common::SleepCurrentThread(0);
|
||||||
}
|
}
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
push_sync.Enter();
|
push_sync.Enter();
|
||||||
while (num_stereo_samples) {
|
while (num_stereo_samples) {
|
||||||
acc += core_sample_rate;
|
acc += core_sample_rate;
|
||||||
|
|
|
@ -111,6 +111,17 @@ void MatrixMul(int n, const float *a, const float *b, float *result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate sum of a float list
|
||||||
|
float MathFloatVectorSum(std::vector<float> Vec)
|
||||||
|
{
|
||||||
|
float Sum = 0.0;
|
||||||
|
for(int i = 0; i < Vec.size(); i++)
|
||||||
|
{
|
||||||
|
Sum += Vec.at(i);
|
||||||
|
}
|
||||||
|
return Sum;
|
||||||
|
}
|
||||||
|
|
||||||
void Matrix33::LoadIdentity(Matrix33 &mtx)
|
void Matrix33::LoadIdentity(Matrix33 &mtx)
|
||||||
{
|
{
|
||||||
memset(mtx.data, 0, sizeof(mtx.data));
|
memset(mtx.data, 0, sizeof(mtx.data));
|
||||||
|
|
|
@ -121,6 +121,7 @@ inline double pow2(double x) {return x * x;}
|
||||||
void SaveSSEState();
|
void SaveSSEState();
|
||||||
void LoadSSEState();
|
void LoadSSEState();
|
||||||
void LoadDefaultSSEState();
|
void LoadDefaultSSEState();
|
||||||
|
float MathFloatVectorSum(std::vector<float>);
|
||||||
|
|
||||||
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
|
#include "MathUtil.h"
|
||||||
|
|
||||||
#include "Console.h"
|
#include "Console.h"
|
||||||
#include "Core.h"
|
#include "Core.h"
|
||||||
|
@ -672,61 +673,143 @@ void Callback_VideoCopiedToXFB()
|
||||||
|
|
||||||
frames++;
|
frames++;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Custom frame limiter
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
if (targetfps > 0)
|
if (targetfps > 0)
|
||||||
{
|
{
|
||||||
new_frametime = Timer.GetTimeDifference() - old_frametime;
|
new_frametime = Timer.GetTimeDifference() - old_frametime;
|
||||||
|
|
||||||
old_frametime = Timer.GetTimeDifference();
|
old_frametime = Timer.GetTimeDifference();
|
||||||
|
|
||||||
wait_frametime = (1000/targetfps) - (u16)new_frametime;
|
wait_frametime = (1000/targetfps) - (u16)new_frametime;
|
||||||
if (targetfps < 35)
|
if (targetfps < 35)
|
||||||
wait_frametime--;
|
wait_frametime--;
|
||||||
if (wait_frametime > 0)
|
if (wait_frametime > 0)
|
||||||
Common::SleepCurrentThread(wait_frametime*2);
|
Common::SleepCurrentThread(wait_frametime*2);
|
||||||
}
|
}
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Is it possible to calculate the CPU-GPU synced ticks for the dual core mode too?
|
||||||
|
// And possible the idle skipping mode too?
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
|
static int Diff = 0, DistOld = 0;
|
||||||
|
Diff = CommandProcessor::fifo.CPReadWriteDistance - DistOld;
|
||||||
|
// If the CPReadWriteDistance has increased since the last frame we assume the CPU has raced
|
||||||
|
// ahead of the GPU and we adjust the ticks. Why multiply the difference with 700? I don't know,
|
||||||
|
// please fix it if possible.
|
||||||
|
if (Diff > 0) VideoInterface::SyncTicksProgress -= Diff * 700;
|
||||||
|
DistOld = CommandProcessor::fifo.CPReadWriteDistance;
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
if (Timer.GetTimeDifference() >= 1000)
|
if (Timer.GetTimeDifference() >= 1000)
|
||||||
{
|
{
|
||||||
old_frametime=0;
|
// Time passed
|
||||||
|
|
||||||
u64 newTicks = CoreTiming::GetTicks();
|
|
||||||
u64 newIdleTicks = CoreTiming::GetIdleTicks();
|
|
||||||
|
|
||||||
s64 diff = (newTicks - ticks) / 1000000;
|
|
||||||
s64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
|
||||||
|
|
||||||
ticks = newTicks;
|
|
||||||
idleTicks = newIdleTicks;
|
|
||||||
|
|
||||||
float t = (float)(Timer.GetTimeDifference()) / 1000.f;
|
float t = (float)(Timer.GetTimeDifference()) / 1000.f;
|
||||||
|
|
||||||
char temp[256];
|
// Use extended or summary information. The summary information does not print the ticks data,
|
||||||
sprintf(temp, "FPS:%8.2f - Core: %s | %s - Speed: %i MHz [Real: %i + IdleSkip: %i] / %i MHz",
|
// that's more of a debugging interest, it can always be optional of course if someone is interested.
|
||||||
(float)frames / t,
|
//#define EXTENDED_INFO
|
||||||
#if defined(JITTEST) && JITTEST
|
#ifdef EXTENDED_INFO
|
||||||
#ifdef _M_IX86
|
u64 newTicks = CoreTiming::GetTicks();
|
||||||
_CoreParameter.bUseJIT ? "JIT32IL" : "Int32",
|
u64 newIdleTicks = CoreTiming::GetIdleTicks();
|
||||||
#else
|
|
||||||
_CoreParameter.bUseJIT ? "JIT64IL" : "Int64",
|
s64 diff = (newTicks - ticks) / 1000000;
|
||||||
#endif
|
s64 idleDiff = (newIdleTicks - idleTicks) / 1000000;
|
||||||
#else
|
|
||||||
#ifdef _M_IX86
|
ticks = newTicks;
|
||||||
_CoreParameter.bUseJIT ? "JIT32" : "Int32",
|
idleTicks = newIdleTicks;
|
||||||
#else
|
|
||||||
_CoreParameter.bUseJIT ? "JIT64" : "Int64",
|
float TicksPercentage = (float)diff / (float)(SystemTimers::GetTicksPerSecond() / 1000000) * 100;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
_CoreParameter.bUseDualCore ? "DC" : "SC",
|
float FPS = (float)frames / t;
|
||||||
(int)(diff),
|
float FPS_To_VPS_Rate = ((float)FPS / VideoInterface::ActualRefreshRate);
|
||||||
(int)(diff - idleDiff),
|
// ---------------------------------------------------------------------
|
||||||
(int)(idleDiff),
|
// For the sake of the dual core mode calculate an average to somewhat reduce the variations
|
||||||
SystemTimers::GetTicksPerSecond() / 1000000);
|
// in the FPS/VPS rate
|
||||||
|
// ______________________________
|
||||||
|
/**/
|
||||||
|
if (_CoreParameter.bUseDualCore)
|
||||||
|
{
|
||||||
|
static std::vector <float> FPSVPSList;
|
||||||
|
static float AverageOver = 5.0;
|
||||||
|
if (FPSVPSList.size() == AverageOver) FPSVPSList.erase(FPSVPSList.begin());
|
||||||
|
FPSVPSList.push_back(FPS_To_VPS_Rate);
|
||||||
|
if (FPSVPSList.size() == AverageOver)
|
||||||
|
FPS_To_VPS_Rate = MathFloatVectorSum(FPSVPSList) / AverageOver;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
// Correct the FPS/VPS rate for temporary CPU-GPU timing variations. This rate can only be 1/Integer
|
||||||
|
// so we set it to either 0.33, 0.5 or 1.0 depending on which it's closest to.
|
||||||
|
/*
|
||||||
|
1. Notice: This rate can currently not be calculated with any accuracy at all when idle skipping
|
||||||
|
is on (the suggested tick rate in that case is much to high in proportion to the actual FPS)
|
||||||
|
2. When dual core is enabled the CommandProcessor allow some elasticity in the FPS/VPS rate. This
|
||||||
|
is especially noticable in Zelda TP who's FPS/VPS for me varies between 0.25 and 0.6 as a
|
||||||
|
result of this.
|
||||||
|
3. PAL 50Hz games: Are 'patched' so that they still run at the correct speed. So if the NTSC 60Hz
|
||||||
|
version has a FPS/VPS of 0.5 the 50Hz game will run at 0.6.
|
||||||
|
*/
|
||||||
|
// ______________________________
|
||||||
|
/**/
|
||||||
|
if (FPS_To_VPS_Rate > 0 && FPS_To_VPS_Rate < ((1.0/3.0 + 1.0/2.0)/2)) FPS_To_VPS_Rate = 1.0/3.0;
|
||||||
|
else if (FPS_To_VPS_Rate > ((1.0/3.0 + 1.0/2.0)/2) && FPS_To_VPS_Rate < ((1.0/2.0 + 1.0/1.0)/2)) FPS_To_VPS_Rate = 1.0/2.0;
|
||||||
|
else FPS_To_VPS_Rate = 1.0;
|
||||||
|
// PAL patch adjustment
|
||||||
|
if (VideoInterface::TargetRefreshRate == 50) FPS_To_VPS_Rate = FPS_To_VPS_Rate * 1.2;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
float TargetFPS = FPS_To_VPS_Rate * (float)VideoInterface::TargetRefreshRate;
|
||||||
|
float FPSPercentage = (FPS / TargetFPS) * 100.0;
|
||||||
|
float VPSPercentage = (VideoInterface::ActualRefreshRate / (float)VideoInterface::TargetRefreshRate) * 100.0;
|
||||||
|
|
||||||
|
// Settings are shown the same for both extended and summary info
|
||||||
|
std::string SSettings = StringFromFormat(" | Core: %s %s",
|
||||||
|
#if defined(JITTEST) && JITTEST
|
||||||
|
#ifdef _M_IX86
|
||||||
|
_CoreParameter.bUseJIT ? "JIT32IL" : "Int32",
|
||||||
|
#else
|
||||||
|
_CoreParameter.bUseJIT ? "JIT64IL" : "Int64",
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef _M_IX86
|
||||||
|
_CoreParameter.bUseJIT ? "JIT32" : "Int32",
|
||||||
|
#else
|
||||||
|
_CoreParameter.bUseJIT ? "JIT64" : "Int64",
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
_CoreParameter.bUseDualCore ? "DC" : "SC");
|
||||||
|
// Show ~ to indicate that the value may be wrong because the ticks are wrong
|
||||||
|
std::string IdleSkipMessage = "";
|
||||||
|
if (_CoreParameter.bSkipIdle || _CoreParameter.bUseDualCore) IdleSkipMessage = "~";
|
||||||
|
#ifdef EXTENDED_INFO
|
||||||
|
std::string SFPS = StringFromFormat("FPS: %4.1f/%s%2.0f (%s%3.0f%% | %s%1.2f) VPS:%4.0f/%i (%3.0f%%)",
|
||||||
|
FPS, IdleSkipMessage.c_str(), TargetFPS,
|
||||||
|
IdleSkipMessage.c_str(), FPSPercentage, IdleSkipMessage.c_str(), FPS_To_VPS_Rate,
|
||||||
|
VideoInterface::ActualRefreshRate, VideoInterface::TargetRefreshRate, VPSPercentage);
|
||||||
|
std::string STicks = StringFromFormat(" | CPU: %s%i MHz [Real: %i + IdleSkip: %i] / %i MHz (%s%3.0f%%)",
|
||||||
|
IdleSkipMessage.c_str(),
|
||||||
|
(int)(diff),
|
||||||
|
(int)(diff - idleDiff),
|
||||||
|
(int)(idleDiff),
|
||||||
|
SystemTimers::GetTicksPerSecond() / 1000000,
|
||||||
|
IdleSkipMessage.c_str(),
|
||||||
|
TicksPercentage);
|
||||||
|
// Summary information
|
||||||
|
#else
|
||||||
|
std::string SFPS = StringFromFormat("FPS: %4.1f/%s%2.0f (%s%3.0f%%)",
|
||||||
|
FPS, IdleSkipMessage.c_str(), TargetFPS, IdleSkipMessage.c_str(), FPSPercentage);
|
||||||
|
std::string STicks = "";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string SMessage = StringFromFormat("%s%s%s", SFPS.c_str(), SSettings.c_str(), STicks.c_str());
|
||||||
|
|
||||||
|
// Show message
|
||||||
if (g_pUpdateFPSDisplay != NULL)
|
if (g_pUpdateFPSDisplay != NULL)
|
||||||
g_pUpdateFPSDisplay(temp);
|
g_pUpdateFPSDisplay(SMessage.c_str());
|
||||||
|
Host_UpdateStatusBar(SMessage.c_str());
|
||||||
Host_UpdateStatusBar(temp);
|
// Reset frame counter
|
||||||
|
|
||||||
frames = 0;
|
frames = 0;
|
||||||
Timer.Update();
|
Timer.Update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "CoreTiming.h"
|
#include "CoreTiming.h"
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
|
|
||||||
|
#include <mmsystem.h>
|
||||||
|
|
||||||
namespace CoreTiming
|
namespace CoreTiming
|
||||||
{
|
{
|
||||||
|
|
|
@ -641,7 +641,7 @@ void STACKALIGN GatherPipeBursted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This is mostly used in single core mode
|
||||||
void CatchUpGPU()
|
void CatchUpGPU()
|
||||||
{
|
{
|
||||||
// check if we are able to run this buffer
|
// check if we are able to run this buffer
|
||||||
|
|
|
@ -258,7 +258,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFinish
|
// SetFinish
|
||||||
// THIS IS EXECUTED FROM VIDEO THREAD
|
// THIS IS EXECUTED FROM VIDEO THREAD (BPStructs.cpp) when a new frame has been drawn
|
||||||
void SetFinish()
|
void SetFinish()
|
||||||
{
|
{
|
||||||
CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack
|
CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
#include "../PluginManager.h"
|
#include "../PluginManager.h"
|
||||||
#include "../CoreTiming.h"
|
#include "../CoreTiming.h"
|
||||||
#include "../HW/SystemTimers.h"
|
#include "../HW/SystemTimers.h"
|
||||||
|
#include "StringUtil.h"
|
||||||
|
|
||||||
|
#include <mmsystem.h>
|
||||||
|
|
||||||
namespace VideoInterface
|
namespace VideoInterface
|
||||||
{
|
{
|
||||||
|
@ -336,7 +339,7 @@ static u32 LineCount = 0;
|
||||||
static u32 LinesPerField = 0;
|
static u32 LinesPerField = 0;
|
||||||
static u64 LastTime = 0;
|
static u64 LastTime = 0;
|
||||||
static u32 NextXFBRender = 0;
|
static u32 NextXFBRender = 0;
|
||||||
|
int TargetRefreshRate = 0, SyncTicksProgress = 0; float ActualRefreshRate = 0.0;
|
||||||
|
|
||||||
void DoState(PointerWrap &p)
|
void DoState(PointerWrap &p)
|
||||||
{
|
{
|
||||||
|
@ -965,6 +968,7 @@ void UpdateInterrupts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is unused
|
||||||
void GenerateVIInterrupt(VIInterruptType _VIInterrupt)
|
void GenerateVIInterrupt(VIInterruptType _VIInterrupt)
|
||||||
{
|
{
|
||||||
switch(_VIInterrupt)
|
switch(_VIInterrupt)
|
||||||
|
@ -1001,6 +1005,10 @@ u8* GetXFBPointerBottom()
|
||||||
return Memory::GetPointer(m_XFBInfoBottom.FBB);
|
return Memory::GetPointer(m_XFBInfoBottom.FBB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Screenshot and screen message
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
void UpdateTiming()
|
void UpdateTiming()
|
||||||
{
|
{
|
||||||
switch (m_DisplayControlRegister.FMT)
|
switch (m_DisplayControlRegister.FMT)
|
||||||
|
@ -1027,9 +1035,42 @@ void UpdateTiming()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Purpose 1: Send VI interrupt for every screen refresh
|
||||||
|
// Purpose 2: Execute XFB copy in homebrew games
|
||||||
|
// Run when: This is run 7200 times per second on full speed
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Calculate actual refresh rate
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
|
// Update the target refresh rate
|
||||||
|
TargetRefreshRate = (m_DisplayControlRegister.FMT == 0 || m_DisplayControlRegister.FMT == 2)
|
||||||
|
? 60 : 50;
|
||||||
|
|
||||||
|
// Calculate actual refresh rate
|
||||||
|
static u64 LastTick = 0;
|
||||||
|
static int UpdateCheck = timeGetTime() + 1000, TickProgress = 0;
|
||||||
|
if (UpdateCheck < timeGetTime())
|
||||||
|
{
|
||||||
|
UpdateCheck = timeGetTime() + 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)SyncTicksProgress / (float)TicksPerFrame) * 2.0;
|
||||||
|
LastTick = CoreTiming::GetTicks();
|
||||||
|
SyncTicksProgress = 0;
|
||||||
|
}
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Go through all lines
|
||||||
while ((CoreTiming::GetTicks() - LastTime) > (TicksPerFrame / LineCount))
|
while ((CoreTiming::GetTicks() - LastTime) > (TicksPerFrame / LineCount))
|
||||||
{
|
{
|
||||||
LastTime += (TicksPerFrame / LineCount);
|
LastTime += (TicksPerFrame / LineCount);
|
||||||
|
@ -1101,5 +1142,6 @@ void Update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
}
|
} // namespace
|
||||||
|
|
|
@ -52,6 +52,7 @@ namespace VideoInterface
|
||||||
|
|
||||||
// Update and draw framebuffer(s)
|
// Update and draw framebuffer(s)
|
||||||
void Update();
|
void Update();
|
||||||
|
extern float ActualRefreshRate; extern int TargetRefreshRate, SyncTicksProgress;
|
||||||
|
|
||||||
// UpdateInterrupts: check if we have to generate a new VI Interrupt
|
// UpdateInterrupts: check if we have to generate a new VI Interrupt
|
||||||
void UpdateInterrupts();
|
void UpdateInterrupts();
|
||||||
|
|
|
@ -160,7 +160,8 @@ void BPWritten(const Bypass& bp)
|
||||||
PixelShaderManager::SetDestAlpha(bpmem.dstalpha);
|
PixelShaderManager::SetDestAlpha(bpmem.dstalpha);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BPMEM_SETDRAWDONE: // This is called when the game is done drawing (eg: like in DX: Begin(); Draw(); End();)
|
// This is called when the game is done drawing the new frame (eg: like in DX: Begin(); Draw(); End();)
|
||||||
|
case BPMEM_SETDRAWDONE:
|
||||||
switch (bp.newvalue & 0xFF)
|
switch (bp.newvalue & 0xFF)
|
||||||
{
|
{
|
||||||
case 0x02:
|
case 0x02:
|
||||||
|
|
|
@ -76,26 +76,6 @@ u8* FAKE_GetFifoEndPtr()
|
||||||
return &videoBuffer[size];
|
return &videoBuffer[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
// The loop in EnterLoop sends data through this function.
|
|
||||||
// TODO: Possibly inline it? This one is exported so it will likely not be inlined at all.
|
|
||||||
void Video_SendFifoData(u8* _uData, u32 len)
|
|
||||||
{
|
|
||||||
if (size + len >= FIFO_SIZE)
|
|
||||||
{
|
|
||||||
int pos = (int)(g_pVideoData - videoBuffer);
|
|
||||||
if (size - pos > pos)
|
|
||||||
{
|
|
||||||
PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos);
|
|
||||||
}
|
|
||||||
memmove(&videoBuffer[0], &videoBuffer[pos], size - pos);
|
|
||||||
size -= pos;
|
|
||||||
g_pVideoData = FAKE_GetFifoStartPtr();
|
|
||||||
}
|
|
||||||
memcpy(videoBuffer + size, _uData, len);
|
|
||||||
size += len;
|
|
||||||
OpcodeDecoder_Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Executed from another thread, no the graphics thread!
|
// Executed from another thread, no the graphics thread!
|
||||||
// Basically, all it does is set a flag so that the loop will eventually exit, then
|
// Basically, all it does is set a flag so that the loop will eventually exit, then
|
||||||
// waits for the event to be set, which happens when the loop does exit.
|
// waits for the event to be set, which happens when the loop does exit.
|
||||||
|
@ -124,7 +104,35 @@ void Fifo_ExitLoopNonBlocking() {
|
||||||
fifoStateRun = false;
|
fifoStateRun = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Description: Fifo_EnterLoop() sends data through this function.
|
||||||
|
// TODO: Possibly inline it? This one is exported so it will likely not be inlined at all.
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
|
void Video_SendFifoData(u8* _uData, u32 len)
|
||||||
|
{
|
||||||
|
if (size + len >= FIFO_SIZE)
|
||||||
|
{
|
||||||
|
int pos = (int)(g_pVideoData - videoBuffer);
|
||||||
|
if (size - pos > pos)
|
||||||
|
{
|
||||||
|
PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos);
|
||||||
|
}
|
||||||
|
memmove(&videoBuffer[0], &videoBuffer[pos], size - pos);
|
||||||
|
size -= pos;
|
||||||
|
g_pVideoData = FAKE_GetFifoStartPtr();
|
||||||
|
}
|
||||||
|
// Copy new video instructions to videoBuffer for future use in rendering the new picture
|
||||||
|
memcpy(videoBuffer + size, _uData, len);
|
||||||
|
size += len;
|
||||||
|
OpcodeDecoder_Run();
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Description: Main FIFO update loop
|
||||||
|
// Purpose: Keep the Core HW updated about the CPU-GPU distance
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
|
void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
|
||||||
{
|
{
|
||||||
fifoStateRun = true;
|
fifoStateRun = true;
|
||||||
|
@ -160,7 +168,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
|
||||||
video_initialize.pPeekMessages();
|
video_initialize.pPeekMessages();
|
||||||
peek_counter = 0;
|
peek_counter = 0;
|
||||||
}
|
}
|
||||||
// read the data and send it to the VideoPlugin
|
// Create pointer to video data and send it to the VideoPlugin
|
||||||
u32 readPtr = _fifo.CPReadPointer;
|
u32 readPtr = _fifo.CPReadPointer;
|
||||||
u8 *uData = video_initialize.pGetMemoryPointer(readPtr);
|
u8 *uData = video_initialize.pGetMemoryPointer(readPtr);
|
||||||
|
|
||||||
|
@ -204,6 +212,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
|
||||||
readPtr += distToSend;
|
readPtr += distToSend;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
// Execute new instructions found in uData
|
||||||
Video_SendFifoData(uData, distToSend);
|
Video_SendFifoData(uData, distToSend);
|
||||||
Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadPointer, readPtr);
|
Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadPointer, readPtr);
|
||||||
Common::SyncInterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -distToSend);
|
Common::SyncInterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -distToSend);
|
||||||
|
@ -217,3 +226,4 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
|
||||||
fifo_exit_event.SetTimer();
|
fifo_exit_event.SetTimer();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
@ -379,9 +379,10 @@ void Shutdown(void)
|
||||||
OpenGL_Shutdown();
|
OpenGL_Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Enter and exit the video loop
|
// Enter and exit the video loop
|
||||||
// -------------------------------
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
void Video_EnterLoop()
|
void Video_EnterLoop()
|
||||||
{
|
{
|
||||||
Fifo_EnterLoop(g_VideoInitialize);
|
Fifo_EnterLoop(g_VideoInitialize);
|
||||||
|
@ -391,13 +392,27 @@ void Video_ExitLoop()
|
||||||
{
|
{
|
||||||
Fifo_ExitLoop();
|
Fifo_ExitLoop();
|
||||||
}
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Screenshot and screen message
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
void Video_Screenshot(const char *_szFilename)
|
void Video_Screenshot(const char *_szFilename)
|
||||||
{
|
{
|
||||||
Renderer::SetScreenshot(_szFilename);
|
Renderer::SetScreenshot(_szFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Video_AddMessage(const char* pstr, u32 milliseconds)
|
||||||
|
{
|
||||||
|
OSD::AddMessage(pstr, milliseconds);
|
||||||
|
}
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Run from the CPU thread (from VideoInterface.cpp) for certain homebrew games only
|
||||||
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|
||||||
void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling)
|
void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, bool scheduling)
|
||||||
{
|
{
|
||||||
if (g_Config.bUseXFB && XFB_isInit())
|
if (g_Config.bUseXFB && XFB_isInit())
|
||||||
|
@ -422,8 +437,4 @@ void Video_UpdateXFB(u8* _pXFB, u32 _dwWidth, u32 _dwHeight, s32 _dwYOffset, boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
void Video_AddMessage(const char* pstr, u32 milliseconds)
|
|
||||||
{
|
|
||||||
OSD::AddMessage(pstr, milliseconds);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue