Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.24)

project(claycore
VERSION 0.120.0
VERSION 0.120.1
DESCRIPTION "Portable C++20 SDF + voxel engine library"
LANGUAGES C CXX) # C: ufbx

Expand Down
2 changes: 1 addition & 1 deletion bindings/c/clay.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern "C" {

#define CLAY_ABI_MAJOR 0
#define CLAY_ABI_MINOR 120
#define CLAY_ABI_PATCH 0
#define CLAY_ABI_PATCH 1

/* Upper bound on the element count of any batch call: points, rays, cells,
* selected node ids, stroke points, polygon vertices. A count above it is
Expand Down
85 changes: 85 additions & 0 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,91 @@ forward-refuse).
v0.116.0 tag and its first commit is the bump, so the gap that produced the
0.78.0 and 0.103.0 stragglers below did not open this time.

**0.120.1 is a patch: no symbol added or removed and no struct re-laid out,
AND IT IS A RELEASE A CALLER OBSERVES, in seven ways, none announced by any
version gate.** Against v0.120.0: 708 `clay_*(` symbols both sides, and the
seven `-` lines in the `clay.h` diff are the `CLAY_ABI_PATCH` define and six
comment lines, none inside a `typedef struct`. Every added line but the
define is a comment. No kernel, `.clayspace`, scene or kernels-fixture
change.

**An unconfined infinite repeat grid reports an unbounded `tape.bounds`**
(#645 -- issue #640). It reported one cell, although the copies fill space:
a 0.4 box on a 1.5 grid minus a radius-1 sphere had **5,824 of 5,824**
material samples outside the box, measured on main at `9616286c`. Now
`clay_tape_info` returns +/-FLT_MAX as for a plane, and `clay_mesh`,
`clay_voxel_rasterize`, the bakes, pyclay's `Volume` constructors and
`clay_sdf_smooth_begin` REFUSE such a document without a region where they
used to work on one cell. A random-document probe found 1,269,305 samples
outside on main and 0 after; documents with no infinite grid plan
byte-identical bricks.

**`tape.bounds` narrows per operator** (#637): a subtract keeps its left
operand, an intersect the overlap, and a smooth group now adds its own blend
ring. A carve by a large cutter marched **9,640,000 -> 125,000 cells** and
meshed 42.18 -> 5.26 ms (Apple M2 Max, Release, median of 200). A mesh sized
by pyclay's `resolution=` meshes finer at the same resolution: the gallery's
`37_groups` went 12,418 -> 28,846 triangles and **746 KiB against its 400 KiB
budget** at resolution 88, and now asks for 58. Nothing changes at a fixed
voxel size.

**0.120.1 restores regression coverage on the device.** Its gate is the first
same-OS comparison since the iOS 27.0 re-baseline (#625), which left 0.120.0
comparing a run against itself. It passed: iPad15,5 on iOS 27.0, 75 cases,
**median ratio 0.998 against the baseline, none above both the 1.4 tolerance
and the 0.125 ms floor**, with the canary drifting x1.65 while thermalState
read nominal. `stroke_build`, #634's watch item, read **0.324 ms against the
baseline's 0.488** (0.66x, within 1.09x of its 26.5.2 figure); #634 is closed
on that, the cause of v0.120.0's high reading left unresolved.

**`clay_document_undo_bound` / `_redo_bound` report a grab, not its node**
(#648 -- issue #639). A step whose chains differ only in a head of grab,
magnify, blob or alpha links reports their balls, placed and dilated as the
item is, and clamped into the node's bound: **12 bricks where it was 1,000 /
1,440 / 4,000 at 1 / 10 / 40 grabs**, undo p50 1.665 -> 0.089 ms at one grab
(Mac M-series, CPU backend, `cpu-only` Release). The per-brick price still
grows with the chain, **7.4 -> 28.1 us from 1 to 160 grabs**.

**`clay_layer_node_influence_bound` and `clay_brick_cache_mark_dirty_nodes`
widen behind a smooth sibling** (#653 -- issue #650), by its full blend
support: a later smooth combine carries an edit back into the band. Two
r = 0.3 spheres blended at k = 0.3, the first moved 0.1: **6,359 band samples
outside the bound and 25 stale bricks before, 0 and 0 after**; a randomized
oracle refills 2.5% and 2.8% more bricks. A node followed only by hard
combines, including every node appended last, is bit-identical.

**A full brick build keeps a grab the deformer cull cannot judge** (#652,
part of issue #649): on a repeated item, and behind a magnify, twist or pose.
Rich random documents with a build off the raw field went **62 -> 35, worst
0.2632 -> 0.0825**. #649 stays open on two other mechanisms.

**Voxel sculpt-layer operations are undo steps** (#647 and #651 -- issue
#642). Strength, visibility, move, remove and merge-down record one each, as
do a layer's creation and every edit inside a recording layer; through
0.120.0 none did, and undoing a pass left its **110** cells in the layer's
record for the next dial to put back. A pass in a new layer is now two undos.
**An unmatched `clay_document_end_undo_group` is a no-op** -- it folded every
step since the session began into one -- and **a nested group folds once**.
Enabling undo mid-session starts an empty history, as it always did; that is
now documented, with #641's advice to save right after. The crash journal
gains a session-private event kind, so a v0.120.1 journal carrying one is not
replayable by v0.120.0.

**pyclay's multires Layer brushes move the surface** (#636, #646 -- issues
#627, #628): `MultiresSculptor.stamp` and `SculptLayerStroke.stamp` take
`layer_height`, default 0.05, where a zero ceiling moved **0 classes against
Draw's 37**. The layered smooths average a rim vertex over its whole ring
(**64 of 64** rim vertices were off the dense hierarchy), and a multires
sculptor rebinds after a restructure (reachable through
`clay_multires_remove_highest_level` + `_add_level_for_patches`) and after a
C++-only `set_base_mesh` that had left it stamping through a freed level.
#635 and #638 are docs, OpenSpec and tests only.

**All eleven merges carry the already-released 0.120.0 version line and are
NOT in v0.120.0**: #635-#638, #645-#648 and #651-#653 merged after the tag
without a bump, and ship here. The bump is the release branch's own commit,
`7f6cf38f`.

**0.113.0 stops decimation breaking a manifold it was given** (#567).
meshoptimizer chooses its own collapses and does not apply the link condition
collapse_edge refuses on, so clay_document_mesh -- documented as the
Expand Down
26 changes: 26 additions & 0 deletions docs/release-notes/v0.120.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,32 @@ the `struct_size` pattern:

## The gates behind this release

> **CORRECTED 2026-09-23, in v0.120.1's release PR.** This section was written
> before the gates it describes had run, and it was not updated before the tag.
> It said all four hardware gates and the device gate were stale. What the tag
> actually carries is below; the original text follows it, unchanged.

**As tagged:**

- **The four hardware gates were WAIVED, not re-run.** `tests/hardware/manual-gates.json`
records all four (`cuda-parity`, `nvcc-build`, `opencl-device`, `vulkan-device`)
as `waived` at `59e42ccf`, with the RTX 5060 run at `0c3a392c` kept as
`prior_run`. The reason: the only kernel-relevant change was #618's two
trailing comments in `include/clay/kernel/tape.h`, enum values unchanged. **No
GPU backend ran against v0.120.0's tree**; the cpu `parity` row covers cpu alone.
- **The device gate PASSED, on a new OS.** `tests/device/last-gate.json` records
claycore `704f2d4a` at ABI 0.120.0 on `iPad15,5` / **iOS 27.0 (24A437)**: 75
cases, seven cold sessions, thermal state nominal at both ends,
`treeDirty: false`. The reference iPad had moved from iOS 26.5.2, so the
baseline was re-taken from this run (#625). **That means v0.120.0 had no
regression coverage**: a baseline compared against the run it came from
cannot fire `REGRESSION`. `BUDGET` and `GROWTH` still gated. Coverage returns
with v0.120.1, the first same-OS comparison.
- The device host needed the scene lifecycle to launch on the iOS 27 SDK
(`tests/device/Host/`); without it, session 1 died before any test ran.

**As originally written** (superseded by the above):

**The four manual hardware gates are STALE and must be re-answered before this
is tagged.** All four (`cuda-parity`, `nvcc-build`, `opencl-device`,
`vulkan-device`) are recorded as **run** on an RTX 5060 host at commit
Expand Down
Loading
Loading