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 ## Read this first
**Pull request #11 was reviewed on 2026-09-13 and the review has not been read.** **Pull request #11 has been reviewed four times; the fourth is answered in
The session that got it was asked to write this document and stop rather than `29d390d` and the fifth has not arrived.** Check for one before doing anything
spend context on it. So the first thing to do is read it: else:
```sh ```sh
TOKEN=$(cat ~/.config/gitea/token) 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 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 Check the newest `submitted_at` rather than assuming the first result is
newest `submitted_at` rather than assuming the first result is current. My current. My replies are ordinary issue comments on the same PR and say what
replies to the earlier rounds are ordinary issue comments on the same PR and each change was for.
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 ## 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 - 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`. canonical; work in the fork and open PRs into `iris/iris:main`.
- **#11** `split/11-texture-bindings`, worktree `/home/bob/repos/iris-pr11`, - **#11** `split/11-texture-bindings`, worktree `/home/bob/repos/iris-pr11`,
head `7b318e3`, eight commits on top of `0f6a28b`. Reviewed four times; head `29d390d`, nine commits on top of `0f6a28b`. Reviewed four times, all
the fourth is unread. answered.
- **#12** `split/12-pointer-routing`, worktree `/home/bob/repos/iris-pr12`, - **#12** `split/12-pointer-routing`, worktree `/home/bob/repos/iris-pr12`,
head `028521b`. A scroll falling through a hovered button. Not reviewed as 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. 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 ## What #11 became
It started as "draw the glyph atlas as an array texture" and the review turned It started as "draw the glyph atlas as an array texture" and four rounds of
it into a rework of how primitives are drawn. Worth knowing before reading the review turned it into a rework of how primitives are drawn. Worth knowing
new review, because the earlier rounds are what produced this shape: 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 - **Atlas pages are their own thing.** `GlyphAtlas` owns its page images and
emits dirty rectangles; `GpuPages` owns the array texture they upload to. emits dirty rectangles; `GpuPages` owns the array texture they upload to.
`Textures` holds standalone images only. `Textures` holds standalone images only.
- **Every primitive is a registration.** `PrimitiveRegistry::register::<P>` - **A primitive registers itself by being drawn.** The type carries its own
takes the primitive's WGSL and what it samples, and returns a WGSL and `PrimitiveRegistry` keys ids by `TypeId`, so the kind comes from the
`PrimitiveKind<P>`. Its list, free list, buffers, bind group layout and type and there are no `RECT`/`GLYPH`/`TEXTURE` constants. Built-ins are
pipeline all follow from that. Rect, glyph and texture are registered the seeded up front only so first-draw order cannot shuffle them.
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.
- **Nothing dispatches dynamically.** Push, free, renumber, upload and draw are - **Nothing dispatches dynamically.** Push, free, renumber, upload and draw are
identical for every primitive; only the entry size and the pipeline differ, 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 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 bytes. Measured: 0.2ns per write over a statically typed list, against 1.6ns
for a trait object. 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**, - **`shader.wgsl` became `shader/prelude.wgsl` plus one file per primitive**,
because one module cannot declare two types at the same binding. The prelude because one module cannot declare two types at the same binding. The prelude
carries the window, masks, sampled texture, vertex shader and `masked()`. carries the window, masks, atlas, sampler, vertex shader and `masked()`, and
- **Within a layer, types draw in registration order** — rect under glyph under is the one place binding numbers are written down.
texture. Order within a layer was never meaningful (freeing an instance - **Order within a layer means nothing** and nothing claims otherwise -- but
swaps another into its place), and layers are the compositing mechanism. 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 ## Standing instructions from the owner