DocsAPI reference

cinder.core

cinder.core #

Source: cinder/core/__init__.mojo

Core: math, logging, assertions, allocators, containers, handles, time, file system. No I/O to GPU, audio or window systems lives here.

cinder.core.arena #

Source: cinder/core/arena.mojo

LinearArena: a bump allocator for per-frame scratch memory. One block is allocated up front; alloc_bytes hands out aligned sub-ranges in O(1) and reset returns everything at once. There is no per-allocation free. After warm-up the engine's frame loop allocates only from arenas and pools, which is what makes the zero-allocation steady-state test possible.

struct LinearArena #

mojo
struct LinearArena(Movable)

Bump allocator over one fixed block. Every address returned is aligned as requested.

Fields:

  • __init__ __init__(out self, capacity: Int) raises
    Allocates capacity bytes once. Raises on a non-positive capacity.
  • capacity capacity(self) -> Int
    Usable bytes requested at construction; the 64-byte alignment slack is not counted.
  • used used(self) -> Int
    Bytes handed out since the last reset, including alignment padding.
  • high_water high_water(self) -> Int
    Largest used() ever observed; the number to size the arena by.
  • alloc_bytes alloc_bytes( mut self, size: Int, alignment: Int = 16 ) raises -> Pointer[UInt8, MutUntrackedOrigin]
    Returns size bytes aligned to alignment (a power of two <= 64). Raises when exhausted.
  • alloc alloc[ T: AnyType ](mut self, count: Int = 1) raises -> Pointer[T, MutUntrackedOrigin]
    Uninitialised storage for count values of T, aligned for T. The caller initialises it.
  • reset reset(mut self)
    Makes the whole block available again. Previously returned pointers become dangling.

cinder.core.assertions #

Source: cinder/core/assertions.mojo

Engine assertions: broken invariants abort with an explicit message. cinder_assert is compiled in unless the build defines CINDER_ASSERTS=0 (mojo build -D CINDER_ASSERTS=0), which release builds of the shipped game set and nothing else does. cinder_check is never compiled out and is used where the cost is negligible and the invariant guards memory safety (handle validation, FFI preconditions). Both abort rather than raise: an invariant violation is a bug, not a recoverable condition (section 4 of the brief).

  • comptime ASSERTS_ENABLED: Bool = get_defined_bool["CINDER_ASSERTS", True]() — Whether cinder_assert is compiled in. Read from -D CINDER_ASSERTS at build time.

def cinder_assert #

mojo
def cinder_assert(condition: Bool, message: StaticString)

Aborts with message when condition is false, unless asserts are compiled out.

def cinder_check #

mojo
def cinder_check(condition: Bool, message: StaticString)

Aborts with message when condition is false, in every build.

cinder.core.base64 #

Source: cinder/core/base64.mojo

Base64 decoding (RFC 4648, with or without padding) for data URIs in glTF files.

def decode_base64 #

mojo
def decode_base64(text: StringSpan) raises -> List[UInt8]

Decodes base64 text; whitespace is skipped and '=' padding is optional.

cinder.core.boxed #

Source: cinder/core/boxed.mojo

Boxed: a value at a stable heap address. Subsystems that hand out Pointers to themselves (Gfx, Resources, Renderer, JobSystem, ...) must never move once those pointers exist. Boxed[T] owns one T on the heap so the owner can move freely while the pointee stays put.

struct Boxed #

mojo
struct Boxed[T: Deinitable & Movable](Movable)

Owns one T on the heap so pointers to it stay valid while the box itself moves. The value is moved in at construction and destroyed, then freed, when the box is destroyed. ptr() and [] give untracked mutable access; neither may outlive the box.

Fields:

  • __init__ __init__(out self, var value: Self.T)
  • ptr ptr(self) -> Pointer[Self.T, MutUntrackedOrigin]
    A raw pointer valid for the box's lifetime.
  • __getitem__ __getitem__(ref self) -> ref[MutUntrackedOrigin] Self.T

cinder.core.bytes #

Source: cinder/core/bytes.mojo

