Dispatch

Purpose

Dispatch transfers owned work to the platform/graphics or simulation owner. Use it when an operation requires that owner, rather than running arbitrary CPU work through an owner queue or calling a thread-affine adapter directly.

Interface

Types below are in CE::Runtime unless another namespace is shown.

Types and operations

API and purpose

PlatformDispatcher
Submit work taking Context& to platform/graphics through the context. Platform dispatch.

SimulationDispatcher
Submit work without a context argument to simulation through the runtime. Simulation boundary.

PlatformDispatcher::Submission, SimulationDispatcher::Submission
Save copyable submission endpoints that reject safely after closure/destruction. Saved endpoints and cancellation.

CE::Diagnostics::DispatchStats
Inspect accepted/completed/pending observations without treating queue emptiness as completion. Dispatcher statistics.

Submission and completion

Operation

Purpose and details

submit()

Decay-own work/captures and retain its future; borrowed pointees still need a lifetime. Ownership and future results.

submission()

Retain queue state independently of the borrowed service facade. Endpoint lifetime.

Future readiness / result

Consume accepted results/errors without waiting on the owner needed to produce them. Dependency progress.

Runtime closure

Settle required accepted dependencies, then cancel unexecuted work before game cleanup. Shutdown order.

Usage

Save a copyable submission endpoint during initialization. Producers submit owned inputs through that endpoint and retain the returned future; a later update adopts the result after readiness is observed.

        ---
config:
  theme: base
  themeVariables:
    fontFamily: system-ui, sans-serif
    fontSize: 14px
  flowchart:
    htmlLabels: true
    curve: linear
    nodeSpacing: 22
    rankSpacing: 38
    padding: 12
---
flowchart LR
    accTitle: Dispatch · application owner requests
    accDescr: Local names omit Runtime::; ambiguous nested namespaces remain qualified. Arrows point toward the API or type used, not call order or data flow. Methods beneath type names are selected application entry points; Implement hooks marks engine-invoked overrides. Dashed arrows mark optional choices.
    k0(["Application<br/>init_platform()"]):::app
    k1["<b>Context</b><br/><br/>platform_dispatcher(): PlatformDispatcher<br/><br/>resources(): Rendering::Resources::Provider"]:::neighbor
    k2["<b>PlatformDispatcher</b><br/><br/>submission(): PlatformDispatcher::Submission"]:::focal
    k3["<b>PlatformDispatcher::<br/>Submission</b><br/><br/>submit(Work): Result"]:::focal
    k4(["Application<br/>producer"]):::app
    k5(["Application<br/>platform-owner work"]):::app
    k6["<b>Rendering::Resources::<br/>Provider</b><br/><br/>create_image(Assets::Images::Decoded):<br/>Rendering::Resources::Image<br/><br/>upload_geometry(<br/>Rendering::Geometry::Vertex2D[],<br/>Rendering::Geometry::Topology):<br/>Rendering::Geometry::Buffer2D"]:::neighbor
    k7(["Application<br/>composition"]):::app
    k8["<b>GameRuntime</b><br/><br/>simulation_dispatcher(): SimulationDispatcher"]:::neighbor
    k9["<b>SimulationDispatcher</b><br/><br/>submission(): SimulationDispatcher::Submission"]:::focal
    k10["<b>SimulationDispatcher::<br/>Submission</b><br/><br/>submit(Work): Result"]:::focal
    k11(["Application<br/>simulation-owner work"]):::app
    k12["Game::<br/>TickContext"]:::neighbor
    k0 --> k1
    k1 --> k2
    k2 --> k3
    k4 --> k3
    k5 --> k1
    k1 --> k6
    k7 --> k8
    k8 --> k9
    k9 --> k10
    k4 --> k10
    k11 --> k12
    classDef focal fill:#e7f3ef,stroke:#387d6b,color:#153d33,stroke-width:1.2px
    classDef neighbor fill:#f0f3f8,stroke:#8c9bb1,color:#35445b,stroke-width:1px
    classDef app fill:#fff2df,stroke:#c09452,color:#674515,stroke-dasharray:5 3
    classDef system fill:#eaf0fc,stroke:#617fab,color:#263f69,stroke-width:1.2px
    linkStyle default stroke:#7e8fa7,stroke-width:1.2px

    

