Skip to content

Keep a grab the deformer cull cannot judge in the brick's tape (#649) - #652

Merged
leonardoaraujosantos merged 1 commit into
mainfrom
fix/649-brick-build-matches-raw-field
Sep 23, 2026
Merged

leonardoaraujosantos merged 1 commit into
mainfrom
fix/649-brick-build-matches-raw-field

Conversation

@leonardoaraujosantos

Copy link
Copy Markdown
Contributor

Why

#649: a full brick build of a document disagrees with that document's own raw field (clay_eval_points) at in-band samples, by up to 0.263 on random rich documents. Before changing anything I reproduced it on origin/main (8b7a571) with the issue's probe: seeds 933, 5111 and 6043 give the numbers the issue lists (125 / 1,219 / 216 samples, worst 0.0105 / 0.1894 / 0.2632).

To tell the causes apart I compiled the brick tape without a cull region, and then shrank each failing document and compared the culled tape with the whole-document tape at the bad samples. Three separate mechanisms show up. This PR fixes the one that produces the large errors, the deformer cull. It also fixes a second hole in that same function that the investigation found. The other two mechanisms are reported below and are not changed here.

The cause fixed here: the deformer cull (#452) dropped grabs that reach the brick

Compiler::cull_deformers tests each grab's ball against the cull region in the item's local frame. Its soundness argument is induction along the chain: the region grows by every move a kept link can make. That argument breaks in two places:

  1. A repeated item. The interpreter folds the local point into its repetition cell before the chain runs (ctape_repeat_point, then ctape_prim_local). The chain therefore sees the folded point, while the region test used the unfolded one. A brick over any copy except the source cell dropped the grab, so that copy was filled undeformed. All of 5111's and 6043's error has this cause: each shrinks to one item with a radial or finite-grid repeat and one grab.
  2. A grab behind another point warp. Only a grab's move widened the region. A kept magnify, twist or pose moved the point without widening it, so every grab after it was tested against the bare region. The regression fixture puts a magnify ahead of a grab and was 0.038 off.

What lands

  • cull_deformers takes the item's Repeat. A repeated item keeps its whole chain.
  • The first link that is not a grab ends the test, and the rest of the chain is kept as it is. A Move's grabs sit at the head of a chain, ahead of anything else, so the hot path keeps its culling.
  • The comments now state both limits beside the induction argument.

Measured (RV_RAWCHECK=1 undo_bound_oracle_probe)

sweep main this PR
seeds 1..1500, plain 21 docs, worst 0.0310 21 docs, worst 0.0310
seeds 5001..6500, rich 62 docs, worst 0.2632 35 docs, worst 0.0825

Plain documents carry no repeats, so they do not move. The issue is not closed by this PR. What remains comes from the two mechanisms below, which are not in the deformer cull.

What remains (not changed here)

A. The chain cull pad is below one blend's support. Seed 978 shrinks to two boxes: a hard Add, then a quadratic smooth Add with k = 0.262. The pad is k * envelope(N) = 2.80k = 0.735, but one blend moves the result wherever a < b + 4k. The error at the brick is 0.0039, and the unshrunk document reaches 0.031. For a two-item chain the support (4k), the pad before #335, is exactly sufficient. Below it the error can reach about 0.1k. The #335 envelope was measured only at N >= 75 and applies its N = 75 value to every smaller layer. Forcing the pad to the support leaves 2 plain and 7 rich documents, and those come from longer chains (a fixed pad was never a proof there) plus B. The scene-model spec makes the envelope evidence-bound and its bar relative ("equal-or-fewer disagreements than the pre-envelope pad"). These documents break that bar, so how to repair it is a decision about the pad versus the #335 perf win. It belongs in its own change.

B. Squashed placements are culled as if their field were a distance. Seeds 5743 and 6290 shrink to items or layers with a non-uniform per-axis scale. cscale_nu_dist understates the distance by up to max(s)/min(s), as scene/types.h says. The per-brick cull still drops such an item once its box is band + pad away. item_geometry_reach_in_document already refuses a squashed placement for this reason. The cull needs the equivalent: dilate by (q - 1) * (band + pad), which in turn needs the band carried in CullRegion and in the cached CullIndex entries.

The forward-Move rows in the issue (seeds 933 and 5229) are A and B, and they do not change here.

Verification

  • 3 regression tests in tests/unit/test_deformer_cull.cpp: a radial copy, a grid copy, and a magnify ahead of a grab. All three fail on origin/main (worst 0.0151, 0.12, and 117 unequal samples) and pass with the fix. Each asserts that the grab really moves samples in the region, so none of them can pass because nothing was there to cull.
  • Full unit suite, 4 shards plus the partition check: 10/10 ctest entries passed (Release, CLAY_BUILD_TESTS=ON).
  • check_layering, check_kernel_dialect, check_licenses, check_c_abi, check_test_shards --binary (2935 cases partitioned) and check_doc_latency all pass.
  • No ABI, format or spec change. The scene-model requirement on the deformer cull already says "this SHALL NOT change the field", and this PR makes the implementation meet it.

Part of #649.

The per-brick deformer cull (#452) tests each grab's ball against the
cull region in the item's local frame, dilating the region by every move
a kept link can make. Two cases broke that induction and dropped grabs
that do reach the brick, so the brick build disagreed with
clay_eval_points:

- A repeated item. The interpreter folds the local point into its
  repetition cell before the chain runs, so a copy away from the source
  cell samples the grab at points the unfolded region never contains.
  This was the large half of #649: in-band brick samples off the raw
  field by up to 0.26 on random documents with a radial or grid repeat.
  A repeated item now keeps its whole chain.

- A grab behind any other point warp. Only a grab's move is bounded, but
  a kept magnify, twist or pose did not dilate the region, so the grabs
  after it were tested against the bare region (0.038 off in the
  regression fixture). The first non-grab link now ends the test and the
  rest of the chain is kept. A Move's grabs sit at the head of a chain
  and are still culled.

RV_RAWCHECK on undo_bound_oracle_probe: rich sweep 62 -> 35 documents,
worst 0.2632 -> 0.0825; plain sweep unchanged at 21 (no repeats there).
What remains is the chain cull pad and squashed placements, not the
deformer cull.
@leonardoaraujosantos
leonardoaraujosantos merged commit f3b04e0 into main Sep 23, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant