statesave prep: Change coretiming to index/registration model, instead of storing pointers. This will make it possible to save/load the event queue.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@258 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard 2008-08-21 20:01:18 +00:00
parent 9e3747e2f3
commit 6a4d697f5f
15 changed files with 174 additions and 138 deletions

View File

@ -39,7 +39,6 @@ struct SCoreStartupParameter
bool bUseDualCore; bool bUseDualCore;
bool bNTSC; bool bNTSC;
bool bHLEBios; bool bHLEBios;
bool bThrottle;
bool bUseFastMem; bool bUseFastMem;
bool bLockThreads; bool bLockThreads;
bool bOptimizeQuantizers; bool bOptimizeQuantizers;

View File

@ -15,6 +15,8 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include <vector>
#include "Thread.h" #include "Thread.h"
#include "PowerPC/PowerPC.h" #include "PowerPC/PowerPC.h"
#include "CoreTiming.h" #include "CoreTiming.h"
@ -24,12 +26,52 @@
namespace CoreTiming namespace CoreTiming
{ {
struct EventType
{
TimedCallback callback;
const char *name;
};
std::vector<EventType> event_types;
struct Event
{
s64 time;
u64 userdata;
Event *next;
int type;
};
// STATE_TO_SAVE (how?)
Event *first;
Event *tsFirst;
int downcount, slicelength; int downcount, slicelength;
int maxSliceLength = 20000; int maxSliceLength = 20000;
s64 globalTimer; s64 globalTimer;
s64 idledCycles; s64 idledCycles;
Common::CriticalSection externalEventSection;
int RegisterEvent(const char *name, TimedCallback callback)
{
EventType type;
type.name = name;
type.callback = callback;
event_types.push_back(type);
return event_types.size() - 1;
}
void UnregisterAllEvents()
{
if (first)
PanicAlert("Cannot unregister events with events pending");
event_types.clear();
}
u64 GetTicks() u64 GetTicks()
{ {
return (u64)globalTimer; return (u64)globalTimer;
@ -40,29 +82,14 @@ u64 GetIdleTicks()
return (u64)idledCycles; return (u64)idledCycles;
} }
struct Event
{
TimedCallback callback;
Event *next;
s64 time;
u64 userdata;
const char *name;
};
Event *first;
Event *tsFirst;
Common::CriticalSection externalEventSection;
// This is to be called when outside threads, such as the graphics thread, wants to // This is to be called when outside threads, such as the graphics thread, wants to
// schedule things to be executed on the main thread. // schedule things to be executed on the main thread.
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, TimedCallback callback, const char *name, u64 userdata) void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata)
{ {
externalEventSection.Enter(); externalEventSection.Enter();
Event *ne = new Event; Event *ne = new Event;
ne->time = globalTimer + cyclesIntoFuture; ne->time = globalTimer + cyclesIntoFuture;
ne->name = name; ne->type = event_type;
ne->callback = callback;
ne->next = tsFirst; ne->next = tsFirst;
ne->userdata = userdata; ne->userdata = userdata;
tsFirst = ne; tsFirst = ne;
@ -125,12 +152,11 @@ void AddEventToQueue(Event *ne)
// This must be run ONLY from within the cpu thread // This must be run ONLY from within the cpu thread
// cyclesIntoFuture may be VERY inaccurate if called from anything else // cyclesIntoFuture may be VERY inaccurate if called from anything else
// than Advance // than Advance
void ScheduleEvent(int cyclesIntoFuture, TimedCallback callback, const char *name, u64 userdata) void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata)
{ {
Event *ne = new Event; Event *ne = new Event;
ne->callback = callback;
ne->userdata = userdata; ne->userdata = userdata;
ne->name = name; ne->type = event_type;
ne->time = globalTimer + cyclesIntoFuture; ne->time = globalTimer + cyclesIntoFuture;
AddEventToQueue(ne); AddEventToQueue(ne);
@ -144,24 +170,24 @@ void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted))
advanceCallback = callback; advanceCallback = callback;
} }
bool IsScheduled(TimedCallback callback) bool IsScheduled(int event_type)
{ {
if (!first) if (!first)
return false; return false;
Event *e = first; Event *e = first;
while (e) { while (e) {
if (e->callback == callback) if (e->type == event_type)
return true; return true;
e = e->next; e = e->next;
} }
return false; return false;
} }
void RemoveEvent(TimedCallback callback) void RemoveEvent(int event_type)
{ {
if (!first) if (!first)
return; return;
if (first->callback == callback) if (first->type == event_type)
{ {
Event *next = first->next; Event *next = first->next;
delete first; delete first;
@ -173,7 +199,7 @@ void RemoveEvent(TimedCallback callback)
Event *ptr = prev->next; Event *ptr = prev->next;
while (ptr) while (ptr)
{ {
if (ptr->callback == callback) if (ptr->type == event_type)
{ {
prev->next = ptr->next; prev->next = ptr->next;
delete ptr; delete ptr;
@ -219,8 +245,7 @@ void Advance()
// LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ", // LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ",
// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
first->callback(first->userdata, (int)(globalTimer - first->time)); event_types[first->type].callback(first->userdata, (int)(globalTimer - first->time));
Event *next = first->next; Event *next = first->next;
delete first; delete first;
first = next; first = next;

View File

@ -14,32 +14,49 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#ifndef _CORETIMING_H #ifndef _CORETIMING_H
#define _CORETIMING_H #define _CORETIMING_H
// This is a system to schedule events into the emulated machine's future. Time is measured
// in main CPU clock cycles.
// To schedule an event, you first have to register its type. This is where you pass in the
// callback. You then schedule events using the type id you get back.
#include "Common.h" #include "Common.h"
namespace CoreTiming namespace CoreTiming
{ {
enum {
SOON = 100
};
typedef void (*TimedCallback)(u64 userdata, int cyclesLate); typedef void (*TimedCallback)(u64 userdata, int cyclesLate);
u64 GetTicks(); u64 GetTicks();
u64 GetIdleTicks(); u64 GetIdleTicks();
// The int that the callbacks get is how many cycles late it was. // The int that the callbacks get is how many cycles late it was.
// So to schedule a new event on a regular basis: // So to schedule a new event on a regular basis:
// inside callback: // inside callback:
// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") // ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
void ScheduleEvent(int cyclesIntoFuture, TimedCallback callback, const char *name, u64 userdata=0);
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, TimedCallback callback, const char *name, u64 userdata=0); // Returns the event_type identifier.
void RemoveEvent(TimedCallback callback); int RegisterEvent(const char *name, TimedCallback callback);
bool IsScheduled(TimedCallback callback); void UnregisterAllEvents();
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
// when we implement state saves.
void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata=0);
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0);
// We only permit one event of each type in the queue at a time.
void RemoveEvent(int event_type);
bool IsScheduled(int event_type);
void Advance(); void Advance();
// Pretend that the main CPU has executed enough cycles to reach the next event.
void Idle(); void Idle();
// Clear all pending events. This should ONLY be done on exit.
void Clear(); void Clear();
void LogPendingEvents(); void LogPendingEvents();
void SetMaximumSlice(int maximumSliceLength); void SetMaximumSlice(int maximumSliceLength);

View File

@ -22,7 +22,7 @@ public:
virtual void setPC(unsigned int /*address*/) {} virtual void setPC(unsigned int /*address*/) {}
virtual void step() {} virtual void step() {}
virtual void runToBreakpoint() {} virtual void runToBreakpoint() {}
virtual void insertBLR(unsigned int address) {} virtual void insertBLR(unsigned int /*address*/) {}
virtual int getColor(unsigned int /*address*/){return 0xFFFFFFFF;} virtual int getColor(unsigned int /*address*/){return 0xFFFFFFFF;}
virtual std::string getDescription(unsigned int /*address*/) = 0; virtual std::string getDescription(unsigned int /*address*/) = 0;
}; };

View File

@ -91,6 +91,7 @@ union UCPClearReg
UCPClearReg(u16 _hex) {Hex = _hex; } UCPClearReg(u16 _hex) {Hex = _hex; }
}; };
// STATE_TO_SAVE
// variables // variables
UCPStatusReg m_CPStatusReg; UCPStatusReg m_CPStatusReg;
UCPCtrlReg m_CPCtrlReg; UCPCtrlReg m_CPCtrlReg;
@ -114,6 +115,13 @@ inline void WriteHigh(u32& _reg, u16 highbits) {_reg = (_reg & 0x0000FFFF) | ((u
inline u16 ReadLow (u32 _reg) {return (u16)(_reg & 0xFFFF);} inline u16 ReadLow (u32 _reg) {return (u16)(_reg & 0xFFFF);}
inline u16 ReadHigh (u32 _reg) {return (u16)(_reg >> 16);} inline u16 ReadHigh (u32 _reg) {return (u16)(_reg >> 16);}
int et_UpdateInterrupts;
void UpdateInterrupts_Wrapper(u64 userdata, int cyclesLate)
{
UpdateInterrupts();
}
void Init() void Init()
{ {
m_CPStatusReg.Hex = 0; m_CPStatusReg.Hex = 0;
@ -132,6 +140,9 @@ void Init()
fifo.bFF_GPReadEnable = false; fifo.bFF_GPReadEnable = false;
fifo.bFF_GPLinkEnable = false; fifo.bFF_GPLinkEnable = false;
fifo.bFF_BPEnable = false; fifo.bFF_BPEnable = false;
et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper);
#ifdef _WIN32 #ifdef _WIN32
InitializeCriticalSection(&fifo.sync); InitializeCriticalSection(&fifo.sync);
#endif #endif
@ -464,14 +475,9 @@ void UpdateInterrupts()
} }
} }
void UpdateInterrupts_Wrapper(u64 userdata, int cyclesLate)
{
UpdateInterrupts();
}
void UpdateInterruptsFromVideoPlugin() void UpdateInterruptsFromVideoPlugin()
{ {
CoreTiming::ScheduleEvent_Threadsafe(0, &UpdateInterrupts_Wrapper, "CP:UI"); CoreTiming::ScheduleEvent_Threadsafe(0, et_UpdateInterrupts);
} }
} // end of namespace CommandProcessor } // end of namespace CommandProcessor

View File

@ -164,6 +164,8 @@ struct ARDMA
} }
}; };
// STATE_TO_SAVE
u8 *g_ARAM = NULL; u8 *g_ARAM = NULL;
DSPState g_dspState; DSPState g_dspState;
AudioDMA g_audioDMA; AudioDMA g_audioDMA;
@ -179,11 +181,19 @@ void WriteARAM(u8 _iValue, u32 _iAddress);
bool Update_DSP_ReadRegister(); bool Update_DSP_ReadRegister();
void Update_DSP_WriteRegister(); void Update_DSP_WriteRegister();
int et_GenerateDSPInterrupt;
void GenerateDSPInterrupt_Wrapper(u64 userdata, int cyclesLate)
{
GenerateDSPInterrupt((DSPInterruptType)(userdata&0xFFFF), (bool)((userdata>>16) & 1));
}
void Init() void Init()
{ {
g_ARAM = (u8 *)AllocateMemoryPages(ARAM_SIZE); g_ARAM = (u8 *)AllocateMemoryPages(ARAM_SIZE);
g_dspState.DSPControl.Hex = 0; g_dspState.DSPControl.Hex = 0;
g_dspState.DSPControl.DSPHalt = 1; g_dspState.DSPControl.DSPHalt = 1;
et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt_Wrapper);
} }
void Shutdown() void Shutdown()
@ -525,16 +535,11 @@ void GenerateDSPInterrupt(DSPInterruptType type, bool _bSet)
UpdateInterrupts(); UpdateInterrupts();
} }
void GenerateDSPInterrupt_Wrapper(u64 userdata, int cyclesLate)
{
GenerateDSPInterrupt((DSPInterruptType)(userdata&0xFFFF), (bool)((userdata>>16) & 1));
}
// CALLED FROM DSP PLUGIN, POSSIBLY THREADED // CALLED FROM DSP PLUGIN, POSSIBLY THREADED
void GenerateDSPInterruptFromPlugin(DSPInterruptType type, bool _bSet) void GenerateDSPInterruptFromPlugin(DSPInterruptType type, bool _bSet)
{ {
CoreTiming::ScheduleEvent_Threadsafe( CoreTiming::ScheduleEvent_Threadsafe(
0, GenerateDSPInterrupt_Wrapper, "DSPInt", type | (_bSet<<16)); 0, et_GenerateDSPInterrupt, type | (_bSet<<16));
} }
void Update_ARAM_DMA() void Update_ARAM_DMA()