Local names omit Runtime:: where unambiguous. Explore this diagram and its other views →

Explore dispatcher ownership and engine consumers.

Choose the destination

Work

Queue or service

Execution boundary

Upload resources, mutate/query a native window, perform graphics operations

engine.platform_dispatcher().submission()

Runtime::PlatformDispatcher invokes work(Runtime::Context&) on platform with the selected graphics context.

Apply an owned result to simulation state

runtime.simulation_dispatcher().submission()

Runtime::SimulationDispatcher invokes work() at the scheduled simulation boundary before update. Posting does not create an early tick.

Decode, shape or otherwise prepare CPU data

Workers::Group

Independent CPU capacity; the application arranges later owner upload/adoption. Workers.

Observe an event on a selected owner

Events::platform_delivery() / simulation_delivery()

Adapt a saved endpoint into event delivery; retain a registration and independent error sink. Events.

Submission always queues, even when the producer is already on the destination owner. Capture owned values, keep borrowed targets alive through execution or cancellation, and avoid waiting on work that needs the current owner. The complete result-adoption example shows endpoint storage, readiness checking and error handling.

Uses

Dependency

Dispatch’s use

Runtime::Context

Supply the platform callback’s live adapter/resource access. Simulation callbacks receive no Context argument. Runtime.

Diagnostics::DispatchStats

Observe accepted, queued, running, completed and cancelled work. These counters do not replace the request’s future. Diagnostics.

Logging

Report queue lifecycle and high-water observations; callback failures remain with their futures.

GameRuntime controls opening, owner binding, draining and closure. A saved endpoint retains queue state, without retaining Context or native adapters.

Used by

Consumer

What it submits

Runtime::GameRuntime

Service owner batches and settle/cancel outstanding work in the session shutdown order.

UI::TGUI::SceneUploader, UI::RmlUi::SceneUploader

Upload owned prepared scene data to the graphics owner. GUI.

Text updates

Prepare away from graphics, submit upload and adopt a ready generation on simulation.

Event delivery adapters

Transfer optional callbacks through a saved platform/simulation endpoint; the bus itself does not own a dispatcher.

Application asset preparation or jobs

Submit owner-affine follow-up using saved endpoints. A Loader or worker pool does not automatically schedule that orchestration. Assets.

Contracts

Each drain detaches one FIFO batch. New/nested posts wait for another drain; submission never executes inline. Captures are decay-owned, but references and reference-valued results still borrow targets. Accepted exceptions reach the future; unexecuted cancellation breaks its promise. Never wait on a future from the owner needed to complete it.

Endpoints retain queue state rather than borrowed service pointers. They reject before opening, after closing and after destruction. has_pending() only sees queued work; an empty queue does not establish completion of a detached batch.

Lifecycle

Runtime opens queues during initialization and binds simulation to its selected owner. Save endpoints for longer-lived adapters without extending backend lifetime. During shutdown, runtime services accepted dependencies while joining simulation and settling tracked CPU work, then cancels remaining requests before game cleanup. Borrowed captures must survive either execution or cancellation.

Execution

Platform requests run with the runtime’s selected graphics context on platform. Simulation batches drain before scheduled updates without consuming input merely because mailbox work ran. Cross-domain ordering follows explicit dependencies; there is no total order between independent platform, simulation and worker work.

Integration

During init_platform(), obtain a copyable endpoint and retain it in the producer that will submit requests. During the live session that producer can request a window observation on its owner:

auto platform_requests = engine_.platform_dispatcher().submission(); // Save during init_platform().
// Later, the producer uses its saved endpoint:
auto pending = platform_requests.submit([](CE::Runtime::Context& owner) {
    return owner.window().framebuffer_size();
});

The endpoint and pending are retained values, not temporary service pointers. The complete readiness example shows their member storage and adoption during update. Inspect future readiness from later updates; do not block simulation while it depends on platform progress. Ordinary game layout should use tick dimensions directly; the example shows where an explicitly requested live observation runs.

Limits and further reading

Dispatch is not a general executor, a completion barrier based on queue emptiness or automatic lifetime extension for captured raw pointers. Read thread dispatch, runtime shutdown and the platform interface.