14 KiB
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:
- 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.
- 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.
- 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_clearalgorithm 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:
& $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.
& ./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,screenshotexplicitly return RGBA;Renderer::resetresets 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-scalefill_texttransforms the position, scales font size/absolute line height, and emitsText::Cached. Nonuniform/rotated vector fallback is outside this experiment.iced_widget-0.14.2/src/canvas.rs: canvas draws generated geometry throughgeometry::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:2158translates the camera then scales its actual Frame. This evaluator preserves that order without claiming to import the whole Scene.