Little-endian byte packing for GPU uploads (std140/std430-style layouts are the caller's job).

struct ByteWriter #

mojo
struct ByteWriter(Movable, Sized)

Appends plain values to a byte buffer. pad_to aligns the length for struct rules.

Fields:

  • bytes: List[UInt8]

  • __init__ __init__(out self, capacity: Int = 256)

  • __len__ __len__(self) -> Int

  • clear clear(mut self)
    Empties the buffer, keeping its capacity (same as reset).

  • u8 u8(mut self, v: UInt8)
    Appends one byte.

  • u32 u32(mut self, v: UInt32)
    Appends v as four bytes, little-endian.

  • i32 i32(mut self, v: Int32)
    Appends v as four bytes, little-endian two's complement.

  • f32 f32(mut self, v: Float32)
    Appends the IEEE-754 bits of v as four bytes, little-endian.

  • vec2 vec2(mut self, v: Vec2)
    Appends x then y as two f32 (8 bytes, no padding).

  • vec3 vec3(mut self, v: Vec3)
    Three floats, no padding (callers add f32 or pad_to(16) for vec4 alignment).

  • vec3_padded vec3_padded(mut self, v: Vec3, w: Float32 = 0.0)
    Appends x, y, z, then w (default 0) as four f32: a vec3 in a 16-byte vec4 slot.

  • vec4 vec4(mut self, v: Vec4)
    Appends x, y, z, w as four f32 (16 bytes).

  • mat4 mat4(mut self, m: Mat4)
    Column-major, matching WGSL mat4x4<f32>.

  • pad_to pad_to(mut self, alignment: Int)
    Zero-fills to the next multiple of alignment; no-op when already aligned. alignment must be positive: 0 or a negative value is a bug and cinder_assert aborts. With assertions compiled out it leaves the bytes unchanged.

  • span span(self) -> Span[UInt8, origin_of(self.bytes)]
    A view of the bytes written so far, borrowed from the writer; invalid after it changes.

  • reset reset(mut self)
    Empties the writer, keeping its capacity (frame scratch buffers).

  • take take(deinit self) -> List[UInt8]
    Consumes the writer and returns its bytes.

cinder.core.cstring #

Source: cinder/core/cstring.mojo

C strings to Mojo Strings, decoding the bytes as UTF-8. C APIs (SDL, the shim, stb) hand back NUL-terminated UTF-8. Converting byte by byte with chr treats every byte as a code point and double-encodes anything outside ASCII, so every conversion goes through string_from_c instead.

def string_from_c #

mojo
def string_from_c( p: Optional[Pointer[c_char, MutUntrackedOrigin]], max_len: Int = -1 ) -> String

The UTF-8 text at p up to its NUL terminator, or up to max_len bytes when max_len is not negative (fixed-size C buffers). A null pointer gives the empty string. Invalid UTF-8 sequences become U+FFFD instead of failing: C text is diagnostic data here and must never make the caller raise. The caller guarantees p points at a terminated string or at least max_len readable bytes.

cinder.core.fs #

Source: cinder/core/fs.mojo

Filesystem helpers with typed errors. Thin on purpose: the asset database layers on top.

struct FileError #

mojo
struct FileError(Copyable, Movable, Writable)

A failed file operation, with the path and the underlying message.

Fields:

  • path: String

  • message: String

  • write_to write_to(self, mut writer: Some[Writer])

def file_exists #

mojo
def file_exists(path: String) -> Bool

True when something exists at path (a file or a directory). Never raises.

def read_bytes #

mojo
def read_bytes(path: String) raises FileError -> List[UInt8]

Reads a whole file into memory.

def read_text #

mojo
def read_text(path: String) raises FileError -> String

Reads a whole UTF-8 text file.

def write_bytes #

mojo
def write_bytes(path: String, data: Span[UInt8, _]) raises FileError

Writes (truncating) data to path.

def write_text #

mojo
def write_text(path: String, text: String) raises FileError

Writes (truncating) text to path. Raises FileError carrying the path on failure.

cinder.core.handles #

Source: cinder/core/handles.mojo

Generational handles and the slot pool that issues them. A Handle[Tag] names a slot in a Pool[T, Tag]. The generation counter makes a stale handle (one whose slot was freed and reused) fail validation instead of aliasing a new object. Tag is a phantom type so that, for example, a texture handle cannot be passed where a buffer handle is expected.

struct Handle #

mojo
struct Handle[Tag: AnyType]( Boolable, Equatable, Hashable, ImplicitlyCopyable, RegisterPassable, Writable, )

A 64-bit generational handle: 32-bit slot index, 32-bit generation. Zero is the null handle.

Fields:

  • index: UInt32

  • generation: UInt32

  • null null() -> Handle[Self.Tag]
    The null handle (index 0, generation 0); never valid in any Pool.

  • is_null is_null(self) -> Bool
    True when the generation is 0, whatever the index; pools never issue generation 0.

  • packed packed(self) -> UInt64
    The handle as one integer, for serialization and GPU-side ids.

  • from_packed from_packed(v: UInt64) -> Handle[Self.Tag]
    Inverse of packed(): low 32 bits are the index, high 32 bits the generation.

  • write_to write_to(self, mut writer: Some[Writer])

struct Pool #

mojo
struct Pool[T: Copyable & Deinitable, Tag: AnyType](Movable)

A slot map: O(1) insert, remove and lookup by Handle[Self.Tag], with generation checking. Slots are never reallocated once created, so a Pool holds at most capacity live values; insert raises when full, which keeps steady-state frames free of allocation (section 4 of the brief). Freed slots are recycled through an intrusive free list. Generations start at 1 and skip 0 so the zero handle is never valid.

Fields:

  • __init__ __init__(out self, capacity: Int) raises
    Creates a pool with room for capacity values (1 <= capacity < 2^32).
  • capacity capacity(self) -> Int
    Number of slots, fixed at construction; the pool never grows past it.
  • live live(self) -> Int
    Number of values currently stored.
  • insert insert(mut self, var value: Self.T) raises -> Handle[Self.Tag]
    Stores value and returns its handle. Raises when the pool is full.
  • is_valid is_valid(self, h: Handle[Self.Tag]) -> Bool
    True when h names a live value (right slot, current generation).
  • get get(self, h: Handle[Self.Tag]) raises -> Self.T
    A copy of the value behind h; raises on a null, stale or foreign handle. Pooled values are small records (handles, descriptors), so copying is the intended access pattern; update writes a value back.
  • update update(mut self, h: Handle[Self.Tag], var value: Self.T) raises
    Replaces the value behind h; raises on an invalid handle.
  • remove remove(mut self, h: Handle[Self.Tag]) raises -> Self.T
    Removes and returns the value behind h, retiring the handle's generation.
  • handles handles(self) -> List[Handle[Self.Tag]]
    Handles of every live value, in slot order (allocates; for tools and tests).

cinder.core.hash #

Source: cinder/core/hash.mojo

Content hashing: 64-bit FNV-1a over bytes and strings (stable across runs and platforms).

  • comptime FNV_OFFSET: UInt64 = 14695981039346656037 — FNV-1a 64-bit offset basis; also the hash of empty input.
  • comptime FNV_PRIME: UInt64 = 1099511628211 — FNV-1a 64-bit prime.

def fnv1a64 #

mojo
def fnv1a64(data: Span[UInt8, _]) -> UInt64

64-bit FNV-1a of data. Stable across runs and platforms, which is why the asset cooker uses it as a content hash. Not cryptographic: do not use it where collisions could be engineered.

def fnv1a64_string #

mojo
def fnv1a64_string(text: String) -> UInt64

fnv1a64 of the string's UTF-8 bytes, without a terminator.

def hex64 #

mojo
def hex64(v: UInt64) -> String

Sixteen lowercase hex digits.

cinder.core.image #

Source: cinder/core/image.mojo

CPU images: RGBA8 load/decode/save through the shim (stb) and perceptual comparison (SSIM). Image owns its pixels in a List[UInt8] (tightly packed RGBA8). ssim is the structural similarity index on luma with an 8x8 box window; golden-image tests compare against it.

struct Image #

mojo
struct Image(Copyable, Movable)

A tightly packed RGBA8 image.

Fields:

  • width: Int

  • height: Int

  • pixels: List[UInt8]

  • __init__ __init__(out self, width: Int, height: Int)

  • __init__ __init__( out self, width: Int, height: Int, var pixels: List[UInt8] ) raises

  • from_rows from_rows( width: Int, height: Int, rows: List[UInt8], stride: Int ) raises -> Image
    Repacks rows of stride bytes (GPU readback with 256-byte alignment) into a tight image.

  • pixel pixel(self, x: Int, y: Int) -> SIMD[DType.uint8, 4]
    The RGBA8 value at column x, row y; (0, 0) is the first pixel in memory. x must be in [0, width) and y in [0, height). Anything else is a bug and aborts through cinder_check in every build (an unchecked index would read outside the pixel list, or silently wrap into the next row).

  • set_pixel set_pixel(mut self, x: Int, y: Int, value: SIMD[DType.uint8, 4])
    Writes RGBA8 value at column x, row y; addressed and range-checked like pixel.

  • swap_rb swap_rb(mut self)
    BGRA <-> RGBA in place (swapchain readbacks are BGRA).

  • luma luma(self, x: Int, y: Int) -> Float64
    Rec. 709 luma of the pixel at (x, y) in [0, 255], from the stored (gamma-encoded) values; alpha is ignored. compare computes SSIM on this value.

def load_image #

mojo
def load_image(lib: NativePtr, path: String) raises -> Image

Loads the image file at path through the shim's stb_image and returns it as RGBA8. Any channel count in the file is expanded to four. lib must point at the loaded native shim. Raises with stb's failure reason when the file cannot be read or decoded. The stb buffer is copied into the Image and freed before returning.

def decode_image #

mojo
def decode_image(lib: NativePtr, bytes: Span[UInt8, _]) raises -> Image

Decodes an encoded image held in memory (stb_image) to RGBA8. Raises with stb's failure reason on undecodable data; the shim also rejects inputs larger than 2^31 - 1 bytes. The stb buffer is copied into the Image and freed before returning.

def save_png #

mojo
def save_png(lib: NativePtr, path: String, img: Image) raises

Writes img to path as an RGBA8 PNG through the shim's stb_image_write. Raises when the file cannot be written; the shim also refuses an image with zero width or height.

struct ImageDiff #

mojo
struct ImageDiff(ImplicitlyCopyable, Movable, Writable)

Result of compare: SSIM on luma, plus the raw pixel statistics tests may also bound.

Fields:

  • ssim: Float64

  • max_channel_delta: Int

  • differing_pixels: Int — Pixels where any channel differs by more than tolerance passed to compare.

  • write_to write_to(self, mut w: Some[Writer])

def compare #

mojo
def compare( a: Image, b: Image, tolerance: Int = 2, window: Int = 8 ) raises -> ImageDiff

SSIM (luma, non-overlapping window boxes, K1 = 0.01, K2 = 0.03, L = 255) and pixel deltas.

cinder.core.jobs #

Source: cinder/core/jobs.mojo

The job system: a work-stealing thread pool with dependency counters. Design (ADR-0008, docs/THREADING.md): - One worker per logical core minus one (the main thread is the remaining core), each with a fixed-capacity Chase-Lev deque of job indices. A worker pushes and pops at the bottom of its own deque; idle workers steal from the top of others'. - Jobs are JobFn thin function pointers plus an untyped data pointer; the caller owns the data for the job's lifetime. Each job decrements a Counter when it finishes; wait on a counter helps run jobs until it reaches zero, so waiting never idles a core. - OS threads and semaphores come from the native shim (cn_thread_*, cn_semaphore_*); worker entry points are Mojo abi("C") functions (verified by spike 4). Everything else is Mojo with Atomic operations. - No allocation after construction: the job table, deques and counters are fixed-capacity. submit returns False when the system is saturated; callers then run the job inline.

  • comptime JobFn = def(Pointer[NoneType, MutUntrackedOrigin]) thin -> None — Signature of a job: receives the untyped data pointer it was submitted with.
  • comptime INVALID_JOB: Int32 = -1 — Job index meaning "no job": Deque.pop and Deque.steal return it when nothing was taken.
  • comptime MAX_WORKERS = 64 — Largest worker_count that JobSystem accepts.

struct Counter #

mojo
struct Counter(Movable)

Completion counter: incremented per submitted job, decremented when each finishes.

Fields:

  • remaining: Atomic[Int32]

  • __init__ __init__(out self)

  • load load(self) -> Int32
    Jobs still outstanding on this counter (acquire load); 0 when all have finished.

struct Job #

mojo
struct Job(ImplicitlyCopyable, Movable)

One unit of work in the job table.

Fields:

  • run: JobFn

  • data: Pointer[NoneType, MutUntrackedOrigin]

  • counter: Optional[Pointer[Counter, MutUntrackedOrigin]]

  • __init__ __init__( out self, run: JobFn, data: Pointer[NoneType, MutUntrackedOrigin], counter: Optional[Pointer[Counter, MutUntrackedOrigin]], )

struct Deque #

mojo
struct Deque(Movable)

Chase-Lev work-stealing deque of job indices with a fixed power-of-two capacity. The owner thread calls push and pop; any thread calls steal. Indices wrap through the mask; push fails when the deque is full instead of growing.

Fields:

  • __init__ __init__(out self, capacity: Int)
  • push push(mut self, job: Int32) -> Bool
    Owner only. Returns False when full.
  • pop pop(mut self) -> Int32
    Owner only. Returns INVALID_JOB when empty.
  • steal steal(mut self) -> Int32
    Any thread. Returns INVALID_JOB when empty or when it lost a race (caller retries elsewhere).

struct Shared #

mojo
struct Shared(Movable)

State shared by all workers; lives at a stable heap address for the system's lifetime.

Fields:

  • jobs: Allocation[Job]

  • job_capacity: Int

  • next_job: Atomic[Int64]

  • deques: List[Deque]

  • worker_count: Int

  • stop: Atomic[Int32]

  • idle: Atomic[Int32]

  • semaphore: Optional[Pointer[CnSemaphore, MutUntrackedOrigin]]

  • executed: Atomic[Int64]

  • lib: Pointer[CinderNative, MutUntrackedOrigin]

  • __init__ __init__( out self, worker_count: Int, job_capacity: Int, semaphore: Optional[Pointer[CnSemaphore, MutUntrackedOrigin]], lib: Pointer[CinderNative, MutUntrackedOrigin], )

struct WorkerContext #

mojo
struct WorkerContext(Movable)

Passed to each worker thread entry: which deque is mine and where the shared state lives.

Fields:

  • shared: Pointer[Shared, MutUntrackedOrigin]

  • index: Int

  • __init__ __init__( out self, shared: Pointer[Shared, MutUntrackedOrigin], index: Int )

struct JobSystem #

mojo
struct JobSystem(Movable)

Owns the worker threads, the shared state and the job table. Deque 0 belongs to the main thread. Hold one instance for the life of the program (section 4: the job system runs everything that is not SDL or presentation). __deinit__ stops and joins every worker.

Fields:

  • __init__ __init__( out self, lib: Pointer[CinderNative, MutUntrackedOrigin], worker_count: Int, job_capacity: Int = 1 << 14, ) raises
    Starts worker_count workers (0 runs everything on the calling thread). job_capacity must be a power of two; it bounds the number of jobs in flight between reset calls.
  • worker_count worker_count(self) -> Int
    Number of worker threads started, not counting the main thread (which owns deque 0).
  • executed executed(self) -> Int
    Jobs completed since construction (diagnostics).
  • submit submit( mut self, run: JobFn, data: Pointer[NoneType, MutUntrackedOrigin], counter: Optional[Pointer[Counter, MutUntrackedOrigin]], ) -> Bool
    Queues a job from the main thread. Increments counter before the job can run. Returns False when the job table or the main deque is full; the caller must then run it inline.
  • wait wait(mut self, counter: Pointer[Counter, MutUntrackedOrigin])
    Runs jobs on the calling thread until counter reaches zero.
  • reset reset(mut self)
    Recycles the job table. Only valid when no job is queued or running (e.g. at frame start).

cinder.core.log #

Source: cinder/core/log.mojo

Structured, levelled logging. A Logger owns a minimum level and a sink. Records carry a level, a category and a message plus optional key=value fields, and are formatted as one line: [LEVEL] category: message key=value .... Sinks are a trait so tests capture records in memory and the engine routes them to stderr, a file or the debug console. Logging never raises; a failing sink drops the record.

struct LogLevel #

mojo
struct LogLevel( Comparable, Equatable, ImplicitlyCopyable, RegisterPassable, Writable )

Severity, ordered. Compare with the usual operators.

Fields:

  • value: UInt8

  • trace trace() -> LogLevel
    Severity 0, the lowest: a logger at this level passes everything.

  • debug debug() -> LogLevel
    Severity 1, between trace and info.

  • info info() -> LogLevel
    Severity 2; the default Logger minimum level.

  • warn warn() -> LogLevel
    Severity 3, between info and error.

  • error error() -> LogLevel
    Severity 4, the highest named level.

  • name name(self) -> StaticString
    Label printed in records: TRACE, DEBUG, INFO, WARN, or ERROR for 4 and above.

  • write_to write_to(self, mut writer: Some[Writer])

struct LogField #

mojo
struct LogField(Copyable, Movable, Writable)

One key=value pair attached to a record.

Fields:

  • key: String

  • value: String

  • write_to write_to(self, mut writer: Some[Writer])

struct LogRecord #

mojo
struct LogRecord(Copyable, Movable, Writable)

A formatted log entry.

Fields:

  • level: LogLevel

  • category: String

  • message: String

  • fields: List[LogField]

  • __init__ __init__( out self, level: LogLevel, category: String, message: String, var fields: List[LogField], )

  • write_to write_to(self, mut writer: Some[Writer])

trait LogSink #

mojo
trait LogSink

Receives formatted records. Implementations must not raise.

  • emit emit(mut self, record: LogRecord)
    Handles one record that passed the filter. Must not raise: drop the record instead.

struct PrintSink #

mojo
struct PrintSink(LogSink, Movable)

Writes each record as one line to standard output.

Fields:

  • emitted: Int — Number of records written so far.

  • __init__ __init__(out self)

  • emit emit(mut self, record: LogRecord)
    Prints the record as one line to standard output and increments emitted.

struct MemorySink #

mojo
struct MemorySink(LogSink, Movable)

Keeps every record; used by tests and the in-engine console.

Fields:

  • records: List[LogRecord]

  • __init__ __init__(out self)

  • emit emit(mut self, record: LogRecord)
    Appends a copy of the record to records, which grows without bound.

struct Logger #

mojo
struct Logger[S: LogSink & Movable & Deinitable](Movable)

Filters by minimum level and forwards to its sink.

Fields:

  • min_level: LogLevel

  • sink: Self.S

  • __init__ __init__( out self, var sink: Self.S, min_level: LogLevel = LogLevel.info() )

  • enabled enabled(self, level: LogLevel) -> Bool
    True when level >= min_level; check it before building costly messages.

  • log log( mut self, level: LogLevel, category: String, message: String, var fields: List[LogField], )
    Emits a record when level passes the filter. Field lists are consumed.

  • log log(mut self, level: LogLevel, category: String, message: String)
    Emits a record without fields when level passes the filter.

  • trace trace(mut self, category: String, message: String)
    Logs message under category at TRACE, subject to min_level.

  • debug debug(mut self, category: String, message: String)
    Logs message under category at DEBUG, subject to min_level.

  • info info(mut self, category: String, message: String)
    Logs message under category at INFO, subject to min_level.

  • warn warn(mut self, category: String, message: String)
    Logs message under category at WARN, subject to min_level.

  • error error(mut self, category: String, message: String)
    Logs message under category at ERROR, subject to min_level.

cinder.core.math #

Source: cinder/core/math/__init__.mojo

SIMD math for the engine. Conventions (docs/ARCHITECTURE.md, fixed): right-handed, Y up, -Z forward, metres, column-major matrices, reverse-Z depth with an infinite far plane. All types are plain 32-bit float values backed by SIMD[DType.float32, 4]; a Vec3 keeps its fourth lane at zero.

cinder.core.math.mat4 #

Source: cinder/core/math/mat4.mojo

Mat4: a column-major 4x4 float matrix, as WGSL and glTF store it.

struct Mat4 #

mojo
struct Mat4(Equatable, ImplicitlyCopyable, RegisterPassable, Writable)

Column-major 4x4 matrix. c0..c3 are the columns; element (row r, column c) is cC[r].

Fields:

  • c0: F4

  • c1: F4

  • c2: F4

  • c3: F4

  • identity identity() -> Mat4
    The identity matrix.

  • zero zero() -> Mat4
    All sixteen elements zero; also what inverse returns for a singular matrix.

  • from_rows from_rows(r0: Vec4, r1: Vec4, r2: Vec4, r3: Vec4) -> Mat4
    Builds a matrix from its rows (handy for literals written the way maths books print them).

  • translation translation(t: Vec3) -> Mat4
    Translation by t (metres): the identity with t in the last column.

  • scaling scaling(s: Vec3) -> Mat4
    Scale by s.x(), s.y(), s.z() along the x, y and z axes.

  • rotation rotation(q: Quat) -> Mat4
    Rotation matrix of a unit quaternion.

  • trs trs(t: Vec3, r: Quat, s: Vec3) -> Mat4
    Translation * Rotation * Scale, the glTF node transform order.

  • look_at look_at(eye: Vec3, target: Vec3, up: Vec3) -> Mat4
    Right-handed view matrix: the camera looks down -Z, with +Y up and +X right.

  • perspective_reverse_z_infinite perspective_reverse_z_infinite( fov_y: Float32, aspect: Float32, near: Float32 ) -> Mat4
    Right-handed perspective projection, reverse-Z (near maps to 1) with an infinite far plane, clip depth in [0, 1] as wgpu expects.

  • orthographic_reverse_z orthographic_reverse_z( left: Float32, right: Float32, bottom: Float32, top: Float32, near: Float32, far: Float32, ) -> Mat4
    Right-handed orthographic projection with reverse-Z (near maps to 1, far to 0).

  • column column(self, c: Int) -> Vec4
    Column c (0 to 3) as a Vec4. Any other index is a bug: cinder_assert aborts. With assertions compiled out (-D CINDER_ASSERTS=0) the index wraps to c & 3.

  • __getitem__ __getitem__(self, row: Int, col: Int) -> Float32
    Element (row, col); both indices are checked like row and column.

  • row row(self, r: Int) -> Vec4
    Row r (0 to 3), gathered from the four columns. Any other index is a bug: cinder_assert aborts. With assertions compiled out the index wraps to r & 3.

  • to_quat to_quat(self) -> Quat
    Rotation of an orthonormal (possibly scaled) matrix's upper 3x3 (Shepperd's method, choosing the largest diagonal term for stability). Scale is removed first.

  • transform_point transform_point(self, p: Vec3) -> Vec3
    Applies the affine part to a point (w = 1), ignoring any projective row.

  • transform_vector transform_vector(self, v: Vec3) -> Vec3
    Applies the linear part to a direction (w = 0).

  • project_point project_point(self, p: Vec3) -> Vec3
    Full projective transform with perspective divide.

  • transpose transpose(self) -> Mat4
    Rows and columns swapped; for a pure rotation this is also its inverse.

  • determinant determinant(self) -> Float32
    Determinant of the full 4x4 matrix, by expansion in 2x2 minors. Exactly 0 is what makes inverse return the zero matrix; a negative value means the matrix mirrors space (Transform.from_mat4 relies on this).

  • inverse inverse(self) -> Mat4
    General inverse by cofactors. A singular matrix returns the zero matrix; callers that need to detect that check determinant() first.

  • approx_equal approx_equal(self, o: Mat4, eps: Float32 = EPSILON) -> Bool
    True when every element matches within eps under approx_eq.

  • write_to write_to(self, mut writer: Some[Writer])

