Record the fourth #11 review and the agreed texture design

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-13 16:15:59 -04:00
1 parent 26f86bc6f7
commit 8daf1bcac1
1 file changed
+36 -23
+36 -23
View File
@@ -5,9 +5,9 @@ Not a decisions log; delete it when the extraction is done.
## Read this first
**Pull request #11 was reviewed on 2026-09-13 and the review has not been read.**
The session that got it was asked to write this document and stop rather than
spend context on it. So the first thing to do is read it:
**Pull request #11 has been reviewed four times; the fourth is answered in
`29d390d` and the fifth has not arrived.** Check for one before doing anything
else:
```sh
TOKEN=$(cat ~/.config/gitea/token)
@@ -19,10 +19,18 @@ curl -s -H "Authorization: token $TOKEN" \
https://git.arirex.me/api/v1/repos/iris/iris/issues/11/comments
```
Three rounds of review had already landed on #11 before this one, so check the
newest `submitted_at` rather than assuming the first result is current. My
replies to the earlier rounds are ordinary issue comments on the same PR and
say what each change was for.
Check the newest `submitted_at` rather than assuming the first result is
current. My replies are ordinary issue comments on the same PR and say what
each change was for.
**Agree a design before sending another variation of it.** The owner stopped
the fourth round with *"we should probably agree on the design here rather
than you keep submitting variations that I review"*. When a review comes back
about the shape of something rather than a defect in it, put the options and a
recommendation in front of her and implement what she picks. She chose the
per-image bind group for textures on 2026-09-13; the alternatives were
packing images into arrays like atlas pages, and a bindless `binding_array`
(ruled out by Android support).
## Stop point
@@ -31,8 +39,8 @@ say what each change was for.
- Agent fork: `git@git.arirex.me:iris-ai/iris.git`. The bot cannot push
canonical; work in the fork and open PRs into `iris/iris:main`.
- **#11** `split/11-texture-bindings`, worktree `/home/bob/repos/iris-pr11`,
head `7b318e3`, eight commits on top of `0f6a28b`. Reviewed four times;
the fourth is unread.
head `29d390d`, nine commits on top of `0f6a28b`. Reviewed four times, all
answered.
- **#12** `split/12-pointer-routing`, worktree `/home/bob/repos/iris-pr12`,
head `028521b`. A scroll falling through a hovered button. Not reviewed as
of the last check. Independent of #11 and can merge in either order.
@@ -40,31 +48,36 @@ say what each change was for.
## What #11 became
It started as "draw the glyph atlas as an array texture" and the review turned
it into a rework of how primitives are drawn. Worth knowing before reading the
new review, because the earlier rounds are what produced this shape:
It started as "draw the glyph atlas as an array texture" and four rounds of
review turned it into a rework of how primitives are drawn. Worth knowing
before reading a new review, because the earlier rounds are what produced this
shape:
- **Atlas pages are their own thing.** `GlyphAtlas` owns its page images and
emits dirty rectangles; `GpuPages` owns the array texture they upload to.
`Textures` holds standalone images only.
- **Every primitive is a registration.** `PrimitiveRegistry::register::<P>`
takes the primitive's WGSL and what it samples, and returns a
`PrimitiveKind<P>`. Its list, free list, buffers, bind group layout and
pipeline all follow from that. Rect, glyph and texture are registered the
same way at startup, so a caller registering their own primitive with their
own shader takes the identical path. That is deliberate — the owner wants
custom user shaders later.
- **A primitive registers itself by being drawn.** The type carries its own
WGSL and `PrimitiveRegistry` keys ids by `TypeId`, so the kind comes from the
type and there are no `RECT`/`GLYPH`/`TEXTURE` constants. Built-ins are
seeded up front only so first-draw order cannot shuffle them.
- **Nothing dispatches dynamically.** Push, free, renumber, upload and draw are
identical for every primitive; only the entry size and the pipeline differ,
and those are data. `InstanceList` carries a runtime stride and its data as
bytes. Measured: 0.2ns per write over a statically typed list, against 1.6ns
for a trait object.
- **The shared group is what every pipeline gets**: window, masks, the glyph
atlas array, and the one sampler. A mask texture would go here too. A
primitive that samples an image of its own declares `Primitive::TEXTURE`,
which gives it an image bind group, a draw call per instance, and the slot
recorded at write time; no other pipeline mentions textures at all.
- **`shader.wgsl` became `shader/prelude.wgsl` plus one file per primitive**,
because one module cannot declare two types at the same binding. The prelude
carries the window, masks, sampled texture, vertex shader and `masked()`.
- **Within a layer, types draw in registration order** — rect under glyph under
texture. Order within a layer was never meaningful (freeing an instance
swaps another into its place), and layers are the compositing mechanism.
carries the window, masks, atlas, sampler, vertex shader and `masked()`, and
is the one place binding numbers are written down.
- **Order within a layer means nothing** and nothing claims otherwise -- but
the widgets do rely on it today, since a `Text` over a `Rect` in one layer
only works because glyph registers after rect. Open question, raised on the
PR: text may need a layer of its own.
## Standing instructions from the owner