API reference

Every public class, method, and event in @codexo/exojs. Generated from source.

C

classRenderingContext

@codexo/exojs / rendering / stable

Owns rendering orchestration: builds, optimizes and plays the internal RenderPlan for a RenderNode subtree, manages render-target/view state for off-screen capture, and exposes the low-level backend as an escape hatch. The conceptual model is "the context renders the node": context.render(node) // into the active target (canvas by default) context.render(node, { view }) // override view (the world view, screenView, etc.) context.renderTo(node, { target }) // into a caller-owned off-screen target (per-frame) context.capture(node, { width, height }) // into a freshly allocated RenderTexture

4
props
13
methods
0
events
Import
import { RenderingContext } from '@codexo/exojs'

Owns rendering orchestration: builds, optimizes and plays the internal RenderPlan for a RenderNode subtree, manages render-target/view state for off-screen capture, and exposes the low-level backend as an escape hatch.

The conceptual model is "the context renders the node": context.render(node) // into the active target (canvas by default) context.render(node, { view }) // override view (the world view, screenView, etc.) context.renderTo(node, { target }) // into a caller-owned off-screen target (per-frame) context.capture(node, { width, height }) // into a freshly allocated RenderTexture

Constructors1
new(backend: RenderBackend): RenderingContext
Methods13
Renders node into a freshly allocated RenderTexture and returns it. **The returned texture belongs to the caller**, destroy() included. This is one of three ownership patterns for GPU-backed render resources, and the only one that hands ownership across the API boundary: - **Caller-owned** - this method. Nothing releases the result for you; hold it as long as you need it and destroy it when you are done. Capturing per frame without destroying leaks VRAM steadily. - **Backend-pooled, borrowed** - acquireRenderTexture() / releaseRenderTexture() on the backend, used for filter intermediates. Whoever acquires returns it; destroying a pooled texture instead of releasing it corrupts the pool. - **Node-owned** - RenderNode's cacheAsTexture texture. Entirely internal; the node allocates, resizes and frees it. Using any of them after destroy() throws - see assertLiveResource.
Clear the active render target to color. Routes through the pass coordinator so it respects the clear-vs-load policy and never leaks the clear onto another target. Falls back to a raw backend clear when no coordinator is present (test stubs). clear here is the graphics-API verb (as in gl.clear) - it overwrites the target's pixels. It is deliberately **not** the collection clear() used elsewhere in the engine, which empties a container; nothing is released or reset by this call.
Open a standing readback over source for a caller that reads it repeatedly. Where readPixels answers once and, on WebGL2, waits for the GPU to do it, a PixelReader never blocks and never allocates per read: each request copies into one of the reader's own slots and is polled from the frame loop until the pixels land, a frame or more later. ts const probe = app.rendering.createPixelReader(app.frameTexture, { region: cursorRect }); The reader is yours: destroy it when the reads stop. Its slots cost slots * width * height * 4 bytes for as long as it lives, which is why a reader is created for a purpose rather than kept around just in case. Formats and regions are checked as for readPixels.
Immediately draw an instanced RenderBatch - one geometry + material drawn once with the batch's N per-instance (transform, tint) pairs as a single instanced draw call. This is the high-throughput immediate path: use it for many like items (tiles, bullets, procedural instances) where drawGeometry would issue one draw call each. The batch is recorded at once and lands in call order relative to the surrounding render calls. An empty batch is a no-op. drawInstanced records the draw immediately rather than queueing it, so this path leaves the backend's pass open: consecutive drawBatch calls merge into one GPU render pass and one submit instead of paying a pass plus a submit each. Ordering is unaffected - a renderer switch, a target/view change, or the end of the frame still closes the pass, and each batch takes its own slice of every shared buffer it writes. With no material the batch renders through the default mesh material (per-instance tint over the geometry's vertex colors). A custom material is supported, but its shader must read the per-instance transform from the shared transform buffer via a_nodeIndex - build it on INSTANCE_TRANSFORM_GLSL / INSTANCE_TRANSFORM_WGSL, which supply that contract. A shader that does not satisfy it throws on the first draw (the check reads the linked program, so it cannot run any earlier).
Immediately draw a single Geometry with transform as its world matrix - no retained RenderNode required. Useful for procedural or data-driven shapes that would be wasteful to wrap in a node. The draw is submitted through the mesh renderer and flushed at once, so it lands in call order relative to the surrounding render calls: a drawGeometry issued after a layer's render draws on top of it. All output is presented at the frame-end backend flush, as usual. transform is taken as the raw world matrix (a, b, c, d, tx, ty), bypassing the position / rotation / scale / origin composition a node would apply - build it with Matrix directly. The geometry must use the triangle-list topology and the standard mesh attribute layout (position, optional texcoord and color); custom per-vertex attributes are dropped. This is the single-draw convenience of the immediate API: each call is its own flush and draw call, and the geometry is repacked every call, so it is best for a handful of draws. An instanced batch path for drawing many like items as one upload + one draw follows in a later release.
Advance follow, shake, and bounds-constraint animations on the active view, every view rendered last frame (automatic), and any trackView-ed view. The SystemMethods.preFrame phase, at SystemOrder.CoreRendering - last of the engine's core systems.
Read a render texture's pixels back to the CPU. ts const frame = await app.rendering.readPixels(app.frameTexture); const image = new ImageData(frame.data, frame.width, frame.height); The payload is laid out exactly as ImageData wants it - RGBA bytes, four per pixel, top row first - so a screenshot, an export or a colour picked off the frame is the two lines above and nothing more. Both backends agree on that layout even though only one of them produces it natively. region reads part of the texture instead of all of it, in pixels from its top-left corner; a picker wants a 1x1 rectangle rather than a frame. Everything drawn into source before this call is included: pending work is submitted first. The result is the state at that moment and does not track the texture afterwards. # Cost A readback waits for the GPU to reach this point and hands the pixels back over the bus, which is why it is asynchronous and why it does not belong in a per-frame path - a full 1080p frame is ~8 MB per call. Await it in the postFrame phase, or from a coroutine, so the frame it reads is already finished. RenderStats.downloadBytes counts what this moved. # Formats 'rgba8' only. A float target holds values a byte per channel cannot carry, and no lossless byte answer exists for one; reading those needs a typed payload this does not have.
Render node into the active render target. Sets the backend's active view to options.view (or the default camera) before building and playing the render plan. This is the recommended high-level rendering entry point.
Render node into a caller-owned RenderTexture that is reused across frames - the per-frame, allocation-free counterpart to capture. The target and view are supplied by the caller; the view defaults to the target's own view. Save/restore is handled by the pass coordinator.
resize(width: number, height: number): void
Resize the camera and screen view to match new canvas dimensions. Preserves the camera's center and zoom; only the visible area size changes.
Whether a RenderTexture of the given color format can be rendered into on the active backend. 'rgba8' is always supported; the float formats ('rgba16f' / 'rgba32f') require hardware/extension support (WebGL2 EXT_color_buffer_float). Check this before allocating a float target and fall back to 'rgba8' yourself if unsupported - the engine throws rather than silently producing a broken target.
Register a custom View (e.g. a picture-in-picture or minimap view) so update advances its follow/shake/bounds each frame, alongside the active view. The active view and screenView are managed automatically and need not be tracked. The view is caller-owned: call untrackView before discarding it, otherwise a follow target keeps it (and its target node) referenced.
Properties4
backend: RenderBackend
Raw backend - draws/state only; do NOT switch target/view/clear here.
A RenderingContext-managed screen-space View suitable for UI overlays. Center and size are reset to match canvas logical dimensions on each resize call. The returned reference is stable (the same View object across frames), but its properties may change. Never follows, shakes, or rotates by default.
The active world View - the default for render. Defaults to a view matching the initial backend view. Replace with a custom View for follow, zoom, bounds, or split-screen viewport behavior. An assigned view is caller-owned, the same contract as trackView: assigning a replacement never destroys the outgoing one, so a view can be swapped out and back (per scene, per split-screen mode) and stays valid in between. Destroy the views you create once you are done with them; the context only releases the default camera it created for itself and screenView.
Source