cinder.core.math.quat #

Source: cinder/core/math/quat.mojo

Quat: a unit quaternion (x, y, z, w) representing a rotation.

struct Quat #

mojo
struct Quat(Equatable, ImplicitlyCopyable, RegisterPassable, Writable)

Rotation quaternion. Lanes are (x, y, z, w); w is the scalar part.

Fields:

  • data: F4

  • __init__ __init__(out self, x: Float32, y: Float32, z: Float32, w: Float32)

  • identity identity() -> Quat
    The no-rotation quaternion (0, 0, 0, 1).

  • from_axis_angle from_axis_angle(axis: Vec3, angle: Float32) -> Quat
    Rotation of angle radians about the unit vector axis (right-handed).

  • from_euler from_euler(yaw: Float32, pitch: Float32, roll: Float32) -> Quat
    Yaw about +Y, then pitch about +X, then roll about +Z (applied in that order to a body).

  • x x(self) -> Float32
    Lane 0, the x term of the vector part.

  • y y(self) -> Float32
    Lane 1, the y term of the vector part.

  • z z(self) -> Float32
    Lane 2, the z term of the vector part.

  • w w(self) -> Float32
    Lane 3, the scalar part: cos(angle / 2) for a unit quaternion.

  • vector_part vector_part(self) -> Vec3
    (x, y, z) as a Vec3: the rotation axis times sin(angle / 2) for a unit quaternion.

  • dot dot(self, o: Quat) -> Float32
    4D dot product; negative when the two are on opposite hemispheres (see slerp).

  • length length(self) -> Float32
    Euclidean norm over all four lanes; 1 for a unit quaternion.

  • normalized normalized(self) -> Quat
    Unit quaternion; the zero quaternion becomes the identity.

  • conjugate conjugate(self) -> Quat
    The vector part negated; for a unit quaternion this is the inverse rotation.

  • inverse inverse(self) -> Quat
    Inverse for any non-zero quaternion (equals the conjugate for unit quaternions). The zero quaternion has no inverse and returns the zero quaternion, as Mat4.inverse returns the zero matrix for a singular matrix.

  • rotate rotate(self, v: Vec3) -> Vec3
    Rotates v by this unit quaternion: v' = v + 2w(u x v) + 2u x (u x v).

  • nlerp nlerp(self, o: Quat, t: Float32) -> Quat
    Normalised linear interpolation along the shorter arc.

  • slerp slerp(self, o: Quat, t: Float32) -> Quat
    Spherical interpolation along the shorter arc; falls back to nlerp when nearly parallel.

  • approx_equal approx_equal(self, o: Quat, eps: Float32 = EPSILON) -> Bool
    True when both represent the same rotation (q and -q are equal rotations).

  • write_to write_to(self, mut writer: Some[Writer])

