Commit Graph
209 Commits
Author SHA1 Message Date
David Mann 8c04b175ec Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/automation/src/rpc.rs
#	crates/automation/src/server.rs
#	crates/gpu/src/health.rs
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/panels.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-24 00:23:15 +02:00
FinnNkandClaude Opus 5.5 89d127d259 fix(curves): 32-bit Curves continue past 0..1 like Photoshop (#8782)
* 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>
2026-10-23 17:40:56 -04:00
HEOJUNFOandClaude Opus 5.5 44db720b22 Black & White: keep 32-bit superwhites (#8537) (#8748)
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>
2026-10-23 17:40:00 -04:00
FinnNkandClaude Opus 5.5 c06dd9c27c fix(gpu): rebuild the GPU canvas after an out-of-memory error (#8774)
* fix(gpu): rebuild the GPU canvas after an out-of-memory error

An uncaptured wgpu error of any kind dropped the GPU canvas for the rest
of the session, so one recoverable Out of Memory on a working device
left every redraw on the CPU until Retry GPU. Dropping the canvas frees
its memory, so after an out-of-memory error a new GPU canvas is now
built at once on the same device, with a fresh health flag and no
dialog, up to three times a session; past that the CPU renderer and the
Retry GPU dialog stay as before. Validation errors and device losses
are unchanged.

Fixes #6564

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

* fix(gpu): only wgpu's out-of-memory error is retried

The fault is classified by wgpu's Error::OutOfMemory variant and recorded
as exactly "Out of Memory", so a validation error that mentions memory
keeps its fallback.

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

* fix(gpu): a later out-of-memory error replaces an earlier error

So the canvas still rebuilds itself when memory ran out after a
validation error in the same frame; a device loss still wins.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:39:50 -04:00
Gert Pellin - GPE 10a20743ea Soft Light: dark backdrops follow Photoshop's W3C curve (#8804) (#8817)
* fix(blend): Soft Light uses the W3C curve over dark backdrops (#8804)

Photoshop's Soft Light moves the backdrop towards D(cb) in the upper half,
and D(cb) is the cubic ((16·cb − 12)·cb + 4)·cb below a quarter, not
sqrt(cb). With sqrt the Soft Light cell of psd-tools rgb-blend-modes.psd
was up to 20 levels too light over the dark rainbow; with the cubic it is
within 4 of Photoshop's merged image.

Same change in compose.wgsl so the GPU compositor stays at CPU parity.
Krita's own soft_light (the sqrt curve) now opens as the approximation and
svg:soft-light / krita:soft_light_svg as the exact match, without warning.

Fixes #8804

* compose: the see-through Soft Light test expects the W3C curve over a dark backdrop (#8804)

The #6232 test computed Photoshop's Soft Light of yellow as sqrt(b) for red and
green. Its backdrop #04170F is below a quarter in both channels, where the W3C
curve (Photoshop's, measured in #8804) uses ((16b - 12)b + 4)b instead.
2026-10-23 17:39:40 -04:00
kotulskyandkotulsky 8d1926433f Reuse filled-shape effect maps while moving styled groups (#8993)
* perf(gpu): reuse filled-shape effect maps during translation

Hash filled-shape outlines in local coordinates so whole-pixel movement can use the existing effect-map translation reuse path. Keep its region-size and exact coverage checks, and invalidate the key when the outline deforms.

Add a shape-key regression and a synchronized styled-shape group benchmark with fresh-render parity verification. On RTX 4070 Laptop, the 1600x1200 fixture improves from 61.336 to 33.120 ms median; a 24 MP full-canvas fixture improves from 223.018 to 184.289 ms. Warm frames rebuild zero effect programs, and the quick fixture matches fresh rendering exactly.

* fix(gpu): stabilize fractional shape effect fingerprints

Quantize only origin-normalized fingerprint coordinates to a 1/65536 pixel grid and canonicalize signed zero. Preserve the render path and exact coverage guard.

Cover fractional anchors and handles, positive and negative integer translations and handle deformation. Exercise fractional origins in the benchmark and document its entry points. All 40 GPU tests, Clippy and WASM checks pass; reused output matches a fresh compositor exactly.

---------

Co-authored-by: kotulsky <7805437+kotulsky@users.noreply.github.com>
2026-10-23 17:37:56 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
Dylan M. Taylor a09c0ce85f test(gpu): match the planning error instead of err().expect() 2026-10-23 15:21:47 -04:00
Dylan M. Taylor b20ccefffb security(gpu,ui-egui): bound clipped pass-through planning; sample pattern swatch pixels directly 2026-10-23 14:41:54 -04:00
David Mann 159728aaa0 Merge PR #8118: GPU compositor: build its pipelines in parallel; DX12 startup 13.2 s -> 7.3 s (part of #6419) 2026-10-22 23:40:19 +02:00
David Mann a79d8cb9f6 Merge PR #5937: fix(compose): prevent outside stroke from bleeding into opaque character interior (#5861) 2026-10-22 23:40:19 +02:00
David Mann 7f68d4e06a Merge PR #5370: fix(gpu): catch validation errors in watched compositor work 2026-10-22 23:40:13 +02:00
David Mann 837242536b Merge PR #7840: gpu, docs: fix timing test skip guard and document Windows ARM64 build on RTX Spark 2026-10-22 23:40:12 +02:00
David MannandClaude Opus 5.5 6c7e920d5a Merge PR #7335: Gradient Overlay: add Method selector and retain editable source stops
- 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>
2026-10-22 23:39:14 +02:00
David Mann e7c53231a5 Merge PR #8547: perf(gpu): layer pages that just miss the budget borrow the effect-map share, P60/P61 10× faster on integrated GPUs 2026-10-22 23:38:40 +02:00
3175a3c4cb Integration: keep failed compositor pipelines in the map (#8118)
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>
2026-10-22 21:42:40 +02:00
2e29033b50 Integration: no Outside stroke share under opaque pixels on the GPU too (#5937)
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>
2026-10-22 21:41:01 +02:00
David Mann 87905d448f Integration: merge main (#8118) 2026-10-22 21:25:47 +02:00
aiongg ffa400c754 Keep tests correct and docs current with one test binary per crate
- 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.
2026-10-22 16:55:34 +00:00
aiongg a6428d3dfb Build each crate's integration tests as one binary
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.
2026-10-22 16:55:34 +00:00
b526378de4 Integration: 32-bit Hue/Saturation with Photoshop's saturation factor, finite at lightness 1 (#5781)
The 32-bit branch cut the slider to floor(|s| * 255) / 255, which only matches
Photoshop near 25 and 50 %: measured at every whole percent from -100 to +100
on a 32-bit document, Photoshop lowers by 1 - floor(|s| * 256) / 256 and raises
by 255 / (255 - floor(s * 254)) taken to 1/256 and capped at 32767/256 (201 of
201 exact). +100 gave 512x where Photoshop gives 128x, +90 9.8x for 9.44x.

It also kept the HSL saturation unclipped, which is inf at HSL lightness 1 and
0 and negative above 1: (2, 0, 0), red at +1 stop, turned NaN with any edit,
Hue +40 alone included, where Photoshop gives (2, 1.333, 0). The 32-bit branch
now scales the chroma around the lightness instead (same result in gamut),
on the CPU and in the shader, in whole numbers on both. Colorize clamps its
saturation before the 32-bit scaling, so a PSD's out-of-range value renders
the same on the GPU.

Tests (fail without this): thirty_two_bit_saturation_steps_match_photoshop,
thirty_two_bit_hue_saturation_keeps_bright_colours, the GPU parity
hdr_hue_saturation_bright_colours. Photoshop-oracle floor 188 -> 190 of 258,
measured after merging main.

Co-authored-by: Timothy Daly <100327160+timothydaly1@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:42:03 +00:00
aiongg 576ba0e02b Integration: track t12
# Conflicts:
#	contributors/people.toml
#	crates/compose/src/lib.rs
#	crates/compose/src/tests.rs
#	crates/engine/src/print_cmds.rs
#	crates/engine/src/print_cmds/tests.rs
#	docs/target-app-parity.md
2026-10-22 15:38:33 +00:00
aiongg 71991b0a03 Integration: track t10
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/prefs.rs
#	crates/ui-egui/src/layer_style/contour.rs
#	docs/ui-parity.md
2026-10-22 15:38:28 +00:00
aiongg f72c3c2187 Integration: track t08
# Conflicts:
#	crates/ui-egui/src/camera_raw_ui.rs
#	crates/ui-egui/src/jobs_ui.rs
#	crates/ui-egui/src/widgets.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-22 15:38:23 +00:00
aiongg 446b74a8de Integration: track t07
# Conflicts:
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	crates/ui-egui/src/layer_shortcut_tests.rs
2026-10-22 15:38:21 +00:00
aiongg 0e80156397 Integration: track t16
Resolutions: CLI usage, flag lists and flag parsing carry both --tiff-compression (t15) and
--jpeg-format (t16); file.saveACopy, batch, imageProcessor and layersToFiles param specs carry
both tiffCompression and jpegFormat; file_cmds keeps tiff_compression_param and
jpeg_format_param; atn_cmds keeps flag_set/applies_at_canvas_bounds (t15) and UNHOLDABLE (t16),
edit.transform uses the pixel_target guard (t15) with layer_ref(p)? (t16); ui-egui open paths
build ImportOptions (t16) and apply raw_defaults (t15); docs headers/rows unioned; atn tests keep
both sides; tr.tsv "Keep Using CPU" kept once.
2026-10-22 15:38:13 +00:00
aiongg a7c80a83d2 Merge PR #6791: Intel Iris Xe on DX12: no more red blocks in brush previews and tool icons (#6790) 2026-10-22 15:37:46 +00:00
aiongg dc0094fa78 Merge PR #5781: 32-bit Hue/Saturation as the reference app does it: unclipped saturation and Colorize (two rgb32 oracles pass)
crates/io/tests/corpus.rs: kept main's pass_floor 188 (the PR's 187 would lower it; not re-measured here)
docs/file-format-parity.md: kept main's header and PSD/PSB rows; added the PR's change-log row without its floor figure
docs/gaps.md: kept main's header and evidence line; added the PR's change-log row without its floor figure
docs/scorecard.md: ours
2026-10-22 15:37:35 +00:00
Nicolas FalesyandClaude Opus 5.5 18eb34b012 fix(gpu): an exact resident budget returns the loan and lends nothing (CodeRabbit)
set_resident_budget now gives the borrowed bytes back to the effect-map
share before setting the exact budget, so memory_budget() stays the total,
and fit_layers lends nothing until the next set_memory_budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CD2ipSyvx1tYwnHJZyDs59
2026-10-22 09:48:43 -04:00
Nicolas FalesyandClaude Opus 5.5 336c419f2f perf(gpu): layer pages that just miss borrow the effect-map share (P60/P61)
On an integrated GPU, 150 full-canvas 6 MP layers needed 3800 MB of
pages against a 3607 MB page share (the rest of the 4587 MB budget is
reserved for effect maps and lower-stack chunks), so every opacity or
visibility change went to the CPU compositor. Compositor::fit_layers lends
the pages the shortfall from the effect-map share when that covers it; an
evicted effect map is rebuilt by the next refresh that draws it. The
lower-stack share is never lent, and the loan holds across the budget set
before every refresh.

Arc B390: P60 p50 707-768 -> 66-70 ms, P61 704-766 -> 70 ms, peak RSS
7.0 -> 4.5 GB.

Part of #5973.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CD2ipSyvx1tYwnHJZyDs59
2026-10-22 09:18:02 -04:00
Yonghyuk Choi dc029b93c5 Merge main into Gradient Overlay method branch 2026-10-22 12:38:20 +09:00
aiongg d8865fc682 Merge PR #7638: Map the bevel's shading through its gloss contour, not its light (#7360)
crates/compose/src/effects.rs: kept main's par_units row loop in bevel_maps and ported the PR's change into it: k maps shade and the flat-face level through the gloss contour (lut_at), and the closure returns (hi, sh) without the apply_contour pair.
2026-10-22 02:27:06 +00:00
aiongg b43ba11beb Merge PR #8109: Compose: Color Balance works out each channel's curve once, not per pixel 2026-10-22 02:23:07 +00:00
aiongg 2b3af0f815 Merge PR #3193: fix(compose): resolve Dissolve coverage across adjustment, effects, a…
Conflicts:
crates/compose/src/tests.rs: appended tests, kept both
crates/gpu/tests/parity.rs: appended tests, kept both
2026-10-22 02:23:06 +00:00
aiongg ec663f4981 Merge PR #7204: Layer effects in Dissolve mode speckle, as in the reference app (#6995) 2026-10-22 02:22:05 +00:00
aiongg 1bc45862cb Merge PR #6813: A layer that doesn't fit in GPU memory no longer breaks GPU drawing: older pages are freed and the layer is retried 2026-10-21 21:56:21 +00:00
aiongg b9b97f15ff Merge PR #6243: GPU: pin the keep-overlap ordering with a tile-growth regression test 2026-10-21 21:56:20 +00:00
aiongg 94d28535d3 Merge PR #6464: Survive a lost window GPU device: draw nothing, save unsaved work for recovery, close (#3438, #1215) 2026-10-21 18:15:02 +00:00
aiongg 77557d75f1 Merge PR #8229: Integration batch: 8 PRs on main
# Conflicts:
#	crates/engine/src/layer_style.rs
#	crates/io/src/psd_export.rs
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
2026-10-21 15:45:07 +00:00
Branch Sync 48d2fb45bd Merge remote-tracking branch 'refs/remotes/upstream/main' into HEAD 2026-10-21 11:18:54 -04:00
Branch Sync d5d937ac3e Merge remote-tracking branch 'refs/remotes/upstream/main' into HEAD 2026-10-21 11:17:09 -04:00
David Mann 86811ddeee Merge PR #6002: Stroke: keep Overprint (overprint)
# Conflicts:
#	crates/engine/src/layer_style.rs
#	crates/ui-egui/src/layer_style.rs
#	docs/file-format-parity.md
2026-10-21 11:36:58 +02:00
David Mann a492716367 Merge PR #5990: Bevel & Emboss: keep the Gloss Contour's Anti-aliased (antialiasGloss) 2026-10-21 11:29:05 +02:00
aiongg fc371711e6 Merge PR #5847: Shapes: Density and Feather on shape layers (#5846)
# Conflicts:
#	docs/scorecard.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-21 05:04:03 +00:00
xsvm aab9cdc045 Compose: Color Balance works out each channel's curve once, not per pixel
color_balance_channel recomputed the folded black point, white point and midtone gamma, and four of the five powf calls of the gamma > 1 Hermite join, for every sample. Those depend only on the sliders, so BalanceCurve works them out once per channel and the per-sample step is the clamp and one powf (or the Hermite polynomial). The CPU compositor builds the three curves once per buffer and the GPU plan once per LUT (it sampled color_balance_channel 3 x 4096 times). Results are bit-identical: a new test compares the curve against the per-sample formula it replaced, to the bit, over 123 slider settings, both Preserve Luminosity modes and a 1/1024 ramp.
2026-10-21 10:10:57 +08:00
00xJ0 d383afe428 Test: a compositor pipeline that fails on a worker thread is an error, not a panic (#6419) 2026-10-20 18:40:30 -07:00
00xJ0 3568ede54e GPU compositor: build its pipelines in parallel; DX12's FXC compiles one at a time and took ~8 s on one core (#6419) 2026-10-20 18:40:30 -07:00
Higgs32584andClaude Opus 5.5 ac1a7443a9 GPU compositor: pass the timer and batch runs in the OOM retry (main changed submit_batch and run_cell)
main added a GPU timer argument to submit_batch and the batch runs to run_cell,
so the OOM submit-and-retry path no longer compiled after the merge. Also add
the admission-evicted bytes to the flush counter with saturating arithmetic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:09:52 -04:00
Higgs32584 57948f1155 Merge remote-tracking branch 'upstream/main' into fix/6563-layer-texture-oom 2026-10-20 21:09:26 -04:00
aiongg d4576a0c5a Merge PR #5110: Performance: brush dab on a CMYK document within budget (#5108) 2026-10-21 01:06:11 +00:00