Architecture, Development: text and boxes on device pixels
- Architecture: how the toolkit puts text and boxes on device
pixels (crispText, crisp, hairline, the nudge of the stage), and
what it does not give (subpixel smoothing).
- Development: the run is at 100 %, step 62 alone runs at 150 %.
The hooks that read the frame as the screen gets it, and the
deviceScaleFactor option of the harness. The sum is 1656.
Belongs to 32a0af6 in the main repository.
1 parent
875594a733
commit
f59d7604bf
2 files changed
+25
-1
No files matched your search
+15
@@ -25,6 +25,21 @@
|
||||
own edges. The toolkit only folds; which groups are folded is kept by the editor
|
||||
(`editor/sectionFolds.ts`, one entry in `localStorage`). Every title of the inspector goes through
|
||||
`addTitle` or `sectionHeader` in `inspector/host.ts`, so a new section gets the band by itself.
|
||||
**Text and boxes sit on device pixels** (`ui/pixelGrid.ts`). The layout counts in whole CSS
|
||||
pixels, and on a screen scaled to 125 % or 150 % most of those fall between two device pixels:
|
||||
the GPU averages both, text goes soft and a 1 px line becomes a pixel and a half of mixed colour.
|
||||
So every text of the interface is made with `crispText()` instead of `new Text()` — it sets
|
||||
Pixi's `roundPixels`, which rounds the corners of what is drawn to device pixels — and a
|
||||
`Graphics` that holds nothing but upright boxes is passed through `crisp()`, which does the same
|
||||
where the scale is not a whole number and nothing at 100 % or 200 %. A curve, an icon or anything
|
||||
of the game must *not* go through it: rounding its points bends it. A line is drawn `hairline()`
|
||||
wide, one device pixel as a browser draws `1px`, and flush with the edge it belongs to
|
||||
(`h - hairline()`, not `h - 1`); `drawControlBox`, `drawHeaderBar` and `drawSectionBand` do both
|
||||
already. At 150 % every odd pixel is an exact half, and which way the GPU rounds a tie is
|
||||
decided by the last bit of its arithmetic — one band came out a row shorter than its neighbours —
|
||||
so the stage is nudged by 1/32 px (`tieBreakFor`), which sends every tie the same way.
|
||||
What this does not give is the subpixel smoothing a browser applies to its own text: Pixi draws
|
||||
a text into a transparent picture first, and there only greyscale smoothing is possible.
|
||||
Double-clicks are counted in `clicks.ts`, not read off the event: Pixi only puts a click count
|
||||
on the `click` it synthesises and leaves `detail` at 0 on `pointerdown`, so every press handler
|
||||
that trusted `e.detail` never saw a second click. Presses are counted per key rather than per
|
||||
|
||||
+10
-1
@@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun
|
||||
# has and the web server's container has not (SKIP there)
|
||||
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
|
||||
php tests/php/releases.test.php # the release store (#266), the same way
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1648 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1656 editor checks, screenshots in /tmp/gm-e2e-*.png
|
||||
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
|
||||
./e2e.sh start [GM_STEPS] # the same run detached, see below
|
||||
./e2e.sh status # live progress, or the last run's result
|
||||
@@ -44,6 +44,15 @@ gets no WebGL context with them at all, so the app never renders.) Every run als
|
||||
in the log (`renderer: …`) and stops if that is a software one such as llvmpipe or SwiftShader:
|
||||
under WSL2 a missing GPU driver does not fail by itself, llvmpipe still offers WebGL 2 there.
|
||||
|
||||
**The run is at 100 %, one step is not.** The harness sets `deviceScaleFactor: 1`, where a layout
|
||||
pixel is a device pixel and nothing can sit between two. What a screen scaled to 150 % shows is
|
||||
therefore invisible to every step but 62 (`62-device-pixels.mjs`), which switches the scale over
|
||||
CDP, reloads the page and reads the frame as the screen gets it: `__gm.deviceColumn()` gives the
|
||||
device pixels down a column, `__gm.edgeContrast()` how hard the edges in a rectangle are, and
|
||||
`__gm.pixelGrid()` the scale and the width of a hairline. It switches back and reloads again, so it
|
||||
can stand anywhere in the order. A probe of your own takes the scale as an option:
|
||||
`runChecks({ projectId, baseUrl, deviceScaleFactor: 1.5 }, body)`.
|
||||
|
||||
**Leave the full run to CI.** Every push runs the whole proof on the runner (see *Continuous
|
||||
integration*), so locally take only what the change needs: the gate, plus the step that covers it —
|
||||
`GM_STEPS` keeps that to seconds. The whole suite works here too and costs about four minutes and
|
||||
|
||||
Reference in new issue
Block a user