cinder.core.math.scalar #

Source: cinder/core/math/scalar.mojo

Scalar helpers shared by the vector, quaternion and matrix types.

  • comptime EPSILON: Float32 = 1e-6 — Default tolerance for approximate comparisons of unit-scale quantities.

def radians #

mojo
def radians(deg: Float32) -> Float32

Converts degrees to radians.

def degrees #

mojo
def degrees(rad: Float32) -> Float32

Converts radians to degrees.

def clamp #

mojo
def clamp(x: Float32, lo: Float32, hi: Float32) -> Float32

Clamps x to [lo, hi].

def saturate #

mojo
def saturate(x: Float32) -> Float32

Clamps x to [0, 1].

def lerp #

mojo
def lerp(a: Float32, b: Float32, t: Float32) -> Float32

Linear interpolation; exact at t = 0 and t = 1.

def approx_eq #

mojo
def approx_eq(a: Float32, b: Float32, eps: Float32 = EPSILON) -> Bool

True when |a - b| <= eps * max(1, |a|, |b|): absolute near zero, relative elsewhere.

cinder.core.math.transform #

Source: cinder/core/math/transform.mojo

Transform: translation, rotation and scale as separate terms (poses, animation keys).

struct Transform #

mojo
struct Transform(Equatable, ImplicitlyCopyable, Movable, Writable)

