Port behavior and regression coverage into current modules; preserve current upstream behavior.
Current modular compositors already implement spatial Dissolve for adjustments and finished effects. Preserve equivalent implementations and newer Fill/HDR behavior, append approved missing CPU/GPU regression coverage.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Current CPU and GPU already implement binary clipped Dissolve at document coordinates. Preserve current equivalent implementation and stronger negative-origin/depth regression; retain original named regression and added GPU parity coverage.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Integrated independent exact shape paints and atomic pattern clipboard snapshots into current modules, preserving current soft-mask coverage, shape cache keys and gradient stop caps. Shared bounded COW pattern sampling now retains minification filtering; shapes/clipboards resolve lazy catalog entries to full pixels. Preserved CPU/GPU/depth/model regression matrices; also corrected missing glow-test enum imports found by the 80-head check.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported mappings and smart Save into current modules, preserving bounded authorized playback; migrated legacy PAT by committing validated per-pattern assets before atomic authoritative index; preserved same-ID replacements and builtin edits; added temporary-store interruption, corruption, duplicate ID/name and restart regression coverage. Current all-target compilation passed before final ownership guard refinement; focused tests pending.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Port responsive three-column Layer Style with owned right-side actions, preserve saved defaults/Blend If and current Curves/modal routing, and keep all original layout/Cancel tests registered.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Judge Blend If using the adjustment result before Opacity and masks in CPU/GPU, with the original regression in the current test module.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Combine Fill-aware adjustment blending with stable HDR coverage and add cross-feature CPU/GPU regression matrix.
A Pass Through group composites its children straight into the
backdrop, so an Invert inside it changed the layer beneath even when
the group had Blend If. Photoshop renders such a group isolated, as it
does below 100% Fill: the backdrop stays (64,128,192) under a group
holding an Invert with Red Underlying white at 110. The CPU and GPU
pass-through shortcuts are now skipped for a group with Blend If.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blend If on an adjustment layer judged This Layer by the composite after
Opacity and the mask had mixed the result into the backdrop. Photoshop
judges the adjustment's result itself: Invert at 40% over (64,128,192)
with Red This Layer black 150 keeps the effect, (115,128,140), as
Invert's red 191 passes 150. The CPU and the GPU now apply the
adjustment to the backdrop and judge that.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests' setup returned None, without a message, when the device
request or the pipeline build failed, so every test passed early: with
a type error appended to camera_raw.wgsl all 13 tests still passed on a
machine with a GPU. A pipeline that doesn't build on a supported adapter
now fails the tests, a missing device skips with a message (and fails
with WGPU_BACKEND set, as in parity.rs), and a naga validation of the
shader fails on runners without a GPU too.
Found by CodeRabbit on #9386.
Co-authored-by: p0rps <134095787+fjamesprice@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CodeRabbit on #9512 (inline 4371923258): the planner's clipped path
(atop_any) took the special eight's separate Fill too, while compose's
clipped branch (composite_atop) uses opacity × Fill as coverage, so a
clipped adjustment at partial Fill rendered differently on the GPU (53
levels off at 30% Fill). The clipped path now uses combined coverage.
Test: special_eight_fill_on_adjustment_layers_matches_cpu (adds a clipped
adjustment for each mode and Fill)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Below 100% Fill, Photoshop blends Color Burn, Linear Burn, Color Dodge,
Linear Dodge, Vivid Light, Linear Light, Hard Mix and Difference with
the source pulled toward the mode's neutral colour, at Opacity. Pixel
layers already did (psblend::composite_fill); adjustment layers used
Fill as coverage on the CPU (blend_back) and the GPU (AdjMix), so a
40% Fill Color Dodge gave (90,179,217) instead of (80,160,240).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Background redraw: key kept composites on the text gamma, trim them every frame (#8520)
Two gaps in the kept background redraws:
- Color Settings > "Blend Text Colors Using Gamma" is an application
setting that type layers composite with, but it wasn't part of the key.
After toggling it, going back to a kept state (an Alt-click solo and
again, undo/redo) uploaded type edges drawn with the old gamma. The key
now holds the gamma, and a redraw that ran while the gamma changed
(even if changed back, and also while another document was shown)
isn't kept. Every running redraw is checked every frame; two changes
within one frame aren't seen.
- Kept composites were trimmed only when the next deferred redraw
started. A state History dropped (or purged) kept its texels, and
lowering Memory Usage or a small edit that grew a kept document didn't
release them. Each frame now drops dead entries, and the cap is taken
again when Memory Usage or the revision of any kept document changes.
Tests (each fails without its part of the fix):
a_state_drawn_before_the_text_gamma_changed_is_composited_again,
a_redraw_across_a_text_gamma_change_is_not_kept,
a_redraw_across_a_text_gamma_change_and_back_is_not_kept,
a_hidden_documents_redraw_across_a_text_gamma_change_and_back_is_not_kept,
kept_composites_go_when_memory_usage_drops_between_redraws,
kept_composites_go_when_a_small_edit_grows_the_document_past_the_cap,
an_older_kept_composite_goes_when_its_document_grows_past_the_cap,
a_kept_state_history_purged_goes_at_the_next_frame.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* GPU compositor: test the layer page out-of-memory paths, keep GPU timing after one (#8521)
The PR's tests passed with its fix reverted: main's after-cell evict()
already meets every assert of the zero-budget test, so the out-of-memory
fallback had no test. A test hook (Compositor::fail_layer_allocs) now makes
the next n page textures fail as out of memory, and a parity test covers
the in-place retry, the submit-and-retry, the CPU fallback with a healthy
device, and the next refresh back on the GPU. Each of those fails when its
part of the fix is removed.
A retry that failed after its submit also switched GPU timing off for the
rest of the session: the submit marks the measurement flushed, and
cancelling a flushed measurement stops timing for good. The retry waits
for the GPU, so it now clears that mark (GpuTimer::drained);
an_out_of_memory_refresh_does_not_stop_gpu_timing fails without it.
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Address CodeRabbit review: serialise the OOM timing test with GPU_LOCK
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Michael Higgins <55243596+Higgs32584@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Loading the Intel Vulkan driver on Windows (igvk64.dll) crashes the process
with STATUS_ACCESS_VIOLATION (#4). The app leaves Vulkan out on Intel, but
the tests use wgpu's defaults, so on such a machine `cargo test` dies in the
first GPU test (ui-egui's background_redraw, adjust_preview_gpu, ...).
WGPU_BACKEND=dx12 avoids it for the kittest harnesses, but seven tests and
four benches built their instance with wgpu::Instance::default(), which takes
every backend and ignores the environment. They now use
InstanceDescriptor::new_without_display_handle_from_env(): the same defaults
with WGPU_BACKEND and friends applied, so nothing changes where it is unset.
Document the workaround in docs/development.md and the testkit driver notes.
* fix(curves): 32-bit Curves continue past 0..1 like Photoshop
In a 32-bit RGB document an edited Curves clipped every sample to the
curve's table, so HDR values above 1 (and negative ones) were flattened.
Photoshop extends the curve beyond its end points along its end segments:
the straight curve [[0,0],[255,128]] maps -0.5, 0.5 and 2 to
-0.250980407, 0.250980407 and 1.003921628 (measured in Photoshop 27.11 on
the issue). The CPU compositor, Image > Adjustments and the GPU kernel
now do the same, with the end slopes measured over 16 table steps.
8/16-bit documents and unedited curves are unchanged.
Fixes#7157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(curves): extend 32-bit curves in RGB documents only
The curve's space is not the document's: a grayscale, CMYK or Lab
32-bit document with an RGB-space curve keeps the table's ends. The
document mode now reaches the decision on the CPU (apply_in, the
compositor) and the GPU (adjustment_program).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The grey was clamped to 0..1 at every depth, so a 32-bit (2, 2, 2) came
out as (1, 1, 1). In 32-bit documents it now clamps to 0..16, as
Photoshop 27.11 does; 8 and 16-bit still clip at 1. CPU and GPU both.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- crates/ui-egui/src/layer_style.rs: values_of keeps the PR's method/smoothness keys and source-stop ends, and main's (#7072) unconditional From/To and geometry fields.
- crates/compose/src/cmyk_tests.rs (no textual conflict): add the PR's new `interpolation: None` field to main's CMYK test GradientOverlay literal.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The parallel build skipped a non-map pipeline whose creation raised an
error, so with new_with_format (errors logged, compositor still returned)
its key was missing from kit.pipelines and the first draw panicked on the
`self.kit.pipelines[&(kernel, acc)]` index. As before the PR, a failed
non-map pipeline now keeps its invalid handle (draws report GPU errors);
only a failed effect-map pipeline is skipped (all maps go to the CPU).
Test: a_compositor_with_failed_pipelines_draws_without_panicking builds
new_with_format(Rgb9e5Ufloat) on a device with an uncaptured-error
handler and renders: it panicked at the index without the fix. The PR's
test moves into tests/it (one test binary) and takes GPU_LOCK.
Co-authored-by: 00xJS <56056764+00xJS@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The PR gives an Outside stroke no share beneath fully covered pixels on the
CPU, but no test failed without it: under a Normal layer the opaque pixel hides
the stroke anyway. It shows with Blend Interior Effects as Group on a Multiply
layer: main multiplied the red interior with the blue stroke beneath it (black).
New test outside_stroke_under_multiply_layer_leaves_interior_to_backdrop
(raster and filled shape) fails on main's effects.rs, passes with the PR.
The GPU compositor mirrored the old math (outline_share 1 at full coverage, and
1 under an opaque pixel layer); compose.wgsl now matches the CPU. Blend Interior
Effects as Group itself renders on the CPU, so on the GPU this is consistency;
a parity case pins non-Normal layers with an Outside stroke.
Co-authored-by: seotarek <30214297+seotarek@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- crates/gpu/tests/camera_raw.rs was its own test binary taking
testkit's gpu_lock; since #8644 the crate's integration tests are one
binary (tests/it) sharing crate::GPU_LOCK, so in that binary it would
open a device next to the other modules. Moved into tests/it with
GPU_LOCK; the testkit dev-dependency goes.
- Default (neutral) settings uploaded, rendered and read back the proxy
although the CPU develop returns at once. CameraRawGpu::develop now
leaves them to the CPU; neutral_settings_skip_the_gpu_round_trip fails
without it (backend "gpu").
- docs/hardware-parity.md: the CPU range was one backend's (21-28 ms);
measured 11-16 ms on an RTX 5070 Ti, so 11-28 ms, machine-dependent.
Co-authored-by: p0rps <134095787+fjamesprice@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- gpu: the test modules share one GPU_LOCK, and timing and vector_mask
take it too, so one process drives one wgpu device at a time.
- ui-egui: the wgpu tests in pattern_preview_canvas, adjust_preview_gpu,
marquee_autoscroll and layout_perf take gpu_lock().
- affinity compound_limits: the child process selects its test by the
module path (compound_limits::<name>); with --exact, the bare name
matches no test and the child passes without running the check.
- xtask scorecard reads crates/io/tests/it/corpus.rs.
- Docs and comments name the new test paths and the --test it filters.
Every tests/*.rs file is its own executable that links the whole crate
graph. Move each crate's test files into tests/it/ as modules of one
tests/it/main.rs; helper modules under tests/common move with them.
These keep their own binary (own process):
- engine panic_hunt: a process-wide panic hook counts every panic.
- engine text_gamma: changes the process-wide text gamma.
- engine smart_filter_stages, mask_cache_purge: assert on global caches.
- codecs pnm_peak_memory: reads the process's peak RSS.
- photocraft mac_menu_appkit: harness = false.