Skip to content
hikari
RenderingEditorAIToolchainDocumentation
GitHub
hikari

© 2026 Flying Rat Studio.
All rights reserved.

Explore the engineDocumentationContributorsLicenseBack to top
Documentation / Systems
Browse docs
Overview
Tutorials16
OverviewFirst game projectFirst entityAuthored user_data and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesPlay, Edit, and scenesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides16
OverviewDevelopment guideGameplay APIChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / UnrealActor and component lifecycle
Systems28
OverviewArchitectureApplication lifecycleFrontends and driversPlatforms and supportSession services and cross-scene stateScenes and gameplayActor communication (hi.actors)Game-facing refsSave / replication wire versionCoordinate space and camera conventionsRenderingRenderer architecture mapFrame governorGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)UI and editorEditor asset hot reloadEditor Project Selector
Language reference2
OverviewAkari language referenceKawa language reference
Engine overview
Start exploring
  • No matching sections. Try fewer words or another topic.
NavigateEnter Openesc Close
Overview
Tutorials16
OverviewFirst game projectFirst entityAuthored user_data and the inspectorFirst mesh and materialFirst physics body and triggerFirst character controllerFirst session servicesFirst UIFirst input actionFirst messagesPlay, Edit, and scenesFirst runtime spawnFirst motionFirst skeletal animationAssets in Play (soft refs and hot reload)Dynamic editor recompile
Guides16
OverviewDevelopment guideGameplay APIChoosing component storageBuild and packagingProject filePluginsHikari Plugin APIData-driven content, JSON, and pathsScripting with KawaShader authoringTime of dayUser interfaceUI layoutUI widgetsMigration from Unity / UnrealActor and component lifecycle
Systems28
OverviewArchitectureApplication lifecycleFrontends and driversPlatforms and supportSession services and cross-scene stateScenes and gameplayActor communication (hi.actors)Game-facing refsSave / replication wire versionCoordinate space and camera conventionsRenderingRenderer architecture mapFrame governorGPU particlesVisual ZonesVolumetric mediaInputAudioPhysicsMotion KitTemporal KitAssets and ShinraPrefabsAsset residencyAsset formats (Shinra pipeline)UI and editorEditor asset hot reloadEditor Project Selector
Language reference2
OverviewAkari language referenceKawa language reference
Engine overview
Systems14 min read

Renderer architecture map

On this page
On this pageLayer stackShared code never asks what a backend hasOwnership and threadingModule mapRHI vocabularyRender-graph lifecycle (one frame)Assemble vs executePass inventory and data flowFeature layers (orthogonal)Sync points1. CPU frames-in-flight (shared contract)GPU pass timestamps2. Intra-queue barriers (same QueueClass)3. Cross-queue sync (QueueSync + QueueSyncEdge)4. Texture upload (residency)5. Ray-tracing structures6. Present / outputLite vs full schedule (mental model)Where to change whatCode mapRelated docs Back to top

Map of how Hikari owns rendering: layers, RHI vocabulary, render graph, passes, and sync points. Behavior details and pass gates live in Rendering; this page is the structural overview.

Active backends: Metal (macOS) and D3D12 (Windows) share one graph path. Linux Vulkan is scaffolding only.


Layer stack

