Commit Graph
4688 Commits
Author SHA1 Message Date
Dylan M. Taylor 6bd20d6082 Merge latest main with GPU test scheduling fix 2026-11-13 04:14:15 +00:00
8f21de0a0c ci: limit concurrent GPU tests in nextest (#3323)
Co-authored-by: skylartaylor <skylartaylor@users.noreply.github.com>
Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
2026-11-12 23:06:17 -05:00
Dylan M. Taylor 092a6136d3 Merge latest main with archived xtask path fix 2026-11-13 04:05:16 +00:00
skylar taylor-barrickandskylartaylor 547368febf test: resolve xtask from relocated nextest archives (#3322)
Co-authored-by: skylartaylor <skylartaylor@users.noreply.github.com>
2026-11-12 22:51:00 -05:00
Dylan M. Taylor 8dca7d4e8a Merge remote-tracking branch 'origin/main' into codex/778-effect-migration
# Conflicts:
#	crates/aep/src/container.rs
#	crates/aep/src/container/aepx.rs
#	crates/aep/src/container/arbitrary.rs
#	crates/aep/src/container/camera.rs
#	crates/aep/src/container/effects.rs
#	crates/aep/src/container/footage.rs
#	crates/aep/src/container/header.rs
#	crates/aep/src/container/keys.rs
#	crates/aep/src/container/masks.rs
#	crates/aep/src/container/read.rs
#	crates/aep/src/container/settings.rs
#	crates/aep/src/container/shapes.rs
#	crates/aep/src/container/textdoc.rs
#	crates/aep/src/container/typed.rs
#	crates/aep/src/container/values.rs
#	crates/aep/src/container/version.rs
#	crates/aep/src/container/write.rs
#	crates/aep/src/import/markers.rs
#	crates/aep/tests/it/container.rs
#	crates/aep/tests/it/container/batch3_core.rs
#	crates/aep/tests/it/container/batch3_objects.rs
#	crates/aep/tests/it/container/cases_600.rs
#	crates/aep/tests/it/container/header.rs
#	crates/aep/tests/it/container/helpers.rs
#	crates/aep/tests/it/container/keyframes.rs
#	crates/aep/tests/it/container/layer_switches.rs
#	crates/aep/tests/it/container/matte.rs
#	crates/aep/tests/it/container/robustness.rs
#	crates/aep/tests/it/container/text_markers_footage.rs
#	crates/aep/tests/it/container/typed.rs
#	crates/aep/tests/it/container/values.rs
2026-11-13 02:53:56 +00:00
Dylan M. Taylor 92274ec914 Merge remote-tracking branch 'origin/main' into codex/778-effect-migration
# Conflicts:
#	crates/aep/src/container.rs
#	crates/aep/src/container/footage.rs
#	crates/aep/tests/it/container/robustness.rs
#	crates/model/src/fbx.rs
#	crates/model/src/gltf.rs
#	crates/model/src/obj.rs
2026-11-13 02:26:44 +00:00
Valentin KleinandFredApps a4bde72bca .aep: import twice as fast, faster export (#3302)
* aep: a layer's markers and Source Text are read where they are stored, not searched for (#3276)

A layer's marker list is a direct child of its root tdgp, and its Source Text record
btds a direct child of the ADBE Text Properties group. Markers walked the whole layer
subtree twice per layer (most layers have no marker list, so the walk always ran to the
end), and the three text-document accessors walked it once each.

* aep export: build the prototype template once, sort names with cached keys (#3277)

The template every export clones from is immutable; it is now built by the
first export of the process and kept (about 0.93 MB). The name sort builds
each natural key once instead of on every comparison. Root solids move into
the Solids folder instead of being copied. The two key-list rewrites share one
emit-local helper and match names compare as bytes instead of a decoded String.
Output is byte-identical.

* aep container: size sibling lists, hex and write output once; borrow plain .aepx attributes (#3275)

- read.rs: count the chunk headers of a sequence first and allocate the
  children Vec once (bounded by the input and by the chunk limit).
- aepx.rs: decode_hex sizes its output once; attribute values without
  an entity are borrowed instead of copied through decode_entities.
- typed.rs: PropGroup::get compares match names without building a
  String (same result as the lossy compare, invalid UTF-8 included).
- write.rs: reserve the exact output size up front (try_reserve, so a
  hand-built tree cannot abort on the allocation).
- read.rs, settings.rs, values.rs: use the shared bytes.rs readers.

Output is identical on the corpus (tree, written bytes, import).

* aep container: review fixes (#3275)

- read.rs: reserve with try_reserve_exact, so a raised chunk limit on a
  small target cannot abort before parsing; the capped reservation and a
  deep, retry-heavy nest of broken lists are pinned by tests.
- write.rs: a tree past the 32-bit container size fails with TooLarge
  before anything is serialised.
- aepx.rs: drop an unneeded local and lifetime annotation.

* aep export: note what the concurrency test covers and why a failed template build is not kept (#3277)

* aep: text documents are read without a String per token, defaults built once (#3276)

TextDocument::parse reads a borrowed tree: names, bare words and strings without
escapes point into the body, keys are borrowed when they are /names, numbers parse from
the borrowed bytes, and dictionaries and arrays are moved into vectors of their exact size
when they close. The style and paragraph defaults are built once instead of per document.
parse_text_dict keeps its public TextValue result (the borrowed tree turned owned).

The old owning parser stays as a test-only oracle: a differential test compares both on
40 000 generated bodies (every key form the owned keys canonicalise, non-UTF-8 names,
escapes, unclosed strings, non-finite numbers), every cut of a realistic document and
nesting around the depth limit.

* aep: text-dict test builds its prefix without repeat(1) (#3276)

* aep: an escaped text string drops its byte order mark without a range drain (#3276)

* aep export: one key-time loop, key-list rewrite, tdmn decoder and byte writer for the listed modules (#3278)

In masks, shapes, styles, synth, fxsynth, base, camlight, skeleton,
textanim and tree (effects.rs, plan.rs and emit.rs keep their copies for
the follow-up work):
- New export/keys.rs: the key-time loop (timed_keys), the key-list rewrite
  (fill_key_list, set_key_list, replace_cdat) and the spatial key record,
  shared by masks, shapes and styles.
- One tdmn decoder (synth::tdmn_name, lossy UTF-8 up to the first NUL, as the
  importer reads it) and one encoder (synth::tdmn) instead of the copies in
  masks, styles, shapes, textanim, camlight and skeleton.
- One byte writer: tree::put, which errors. base::bytes, synth::tdb4,
  synth::key_list, fxsynth::pard, masks::shph and the keyed path records write
  through it; their fixed layouts keep the infallible signatures behind
  tree::fixed, which logs, and a test builds every layout and requires no error.
- camlight::put_group uses the shared group split (tree::split_group), which
  errors on a match name without a node instead of stopping quietly.

Export output is byte-identical on the corpus and the synthetic fixtures.

* aep export: pin the key-time loop's report, name the layout in the base::bytes log (#3278)

* aep export: review fixes for #3278

- tree::Fixed replaces tree::fixed: a fixed layout writes every field that
  fits (as the old code did) and keeps the first error; finish() logs it
  once with the layout's description and counts it per thread, so the
  every-layout test cannot race with other tests.
- base::bytes logs one line naming its length and fields.
- masks::build_atom errors when the prototype has no Mask Shape instead of
  dropping the path of every mask quietly.
- log sorted into the dependency list.

* aep: review fixes for #3276 text documents and markers

text_document reads the first btdk in the Source Text record, the documents
text_document_count counts. The old slot checks (same, is_neutral, undecoded_fields)
join the test oracle and are compared on 20 000 generated slot dictionaries; mixed
dictionary/array chains and a document left open at the depth limit join the parser
differential. An escaped string's byte order mark is drained without a copy.

* aep: remove test-only container accessors, share report and time-base code (#3281)

- Remove 35 public container accessors that only tests called (header,
  camera/light, keys, footage, masks, shapes, version) and the dead
  StyleMap::switch_name. Tests now check the importer's own reading of the
  same bytes, or the decoder it uses; three import tests gain the values
  the removed checks covered.
- Import and export reports share one loss entry (Loss<C>) with record,
  total, count and summary; the JSON shape is pinned for both.
- Collector::lose_with builds a detail only below the detail cap (callers
  switch later).
- The export uses the container's TimeBase; one NTSC table serves import
  and export; TimeBase::frames saturates like units instead of returning 0.

* aep: review fixes for #3281: NTSC table beside TimeBase, real-path NTSC round trip, light and camera defaults on import, doc wording

* aep: drop a local check tool committed by mistake (#3281)

* aep export: sort keys as one compact byte string, bounded by the name (#3277)

The cached natural keys were Vecs of (bool, String, u128) segments: a 255-byte
name of alternating letters and digits took about 14 KB, held for every item at
once. Each key is now one byte string whose byte order is the natural order,
at most 5 bytes per name byte. Order and output are unchanged.

* aep container: check bdata digits and size before reserving (#3275)

decode_hex reserved the decoded size before it knew the value decodes,
so a large bdata with a bad digit reserved half its length first. It now
refuses a value larger than a chunk's 32-bit size field (TooLarge), checks
every digit in one vectorisable pass, and only then reserves, with
try_reserve_exact so a value that does not fit in memory is an error
instead of an abort.

* aep export: the every-layout test counts from a baseline, not from zero (#3278)

* aep export: correct the order the natural sort key doc gives for equal numbers (#3277)

* aep: the format notes name the accessors that remain (#3281)

* aep: the format notes name the 1 x 1 mask scale of text and shape layers (#3281)

* aep: read imports through a flat tree index (#3300)

Import reads an .aep through TreeIndex, one depth-first array of chunks that
borrows the file's bytes, instead of building and freeing the owned Chunk tree
(one Vec per list). The parser mirrors read::Parser step for step: the same
chunk count order against max_chunks, the same depth limit and opaque
fallbacks, the same errors. The typed view reads through Node, a handle to a
chunk of either tree, so the accessors work unchanged on both; .aepx keeps its
owned tree. Chunk, parse and write stay for export and editing.

The array starts small and doubles, never past what the input can hold or the
chunk limit allows; a failed allocation is TooLarge, a tree past u32 indices
TooManyChunks.

* aep export: the time remap and Transform keys use the shared key-list and match-name helpers; doc comments for the audit's functions (#3300)

* #3300: import .aepx through the flat index

The .aepx reader no longer builds the owned chunk tree element by
element. It decodes every body (hex, text, alias JSON, numbers) into
one buffer and records the chunks as depth-first entries whose bodies
are ranges of that buffer; TreeIndex::from_drafts turns them into the
index the importer reads, which borrows the buffer. read_aepx converts
that index into the owned tree in one pass.

The tree and the errors are those of the previous reader, which the
tests keep as the reference: generated and damaged documents under
seven limit sets, invalid UTF-8, hostile input, the fixture directory,
and imports through both. Bound tests pin the chunk list to the input
and the limit, and check that dropped content leaves nothing behind.

* #3300: read_aepx keeps its own owned-tree reader

read_aepx, read_aepx_with_limits and parse_any build the owned chunk
tree element by element again, as before the .aepx index, instead of
converting the index (which held the decoded buffer and the owned tree
at the same time). Import keeps the index reader. The tests now
compare the two production readers.

* #3300: test keys that round to the same time

* aep: compare the destination projects in the index-equivalence tests

both() and both_aepx() kept only the ImportResult of each path; they now keep
both destination Projects and assert structural and serialized equality, so a
layer or property value regression cannot pass on equal ids and reports.

---------

Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
2026-11-13 00:54:29 +01:00
Valentin Klein 10a34b854f .aep import/export: text animator Stroke Opacity and Source Text expressions (#3170) (#3305)
* #3170: .aep import of the text animator Stroke Opacity and the Source Text expression

ADBE Text Stroke Opacity maps to the animator's strokeOpacity (percent, as
After Effects shows it). The Source Text expression is imported like any
property's (Utf8 text, tdb4 bytes 119/120); one whose flags give no state
is still reported. New committed fixture text-synthetic.aep, made in After
Effects 2026 with the working space None.

* #3170: .aep export of the Source Text expression and the text animator Stroke Opacity

The Source Text expression is written enabled or disabled as After Effects
stores it (tdb4 bytes 119/120, Utf8 after cdat); a disabled one is no longer
dropped without a report. strokeOpacity is written as ADBE Text Stroke
Opacity with the record After Effects wrote in text-synthetic.aep. A test
exports the fixture and imports it again.

* #3170: doc comments on the functions this change touches
2026-11-13 00:16:37 +01:00
Valentin Klein 8028f46808 .aep import: a test pins the Orientation expression import (#3087) (#3313)
* .aep import: Photoshop layers, placeholder effects, shape direction and Orientation expressions (#3087)

- A footage item showing one Photoshop layer shows that layer (record index, bounds and name
  from its opti) and is named <layer>/<file>; an adjustment layer shows white where it applies
  (opt-in through SourceLayer::adjustment_white, the direct Photoshop import is unchanged).
- Effects without a counterpart arrive as placeholder effects with their parameters, so
  expressions reading them keep working; pseudo effects are no loss, a Dropdown Menu Control
  becomes EffectCraft's. Slider ranges are checked, sizes are bounded.
- The loss prompt shows every detail line the report keeps and how many more there are.
- Shape direction and gradient highlight records in the default state are no loss.
- An expression on Orientation is imported with its value or keys.

* .aep import: report placeholder records and popup defaults that borrowed definitions leave open (#3087)

A value record the borrowed definitions do not name is reported instead of dropped, and a
borrowed popup without a record reports that its default item is not known (the first is used).

* Merge upstream/main into aep-import-bugs-3087
2026-11-13 00:15:49 +01:00
Altynzhan KarybaiandClaude Sonnet 5.5 4035d18132 SVG: support static pattern fills and strokes (#2732)
* fix(svg): support static SVG patterns

* fix(svg): cache patterns per viewport

* fix(svg): share cached pattern content

* fix(svg): harden pattern cache keys

* fix(svg): harden pattern inheritance and caching

* Preserve SVG pattern fallback paint

* Format SVG pattern fallback conversion

* Harden SVG pattern resource bounds

* Fix SVG pattern test formatting

* fix(svg): bound hostile path and raster work

* Bound SVG ellipse expansion

* fix(ui-egui): suppress clippy warnings on wasm target

Rename unused `frame` parameter to `_frame` in draw_inner since
pointer_wrap::finish_native is cfg'd out on wasm32. Guard State::disabled
field with #[cfg(any(not(target_arch = "wasm32"), test))] so it only
exists where it's read (finish_frame on native + tests).

* fix: resolve clippy warnings blocking CI

- Rename unused `frame` parameter to `_frame` in draw_inner (wasm target)
- Guard State::disabled field with cfg(native + test) (wasm target)
- Replace chunks_exact(2) with as_chunks::<2>() in SVG polyline/polygon parsing

* fix(svg): budget raster work by affected region and report refusals

Address review on #2732: the finite-work guards charged the whole canvas,
dropping valid sparse art (8x8 image scaled to 1px on 2049x2049, 2x2 rect
under a 1,028-element clip on 512x512, one of six 2x2 rects sharing a 1x1
pattern on 3840x2160).

- Build fill/stroke/image/clip coverage over the device region only and
  charge that region; remaining whole-canvas passes are charged to a
  render-wide pixel budget (caps are not raised).
- Add try_rasterize/try_rasterize_with returning RasterError; rasterize
  keeps its signature. media layered decode now surfaces refusals.
- Pattern content reached through href inherits styles from the owning
  pattern; device_region falls back to the full canvas on non-finite input.
- Document new items and the limits in the svg README; regression tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 21:12:42 +01:00
ash fbfeed2974 Merge pull request #2875 from arnav-hiwarkar/fix/issue-2869-effect-arrow-disclosure
Fix Effect Controls arrow-key collapse and expand (#2869)
2026-11-12 19:11:21 +05:30
ash d43b33db55 Merge pull request #2876 from arnav-hiwarkar/fix/issue-2817-open-close-path
Fix opening and closing manually drawn paths
2026-11-12 19:09:25 +05:30
arnav-hiwarkar ce11fbf75f #2869: prove Left still collapses once viewer text editing ends 2026-11-12 18:40:39 +05:30
arnav-hiwarkar 5b3a469181 #2817: read the selection in place and name the Closed tests for what they prove 2026-11-12 18:36:20 +05:30
arnav-hiwarkar 6079a2ee06 #2869: disclose effects only while Effect Controls is drawn
Decide visibility from the panels drawn last frame instead of re-deriving the
maximize rule: a maximized frame shows its front tab, which may be Effect
Controls, and hides floating windows. Leave arrows to viewer text editing.
2026-11-12 18:33:27 +05:30
arnav-hiwarkar 97fc3eca54 #2869: test Effect Controls arrows only where the panel is on screen
Replace the floating-beside-maximized case (a maximized frame hides floating
windows too) with hidden and on-screen cases, wait for the rename field the
way the other rename tests do, and keep viewer text editing's arrows.
2026-11-12 18:33:27 +05:30
arnav-hiwarkar 0ccfd669fb #2817: Closed fits short tangent lists like mask.setClosed 2026-11-12 18:24:01 +05:30
arnav-hiwarkar c9a5372e95 Merge remote-tracking branch 'upstream/main' into fix/issue-2869-effect-arrow-disclosure 2026-11-12 18:22:25 +05:30
arnav-hiwarkar d7011a36b8 #2817: Closed falls back to the layer's paths when the selection names none
Selecting a non-path property (Position) or a stale selection disabled Closed, unlike
RotoBezier and the other Mask and Shape Path commands, which fall back to every mask of
the selected layers. A selected mask property (Feather, Opacity, Expansion) now selects
its mask. A selected locked mask still counts as a match, so it never falls back to its
unlocked siblings.
2026-11-12 18:22:21 +05:30
arnav-hiwarkar 36aa437d34 #2817: pin RotoBezier tangents, mixed checkmark and static paths for Closed 2026-11-12 18:20:05 +05:30
arnav-hiwarkar 37b0ea1145 Merge upstream main into fix/issue-2817-open-close-path
# Conflicts:
#	crates/engine/src/cmd/mask.rs
2026-11-12 18:14:02 +05:30
Dylan M. Taylor 63fda85a51 3D models: bound and share decoded textures, share skin matrices, index OBJ materials
* fix(model): share decoded textures and skin matrices, bound texture pixels, index OBJ materials

Texture::decode expanded any image to float RGBA without a size check. Repeated mtllib lines
reparsed the library and decoded its textures again; glTF textures with a non-default sampler
each copied their image's pixels; every instance of a skin built its own joint matrices. OBJ
usemtl and faces scanned the growing material and primitive lists, and contour grouping kept
every ancestor of every contour.

* address review: keep largest contours and dedupe mtllib by set

* address review: contour depth counts containers, glTF wrap copies and FBX textures share the import pixel budget

* address review: holes go to the innermost outline; glTF wrap variants keyed by decoded image

* address review: create every outline slot before attaching holes

* Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting)

* Models: left-out textures are import warnings; the import texture budget is 2^29 pixels

* Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting)

* Import: a model's left-out parts reach the user as an import warning

* FBX textures move to #2955 (it reworks the same code)
2026-11-12 07:00:15 -05:00
ash b9da77c63d Merge pull request #3571 from arnav-hiwarkar/fix/effect-ranges-easy-batch
Effects: 18 parameters take After Effects' valid ranges (Levels IC, 4-Color Gradient, Change Color, Noise HLS, Grid, Venetian Blinds, Radial Wipe, Scatter, Beam, Transform, Turbulent Displace)
2026-11-12 17:19:57 +05:30
ash bde8bea2bb Merge pull request #3564 from arnav-hiwarkar/fix/3535-analysis-inputs
Warp Stabilizer, 3D Camera Tracker: analysis keys cover masks, effects above and precomp contents (#3535)
2026-11-12 17:19:35 +05:30
ash 6ea9476ee4 Merge pull request #3560 from arnav-hiwarkar/fix/3538-ocio-file-reload
OCIO File Transform and custom OCIO configs follow their files on disk (#3538)
2026-11-12 17:08:11 +05:30
ash 46eb4d104d Merge pull request #3561 from arnav-hiwarkar/fix/3539-viewer-reload
Viewers show footage reloaded with unchanged metadata (#3539)
2026-11-12 17:03:17 +05:30
Dylan M. Taylor 740afc2869 FBX: records stay within their parent; textures, materials and links are indexed
* fix(model): FBX records stay within their parent, and textures, materials and links are indexed

A binary child record could end past its parent: the parent's end was then restored and the
child's tail parsed again. A failed array decompression printed the decompressed bytes in the
error. Texture objects aliasing one embedded Video or file each decoded and kept their own
pixels, outside the import budget. Each polygon scanned the primitive groups for its material,
and each object scanned every connection for its children.

* address review: FBX textures: key image cache by the source used, charge decoded size before decoding, propagate budget errors

* Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting)

* Import: a model's left-out parts reach the user as an import warning

* FBX: textures past the limits are left out with an import warning, not a failed import; 2^29-pixel allowance
2026-11-12 06:30:20 -05:00
Dylan M. Taylor cb5bc81d28 Automation: refuse Watch Folder and Edit Original while file access is confined
* fix(automation): refuse Watch Folder and Edit Original while file access is confined
2026-11-12 06:30:03 -05:00
Dylan M. Taylor 075aa03ebe Render: memoized audio detection, bounded stretch, preroll, waveform, cache entries, 3D bounds, solids and selector time
* Render: memoized audio detection, bounded stretch, preroll, waveform, cache entries, 3D bounds, solids and selector time

comp_has_audio walked a precomp again for every layer using it; each comp
is answered once. A tiny stored stretch made one audio block fetch a vast
source range; playback speed is capped at 100x. Effect preroll is capped at
10 s. Waveforms stop where the audio ends and summarise at most a day.
Disk cache entries are decompressed only when their stated size matches
what their dimensions need (and at most 1 GiB). Advanced 3D triangle bounds
are clamped on both sides before narrowing. Solids larger than 16 Mpx are
made at a lower scale. A camera-facing layer's parent recursion is bounded.
A layer's Expression Selectors share one time allowance. A disk cache
folder under the temp folder is made private and may not be a link.

* address review: audio detection by breadth-first search, private cache folder resolved before the temp check, oversized layers not written, solid cap after rounding, selector static amount, viewport i32 clamp

* address review: private-folder check skips the temp folder itself; override value in expression fallback; assert alias cache opens

* address review: refuse the shared temp folder itself as a disk cache folder

* address review: shared counted selector budget, 30 s preroll, cache hardening (shared temp roots, matte discard, exact LZ4 length, render version 7), orient nesting guard
2026-11-12 06:29:56 -05:00
Dylan M. Taylor a287044644 Media: check still, EXR channel, GIF and WebP canvas sizes before allocating
* fix(media): check still, EXR channel, GIF and WebP canvas sizes before allocating

* address review: GIF frame allocation limit, lower wasm32 still limit, no EXR fallback after a size-limit error

* address review: the 2^25-pixel still limit also applies on 32-bit native targets

* Media: raise the still pixel limit to 2^28 (16384 x 16384) on 64-bit targets
2026-11-12 06:29:46 -05:00
Dylan M. Taylor 4d6036265b Convert Expression to Keyframes: refuse layers of more than 2^18 frames
* fix(engine): refuse to convert an expression on a layer of more than 2^18 frames

* address review: refuse a non-positive frame duration; count a partial last frame; check every selected layer before converting

* Convert Expression to Keyframes: cap at 2^20 frames (a 3-hour 60 fps comp is 648,000)
2026-11-12 06:29:38 -05:00
Dylan M. Taylor 2b30f53e7a Export: Skip Existing keeps a frame already in the overflow folder
* Export: Skip Existing keeps a frame already in the overflow folder

* Export: Skip Existing checks every overflow folder before a frame is placed
2026-11-12 06:29:31 -05:00
Dylan M. Taylor bdc56ca75e Timewarp: bound the motion search's total work by coarsening the vector grid
* Timewarp: bound the motion search's total comparisons by coarsening the vector grid

* Timewarp: coarsen within the frame, keep the pyramid depth and the window inside the frame
2026-11-12 06:29:25 -05:00
Dylan M. Taylor 9f93ac03fa Text layout: a wrapped line finds its glyphs and end caret by bisection
* Text layout: a wrapped line finds its glyphs and end caret by bisection

* Fix merge of layout tests
2026-11-12 06:29:20 -05:00
Dylan M. Taylor 6e1dd08147 Import: a 3D model's left-out textures reach the user as an import warning
* Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting)

* Import: a model's left-out parts reach the user as an import warning
2026-11-12 06:29:14 -05:00
arnav-hiwarkar f88d0d0a8d #3487, #3488, #3489, #3490, #3491: Levels (Individual Controls): skip the overflowing pow with a branch
WGSL's select evaluates both of its arguments, so the previous commit still
ran the overflowing pow and only discarded it. An explicit branch now skips
the power for a curve past the bound.
2026-11-12 16:37:09 +05:30
arnav-hiwarkar 047b912fc5 #3487, #3488, #3489, #3490, #3491: Levels (Individual Controls): GPU saturates the curve before pow can overflow
WGSL leaves the result of an overflowing pow indeterminate, so clamping
after it did not guarantee the CPU's 1e30 bound on every backend. A curve
past the bound (log2|t| / gamma > 99.7) now saturates to +-1e30 without
calling pow; between 1e30 and 2^99.7 the clamp gives the same 1e30, so the
GPU matches the CPU everywhere.
2026-11-12 16:35:08 +05:30
arnav-hiwarkar 28c63ce728 #3535: the analysis key leaves out the effects above while the Effects switch is off
The renderer skips the whole effect stack then, so editing those effects
doesn't change the analysed frames. Roto Brush Input Keys keep their text.
2026-11-12 16:30:24 +05:30
arnav-hiwarkar c0387ea18a #3470, #3472, #3473, #3474, #3475, #3476, #3477, #3480, #3483, #3484, #3487-#3491: test the ends of the new ranges on the GPU
CPU / GPU comparisons at the new ends for Venetian Blinds (width 1, width
and feather 32000), Radial Wipe (feather 32000), Grid (border 2000),
Scatter (1000), Beam (4000), Change Color (1710 degrees) and Turbulent
Displace (seed 100000). The comparison counts infinity on both sides as
equal, so Levels (Individual Controls) gets a direct check that the GPU's
output is finite at the ends of its ranges. The Noise HLS test asserts the
spec's ranges, so it fails without them.
2026-11-12 16:24:17 +05:30
arnav-hiwarkar acf60eeaf3 #3487, #3488, #3489, #3490, #3491: Levels (Individual Controls): bound the curve only where it would overflow
The 1e6 bound also capped curves that were finite before (gamma 0.1 on an
HDR value 4 x Input White is 4^10, about 1.05e6). The bound is now 1e30:
1e30 times the widest output range (2e4) is still finite, and every curve
below it renders as before. A test pins 4^10.
2026-11-12 16:24:17 +05:30
arnav-hiwarkar 22e66e3d97 Merge remote-tracking branch 'upstream/main' into fix/effect-ranges-easy-batch 2026-11-12 16:15:01 +05:30
arnav-hiwarkar c41d889686 #3470, #3471, #3472, #3473, #3474, #3475, #3476, #3477, #3478, #3479, #3480, #3483, #3484, #3487, #3488, #3489, #3490, #3491: docs: effect ranges in the parity revision history 2026-11-12 16:14:55 +05:30
arnav-hiwarkar b0133cf862 Merge remote-tracking branch 'upstream/main' into fix/3535-analysis-inputs 2026-11-12 16:14:53 +05:30
ash 44aa7302f8 Merge pull request #3550 from arnav-hiwarkar/fix/3534-3536-analysis-signatures
Warp Stabilizer, 3D Camera Tracker, Roto Brush: one explicit analysis key; offline footage keeps analyses (#3534, #3536)
2026-11-12 16:11:59 +05:30
arnav-hiwarkar 64c28b56d7 #3535: docs: which key covers what, why markers and audio are or aren't keyed 2026-11-12 15:47:50 +05:30
arnav-hiwarkar 98d82ae761 #3470: Turbulent Displace: Random Seed takes After Effects' range
Random Seed takes 0..100000, After Effects' recorded valid range (and what
the other effects' Random Seed already takes). The CPU and GPU hash it the
same way with wrapping u32 arithmetic, so any seed costs the same.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar 74bcb30540 #3471: Transform: Scale Height and Scale Width take After Effects' range
Scale Height and Scale Width take -30000..30000 %, After Effects' recorded
valid range in the same units. The CPU and GPU share the matrix
(transform_matrix_px) and the buffer growth, which is capped at 16 Mpx
(transform_grow; the CPU test already renders 100000 %), so larger scales
stay bounded.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar 3bd7de6ad8 #3475: Beam: Starting and Ending Thickness take After Effects' range
Starting and Ending Thickness take 0..4000, After Effects' recorded valid
range in the same units (pixels). The CPU and GPU renderers draw the beam
per pixel from its distance to the segment, within the layer's buffer, so
a thicker beam costs the same.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar c4cd9f1c2e #3477: Scatter: Scatter Amount takes After Effects' range
Scatter Amount takes 0..1000, After Effects' recorded valid range in the
same units (pixels); the slider stays 0..127. The CPU and GPU renderers move
each pixel by one random offset of up to the amount, so a larger amount
costs the same; samples outside the layer are transparent.
Default and slider are unchanged.
2026-11-12 15:47:15 +05:30
arnav-hiwarkar a08240155c #3474: Radial Wipe: Feather takes After Effects' range
Feather takes 0..32000, After Effects' recorded valid range in the same
units. The CPU and GPU renderers evaluate the feather per pixel in closed
form (a Gaussian of the distance to the wedge, radial_wipe_alpha), so a
wide feather costs the same and stays finite.
Default and slider are unchanged.
2026-11-12 15:47:14 +05:30