A TRS transform. Composition order is scale, then rotation, then translation.

Fields:

  • translation: Vec3

  • rotation: Quat

  • scale: Vec3

  • identity identity() -> Transform
    Zero translation, identity rotation and unit scale.

  • from_mat4 from_mat4(m: Mat4) -> Transform
    Decomposes a matrix without shear: translation from the last column, scale from the column lengths, rotation from the normalised columns. A mirrored matrix (negative determinant) has no proper rotation; the reflection is folded into a negative x scale and the rotation is taken from the matrix with its first column negated, so to_mat4() rebuilds the input.

  • to_mat4 to_mat4(self) -> Mat4
    The equivalent matrix, Mat4.trs: scale, then rotation, then translation.

  • lerp lerp(self, o: Transform, t: Float32) -> Transform
    Linear translation and scale, normalised-lerp rotation along the shorter arc.

  • approx_equal approx_equal(self, o: Transform, eps: Float32 = EPSILON) -> Bool
    True when translation, rotation and scale match within eps (q and -q are equal).

  • write_to write_to(self, mut writer: Some[Writer])

cinder.core.math.vec #

Source: cinder/core/math/vec.mojo

Vec2, Vec3 and Vec4: 32-bit float vectors backed by SIMD registers.

  • comptime F2 = SIMD[DType.float32, 2] — Storage of Vec2: two float32 lanes.
  • comptime F4 = SIMD[DType.float32, 4] — Storage of Vec3, Vec4, Quat and each Mat4 column: four float32 lanes.

