test(raster): lock POLY spans against a 4×4 sample oracle - #333
Draft
qianiaoo wants to merge 2 commits into
Draft
Conversation
`TRI` has no coverage field, so a rotated solid box resolves to two grey levels at any resolution — `emit_box` -> Sutherland-Hodgman -> `emit_tri` rounds every vertex to an integer pixel and there is nowhere to put a partial one. Recorded at `draw.rs:10-18` as a v1 degradation; what was missing was the price. `POLY` (opcode 10, `3 + N` words) carries the whole clipped convex polygon and one flat colour, so coverage is computed over the shape rather than per triangle. Per-triangle coverage is the wrong fix and the guard says so: two sequential blends are not one blend, and the shared diagonal of a rotated box keeps 68 interior partial pixels. Over the polygon it keeps none. It is not slower. `poly()` solves each scanline for the fully-interior x-range, fills it as one run and samples 4x4 only at the ends — O(perimeter), not O(area) — against a `tri` that evaluated three `orient()` calls for every pixel of the bounding box with no incremental stepping. Measured at 0.99x on a standalone bench and 22% faster end to end on eight rotated bars at 1080p. The inner loop stays integer: edge functions in 4*F fixed point, quarter-pixel offsets as +/-1 and +/-3, `div_euclid` for the span solve. No float enters it, so the frame-hash contract carries over. Hardware backends without per-pixel coverage decode `POLY` to a triangle fan — today's binary fill, byte-identical output. `Fill::Grad` keeps its TRI fan. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
div_euclid ceils on a negative divisor, so the s<0 span solver was handing one extra boundary column to fill_opaque. Compare the optimized fill to a per-pixel 4×4 oracle across rotations and clip cases, and raster the rotated rounded-box and solid 3D-face paths that previously only checked DrawList ops.
qianiaoo
force-pushed
the
test/poly-span-oracle
branch
from
August 25, 2026 03:23
18e8c58 to
1c7c914
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The POLY interior-span solver used
div_euclidon a negative edge step as if it floored. For a negative divisor it ceils, so one extra boundary column was handed tofill_opaqueand a partial-coverage pixel became solid. Existing tests only count interior partials, which cannot see that direction of error.What
s < 0span bound with(-k).div_euclid(-s).rounded-[999px]+rotate-28) and a solid 3D face (perspective+rotate-x/y), which previously only asserted DrawList ops.cargo test --releaseinengine/core: 132 passed.