Merge pull request #10398 from yoriswind/codex/fallible-tile-fill

perf(raster): fill fallible default tiles by row
This commit is contained in:
Tanish Manku authored and GitHub committed 2026-11-11 00:15:59 -07:00
commit b0c0b7a044
2 files changed
+92 -1

No files matched your search

+8 -1
View File
@@ -106,14 +106,21 @@ pub struct Tile {
}
impl Tile {
/// Fill a new tile with one encoded pixel, reporting size or allocation failures.
fn try_filled(format: &PixelFormat, pixel: &[u8]) -> Result<Self, AllocationError> {
let n = (TILE_SIZE * TILE_SIZE) as usize;
let bytes = n.checked_mul(format.bytes_per_pixel()).ok_or(AllocationError::SizeOverflow)?;
let mut data = scratch::try_take_exact(bytes, 0u8).ok_or(AllocationError::NotEnoughMemory { bytes })?;
if pixel.iter().any(|&b| b != 0) {
for px in data.chunks_exact_mut(pixel.len()) {
// Fill the first row, then copy it to the other rows without another allocation.
let row_len = TILE_SIZE as usize * format.bytes_per_pixel();
let (row, rest) = data.split_at_mut(row_len);
for px in row.chunks_exact_mut(pixel.len()) {
px.copy_from_slice(pixel);
}
for dst in rest.chunks_exact_mut(row_len) {
dst.copy_from_slice(row);
}
}
Ok(Tile::from_vec(data))
}
+84
View File
@@ -379,6 +379,90 @@ fn fallible_tile_edits_move_weakly_cached_pixels_and_copy_shared_pixels() {
}
}
/// Check encoded defaults across formats and keep cloned tiles isolated during edits.
#[test]
fn fallible_default_tiles_preserve_every_encoded_pixel() {
for mode in [
ColorMode::Bitmap,
ColorMode::Grayscale,
ColorMode::Indexed,
ColorMode::Rgb,
ColorMode::Cmyk,
ColorMode::Lab,
ColorMode::Multichannel,
ColorMode::Duotone,
] {
for sample in SampleType::ALL {
for alpha in [false, true] {
let fmt = PixelFormat::new(mode, sample, alpha);
let channels = fmt.channels();
let defaults = [
("zero", vec![0.0; channels]),
("mixed channels", (1..=channels).map(|i| i as f32 / 8.0).collect()),
("negative zero", vec![-0.0; channels]),
];
for (name, values) in defaults {
let mut surface = Surface::with_default(fmt, &values);
let expected = surface.default_pixel.repeat((TILE_SIZE * TILE_SIZE) as usize);
assert_eq!(&*surface.try_default_tile().unwrap().bytes(), expected.as_slice(), "{fmt:?}, {name}");
let coord = TileCoord::new(-1, 2);
assert_eq!(&*surface.try_tile_mut(coord).unwrap().bytes(), expected.as_slice());
let snapshot = surface.clone();
surface.try_tile_mut(coord).unwrap().bytes_mut()[0] ^= 0xff;
assert_eq!(&*snapshot.tile(coord).unwrap().bytes(), expected.as_slice(), "copy-on-write: {fmt:?}");
assert_ne!(&*surface.tile(coord).unwrap().bytes(), expected.as_slice());
}
}
}
}
}
/// Tile initialization only: 384 tiles cover a 6000 x 4000 image. Each tile is dropped before
/// allocating the next, so this is not an end-to-end image-open or peak-memory measurement.
#[test]
#[ignore = "manual 24 MP release benchmark"]
fn benchmark_fallible_default_tiles() {
use std::{hint::black_box, time::Instant};
// Isolate the unchanged float-zero control from the other cases' different CPU loads.
let zero_only = std::env::var_os("PHOTOCRAFT_TILE_FILL_ZERO_ONLY").is_some();
let count = Rect::new(0, 0, 6000, 4000).tile_count();
for fmt in [PixelFormat::GRAY8, PixelFormat::RGBA8, PixelFormat::CMYKA8, PixelFormat::RGBA16, PixelFormat::RGBA32F] {
if zero_only && fmt != PixelFormat::RGBA32F {
continue;
}
for value in [0.0, 0.25] {
if zero_only && value != 0.0 {
continue;
}
let values: Vec<f32> = (0..fmt.channels()).map(|i| value * (i + 1) as f32).collect();
let surface = Surface::with_default(fmt, &values);
for trial in 0..7 {
let start = Instant::now();
for _ in 0..count {
black_box(black_box(&surface).try_default_tile().unwrap());
}
println!("tile_fill,{fmt:?},{value},{trial},{:.6}", start.elapsed().as_secs_f64() * 1000.0);
}
}
}
if zero_only {
return;
}
// A fallible region write also converts and stores the incoming samples. Measure that
// caller separately so tile-initialization gains are not presented as editing speedups.
let rect = Rect::new(0, 0, 6000, 4000);
let samples = vec![0.75; 6000 * 4000];
for trial in 0..7 {
let mut mask = Surface::with_default(PixelFormat::GRAY8, &[1.0]);
let start = Instant::now();
mask.try_write_region(rect, black_box(&samples)).unwrap();
let elapsed = start.elapsed().as_secs_f64() * 1000.0;
assert!(mask.to_interleaved(rect).iter().all(|&b| b == 191));
black_box(&mask);
println!("tile_write,Gray8 mask,1,{trial},{elapsed:.6}");
}
}
#[test]
fn fill_rect_shares_covered_tiles_and_edits_stay_local() {
// A 300000² background used to materialize ~360 GB of tiles (#705).