Skip to content
CppCooperProgramming Portfolio

Article

Runtime Ownership in Cheryl Engine 4

ArchitectureParallel Computing

A game runtime needs clear owners for the window, input, simulation and graphics work. In Cheryl Engine 4, the platform caller owns native polling and rendering. Simulation has its own owner. Sequential execution puts both roles on the caller; concurrent execution gives simulation a dedicated worker.

That changes where the game runs while preserving the boundaries between its work. The same game hooks advance authoritative state and prepare render data in both modes.

The context owns adapters; the runtime coordinates a session

CE::Runtime::Context owns the compatible display, presentation surface, renderer and resource-provider adapters. Input can be owned or explicitly borrowed. Obtaining an adapter reference from the context does not move its operations onto the correct thread: an upload still has the backend’s graphics-owner requirements.

CE::Runtime::GameRuntime borrows the context and the CE::Game::AbstractGame object. They must remain alive until run() returns or throws. Construction validates options; run() reserves and executes the session. Both the runtime and its context permit one session.

With a compatible context and game already assembled, this startup fragment chooses concurrent execution and fixed simulation steps. These are independent selections:

CE::Runtime::Simulation::TimingOptions timing;
timing.mode = CE::Runtime::Simulation::Mode::Fixed;

CE::Runtime::GameRuntime runtime(
    context, game, CE::Runtime::RunMode::Concurrent, {}, timing);
runtime.run();

The dedicated simulation thread is separate from context-created CPU worker groups. Changing general worker capacity does not select the runtime mode.

Put initialization beside its owner

The runtime initializes the renderer and input, then calls init_platform() on the platform owner. Registering bindings and uploading initial graphics resources belong here. The constructor is available for setup that needs neither initialized owner.

Simulation services and the initial copied view are established before init_simulation(). In concurrent mode that hook runs on the worker, along with update(), prepare_render_frame() and deinit_simulation(). Sequential mode calls them on the platform caller.

This gives simulation-owned view watchers and step services an initialization point before the first update. The Ready milestone follows successful initialization and precedes updates. In concurrent startup the worker waits behind a gate while platform publishes Ready, then platform releases it. An immediate Ready callback that waits for the first update would prevent the update it is waiting for.

Follow one moving actor through the boundary

The maintained First Game application provides a concrete flow. Its platform hook binds controls and loads sprite, material and text resources. Its update reads the tick’s actions, advances the actor’s position using delta_seconds and adjusts the camera using copied framebuffer dimensions.

Each update first receives simulation-dispatch work, accumulated input and the selected view; required step services run before the game hook. TickContext::input is borrowed for that call. The tick’s simulation delta and observed_seconds() describe different intervals, so they should not be substituted casually. The input-routing article follows that transfer in more detail.

When a slot is available after a useful update batch, frame preparation turns the actor and camera values into draw data. Rendering consumes that prepared state. Even sequential execution uses this boundary; it does not give the renderer permission to inspect mutable game objects. The prepared-frame article explains the handoff.

What the execution mode changes

Sequential execution makes the order direct: platform work, updates, preparation, rendering and presentation use one caller. A long update also occupies the thread needed for polling and rendering.

Concurrent execution lets platform work proceed while the simulation worker advances the game. It also introduces explicit transfers and bounded storage. Input backlog capacity can pause further polling; frame-slot pressure can skip a visual publication while simulation continues.

Both modes use the variable/fixed scheduler and bounded recovery policy. A render cycle need not contain an update, and a batch of updates need not produce a frame for every intermediate state.

Shutdown follows the remaining users

Calling stop(), or tick.request_stop() from an update, requests normal teardown. It does not interrupt the current hook. First Game returns immediately after its stop request because it has no further work to do in that tick. Destroying the runtime is not a substitute for letting its active run() finish.

Shutdown closes context CPU submissions and simulation dispatch. Simulation cleanup runs on its owner. In concurrent mode platform continues servicing dependencies while the worker finishes, then joins it. quiesce() runs on platform to stop external producers while their targets and resources still exist.

Accepted CPU work then settles with platform dependencies still serviced. Retained frames recycle before deinit_platform() releases the game’s bindings and resources; adapter teardown follows. Simulation cleanup must therefore leave dependencies alive if accepted jobs still use them. A platform quiesce hook must not block on work requiring the platform dispatcher it is currently occupying.

Initialization can fail partway through. Each cleanup hook pairs with attempted initialization, so cleanup needs to tolerate partial state. The runtime preserves the first failure while attempting later cleanup and reporting secondary failures. Ownership is consequently a rule for failure paths as well as the ordinary loop.

Cheryl Engine 4 reading path

  1. Runtime ownership and lifecycle (this article)
  2. Prepared frames and rendering handoff
  3. Retained resources and cleanup
  4. Input routing and editable menus
  5. Unicode layout and retained text
  6. Optional modules and the installed SDK