View File

@ -129,11 +129,11 @@ IEXIDevice* EXIDevice_Create(TEXIDevices _EXIDevice)
break; break;
case EXIDEVICE_MEMORYCARD_A: case EXIDEVICE_MEMORYCARD_A:
return new CEXIMemoryCard("MemoryCardA", Core::GetStartupParameter().m_strMemoryCardA); return new CEXIMemoryCard("MemoryCardA", Core::GetStartupParameter().m_strMemoryCardA, 0);
break; break;
case EXIDEVICE_MEMORYCARD_B: case EXIDEVICE_MEMORYCARD_B:
return new CEXIMemoryCard("MemoryCardB", Core::GetStartupParameter().m_strMemoryCardB); return new CEXIMemoryCard("MemoryCardB", Core::GetStartupParameter().m_strMemoryCardB, 1);
break; break;
case EXIDEVICE_IPL: case EXIDEVICE_IPL:

View File

@ -31,9 +31,21 @@
#define MC_STATUS_PROGRAMEERROR 0x08 #define MC_STATUS_PROGRAMEERROR 0x08
#define MC_STATUS_READY 0x01 #define MC_STATUS_READY 0x01
CEXIMemoryCard::CEXIMemoryCard(const std::string& _rName, const std::string& _rFilename) : static CEXIMemoryCard *cards[2];
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
{
CEXIMemoryCard *ptr = cards[userdata];
ptr->Flush();
}
CEXIMemoryCard::CEXIMemoryCard(const std::string& _rName, const std::string& _rFilename, int card_index) :
m_strFilename(_rFilename) m_strFilename(_rFilename)
{ {
this->card_index = card_index;
cards[card_index] = this;
et_this_card = CoreTiming::RegisterEvent(_rName.c_str(), FlushCallback);
nintendo_card_id = 0x00000010; // 16MBit nintendo card nintendo_card_id = 0x00000010; // 16MBit nintendo card
card_id = 0xc221; card_id = 0xc221;
/* nintendo_card_id = 0x00000510; // 16MBit "bigben" card /* nintendo_card_id = 0x00000510; // 16MBit "bigben" card
@ -85,13 +97,7 @@ void CEXIMemoryCard::Flush()
} }
fwrite(memory_card_content, memory_card_size, 1, pFile); fwrite(memory_card_content, memory_card_size, 1, pFile);
fclose(pFile); fclose(pFile);
} Core::DisplayMessage(StringFromFormat("Wrote memory card contents to %s", GetFileName().c_str()), 4000);
void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
{
CEXIMemoryCard *ptr = reinterpret_cast<CEXIMemoryCard*>(userdata);
ptr->Flush();
Core::DisplayMessage(StringFromFormat("Wrote memory card contents to %s", ptr->GetFileName().c_str()), 4000);
} }
@ -161,8 +167,8 @@ void CEXIMemoryCard::SetCS(int cs)
// Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec) // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)
// But first we unschedule already scheduled flushes - no point in flushing once per page for a large write. // But first we unschedule already scheduled flushes - no point in flushing once per page for a large write.
CoreTiming::RemoveEvent(&CEXIMemoryCard::FlushCallback); CoreTiming::RemoveEvent(et_this_card);
CoreTiming::ScheduleEvent(500000000, &CEXIMemoryCard::FlushCallback, "Memory Card Flush", reinterpret_cast<u64>(this)); CoreTiming::ScheduleEvent(500000000, et_this_card, card_index);
break; break;
} }
} }

View File

@ -21,7 +21,7 @@
class CEXIMemoryCard : public IEXIDevice class CEXIMemoryCard : public IEXIDevice
{ {
public: public:
CEXIMemoryCard(const std::string& _rName, const std::string& _rFilename); CEXIMemoryCard(const std::string& _rName, const std::string& _rFilename, int card_index);
virtual ~CEXIMemoryCard(); virtual ~CEXIMemoryCard();
void SetCS(int cs); void SetCS(int cs);
void Update(); void Update();
@ -58,7 +58,8 @@ private:
}; };
std::string m_strFilename; std::string m_strFilename;
int card_index;
int et_this_card;
//! memory card state //! memory card state
// STATE_TO_SAVE // STATE_TO_SAVE

View File

@ -34,6 +34,7 @@
#include "VideoInterface.h" #include "VideoInterface.h"
#include "WII_IPC.h" #include "WII_IPC.h"
#include "../Plugins/Plugin_Video.h" #include "../Plugins/Plugin_Video.h"
#include "../CoreTiming.h"
#include "SystemTimers.h" #include "SystemTimers.h"
#include "../IPC_HLE/WII_IPC_HLE.h" #include "../IPC_HLE/WII_IPC_HLE.h"
@ -75,5 +76,7 @@ namespace HW
WII_IPC_HLE_Interface::Shutdown(); WII_IPC_HLE_Interface::Shutdown();
WII_IPCInterface::Shutdown(); WII_IPCInterface::Shutdown();
Thunk_Shutdown(); Thunk_Shutdown();
CoreTiming::UnregisterAllEvents();
} }
} }

View File

@ -57,12 +57,21 @@ static u16 g_token = 0;
static bool g_bSignalTokenInterrupt; static bool g_bSignalTokenInterrupt;
static bool g_bSignalFinishInterrupt; static bool g_bSignalFinishInterrupt;
int et_SetTokenOnMainThread;
int et_SetFinishOnMainThread;
void UpdateInterrupts(); void UpdateInterrupts();
void SetToken_OnMainThread(u64 userdata, int cyclesLate);
void SetFinish_OnMainThread(u64 userdata, int cyclesLate);
void Init() void Init()
{ {
g_token = 0; g_token = 0;
g_ctrlReg.Hex = 0; g_ctrlReg.Hex = 0;
et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread);
et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread);
} }
void Read16(u16& _uReturnValue, const u32 _iAddress) void Read16(u16& _uReturnValue, const u32 _iAddress)
@ -164,7 +173,7 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate)
void SetToken(const unsigned __int16 _token, const int _bSetTokenAcknowledge) void SetToken(const unsigned __int16 _token, const int _bSetTokenAcknowledge)
{ {
CoreTiming::ScheduleEvent_Threadsafe( CoreTiming::ScheduleEvent_Threadsafe(
0, SetToken_OnMainThread, "SetToken", _token | (_bSetTokenAcknowledge << 16)); 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16));
} }
// SetFinish // SetFinish
@ -172,7 +181,7 @@ void SetToken(const unsigned __int16 _token, const int _bSetTokenAcknowledge)
void SetFinish() void SetFinish()
{ {
CoreTiming::ScheduleEvent_Threadsafe( CoreTiming::ScheduleEvent_Threadsafe(
0, SetFinish_OnMainThread, "SetFinish"); 0, et_SetFinishOnMainThread);
LOG(PIXELENGINE, "VIDEO Set Finish"); LOG(PIXELENGINE, "VIDEO Set Finish");
} }

View File

@ -37,7 +37,6 @@ namespace SystemTimers
u32 CPU_CORE_CLOCK = 486000000u; // 486 mhz (its not 485, stop bugging me!) u32 CPU_CORE_CLOCK = 486000000u; // 486 mhz (its not 485, stop bugging me!)
const int ThrottleFrequency = 60;
s64 fakeDec; s64 fakeDec;
//ratio of TB and Decrementer to clock cycles //ratio of TB and Decrementer to clock cycles
@ -46,6 +45,15 @@ enum {
TIMER_RATIO = 8 TIMER_RATIO = 8
}; };
int et_Dec;
int et_VI;
int et_SI;
int et_AI;
int et_AudioFifo;
int et_DSP;
int et_IPC_HLE;
int et_GPU;
// These are badly educated guesses // These are badly educated guesses
// Feel free to experiment // Feel free to experiment
int int
@ -58,7 +66,6 @@ int
// We should obey that instead of arbitrarly checking at 60fps. // We should obey that instead of arbitrarly checking at 60fps.
SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers
// These are the big question marks IMHO :)
// This one should simply be determined by the increasing counter in AI. // This one should simply be determined by the increasing counter in AI.
AI_PERIOD = GetTicksPerSecond() / 80, AI_PERIOD = GetTicksPerSecond() / 80,
@ -67,7 +74,7 @@ int
// This is completely arbitrary. If we find that we need lower latency, we can just // This is completely arbitrary. If we find that we need lower latency, we can just
// increase this number. // increase this number.
HLE_IPC_PERIOD = GetTicksPerSecond() / 250, IPC_HLE_PERIOD = GetTicksPerSecond() / 250,
// This one is also fairly arbitrary. Every N cycles, run the GPU until it starves (in single core mode only). // This one is also fairly arbitrary. Every N cycles, run the GPU until it starves (in single core mode only).
GPU_PERIOD = 10000; GPU_PERIOD = 10000;
@ -87,14 +94,14 @@ void AICallback(u64 userdata, int cyclesLate)
// Update disk streaming. All that code really needs a revamp, including replacing the codec with the one // Update disk streaming. All that code really needs a revamp, including replacing the codec with the one
// from in_cube. // from in_cube.
AudioInterface::Update(); AudioInterface::Update();
CoreTiming::ScheduleEvent(AI_PERIOD-cyclesLate, &AICallback, "AICallback"); CoreTiming::ScheduleEvent(AI_PERIOD-cyclesLate, et_AI);
} }
void DSPCallback(u64 userdata, int cyclesLate) void DSPCallback(u64 userdata, int cyclesLate)
{ {
// ~1/6th as many cycles as the period PPC-side. // ~1/6th as many cycles as the period PPC-side.
PluginDSP::DSP_Update(DSP_PERIOD / 6); PluginDSP::DSP_Update(DSP_PERIOD / 6);
CoreTiming::ScheduleEvent(DSP_PERIOD-cyclesLate, &DSPCallback, "DSPCallback"); CoreTiming::ScheduleEvent(DSP_PERIOD-cyclesLate, et_DSP);
} }
void AudioFifoCallback(u64 userdata, int cyclesLate) void AudioFifoCallback(u64 userdata, int cyclesLate)
@ -102,19 +109,19 @@ void AudioFifoCallback(u64 userdata, int cyclesLate)
int period = CPU_CORE_CLOCK / (AudioInterface::GetDSPSampleRate() * 4 / 32); int period = CPU_CORE_CLOCK / (AudioInterface::GetDSPSampleRate() * 4 / 32);
DSP::UpdateAudioDMA(); // Push audio to speakers. DSP::UpdateAudioDMA(); // Push audio to speakers.
CoreTiming::ScheduleEvent(period - cyclesLate, &AudioFifoCallback, "AudioFifoCallback"); CoreTiming::ScheduleEvent(period - cyclesLate, et_AudioFifo);
} }
void IPC_HLE_UpdateCallback(u64 userdata, int cyclesLate) void IPC_HLE_UpdateCallback(u64 userdata, int cyclesLate)
{ {
WII_IPC_HLE_Interface::Update(); WII_IPC_HLE_Interface::Update();
CoreTiming::ScheduleEvent(HLE_IPC_PERIOD-cyclesLate, &IPC_HLE_UpdateCallback, "IPC_HLE_UpdateCallback"); CoreTiming::ScheduleEvent(IPC_HLE_PERIOD-cyclesLate, et_IPC_HLE);
} }
void VICallback(u64 userdata, int cyclesLate) void VICallback(u64 userdata, int cyclesLate)
{ {
VideoInterface::Update(); VideoInterface::Update();
CoreTiming::ScheduleEvent(VI_PERIOD-cyclesLate, &VICallback, "VICallback"); CoreTiming::ScheduleEvent(VI_PERIOD-cyclesLate, et_VI);
} }
void SICallback(u64 userdata, int cyclesLate) void SICallback(u64 userdata, int cyclesLate)
@ -123,7 +130,7 @@ void SICallback(u64 userdata, int cyclesLate)
PatchEngine_ApplyFramePatches(); PatchEngine_ApplyFramePatches();
// OK, do what we are here to do. // OK, do what we are here to do.
SerialInterface::UpdateDevices(); SerialInterface::UpdateDevices();
CoreTiming::ScheduleEvent(SI_PERIOD-cyclesLate, &SICallback, "SICallback"); CoreTiming::ScheduleEvent(SI_PERIOD-cyclesLate, et_SI);
} }
void DecrementerCallback(u64 userdata, int cyclesLate) void DecrementerCallback(u64 userdata, int cyclesLate)
@ -138,8 +145,8 @@ void DecrementerSet()
{ {
u32 decValue = PowerPC::ppcState.spr[SPR_DEC]; u32 decValue = PowerPC::ppcState.spr[SPR_DEC];
fakeDec = decValue*TIMER_RATIO; fakeDec = decValue*TIMER_RATIO;
CoreTiming::RemoveEvent(DecrementerCallback); CoreTiming::RemoveEvent(et_Dec);
CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, DecrementerCallback, "DecCallback"); CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, et_Dec);
} }
void AdvanceCallback(int cyclesExecuted) void AdvanceCallback(int cyclesExecuted)
@ -151,43 +158,10 @@ void AdvanceCallback(int cyclesExecuted)
PowerPC::ppcState.spr[SPR_DEC] = (u32)fakeDec / TIMER_RATIO; PowerPC::ppcState.spr[SPR_DEC] = (u32)fakeDec / TIMER_RATIO;
} }
void RunGPUCallback(u64 userdata, int cyclesLate) void GPUCallback(u64 userdata, int cyclesLate)
{ {
CommandProcessor::CatchUpGPU(); CommandProcessor::CatchUpGPU();
CoreTiming::ScheduleEvent(GPU_PERIOD-cyclesLate, &RunGPUCallback, "RunGPUCallback"); CoreTiming::ScheduleEvent(GPU_PERIOD-cyclesLate, et_GPU);
}
// TODO(ector): improve, by using a more accurate timer
// calculate the timing over the past 7 frames
// calculating over all time doesn't work : if it's slow for a while, will run like crazy after that
// calculating over just 1 frame is too shaky
#define HISTORYLENGTH 7
int timeHistory[HISTORYLENGTH] = {0,0,0,0,0};
void Throttle(u64 userdata, int cyclesLate)
{
if (!Core::GetStartupParameter().bThrottle)
return;
static Common::Timer timer;
for (int i=0; i<HISTORYLENGTH-1; i++)
timeHistory[i] = timeHistory[i+1];
int t = (int)timer.GetTimeDifference();
//timer.Update();
if (timeHistory[0] != 0)
{
const int delta = (int)(1000*(HISTORYLENGTH-1)/ThrottleFrequency);
while (t - timeHistory[0] < delta)
{
// ugh, busy wait
Common::SleepCurrentThread(0);
t = (int)timer.GetTimeDifference();
}
}
timeHistory[HISTORYLENGTH-1] = t;
CoreTiming::ScheduleEvent((int)(GetTicksPerSecond()/ThrottleFrequency)-cyclesLate, &Throttle, "Throttle");
} }
void Init() void Init()
@ -202,7 +176,7 @@ void Init()
AI_PERIOD = GetTicksPerSecond() / 80; AI_PERIOD = GetTicksPerSecond() / 80;
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f); DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f);
HLE_IPC_PERIOD = (int)(GetTicksPerSecond() * 0.003f); IPC_HLE_PERIOD = (int)(GetTicksPerSecond() * 0.003f);
} }
else else
{ {
@ -215,25 +189,28 @@ void Init()
DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f); DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f);
} }
Common::Timer::IncreaseResolution(); Common::Timer::IncreaseResolution();
memset(timeHistory, 0, sizeof(timeHistory));
CoreTiming::Clear();
CoreTiming::ScheduleEvent((int)(GetTicksPerSecond() / ThrottleFrequency), &Throttle, "Throttle"); et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback);
et_AI = CoreTiming::RegisterEvent("AICallback", AICallback);
et_VI = CoreTiming::RegisterEvent("VICallback", VICallback);
et_SI = CoreTiming::RegisterEvent("SICallback", SICallback);
et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback);
et_GPU = CoreTiming::RegisterEvent("GPUCallback", GPUCallback);
et_AudioFifo = CoreTiming::RegisterEvent("AudioFifoCallback", AudioFifoCallback);
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
CoreTiming::ScheduleEvent(AI_PERIOD, &AICallback, "AICallback"); CoreTiming::ScheduleEvent(AI_PERIOD, et_AI);
CoreTiming::ScheduleEvent(VI_PERIOD, &VICallback, "VICallback"); CoreTiming::ScheduleEvent(VI_PERIOD, et_VI);
CoreTiming::ScheduleEvent(DSP_PERIOD, &DSPCallback, "DSPCallback"); CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP);
CoreTiming::ScheduleEvent(SI_PERIOD, &SICallback, "SICallback"); CoreTiming::ScheduleEvent(SI_PERIOD, et_SI);
CoreTiming::ScheduleEvent(CPU_CORE_CLOCK / (32000 * 4 / 32), &AudioFifoCallback, "AudioFifoCallback"); CoreTiming::ScheduleEvent(CPU_CORE_CLOCK / (32000 * 4 / 32), et_AudioFifo);
if (!Core::GetStartupParameter().bUseDualCore) if (!Core::GetStartupParameter().bUseDualCore)
CoreTiming::ScheduleEvent(GPU_PERIOD, &RunGPUCallback, "RunGPUCallback"); CoreTiming::ScheduleEvent(GPU_PERIOD, et_GPU);
if (Core::GetStartupParameter().bWii) if (Core::GetStartupParameter().bWii)
{ CoreTiming::ScheduleEvent(IPC_HLE_PERIOD, et_IPC_HLE);
CoreTiming::ScheduleEvent(HLE_IPC_PERIOD, &IPC_HLE_UpdateCallback, "IPC_HLE_UpdateCallback");
}
CoreTiming::RegisterAdvanceCallback(&AdvanceCallback); CoreTiming::RegisterAdvanceCallback(&AdvanceCallback);
} }
@ -243,4 +220,4 @@ void Shutdown()
CoreTiming::Clear(); CoreTiming::Clear();
} }
} } // namespace

View File

@ -66,7 +66,6 @@ void SConfig::SaveSettings()
ini.Set("Core", "HLEBios", m_LocalCoreStartupParameter.bHLEBios); ini.Set("Core", "HLEBios", m_LocalCoreStartupParameter.bHLEBios);
ini.Set("Core", "UseDynarec", m_LocalCoreStartupParameter.bUseDynarec); ini.Set("Core", "UseDynarec", m_LocalCoreStartupParameter.bUseDynarec);
ini.Set("Core", "UseDualCore", m_LocalCoreStartupParameter.bUseDualCore); ini.Set("Core", "UseDualCore", m_LocalCoreStartupParameter.bUseDualCore);
ini.Set("Core", "Throttle", m_LocalCoreStartupParameter.bThrottle);
ini.Set("Core", "LockThreads", m_LocalCoreStartupParameter.bLockThreads); ini.Set("Core", "LockThreads", m_LocalCoreStartupParameter.bLockThreads);
ini.Set("Core", "DefaultGCM", m_LocalCoreStartupParameter.m_strDefaultGCM); ini.Set("Core", "DefaultGCM", m_LocalCoreStartupParameter.m_strDefaultGCM);
ini.Set("Core", "OptimizeQuantizers", m_LocalCoreStartupParameter.bOptimizeQuantizers); ini.Set("Core", "OptimizeQuantizers", m_LocalCoreStartupParameter.bOptimizeQuantizers);
@ -117,7 +116,6 @@ void SConfig::LoadSettings()
ini.Get("Core", "HLEBios", &m_LocalCoreStartupParameter.bHLEBios, true); ini.Get("Core", "HLEBios", &m_LocalCoreStartupParameter.bHLEBios, true);
ini.Get("Core", "UseDynarec", &m_LocalCoreStartupParameter.bUseDynarec, true); ini.Get("Core", "UseDynarec", &m_LocalCoreStartupParameter.bUseDynarec, true);
ini.Get("Core", "UseDualCore", &m_LocalCoreStartupParameter.bUseDualCore, false); ini.Get("Core", "UseDualCore", &m_LocalCoreStartupParameter.bUseDualCore, false);
ini.Get("Core", "Throttle", &m_LocalCoreStartupParameter.bThrottle, true);
ini.Get("Core", "LockThreads", &m_LocalCoreStartupParameter.bLockThreads, true); ini.Get("Core", "LockThreads", &m_LocalCoreStartupParameter.bLockThreads, true);
ini.Get("Core", "OptimizeQuantizers", &m_LocalCoreStartupParameter.bOptimizeQuantizers, true); ini.Get("Core", "OptimizeQuantizers", &m_LocalCoreStartupParameter.bOptimizeQuantizers, true);
} }

View File

@ -94,7 +94,6 @@ EVT_MENU(IDM_BROWSE, CFrame::OnBrowse)
EVT_MENU(IDM_MEMCARD, CFrame::OnMemcard) EVT_MENU(IDM_MEMCARD, CFrame::OnMemcard)
EVT_MENU(IDM_TOGGLE_FULLSCREEN, CFrame::OnToggleFullscreen) EVT_MENU(IDM_TOGGLE_FULLSCREEN, CFrame::OnToggleFullscreen)
EVT_MENU(IDM_TOGGLE_DUALCORE, CFrame::OnToggleDualCore) EVT_MENU(IDM_TOGGLE_DUALCORE, CFrame::OnToggleDualCore)
EVT_MENU(IDM_TOGGLE_THROTTLE, CFrame::OnToggleThrottle)
EVT_MENU(IDM_TOGGLE_TOOLBAR, CFrame::OnToggleToolbar) EVT_MENU(IDM_TOGGLE_TOOLBAR, CFrame::OnToggleToolbar)
EVT_HOST_COMMAND(wxID_ANY, CFrame::OnHostMessage) EVT_HOST_COMMAND(wxID_ANY, CFrame::OnHostMessage)
END_EVENT_TABLE() END_EVENT_TABLE()
@ -197,8 +196,6 @@ void CFrame::CreateMenu()
pOptionsMenu->Append(IDM_TOGGLE_FULLSCREEN, _T("&Fullscreen")); pOptionsMenu->Append(IDM_TOGGLE_FULLSCREEN, _T("&Fullscreen"));
pOptionsMenu->AppendCheckItem(IDM_TOGGLE_DUALCORE, _T("&Dual-core (unstable!)")); pOptionsMenu->AppendCheckItem(IDM_TOGGLE_DUALCORE, _T("&Dual-core (unstable!)"));
pOptionsMenu->Check(IDM_TOGGLE_DUALCORE, SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore); pOptionsMenu->Check(IDM_TOGGLE_DUALCORE, SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore);
pOptionsMenu->AppendCheckItem(IDM_TOGGLE_THROTTLE, _T("&Enable throttle"));
pOptionsMenu->Check(IDM_TOGGLE_THROTTLE, SConfig::GetInstance().m_LocalCoreStartupParameter.bThrottle);
m_pMenuBar->Append(pOptionsMenu, _T("&Options")); m_pMenuBar->Append(pOptionsMenu, _T("&Options"));
// misc menu // misc menu
@ -499,12 +496,6 @@ void CFrame::OnToggleDualCore(wxCommandEvent& WXUNUSED (event))
SConfig::GetInstance().SaveSettings(); SConfig::GetInstance().SaveSettings();
} }
void CFrame::OnToggleThrottle(wxCommandEvent& WXUNUSED (event))
{
SConfig::GetInstance().m_LocalCoreStartupParameter.bThrottle = !SConfig::GetInstance().m_LocalCoreStartupParameter.bThrottle;
SConfig::GetInstance().SaveSettings();
}
void CFrame::OnToggleToolbar(wxCommandEvent& event) void CFrame::OnToggleToolbar(wxCommandEvent& event)
{ {
wxToolBarBase* toolBar = GetToolBar(); wxToolBarBase* toolBar = GetToolBar();

View File

@ -34,7 +34,6 @@ enum
IDM_CONFIG_PAD_PLUGIN, IDM_CONFIG_PAD_PLUGIN,
IDM_TOGGLE_FULLSCREEN, IDM_TOGGLE_FULLSCREEN,
IDM_TOGGLE_DUALCORE, IDM_TOGGLE_DUALCORE,
IDM_TOGGLE_THROTTLE,
IDM_TOGGLE_TOOLBAR, IDM_TOGGLE_TOOLBAR,
IDM_NOTIFYMAPLOADED, IDM_NOTIFYMAPLOADED,
IDM_UPDATELOGDISPLAY, IDM_UPDATELOGDISPLAY,