Skip to content

Every non-final stamp of an SDF relief/incise stroke is bounded 4×strength wide, so a 4-sample stroke dirties 53× the bricks of one stamp #672

Closed

Description

@leonardoaraujosantos

What happens

On v0.120.1, one clay_layer_apply_stroke call with op RELIEF (INCISE behaves the same) authors one node per stamp. clay_layer_node_influence_bound reports those nodes as follows:

  • The last stamp is local: its x-extent is about 3× the brush radius on each side, under 1 world unit.
  • Every earlier stamp is widened by 4 × strength world units on each side, whatever the radius: +2.0 at strength 0.5, +4.0 at strength 1.0.

clay_brick_cache_mark_dirty_nodes follows those bounds. So a 4-sample stroke on a unit sphere dirties 2,548 bricks and the host re-meshes the whole sphere, while a single stamp dirties 48.

Repro

  • A document with one SDF layer holding a unit sphere.
  • One clay_layer_apply_stroke, op CLAY_OP_RELIEF (same with CLAY_OP_INCISE).
    • Preset: radius 0.12, strength 0.5, default spacing.
    • 4 samples at x = 0, 0.03, 0.06, 0.09 (y = 0, z = 1), pressure 1.
  • Node ids from sizing with clay_stroke_resolve. For each authored node: clay_layer_node_influence_bound(doc, layer, node, low, high, &has, &infinite).
Stamp x half-extent beyond its centre
last about 3 × radius (0.36)
each earlier stamp about 3 × radius + 4 × strength (+2.0 at strength 0.5)

Brick drain (clay_brick_cache_mark_dirty_nodes over the authored nodes, then take_dirty): 2,548 bricks for the stroke against 48 for one stamp.

Suspected cause

The #650 fix (PR #653, "Bound an edit by the smooth combines after it") dilates an edit's bound by the support of the smooth combines that follow it. For the non-final stamps of a stroke, the next stamp's relief amplitude (4 × strength) seems to be read as a blend support. Only the final stamp has no following combine, which fits the last stamp being the only local one.

Expected

Each stamp is bounded by its own reach (plus any genuine blend support), as a lone stamp is. The dirty region of a stroke should scale with the stroke's footprint, not with strength × stamp count.

Impact on hosts

This is the "cost scales with the edit, never with the sculpture" rule: every relief/incise stroke re-meshes most of a small form, and the dab latency budget is lost on larger ones. Hosts can't safely narrow the bound themselves, because that risks stale bricks.

Host evidence

ClaySpaceIOS pins this in Packages/ClayCoreKit/Tests/ClayCoreKitTests/StrokeInfluenceTripwire.swift (everyStampOfAStrokeIsBoundedByItsOwnReach). It's a known-issue test that flips when the engine fixes it; the branch will be linked once it's pushed. The app doesn't narrow the bound on its side meanwhile. Context: CyberdyneCorp/ClaySpaceIOS#3.

Activity

  1. leonardoaraujosantos commented on Sep 30, 2026

    @leonardoaraujosantos
    ContributorAuthor

    ClaySpaceIOS pins this with a known-issue test that will fail once it's fixed: StrokeInfluenceTripwire.swift (everyStampOfAStrokeIsBoundedByItsOwnReach). It's in CyberdyneCorp/ClaySpaceIOS#9.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions