diff --git a/.gitignore b/.gitignore index ec5589b..b432725 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ build/ bin/ pipeline_cache.bin /AGENTS.md +.review-gates/push/ +.review-gates/trees/ diff --git a/.review-gates/config.json b/.review-gates/config.json new file mode 100644 index 0000000..19102e6 --- /dev/null +++ b/.review-gates/config.json @@ -0,0 +1,39 @@ +{ + "base": "origin/master", + "rangeFrom": "upstream", + "authors": [ + "jorijnvdgraaf@catcrafts.net" + ], + "aiTrailers": "forbid", + "requireSigned": true, + "modes": { + "commits": { + "kind": "commits", + "lenses": [ + "repo-better-solution", + "repo-bug-prover", + "repo-facts" + ], + "effort": "xhigh" + } + }, + "gate0": [ + "CRAFTER_GRAPHICS_VALIDATION=1 crafter-build test --jobs=8", + "crafter-build --target=wasm32-wasip1", + "crafter-build test --target=x86_64-w64-mingw32 --runner=cmd:true Win32Link", + "ok=1; for d in examples/*/; do (cd \"$d\" && crafter-build) || ok=0; done; [ $ok = 1 ]", + "node --check additional/dom-env.js", + "node --check additional/dom-webgpu.js" + ], + "lenses": { + "repo-better-solution": { + "section": ".review-gates/lenses/repo-better-solution.md" + }, + "repo-bug-prover": { + "section": ".review-gates/lenses/repo-bug-prover.md" + }, + "repo-facts": { + "section": ".review-gates/lenses/repo-facts.md" + } + } +} diff --git a/.review-gates/lenses/repo-better-solution.md b/.review-gates/lenses/repo-better-solution.md new file mode 100644 index 0000000..3c2f76a --- /dev/null +++ b/.review-gates/lenses/repo-better-solution.md @@ -0,0 +1,67 @@ +## This repository: Crafter.Graphics + +The Crafter engine's graphics library: one C++ module, `Crafter.Graphics`, +one partition per file. On native targets it is a Vulkan 1.3+ renderer +built around `VK_EXT_descriptor_heap`, with a bindless descriptor-set +fallback, hardware ray tracing, a compute-shader UI and a Wayland or Win32 +window; on `wasm32-*` the same C++ surface runs in a browser over WebGPU, +through the JS bridge in `additional/`. The stated design is `README.md` +(the manual), `AGENTS.template.md` (the contributor guide; `AGENTS.md` is +written from it and the dependencies' notes by every crafter-build run, +and is not in the tree), `AGENTS.consumer.md` (the notes consumers' agents +get) and the driver investigations in `examples/VulkanTriangle/README.md` +and `examples/Sponza/README.md`. The guide's "Design rules" are the +owner's decisions: a range that departs from one without saying so is a +finding; whether one should change is the owner's call, not a defect of +the range. + +The layers: the partitions (`interfaces/`, `implementations/`), where the +target picks the backend in `project.cpp` and there is no runtime switch +and no abstraction layer: where the backends' APIs differ, the call site +`#ifdef`s. Every partition exists on every target, its body gated. The UI +kernels in `shaders/`, each compiled twice from one source, for the +descriptor heap and for the descriptor-set build (`.sets.spv`), which +`UIFallbackParity` holds pixel-identical, with their WGSL counterparts +baked into `additional/dom-webgpu.js`. `project.cpp`: the dependencies, the partition +and implementation lists, the shader builds and every test. The examples: +the in-tree consumers, GLSL and WGSL side by side. `UIDispatchHeader` +and the item structs are frozen and grow only through their reserved +`flags` bits and `_pad`, and `UIFusedHeader` is a separate contract, for +`DispatchFused`; a component is a stateless function over +buffers the user owns, never a hook or a virtual; the library enables only +the device features its own shaders use. + +Search first: `Device::EnqueueDeletion` for a handle a frame in flight may +still use; `VulkanBuffer::Stage` for a staged re-upload; +`DescriptorHeapVulkan`'s slots (`AllocateBufferSlots`, `BufferSlot`, +`AddSwapchainSlot`); the barrier builders (`SwapchainStageUnion`, +`SwapchainWriterAccess`, `BuildSwapchainInterPassBarrier`, +`BuildTlasWriteBarrier`, `BuildTlasReadBarrier`) and `Device::stageMask`; +`ImageVulkan::Create` and `VulkanTransition`; +`Device::Initialize(required, optional)` for a feature an application's +shader needs; `UIRenderer`'s helpers, `Rect::SubRect` and `Inset`; the +decode helpers in `shaders/ui-shared.glsl`; the vendored `lib/` (stb, +libevdev, the generated Wayland protocol code); Crafter.Event, +Crafter.Math and Crafter.Asset as cloned under +`~/.cache/crafter.build/external/` or checked out beside this repository, +and the Vulkan headers and Vulkan-Utility-Libraries cloned there too. +What belongs in one of the three dependencies lands there first (the +commit guidelines), not as a local copy here. A driver workaround takes +the shape of the one there is: a `Device` flag, one fenced block, a kill +switch, a probe test and a status note. + +Commit shape here, as the history shows and design rule 11 asks for the +examples and tests: a change travels in one commit with its test, the +examples and tests it breaks, and the README, `AGENTS.template.md` and +`AGENTS.consumer.md` text it makes untrue; a bug fix ships with a test +that fails before the fix (the testing instructions). + +Compatibility: the consumers are the examples, each a `LocalProject` of +`../../project.cpp`, and the owner's applications, which pull `master` +unpinned and get `AGENTS.consumer.md` on their next build. Design rule 11 +prefers the clean breaking change to a compatibility shim, so a break is +no finding by itself; a break the range does not carry through every +example, test and consumer-facing document in this tree is. The +environment switches `AGENTS.consumer.md` lists, the GLSL contract user +shaders `#include` from `ui-shared.glsl` and the shader file names +`ComputeShader::Load` resolves are contracts too. diff --git a/.review-gates/lenses/repo-bug-prover.md b/.review-gates/lenses/repo-bug-prover.md new file mode 100644 index 0000000..e21bfd5 --- /dev/null +++ b/.review-gates/lenses/repo-bug-prover.md @@ -0,0 +1,75 @@ +## This repository: Crafter.Graphics + +The execution contexts to enumerate, beside the charter's: +- One source, several builds. `project.cpp` defines exactly one of + `CRAFTER_GRAPHICS_WINDOW_WAYLAND`, `CRAFTER_GRAPHICS_WINDOW_WIN32` and + `CRAFTER_GRAPHICS_WINDOW_DOM` from the target: native Linux (Wayland and + Vulkan), `x86_64-w64-mingw32` (Win32 and Vulkan, gamepads through + Windows.Gaming.Input), `wasm32-wasip1` (the DOM and WebGPU, through + `additional/dom-env.js` and `additional/dom-webgpu.js`) and + `aarch64-alpine-linux-musl` (Mesa Turnip: no descriptor heap, no ray + tracing). Code behind one backend's `#ifdef` is still the range's code, + and a change to one backend's half can leave the other's behind. +- One device, several paths, chosen at start-up: the descriptor heap or + the bindless descriptor-set fallback (`CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP` + forces it), with each UI kernel's two SPIR-V builds; ray tracing or none + (heap-only; `Device::stageMask` drops its stage without it); Vulkan 1.4 + or 1.3 (`CRAFTER_GRAPHICS_NO_VULKAN_1_4`); `swapchain_maintenance1` or + not; GPU memory decompression or the CPU path + (`Device::memoryDecompressionSupported`); the NVIDIA AS workaround on or + off (`Device::workaroundDescriptorHeapAS`, + `CRAFTER_GRAPHICS_NO_AS_WORKAROUND`); llvmpipe on a machine without a + GPU. +- The GPU timeline. Three frames in flight, a fence each: what lives per + frame is keyed by `Window::currentFrame`, what lives per swapchain image + by `currentImage`, and the image count follows the present mode. A + handle a frame in flight may still use is released through the deletion + queue (freed `framesInFlight` frames later) or after a wait for the GPU; + a freed descriptor slot is handed out again at once; a swapchain rebuild + asked for from a listener mid-frame (`Resize`, `SetPresentMode`) waits + for that frame's present, and `onResize` runs after it; a static owner + is destroyed after the driver is gone. Within one dispatch, Vulkan + orders nothing between workgroups. +- Re-entry from the platform. The Wayland listeners run inside + `wl_display_dispatch`, `wl_display_dispatch_pending` and + `wl_display_roundtrip`, which the frame loop, window creation, + `Device::Initialize` and the clipboard's reads call. Windows calls the + window procedure from the message loop and also synchronously from + inside `CreateWindowEx`, `SendMessage` and `DefWindowProc`. The DOM's + listeners in `dom-env.js` call the `__crafterDom_*` exports. A listener + can resize, close or destroy the window it was called for. + Windows.Gaming.Input reports gamepad connects and disconnects on threads + of its own, queued under a lock until the event loop takes them. +- Shared state: `Device`'s statics are process-wide; `Device::frameCounter` + is one clock for every window, which the deletion queue, the staging + rings and the font atlas's eviction run on; the deletion queue is shared + under `deletionMutex`. The caches keyed on a `Font*` alias a new font at + a freed one's address unless `UIRenderer::InvalidateFont` ran. +- Data the GPU and JavaScript read: an item or header struct is mirrored + byte for byte by a `std430` block in `shaders/ui-shared.glsl`, by the + WGSL in `dom-webgpu.js` and by every shader that includes it, the + examples' among them; one push block serves both builds, through + `vkCmdPushDataEXT` on the heap and `vkCmdPushConstants` in the set + build, within 256 bytes. +- Tests: each its own process, in parallel, with a 60 s timeout; every + test that calls `Device::Initialize` needs a Wayland display, and one + that lacks a capability exits 77 and pins nothing on that machine. A + validation check counts only in a run where the layer loaded. + +Semantics to read on disk rather than recall: the Vulkan headers and +registry (`include/vulkan/`, `registry/vk.xml`) and Vulkan-Utility-Libraries +as cloned (`~/.cache/crafter.build/external/Vulkan-Headers-/`, +`Vulkan-Utility-Libraries-/`; several clones may sit side by side); +the Wayland protocols (`/run/host/usr/share/wayland/wayland.xml`, +`/run/host/usr/share/wayland-protocols/`, the generated code in `lib/`) and +the client headers (`/run/host/usr/include/wayland-client*.h`, +`xkbcommon/`); the MinGW headers (`/run/host/usr/x86_64-w64-mingw32/include/`); +libc++ and clang as the host installs them (`/run/host/usr/include/c++/v1`, +`/run/host/usr/lib/clang//include`); the vendored stb and libevdev +in `lib/`; Crafter.Event, Crafter.Math and Crafter.Asset as cloned +(`~/.cache/crafter.build/external/-/`). The crafter-build +Flatpak compiles with an LLVM of its own, which is not readable from +here: where a semantic may differ between LLVM releases, the host's +headers settle it for the host's build only. The Vulkan specification's +valid-usage rules, the WebGPU and WGSL specifications and Microsoft's API +documentation are not on disk: a semantic only they settle is unverified. diff --git a/.review-gates/lenses/repo-facts.md b/.review-gates/lenses/repo-facts.md new file mode 100644 index 0000000..041b8b5 --- /dev/null +++ b/.review-gates/lenses/repo-facts.md @@ -0,0 +1,35 @@ +## This repository: Crafter.Graphics + +- A claim that a tree passes the test suite, builds for a target, links + `Win32Link`, builds an example or passes `node --check` traces to the + Gate 0 log among the evidence paths INPUTS names: each command with its + exit code and the sha it ran at. A test the log shows as skipped, by + exit 77 or a missing tool, is not a pass. A claim of a run anywhere else + (the aarch64 phone target, the Win32 tests under Wine, a named GPU or + driver version, a browser, Deno's WebGPU, an example run by hand, a + frame judged by eye) traces to a capture of that run that names the + commit, or is unverifiable. +- A validation claim ("clean under the validation layer", "no validation + errors") holds only for a run in which the layer loaded. Read + `Device::Initialize` at HEAD for what a run with + `CRAFTER_GRAPHICS_VALIDATION` set does when `VK_LAYER_KHRONOS_validation` + is not installed: where it creates the instance without the layer and + says nothing, every validation check passes with nothing seen, and since + crafter-build prints only the name and time of a passing test, the Gate + 0 log alone does not show that the layer ran. +- A test a message names exists as a `LibraryTest("")` in + `project.cpp` and a `tests//main.cpp` whose checks pin what the + message says. +- `AGENTS.md` is generated from `AGENTS.template.md` and the dependencies' + `AGENTS.consumer.md` and is not in the tree: a claim about it is checked + against those sources. `AGENTS.consumer.md` reaches consumers' agents on + their next build: every sentence in it is a claim about the code at the + commit that writes it. +- A statement of where a driver stands (the NVIDIA notes in `README.md`, + `examples/VulkanTriangle/README.md`, `examples/Sponza/README.md` and the + guide's gotchas) names the driver version and traces to a run of the + probe on it (`HeapASRead --no-workaround`, `HeapDynamicIndexHit`), or is + unverifiable. +- A size or layout claim about a struct the GPU reads is checked against + the `static_assert` beside the C++ struct, the `std430` block in + `shaders/ui-shared.glsl` and the WGSL in `additional/dom-webgpu.js`. diff --git a/AGENTS.template.md b/AGENTS.template.md index c4ea98e..6582d28 100644 --- a/AGENTS.template.md +++ b/AGENTS.template.md @@ -20,6 +20,7 @@ Crafter.Graphics is the Crafter engine's graphics library: one C++ module, `Craf | `README.md`, `examples/*/README.md` | The manual, and the NVIDIA driver investigations (`VulkanTriangle`, `Sponza`). | | `AGENTS.consumer.md` | Notes for consumers' agents; crafter-build injects them into consumers' `AGENTS.md`. | | `bin/`, `build/`, `pipeline_cache.bin` | Generated and gitignored. `build/test-logs/` keeps the output of failed tests; `pipeline_cache.bin` is written by any example or test run from here. | +| `.review-gates/` | The pre-push review's `config.json` and this repository's lens sections (`lenses/`), read by Crafter.Review. Its `push/` and `trees/` are generated and gitignored. | Partitions, by what they are for. Every partition file exists on every target; one that does not apply compiles to an empty module behind `#ifdef CRAFTER_GRAPHICS_WINDOW_DOM` (or `#ifndef`), and several hold both halves in one file (`Window`, `Device`, `UI`, `Mesh`, `RenderingElement3D`, `RTPass`, `RenderPass`, `Image2D`, `FontAtlas`, `RT`). @@ -120,9 +121,11 @@ These were decided deliberately. Do not undo them without the maintainer's say-s ## Commit and PR guidelines - Branch from `master`; PRs go to `master` on `forgejo.catcrafts.net/Catcrafts/Crafter.Graphics`. Agent branches are `claude/`, issue work `claude/issue-`. The fetch URL is HTTPS and the push URL SSH (`forgejo@forgejo.catcrafts.net:Catcrafts/Crafter.Graphics.git`); the server refuses HTTPS pushes. -- Subject: `type(scope): imperative summary`, with `feat`, `fix`, `perf`, `refactor`, `docs` or `test` and the area as the scope (`device`, `rt`, `ui`, `window`, `wayland`, `win32`, `webgpu`, `font-atlas`, `compute`, `gamepad`). The body is grouped under the components it touches (`Device`, `Shaders`, `Window`, `Tests`) and says what was wrong, why, and why the fix has this shape; `git log -3` shows the voice. Add a `Co-Authored-By:` trailer when an agent wrote it. +- Subject: `type(scope): imperative summary`, with `feat`, `fix`, `perf`, `refactor`, `docs` or `test` and the area as the scope (`device`, `rt`, `ui`, `window`, `wayland`, `win32`, `webgpu`, `font-atlas`, `compute`, `gamepad`). The body is grouped under the components it touches (`Device`, `Shaders`, `Window`, `Tests`) and says what was wrong, why, and why the fix has this shape; `git log -3` shows the voice. - Update the README (and the example README that holds the investigation) in the same change when user-visible behaviour changes, and `AGENTS.consumer.md` when a convention, a signature, a contract or an environment variable consumers lean on changes. Consumers pick the new notes up on their next build. - Crafter.Event, Crafter.Math and Crafter.Asset are pulled unpinned, so a change that needs one of them lands there first and is built here with `--local` meanwhile. Pushing is per repository: land the sibling, then this one. +- Every push of new commits goes through Crafter.Review's pre-push review first, and goes out only once the owner approves the frozen hash. Run `flatpak run net.catcrafts.CrafterReview init` once in a fresh checkout or worktree: it writes the `/prepush` skill that drives the review, and the skill stays untracked. There is no CI, so the review's gate0, the commands in `.review-gates/config.json`, is the check before the push: the "Before a change is done" checks above, with every example built, and `node --check` on the two JS files in `additional/`. `freeze` refuses merge commits, so the merge of a reviewed branch into `master` is pushed without a freeze of its own. +- Every commit of a pushed range is GPG-signed, has `jorijnvdgraaf@catcrafts.net` as its author and committer, and carries no AI attribution trailer in its message, neither `Co-Authored-By:` nor `Assisted-by:`. `freeze` refuses a range with a commit that breaks any of these (`requireSigned`, `authors` and `aiTrailers` in `.review-gates/config.json`). - Never commit `bin/`, `build/` or `pipeline_cache.bin`, and never edit `lib/`. - A second worktree of this repository may sit beside it (`git worktree list`); its branch shows with a `+` in `git branch`. Leave it to whoever opened it.