switched to agents.md template
This commit is contained in:
parent
7555504644
commit
e0b1cf7b9e
2 changed files with 2 additions and 53 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
build/
|
build/
|
||||||
bin/
|
bin/
|
||||||
pipeline_cache.bin
|
pipeline_cache.bin
|
||||||
|
/AGENTS.md
|
||||||
|
|
|
||||||
|
|
@ -154,55 +154,3 @@ These were decided deliberately. Do not undo them without the maintainer's say-s
|
||||||
- The text kernels decode `FontAtlas`'s SDF in one place, `ui-shared.glsl` (`UI_SDF_ON_EDGE`, `UI_SDF_PIXEL_DIST_SCALE`, `uiGlyphRamp`, `uiGlyphCoverage`), and the WGSL text kernel in `dom-webgpu.js` repeats it; `FontAtlasDecode` holds both copies of the constants to `FontAtlas`'s, and nothing renders the WGSL, so check a change to it by hand (`examples/HelloUI` built with `--target=wasm32-wasip1`, in a browser, or the kernel run through Deno's WebGPU). The WGSL lives in `String.raw` literals: a backtick in a WGSL comment ends the literal and breaks the whole backend, which only `node --check additional/dom-webgpu.js` notices.
|
- The text kernels decode `FontAtlas`'s SDF in one place, `ui-shared.glsl` (`UI_SDF_ON_EDGE`, `UI_SDF_PIXEL_DIST_SCALE`, `uiGlyphRamp`, `uiGlyphCoverage`), and the WGSL text kernel in `dom-webgpu.js` repeats it; `FontAtlasDecode` holds both copies of the constants to `FontAtlas`'s, and nothing renders the WGSL, so check a change to it by hand (`examples/HelloUI` built with `--target=wasm32-wasip1`, in a browser, or the kernel run through Deno's WebGPU). The WGSL lives in `String.raw` literals: a backtick in a WGSL comment ends the literal and breaks the whole backend, which only `node --check additional/dom-webgpu.js` notices.
|
||||||
- Crafter.Math's misspelled identifiers (`Quanternion*`, `MulitplyAdd`, `CollumSize`) are its API; do not correct them here.
|
- Crafter.Math's misspelled identifiers (`Quanternion*`, `MulitplyAdd`, `CollumSize`) are its API; do not correct them here.
|
||||||
- Dependency notes: crafter-build 3.0.0 or newer keeps a section at the end of this file holding the `AGENTS.consumer.md` of every dependency that ships one (2.2.0 leaves the file alone), rewritten on every `crafter-build` and `crafter-build test`. As of that date Crafter.Math's notes are on its `master`, Crafter.Asset's only on its `claude/glb-loader` branch, and Crafter.Event has none, so a plain build adds Math's and a `--local` build whatever the sibling checkouts hold. Edits inside the section's markers are lost on the next build; a change to those notes belongs in their repositories. Commit the section when it changes.
|
- Dependency notes: crafter-build 3.0.0 or newer keeps a section at the end of this file holding the `AGENTS.consumer.md` of every dependency that ships one (2.2.0 leaves the file alone), rewritten on every `crafter-build` and `crafter-build test`. As of that date Crafter.Math's notes are on its `master`, Crafter.Asset's only on its `claude/glb-loader` branch, and Crafter.Event has none, so a plain build adds Math's and a `--local` build whatever the sibling checkouts hold. Edits inside the section's markers are lost on the next build; a change to those notes belongs in their repositories. Commit the section when it changes.
|
||||||
|
|
||||||
<!-- crafter-build: dependency notes begin -->
|
|
||||||
<!-- Written by crafter-build from the AGENTS.consumer.md each dependency ships, and rewritten on every build. Edit those files, not this section; cfg.dependencyNotes in project.cpp picks which dependencies are included. -->
|
|
||||||
|
|
||||||
<!-- from Crafter.Math -->
|
|
||||||
## Crafter.Math
|
|
||||||
|
|
||||||
The Crafter engine's math library: a C++ module with no implementation units and no dependencies, so everything is a template or an inline body compiled into your translation units with your build's `--target` and `--march`. Source and issues: https://forgejo.catcrafts.net/Catcrafts/Crafter.Math. Namespace `Crafter`. The README there documents the whole surface; these notes are what an agent working in a consumer needs.
|
|
||||||
|
|
||||||
### Which type
|
|
||||||
|
|
||||||
**`Vector<T, Len, Alignment = 0>`** is plain data, for vertex and uniform structs, events, anything stored or passed around. `.x .y .z .w` (and `.r .g .b .a`) alias `v[]`; constructors exist for `Len` 2, 3 and 4 (`Vector<float, 2>{x, y}`); `+ - * /` with vectors and scalars, `+=` and friends, `==`, unary `-`; statics `Dot(a, b)`, `Cross(a, b)` (`Len == 3`), `Normalize(a)`; members `Length()`, `LengthSq()`, `Normalize()`, `Invert()`. `std::format("{}", v)` prints `{x,y,z}`.
|
|
||||||
|
|
||||||
- `Alignment` is the number of stored elements. `0` means `Len`, naturally aligned: `Vector<float, 3>` is 12 bytes aligned 4. Any `N >= Len` stores `T v[N]` and marks the struct `__attribute__((packed))`, alignment 1: `Vector<float, 3, 3>` is 12 bytes aligned 1 (the vertex type Crafter.Graphics uses), `Vector<float, 3, 4>` is 16 bytes, a `vec4` slot. Conversion between alignments is implicit.
|
|
||||||
- Arithmetic between two lengths covers the shorter one and leaves the other lanes of the result uninitialised, and `==` compares the shorter. The member `Normalize()` divides without a zero check; the static `Normalize(a)` checks.
|
|
||||||
|
|
||||||
**`VectorF32<Len, Packing>`** is one SIMD register holding `Packing` vectors of `Len` floats, for math-heavy loops. Construct from a `const float*` or broadcast one `float`; `Load(ptr)`, `Store(ptr)`, and `Store<float>()` returning a `std::array`. Operators as above; statics `Cross` (`Len == 3`), `MulitplyAdd(a, b, c)` (`a*b + c`), `MulitplySub(a, b, c)` (`a*b - c`), `Rotate(v, q)` and `RotatePivot(v, q, pivot)` by a `VectorF32<4, Packing>` quaternion, `QuanternionFromEuler(halfAngles)`, `Blend<{{bools}}>(a, b)`; members `Sin()`, `Cos()`, `SinCos()`, `Shuffle<{{i, j, k}}>()`, `Negate<{{bools}}>()`, `ExtractLo<N>()`. Inside a template write `v.template Store<float>()`.
|
|
||||||
|
|
||||||
- `Load`, `Store` and the pointer constructor move the whole register, `AlignmentElement` floats (4, 8 or 16), not `Len * Packing`. Give them a buffer that big (`alignas(16) float buf[4] = { x, y, z, 0 };` is the idiom in the tests), zero the padding lanes on the way in and ignore them on the way out. The pointer need not be aligned.
|
|
||||||
- `Dot`, `Length`, `LengthSq` and `Normalize` are batched with the arity fixed at compile time: `Normalize`, `Length` and `LengthSq` take exactly `BatchSize` vectors, `Dot` takes `2 * BatchSize` (a0, b0, a1, b1, ...). They return `VectorF32<1, Packing * BatchSize>` with one lane per input vector (`Normalize` returns `std::array<VectorF32<Len, Packing>, BatchSize>`). There is no two-argument `Dot`: for one product pass the same pair `BatchSize` times and read lane 0, as `MatrixRowMajor::operator*` does, or use `Vector<float, 3>::Dot`.
|
|
||||||
- `BatchSize` is `AlignmentElement / Packing`, 4 for every `VectorF32<Len, 1>`. `OptimalPacking` is how many `Len`-vectors fill one register (table below). Shapes convert implicitly, copying the lanes they share.
|
|
||||||
- The x86 backend lacks `const` on the scalar operators, `Sin`, `Cos`, `SinCos` and `Shuffle`: call them on non-const objects.
|
|
||||||
|
|
||||||
**`VectorF16<Len, Packing>`** is the same surface for `_Float16` where the target has native half arithmetic: x86 with AVX512-FP16, AArch64 with ARMv8.2-FP16. Everywhere else, including every x86 tier below `sapphirerapids` and all of RISC-V, it is `using VectorF16 = VectorF32`, converting on `Load` and `Store` from `_Float16*`. Never overload a function on `VectorF16` and `VectorF32` both, and never assume its lanes are halves.
|
|
||||||
|
|
||||||
**`MatrixRowMajor<float, 4, RowSize, 1>`** is four-column and row-major, each row a `VectorF32<4, 1>` in the public `rows[]`; constructors take the 12 or 16 floats row by row. Only `T = float`, `CollumSize = 4`, `Repeats = 1` exist.
|
|
||||||
|
|
||||||
- `<float, 4, 3, 1>` (48 bytes) is the column-vector shape: `M * VectorF32<3, 1>` applies it with an implicit `w = 1`, `TransformNormal(v)` with `w = 0`; translation sits in column 3. `Store(float*)` writes the 12 floats row-major, which is `VkTransformMatrixKHR`: `m.Store(reinterpret_cast<float*>(instance.transform.matrix))`.
|
|
||||||
- `<float, 4, 4, 1>` (64 bytes) is the row-vector shape for the GPU side (`v * M`, translation in row 3). There is no matrix-times-vector operator for it in C++. `operator*` between two 4x4s, `Transpose()`, `LookTo(eye, direction, up)` and `LookAt(eye, focus, up)` are 4x4 only.
|
|
||||||
- Both shapes have `Identity()`, `Scaling(x, y, z)`, `Translation(x, y, z)` and `Rotation(pitch, yaw, roll)` in radians (each also from a `VectorF32<3, 1>`), `Rotation(Vector<float, 4> quaternion)`, `TRS(t, q, s)` (scale, then rotate, then translate: a glTF node), `InverseAffine()` (any rotation, scale and shear plus a translation, never a projection) and `Decompose()` returning `(t, q, s)`, with a mirroring expressed as a negative X scale. Each means the same transform in both shapes. `std::format` prints both.
|
|
||||||
|
|
||||||
**Quaternions** are `Vector<T, 4>` in `(x, y, z, w)` order, unit, `w` the scalar part, applied by `Vector<T, 3>::Rotate(v, q)` and `RotatePivot(v, q, pivot)`; identity is `(0, 0, 0, 1)`. The functions are statics of `Vector<T, 4>` and are spelled `Quanternion`: `QuanternionMultiply(a, b)` is the Hamilton product, b first then a (`Rotate(v, a*b) == Rotate(Rotate(v, b), a)`); `QuanternionConjugate(q)` is the inverse rotation; `QuanternionFromAxisAngle(unitAxis, radians)` is right-handed (+90° about Z takes X to Y); `QuanternionFromUnitVectors(from, to)` is the shortest arc, defined for opposite directions too; `QuanternionFromBasis(right, up, forward)`; `QuanternionFromEuler(rollHalf, pitchHalf, yawHalf)` takes **half** angles; `QuanternionNlerp(a, b, t)` and `QuanternionSlerp(a, b, t)` take the shorter arc and return unit quaternions.
|
|
||||||
|
|
||||||
**Intersection tests** (`IntersectionTestRayTriangle`, `IntersectionTestRaySphere`, `IntersectionTestRayOrientedBox`, `IntersectionTestSphereOrientedBox`, `IntersectionTestOrientedBoxOrientedBox`, with `PackedOBBs<Packing>` and `GetOBBCorners`) are batched over `Packing * BatchSize` primitives per call; `Packing` defaults to `VectorF32<3, 1>::OptimalPacking` (the ray-box test to the smaller of the 3- and 4-vector packings). A vertex argument is `std::array<VectorF32<3, Packing>, BatchSize>`: element `b`, lanes `s*3 .. s*3+2`, holds primitive `b * Packing + s`. The result is a `VectorF32<1, Total>` with lane `i` for primitive `i`: the ray parameter `t`, or `std::numeric_limits<float>::max()` for a miss (the two box-overlap tests return `0` for a hit). The triangle test culls back faces; the sphere test returns the near root, so a ray starting inside misses. `tests/Intersection/main.cpp` in the Crafter.Math repository has `PackVec3`, `PackVec4` and `PackScalars` helpers worth copying.
|
|
||||||
|
|
||||||
### Targets and SIMD
|
|
||||||
|
|
||||||
| Backend | Register | `MaxElement` (floats) | `VectorF32<3,1>::OptimalPacking` | `BatchSize` of `VectorF32<3, Opt>` |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| x86 AVX-512 (`x86-64-v4`, `sapphirerapids`) | 64 B | 16 | 5 | 3 (15 lanes) |
|
|
||||||
| x86 AVX2 (`x86-64-v3`), and the scalar fallback | 32 B | 8 | 2 | 4 (8 lanes) |
|
|
||||||
| AArch64 NEON, WebAssembly SIMD128 | 16 B | 4 | 1 | 4 |
|
|
||||||
| RISC-V RVV | 16, 32 or 64 B from the `Zvl*` in the march | 4, 8 or 16 | | |
|
|
||||||
|
|
||||||
- The backend and register width are fixed when the module is compiled, from predefined macros, so from the `--march` your build uses. crafter-build's default is `native`: fine for a developer build, wrong for a binary that ships. `--march=x86-64-v3` is the lowest x86 tier the library is tested on (its SSE paths use FMA); the `sapphirerapids` tier runs only on CPUs with AVX512-FP16.
|
|
||||||
- Tested as of 2026-10-31: all three x86 tiers on Linux and on Windows (`x86_64-w64-mingw32` under wine); AArch64 `armv8-a` and `armv8.2-a+fp16` under qemu, except Crafter.Math's own Matrix test, which does not compile for AArch64 with clang 23 (`arm_neon.h` wrapper functions defined twice across module units; it bites a unit that emits `VectorF32<3, 1>` arithmetic before a `VectorF32<3, 1>` `Sin`, `Cos` or `SinCos`, as that test does through `MatrixRowMajor::Rotation(pitch, yaw, roll)`). If an AArch64 build of yours fails with `definition with same mangled name` in `arm_neon.h`, it is that issue. RISC-V has never been built. WebAssembly builds (crafter-build adds `-msimd128`; flags you add to your own units must match) and has no tests.
|
|
||||||
|
|
||||||
### Spellings
|
|
||||||
|
|
||||||
- `Quanternion*`, `MulitplyAdd`, `MulitplySub`, `CollumSize`, `Aligment` are the real identifiers. Don't correct them in a consumer; a rename is an upstream change that updates every consumer at once.
|
|
||||||
|
|
||||||
<!-- crafter-build: dependency notes end -->
|
|
||||||
Loading…
Reference in a new issue