struct Vec2 #

mojo
struct Vec2(Equatable, ImplicitlyCopyable, RegisterPassable, Writable)

A 2D vector.

Fields:

  • data: F2

  • __init__ __init__(out self, x: Float32, y: Float32)

  • zero zero() -> Vec2
    (0, 0).

  • x x(self) -> Float32
    Lane 0.

  • y y(self) -> Float32
    Lane 1.

  • dot dot(self, o: Vec2) -> Float32
    Sum of the component-wise products.

  • length_squared length_squared(self) -> Float32
    Squared Euclidean length; avoids the square root when only comparing lengths.

  • length length(self) -> Float32
    Euclidean length.

  • approx_equal approx_equal(self, o: Vec2, eps: Float32 = EPSILON) -> Bool
    True when x and y each match within eps under approx_eq.

  • write_to write_to(self, mut writer: Some[Writer])

struct Vec3 #

mojo
struct Vec3(Equatable, ImplicitlyCopyable, RegisterPassable, Writable)

A 3D vector. The fourth SIMD lane is always zero so 4-wide operations stay exact.

Fields:

  • data: F4

  • __init__ __init__(out self, x: Float32, y: Float32, z: Float32)

  • zero zero() -> Vec3
    (0, 0, 0).

  • one one() -> Vec3
    (1, 1, 1), the unit scale.

  • unit_x unit_x() -> Vec3
    +X, the engine's right axis.

  • unit_y unit_y() -> Vec3
    +Y, the engine's up axis (right-handed, Y-up).

  • unit_z unit_z() -> Vec3
    +Z, pointing backwards: the engine's forward is -Z.

  • forward forward() -> Vec3
    The engine's forward direction, -Z.

  • splat splat(v: Float32) -> Vec3
    All three components set to v (the fourth lane stays zero).

  • x x(self) -> Float32
    Lane 0.

  • y y(self) -> Float32
    Lane 1.

  • z z(self) -> Float32
    Lane 2.

  • __getitem__ __getitem__(self, i: Int) -> Float32

  • with_x with_x(self, v: Float32) -> Vec3
    A copy with x replaced by v.

  • with_y with_y(self, v: Float32) -> Vec3
    A copy with y replaced by v.

  • with_z with_z(self, v: Float32) -> Vec3
    A copy with z replaced by v.

  • dot dot(self, o: Vec3) -> Float32
    Sum of the component-wise products of x, y and z.

  • cross cross(self, o: Vec3) -> Vec3
    Right-handed cross product self x o (unit_x cross unit_y is unit_z).

  • length_squared length_squared(self) -> Float32
    Squared Euclidean length; avoids the square root when only comparing lengths.

  • length length(self) -> Float32
    Euclidean length.

  • distance distance(self, o: Vec3) -> Float32
    Euclidean distance to o.

  • normalized normalized(self) -> Vec3
    Unit vector in the same direction; zero stays zero.

  • lerp lerp(self, o: Vec3, t: Float32) -> Vec3
    self + (o - self) * t per component; t is unclamped, so it extrapolates.

  • min min(self, o: Vec3) -> Vec3
    Component-wise minimum.

  • max max(self, o: Vec3) -> Vec3
    Component-wise maximum.

  • abs abs(self) -> Vec3
    Component-wise absolute value.

  • max_component max_component(self) -> Float32
    Largest of x, y and z by signed value (not by magnitude; use abs() first for that).

  • approx_equal approx_equal(self, o: Vec3, eps: Float32 = EPSILON) -> Bool
    True when x, y and z each match within eps under approx_eq.

  • to_vec4 to_vec4(self, w: Float32) -> Vec4
    (x, y, z, w): pass 1 for a point and 0 for a direction.

  • write_to write_to(self, mut writer: Some[Writer])

