Add Yield/SwitchToThread in SNES IPCRingBuffer. Improves performance on Core 2 Duo, about the same on i5, and will prevent it from going < 1 fps on a single core machine :).
This commit is contained in:
parent
60ed815b68
commit
2f5a7543ca
|
@ -4,13 +4,14 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// a ring buffer suitable for IPC. It uses a spinlock to control access, so overhead can be kept to a minimum.
|
||||
/// you'll probably need to use this in pairs, so it will occupy two threads and degrade entirely if there is less than one processor.
|
||||
/// you'll probably need to use this in pairs, so it will occupy two threads.
|
||||
/// </summary>
|
||||
public unsafe class IPCRingBuffer : IDisposable
|
||||
{
|
||||
|
@ -80,6 +81,7 @@ namespace BizHawk.Common
|
|||
if (Available > amt)
|
||||
return;
|
||||
//this is a greedy spinlock.
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,6 +121,7 @@ namespace BizHawk.Common
|
|||
if (available > 0)
|
||||
return available;
|
||||
//this is a greedy spinlock.
|
||||
Thread.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -231,6 +231,7 @@ private:
|
|||
if (Available() > amt)
|
||||
return;
|
||||
//this is a greedy spinlock.
|
||||
SwitchToThread();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +285,7 @@ public:
|
|||
if (available > 0)
|
||||
return available;
|
||||
//this is a greedy spinlock.
|
||||
SwitchToThread();
|
||||
//NOTE: it's annoying right now because libsnes processes die and eat a whole core.
|
||||
//we need to gracefully exit somehow
|
||||
}
|
||||
|
@ -504,71 +506,71 @@ Blob ReadPipeBlob()
|
|||
return ret;
|
||||
}
|
||||
|
||||
void snes_video_refresh(const uint32_t *data, unsigned width, unsigned height)
|
||||
{
|
||||
void snes_video_refresh(const uint32_t *data, unsigned width, unsigned height)
|
||||
{
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_video_refresh;
|
||||
s_EmulationControl.cb_video_refresh_params.data = data;
|
||||
s_EmulationControl.cb_video_refresh_params.width = width;
|
||||
s_EmulationControl.cb_video_refresh_params.height = height;
|
||||
|
||||
SETCONTROL;
|
||||
}
|
||||
|
||||
bool audio_en = false;
|
||||
static const int AUDIOBUFFER_SIZE = 44100*2;
|
||||
uint16_t audiobuffer[AUDIOBUFFER_SIZE];
|
||||
int audiobuffer_idx = 0;
|
||||
|
||||
void SIG_FlushAudio()
|
||||
{
|
||||
SETCONTROL;
|
||||
}
|
||||
|
||||
bool audio_en = false;
|
||||
static const int AUDIOBUFFER_SIZE = 44100*2;
|
||||
uint16_t audiobuffer[AUDIOBUFFER_SIZE];
|
||||
int audiobuffer_idx = 0;
|
||||
|
||||
void SIG_FlushAudio()
|
||||
{
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_audio_flush;
|
||||
SETCONTROL;
|
||||
}
|
||||
|
||||
//this is the raw callback from the emulator internals when a new audio sample is available
|
||||
void snes_audio_sample(uint16_t left, uint16_t right)
|
||||
{
|
||||
if(!audio_en) return;
|
||||
|
||||
//if theres no room in the audio buffer, we need to send a flush signal
|
||||
if(audiobuffer_idx == AUDIOBUFFER_SIZE)
|
||||
SIG_FlushAudio();
|
||||
|
||||
audiobuffer[audiobuffer_idx++] = left;
|
||||
}
|
||||
|
||||
//this is the raw callback from the emulator internals when a new audio sample is available
|
||||
void snes_audio_sample(uint16_t left, uint16_t right)
|
||||
{
|
||||
if(!audio_en) return;
|
||||
|
||||
//if theres no room in the audio buffer, we need to send a flush signal
|
||||
if(audiobuffer_idx == AUDIOBUFFER_SIZE)
|
||||
SIG_FlushAudio();
|
||||
|
||||
audiobuffer[audiobuffer_idx++] = left;
|
||||
audiobuffer[audiobuffer_idx++] = right;
|
||||
}
|
||||
|
||||
void snes_input_poll(void)
|
||||
{
|
||||
}
|
||||
|
||||
void snes_input_poll(void)
|
||||
{
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_poll;
|
||||
SETCONTROL;
|
||||
}
|
||||
int16_t snes_input_state(unsigned port, unsigned device, unsigned index, unsigned id)
|
||||
{
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_poll;
|
||||
SETCONTROL;
|
||||
}
|
||||
int16_t snes_input_state(unsigned port, unsigned device, unsigned index, unsigned id)
|
||||
{
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_state;
|
||||
s_EmulationControl.cb_input_state_params.port = port;
|
||||
s_EmulationControl.cb_input_state_params.device = device;
|
||||
s_EmulationControl.cb_input_state_params.index = index;
|
||||
s_EmulationControl.cb_input_state_params.id = id;
|
||||
SETCONTROL;
|
||||
return s_EmulationControl.cb_input_state_params.result;
|
||||
}
|
||||
void snes_input_notify(int index)
|
||||
{
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_state;
|
||||
s_EmulationControl.cb_input_state_params.port = port;
|
||||
s_EmulationControl.cb_input_state_params.device = device;
|
||||
s_EmulationControl.cb_input_state_params.index = index;
|
||||
s_EmulationControl.cb_input_state_params.id = id;
|
||||
SETCONTROL;
|
||||
return s_EmulationControl.cb_input_state_params.result;
|
||||
}
|
||||
void snes_input_notify(int index)
|
||||
{
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_notify;
|
||||
s_EmulationControl.cb_input_notify_params.index = index;
|
||||
SETCONTROL;
|
||||
}
|
||||
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_input_notify;
|
||||
s_EmulationControl.cb_input_notify_params.index = index;
|
||||
SETCONTROL;
|
||||
}
|
||||
|
||||
void snes_trace(const char *msg)
|
||||
{
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_trace;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_trace;
|
||||
s_EmulationControl.cb_trace_params.msg = msg;
|
||||
SETCONTROL;
|
||||
}
|
||||
|
@ -606,24 +608,24 @@ public:
|
|||
|
||||
static std::map<void*,SharedMemoryBlock*> memHandleTable;
|
||||
|
||||
void* implementation_snes_allocSharedMemory()
|
||||
{
|
||||
const char* memtype = s_EmulationControl.cb_allocSharedMemory_params.memtype;
|
||||
size_t amt = s_EmulationControl.cb_allocSharedMemory_params.amt;
|
||||
|
||||
if(!running)
|
||||
{
|
||||
s_EmulationControl.cb_allocSharedMemory_params.result = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//printf("WritePipe(eMessage_SIG_allocSharedMemory)\n");
|
||||
WritePipe(eMessage_SIG_allocSharedMemory);
|
||||
WritePipeString(memtype);
|
||||
WritePipe(amt);
|
||||
|
||||
std::string blockname = ReadPipeString();
|
||||
|
||||
void* implementation_snes_allocSharedMemory()
|
||||
{
|
||||
const char* memtype = s_EmulationControl.cb_allocSharedMemory_params.memtype;
|
||||
size_t amt = s_EmulationControl.cb_allocSharedMemory_params.amt;
|
||||
|
||||
if(!running)
|
||||
{
|
||||
s_EmulationControl.cb_allocSharedMemory_params.result = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//printf("WritePipe(eMessage_SIG_allocSharedMemory)\n");
|
||||
WritePipe(eMessage_SIG_allocSharedMemory);
|
||||
WritePipeString(memtype);
|
||||
WritePipe(amt);
|
||||
|
||||
std::string blockname = ReadPipeString();
|
||||
|
||||
auto mapfile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, blockname.c_str());
|
||||
|
||||
if(mapfile == INVALID_HANDLE_VALUE)
|
||||
|
@ -636,36 +638,36 @@ void* implementation_snes_allocSharedMemory()
|
|||
smb->handle = mapfile;
|
||||
|
||||
memHandleTable[ptr] = smb;
|
||||
|
||||
s_EmulationControl.cb_allocSharedMemory_params.result = ptr;
|
||||
|
||||
s_EmulationControl.cb_allocSharedMemory_params.result = ptr;
|
||||
}
|
||||
|
||||
void* snes_allocSharedMemory(const char* memtype, size_t amt)
|
||||
{
|
||||
//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)
|
||||
if(!running) return NULL;
|
||||
|
||||
void* snes_allocSharedMemory(const char* memtype, size_t amt)
|
||||
{
|
||||
//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)
|
||||
if(!running) return NULL;
|
||||
|
||||
s_EmulationControl.exitReason = eEmulationExitReason_SIG;
|
||||
s_EmulationControl.exitCallbackType = eEmulationCallback_snes_allocSharedMemory;
|
||||
s_EmulationControl.cb_allocSharedMemory_params.memtype = memtype;
|
||||
s_EmulationControl.cb_allocSharedMemory_params.amt = amt;
|
||||
SETCONTROL;
|
||||
|
||||
return s_EmulationControl.cb_allocSharedMemory_params.result;
|
||||
}
|
||||
|
||||
void implementation_snes_freeSharedMemory()
|
||||
{
|
||||
void* ptr = s_EmulationControl.cb_freeSharedMemory_params.ptr;
|
||||
|
||||
return s_EmulationControl.cb_allocSharedMemory_params.result;
|
||||
}
|
||||
|
||||
void implementation_snes_freeSharedMemory()
|
||||
{
|
||||
void* ptr = s_EmulationControl.cb_freeSharedMemory_params.ptr;
|
||||
if(!ptr) return;
|
||||
auto smb = memHandleTable.find(ptr)->second;
|
||||
UnmapViewOfFile(ptr);
|
||||
CloseHandle(smb->handle);
|
||||
//printf("WritePipe(eMessage_SIG_freeSharedMemory);\n");
|
||||
WritePipe(eMessage_SIG_freeSharedMemory);
|
||||
WritePipeString(smb->memtype.c_str());
|
||||
}
|
||||
|
||||
WritePipe(eMessage_SIG_freeSharedMemory);
|
||||
WritePipeString(smb->memtype.c_str());
|
||||
}
|
||||
|
||||
void snes_freeSharedMemory(void* ptr)
|
||||
{
|
||||
//its important that this happen before the message marshaling because allocation/free attempts can happen before the marshaling is setup (or at shutdown time, in case of errors?)
|
||||
|
@ -997,7 +999,7 @@ TOP:
|
|||
break;
|
||||
}
|
||||
case eEmulationCallback_snes_audio_flush:
|
||||
Handle_SIG_audio_flush();
|
||||
Handle_SIG_audio_flush();
|
||||
break;
|
||||
case eEmulationCallback_snes_input_poll:
|
||||
WritePipe(eMessage_SIG_input_poll);
|
||||
|
@ -1203,13 +1205,13 @@ void emuthread()
|
|||
//EDIT - well, we changed that, but.. we still want this probably, for debugging and stuff
|
||||
for(;;)
|
||||
{
|
||||
SNES::scheduler.sync = SNES::Scheduler::SynchronizeMode::None;
|
||||
SNES::scheduler.clearExitReason();
|
||||
SNES::scheduler.enter();
|
||||
if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::FrameEvent)
|
||||
{
|
||||
SNES::video.update();
|
||||
break;
|
||||
SNES::scheduler.sync = SNES::Scheduler::SynchronizeMode::None;
|
||||
SNES::scheduler.clearExitReason();
|
||||
SNES::scheduler.enter();
|
||||
if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::FrameEvent)
|
||||
{
|
||||
SNES::video.update();
|
||||
break;
|
||||
}
|
||||
//not used yet
|
||||
if(SNES::scheduler.exit_reason() == SNES::Scheduler::ExitReason::DebuggerEvent)
|
||||
|
@ -1273,7 +1275,7 @@ int xmain(int argc, char** argv)
|
|||
printf("running\n");
|
||||
|
||||
RunControlMessageLoop();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue