agentleFS
ForgeAXForgeAX/forgeax-engine/skills/forgeax-engine-debug-draw/SKILL.md

forgeax-engine-debug-draw

Skill70 starsChanged 18 days agoApache-2.0View on GitHub

ForgeAX immediate-mode debug geometry. Use when visualizing positions, colliders, frustums, axes, or radii without creating ECS entities.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    No ECS/render-graph/shader-registry dependency.

    line 19
  2. Distinctive:

    Call shapes from update systems or other code.

    line 55
  3. Very specific:

    Call flush(encoder, view, viewProj) at the end of the custom render graph or capture script.

    line 84
  4. Very specific:

    Missing createShaderModule injection: import the WGSL factory from @forgeax/engine-rhi-webgpu.

    line 134

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

forgeax-engine-debug-draw

debug-draw visualizes gameplay facts: app.debugDraw.line(a, b, RED) draws wire geometry in the final frame without ECS entities/components/systems. GPU recording and frame analysis use forgeax-engine-rhi-debug. Use debug-draw for demos, physics tuning, cameras, and audio radii.

Mental model

@forgeax/engine-debug-draw is an immediate-mode RHI convenience layer:

  • Shape calls (line / sphere / aabb / frustum / arrow / axes) accumulate vertices in CPU staging. arrow uses a body and four tip lines, with default tipLength = |end-start|/10; axes(worldMat,length) draws RGB arrows along local XYZ, like Bevy gizmos.axes.
  • At frame end, flush(encoder, view, viewProj) uploads staging, issues one draw, and clears staging for the next frame.
  • Nothing persists: omitting a shape call removes it from the next frame.
  • No ECS/render-graph/shader-registry dependency; the package compiles its own WGSL and creates its PSO.

Runtime users use automatic app.debugDraw.*; low-level RHI scripts use createDebugDraw(...) and explicit flush(...).

Dependency level alongside engine-math

PackageDependenciesPurpose
engine-mathNo ForgeAX dependenciesVec / Mat / Quat / Color / frustum reconstruction.
engine-debug-drawrhi + math + typesWire overlay collection and GPU flush.
engine-runtimedebug-draw (thin glue)Attach app.debugDraw during createApp.

Runtime wiring lives in packages/render/src/debug-draw-glue.ts; debug-draw itself does not depend on runtime, ECS, render-graph, or shader.

Integration paths

Runtime path (automatic, recommended)

import { createApp } from '@forgeax/engine-runtime';

const app = await createApp({ canvas });

// In any update system or hook:
app.debugDraw.line([0, 0, 0], [1, 1, 1], [1, 0, 0, 1]);   // red line
app.debugDraw.sphere([0, 0, 0], 1, [0, 1, 0, 1]);          // green sphere
app.debugDraw.aabb([-1, -1, -1], [1, 1, 1], [0, 0, 1, 1]); // blue box
app.debugDraw.frustum(cameraViewProj, [1, 1, 0, 1]);        // yellow frustum
app.debugDraw.arrow([0, 0, 0], [0, 2, 0], [1, 1, 1, 1]);    // arrow (body + head); tipLength optional
app.debugDraw.axes(transform.world, 1);                     // local frame: X=red / Y=green / Z=blue arrows

// No manual flush -- runtime appends a DebugOverlay pass to the render graph
// and draws the staged vertices every frame.
  • createApp attaches app.debugDraw through the debug-draw-glue.ts hook.
  • Call shapes from update systems or other code; vertices accumulate and clear automatically at frame end.
  • Runtime owns the GPU overlay: app.debugDraw is a CPU source the Renderer takes once per frame and draws in every view; update-only or non-drawing frames discard its geometry.
  • An overlay failure skips the overlay and fires one debug-overlay-failed error per reason (overlay-source-failed, overlay-upload-failed, overlay-pipeline-failed); the scene frame continues. Read renderer.inspect().debugOverlay for the latest outcome.

Low-level path (custom RHI and graph)

import { createDebugDraw } from '@forgeax/engine-debug-draw';
import { createShaderModule } from '@forgeax/engine-rhi-webgpu';

const r = await createDebugDraw({ device, queue, createShaderModule });
if (!r.ok) {
  switch (r.error.code) {
    case 'pipeline-create-failed':  /* ... */
    case 'buffer-allocation-failed': /* ... */
  }
}
const dd = r.value;

// Per-frame:
dd.line([0, 0, 0], [1, 1, 1], [1, 0, 0, 1]);
// ... more shape calls ...
dd.flush(encoder, swapChainView, cameraViewProj);

