cinder.render
cinder.render #
Source: cinder/render/__init__.mojo
Renderer: cameras, meshes, materials, textures, the frame graph and the forward+ pipeline. Everything here talks to the GPU only through cinder.gfx.
cinder.render.camera #
Source: cinder/render/camera.mojo
Cameras and frustum culling. Right-handed, Y up, camera looks down -Z. Projection is reverse-Z with an infinite far plane, so the frustum has five planes. cull_spheres tests four spheres at a time with SIMD.
comptime F4 = SIMD[DType.float32, 4]— Four float32 lanes: one coordinate of four spheres inFrustum.cull_spheres_into.
struct Plane #
struct Plane(ImplicitlyCopyable, RegisterPassable)normal . p + d >= 0 is the inside half-space.
Fields:
-
normal: Vec3 -
d: Float32 -
from_rowfrom_row(v: Vec4) -> Plane
Plane from a clip-space row combination, scaled sonormalhas unit length. -
distancedistance(self, p: Vec3) -> Float32
Signed distance frompto the plane (positive inside), in the units ofp.
struct Frustum #
struct Frustum(Copyable, Movable)Left, right, bottom, top, near planes extracted from a view-projection matrix (Gribb/Hartmann). Reverse-Z with infinite far: the near plane is row3 - row2 (clip z <= w); no far plane.
Fields:
-
planes: Array[Plane, 5] -
__init____init__(out self, view_proj: Mat4) -
contains_spherecontains_sphere(self, centre: Vec3, radius: Float32) -> Bool
True unless the sphere lies entirely behind one of the five planes. Conservative: a sphere near a frustum edge or corner may pass without intersecting it. -
contains_pointcontains_point(self, p: Vec3) -> Bool
True whenpis on the inside of all five planes (near, left, right, bottom, top). -
cull_spheres_intocull_spheres_into(self, spheres: List[Vec4], mut out: List[Bool])
Visibility per sphere (xyz centre, w radius), four spheres per SIMD step, written into a list supplied by the caller (cleared first, capacity kept). -
cull_spherescull_spheres(self, spheres: List[Vec4]) -> List[Bool]
Visibility per sphere (xyz centre, w radius); allocates the result list. Usecull_spheres_intoin per-frame code to reuse a scratch list.
struct Camera #
struct Camera(ImplicitlyCopyable, Movable)A perspective camera. near is the only clip distance (infinite far).
Fields:
-
position: Vec3 -
rotation: Quat -
fov_y: Float32 -
aspect: Float32 -
near: Float32 -
__init____init__( out self, position: Vec3, rotation: Quat, fov_y: Float32 = 1.0471976, aspect: Float32 = 16.0 / 9.0, near: Float32 = 0.05, ) -
look_atlook_at( eye: Vec3, target: Vec3, up: Vec3 = Vec3.unit_y(), fov_y: Float32 = 1.0471976, aspect: Float32 = 16.0 / 9.0, near: Float32 = 0.05, ) -> Camera
A camera ateyelooking attarget, with its rotation taken fromMat4.look_at.uponly orients the camera (the storedup()is re-orthogonalised);fov_yis the vertical field of view in radians,nearthe near clip distance in metres. -
forwardforward(self) -> Vec3
World-space view direction:Vec3.forward()(-Z) rotated byrotation, unit length. -
rightright(self) -> Vec3
World-space right axis: +X rotated byrotation. -
upup(self) -> Vec3
World-space up axis: +Y rotated byrotation. -
viewview(self) -> Mat4
World-to-view matrix (right-handed, looking down -Z), column-major. -
projectionprojection(self) -> Mat4
Reverse-Z perspective with an infinite far plane: depth 1 atnear, 0 at infinity. -
view_projectionview_projection(self) -> Mat4
projection() * view(): world space to clip space. -
frustumfrustum(self) -> Frustum
The five-plane culling frustum ofview_projection()(no heap allocation). -
set_viewportset_viewport(mut self, width: Int, height: Int)
Setsaspecttowidth / heightin pixels; ignored whenheightis not positive.
cinder.render.frame_graph #
Source: cinder/render/frame_graph.mojo
Frame graph: declared passes, transient textures with lifetimes and aliasing, automatic ordering. The graph is rebuilt every frame (cheap: a few passes). compile orders passes by their read/write dependencies, culls passes that contribute nothing to an exported resource, computes each transient's lifetime as a pass-index interval and assigns physical textures so that transients with compatible descriptions and disjoint lifetimes share one allocation. Physical textures persist in a pool across frames and are dropped when their description stops being requested (resize). The renderer executes the compiled order itself (order) and looks up each transient's view with view_of; the graph never calls back into user code (no closures cross the GPU layer). to_graphviz renders the compiled graph for the debug UI.
struct TransientDesc #
struct TransientDesc(Equatable, ImplicitlyCopyable, Movable)Size in pixels, format, WGPUTextureUsage bits, sample and mip count of a transient. Transients share one pooled physical texture only when their descriptions are equal.
Fields:
width: UInt32height: UInt32format: WGPUTextureFormatusage: UInt64sample_count: UInt32mip_levels: UInt32
struct FrameGraph #
struct FrameGraph(Movable)One frame's passes and resources, compiled into an execution order and pooled textures. Per frame: reset, declare resources (create, import_view, create_external) and passes (add_pass, read, write), export what must survive, compile, then walk order and look up views with view_of. Physical textures are owned by the graph and freed by release (or when a compile no longer requests them); declaration and compile do not allocate in steady state.
Fields:
-
resources: List[_Resource] -
passes: List[_Pass] -
order: List[Int] — Pass indices in execution order (set bycompile). -
physicals: List[_Physical] -
compiled: Bool -
culled_count: Int -
__init____init__(out self) -
resetreset(mut self)
Starts a new frame's declaration. Physical textures stay pooled; pass records keep their read/write list capacity for reuse. -
createcreate(mut self, name: String, desc: TransientDesc) -> Int
Declares a transient texture and returns its resource index (valid untilreset). The physical texture is assigned bycompile, possibly shared with other transients. -
import_viewimport_view( mut self, name: String, view: TextureViewHandle, desc: TransientDesc ) -> Int
A view the caller keeps alive (swapchain, persistent target). Never aliased. -
create_externalcreate_external(mut self, name: String) -> Int
A buffer-backed resource (no texture): passes writing it are ordered before readers and kept alive by them, but no physical texture is allocated. -
exportexport(mut self, resource: Int)
Marks a resource as needed outside the graph; passes feeding it are kept. -
add_passadd_pass(mut self, name: String) -> Int
Declares a pass and returns its index; reuses a spare pass record when there is one. -
readread(mut self, pass_: Int, resource: Int)
Declares thatpass_readsresource(ordered after the resource's producer). -
writewrite(mut self, pass_: Int, resource: Int)
Declares thatpass_writesresource; writers of a resource keep declaration order. -
compilecompile(mut self, mut res: Resources) raises
Orders, culls, computes lifetimes and assigns physical textures. -
releaserelease(mut self, mut res: Resources) raises
Destroys every pooled physical texture and its view; raises on aResourceserror. -
view_ofview_of(self, resource: Int) raises -> TextureViewHandle
View ofresourceaftercompile: the imported view or its physical texture's view. Raises for an external (buffer-backed) resource and for one with no physical texture (unused, culled or not compiled). -
texture_oftexture_of(self, resource: Int) raises -> TextureHandle
Physical texture behind a transient aftercompile. Raises for imported and external resources and for transients without a physical texture. -
findfind(self, name: String) -> Int
Index of the resource with this name, or -1. -
physical_countphysical_count(self) -> Int
Pooled physical textures after the lastcompile(FrameStats.transient_textures). -
is_culledis_culled(self, pass_: Int) -> Bool
True when the lastcompileculled the pass (it feeds no exported resource). -
lifetimelifetime(self, resource: Int) -> Tuple[Int, Int]
(first, last) execution-order indices using the resource; (-1, -1) if no kept pass does. -
to_graphvizto_graphviz(self) -> String
DOT text: passes as boxes in execution order, resources as ellipses, edges for reads/writes.
cinder.render.lights #
Source: cinder/render/lights.mojo
Lights and the clustered-culling configuration shared with shaders/common.wgsl.
comptime CLUSTER_X = 16— Cluster grid columns across the screen; mirrorsCLUSTER_Xin common.wgsl.comptime CLUSTER_Y = 9— Cluster grid rows across the screen; mirrorsCLUSTER_Yin common.wgsl.comptime CLUSTER_Z = 24— Logarithmic depth slices of the cluster grid; mirrorsCLUSTER_Zin common.wgsl.comptime CLUSTER_COUNT = CLUSTER_X * CLUSTER_Y * CLUSTER_Z— Total clusters (16 x 9 x 24); sizes thecluster_gridcount buffer (one u32 each).comptime CLUSTER_MAX_LIGHTS = 32— Light indices stored per cluster; lights beyond this are dropped byclusters.wgsl.comptime LIGHT_BYTES = 64— Bytes per packed light: the size ofLightin common.wgsl.comptime MAX_LIGHTS = 1024— Most local lights the renderer uploads per frame; extra lights are dropped.
struct LightKind #
struct LightKind(Equatable, ImplicitlyCopyable, RegisterPassable)Light type tag; values mirror LIGHT_* in common.wgsl (0 directional, 1 point, 2 spot).
Fields:
-
value: UInt32 -
directionaldirectional() -> LightKind
Sun-like light at infinity; only its direction matters. -
pointpoint() -> LightKind
Omnidirectional light at a position, attenuated to zero atrange. -
spotspot() -> LightKind
Cone light at a position alongdirection, attenuated to zero atrange.
struct Light #
struct Light(ImplicitlyCopyable, Movable)A light. Directional: direction (travel direction), color * intensity in lux. Point and spot: position, range (metres), intensity in watts, spot cone angles in radians.
Fields:
-
kind: LightKind -
position: Vec3 -
direction: Vec3 -
color: Vec3 -
intensity: Float32 -
range: Float32 -
inner_angle: Float32 -
outer_angle: Float32 -
cast_shadows: Bool -
__init____init__( out self, kind: LightKind, position: Vec3 = Vec3.zero(), direction: Vec3 = Vec3(0.0, -1.0, 0.0), color: Vec3 = Vec3.one(), intensity: Float32 = 1.0, range: Float32 = 10.0, inner_angle: Float32 = 0.4, outer_angle: Float32 = 0.6, cast_shadows: Bool = False, ) -
sunsun( direction: Vec3, color: Vec3 = Vec3.one(), lux: Float32 = 3.0, cast_shadows: Bool = True, ) -> Light
Directional light travelling alongdirection(normalised), withluxilluminance. Position, range and cone angles are zero; shadows default to on (cascaded shadow maps). -
point_lightpoint_light( position: Vec3, color: Vec3, watts: Float32, range: Float32, cast_shadows: Bool = False, ) -> Light
Point light atposition(metres) emittingwatts, reaching zero atrangemetres. -
spot_lightspot_light( position: Vec3, direction: Vec3, color: Vec3, watts: Float32, range: Float32, inner: Float32, outer: Float32, cast_shadows: Bool = False, ) -> Light
Spot light alongdirection(normalised) emittingwattswith arangein metres.innerandouterare cone half-angles in radians from the axis: full intensity insideinner, fading to zero atouter. -
bounding_spherebounding_sphere(self) -> Vec4
Influence sphere: xyz position, w range in metres (meaningless for directional lights). -
packpack(self, mut w: ByteWriter, shadow_view: Int)
64 bytes matchingLightin common.wgsl.
def pack_lights_into #
def pack_lights_into( mut w: ByteWriter, lights: List[Light], shadow_views: List[Int] )Appends the GPU light array (at least one zeroed entry) to w.
def pack_lights #
def pack_lights(lights: List[Light], shadow_views: List[Int]) -> List[UInt8]pack_lights_into into a new byte list (allocates; at least one zeroed 64-byte entry). shadow_views[i] is light i's first shadow-view index (-1 or missing: no shadow).
struct ClusterParams #
struct ClusterParams(ImplicitlyCopyable, Movable)Logarithmic z slicing: slice = log2(z) * scale + bias between near and far.
Fields:
-
scale: Float32 -
bias: Float32 -
far: Float32 -
computecompute(near: Float32, far: Float32) -> ClusterParams
Slice parameters mappingnearto slice 0 andfartoCLUSTER_Z(metres). -
slice_ofslice_of(self, view_z: Float32) -> Int
Z slice for a positive view-space distance in metres, clamped to [0, CLUSTER_Z - 1]. Mirrorscluster_z_slicein common.wgsl.
cinder.render.material #
Source: cinder/render/material.mojo
glTF 2.0 metallic-roughness materials and their GPU bind groups. Uniform layout (96 bytes, 16-byte aligned fields, mirrored by shaders/material.wgsl): 0 base_color: vec4 16 emissive.rgb, emissive_strength 32 metallic, roughness, normal_scale, occlusion_strength 48 alpha_cutoff, alpha_mode (0 opaque, 1 mask, 2 blend), double_sided, texture_flags 64 uv_scale.xy, uv_offset.xy (KHR_texture_transform, applied to every map) 80 uv_rotation, texcoord_bits (3 bits per map: base, mr, normal, occlusion, emissive), pad, pad
comptime MATERIAL_UNIFORM_SIZE = 96— Bytes in the material uniform (group 2, binding 0); see the module docstring for the layout.
struct AlphaMode #
struct AlphaMode(Equatable, ImplicitlyCopyable, RegisterPassable)How alpha is used; packed into the material uniform and mirrored in WGSL.
Fields:
-
value: UInt32 -
opaqueopaque() -> AlphaMode
Alpha is ignored; the surface is fully opaque. -
maskmask() -> AlphaMode
Alpha-tested: fragments with alpha belowalpha_cutoffare discarded. -
blendblend() -> AlphaMode
Alpha-blended; drawn after opaque batches, sorted back to front. -
comptime TEX_BASE_COLOR = 0— Slot of the base colour map inMaterial.textures,texcoordsandtexture_flags. -
comptime TEX_METALLIC_ROUGHNESS = 1— Slot of the metallic-roughness map inMaterial.textures,texcoordsandtexture_flags. -
comptime TEX_NORMAL = 2— Slot of the normal map inMaterial.textures,texcoordsandtexture_flags. -
comptime TEX_OCCLUSION = 3— Slot of the occlusion map inMaterial.textures,texcoordsandtexture_flags. -
comptime TEX_EMISSIVE = 4— Slot of the emissive map inMaterial.textures,texcoordsandtexture_flags.
struct Material #
struct Material(Copyable, Movable)CPU description. Texture views are null when a map is absent (defaults are bound instead).
Fields:
-
name: String -
base_color: Vec4 -
metallic: Float32 -
roughness: Float32 -
emissive: Vec3 -
emissive_strength: Float32 -
normal_scale: Float32 -
occlusion_strength: Float32 -
alpha_mode: AlphaMode -
alpha_cutoff: Float32 -
double_sided: Bool -
uv_scale: Vec2 -
uv_offset: Vec2 -
uv_rotation: Float32 -
textures: Array[TextureViewHandle, 5] -
texcoords: Array[UInt8, 5] -
sampler: SamplerHandle -
__init____init__(out self, name: String = String("material")) -
texture_flagstexture_flags(self) -> UInt32
Bitiset when map sloti(TEX_*) has a texture view. -
texcoord_bitstexcoord_bits(self) -> UInt32
Texture coordinate set per map, 3 bits perTEX_*slot (the shader takes uv1 for 1). -
pack_uniformpack_uniform(self) -> List[UInt8]
The 96-byte uniform laid out as in the module docstring, in a new list (allocates).
def material_bind_group_layout #
def material_bind_group_layout( mut res: Resources, ) raises -> BindGroupLayoutHandleCreates the group-2 material layout matching shaders/material.wgsl; raises on a GPU error. Binding 0 is the uniform (vertex and fragment), 1 the sampler, 2..6 the maps in TEX_* order.
struct GpuMaterial #
struct GpuMaterial(ImplicitlyCopyable, Movable)Uniform buffer and bind group of an uploaded material. alpha_mode and double_sided are copied from the Material to select its pipeline.
Fields:
uniform: BufferHandlebind_group: BindGroupHandlealpha_mode: AlphaModedouble_sided: Bool
def upload_material #
def upload_material( mut res: Resources, layout: BindGroupLayoutHandle, defaults: DefaultTextures, material: Material, ) raises -> GpuMaterialCreates the material's uniform buffer and group-2 bind group; raises on a GPU error. Absent maps bind the defaults (white base colour, metallic-roughness and occlusion, flat normal, black emissive) and a null sampler binds sampler_linear_repeat. The texture views stay owned by the caller; the returned buffer and bind group are new allocations.
def update_material #
def update_material( mut res: Resources, gpu: GpuMaterial, material: Material ) raisesRe-uploads the parameters (textures are fixed per bind group).
cinder.render.mesh #
Source: cinder/render/mesh.mojo
Mesh data on the CPU, GPU meshes, the engine vertex format and procedural primitives. Vertex stream (56 bytes): position f32x3 @0, normal f32x3 @12, tangent f32x4 @24 (w = handedness), uv0 f32x2 @40, uv1 f32x2 @48. Skinning attributes live in a second stream (SkinVertex, 24 bytes): joints u16x4 @0, weights f32x4 @8. Indices are u32.
comptime VERTEX_STRIDE = 56— Bytes per vertex in the static stream (slot 0); layout in the module docstring.comptime SKIN_VERTEX_STRIDE = 24— Bytes per vertex in the skinning stream (slot 1): u16x4 joints, f32x4 weights.comptime PI = Float32(3.14159265358979)— Pi as Float32, for the procedural primitives.
def vertex_layout #
def vertex_layout() -> VertexLayoutSlot 0: the static vertex stream. Locations 0..4.
def skin_vertex_layout #
def skin_vertex_layout() -> VertexLayoutSlot 1: joints and weights. Locations 5 and 6.
struct Bounds #
struct Bounds(ImplicitlyCopyable, Movable)Axis-aligned bounding box in mesh space (metres).
Fields:
-
min: Vec3 -
max: Vec3 -
centrecentre(self) -> Vec3
Midpoint of the box. -
radiusradius(self) -> Float32
Half the box diagonal: the radius of the bounding sphere aroundcentre(). -
spheresphere(self) -> Vec4
Bounding sphere: xyz centre, w radius (the input ofFrustum.cull_spheres_into). -
emptyempty() -> Bounds
Inverted box (min +3.4e38, max -3.4e38); the firstincludecollapses it to a point. -
includeinclude(mut self, p: Vec3)
Grows the box to containp.
struct MorphTarget #
struct MorphTarget(Copyable, Movable)Per-vertex position and normal deltas of one morph target (normals may be empty).
Fields:
positions: List[Vec3]normals: List[Vec3]
struct MeshLod #
struct MeshLod(ImplicitlyCopyable, Movable)An index range of the combined index buffer and the simplification error it carries.
Fields:
first_index: UInt32index_count: UInt32error: Float32
struct MeshData #
struct MeshData(Copyable, Movable)CPU-side triangle mesh. Parallel per-vertex arrays; tangents, uv1, joints and weights may be empty and are filled with defaults at upload.
Fields:
-
positions: List[Vec3] -
normals: List[Vec3] -
tangents: List[Vec4] -
uv0: List[Vec2] -
uv1: List[Vec2] -
joints: List[SIMD[DType.uint16, 4]] -
weights: List[Vec4] -
indices: List[UInt32] -
morph_targets: List[MorphTarget] -
__init____init__(out self) -
vertex_countvertex_count(self) -> Int
Number of vertices (len(positions)). -
triangle_counttriangle_count(self) -> Int
Number of triangles (len(indices) // 3). -
has_skinhas_skin(self) -> Bool
True when every vertex has joint indices and weights (jointsandweightsboth matchpositions, non-empty), which is whatpack_skin_verticesreads. -
boundsbounds(self) -> Bounds
Bounds ofpositions; a zero box at the origin when the mesh has no vertices. -
validatevalidate(self) raises
Raises when the arrays are inconsistent. Checks that normals and uv0 match the vertex count, that tangents and uv1 are empty or match it, thatjointsandweightsare either both empty or both match it (a skin needs one of each per vertex), that the index count is a multiple of 3 and that every index is in range. Morph targets are not checked (missing deltas pack as zero). -
compute_normalscompute_normals(mut self)
Area-weighted face normals accumulated per vertex. -
compute_tangentscompute_tangents(mut self, lib: NativePtr) raises
MikkTSpace tangents through the shim (requires normals and uv0). -
pack_verticespack_vertices(self) -> List[UInt8]
The 56-byte-per-vertex static stream as a new byte list (allocates). Missing tangents become (1, 0, 0, 1) and missing uv1 repeats uv0. Expects normals and uv0 for every vertex (seevalidate). -
pack_skin_verticespack_skin_vertices(self) -> List[UInt8]
The 24-byte-per-vertex skin stream: little-endian u16x4 joints, then f32x4 weights. Allocates; expectsjointsandweightsfor every vertex. -
pack_indicespack_indices(self) -> List[UInt8]
indicesas little-endian u32 bytes in a new list (allocates). -
pack_morph_deltaspack_morph_deltas(self) -> List[UInt8]
Per target, per vertex: position delta (vec4) then normal delta (vec4), as common.wgsl reads. -
build_lodsbuild_lods( self, lib: NativePtr, levels: Int = 4 ) raises -> List[List[UInt32]]
LOD index lists (level 0 is the original, vertex-cache optimised) via meshoptimizer. Each level targets half the triangles of the previous one with a relative error bound of 2%; a level that cannot be simplified further ends the chain.
struct GpuMesh #
struct GpuMesh(Copyable, Movable)Buffers for one mesh. skin_vertices is null when the mesh is not skinned, morph_buffer when it has no morph targets. lods[0] is the full-detail index range. index_count is the full-detail index count (lods[0].index_count, 0 without LODs), the same for upload_mesh_lods and upload_mesh_bytes; the index buffer holds every LOD range, so its total length is the sum over lods.
Fields:
-
vertices: BufferHandle -
skin_vertices: BufferHandle -
indices: BufferHandle -
index_count: UInt32 -
vertex_count: UInt32 -
bounds: Bounds -
lods: List[MeshLod] -
morph_buffer: BufferHandle -
morph_target_count: UInt32 -
__init____init__( out self, vertices: BufferHandle, skin_vertices: BufferHandle, indices: BufferHandle, index_count: UInt32, vertex_count: UInt32, bounds: Bounds, var lods: List[MeshLod], morph_buffer: BufferHandle, morph_target_count: UInt32, ) -
select_lodselect_lod( self, projected_diameter_px: Float32, screen_height_px: Float32 ) -> Int
Smaller on screen -> coarser LOD. Level 1 below 1/4 of the screen height, level 2 below 1/8, level 3 below 1/16 (resolution independent).
def upload_mesh #
def upload_mesh( mut res: Resources, label: String, data: MeshData ) raises -> GpuMeshUploads with a single LOD.
def upload_mesh_lods #
def upload_mesh_lods( mut res: Resources, label: String, data: MeshData, lod_indices: List[List[UInt32]], ) raises -> GpuMeshUploads vertex streams plus one combined index buffer holding every LOD range.
def upload_mesh_bytes #
def upload_mesh_bytes( mut res: Resources, label: String, vertex_count: UInt32, vertex_bytes: List[UInt8], skin_bytes: List[UInt8], index_bytes: List[UInt8], var lods: List[MeshLod], bounds: Bounds, morph_target_count: UInt32, morph_bytes: List[UInt8], ) raises -> GpuMeshUploads streams that are already in GPU layout (cooked meshes).
def destroy_mesh #
def destroy_mesh(mut res: Resources, mesh: GpuMesh) raisesDestroys the mesh's vertex and index buffers, and its skin and morph buffers when present. The handles in mesh are left as they are; raises on a Resources error.
def cube #
def cube(half: Float32 = 0.5) -> MeshData24-vertex cube with per-face normals, CCW winding seen from outside.
def uv_sphere #
def uv_sphere( radius: Float32 = 0.5, rings: Int = 16, segments: Int = 32 ) -> MeshDataUV sphere centred at the origin, poles on +Y and -Y; radius in metres. (rings + 1) * (segments + 1) vertices (the seam is duplicated), counter-clockwise seen from outside. u runs around the sphere from +X towards +Z, v from the top pole (0) to the bottom (1); tangents point along increasing u.
def plane #
def plane(half: Float32 = 0.5, subdivisions: Int = 1) -> MeshDataAn XZ plane facing +Y.
cinder.render.renderer #
Source: cinder/render/renderer.mojo
The forward+ renderer: shadows, depth prepass, GTAO, clustered lights, PBR, sky, bloom, tonemap. Renderer.render(scene, target) builds the frame graph for the current settings, compiles it (ordering, transient aliasing), fills the GPU buffers from the scene and records every pass into one command buffer. CPU work per frame: frustum culling (SIMD), LOD selection, batching by (mesh, material, skinning), cascade fitting, light packing. Nothing here waits on the GPU.
comptime FRAME_UNIFORM_SIZE = 720— Bytes in the frame uniform: the size ofFramein common.wgsl (checked each frame).comptime PASS_UNIFORM_STRIDE = 256— Bytes per view-projection slot inpass_uniforms, the dynamic-offset alignment.comptime MAX_PASS_VIEWS = 128— Slots inpass_uniforms: four cascades, the atlas views, then the camera.comptime INSTANCE_BYTES = 144— Bytes per instance in theinstancesbuffer: model, normal matrix, u32x4 params.comptime MORPH_WEIGHTS_BYTES = 32— Bytes of morph weights bound per morphed instance: eight f32 weights.comptime POST_UNIFORM_STRIDE = 256— Bytes per slot inpost_uniforms(and per morph-weight slot), the dynamic-offset alignment.comptime MAX_CACHED_POST_GROUPS = 64— Post bind groups kept across frames before the cache is rebuilt.comptime MAX_POST_PASSES = 32— Slots inpost_uniformsper frame.comptime INITIAL_MORPH_SLOTS = 32— Initial slots in the morph-weight buffer; it doubles when a frame has more morphed instances.comptime BLOOM_MIPS = 6— Levels in the bloom chain; level 0 is half the output resolution.comptime ENV_SIZE = 256— Face size in pixels of the baked environment cube (RGBA16F, full mip chain).comptime PREFILTER_SIZE = 128— Face size in pixels of the prefiltered specular cube.comptime PREFILTER_MIPS = 6— Mips of the prefiltered cube; mip m holds roughnessm / (PREFILTER_MIPS - 1).comptime IRRADIANCE_SIZE = 32— Face size in pixels of the diffuse irradiance cube.comptime BRDF_LUT_SIZE = 256— Size in pixels of the split-sum BRDF lookup texture.comptime SHADER_DIR = "cinder/render/shaders"— Directory of the renderer's WGSL files, relative to the working directory.
struct Tonemapper #
struct Tonemapper(Equatable, ImplicitlyCopyable, RegisterPassable)Tone curve of the tonemap pass; values mirror post.settings.z in post.wgsl.
Fields:
-
value: UInt32 -
agxagx() -> Tonemapper
AgX (the default). -
acesaces() -> Tonemapper
ACES filmic curve. -
nonenone() -> Tonemapper
No curve: the exposed colour is clamped to [0, 1]; also used for debug views.
struct DebugMode #
struct DebugMode(Equatable, ImplicitlyCopyable, RegisterPassable)Mirrors DEBUG_* in common.wgsl.
Fields:
-
value: UInt32 -
nonenone() -> DebugMode
Normal lit output. -
normalsnormals() -> DebugMode
World-space normals mapped to colour (n * 0.5 + 0.5). -
cascadescascades() -> DebugMode
Colour per sun shadow cascade, shaded by the sun angle. -
clustersclusters() -> DebugMode
Heat map of the light count in each fragment's cluster (full scale at 16 lights). -
overdrawoverdraw() -> DebugMode
Overdraw count: forward pipelines blend additively with the depth test off, so every rasterised fragment (hidden or not, opaque or blended) adds 0.1 grey; brightness is the fragment count / 10 (white at 10 or more) and empty sky is black. -
lodlod() -> DebugMode
Colour per instance from its LOD index (bits 8..15 of the instance flags), skinned and morphed instances included. -
albedoalbedo() -> DebugMode
Unlit base colour (material factor times the base colour map). -
aoao() -> DebugMode
The ambient occlusion term as greyscale.
struct RenderSettings #
struct RenderSettings(ImplicitlyCopyable, Movable)Quality knobs, read at the start of every render (change them between frames). cascade_resolution, atlas_size and atlas_tile are read at construction only.
Fields:
-
msaa: UInt32 — 1 or 4. -
bloom: Bool -
bloom_strength: Float32 -
bloom_threshold: Float32 -
tonemapper: Tonemapper -
exposure: Float32 -
fxaa: Bool -
ao: Bool -
shadows: Bool -
shadow_distance: Float32 -
cascade_resolution: Int -
atlas_size: Int -
atlas_tile: Int -
ibl_intensity: Float32 -
debug: DebugMode -
wireframe: Bool -
cull: Bool -
__init____init__(out self)
struct Environment #
struct Environment(ImplicitlyCopyable, Movable)Procedural sky by default; equirect (an RGBA16F 2D texture view) replaces it when set.
Fields:
-
zenith: Vec3 -
ground: Vec3 -
sun_intensity: Float32 -
equirect: TextureViewHandle -
__init____init__(out self)
struct RenderInstance #
struct RenderInstance(Copyable, Movable)One drawn object: mesh and material indices into the renderer's registries.
Fields:
-
mesh: Int -
material: Int -
transform: Mat4 -
joint_offset: Int — Index of the first joint matrix inRenderScene.joints(skinned meshes), else -1. -
morph_weights: List[Float32] -
cast_shadows: Bool -
__init____init__( out self, mesh: Int, material: Int, transform: Mat4, joint_offset: Int = -1, cast_shadows: Bool = True, )
struct RenderScene #
struct RenderScene(Movable)Everything one render call draws; owned by the caller and only read by the renderer.
Fields:
-
camera: Camera -
lights: List[Light] -
instances: List[RenderInstance] -
joints: List[Mat4] — Skinning matrices (joint world * inverse bind), referenced by instances. -
environment: Environment -
__init____init__(out self, camera: Camera)
struct FrameStats #
struct FrameStats(ImplicitlyCopyable, Movable)Counters of the last render, in Renderer.stats.
Fields:
instances_submitted: Intinstances_drawn: Intdraw_calls: Int — Forward (lit) draws, opaque and blended.prepass_draw_calls: Intshadow_draw_calls: Intlights: Intshadow_views: Intpasses: Inttransient_textures: Int
struct Renderer #
struct Renderer(Movable)The forward+ renderer; render draws a RenderScene into a target view. Owns the pipelines, persistent GPU buffers and textures, the mesh and material registries, the frame graph, the shader library and per-frame scratch (steady-state frames do not allocate). Holds raw pointers to Gfx and Resources, which must outlive it.
Fields:
-
gfx: Pointer[Gfx, MutUntrackedOrigin] -
res: Pointer[Resources, MutUntrackedOrigin] -
lib: NativePtr -
shaders: ShaderManager -
library: ShaderLibrary -
settings: RenderSettings -
defaults: DefaultTextures -
graph: FrameGraph -
stats: FrameStats -
frame_index: Int -
graphviz: String — DOT text of the last compiled frame graph; refreshed bygraph_dot()on demand. -
morph_groups: List[BindGroupHandle] — Object groups binding a morphed mesh's delta buffer, one per morph buffer in use; batches of meshes without morph targets useobject_group. -
morph_group_keys: List[SIMD[DType.int64, 2]] — (index, generation) of the morph buffer each entry ofmorph_groupsbinds. -
post_group_keys: List[SIMD[DType.int64, 4]] — (input index, input generation, bloom index, bloom generation) per cached post group. -
alloc_marks: Bool — Print the Mojo allocation counter after each render phase (needs the counter library). -
gtao_groups: List[BindGroupHandle] -
gtao_group_keys: List[SIMD[DType.int64, 4]] -
meshes: List[GpuMesh] -
materials: List[GpuMaterial] -
material_params: List[Material] -
forward_shader: ShaderHandle -
depth_shader: ShaderHandle -
clusters_shader: ShaderHandle -
sky_shader: ShaderHandle -
ibl_shader: ShaderHandle -
post_shader: ShaderHandle -
gtao_shader: ShaderHandle -
gtao_msaa_shader: ShaderHandle -
frame_layout: BindGroupLayoutHandle -
object_layout: BindGroupLayoutHandle -
material_layout: BindGroupLayoutHandle -
pass_layout: BindGroupLayoutHandle -
cluster_out_layout: BindGroupLayoutHandle -
gtao_layout: BindGroupLayoutHandle -
gtao_msaa_layout: BindGroupLayoutHandle -
ibl_layout: BindGroupLayoutHandle -
post_layout: BindGroupLayoutHandle -
scene_pipeline_layout: PipelineLayoutHandle -
cluster_pipeline_layout: PipelineLayoutHandle -
gtao_pipeline_layout: PipelineLayoutHandle -
gtao_msaa_pipeline_layout: PipelineLayoutHandle -
ibl_pipeline_layout: PipelineLayoutHandle -
post_pipeline_layout: PipelineLayoutHandle -
frame_uniform: BufferHandle -
pass_uniforms: BufferHandle -
post_uniforms: BufferHandle -
lights_buffer: BufferHandle -
shadow_views_buffer: BufferHandle -
cluster_grid: BufferHandle -
cluster_indices: BufferHandle -
instances_buffer: BufferHandle -
instances_capacity: Int -
joints_buffer: BufferHandle -
joints_capacity: Int -
morph_weights_buffer: BufferHandle -
morph_slot_capacity: Int — Morph-weight slots (POST_UNIFORM_STRIDE bytes each) inmorph_weights_buffer. -
empty_morph_buffer: BufferHandle -
cascade_texture: TextureHandle -
cascade_array_view: TextureViewHandle -
cascade_layer_views: List[TextureViewHandle] -
atlas_texture: TextureHandle -
atlas_view: TextureViewHandle -
env_texture: TextureHandle -
env_cube_view: TextureViewHandle -
prefiltered_texture: TextureHandle -
prefiltered_view: TextureViewHandle -
irradiance_texture: TextureHandle -
irradiance_view: TextureViewHandle -
brdf_texture: TextureHandle -
brdf_view: TextureViewHandle -
shadow_sampler: SamplerHandle -
env_sampler: SamplerHandle -
post_sampler: SamplerHandle -
nearest_sampler: SamplerHandle -
env_baked: Bool -
env_key: String -
frame_group: BindGroupHandle -
frame_group_ao_view: TextureViewHandle -
shadow_frame_group: BindGroupHandle -
cluster_frame_group: BindGroupHandle — Frame group variant for the cluster pass: the cluster buffers it writes are replaced by a dummy. -
dummy_cluster_buffer: BufferHandle — Frame group variant for shadow passes: the shadow maps being written are replaced by dummies. -
dummy_depth_array: TextureHandle -
dummy_depth_2d: TextureHandle -
object_group: BindGroupHandle -
object_group_key: String -
pass_group: BindGroupHandle -
cluster_out_group: BindGroupHandle -
pipelines: List[_CachedPipeline] -
sky_pipeline: RenderPipelineHandle -
sky_pipeline_msaa: UInt32 -
cluster_pipeline: ComputePipelineHandle -
gtao_pipeline: ComputePipelineHandle -
gtao_blur_pipeline: ComputePipelineHandle -
gtao_msaa_pipeline: ComputePipelineHandle -
gtao_msaa_blur_pipeline: ComputePipelineHandle -
ibl_env_pipeline: ComputePipelineHandle -
ibl_prefilter_pipeline: ComputePipelineHandle -
ibl_irradiance_pipeline: ComputePipelineHandle -
ibl_brdf_pipeline: ComputePipelineHandle -
bloom_down_pipeline: RenderPipelineHandle -
bloom_up_pipeline: RenderPipelineHandle -
tonemap_pipeline: RenderPipelineHandle -
fxaa_pipeline: RenderPipelineHandle -
blit_pipeline: RenderPipelineHandle -
post_target_format: WGPUTextureFormat -
atlas: ShadowAtlas -
batches: List[_Batch] -
blend_batches: List[_Batch] -
instance_bytes: ByteWriter -
post_groups: List[BindGroupHandle] -
cluster_params: ClusterParams -
__init____init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], lib: NativePtr, settings: RenderSettings, target_format: WGPUTextureFormat, ) raises -
add_meshadd_mesh( mut self, label: String, data: MeshData, lods: Bool = True ) raises -> Int
Uploadsdataand returns its index inmeshes; raises if validation or upload fails. Withlodsand more than 64 triangles, up to four LODs are built with meshoptimizer; otherwise the mesh has one. Always appends (removed slots are only reused byadd_cooked_mesh). -
add_cooked_meshadd_cooked_mesh( mut self, label: String, cooked: CookedMesh ) raises -> Int
Registers a cooked mesh (streams already in GPU layout). Reuses a removed slot if any. -
replace_cooked_meshreplace_cooked_mesh( mut self, index: Int, label: String, cooked: CookedMesh ) raises
Hot reload: swaps the GPU buffers behindindex; instances keep their mesh index. -
remove_meshremove_mesh(mut self, index: Int) raises
Frees the GPU buffers; the slot is reused by the nextadd_cooked_mesh. Instances that still reference it are the caller's error (rendering them raises on the invalid handle). -
add_materialadd_material(mut self, material: Material) raises -> Int
Uploadsmaterialand returns its index inmaterials; raises on a GPU error. A copy of the parameters is kept inmaterial_params; texture views stay the caller's. -
replace_materialreplace_material(mut self, index: Int, material: Material) raises
Hot reload: uploadsmaterialinto slotindex(instances keep their material index) and frees the uniform buffer and bind group of the version it replaces. The new version is uploaded before anything is freed, so a failed upload raises and leaves the slot as it was. Replacing a removed slot makes it live again.material_params[index]becomes a copy ofmaterial. Texture views and samplers are the caller's: the old ones are not destroyed (destroy them after this call if nothing else uses them). Raises whenindexis out of range. -
remove_materialremove_material(mut self, index: Int) raises
Frees the uniform buffer and bind group of slotindexand leaves the slot empty (null handles); removing an empty slot does nothing. Indices stay stable:add_materialalways appends, so a removed slot is never handed out again (onlyreplace_materialon the same index revives it).renderraises if a visible instance still uses a removed material. Texture views and samplers are the caller's, andmaterial_params[index]keeps the last parameters. Raises whenindexis out of range. -
material_is_livematerial_is_live(self, index: Int) -> Bool
True when slotindexexists and has not been removed. -
mesh_lod_countmesh_lod_count(self, mesh: Int) -> Int
Number of LOD index ranges of meshmesh; level 0 is full detail. -
reload_shadersreload_shaders(mut self) raises -> Int
Hot reload: recompiles changed programs and rebuilds every pipeline on success. Pipelines keep their handles; a failed compile keeps the last good module and pipelines. -
renderrender( mut self, scene: RenderScene, target: TextureViewHandle, width: Int, height: Int, ) raises
Renderssceneintotarget(format given at construction). Raises GfxError on device loss. -
graph_dotgraph_dot(mut self) -> String
DOT text of the last frame's graph (built on demand: debug UI, docs). -
releaserelease(mut self) raises
Frees the frame graph's pooled textures (everything else dies with Resources).
cinder.render.shader_library #
Source: cinder/render/shader_library.mojo
Shader programs assembled from WGSL files with #include "file.wgsl" lines, with hot reload. WGSL has no preprocessor, so includes are resolved here (relative to the shader directory, once per program, in order of first appearance). Error locations reported by naga refer to the assembled text; ShaderLibrary maps them back to the file and line they came from before raising.
struct AssembledShader #
struct AssembledShader(Copyable, Movable)WGSL text with every include spliced in, mapped back to its source files. chunks locate each assembled line in its file; files lists every file read (directory/name), in order of first inclusion.
Fields:
-
text: String -
chunks: List[_Chunk] -
files: List[String] -
__init____init__(out self) -
locatelocate(self, assembled_line: Int) -> Tuple[String, Int]
File and file line for a line of the assembled text.
def assemble #
def assemble(directory: String, entry: String) raises -> AssembledShaderReads entry and splices every #include "x" (each file at most once) into one text.
struct ShaderLibrary #
struct ShaderLibrary(Movable)Loads directory/<entry>.wgsl programs with includes; check_for_changes hot reloads.
Fields:
-
gfx: Pointer[Gfx, MutUntrackedOrigin] -
res: Pointer[Resources, MutUntrackedOrigin] -
shaders: Pointer[ShaderManager, MutUntrackedOrigin] -
directory: String -
programs: List[_Program] -
diagnostics: List[LogRecord] -
reloaded: List[ShaderHandle] — Handles whose module changed in the lastcheck_for_changes(pipelines must be rebuilt). -
__init____init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], shaders: Pointer[ShaderManager, MutUntrackedOrigin], directory: String, ) -
compilecompile(mut self, entry: String) raises -> WGPUShaderModule
Assembles and compiles without registering (used for one-off modules and byload). -
loadload(mut self, entry: String) raises -> ShaderHandle
Assembles, compiles and registersentry(a file indirectory) for hot reload. Returns the handleResourcesholds the module under. Raises on a missing include or a compile error, with locations mapped to the source files. -
check_for_changescheck_for_changes(mut self) -> Int
Recompiles programs whose files changed. Returns how many modules were replaced; failures keep the last good module and add an error diagnostic naming file:line:column. -
take_diagnosticstake_diagnostics(mut self) -> List[LogRecord]
Returns and clears the reload records fromcheck_for_changes(info or error).
cinder.render.shadows #
Source: cinder/render/shadows.mojo
Cascaded shadow maps for the sun and an atlas for spot and point lights (CPU-side maths). Cascade fitting: each cascade covers a slice of the camera frustum (practical split scheme, lambda 0.75). The orthographic projection is fitted to the slice's bounding sphere so it has a constant size while the camera rotates, and its origin is snapped to whole shadow texels in light space so edges do not shimmer when the camera moves.
comptime CASCADE_COUNT = 4— Sun shadow cascades; matches the four-entry cascade arrays ofFramein common.wgsl.comptime SHADOW_VIEW_BYTES = 96— Bytes per packedShadowView: the size ofShadowViewin common.wgsl.
struct Cascade #
struct Cascade(ImplicitlyCopyable, Movable)One fitted sun cascade: an orthographic reverse-Z light view-projection (world to clip).
Fields:
view_proj: Mat4split_end: Float32 — View-space distance where this cascade ends.texel_world: Float32 — World-space size of one shadow texel in this cascade.
def cascade_splits_into #
def cascade_splits_into( mut out: Array[Float32, CASCADE_COUNT], near: Float32, shadow_distance: Float32, lambda_: Float32 = 0.75, )End distances of the four cascades (practical split: blend of log and uniform) into a stack array (no allocation).
def cascade_splits #
def cascade_splits( near: Float32, shadow_distance: Float32, lambda_: Float32 = 0.75 ) -> List[Float32]End distances of the four cascades (practical split: blend of log and uniform).
def fit_cascade #
def fit_cascade( camera: Camera, light_dir: Vec3, z0: Float32, z1: Float32, resolution: Int ) -> CascadeOrthographic light view-projection for one slice with texel snapping. light_dir is the direction light travels. Depth range is the sphere diameter plus a generous margin behind the slice so casters outside the camera frustum still cast.
def fit_cascades_into #
def fit_cascades_into( mut out: List[Cascade], camera: Camera, light_dir: Vec3, shadow_distance: Float32, resolution: Int, )fit_cascades into a list supplied by the caller (cleared first, capacity kept).
def fit_cascades #
def fit_cascades( camera: Camera, light_dir: Vec3, shadow_distance: Float32, resolution: Int ) -> List[Cascade]fit_cascades_into into a new list (allocates); per-frame code uses the _into variant.
struct ShadowView #
struct ShadowView(ImplicitlyCopyable, Movable)One atlas tile: a light-space view-projection and its uv rectangle (matches common.wgsl).
Fields:
-
view_proj: Mat4 -
uv_origin_x: Float32 -
uv_origin_y: Float32 -
uv_size: Float32 -
texel_uv: Float32 -
near: Float32 -
far: Float32 -
packpack(self, mut w: ByteWriter)
Appends the 96-byte GPU form (ShadowViewin common.wgsl).view_proj, then the uv rectangle (origin x, origin y, size, size), then the texel size in uv, near, far and a zero pad.
struct ShadowAtlas #
struct ShadowAtlas(Movable)Fixed grid of square tiles in one depth texture. Spot lights take one tile, point lights six.
Fields:
-
size: Int -
tile_size: Int -
tiles_per_row: Int -
next_tile: Int -
views: List[ShadowView] -
__init____init__(out self, size: Int = 4096, tile_size: Int = 512) -
capacitycapacity(self) -> Int
Number of tiles in the atlas (tiles_per_rowsquared). -
resetreset(mut self)
Frees every tile and clearsviews(capacity kept); called at the start of each frame. -
tile_pixel_origintile_pixel_origin(self, view_index: Int) -> Tuple[Int, Int]
Top-left pixel of the tile used by shadow viewview_index(views fill tiles in order). -
allocateallocate(mut self, light: Light) -> Int
Adds the light's shadow views; returns the first view index or -1 when the atlas is full. -
pack_views_intopack_views_into(self, mut w: ByteWriter)
Appends the GPU shadow-view array (at least one zeroed entry) tow. -
pack_viewspack_views(self) -> List[UInt8]
pack_views_intointo a new byte list (allocates; at least one zeroed entry).
cinder.render.textures #
Source: cinder/render/textures.mojo
GPU textures from CPU images: CPU box-filter mip chains, sRGB or linear formats, defaults.
struct GpuTexture #
struct GpuTexture(ImplicitlyCopyable, Movable)An uploaded 2D texture and a view of all its mips; width and height are mip 0 in pixels. The texture and view are owned by whoever uploaded them and freed through Resources.
Fields:
texture: TextureHandleview: TextureViewHandlewidth: UInt32height: UInt32mip_levels: UInt32
def mip_count #
def mip_count(width: Int, height: Int) -> IntLevels in a full mip chain down to 1x1 (floor(log2(max(width, height))) + 1).
def downsample #
def downsample(img: Image) -> Image2x2 box filter (edge pixels of odd sizes are clamped), in the image's own encoding.
def upload_image #
def upload_image( mut res: Resources, label: String, img: Image, srgb: Bool, mips: Bool = True ) raises -> GpuTextureUploads an RGBA8 image as a 2D texture with a CPU box-filtered mip chain. srgb selects RGBA8UnormSrgb (colour data) over RGBA8Unorm (linear data); mips = False uploads level 0 only. The caller owns the result; raises on a GPU error.
def solid_image #
def solid_image(r: UInt8, g: UInt8, b: UInt8, a: UInt8) -> Image1x1 RGBA8 image of one colour (used for the default textures).
struct DefaultTextures #
struct DefaultTextures(ImplicitlyCopyable, Movable)1x1 fallbacks: white (base colour, occlusion, metallic-roughness), flat normal, black emissive.
Fields:
-
white: GpuTexture -
normal: GpuTexture -
black: GpuTexture -
sampler_linear_repeat: SamplerHandle -
sampler_nearest_clamp: SamplerHandle -
__init____init__(out self, mut res: Resources) raises