RetroPark

Dolphin and a Game Boy core, in the same runtime

RetroPark loads emulators as cores and drives them through a stable flat-C contract. It is to EverythingBox what libretro is to RetroArch: the layer that turns "an emulator" into "a core the host can load, composite, record, rewind and net-sync without knowing what is inside it."

Where this actually stands

RetroPark is now wired into EverythingBox — a permanent dependency, a backend you can select per game or per system, and its own play surface inside the app. What it is not yet is the thing running your games: no ROM goes through it today. The surface currently drives the reference core, and libretro stays the default for everything until you opt a game in.

So the seam is real and the plumbing is live; the payload comes next. Today's emulation still runs on libretro cores and standalone emulators, which is what the emulation page describes.

The problem with one model

libretro has exactly one shape: the host calls retro_run(), the core produces one frame and returns. For a 6502 that is a good fit. For Dolphin or RPCS3 it is hopeless — those emulators own their own threads, their own GPU device and their own frame clock, and they cannot be pumped one frame at a time by somebody else's loop.

The usual workaround is to stop treating heavy emulators as cores at all and shell out to them as separate applications — which is exactly what EverythingBox does today for sixteen modern systems. It works, but the host loses everything: no shared overlay, no unified input routing, no host-owned savestates, a second window and a second set of settings.

RetroPark's contract has two core models instead, so a Game Boy core and a full PS3 emulator can both be a core without either one pretending to be the other.

Driven

The host owns the frame clock

The core runs exactly one frame per call and hands back a framebuffer and audio. This is the libretro shape, and it is the right one for retro systems.

  • Host-owned rewind
  • Savestate scrubbing
  • Deterministic lockstep netplay
  • Rollback netplay

Presenting

The core keeps its own clock

The core keeps its own loop, threads and GPU device, and renders into a shared surface the host provides. The host still owns the final composite and input routing — but not the clock, so a heavy emulator runs at its own full speed with nothing forcing it into lockstep.

  • Host-owned overlays and filters
  • Host-owned input routing
  • No forced lockstep
  • Emulators that own their threads

A core declares which it is in its get_info, and the runtime treats it accordingly. Because the driven model keeps the clock in the host, rewind, scrubbing and deterministic netplay come for free for every core that uses it — the core does not implement any of them.

Frames cross without a copy

A presenting core renders on its own GPU device, and the host composites on its own. Pixels cross between them through a ring of shared textures — a D3D11 shared handle guarded by an IDXGIKeyedMutex, or a Vulkan image with a shared timeline semaphore — carrying a generation counter so a window resize can never hand the host a stale surface. Two devices, and nothing forced to copy on the hot path.

That handoff is the whole reason a full emulator can render inside someone else's window and still have that window draw its own overlay on top.

What already runs

Windows, on both backends. The core ABI is stable at v5, and a core declaring the wrong version is refused at load. macOS and iOS — Metal, and the static-core path that makes a platform without dlopen possible — are designed but not yet built.

Driven · 3

Reference driven corerefcore_driven

The reference driven core. Doubles as the static-core example — compiled in, no dlopen, for locked-down platforms.

Reference rollback corerefcore_rollback

Exercises rollback netplay and resimulation.

libretro shimlibretro_shim

Wraps a real libretro core behind the RetroPark ABI. Runs real NES ROMs with audio, savestates and rewind — so the existing core ecosystem keeps working.

Presenting · 4

Reference presenting corerefcore_presentD3D11

The reference presenting core on Direct3D 11.

Reference presenting corerefcore_present_vkVulkan

The same, on Vulkan.

Dolphindolphin_presentVulkan

Dolphin built from source, rendering real GameCube and Wii titles into RetroPark’s shared Vulkan image — with audio, input, savestates, and Dolphin’s own netplay driven headless.

Built from a working checkout, not vendored — its own licence applies to a combined binary.

RPCS3rpcs3_presentVulkan

RPCS3 as a cross-process frame producer, handing PS3 frames across a process boundary into the same shared image.

Built from a working checkout, not vendored — its own licence applies to a combined binary.

Inside EverythingBox

The integration is deliberately a seam rather than a switch. Every game launch now resolves to a backend — libretro or RetroPark — and you can set that per game, or as a default for a whole system. Un-opted games take the libretro path exactly as before, byte for byte; nothing about today's behaviour changed underneath anyone.

Opt a game in and it launches onto RetroPark's own play surface, a sibling of the libretro one rather than a replacement for it. That surface loads its core statically — compiled in, no dlopen — which is the same path that makes a locked-down platform possible later.

How far along it is

  • A permanent dependency — RetroPark is a submodule, built and linked into the app
  • A backend seam: every launch resolves to libretro or RetroPark
  • Pick the backend per game, or set a default per system
  • A RetroPark play surface in the app, beside the libretro one
  • Continuous integration builds the runtime and runs its probes
  • Real ROMs through RetroPark, via the libretro shim
  • Presenting cores in-window — Dolphin and RPCS3 under the app’s own overlay
  • macOS and iOS, via Metal and the static-core path

The prize at the end is the sixteen systems that currently launch a separate application. Under a presenting core those become cores: rendering inside the app's own window, under its overlay, with its input routing and its savestates — the same treatment the built-in cores get today. One window, one set of controls, one place your saves live.

And the libretro_shim core is why getting there is not a rewrite of the world: existing libretro cores keep working through it, so the retro half of the library does not need porting to gain the rest.

And in your frontend, not just ours

RetroPark is a runtime with a flat-C API, not an application with a runtime bolted inside it. Two headers are the entire surface: one the host calls, one a core implements. A host creates a runtime for a graphics API, loads a core, loads content, then drives it — advance, present, render, set_input, save_state, rewind.

It owns no UI

pause, resume, reset and get_status exist so a frontend can build its own hotkeys and in-game menu on top. The runtime never draws a menu of its own.

Flat C, so any language

No C++ ABI to match, no framework to adopt. The ABI is versioned, and a mismatched core is refused at load rather than crashing you later.

Static cores too

A core can be compiled in and loaded by id instead of by dlopen, which is what makes locked-down platforms possible at all. Designed and exercised by the reference core; the mobile backends are still to come.

It is GPLv3, so a frontend that links it inherits that — worth knowing before you adopt it. The heavy cores pull in their own terms as well: Dolphin is GPLv2-or-later and RPCS3 is GPLv2, and neither is distributed as part of RetroPark.

Read the code

RetroPark is public and GPLv3. The two headers are the best place to start — they are the whole contract.