Hi!
First, to give you a bit of context on my situation so you can better understand my level of expertise and what I'm trying to do: I've been tinkering for a while on a fork of dolphin that adds Python scripting support to Dolphin. Doing that I have acquired "I get by"-level knowledge of C++, but pretty much still consider myself to have "amateur"-level knowledge on both the dolphin codebase and emulators/hardware in general.
My scripting fork has a notion of "events". That basically just means once a python script is running it can register a callback to a predefined list of things that happen during emulation. One prime example is the "frameadvance" event, which fires once per frame. For this event I basically hooked into the VI event, calling into any registered Python function every time a VI event on the CPU thread hits an even or odd field boundary. One important detail here is that this runs synchronously, meaning emulation is effectively paused while the Python code runs.
Now, some events don't originate from the CPU thread. For example, I tried adding an event for "framedrawn", which should give the Python code access to the currently drawn frame (width, height and pixel data). The easiest way to prototype this was to just call into python right in the FrameDumper thread once dumping is done. Scripts that listen to this event can reach back into the emulator through some other offered functionality, e.g. writing directly to memory. As some might have guessed, this led to some concurrency problems, namely occasional crashes (probably) and also deadlocks between the CPU thread guard and the Python Global Interpreter Lock (GIL).
Being a bit wiser now that I should not mix threads wildly, I've come to the conclusion (and you may challenge me here) that any scripting events should be emitted from the CPU thread, running synchronously to emulation just like described for the "frameadvance" event above. And this is where I no longer know how I could achieve that cleanly and ask you for your opinions and help.
To summarize, I currently have 2 usecases I would like to implement:
One solution that came to my mind was scheduling everything onto the CPU thread using CoreTiming's events. But those callbacks can only hold a u64 as data (no pointers allowed), which limits their usefulness as a general-purpose event mechanism. For my frameadvance problem it would work fine, but for my framedrawn event I wouldn't know where to put the actual frame data.
So I'm kindly asking some devs with a more in-depth knowledge of the codebase and emulation in general to share their opinion on how to handle these kind of events.
First, to give you a bit of context on my situation so you can better understand my level of expertise and what I'm trying to do: I've been tinkering for a while on a fork of dolphin that adds Python scripting support to Dolphin. Doing that I have acquired "I get by"-level knowledge of C++, but pretty much still consider myself to have "amateur"-level knowledge on both the dolphin codebase and emulators/hardware in general.
My scripting fork has a notion of "events". That basically just means once a python script is running it can register a callback to a predefined list of things that happen during emulation. One prime example is the "frameadvance" event, which fires once per frame. For this event I basically hooked into the VI event, calling into any registered Python function every time a VI event on the CPU thread hits an even or odd field boundary. One important detail here is that this runs synchronously, meaning emulation is effectively paused while the Python code runs.
Now, some events don't originate from the CPU thread. For example, I tried adding an event for "framedrawn", which should give the Python code access to the currently drawn frame (width, height and pixel data). The easiest way to prototype this was to just call into python right in the FrameDumper thread once dumping is done. Scripts that listen to this event can reach back into the emulator through some other offered functionality, e.g. writing directly to memory. As some might have guessed, this led to some concurrency problems, namely occasional crashes (probably) and also deadlocks between the CPU thread guard and the Python Global Interpreter Lock (GIL).
Being a bit wiser now that I should not mix threads wildly, I've come to the conclusion (and you may challenge me here) that any scripting events should be emitted from the CPU thread, running synchronously to emulation just like described for the "frameadvance" event above. And this is where I no longer know how I could achieve that cleanly and ask you for your opinions and help.
To summarize, I currently have 2 usecases I would like to implement:
- Some way to access the frame data. Right now the frame data is readily accessible from the FrameDumper thread (m_frame_dump_data).
This does not necessarily have to be an event: I can also imagine that scripts simply use the "frameadvance" event and then practially "do a screenshot", which might be easier to implement. Then again, even the built-in screenshot functionality just instructs the framedumper thread, which then does the work asynchronously. So we're back to events?
- Right now my "frameadvance" event fires twice per frame for 30fps games. The information whether a frame is a duplicate frame is currently only readily available inside the Video Thread. I could e.g. add myself to the AfterPresentEvent hookable event, but I am not sure how I would schedule that event back onto the CPU thread to not invoke the python script concurrently, enabling potential race conditions again.
One solution that came to my mind was scheduling everything onto the CPU thread using CoreTiming's events. But those callbacks can only hold a u64 as data (no pointers allowed), which limits their usefulness as a general-purpose event mechanism. For my frameadvance problem it would work fine, but for my framedrawn event I wouldn't know where to put the actual frame data.
So I'm kindly asking some devs with a more in-depth knowledge of the codebase and emulation in general to share their opinion on how to handle these kind of events.