struct Vec4 #

mojo
struct Vec4(Equatable, ImplicitlyCopyable, RegisterPassable, Writable)

A 4D vector, also used for homogeneous points and colours.

Fields:

  • data: F4

  • __init__ __init__(out self, x: Float32, y: Float32, z: Float32, w: Float32)

  • zero zero() -> Vec4
    (0, 0, 0, 0).

  • x x(self) -> Float32
    Lane 0.

  • y y(self) -> Float32
    Lane 1.

  • z z(self) -> Float32
    Lane 2.

  • w w(self) -> Float32
    Lane 3: 1 for a point, 0 for a direction, alpha for a colour.

  • __getitem__ __getitem__(self, i: Int) -> Float32

  • xyz xyz(self) -> Vec3
    (x, y, z) as a Vec3, dropping w without a perspective divide.

  • dot dot(self, o: Vec4) -> Float32
    Sum of the component-wise products over all four lanes.

  • length length(self) -> Float32
    Euclidean length over all four lanes.

  • approx_equal approx_equal(self, o: Vec4, eps: Float32 = EPSILON) -> Bool
    True when all four components match within eps under approx_eq.

  • write_to write_to(self, mut writer: Some[Writer])

cinder.core.profile #

Source: cinder/core/profile.mojo

Tracy profiling for the engine: zones, frame marks, plots and messages. Profiling is off until Profiler.start() (nothing listens on the network before that) and every call is a cheap no-op while it is off. Zone opens on construction and closes when it is destroyed. Mojo destroys a value right after its last use, so end the zone explicitly where it should close: var z = profiler.zone("render") ... # the instrumented work _ = z^ String lifetimes: Tracy copies zone names, zone text and messages during the call, but keys plots by the name pointer and reads it for the rest of the program. Profiler.plot therefore interns each plot name once in a table that is never freed (one small allocation per distinct name, the first time it is plotted while profiling runs).

  • comptime COLOR_RENDER: UInt32 = 0x3F7FBF — Zone colour (0xRRGGBB) for rendering.
  • comptime COLOR_PHYSICS: UInt32 = 0xBF7F3F — Zone colour (0xRRGGBB) for physics and fixed steps.
  • comptime COLOR_ANIM: UInt32 = 0x7FBF3F — Zone colour (0xRRGGBB) for animation.
  • comptime COLOR_AUDIO: UInt32 = 0xBF3F7F — Zone colour (0xRRGGBB) for audio.
  • comptime COLOR_ASSETS: UInt32 = 0x7F7F7F — Zone colour (0xRRGGBB) for asset loading.

