diff --git a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesApi.cs index 9770e3bc96..30dbef84b2 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesApi.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesApi.cs @@ -1,4 +1,9 @@ -using System; +//controls whether the new shared memory ring buffer communication system is used +//on the whole it seems to boost performance slightly for me, at the cost of exacerbating spikes +//not sure if we should keep it +//#define USE_BUFIO + +using System; using System.Linq; using System.Diagnostics; using System.Globalization; @@ -10,6 +15,291 @@ using System.IO.MemoryMappedFiles; namespace BizHawk.Emulation.Consoles.Nintendo.SNES { + + /// + /// 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. + /// + unsafe class IPCRingBuffer : IDisposable + { + MemoryMappedFile mmf; + MemoryMappedViewAccessor mmva; + + byte* mmvaPtr; + volatile byte* begin; + volatile int* head, tail; + int bufsize; + + public string Id; + public bool Owner; + + /// + /// note that a few bytes of the size will be used for a management area + /// + /// + public void Allocate(int size) + { + Owner = true; + Id = SuperGloballyUniqueID.Next(); + mmf = MemoryMappedFile.CreateNew(Id, size); + Setup(size); + } + + public void Open(string id) + { + Id = id; + mmf = MemoryMappedFile.OpenExisting(id); + Setup(-1); + } + + void Setup(int size) + { + bool init = size != -1; + + mmva = mmf.CreateViewAccessor(); + byte* tempPtr = null; + mmva.SafeMemoryMappedViewHandle.AcquirePointer(ref tempPtr); + mmvaPtr = tempPtr; + + //setup management area + head = (int*)mmvaPtr; + tail = (int*)mmvaPtr + 1; + int* bufsizeptr = (int*)mmvaPtr + 2; + begin = mmvaPtr + 12; + + if (init) + *bufsizeptr = bufsize = size - 12; + else bufsize = *bufsizeptr; + } + + public void Dispose() + { + if (mmf == null) return; + mmva.Dispose(); + mmf.Dispose(); + mmf = null; + } + + void WaitForWriteCapacity(int amt) + { + for (; ; ) + { + //dont return when available == amt because then we would consume the buffer and be unable to distinguish between full and empty + if (Available > amt) + return; + //this is a greedy spinlock. + } + } + + public int Available + { + get + { + return bufsize - Size; + } + } + + + public int Size + { + get + { + int h = *head; + int t = *tail; + int size = h - t; + if (size < 0) size += bufsize; + else if (size >= bufsize) + { + //shouldnt be possible for size to be anything but bufsize here, but just in case... + if (size > bufsize) + throw new InvalidOperationException("Critical error code pickpocket panda! This MUST be reported to the developers!"); + size = 0; + } + return size; + } + } + + int WaitForSomethingToRead() + { + for (; ; ) + { + int available = Size; + if (available > 0) + return available; + //this is a greedy spinlock. + } + } + + [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] + static unsafe extern void* CopyMemory(void* dest, void* src, ulong count); + + public void Write(IntPtr ptr, int amt) + { + byte* bptr = (byte*)ptr; + int ofs = 0; + while (amt > 0) + { + int todo = amt; + + //make sure we don't write a big chunk beyond the end of the buffer + int remain = bufsize - *head; + if (todo > remain) todo = remain; + + //dont request the entire buffer. we would never get that much available, because we never completely fill up the buffer + if (todo > bufsize - 1) todo = bufsize - 1; + + //a super efficient approach would chunk this several times maybe instead of waiting for the buffer to be emptied before writing again. but who cares + WaitForWriteCapacity(todo); + + //messages are likely to be small. we should probably just loop to copy in here. but for now.. + CopyMemory(begin + *head, bptr + ofs, (ulong)todo); + + amt -= todo; + ofs += todo; + *head += todo; + if (*head >= bufsize) *head -= bufsize; + } + } + + public void Read(IntPtr ptr, int amt) + { + byte* bptr = (byte*)ptr; + int ofs = 0; + while (amt > 0) + { + int available = WaitForSomethingToRead(); + int todo = amt; + if (todo > available) todo = available; + + //make sure we don't read a big chunk beyond the end of the buffer + int remain = bufsize - *tail; + if (todo > remain) todo = remain; + + //messages are likely to be small. we should probably just loop to copy in here. but for now.. + CopyMemory(bptr + ofs, begin + *tail, (ulong)todo); + + amt -= todo; + ofs += todo; + *tail += todo; + if (*tail >= bufsize) *tail -= bufsize; + } + } + + public class Tester + { + Queue shazam = new Queue(); + string bufid; + + unsafe void a() + { + var buf = new IPCRingBuffer(); + buf.Allocate(1024); + bufid = buf.Id; + + int ctr = 0; + for (; ; ) + { + Random r = new Random(ctr); + ctr++; + Console.WriteLine("Writing: {0}", ctr); + + byte[] temp = new byte[r.Next(2048) + 1]; + r.NextBytes(temp); + for (int i = 0; i < temp.Length; i++) + lock (shazam) shazam.Enqueue(temp[i]); + fixed (byte* tempptr = &temp[0]) + buf.Write((IntPtr)tempptr, temp.Length); + //Console.WriteLine("wrote {0}; ringbufsize={1}", temp.Length, buf.Size); + } + } + + unsafe void b() + { + var buf = new IPCRingBuffer(); + buf.Open(bufid); + + int ctr = 0; + for (; ; ) + { + Random r = new Random(ctr + 1000); + ctr++; + Console.WriteLine("Reading : {0}", ctr); + + int tryRead = r.Next(2048) + 1; + byte[] temp = new byte[tryRead]; + fixed (byte* tempptr = &temp[0]) + buf.Read((IntPtr)tempptr, tryRead); + //Console.WriteLine("read {0}; ringbufsize={1}", temp.Length, buf.Size); + for (int i = 0; i < temp.Length; i++) + { + byte b; + lock (shazam) b = shazam.Dequeue(); + Debug.Assert(b == temp[i]); + } + } + } + + public void Test() + { + var ta = new System.Threading.Thread(a); + var tb = new System.Threading.Thread(b); + ta.Start(); + while (bufid == null) { } + tb.Start(); + } + } + } + + unsafe class IPCRingBufferStream : Stream + { + public IPCRingBufferStream(IPCRingBuffer buf) + { + this.buf = buf; + } + IPCRingBuffer buf; + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return true; } } + public override void Flush() { } + public override long Length { get { throw new NotImplementedException(); } } + public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } + public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); } + public override void SetLength(long value) { throw new NotImplementedException(); } + public override int Read(byte[] buffer, int offset, int count) + { + if (buffer.Length < offset + count) throw new IndexOutOfRangeException(); + fixed (byte* pbuffer = &buffer[offset]) + buf.Read((IntPtr)pbuffer, count); + return count; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (buffer.Length < offset + count) throw new IndexOutOfRangeException(); + fixed (byte* pbuffer = &buffer[offset]) + buf.Write((IntPtr)pbuffer, count); + } + } + + class SuperGloballyUniqueID + { + public static string Next() + { + int myctr; + lock (typeof(SuperGloballyUniqueID)) + myctr = ctr++; + return staticPart + "-" + myctr; + } + + static SuperGloballyUniqueID() + { + staticPart = "bizhawk-" + System.Diagnostics.Process.GetCurrentProcess().Id.ToString() + "-" + Guid.NewGuid().ToString(); + } + + static int ctr; + static string staticPart; + } + public unsafe class LibsnesApi : IDisposable { //this wouldve been the ideal situation to learn protocol buffers, but since the number of messages here is so limited, it took less time to roll it by hand. @@ -39,6 +329,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES MemoryMappedFile mmf; MemoryMappedViewAccessor mmva; byte* mmvaPtr; + IPCRingBuffer rbuf, wbuf; + IPCRingBufferStream rbufstr, wbufstr; + SwitcherStream rstream, wstream; + bool bufio; public enum eMessage : int { @@ -97,6 +391,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES eMessage_snes_allocSharedMemory, eMessage_snes_freeSharedMemory, eMessage_GetMemoryIdName, + + eMessage_SetBuffer, + eMessage_BeginBufferIO, + eMessage_EndBufferIO }; static bool DryRun(string exePath) @@ -155,8 +453,84 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES //TODO - start a thread to wait for process to exit and gracefully handle errors? how about the pipe? pipe.WaitForConnection(); - bwPipe = new BinaryWriter(pipe); - brPipe = new BinaryReader(pipe); + + rbuf = new IPCRingBuffer(); + wbuf = new IPCRingBuffer(); + rbuf.Allocate(1024); + wbuf.Allocate(1024); + rbufstr = new IPCRingBufferStream(rbuf); + wbufstr = new IPCRingBufferStream(wbuf); + + rstream = new SwitcherStream(); + wstream = new SwitcherStream(); + + rstream.SetCurrStream(pipe); + wstream.SetCurrStream(pipe); + + brPipe = new BinaryReader(rstream); + bwPipe = new BinaryWriter(wstream); + + WritePipeMessage(eMessage.eMessage_SetBuffer); + bwPipe.Write(1); + WritePipeString(rbuf.Id); + WritePipeMessage(eMessage.eMessage_SetBuffer); + bwPipe.Write(0); + WritePipeString(wbuf.Id); + bwPipe.Flush(); + } + + class SwitcherStream : Stream + { + //switchstream method? flush old stream? + Stream CurrStream = null; + + public void SetCurrStream(Stream str) { CurrStream = str; } + + public SwitcherStream() + { + } + + public override bool CanRead { get { return CurrStream.CanRead; } } + public override bool CanSeek { get { return CurrStream.CanSeek; } } + public override bool CanWrite { get { return CurrStream.CanWrite; } } + public override void Flush() + { + CurrStream.Flush(); + } + + public override long Length { get { return CurrStream.Length; } } + + public override long Position + { + get + { + return CurrStream.Position; + } + set + { + CurrStream.Position = Position; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + return CurrStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return CurrStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + CurrStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + CurrStream.Write(buffer, offset, count); + } } public void Dispose() @@ -167,6 +541,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES pipe.Dispose(); mmva.Dispose(); mmf.Dispose(); + rbuf.Dispose(); + wbuf.Dispose(); + } + + public void BeginBufferIO() + { +#if USE_BUFIO + bufio = true; + WritePipeMessage(eMessage.eMessage_BeginBufferIO); + rstream.SetCurrStream(rbufstr); + wstream.SetCurrStream(wbufstr); +#endif + } + + public void EndBufferIO() + { +#if USE_BUFIO + bufio = false; + WritePipeMessage(eMessage.eMessage_EndBufferIO); + rstream.SetCurrStream(pipe); + wstream.SetCurrStream(pipe); +#endif } void WritePipeString(string str) @@ -186,14 +582,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES { bwPipe.Write(blob.Length); bwPipe.Write(blob); + bwPipe.Flush(); } public int MessageCounter; void WritePipeMessage(eMessage msg) { - MessageCounter++; + if(!bufio) MessageCounter++; bwPipe.Write((int)msg); + bwPipe.Flush(); } eMessage ReadPipeMessage() @@ -276,6 +674,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES { WritePipeMessage(eMessage.eMessage_snes_get_memory_size); bwPipe.Write((int)id); + bwPipe.Flush(); return brPipe.ReadInt32(); } @@ -283,6 +682,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES { WritePipeMessage(eMessage.eMessage_GetMemoryIdName); bwPipe.Write((uint)id); + bwPipe.Flush(); return ReadPipeString(); } @@ -298,6 +698,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES WritePipeMessage(eMessage.eMessage_peek); bwPipe.Write((uint)id); bwPipe.Write(addr); + bwPipe.Flush(); return brPipe.ReadByte(); } public void poke(SNES_MEMORY id, uint addr, byte val) @@ -306,6 +707,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES bwPipe.Write((uint)id); bwPipe.Write(addr); bwPipe.Write(val); + bwPipe.Flush(); } public int snes_serialize_size() @@ -317,13 +719,12 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count); - - public bool snes_serialize(IntPtr data, int size) { WritePipeMessage(eMessage.eMessage_snes_serialize); bwPipe.Write(size); bwPipe.Write(0); //mapped memory location to serialize to + bwPipe.Flush(); WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?) bool ret = brPipe.ReadBoolean(); if (ret) @@ -339,6 +740,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES CopyMemory(mmvaPtr, data.ToPointer(), (ulong)size); bwPipe.Write(size); bwPipe.Write(0); //mapped memory location to serialize from + bwPipe.Flush(); WaitForCompletion(); //serialize/unserialize can cause traces to get called (because serialize can cause execution?) bool ret = brPipe.ReadBoolean(); return ret; @@ -375,18 +777,21 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES bwPipe.Write(layer); bwPipe.Write(priority); bwPipe.Write(enable); + bwPipe.Flush(); } public void snes_set_backdropColor(int backdropColor) { WritePipeMessage(eMessage.eMessage_snes_set_backdropColor); bwPipe.Write(backdropColor); + bwPipe.Flush(); } public int snes_peek_logical_register(SNES_REG reg) { WritePipeMessage(eMessage.eMessage_snes_peek_logical_register); bwPipe.Write((int)reg); + bwPipe.Flush(); return brPipe.ReadInt32(); } @@ -395,7 +800,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES for (; ; ) { var msg = ReadPipeMessage(); - MessageCounter++; + if (!bufio) MessageCounter++; //Console.WriteLine(msg); switch (msg) { @@ -407,6 +812,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES int width = brPipe.ReadInt32(); int height = brPipe.ReadInt32(); bwPipe.Write(0); //offset in mapped memory buffer + bwPipe.Flush(); brPipe.ReadBoolean(); //dummy synchronization if (video_refresh != null) { @@ -426,6 +832,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES if (input_state != null) ret = input_state(port, device, index, id); bwPipe.Write(ret); + bwPipe.Flush(); break; } case eMessage.eMessage_snes_cb_input_notify: @@ -439,6 +846,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES { int nsamples = brPipe.ReadInt32(); bwPipe.Write(0); //location to store audio buffer in + bwPipe.Flush(); brPipe.ReadInt32(); //dummy synchronization if (audio_sample != null) @@ -453,6 +861,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES } bwPipe.Write(0); //dummy synchronization + bwPipe.Flush(); brPipe.ReadInt32(); //dummy synchronization break; } @@ -509,7 +918,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES } } } - } + } //WaitForCompletion() class SharedMemoryBlock : IDisposable { @@ -556,6 +965,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES this.audio_sample = audio_sample; WritePipeMessage(eMessage.eMessage_snes_enable_audio); bwPipe.Write(audio_sample != null); + bwPipe.Flush(); } public void snes_set_path_request(snes_path_request_t pathRequest) { this.pathRequest = pathRequest; } public void snes_set_scanlineStart(snes_scanlineStart_t scanlineStart) @@ -563,12 +973,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES this.scanlineStart = scanlineStart; WritePipeMessage(eMessage.eMessage_snes_enable_scanline); bwPipe.Write(scanlineStart != null); + bwPipe.Flush(); } public void snes_set_trace_callback(snes_trace_t callback) { this.traceCallback = callback; WritePipeMessage(eMessage.eMessage_snes_enable_trace); bwPipe.Write(callback != null); + bwPipe.Flush(); } public delegate void snes_video_refresh_t(int* data, int width, int height); diff --git a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs index 0436f3d470..8be51d2079 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/SNES/LibsnesCore.cs @@ -333,6 +333,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES { api.MessageCounter = 0; + api.BeginBufferIO(); + // for deterministic emulation, save the state we're going to use before frame advance // don't do this during nocallbacks though, since it's already been done if (!nocallbacks && DeterministicEmulation) @@ -394,6 +396,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES //diagnostics for IPC traffic //Console.WriteLine(api.MessageCounter); + + api.EndBufferIO(); } public DisplayType DisplayType diff --git a/BizHawk.MultiClient/output/dll/libsneshawk-32-compatibility.exe b/BizHawk.MultiClient/output/dll/libsneshawk-32-compatibility.exe index b1ee0dcd39..6982ffa7ed 100644 Binary files a/BizHawk.MultiClient/output/dll/libsneshawk-32-compatibility.exe and b/BizHawk.MultiClient/output/dll/libsneshawk-32-compatibility.exe differ diff --git a/BizHawk.MultiClient/output/dll/libsneshawk-32-performance.exe b/BizHawk.MultiClient/output/dll/libsneshawk-32-performance.exe index 890198a5c4..086038c1f5 100644 Binary files a/BizHawk.MultiClient/output/dll/libsneshawk-32-performance.exe and b/BizHawk.MultiClient/output/dll/libsneshawk-32-performance.exe differ diff --git a/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp b/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp index 199b63f8f0..b398f340d0 100644 --- a/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp +++ b/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp @@ -12,7 +12,158 @@ #include #include +typedef uint8 u8; +typedef int32 s32; +typedef uint32 u32; +class IPCRingBuffer +{ +private: + HANDLE mmf; + u8* mmvaPtr; + volatile u8* begin; + volatile s32* head, *tail; + int bufsize; + + //this code depends on conventional interpretations of volatile and sequence points which are unlikely to be violated on any system an emulator would be running on + + void Setup(int size) + { + bool init = size != -1; + Owner = init; + + mmvaPtr = (u8*)MapViewOfFile(mmf, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + + //setup management area + head = (s32*)mmvaPtr; + tail = (s32*)mmvaPtr + 1; + s32* bufsizeptr = (s32*)mmvaPtr + 2; + begin = mmvaPtr + 12; + + if (init) + *bufsizeptr = bufsize = size - 12; + else bufsize = *bufsizeptr; + } + + void WaitForWriteCapacity(int amt) + { + for (; ; ) + { + //dont return when available == amt because then we would consume the buffer and be unable to distinguish between full and empty + if (Available() > amt) + return; + //this is a greedy spinlock. + } + } + + int Size() + { + int h = *head; + int t = *tail; + int size = h - t; + if (size < 0) size += bufsize; + else if (size >= bufsize) + { + //shouldnt be possible for size to be anything but bufsize here + size = 0; + } + return size; + } + + int Available() + { + return bufsize - Size(); + } + +public: + bool Owner; + + //void Allocate(int size) //not supported + + void Open(const std::string& id) + { + HANDLE h = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, id.c_str()); + if(h == INVALID_HANDLE_VALUE) + return; + + mmf = h; + + Setup(-1); + } + + ~IPCRingBuffer() + { + if (mmf == NULL) return; + CloseHandle(mmf); + mmf = NULL; + } + + int WaitForSomethingToRead() + { + for (; ; ) + { + int available = Size(); + if (available > 0) + return available; + //this is a greedy spinlock. + } + } + + void Write(const void* ptr, int amt) + { + u8* bptr = (u8*)ptr; + int ofs = 0; + while (amt > 0) + { + int todo = amt; + + //make sure we don't write a big chunk beyond the end of the buffer + int remain = bufsize - *head; + if (todo > remain) todo = remain; + + //dont request the entire buffer. we would never get that much available, because we never completely fill up the buffer + if (todo > bufsize - 1) todo = bufsize - 1; + + //a super efficient approach would chunk this several times maybe instead of waiting for the buffer to be emptied before writing again. but who cares + WaitForWriteCapacity(todo); + + //messages are likely to be small. we should probably just loop to copy in here. but for now.. + memcpy((u8*)begin + *head, bptr + ofs, todo); + + amt -= todo; + ofs += todo; + *head += todo; + if (*head >= bufsize) *head -= bufsize; + } + } + + void Read(void* ptr, int amt) + { + u8* bptr = (u8*)ptr; + int ofs = 0; + while (amt > 0) + { + int available = WaitForSomethingToRead(); + int todo = amt; + if (todo > available) todo = available; + + //make sure we don't read a big chunk beyond the end of the buffer + int remain = bufsize - *tail; + if (todo > remain) todo = remain; + + //messages are likely to be small. we should probably just loop to copy in here. but for now.. + memcpy(bptr + ofs, (u8*)begin + *tail, todo); + + amt -= todo; + ofs += todo; + *tail += todo; + if (*tail >= bufsize) *tail -= bufsize; + } + } +}; //class IPCRingBuffer + +static bool bufio = false; +static IPCRingBuffer *rbuf = NULL, *wbuf = NULL; HANDLE hPipe, hMapFile; void* hMapFilePtr; @@ -73,11 +224,20 @@ enum eMessage : int eMessage_snes_allocSharedMemory, eMessage_snes_freeSharedMemory, - eMessage_GetMemoryIdName + eMessage_GetMemoryIdName, + + eMessage_SetBuffer, + eMessage_BeginBufferIO, + eMessage_EndBufferIO }; void ReadPipeBuffer(void* buf, int len) { + if(bufio) + { + rbuf->Read(buf,len); + return; + } DWORD bytesRead; BOOL result = ReadFile(hPipe, buf, len, &bytesRead, NULL); if(!result || bytesRead != len) @@ -106,6 +266,13 @@ void WritePipeBuffer(const void* buf, int len) { //static FILE* outf = NULL; //if(!outf) outf = fopen("c:\\trace.bin","wb"); fwrite(buf,1,len,outf); fflush(outf); + + if(bufio) + { + wbuf->Write(buf,len); + return; + } + DWORD bytesWritten; BOOL result = WriteFile(hPipe, buf, len, &bytesWritten, NULL); if(!result || bytesWritten != len) @@ -377,12 +544,12 @@ void RunMessageLoop() break; case eMessage_snes_get_memory_size: - WritePipe(snes_get_memory_size(ReadPipe())); + WritePipe(snes_get_memory_size(ReadPipe())); break; case eMessage_snes_get_memory_data: { - unsigned int id = ReadPipe(); + unsigned int id = ReadPipe(); char* dstbuf = ReadPipeSharedPtr(); uint8_t* srcbuf = snes_get_memory_data(id); memcpy(dstbuf,srcbuf,snes_get_memory_size(id)); @@ -392,8 +559,8 @@ void RunMessageLoop() case eMessage_peek: { - int id = ReadPipe(); - unsigned int addr = ReadPipe(); + int id = ReadPipe(); + unsigned int addr = ReadPipe(); uint8_t ret; if(id == SNES_MEMORY_SYSBUS) ret = bus_read(addr); @@ -404,8 +571,8 @@ void RunMessageLoop() case eMessage_poke: { - int id = ReadPipe(); - unsigned int addr = ReadPipe(); + int id = ReadPipe(); + unsigned int addr = ReadPipe(); uint8_t val = ReadPipe(); if(id == SNES_MEMORY_SYSBUS) bus_write(addr,val); @@ -420,8 +587,8 @@ void RunMessageLoop() case eMessage_snes_serialize: { - int size = ReadPipe(); - int destOfs = ReadPipe(); + int size = ReadPipe(); + int destOfs = ReadPipe(); char* buf = (char*)hMapFilePtr + destOfs; bool ret = snes_serialize((uint8_t*)buf,size); WritePipe(eMessage_Complete); @@ -431,8 +598,8 @@ void RunMessageLoop() case eMessage_snes_unserialize: { //auto blob = ReadPipeBlob(); - int size = ReadPipe(); - int destOfs = ReadPipe(); + int size = ReadPipe(); + int destOfs = ReadPipe(); char* buf = (char*)hMapFilePtr + destOfs; bool ret = snes_unserialize((uint8_t*)buf ,size); WritePipe(eMessage_Complete); @@ -474,19 +641,19 @@ void RunMessageLoop() case eMessage_snes_set_layer_enable: { - int layer = ReadPipe(); - int priority = ReadPipe(); + int layer = ReadPipe(); + int priority = ReadPipe(); bool enable = ReadPipe(); snes_set_layer_enable(layer,priority,enable); break; } case eMessage_snes_set_backdropColor: - snes_set_backdropColor(ReadPipe()); + snes_set_backdropColor(ReadPipe()); break; case eMessage_snes_peek_logical_register: - WritePipe(snes_peek_logical_register(ReadPipe())); + WritePipe(snes_peek_logical_register(ReadPipe())); break; case eMessage_GetMemoryIdName: @@ -498,6 +665,25 @@ void RunMessageLoop() break; } + case eMessage_SetBuffer: + { + int which = ReadPipe(); + std::string name = ReadPipeString(); + IPCRingBuffer* ipcrb = new IPCRingBuffer(); + ipcrb->Open(name); + if(which==0) rbuf = ipcrb; + else wbuf = ipcrb; + break; + } + + case eMessage_BeginBufferIO: + bufio = true; + break; + + case eMessage_EndBufferIO: + bufio = false; + break; + } //switch(msg) } }