Engine / QUBICENGINE HANDBOOK

Architecture & ownership

Understand modules, stable handles, immutable render snapshots, and the boundary between an engine and its graphics backends.

Ownership makes complexity manageable#

Imagine two people editing the same drawing at once. One erases a shape while the other tries to color it. Engine threads face the same problem when they share mutable data without a clear owner. QubicEngine gives each kind of data one authoritative home and passes deliberate snapshots across thread boundaries. Application owns PlatformWindow, InputRouter, JobSystem, AssetStore, Scene, PhysicsWorld, AnimationSystem, AudioWorld, and Renderer. Renderer owns GraphicsDevice and RenderGraph. The editor uses Application through a document and command layer. The engine’s public scene API contains no ID3D12Resource or VkImage objects.

01SceneSimulation data02RenderWorldImmutable snapshot03RenderGraphPass dependencies04GraphicsDeviceDX12 / Vulkan
Ownership and dependencies in QubicEngine’s documented reference architecture.

Stable identity, changing storage#

An entity handle contains an index and generation. When an entity is deleted, its generation changes. An old handle therefore fails validation instead of silently referring to the next entity that occupies that index. Asset handles use the same principle, with a version to distinguish a newly imported model from its predecessor. Components are stored in dense arrays. A Transform component stores local translation, rotation, scale, a parent entity, and cached world data. MeshRenderer stores handles, not ownership of the mesh’s allocation. AssetStore keeps an asset alive while clients or pending GPU work still reference its version.

Implementation: the boundary types
cpp · REFERENCE EXCERPT
struct Entity { uint32_t index; uint32_t generation; };
struct AssetHandle { uint32_t index; uint32_t generation; };
struct MeshInstance {
    AssetHandle mesh;
    AssetHandle material;
    Matrix4 world; // Row-major, row-vector convention.
    Bounds worldBounds;
    uint32_t skinPaletteOffset;
};
struct RenderWorld {
    std::vector<MeshInstance> instances;
    CameraData camera;
    uint64_t simulationVersion;
};

These are reference declarations. Matrix4 is a sixteen-float row-major matrix; Bounds contains a center and half extents; CameraData contains view/projection matrices and clip distances. The engine math module owns these types. In the complete native samples, DirectXMath supplies the actual matrix implementation. Extraction creates a new RenderWorld and publishes it after all contributing jobs finish. The render thread keeps a shared immutable snapshot until its command-recording work completes. GPU buffers created from that snapshot have a separate retirement fence; finishing CPU recording is not GPU completion.

Backend contracts#

GraphicsDevice exposes resource descriptions, opaque handles, descriptors, command contexts, capability queries, queue submission, and fence points. A resource description includes size, format, allowed usages, and memory intent. The backend chooses DX12 heaps or Vulkan memory types and keeps native objects private. RenderGraph describes reads and writes rather than passing API-specific barriers through every engine module. Its compiler generates a backend-neutral dependency plan; the selected backend translates that plan into states and barriers with the required access semantics. The Vulkan implementation also tracks image layouts and queue-family ownership when needed.

Costs and failure modes#

An immutable snapshot costs copying or retaining render-visible data, but eliminates many unpredictable locking points. Copy compact render records rather than a whole Scene. Reuse snapshot storage only when every CPU consumer releases it. Profile extraction independently from command recording. If objects randomly disappear after deletion, inspect generations and reference retention. If edits appear one frame later, compare simulationVersion and extraction timing. If a backend abstraction needs hundreds of unrelated flags, separate resource creation from command execution and expose the engine’s actual requirements instead of flattening both APIs into their smallest overlap.

Continue the thread#

Trace a frame, inspect scene transforms, or compare DX12 and Vulkan. The glossary explains handles and ownership.

Search titles and full article text.