struct Profiler #

mojo
struct Profiler(ImplicitlyCopyable, Movable)

A handle to the shim's Tracy client; copies share the process-wide profiler and its plot name table.

Fields:

  • lib: NativePtr

  • __init__ __init__(out self, lib: NativePtr)

  • available available(self) -> Bool
    Whether the shim was built with Tracy.

  • start start(self) -> Bool
    Starts the Tracy client (idempotent). Returns False when Tracy is not built in.

  • stop stop(self)
    Shuts the Tracy client down if it is running; a no-op otherwise or without Tracy.

  • is_started is_started(self) -> Bool
    Whether the Tracy client is running (start without stop).

  • is_connected is_connected(self) -> Bool
    Whether a Tracy viewer is connected; always False while stopped or without Tracy.

  • zone zone(self, name: String, color: UInt32 = 0) -> Zone
    Opens a zone named name that ends when the returned Zone is destroyed. color is 0xRRGGBB (0 keeps Tracy's default; see the COLOR_* constants). Mojo destroys a value after its last use, so end the zone explicitly with _ = z^ where it should close. While profiling is off the zone is inert and its id is 0.

  • frame_mark frame_mark(self)
    Marks the end of a frame.

  • plot plot(self, name: String, value: Float64)
    Adds value to the Tracy plot called name. Does nothing while profiling is off. Tracy keeps the name pointer for the program's life, so the name is interned (copied once into a never-freed table); later calls with the same name reuse that copy and do not allocate.

  • plot_name_count plot_name_count(self) -> Int
    How many distinct plot names have been interned (shared by every copy).

  • message message(self, text: String)
    Sends text to Tracy's message log (info severity); a no-op while profiling is off.

struct Zone #

mojo
struct Zone(Movable)

An open profiling zone; ends when destroyed. id is 0 while profiling is off.

Fields:

  • lib: NativePtr

  • id: UInt64

  • __init__ __init__(out self, lib: NativePtr, name: String, color: UInt32)

  • text text(self, note: String)
    Attaches text to the open zone.

cinder.core.ring #

Source: cinder/core/ring.mojo

RingBuffer: a fixed-capacity FIFO queue with no allocation after construction.

struct RingBuffer #

mojo
struct RingBuffer[T: Copyable & Deinitable](Movable, Sized)

Single-threaded bounded FIFO. push returns False when full; pop returns None when empty. Capacity must be a power of two so index wrapping is a mask. Cross-thread queues live in the native shim (CnEventQueue); this type is for in-engine command and event buffering.

Fields:

  • __init__ __init__(out self, capacity: Int) raises
  • capacity capacity(self) -> Int
    Number of slots, a power of two fixed at construction.
  • __len__ __len__(self) -> Int
  • is_empty is_empty(self) -> Bool
    True when nothing is queued (pop would return None).
  • is_full is_full(self) -> Bool
    True when every slot is taken (push would return False).
  • push push(mut self, var value: Self.T) -> Bool
    Appends value; returns False (and drops nothing already queued) when full.
  • pop pop(mut self) -> Optional[Self.T]
    Removes and returns the oldest value, or None when empty.
  • peek peek(self) raises -> Self.T
    A copy of the oldest value; raises when empty.
  • clear clear(mut self)
    Pops and destroys every queued value, oldest first.

cinder.core.time #

Source: cinder/core/time.mojo

Time: a monotonic clock and the fixed-timestep accumulator that drives the simulation. The frame loop (section 4 of the brief) simulates at a fixed 60 Hz and renders at whatever rate the display allows, interpolating between the last two simulation states with alpha.

  • comptime NS_PER_SECOND: Int = 1_000_000_000 — Nanoseconds per second.

struct Clock #

mojo
struct Clock(Movable)

Monotonic wall clock in nanoseconds, with per-frame deltas.

Fields:

  • __init__ __init__(out self)
  • now_ns now_ns(self) -> Int
    Current perf_counter_ns() reading. Monotonic, but its origin is arbitrary (not the clock's creation), so only differences between readings are meaningful.
  • elapsed_seconds elapsed_seconds(self) -> Float64
    Seconds since the clock was created.
  • tick tick(mut self) -> Float64
    Seconds since the previous tick (or creation). Never negative.

struct FixedStep #

mojo
struct FixedStep(ImplicitlyCopyable, Movable)

Fixed-timestep accumulator. Feed real frame times with advance; it returns how many simulation steps of step_seconds to run (capped at max_steps_per_frame to avoid the spiral of death) and leaves alpha() as the interpolation factor for rendering between the previous and current simulation state.

Fields:

  • step_seconds: Float64

  • max_steps_per_frame: Int

  • __init__ __init__(out self, hz: Float64 = 60.0, max_steps_per_frame: Int = 8)

  • advance advance(mut self, frame_seconds: Float64) -> Int
    Accounts for frame_seconds of real time and returns the number of steps to simulate.

  • alpha alpha(self) -> Float64
    Fraction of a step accumulated but not yet simulated, in [0, 1).

  • total_steps total_steps(self) -> Int
    Sum of every step count advance has returned.

View as MarkdownSource: docs/api/core.md