// When done:
dd.destroy();
  • Supply device, queue, and the injected WGSL compilation factory createShaderModule from @forgeax/engine-rhi-webgpu.
  • Call flush(encoder, view, viewProj) at the end of the custom render graph or capture script.
  • destroy() releases GPU resources; subsequent shape calls are no-ops with one console warning, and flush returns Result.err.

Core API quick reference

APIFormVertex count
dd.line(a, b, color)a: Vec3, b: Vec3, color: ColorLike => void2
dd.sphere(center, radius, color, segments?)center: Vec3, radius: number, color: ColorLike, segments?: number => void$3 \times 2 \times \text{segments}$ (default segments=16: 96)
dd.aabb(min, max, color)min: Vec3, max: Vec3, color: ColorLike => void24 (12 edges)
dd.frustum(viewProj, color)viewProj: Mat4, color: ColorLike => void24 (12 edges)
dd.flush(encoder, view, viewProj)encoder: RhiCommandEncoder, view: TextureView, viewProj: Mat4 => Result<void, DebugDrawError>--
dd.destroy()() => void--

ColorLike accepts plain [r, g, b, a?] tuples, Float32Array, or branded Color from @forgeax/engine-math; no as Vec4 assertion is needed.

Depth mode

ModeBehavior
'always' (default)Draw overlays above the scene, ignoring depth.
'less-equal'Foreground geometry occludes the overlay using scene depth.

createDebugDraw({ depthMode: 'less-equal', depthFormat: 'depth24plus' }) requires depthFormat. Each instance compiles one PSO for its fixed depth mode. To use both modes in one frame, create two instances (AC-06 demo).

Capacity resize + truncation

TriggerBehavior
Vertices exceed capacity but remain below MAX_VERTEX_CAPACITY (1M)Double capacity and log old/new sizes once.
Vertices exceed MAX_VERTEX_CAPACITYFlush within the limit, discard overflow, and warn at most once per frame.

Exported constants: INITIAL_VERTEX_CAPACITY (1024), MAX_VERTEX_CAPACITY (1_000_000), VERTEX_STRIDE_BYTES (16).

Error model

DebugDrawErrorCode is a closed union. Each member carries .code / .expected / .hint / .detail, following AGENTS.md:

err.codeTriggerActionable .hint
'pipeline-create-failed'PSO or shader compilation failurePipeline creation failed: <rhiError>. Check WGSL syntax, vertex layout, and depth-stencil state.
'buffer-allocation-failed'GPU buffer allocation failureBuffer allocation failed: <rhiError>. Check available device memory and buffer usage flags.
'flushed-after-destroy'Flush after destructionDebugDraw was destroyed; create a new instance via createDebugDraw().
'viewProj-required'Missing viewProj at flushPass a viewProj Mat4 to flush(encoder, view, viewProj).

Handle err.code exhaustively without default. packages/debug-draw/src/errors.ts owns the complete error contract.

Pitfalls

  • Creating ECS mesh entities instead of dd.line(): debug-draw is immediate-mode and needs no component registration. Mesh entities outlive a frame, can leave stale debug geometry, and freeze engine gaps into demos.
  • Missing createShaderModule injection: import the WGSL factory from @forgeax/engine-rhi-webgpu; without it the package cannot create its PSO.
  • Missing depthFormat with less-equal: the PSO needs depthStencil.format; supply depthFormat: 'depth24plus' to avoid pipeline-create-failed.
  • Confusing debug-draw and rhi-debug: debug-draw draws gameplay wire overlays; rhi-debug records GPU calls for frame analysis. See the package README's distinction table.
  • Expecting shape calls to throw after destroy: they are no-ops, write no staging, and warn only once. The next flush reports Result.err({ code: 'flushed-after-destroy' }).
  • Forgetting flush is idempotent: empty staging returns ok(undefined) without beginRenderPass or GPU effects. A second flush in the same frame does not redraw.
  • Excessive sphere segments: 10,000 segments produce 60,000 vertices and may trigger a capacity warning, but do not crash the frame. Default 16 segments yields 96 vertices.

Further reading

  • Shape signatures, depth modes, capacity behavior, and errors: packages/debug-draw/README.md.
  • Closed error union and discriminated details: packages/debug-draw/src/errors.ts.
  • DebugDraw class + createDebugDraw factory + GPU resource lifecycle: packages/debug-draw/src/debug-draw.ts
  • Runtime attachment and final graph pass: packages/render/src/debug-draw-glue.ts.
  • Hello demo (5 modes: runtime / low / depth / hdrp-tonemap / empty): apps/hello/debug-draw/src/main.ts
  • Comparison with rhi-debug: packages/debug-draw/README.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.