added review

This commit is contained in:
Jorijn van der Graaf 2026-11-02 14:57:39 +01:00
commit 005b04b183
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
6 changed files with 222 additions and 1 deletions

View file

@ -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.

View file

@ -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-<hash>/`,
`Vulkan-Utility-Libraries-<hash>/`; 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/<major>/include`); the vendored stb and libevdev
in `lib/`; Crafter.Event, Crafter.Math and Crafter.Asset as cloned
(`~/.cache/crafter.build/external/<Name>-<hash>/`). 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.

View file

@ -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("<Name>")` in
`project.cpp` and a `tests/<Name>/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`.