The binding coroutines in libretro builds are constructed on the VM
stack, so reallocating the VM memory would corrupt the memory of any
currently existing coroutines.
I've changed it so that the coroutines are no longer constructed on the
VM stack so that they're unaffected by VM memory reallocations, and
added a "slot" mechanism for storing variables on the VM stack. (Any
Ruby `VALUE`s used by a coroutine have to be stored on the VM stack so
that the Ruby garbage collector doesn't free them while they're being
used, which is why the slot mechanism is necessary.)
Since the libretro save directory isn't guaranteed to be private for
each core, or even private for just saves, we'd better create a
directory structure in the libretro save directory for our saves.
Files are written to the libretro save directory, which is mounted at
/save in PhysFS. All filesystem calls made from Ruby in libretro builds
are routed through PhysFS, so the game can just use any ordinary
filesystem function provided by Ruby to interact with /save.
It's also union mounted on top of the game directory (located at /game
in PhysFS) so that games that write their save files to the current
working directory will have their save files saved to the libretro save
directory instead of the game directory.
For security and portability reasons, nothing outside of the libretro
save directory can be written to, and nothing outside of the libretro
save directory, the libretro game directory and the various embedded
files used by the runtime can be read from.
Any relative paths that the game tries to access in libretro builds will
now be relative to whatever is the current working directory in the Ruby
sandbox, which will also now be initialized to the game directory during
initialization. Before, all of the bindings that took paths were
hardcoded to prepend the path with the game directory.
* Fixed a bug where frames are still duped when the frontend is
fast-forwarding
* Fixed a bug where manual frame duping (without
`RETRO_ENVIRONMENT_GET_CAN_DUPE`) causes screen flickering during a
`Graphics.transition` call
Apparently we're not supposed to use
`RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO` to change the FPS. The core
should be running at the display refresh rate and resampling the game's
video output.
When not using the threaded audio driver, the frontend is supposed to
block until enough audio samples have been rendered for one frame, so
the frontend throttles the frame rate automatically if the core's frame
rate is slower than the display frame rate.
However, when using the threaded audio driver, there's no more
throttling, so we have to do it ourselves.
This adds a new driver for audio in libretro builds for devices with
multithreading support that defers audio rendering to a worker thread
provided by the libretro frontend.
The threaded driver has the advantage that video lag will not also cause
the audio to lag, which is very noticeable since it manifests in the
form of audio crackling when it happens.
Not sure why, but this fixes crashes when calling variadic functions in
the Ruby API in libretro builds when Ruby is built without `-DNDEBUG`.
Maybe the previous way of calling varargs functions was undefined
behaviour somehow.
`rb_rescue` only catches `StandardError`s, which doesn't include things
like Ruby syntax errors that we'd like to catch. We need to explicitly
use `rb_rescue2` to catch `Exception` in order to catch everything.