Skip to content

Latest commit

 

History

History
101 lines (80 loc) · 8.04 KB

AGENTS.md

File metadata and controls

101 lines (80 loc) · 8.04 KB

ic3d

A small 3D rendering library for iced apps: instanced meshes, a shadow-mapped pipeline with optional post-processing, cameras, lights, a scene graph widget and WGSL shader preludes. Consumers write only a fragment shader.

Commands

go-task build                     # cargo build for the host target
go-task lint                      # fmt check + clippy --all-targets --all-features -D warnings
go-task test                      # unit + GPU tests; GPU tests need Vulkan
go-task test:unit                 # unit tests only, no GPU needed
go-task test:gpu                  # GPU integration tests only
go-task test:one TEST=<name>      # one test, with a backtrace
go-task coverage                  # cargo-tarpaulin HTML report in coverage/
go-task ci                        # lint + test + build; CI runs it as docker:run TARGET=ci
go-task example EXAMPLE=showcase  # run an example, built with the debug feature
go-task test:crates-io            # test the crates.io build and dry-run its publish

Examples: showcase, showcase_advanced (needs debug; keys 1 to 6 switch debug shaders), gizmo, gizmo_manual_draggable, gizmo_manual_interactive, crystal and transparent.

Layout

  • src/pipeline/: RenderPipeline3D, the GPU types, growable buffers, the shadow pass, post-processing and the shader constants.
  • shaders/*.wgsl: the WGSL preludes, embedded into src/pipeline/shaders.rs with include_str!.
  • src/camera/, src/light/, src/mesh/ (primitives and SVG paths), src/math/: the building blocks.
  • src/scene/ builds the per-frame SceneData. src/graph/ is the SceneGraph. src/widget/ holds scene_3d() and Scene3DProgram.
  • src/gizmo/ and src/overlay/: the translation gizmo and the draggable and interactive overlays it is built on.
  • tests/gpu_*.rs: GPU integration tests. tests/gpu_helper.rs makes a headless Vulkan device.

Conventions

  • One type or concept per file, grouped by folder. A new camera goes in camera/, a new light in light/, re-exported from mod.rs. Never merge unrelated types into one file.
  • Struct fields stay private, behind accessors. Never expose internal GPU buffers or state.
  • Every public type has a doc comment.
  • WGSL lives in shaders/*.wgsl. A Rust GPU struct and its WGSL twin must match byte for byte.
  • Each source file's unit tests sit in its own <name>_tests.rs, pulled in with #[cfg(test)] #[path = "<name>_tests.rs"] mod tests;. Never combine several source files' tests in one file.
  • Unlike the shared default, this repo requires tests with every new feature or type, and aims for 80% coverage. docs/testing.md says what each kind of change needs.

Interfaces

  • grid-rs (the Grid app's background) uses compose_shader, RenderPipeline3D, Mesh and MeshBuilder, svg::parse_path, math and ic3d::glam.
  • The public API is what lib.rs re-exports, plus the gizmo, graph, math and widget modules. There is no prelude.rs.
  • compose_shader(fragment) prepends the engine preludes (scene uniforms, vertex IO, standard vertex shader, PCF shadows) to a consumer's fragment shader.
  • Bind group 0 is the engine's: scene uniforms, the lights array (up to MAX_LIGHTS, 16), the shadow map and its sampler. Group 1 is the consumer's custom uniforms. Changing a binding or a uniform's layout breaks every consumer shader.
  • iced and wgpu are Playtron forks, and glam 0.29. All three are re-exported (ic3d::iced, ic3d::wgpu, ic3d::glam), so consumers use those instead of their own dependency.
  • crates.io rejects git dependencies, so a release publishes a copy that depends on the forks as published on crates.io, iced-kora and wgpu-kora. scripts/crates-io.sh stages it. A fork change ic3d needs must be published as -kora first (playtron-os/iced, kora/README.md).

Read before

  • Before changing the pipeline, a bind group, a GPU type or a WGSL prelude, read docs/architecture.md.
  • Before adding a type or its tests, read docs/testing.md.
  • Before changing the API apps call (pipeline, scene_3d, SceneGraph, Scene3DProgram), read docs/consumer-api.md.

Gotchas

  • WGSL alignment differs from Rust #[repr(C)]: vec2<f32> aligns to 8 bytes, vec3<f32> to 16, and each mat3x3<f32> column takes 16. When adding a uniform struct, check Rust size_of against the WGSL. A mismatch corrupts data silently or fails validation at runtime.
  • iced's shader::Primitive::render() gets no device or queue, so create and resize textures in prepare().
  • iced's Pipeline trait needs Send + Sync, so Box<dyn PostProcessPass> must carry those bounds.
  • Without a Vulkan device, the GPU tests panic. Use go-task test:unit there. CI uses lavapipe.

Shared rules

This repository follows the rules every Playtron repo follows, plus those for libs packages. They are identical in every repo in this group, so fix them upstream rather than here.

Every repository

  • AGENTS.md is this repo's instruction file; CLAUDE.md and GEMINI.md only point at it.
  • Every agent loads AGENTS.md on every turn. Keep it under 150 lines and 1,500 words, this block included. Topic detail goes in docs/, linked in one line that says when to read it.
  • Never append dated notes, design history or one task's findings to AGENTS.md. Add a line only when it changes how the next task must be done.
  • Review policy goes in REVIEW.md, not here. No personal machine paths in either.
  • Comment only what the code cannot say, in one or two short sentences. Longer reasoning goes in the pull request.
  • Keep it simple and flat. Early return over nesting. No advanced language features where a plain one works.
  • After changing Rust: cargo check, cargo fmt, cargo clippy --fix --allow-dirty, until clean.
  • Run the repo's own lint and test tasks before calling work done. Never claim a command passed that you did not run.
  • Conventional Commits, type(scope): subject. feat releases a minor, fix and perf a patch, other types nothing.
  • A breaking change needs a BREAKING CHANGE: footer. feat!: does not parse, so it releases nothing.
  • Commit subjects are imperative and under 72 characters. Add a body only for a non-obvious why, in three lines or fewer.
  • Pull requests: Problem, Fix, Testing. 100 words for a small change, 250 at most. No investigation story, no file-by-file walkthrough.
  • Write plain English: short sentences, everyday words, "Kora" not "she", no invented shorthand. The writing skill has examples.
  • Never hand-edit a version; semantic-release owns it.
  • Do not add tests unless asked or REVIEW.md requires them.
  • Each package is its own repository: depend on a sibling by git URL, never by path, symlink or ../ link.
  • A dev deploy to a Kora host must override every /etc/systemd/user unit running the system build, with drop-ins naming the same store path.
  • A dev build of an agentos-accounts caller prompts on every credential read until root-owned allowed-callers.d names its exact exe.

libs packages

  • Public API is a contract. Export public types from prelude.rs. #[must_use] on builders.
  • Sweep for consumers before changing a public name: gh search code "<name>" --owner playtron-os.
  • Unsafe code is forbidden. Clippy all + pedantic, warnings are errors.
  • WASM-safe: use utils::platform abstractions, never std::time::Instant directly.
  • No hard-coded visual values. Playtron values are the trait and serde defaults; other brands override.
  • Feature-gate component modules with #[cfg(feature = "...")].
  • Read neighbouring files and match them. Consistency beats individual improvement.

Full text with the reasoning: all, libs. With the playtron plugin installed, /playtron:architecture has them loaded.