Diagram
Diagram source
flowchart TB
  subgraph game_thread [Game thread]
    World[World / entities]
    RF[RenderFrame publish]
    RCQ[RenderCommandQueue]
    World --> RF
    World --> RCQ
  end

  subgraph render_thread [Render thread]
    RT[renderer/render_thread.zig]
    Res[renderer_residency / consume]
    Core[renderer/renderer_core.executeFrame]
    Prep[prepareFrame — clusters, shadows, RT, cull]
    Assemble[pipeline.assemble]
    Compile[GraphBuilder.compile]
    Exec[pipeline.execute]
    Present[Adapter.finishOutput]
    RT --> Res --> Core
    Core --> Prep --> Assemble --> Compile --> Exec --> Present
  end

  subgraph shared_policy [Shared graphics — policy]
    Graph[rendergraph/*]
    RHI[rhi/types.zig]
    Drivers[rendergraph/drivers/*]
    Pipelines[renderer_pipelines / gbuffer / shadows]
  end

  subgraph platform [Platform — native only]
    Adapter[renderer_frame Adapter]
    Enc[pass_encoder]
    Native[Metal / D3D12 native]
    Adapter --> Enc --> Native
  end

  RF --> RT
  RCQ --> Res
  Assemble --> Graph
  Compile --> Graph
  Exec --> Drivers
  Drivers --> Enc
  Graph --> RHI
  Compile --> RHI
  Core --> Adapter
LayerOwnsMust not own
Game / WorldVisibility, materials, lights, publish snapshotNative GPU objects
renderer_coreFrame algorithm: prepare → assemble → compile → execute → presentAPI-specific barriers
rendergraphPass order, resource decls, liveness, barriers, queue acquiresNative command lists
rhi/States, queues, formats, barrier POD, capabilitiesAllocation / encoding
Platform AdapterResource tables, encode, GPU signal/wait, presentPass ordering / feature gates
Native Metal/D3D12Device, heaps, PSO bytes, swapchainGraph policy

Invariant: policy above RHI; platforms only lower. Do not fork frame orchestration per API. Shared-type renderer policy state is renderer.common (CommonState).

Host capability is declared, never probed: every renderer states pub const surface_support: renderer_contract.SurfaceSupport (.ui_only for Class 1 test/headless hosts, .scene_composite for the full pipeline). validateCommonState checks the claim against the machinery scene assemble dispatches through, and encodeSurfaceAssemble returns error.SurfaceClassUnsupported rather than downgrading a scene surface to a UI-only graph. Inferring support from incidental fields is what let that downgrade happen silently in release.

Shared code never asks what a backend has

The same rule applies to every structural question. Shared render code never branches on @hasDecl / @hasField; a probe compiles clean on both hosts and on every stub, so a rename would delete a code path on one backend instead of failing a build. Three replacements, in order of preference:

  1. Require the decl and let the backend with nothing to do write an explicit no-op (MetalRenderer.waitBeforeNextFrame, MetalResources.endUpload) whose surrounding contract says why it is empty.
  2. Make a genuine capability difference a declared constant both sides give a value to (BackendOps.raytraced_light_clusters_slot).
  3. When the question is unavoidable, give it one named predicate with its backstop contract written next to it (rendergraph/backend.supportsPluginPostProcess, dispatchesByKind).

renderer_shape.zig states the shape shared code requires of a renderer, because Zig only analyses the branches it reaches. zig build check-probes (a dependency of zig build test) fails the build on a new branch probe; a probe used as a @compileError assertion is the fix and is not counted, and the remaining allowlisted branches carry a reason in src/hikari/build/check_probes.zig.


Ownership and threading

Diagram
Diagram source
sequenceDiagram
  participant GT as Game thread
  participant Lock as world.lock
  participant Pub as RenderFrame triple-buffer
  participant RT as Render thread
  participant GPU as GPU queues

  GT->>Lock: fill write RenderFrame
  GT->>Lock: unlock
  GT->>Pub: publish()
  Note over RT: does not take world.lock
  RT->>Pub: consume latest
  RT->>RT: applyRenderCommands / residency
  RT->>RT: prepareFrame
  RT->>RT: assemble + compile graph
  RT->>GPU: execute passes + barriers
  RT->>GPU: present / finishOutput
  • Frames in flight: graphics/renderer/frames.zig → frames_in_flight = 3. Ringed uniforms/object data; both Metal and D3D12 wait per ring slot before rewrite (Metal last CB on slot; D3D12 fence token). Present/swapchain pacing is orthogonal.
  • Two handle dialects (do not merge):
    • Residency: RenderHandle / TypedHandle (native/handle.zig)
    • Graph/RHI: BufferHandle / TextureHandle / graph handles (rhi/types.zig, rendergraph/types.zig)

Module map

text
src/hikari/src/graphics/
├── api.zig                  Public barrel
├── rhi/                     Backend-neutral vocabulary (types, barriers, queues)
├── renderer/                Frame policy: core, prepare, FIF, residency consume, pass encode
├── rendergraph/             Graph builder, compile, schedule, passes/, drivers/
├── shadow/                  Atlas policy, matrices, per-face culling
├── raytracing/              TLAS policy, bindings, geometry update rules
├── material/                PSO kinds, shader artifacts, uniforms
├── particles/               GPU particle runtime, emitter commands, live-bound
├── residency/               CPU/GPU residency, bindless heap, geometry_residency, telemetry
├── effects/                 G-buffer, content compute, GPU cull, visual look, lighting helpers
├── gpu/                     Vertex buffers, object data, material table, staging
└── debug/                   Visualizers, viewport pick/state

src/hikari/src/platform/{macOS,Windows}/graphics/
├── renderer.zig             Facade + EngineHandles
├── renderer_frame.zig       Graph backend + queue prepare/acquire/release/abort hooks
├── queue_timeline.zig       Native event/fence Signal·Wait for graph timeline tokens
├── pass_encoder.zig         begin / barrier / bind / finish (typed compute lists when async)
├── content_compute.zig    Content-compute ring dispatch (uses graph release for cross-queue tokens)
└── passes/backend_ops.zig   Draw/dispatch emission table

src/hikari/sdk/src/render_config.zig   Feature gates (POD)

RHI vocabulary

graphics/rhi/types.zig is the only place to extend graph sync language.

Diagram
Diagram source
flowchart LR
  subgraph resources [Resources]
    Tex[TextureHandle]
    Buf[BufferHandle]
    AS[AccelerationStructureHandle]
  end

  subgraph queues [QueueClass]
    G[graphics]
    C[compute]
    Cop[copy]
  end

  subgraph domain [PipelineDomain]
    Pg[graphics]
    Pc[compute]
    Pcop[copy]
    Prt[ray_tracing]
  end

  subgraph sync [Sync POD]
    RS[ResourceState]
    PS[PipelineStage]
    AM[AccessMask]
    RB[ResourceBarrier]
    ST[SubmissionToken]
  end

  Tex --> RB
  Buf --> RB
  AS --> RB
  RS --> RB
  PS --> RB
  AM --> RB
  G --> ST
  C --> ST
ConceptRole
ResourceStateLogical layout: color/depth attach, shader read/write, copy, AS build/read, present, …
PipelineStage / AccessMaskHazard granularity for barriers
QueueClassgraphics / compute / copy — independent of PipelineDomain
SubmissionToken{ queue, value } for timeline waits
CapabilitiesWhat the device actually supports (async compute, copy queue, bindless, RT, …)

Backends map these to Metal/D3D12 transitions; they must not invent parallel state enums for the graph.


Render-graph lifecycle (one frame)

Diagram
Diagram source
flowchart TB
  A[Adapter.prepareFrame] --> B[GraphBuilder.init]
  B --> C[pipeline.assemble]
  C --> D[declare textures / FrameTargets]
  C --> E[add conditional passes + accesses]
  E --> F[graph_builder.compile]
  F --> G[liveness cull dead passes]
  F --> H[emit CompiledBarrier list]
  F --> I[emit QueueSyncEdge acquires]
  G --> J[RenderGraphSchedule]
  H --> J
  I --> J
  J --> K[pipeline.execute]
  K --> L[for each live pass: apply barriers → driver.run]
  L --> M[Adapter.finishOutput / present]

Assemble vs execute

PhaseFileJob
Assemblepipeline.assembleGiven RenderPipelineConfig + FrameCtx, create targets and register passes in order
Compilecompile.zigCull unreachable work; derive barriers from access transitions; record cross-queue releases (producer) and acquires (consumer) for every graphics↔compute edge
Executepipeline.execute + driversprepare producer token → queueAcquire → barriers/work → flushPassBatch (when batch_boundary) → publish queueRelease; abort clears staged state on error

Submit batching: CompiledPass.batch_boundary forces a flush only on structural boundaries: queue switch, non-empty acquires/releases, and the last pass. GPU profiling does not participate in scheduling; Metal writes pass timestamp pairs into the active MTL4CommandBuffer, while D3D12 writes queries into the active command list. D3D12 keeps one closed command list per pass and batches ExecuteCommandLists; Metal shares one open command buffer per QueueClass (frame_submission.zig) and commits at the structural boundary. Graph flush is batch_boundary / flushPassBatch; encoder finish only ends the pass. Present stays frame-owned in finishOutput.

Graph texture allocation distinguishes scratch from history. Scratch textures with exact matching descriptors may share a physical pool entry when their compiled lifetimes are strictly disjoint, all accesses use the graphics queue, and the first logical texture's terminal state restores the next one's initial state. Cross-queue, overlapping, storage-only, or descriptor-mismatched textures remain separate. Persistent histories are declared per successfully encoded surface: an omitted history enters the same GPU-safe retirement window as cached scratch, while a skipped surface or failed encode leaves its prior declaration intact. Residency reports transient and persistent rows separately; retiring persistent rows are awaiting that safety window.

Pass kinds are the closed set in pass_registry.Kind. Platform registers callbacks once; assemble only schedules which kinds run this frame.


Pass inventory and data flow

Structural order (conditional rows omitted when gated off):

Diagram
Diagram source
flowchart TB
  Prep["Pre-graph: clusters · shadow pack · TLAS · GPU cull · content ensure"]

  Prep --> SD[shadow_depth]
  Prep --> CC[content_compute*]
  SD --> GB[gbuffer_geometry]
  CC --> GB
  GB --> DP[depth_pyramid]
  GB --> Late["occlusion_cull + gbuffer_geometry_late"]
  Late --> DP
  GB --> VTM[velocity_tile_max]
  SD --> VolD[volumetric_density]
  DP --> VolD
  GB --> VolD
  VolD --> VolI[volumetric_inject]
  VolI --> VolG[volumetric_integrate]
  GB --> AO[ambient_occlusion ± temporal]
  DP --> AO
  AO --> GI[global_illumination ± temporal]
  GI --> Lit{deferred_lighting / raytraced_direct+light}
  AO --> Lit
  GB --> Lit
  Lit --> Bounce[gi_bounce_capture]
  Bounce --> Ref[reflections ± temporal]
  Lit --> Ref
  Ref --> VolC[volumetric_composite]
  VolC --> TC[transmission_capture]
  Ref --> TC
  TC --> FT[forward_transparent]
  VolG --> FT
  FT --> TAA[temporal_reconstruction TAA/TAAU]
  TAA --> Post["exposure → auto_focus / DOF → motion_blur"]
  FT --> Post
  VTM --> Post
  Post --> Bloom[bloom_extract → blur ×2]
  Bloom --> TM["tonemap (+bloom) / debug_visualizers"]
  TM --> UI[ui]
  UI --> BB[(backbuffer / present)]

Gates, plugin post-process slots, and notes: Rendering — Implemented pipeline. Volumetric density/inject/integrate are scheduled before AO/lighting/reflections so froxel work can overlap those graphics passes on the async compute queue (graphics-queue fallback when the device has no usable async compute). Composite is always in the render domain after lighting, before glass and before TAA — fog therefore enters colour history (motion-only trail accepted). Glass applies the same integrated volume in forward. Camera post (exposure, DOF, motion blur) meters the post-reconstruction image, then bloom/tonemap.

Feature layers (orthogonal)

Diagram
Diagram source
flowchart LR
  subgraph A [Contract A — bus]
    GBuf[G-buffer attachments]
  end
  subgraph B [Contract B — classical]
    Def[Deferred + raster atlas]
  end
  subgraph C [Contract C — RT optional]
    RTS[RT scene TLAS]
    RTH[RT hit shading / bindless]
  end
  subgraph D [Contract D — GI]
    GI[Indirect diffuse SS / RT]
  end
  subgraph E [Contract E — content]
    AC[Async compute producers]
  end

  AC -.->|optional vertex texture| GBuf
  GBuf --> Def
  GBuf --> RTS
  RTS --> RTH
  Def --> Post[AO / GI / reflections / vol / TAA / bloom / tonemap / UI]
  RTS --> Post
  GBuf --> GI
  RTS --> GI
  GI --> Def

Master switches live in RenderPipelineConfig (sdk/src/render_config.zig). Disabled features skip assemble (no passes / no PSOs where documented) — see volumetrics zero-cost-off and RT prepare short-circuit in Rendering.


Sync points

1. CPU frames-in-flight (shared contract)

Canonical source: graphics/renderer/frames.zig.

Protocol (Metal and D3D12 — same logic, different wait primitive):

text
BEGIN:
  slot = frame_counter % frames_in_flight   // default 3
  waitUntilSlotComplete(slot)               // Metal: last CB on slot; D3D12: fence token on slot
  write uniforms / lights / object data / per-slot heaps into ring[slot]
  advance orphan free lists (age ≥ frames_in_flight)

RECORD + SUBMIT (binds ring[slot])

END:
  markSlotSubmitted(slot, token)            // Metal: track last CB; D3D12: ring_timeline + fence signal
  present / swapchain pacing                // orthogonal — protects backbuffers only
RuleDetail
SSOT depthframes.frames_in_flight; native mirrors HIKARI_MTL_FRAMES_IN_FLIGHT / HIKARI_D3D12_FRAME_POOL_COUNT (compile-time assert)
Ring waitAlways required before rewriting a slot — never substitute present/swapchain depth
Present waitIndependent; bounds backbuffer reuse (buffer_count, often 2)
Deferred freeOrphan age 0 (idle only) or ≥ frames_in_flight
Handle dialectsResidency RenderHandle vs graph RHI handles — do not merge

Tests: frames.zig (slot math, SlotTimeline, RingWriteTracker, protocol simulation) run under zig build test / graphics tests.

GPU pass timestamps

BackendStampResolve
MetalPer-slot MTL4CounterHeap timestamps (writeTimestampIntoHeap start/end pairs). Graph CBs stamp at create/commit; pre-graph producers sharing a CB stamp when the current pass changes.After ring-slot wait via resolveCounterRange + queryTimestampFrequency
D3D12TIMESTAMP query heap pair per pass + ResolveQueryData on the list. Graph render/compute passes and pre-graph raw lists (beginTimedPass) both write.After ring-slot fence wait + map readback

Shared publish: profiler/gpu_timing.zig → Profiler UI GPU rows. Gated by compile-time -Dprofiler-timing and runtime Recording. Independent of --profiler-residency (residency tabs). Pre-graph producers (light_binning, gpu_culling.prepare, skinning_writeback, raytracing.acceleration_structures) and editor viewport_composite / editor_ui_layer are timestamped the same way as graph passes.

2. Intra-queue barriers (same QueueClass)

Compiled from consecutive PassAccess transitions on a resource (mip/layer aware for textures):

text
CompiledBarrier {
  before → after ResourceState
  source/destination stages + access
  optional texture_range
}

Applied by pass_encoder immediately before the consuming pass encodes.

3. Cross-queue sync (QueueSync + QueueSyncEdge)

Bidirectional: graphics→compute (e.g. depth/shadow → volumetric inject) and compute→graphics (content compute → G-buffer; integrated volumes → forward/composite). Same-queue edges are no-ops. Content compute and engine compute share one graph-owned path.

Diagram
Diagram source
sequenceDiagram
  participant Prod as Producer queue
  participant QS as QueueSync map
  participant Cons as Consumer queue

  Prod->>QS: queuePrepareRelease → nextSignal
  Prod->>Prod: submit work + native Signal (Metal event / D3D12 fence)
  Note over Prod: After successful submit
  Prod->>QS: queueRelease → recordProduced
  Note over Cons: Before consumer encode
  Cons->>QS: producedToken(resource_id)
  Cons->>Cons: queueAcquire → native Wait
  Cons->>Cons: sample / read resource

Rules (queue_sync.zig + platform queue_timeline.zig):

  • Graph owns timeline counters, produced-token map, and compiled release/acquire edges; backends only Signal/Wait.
  • Resource ids cover graph textures, buffers, and acceleration structures (crossQueueResourceId), not only content-compute ring slots.
  • Identity is stable across frames (markCrossQueueProducer for lag-1 reads with no in-frame writer).
  • Resize / recreate → QueueSync.clear() so ids cannot alias stale tokens.
  • Metal encodes waits when a shared command buffer opens and the prepared signal at flush; only a no-submission pass needs the checked signal-only fallback. Intra-queue hazards use per-resource MTLFences (platform/macOS/graphics/barriers.zig).
  • No water/volumetrics ⇒ no edges, no signals (empty-list checks only).
  • Pre-graph jobs (light binning, GPU cull phase 1, skinning writeback) publish queue_sync.pre_graph_resource_id on the graphics timeline after prepare submits. The first graph pass on each QueueClass acquires that token. Metal frame_fence remains an extra safety net; it is not the Windows contract.

4. Texture upload (residency)

BackendPathSync
D3D12COPY queue upload → DIRECT promoteGPU fence wait (no CPU spin); render thread pollPendingTextureUploads
MetalCPU replaceRegionCompletes before publish ready

States: pending → uploading → ready / failed. Loading gates treat uploading as not ready.

5. Ray-tracing structures

  • Shared raytracing_scene.zig: reuse / refit / rebuild TLAS; static BLAS compact; retire after frames_in_flight. Structures are recorded onto the pre-graph buffer and fence-ordered; compaction is the only work allowed to drain the GPU, once per batch rather than once per structure (see Submission model in rendering.md).
  • Effective RT modes need scene_ready (non-empty opaque TLAS), not device support alone.
  • The renderer-global bindless heap activates for raster material preparation; RT hit shading reuses the same stable indices.

6. Present / output

  • Standalone: graph writes backbuffer → present.
  • Editor inset viewport: scene finishes in graph; _engine/composite blit + chrome are outside the scene graph (renderer_viewport.zig).

Lite vs full schedule (mental model)

Diagram
Diagram source
flowchart LR
  subgraph lite [Typical small-title lite]
    S1[shadow_depth]
    S2[gbuffer_geometry]
    S3[depth_pyramid?]
    S4[deferred_lighting]
    S5[forward_transparent?]
    S6[tonemap?]
    S7[ui]
    S1 --> S2 --> S3 --> S4 --> S5 --> S6 --> S7
  end

  subgraph full [Optional heavy stack]
    F1[+ AO / GI temporal]
    F2[+ reflections temporal]
    F3[+ volumetric inject/integrate/composite]
    F4[+ TAA]
    F5[+ exposure / DOF / motion blur]
    F6[+ bloom]
    F7[raytraced_direct + raytraced_lighting / RT scene]
  end

  lite -.->|feature gates| full

Turning RT and volumetrics off removes those passes and (for volumetrics) PSO/alloc work. Deferred G-buffer + shadow atlas remain the baseline cost.


Where to change what

GoalTouch
New pass / reorderpass_registry.Kind + passes/ + pipeline.assemble + driver + platform callback
New resource state / queue hazardrhi/types.zig then compile/encoder lowering
Feature togglerender_config.zig + assemble gates (+ project settings schema if user-facing)
Draw/dispatch encodingplatform/*/passes/backend_ops.zig / pass_encoder.zig
G-buffer layouteffects/gbuffer.zig + geometry/lighting shaders (both backends)
Shadow atlas policyshadow/shadow_types.zig / shadow/shadow_pass.zig
Cross-queue producerGraph accesses on differing QueueClass → compile emits release+acquire; queue_timeline Signal/Wait; markCrossQueueProducer for cross-frame only
Async volumetric injectpasses/volumetric.zig queue param + pipeline.assemble earliest-after-depth placement + supportsAsyncComputeQueue

Code map

Shared modules (src/hikari/src/graphics/):

FileResponsibility
renderer/renderer_core.zigFrame/graph algorithm over a platform Adapter: prepare → assemble → compile → execute → present
renderer/frame_epoch.zigEditor multi-surface protocol (present list, dual-publish, chrome rebind)
renderer/renderer_shared.zigFacade; split under renderer_shared/ (frame, commands/ + facade, consume, prepare, emit/ + facade)
renderer/renderer_prepare.zigFacade; prepareFrame stages under renderer_prepare/ (temporal, objects, rt, uniforms, lighting, …)
renderer/renderer_pipelines.zigFullscreen/effect PSOs, engine pipelines, UI atlas, optional RT
effects/content_compute.zigContent-compute producers, lag policy, ring helpers
effects/gpu_culling.zigCull prep + slot capacity helpers + shared frustum-plane extraction
shadow/shadow_culling.zigPer-face/class meshlet work staging for GPU visible-triangle expansion
renderer/renderer_lifecycle.zigTeardown: residency, PSOs, materials, textures
renderer/renderer_residency.zigPer-frame command drain + consumeRenderFrame
renderer/renderer_common.zigEngineHandles, primary target helpers, feature availability, facade setters
renderer/dynamic_buffer_manifest.zigCompile-time shared frame-ring buffer membership and create/assign/destroy lifecycle; backend types come from platform/*/graphics/dynamic_buffer_types.zig
renderer/pass_encoder.zig / renderer/pass_targets.zigPassEncoder contract + graph texture unwrap
rendergraph/drivers/Shared pass run drivers
rendergraph/graph.zigFacade; types / schedule / compile / builder
rendergraph/pass_registry.zigPass-kind registry + typed callbacks
shadow/shadow_pass.zigAtlas size policy, face uniforms, per-face draws
renderer/platform_resources.zigPlatformResources(Backend) tables
material/shader_uniforms.zigGlobal HikariUniforms CPU mirror
residency/bindless_heap.zigRenderer-global stable buffer/texture indices (raster + RT)
gpu/geometry_table.zigPer-instance bindless geometry rows for meshlet attribute-pull + RT resolve
gpu/material_table.zigSurface-local, frame-ring shared raster/RT material records indexed by object instance id
residency/geometry_residency.zigCPU mesh residency ledger (--profiler-residency=cpu)
residency/render_residency/telemetry.zigGPU ownership telemetry (--profiler-residency=gpu)
raytracing/raytracing_scene.zig / raytracing/raytracing_bindings.zigTLAS policy + instance packing
material/pipeline_kind.zigPSO kind tags + color/depth/blend policy
effects/gbuffer.zigAttachment slots + target_formats

Platform layout (Metal / D3D12 — native only):

FileResponsibility
platform/*/graphics/renderer.zigDevice lifecycle, handles, tick
renderer_frame.zigRender-graph backend + FrameAdapter
pass_encoder.zigNative begin/barrier/bind/finish
renderer_viewport.zigEditor insets and offscreen composite
resources.zigBackend traits for PlatformResources
passes/backend_ops.zigGeometry, lighting, shadow draw emission
shadow_renderer.zigAtlas GPU resources + pass create

Do not dump new policy into the platform facade. Shared behaviour goes under graphics/.

Related docs

DocFocus
RenderingContracts, pass gates, lighting/RT/TAA behavior
ShadersPackages, PipelineKind, binding layouts
LifecycleSession tick → publish → render
PlatformsMetal / D3D12 / Linux status
Volumetric mediaGlobal fog and bounded smoke, froxel path, async compute queue contract, zero-cost-off
src/hikari/AGENTS.mdAgent invariants for graphics / handles / FIF
PreviousRenderingNext Frame governor

Documentation follows the current engine checkout.

Snapshot cc148c75Source docs/systems/renderer-architecture.md
On this pageLayer stackShared code never asks what a backend hasOwnership and threadingModule mapRHI vocabularyRender-graph lifecycle (one frame)Assemble vs executePass inventory and data flowFeature layers (orthogonal)Sync points1. CPU frames-in-flight (shared contract)GPU pass timestamps2. Intra-queue barriers (same QueueClass)3. Cross-queue sync (QueueSync + QueueSyncEdge)4. Texture upload (residency)5. Ray-tracing structures6. Present / outputLite vs full schedule (mental model)Where to change whatCode mapRelated docs Back to top