diff --git a/.opencode/skills/prism-dag/SKILL.md b/.opencode/skills/prism-dag/SKILL.md new file mode 100644 index 0000000..1c06970 --- /dev/null +++ b/.opencode/skills/prism-dag/SKILL.md @@ -0,0 +1,56 @@ +--- +name: prism-dag +description: DAG / graph patterns for Prism — adjacency list rules, arena allocation, Kahn's algorithm, flat buffer layout +license: MIT +compatibility: opencode +metadata: + domain: core +--- +## What I do + +Captures the conventions for Prism's directed acyclic graph (DAG) implementation: how edges are stored, how the graph is validated, and how to avoid common pitfalls. + +## When to use me + +Use this when working on graph/DAG structures (`pr_graph.h`, `pr_graph.c`, or `scratchpad/dag.c`), adding new node types, or modifying the topological sort / cycle detection logic. + +## Conventions + +### Adjacency lists — separate edge nodes + +Adjacency list nodes must be **separately allocated from the vertex array**. Never use the vertex struct itself as a linked-list node in another vertex's adjacency chain — that shares the `next` pointer between two roles and corrupts the graph. + +```c +// correct — per-edge copy on the arena +static void addEdge(PrGraph *g, const WpAllocator *alloc, u64 from, u64 to) { + PrVertex *src = &g->vertices[from]; + PrVertex *dst = wpMemAllocatorAlloc(alloc, sizeof(PrVertex)); + if (!dst) { /* handle OOM */ return; } + dst->id = g->vertices[to].id; + dst->value = g->vertices[to].value; + dst->next = src->next; + src->next = dst; +} + +// wrong — reuses the destination vertex as the list node +static void addEdge_bad(PrGraph *g, u64 from, u64 to) { + PrVertex *src = &g->vertices[from]; + PrVertex *dest = &g->vertices[to]; + dest->next = src->next; + src->next = dest; // overwrites dest->next used elsewhere +} +``` + +Arena bump allocation (`wpMemAllocatorAlloc`) is the natural fit for edge nodes. Always NULL-check the result — arena allocators can fail if the backing buffer is exhausted. + +### Flat buffer layout + +For hot-path graph evaluation, prefer SoA layouts and keep the DAG in contiguous arrays rather than individually allocated linked structures. + +### Cycle detection + +Use Kahn's algorithm integrated into `prGraphAddEdge` for cycle detection with rollback. When an edge would create a cycle, the edge is not added and the function returns an error. + +### Memory management + +Stack-allocate where possible; pass `WpAllocator *` explicitly for heap allocations. diff --git a/.opencode/skills/prism-rhi/SKILL.md b/.opencode/skills/prism-rhi/SKILL.md new file mode 100644 index 0000000..81b8539 --- /dev/null +++ b/.opencode/skills/prism-rhi/SKILL.md @@ -0,0 +1,74 @@ +--- +name: prism-rhi +description: RHI (Rendering Hardware Interface) patterns — compile-time dispatch, by-value desc structs, wapp array aliases, backend file layout +license: MIT +compatibility: opencode +metadata: + domain: rendering +--- +## What I do + +Captures the RHI conventions for Prism: how backends are dispatched, how descriptor structs and arrays are handled, and how the files are organized. + +## When to use me + +Use this when working on any file in `scratchpad/rhi/` or `src/prism/rhi/`, or when creating a new backend (Vulkan, D3D12, Metal). + +## Conventions + +### Backend dispatch + +Backend selection is compile-time via `-D PR_RHI_VULKAN` / `-D PR_RHI_D3D12` / `-D PR_RHI_METAL`. The umbrella header `pr_rhi.h` includes the appropriate alias file: + +```c +#if defined(PR_RHI_VULKAN) +# include "vulkan/pr_rhi_vk_aliases.h" +#elif defined(PR_RHI_D3D12) +# include "d3d12/pr_rhi_d3d12_aliases.h" +#elif defined(PR_RHI_METAL) +# include "metal/pr_rhi_metal_aliases.h" +#else +# error "Define one of: PR_RHI_VULKAN, PR_RHI_D3D12, PR_RHI_METAL" +#endif +``` + +Each `_aliases.h` file maps generic names to backend-specific names: + +```c +#define prRhiCreateDevice prRhiCreateDeviceVk +#define prRhiCreateSwapchain prRhiCreateSwapchainVk +#define prRhiCreateBuffer prRhiCreateBufferVk +// … +``` + +Backend implementations are suffixed with the backend name: `pr_rhi_vk_device.c`, `pr_rhi_vk_swapchain.c`, etc. + +### Desc structs + +All descriptor structs are passed **by value**, not `const *`: + +```c +// correct +PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface, + PrRhiDeviceDesc desc, WpAllocator *alloc); + +// wrong +PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface, + const PrRhiDeviceDesc *desc, WpAllocator *alloc); +``` + +### File layout + +``` +scratchpad/rhi/ +├── pr_rhi.h ← umbrella header (API declarations + backend dispatch) +├── pr_rhi_types.h ← shared types (enums, element types, array aliases, desc structs, opaque handles) +├── vulkan/ +│ ├── pr_rhi_vk.h ← Vulkan backend header (opaque struct defs + Vk-suffixed decls) +│ └── pr_rhi_vk_aliases.h ← #define alias mapping +├── d3d12/ +│ └── … +└── metal/ + └── … +``` + diff --git a/AGENTS.md b/AGENTS.md index 1d1df63..100cfb1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ are rendered via GPU shaders. - **Language**: C11 / C++11 (dual-mode, like `src/wapp/`) - **GPU API**: Vulkan, abstracted behind a Rendering Hardware Interface (RHI) - **Shading language**: Slang, stored in external `.slang` files under `src/shaders/` -- **Build**: TBD — either a standalone shell build script or a `justfile` (Just) +- **Build**: `justfile` (Just) as a task runner - **Dependencies**: `src/wapp/` (local utility library, already vendored) ## Coding Conventions @@ -34,25 +34,20 @@ All code follows the patterns established in `src/wapp/`. The project prefix is ### Formatting -- **Indentation**: tabs (no spaces). Tab width is a viewer preference. -- **Braces**: always required after `if`, `else`, `for`, `while`, `do` — even - when the body is a single statement. This avoids ambiguity and makes diffs - cleaner. +Machine-enforceable rules (tabs, braces, pointer alignment, continuation +alignment) are in `.clang-format` — run `clang-format -i ` to apply. -```c -// correct -if (condition) { - do_thing(); -} - -for (int i = 0; i < n; i++) { - process(i); -} - -// wrong — no braces, spaces instead of tabs -if (condition) - do_thing(); -``` +- **Return-type alignment**: Within each `// =====` section, align function + declaration names so the first letter of every function occupies the same + column. For pointer return types, place `*` directly against the function + name (no space) and put all alignment padding between the type name and `*`. + ```c + // correct — * against fn name, padding before * + PrRhiSwapchain *prRhiCreateSwapchain(…); + void prRhiDestroySwapchain(…); + PrRhiSwapchainResult prRhiAcquireNextImage(…); + ``` + This cannot be automated by clang-format and must be done manually. ### Storage qualifiers @@ -171,6 +166,16 @@ Always use typed array aliases (`WpU64Array`, `PrNodeIdArray`, etc.) rather than raw pointers when declaring array variables. Follow the existing typedef pattern in the module (`typedef Type *TypeArray`). +Typedef pattern: +- Opaque handles use `**` (pointer-to-pointer) +- Value types use `*` (contiguous block) +```c +typedef PrRhiBuffer **PrRhiBufferArray; // opaque handles → ** +typedef PrRhiColorAttachment *PrRhiColorAttachmentArray; // value types → * +``` +Group opaque handle arrays first, value type arrays second, separated by a +blank line. + Use named init flags (`WP_ARRAY_INIT_NONE`, `WP_ARRAY_INIT_FILLED`) instead of bare `0` — they make the initialisation policy explicit. @@ -202,10 +207,21 @@ documents/ ├── RENDERING_HARDWARE_INTERFACE.md ├── NODE_SYSTEM.md ├── ROADMAP.md -└── research/ - └── vulkan-baseline.md +├── research/ +│ └── .md +└── session-logs/ + └── YYYY-MM-DD.md ``` +## Skills + +Domain-specific conventions are stored as skills in `.opencode/skills//SKILL.md`. +These are loaded on-demand by the AI agent when a task matches their description, +keeping AGENTS.md lean. + +- **`prism-rhi`** — RHI backend dispatch, by-value descs, file layout +- **`prism-dag`** — DAG adjacency lists, arena allocation, Kahn's algorithm + ## Workflows for AI agents ### Research / planning @@ -215,20 +231,12 @@ documents/ a summary; iterate on the plan before writing any code. 3. Only start implementing after the plan is approved. -### README +### Skill maintenance -Keep `README.md` in sync with the project as it evolves. Update it when: -- The directory layout changes meaningfully -- Language, toolchain, or build system decisions are settled -- Dependencies are added or removed -- The project reaches a notable milestone - -### Learning from edits - -The user may edit code produced by AI agents. When this happens, infer the -reason for the change and update AGENTS.md with any new conventions, patterns, -or constraints that the edit reveals. This keeps the guide aligned with the -user's evolving preferences. +When you observe the user correcting your output (e.g. formatting, conventions), +infer the rule and add it to the relevant skill's `SKILL.md`. If no skill +matches, add a new one. This keeps AGENTS.md focused on project identity and +critical workflow rules rather than accumulating domain details. ### Committing @@ -236,30 +244,3 @@ Only commit when explicitly asked. When asked: - Stage only intended files. - Write a short, conventional commit message in present tense. - Never amend, force-push, or create PRs without a request. - -## Session Log — 2026-07-05 - -### Completed -- **DAG refactoring** (`scratchpad/dag.c`): Decoupled PrGraph from PrNodeManager. - Graph tracks its own active vertices (`vertex_count`, `max_vertex_ever`, `b8 active`). - `prGraphAddEdge`, `prGraphTopologicalSort` no longer take `PrNodeManager*`. - Kahn's algorithm integrated into `prGraphAddEdge` for cycle detection with rollback. - Output verified matching baseline. -- **RHI research document** written to `documents/research/rendering-hardware-interface.md`. - Covers 8 reference RHIs, architecture patterns, core API surface, Vulkan-specific - considerations, Slang integration, and proposed directory layout / lifecycle design. -- Added NVRHI to `documents/resources.md`. - -### Key Decisions -- Graph API is self-contained — no graphics or node-manager dependencies. -- RHI will use object-based + command-list-oriented architecture with vtbl dispatch. -- Object lifecycle: `prRhiCreate*` / `prRhiDestroy*` with explicit `WpAllocator*`. -- All GPU state explicit (no hidden pipeline state). -- Per-frame command pools and descriptor pools, arena-allocated scratch. -- `#version-macro` convention removed from Slang files; `__slang` define used instead. - -### Next Steps -1. User reviews RHI research document, then iterate if needed. -2. Begin implementing RHI layer: types header, device creation, Vulkan backend skeleton. -3. Implement Slang shader compilation + reflection integration. -4. Wire up per-frame lifecycle (command pool reset, descriptor pool reset, scratch reset). diff --git a/documents/session-logs/2026-07-05.md b/documents/session-logs/2026-07-05.md new file mode 100644 index 0000000..2605080 --- /dev/null +++ b/documents/session-logs/2026-07-05.md @@ -0,0 +1,36 @@ +# Session Log — 2026-07-05 + +## Completed +- **DAG refactoring** (`scratchpad/dag.c`): Decoupled PrGraph from PrNodeManager. + Graph tracks its own active vertices (`vertex_count`, `max_vertex_ever`, `b8 active`). + `prGraphAddEdge`, `prGraphTopologicalSort` no longer take `PrNodeManager*`. + Kahn's algorithm integrated into `prGraphAddEdge` for cycle detection with rollback. + Output verified matching baseline. +- **RHI research document** written to `documents/research/rendering-hardware-interface.md`. + Covers 8 reference RHIs, architecture patterns, core API surface, Vulkan-specific + considerations, Slang integration, and proposed directory layout / lifecycle design. +- Added NVRHI to `documents/resources.md`. +- **RHI API surface** (`scratchpad/rhi/pr_rhi.h`, `pr_rhi_types.h`): Full RHI API + designed with compile-time alias dispatch, by-value desc structs, wapp array types + for pointer+count replacement, `PrRhiSwapchainResult` enum, surface capabilities, + compute pipeline support, all primitive topologies, and aligned function declarations + grouped by subsystem section. +- **Initial opencode setup** (opencode.json, justfile, skills, trimmed AGENTS.md). + +## Key Decisions +- Graph API is self-contained — no graphics or node-manager dependencies. +- RHI will use compile-time alias dispatch (`#define prRhiCreateDevice prRhiCreateDeviceVk`) + over vtbl — zero runtime overhead, dead-stripping, separate builds per backend. +- `justfile` (Just) as task runner. +- Object lifecycle: `prRhiCreate*` / `prRhiDestroy*` with explicit `WpAllocator*`. +- All GPU state explicit (no hidden pipeline state). +- Per-frame command pools and descriptor pools, arena-allocated scratch. +- `#version-macro` convention removed from Slang files; `__slang` define used instead. +- Desc structs passed by value (not `const *`) for simpler caller ergonomics. +- Array aliases grouped: opaque handles (`**`) first, value types (`*`) second, separated + by blank line — element types before array aliases before desc structs. + +## Next Steps +- Begin Vulkan backend implementation starting with `pr_rhi_vk_device.c` (instance/device/surface/swapchain creation). +- Implement Slang shader compilation + reflection integration. +- Wire up per-frame lifecycle (command pool reset, descriptor pool reset, scratch reset). diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..33145f6 --- /dev/null +++ b/opencode.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://opencode.ai/config.json", + "command": { + "build": { + "template": "!just build", + "description": "Build the project" + }, + "lint": { + "template": "!just lint", + "description": "Run linter / typecheck" + }, + "test": { + "template": "!just test", + "description": "Run tests" + } + }, + "permission": { + "edit": { + "src/wapp/**": "deny" + } + } +}