90 lines
14 KiB
Markdown
90 lines
14 KiB
Markdown
# Actual WGPU label-ink evaluator — 2026-09-13
|
|
|
|
Status: **second caller policy authored, not compiled or executed**. The first policy completed an actual Windows WGPU run with eight corridor findings; its raw evidence remains untouched. This revision changes only this harness's caller positioning/width policy and adds backplate-containment checks. The measured-label module, corridor helper and production renderer remain untouched.
|
|
|
|
## Preserved first-policy result and second-policy question
|
|
|
|
The parent gate `.local/scenario-label-wgpu-gates-20260913` completed 32 cases with zero invalid controls, eight findings (all italic) and 12 advance-spill cases. Every finding was `outside_corridor_x_plus_one_pixel`; every case had `outside_plate=0` and `blocker_changed_pixels=0`. The actual resolved italic face was Segoe UI Italic. The first-policy source hash was `48236A9A6FD0156A9DB4CAF7520D48F00FE8697202E036B697A03C27632CED95`; its stable executable hash was `BE5B044A974AF61E471D0A887C8DF1DCD73C5CC582AC5E8CCEBCD343D77752E2`. The gate's receipts/manifests bind the exact module and dependency inputs. Parent owns publication; these local paths describe inspected evidence, not a cross-machine handoff.
|
|
|
|
Independent readback of narrow italic `text.rgba` confirmed the left-side violations: at zoom0.65/DPI1 the true corridor began at physical x60.8125, with one violating pixel in column59; at1/1 it began73.5, with two in column71; at1.75/1 it began100.6875, with seven across96..99; at1/1.5 it began110.25, with five across106..108. All four counts apply the unchanged channel threshold and one-physical-pixel halo. This supports testing reserved internal space; it does not prove a universal three-unit glyph overhang bound.
|
|
|
|
**Policy 2:** keep the true world corridor at x36.25 with width160 or760, and keep camera, viewport and blocker positions unchanged. Reserve 3 world units on each horizontal side: resolve the label against width154 or754, and paint at x39.25. Grow its backplate by the existing3 horizontal/2 vertical world units. Never grow or shift the true corridor to make the result pass. All 32 strings/styles/zoom/DPI/control combinations remain; narrower displayed prefixes may legitimately change because the available advance budget decreased.
|
|
|
|
New oracles require the constructed backplate's exact world left/right edges to remain within the true corridor, with no epsilon or clamp, and measure its actual painted yellow pixels against that corridor (strict and one-pixel-halo counts). A world containment failure or painted spill beyond the halo is a candidate finding. The first policy did not test backplate-within-corridor containment; the additional requirement is recorded explicitly instead of rewriting its historical result. Widths at or below the six-unit reserve are outside these 32 probes; no empty-corridor product behavior is selected here.
|
|
|
|
## Question and competing expectations
|
|
|
|
Does a single-line label measured by the pinned Iced text allocator fit its proposed painted backplate and horizontal free interval after the actual canvas camera transform? Layout advances are not glyph-ink bounds. Italic bearings, combining marks, fallback fonts, antialiasing and device scaling can falsify containment despite a successful width measurement.
|
|
|
|
The experiment distinguishes three claims:
|
|
|
|
1. **Advance-only hypothesis:** every painted pixel lies in the returned layout rectangle. Count strict violations and violations after a one-physical-pixel halo. Violations are evidence, not automatic evaluator failure: the module explicitly does not promise ink containment.
|
|
2. **Candidate padding hypothesis:** adding 3 horizontal / 2 vertical world units to that rectangle contains ink, with a separately reported one-physical-pixel halo. This is an unaccepted probe policy. A violation beyond the halo is a finding, not grounds to silently increase the constant.
|
|
3. **Corridor/blocker hypothesis:** ink does not extend horizontally beyond the fixed true interval plus one physical pixel, the backplate's world edges stay within that interval, its painted pixels remain within the reported halo, and text does not change painted blocker interiors. These are independent probes, not the `widest_clear` algorithm or real Scene membership.
|
|
|
|
Strict counts remain visible even when the one-pixel halo passes; the halo is an explicit experimental allowance, not proof of zero overlap. Pixel difference threshold is greater than 3 in any 8-bit RGBA channel. We retain raw pixels so weaker ink and alternative tolerances can be investigated without rerendering.
|
|
|
|
## Actual path and independent controls
|
|
|
|
`main.rs` imports the existing `scenario_label.rs` using `#[path]`, without copying it and without enabling its test module. It creates an actual `iced::Renderer` through `Headless::new(..., Some("wgpu"))`, creates `canvas::Frame`, applies translation then positive uniform scale, calls the label's `canvas_text` followed by `Frame::fill_text`, records `draw_geometry`, and reads actual RGBA using `Headless::screenshot`. No alternate text renderer, native window, screenshot mock, or hand-drawn glyph is involved.
|
|
|
|
All 32 cases are evaluated in one dedicated executable process. Global fonts initialize before a fresh renderer and `RenderSession`; no font mutation is allowed until process exit. Each frame resets renderer geometry while retaining its genuine WGPU caches. Cases cover:
|
|
|
|
- A realistic existing scenario-style call label, CJK, decomposed combining marks, and an italic label beginning with `jfj`.
|
|
- True corridor widths160 and760 world units, giving policy2 text budgets154 and754. The wide case must preserve the explicit full source; the narrow case must show an ellipsis and retain a nonempty proper source prefix.
|
|
- `(zoom, DPI)` of `(0.65,1)`, `(1,1)`, `(1.75,1)`, `(1,1.5)`. Fractional camera translation and text origins exercise subpixel positioning.
|
|
|
|
The independently constructed `canvas::Text` control must paint the same pixels as the guarded payload. A second actual Frame shifts camera translation by exactly 16 physical pixels; its pixels must equal the original shifted by 16. These checks can reveal wrong coordinate assumptions before any ink conclusion. The viewport has generous right/bottom margin; any observed ink touching its boundary invalidates the probe because clipping may have hidden overhang.
|
|
|
|
Text-only pixels against white provide the ink mask. A separate decoration render contains yellow backplate then blue blocker rectangles. A composite adds actual text. Actual opaque blue interior pixels must remain unchanged. Because the blockers are painted after the backplate, this specifically evaluates foreground text interference; it does not infer that every antialiased blocker border remains untouched. Policy2 separately tests backplate world containment and actual yellow plate pixels, including attenuated antialiased edges. Its fixed blockers begin4 world units outside the true corridor; the maximum backplate may reach the corridor boundary but cannot consume that clearance.
|
|
|
|
Actual shaped glyph IDs and resolved font faces are recorded as a **coverage gate only**. Glyph ID zero, no glyphs, or no resolved italic/oblique face for the italic case invalidates that case. CJK missing-font boxes cannot count as CJK acceptance. The inspected shaping buffer does not supply the ink bounds. System fonts may differ across machines; face identities/families/styles are recorded, but this is not a portable pixel-golden suite or a complete font-file provenance recorder. Pin and hash relevant system font files for a cross-machine comparison.
|
|
|
|
## Build and run: integration owner only
|
|
|
|
No Cargo or new crate dependencies are required. Compile a normal executable, **not `rustc --test`**, against the exact already-built Iced 0.14.0 and unicode-segmentation artifacts, plus their matching transitive dependency directory. The active Iced artifact must have WGPU, canvas and advanced enabled; tiny-skia fallback is not accepted by this harness. This imports the real candidate but does not wire it into desktop production.
|
|
|
|
Equivalent raw invocation, substituting actual absolute paths:
|
|
|
|
```powershell
|
|
& $rustc --edition=2024 --crate-name scenario_label_wgpu --crate-type bin `
|
|
-C debuginfo=0 -C "linker=$clang" -L "dependency=$deps" -L "native=$nativeLibs" `
|
|
--extern "iced=$icedRlib" --extern "unicode_segmentation=$unicodeRlib" `
|
|
$mainRs -o $newExecutable
|
|
& $newExecutable $newPixelDirectory
|
|
```
|
|
|
|
`build-and-run.ps1` accepts those paths explicitly and refuses an existing run directory. It hashes the resolved compiler, linker, direct libraries and source inputs, and passes the resolved linker path through `-C linker=...`. For Windows gnullvm, initialize the parent's existing `.local/toolchain/env.ps1`, resolve `$clang = (Get-Command clang.exe).Source`, and set `$nativeLibs` to the repository's `.local/toolchain/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/windows_x86_64_gnullvm-0.52.6/lib`. This exact native search directory and `linker=clang.exe` appear in the successful parent `.local/scenario-label-gates-attempt4-20260913/compile-receipt.json` (rustc 1.98.1, x86_64-pc-windows-gnullvm). That prior module gate is linkage evidence, not an executed result for this WGPU harness.
|
|
|
|
The runner records the complete recursively enumerated Rust link/metadata artifact set (`.rlib`, `.rmeta`, `.dll`, `.lib`, `.a`, `.o`, `.obj`) in the supplied dependency directory and every file in the explicit native directory. It rehashes and compares both manifests after linking, detecting additions, removals or changed content. It separately rechecks source/direct-library/compiler/linker inputs before running. These are artifact supersets, not assertions that every file was linked. It hashes the executable before and after its fresh-process run and preserves an output manifest of generated logs, receipts, executable, metrics and images, including nonzero evaluator outcomes.
|
|
|
|
The successful attempt4 used Iced `ee73abd722c72024` and unicode-segmentation `85698043f7780858`. Another Iced artifact `a4e48b220c5d9711` also exists; use the matching tested dependency set rather than treating them as interchangeable. Required runtime DLL paths and a working WGPU adapter remain prerequisites. Adapter/device creation failure is an infrastructure failure, never an ignored pass.
|
|
|
|
```powershell
|
|
& ./build-and-run.ps1 -Rustc $rustc -IcedRlib $icedRlib `
|
|
-UnicodeSegmentationRlib $unicodeRlib -DependencyDirectory $deps `
|
|
-Linker $clang -NativeLibraryDirectory $nativeLibs `
|
|
-NewRunDirectory $freshRun
|
|
```
|
|
|
|
The script completed the first-policy build/run described above and requires PowerShell7 (`Path.GetRelativePath`). The second-policy Rust revision remains uncompiled/unexecuted. Select a NEW run directory; never reuse the first raw gate. Compile/run only after acquiring the parent's sole compiler/GPU resource. Before/after hashes do not detect a file temporarily changed and restored during linking; exclusive input ownership remains required. This is not a hermetic toolchain capture: implicitly searched system SDK libraries, clang's auxiliary executables/DLLs, runtime dependencies, system fonts and GPU drivers outside the explicit directories are not all hashed. Preserve that environment separately. The renderer exposes the backend name `wgpu`, not complete adapter identity through `Headless`; retain driver/backend logs and hardware identity separately. `WGPU_BACKEND` is recorded if set. Do not infer native acceptance from this offscreen run.
|
|
|
|
## Artifacts and interpretation
|
|
|
|
Each case writes `metrics.txt` with source/display/prefix, font faces, coordinates, ink pixel bounding box/counts, control differences and findings. It writes `text`, `decoration` and `composite` as both tightly packed, top-to-bottom RGBA8 `.rgba` and standard P6 `.ppm`. Width/height are in `metrics.txt` and the PPM header. Failed controls also preserve their rendered pixels. No image conversion participates in the oracle. These are real WGPU readbacks; inspect the PPMs or convert the preserved pixels for review after the numeric run.
|
|
|
|
Exit 0 means exactly 32 valid probes without a candidate-padding/corridor finding. Exit 2 means a candidate finding. Exit 3 means a coverage or control failure makes at least one probe invalid. Panic, missing summary or fewer than 32 cases is nonacceptance. Every completed run writes `summary.txt`; preserve failures before changing source. Report exact source, executable, artifact and environment provenance alongside any finding. A positive result supports only the specified strings/fonts/scales/padding; no universal raster containment claim follows.
|
|
|
|
This is intentionally smaller than Scene integration: no parsed model, occurrence/navigation hit testing, old renderer comparison, real activation/frame paint ordering, self-call or reversed-message corridor selection, clipping near viewport edges, rotation, wrapping, row-height policy, gesture performance, dynamic font replacement, or native user workflow is accepted. Those remain integration evaluators. A finding here should refine the shared measured-label/ink-clearance contract before production callers are added.
|
|
|
|
## Pinned primary API evidence
|
|
|
|
Registry source root is the repository-local `.local/toolchain/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/` (outside this worktree):
|
|
|
|
- `iced_core-0.14.0/src/renderer.rs`: `Headless::new`, `name`, `screenshot` explicitly return RGBA; `Renderer::reset` resets the frame.
|
|
- `iced_wgpu-0.14.0/src/lib.rs:912`: headless creation uses WGPU's actual adapter/device, RGBA render target and MSAAx4; screenshot uses a physical viewport and supplied DPI.
|
|
- `iced_wgpu-0.14.0/src/geometry.rs:306`: positive uniform-scale `fill_text` transforms the position, scales font size/absolute line height, and emits `Text::Cached`. Nonuniform/rotated vector fallback is outside this experiment.
|
|
- `iced_widget-0.14.2/src/canvas.rs`: canvas draws generated geometry through `geometry::Renderer::draw_geometry`.
|
|
- `iced_graphics-0.14.0/src/text/cache.rs`: glyph coverage introspection uses the actual allocator's buffer and resolved fonts. Its metrics are not substituted for screenshots.
|
|
- Current desktop `behavior.rs:2158` translates the camera then scales its actual Frame. This evaluator preserves that order without claiming to import the whole Scene.
|