diff --git a/vello_gpu_shaders/shaders/helpers/extend.wesl b/vello_gpu_shaders/shaders/helpers/extend.wesl index 0f963abe9..463833254 100644 --- a/vello_gpu_shaders/shaders/helpers/extend.wesl +++ b/vello_gpu_shaders/shaders/helpers/extend.wesl @@ -13,14 +13,22 @@ fn extend_mode(t: f32, mode: u32, max: f32) -> f32 { return clamp(t, 0.0, max - 1.0); } case EXTEND_REPEAT: { - return extend_mode_normalized(t / max, mode) * max; + return euclid_mod(floor(t), max); } case EXTEND_REFLECT, default: { - return extend_mode_normalized(t / max, mode) * max; + let period = max + max; + let r = euclid_mod(floor(t), period); + return select(r, period - 1.0 - r, r >= max); } } } +fn euclid_mod(t: f32, m: f32) -> f32 { + let q = round(t / m); + let r = fma(q, -m, t); + return select(r, r + m, r < 0.0); +} + fn extend_mode_normalized(t: f32, mode: u32) -> f32 { switch mode { case EXTEND_PAD: { diff --git a/vello_gpu_shaders/shaders/helpers/external_texture.wesl b/vello_gpu_shaders/shaders/helpers/external_texture.wesl index 64b4e3966..1296977b9 100644 --- a/vello_gpu_shaders/shaders/helpers/external_texture.wesl +++ b/vello_gpu_shaders/shaders/helpers/external_texture.wesl @@ -3,7 +3,7 @@ //! Sampling helpers for externally bound image textures. -import package::helpers::extend::extend_mode; +import package::helpers::extend::{EXTEND_PAD, extend_mode}; import package::helpers::image::{ IMAGE_QUALITY_HIGH, IMAGE_QUALITY_MEDIUM, @@ -38,21 +38,16 @@ fn sample_image_paint( let packed_tint = image_texel2.y; let image_tint = unpack4x8unorm(packed_tint); let is_multiply = image_texel2.z == TINT_MODE_MULTIPLY; - let extended_xy = vec2( - extend_mode(sample_xy.x, image_extend_modes.x, image_size.x), - extend_mode(sample_xy.y, image_extend_modes.y, image_size.y) - ); - // TODO: add a fast path for images where we are using bilinear sampling and want transparent pixels, // using GPU-native bilinear sampling - let final_xy = image_offset + extended_xy; let sample_color = sample_external_image( external_texture, image_quality, - final_xy, + sample_xy, image_offset, image_size, + image_extend_modes, ); return select( @@ -62,58 +57,113 @@ fn sample_image_paint( ); } -// This is the same as `helpers::image::bilinear_sample`, but for a 2D texture -// instead of an atlas texture array. +fn external_axis_index(coord: f32, extend_mode_id: u32, size: f32) -> i32 { + return i32(min(extend_mode(coord, extend_mode_id, size), size - 1.0)); +} + +// Bilinear sampling with each tap extended within the source region. fn external_bilinear_sample( external_texture: texture_2d, coords: vec2, image_offset: vec2, image_size: vec2, + image_extend_modes: vec2, ) -> vec4 { - let image_max = image_offset + image_size - vec2(1.0); - let clamped_coords = clamp(coords, image_offset, image_max); - let coord_quad = vec4(floor(clamped_coords), ceil(clamped_coords)); - let coord_frac = fract(coords); - let a = textureLoad(external_texture, vec2(coord_quad.xy), 0); - let b = textureLoad(external_texture, vec2(coord_quad.xw), 0); - let c = textureLoad(external_texture, vec2(coord_quad.zy), 0); - let d = textureLoad(external_texture, vec2(coord_quad.zw), 0); + let shifted_coords = coords + vec2(0.5); + let base_coords = floor(shifted_coords); + let coord_frac = shifted_coords - base_coords; + var x: vec2; + var y: vec2; + + if all(image_extend_modes == vec2(EXTEND_PAD)) { + x = vec2(clamp( + base_coords.xx + vec2(-1.0, 0.0), + vec2(0.0), vec2(image_size.x - 1.0), + )); + y = vec2(clamp( + base_coords.yy + vec2(-1.0, 0.0), + vec2(0.0), vec2(image_size.y - 1.0), + )); + } else { + x = vec2( + external_axis_index(base_coords.x - 1.0, image_extend_modes.x, image_size.x), + external_axis_index(base_coords.x, image_extend_modes.x, image_size.x), + ); + y = vec2( + external_axis_index(base_coords.y - 1.0, image_extend_modes.y, image_size.y), + external_axis_index(base_coords.y, image_extend_modes.y, image_size.y), + ); + } + let offset = vec2(image_offset); + let a = textureLoad(external_texture, offset + vec2(x.x, y.x), 0); + let b = textureLoad(external_texture, offset + vec2(x.x, y.y), 0); + let c = textureLoad(external_texture, offset + vec2(x.y, y.x), 0); + let d = textureLoad(external_texture, offset + vec2(x.y, y.y), 0); return mix(mix(a, b, coord_frac.y), mix(c, d, coord_frac.y), coord_frac.x); } -// This is the same as `helpers::image::bicubic_sample`, but for a 2D texture -// instead of an atlas texture array. +// Bicubic sampling with each tap extended within the source region. @if(image_bicubic) fn external_bicubic_sample( external_texture: texture_2d, coords: vec2, image_offset: vec2, image_size: vec2, + image_extend_modes: vec2, ) -> vec4 { - let image_max = image_offset + image_size - vec2(1.0); - let frac_coords = fract(coords + 0.5); + let shifted_coords = coords + vec2(0.5); + let base_coords = floor(shifted_coords); + let frac_coords = shifted_coords - base_coords; let cx = cubic_weights(frac_coords.x); let cy = cubic_weights(frac_coords.y); - let s00 = textureLoad(external_texture, vec2(clamp(coords + vec2(-1.5, -1.5), image_offset, image_max)), 0); - let s10 = textureLoad(external_texture, vec2(clamp(coords + vec2(-0.5, -1.5), image_offset, image_max)), 0); - let s20 = textureLoad(external_texture, vec2(clamp(coords + vec2(0.5, -1.5), image_offset, image_max)), 0); - let s30 = textureLoad(external_texture, vec2(clamp(coords + vec2(1.5, -1.5), image_offset, image_max)), 0); + var x: vec4; + var y: vec4; - let s01 = textureLoad(external_texture, vec2(clamp(coords + vec2(-1.5, -0.5), image_offset, image_max)), 0); - let s11 = textureLoad(external_texture, vec2(clamp(coords + vec2(-0.5, -0.5), image_offset, image_max)), 0); - let s21 = textureLoad(external_texture, vec2(clamp(coords + vec2(0.5, -0.5), image_offset, image_max)), 0); - let s31 = textureLoad(external_texture, vec2(clamp(coords + vec2(1.5, -0.5), image_offset, image_max)), 0); + if all(image_extend_modes == vec2(EXTEND_PAD)) { + x = vec4(clamp( + base_coords.xxxx + vec4(-2.0, -1.0, 0.0, 1.0), + vec4(0.0), vec4(image_size.x - 1.0), + )); + y = vec4(clamp( + base_coords.yyyy + vec4(-2.0, -1.0, 0.0, 1.0), + vec4(0.0), vec4(image_size.y - 1.0), + )); + } else { + x = vec4( + external_axis_index(base_coords.x - 2.0, image_extend_modes.x, image_size.x), + external_axis_index(base_coords.x - 1.0, image_extend_modes.x, image_size.x), + external_axis_index(base_coords.x, image_extend_modes.x, image_size.x), + external_axis_index(base_coords.x + 1.0, image_extend_modes.x, image_size.x), + ); + y = vec4( + external_axis_index(base_coords.y - 2.0, image_extend_modes.y, image_size.y), + external_axis_index(base_coords.y - 1.0, image_extend_modes.y, image_size.y), + external_axis_index(base_coords.y, image_extend_modes.y, image_size.y), + external_axis_index(base_coords.y + 1.0, image_extend_modes.y, image_size.y), + ); + } + let offset = vec2(image_offset); + + let s00 = textureLoad(external_texture, offset + vec2(x.x, y.x), 0); + let s10 = textureLoad(external_texture, offset + vec2(x.y, y.x), 0); + let s20 = textureLoad(external_texture, offset + vec2(x.z, y.x), 0); + let s30 = textureLoad(external_texture, offset + vec2(x.w, y.x), 0); + + let s01 = textureLoad(external_texture, offset + vec2(x.x, y.y), 0); + let s11 = textureLoad(external_texture, offset + vec2(x.y, y.y), 0); + let s21 = textureLoad(external_texture, offset + vec2(x.z, y.y), 0); + let s31 = textureLoad(external_texture, offset + vec2(x.w, y.y), 0); - let s02 = textureLoad(external_texture, vec2(clamp(coords + vec2(-1.5, 0.5), image_offset, image_max)), 0); - let s12 = textureLoad(external_texture, vec2(clamp(coords + vec2(-0.5, 0.5), image_offset, image_max)), 0); - let s22 = textureLoad(external_texture, vec2(clamp(coords + vec2(0.5, 0.5), image_offset, image_max)), 0); - let s32 = textureLoad(external_texture, vec2(clamp(coords + vec2(1.5, 0.5), image_offset, image_max)), 0); + let s02 = textureLoad(external_texture, offset + vec2(x.x, y.z), 0); + let s12 = textureLoad(external_texture, offset + vec2(x.y, y.z), 0); + let s22 = textureLoad(external_texture, offset + vec2(x.z, y.z), 0); + let s32 = textureLoad(external_texture, offset + vec2(x.w, y.z), 0); - let s03 = textureLoad(external_texture, vec2(clamp(coords + vec2(-1.5, 1.5), image_offset, image_max)), 0); - let s13 = textureLoad(external_texture, vec2(clamp(coords + vec2(-0.5, 1.5), image_offset, image_max)), 0); - let s23 = textureLoad(external_texture, vec2(clamp(coords + vec2(0.5, 1.5), image_offset, image_max)), 0); - let s33 = textureLoad(external_texture, vec2(clamp(coords + vec2(1.5, 1.5), image_offset, image_max)), 0); + let s03 = textureLoad(external_texture, offset + vec2(x.x, y.w), 0); + let s13 = textureLoad(external_texture, offset + vec2(x.y, y.w), 0); + let s23 = textureLoad(external_texture, offset + vec2(x.z, y.w), 0); + let s33 = textureLoad(external_texture, offset + vec2(x.w, y.w), 0); let row0 = cx.x * s00 + cx.y * s10 + cx.z * s20 + cx.w * s30; let row1 = cx.x * s01 + cx.y * s11 + cx.z * s21 + cx.w * s31; @@ -132,18 +182,33 @@ fn sample_external_image( coords: vec2, image_offset: vec2, image_size: vec2, + image_extend_modes: vec2, ) -> vec4 { @if(image_bicubic) if quality == IMAGE_QUALITY_HIGH { - return external_bicubic_sample(external_texture, coords, image_offset, image_size); + return external_bicubic_sample( + external_texture, + coords, + image_offset, + image_size, + image_extend_modes, + ); } if quality == IMAGE_QUALITY_MEDIUM { return external_bilinear_sample( external_texture, - coords - vec2(0.5), + coords, image_offset, image_size, + image_extend_modes, ); } - return textureLoad(external_texture, vec2(coords), 0); + return textureLoad( + external_texture, + vec2(image_offset) + vec2( + external_axis_index(coords.x, image_extend_modes.x, image_size.x), + external_axis_index(coords.y, image_extend_modes.y, image_size.y), + ), + 0, + ); } diff --git a/vello_tests/snapshots/external_texture_blurred.png b/vello_tests/snapshots/external_texture_blurred.png index 25fc96688..1ae0f4dd0 100644 --- a/vello_tests/snapshots/external_texture_blurred.png +++ b/vello_tests/snapshots/external_texture_blurred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7b84ac28569358b4bfc6ad96feb43d6a10e0ff18ac0635e2217156ae5890ed9 -size 5368 +oid sha256:1cd8ac69d1986c58e1fe5651a0c856d8958bf99319f7e45d84e36a080d07c7af +size 5105 diff --git a/vello_tests/snapshots/external_texture_clipped.png b/vello_tests/snapshots/external_texture_clipped.png index 55c2509f4..78cca1a7e 100644 --- a/vello_tests/snapshots/external_texture_clipped.png +++ b/vello_tests/snapshots/external_texture_clipped.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d606d491f1f16774a92f4b6ae00415c2cf2c989e00ffe59c2fdb3a27b6581e81 -size 2440 +oid sha256:e74bec0b2584a30f7393f748b12fc1ae1e74d0bf011f4a5d51085ac30baafec5 +size 2439 diff --git a/vello_tests/snapshots/external_texture_composite.png b/vello_tests/snapshots/external_texture_composite.png index 3717b5028..43deebda2 100644 --- a/vello_tests/snapshots/external_texture_composite.png +++ b/vello_tests/snapshots/external_texture_composite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:814a9064e79345b6130a889fd73e3a0f01dec56cd20a15600cbd53af5baa3a18 -size 4067 +oid sha256:3b7ab36e6d770b51843fb915d4e15090e9cbd0c33c5791c9b8084499fd85a0fd +size 4052 diff --git a/vello_tests/snapshots/external_texture_many_sprites.png b/vello_tests/snapshots/external_texture_many_sprites.png index c7fa077fe..08c52d20a 100644 --- a/vello_tests/snapshots/external_texture_many_sprites.png +++ b/vello_tests/snapshots/external_texture_many_sprites.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08eae45eae7b2b9e41e112ee29a274444c3eeab049fd6dfbca10951b8f81c1a7 -size 13253 +oid sha256:4bfbaf50d3e64e640cabc2091b48e585f57f03eb8eba1efca5c1280ed7d735ba +size 13252 diff --git a/vello_tests/snapshots/external_texture_reflect_with_offset.png b/vello_tests/snapshots/external_texture_reflect_with_offset.png new file mode 100644 index 000000000..8678c2f60 --- /dev/null +++ b/vello_tests/snapshots/external_texture_reflect_with_offset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e676ba0d8f98393cfae378bee4421b0f6fda43ee306165a46bae6e341e4eea5 +size 140 diff --git a/vello_tests/snapshots/external_texture_root_painter_order.png b/vello_tests/snapshots/external_texture_root_painter_order.png index d2bf0dbe3..782873a44 100644 --- a/vello_tests/snapshots/external_texture_root_painter_order.png +++ b/vello_tests/snapshots/external_texture_root_painter_order.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b84b78e387a1a0b49cd9d9af887eb62fe5f4725baada75926ec46d34883b6c +oid sha256:27d71e2c4550ce9b27085785dcfe385fc5052a57f8a5634b4fc15399df8cc056 size 334 diff --git a/vello_tests/snapshots/external_texture_skewed.png b/vello_tests/snapshots/external_texture_skewed.png index 1ae171ee5..90d92182c 100644 --- a/vello_tests/snapshots/external_texture_skewed.png +++ b/vello_tests/snapshots/external_texture_skewed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d06ad352104023d1d7bf59de03ab80ae6fda22a8e4d65fb826fc15a513af3695 -size 2733 +oid sha256:039e3944b98e786a7846daf4652aa5e871485a20ffff224596decdf139dcb953 +size 2739 diff --git a/vello_tests/snapshots/external_texture_with_cropped_source.png b/vello_tests/snapshots/external_texture_with_cropped_source.png index 09e2e4f01..e546a2236 100644 --- a/vello_tests/snapshots/external_texture_with_cropped_source.png +++ b/vello_tests/snapshots/external_texture_with_cropped_source.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d48c74f7a240f82024027429fc1adb7924fb61ebc53342e511837db9a7b57b6e -size 1343 +oid sha256:4a6471af338cb97affd47ab6f6c782833de1c44823ef75a7169a3acfb46dc97f +size 1339 diff --git a/vello_tests/snapshots/external_texture_with_paint_transform.png b/vello_tests/snapshots/external_texture_with_paint_transform.png index e230b64db..1630240c2 100644 --- a/vello_tests/snapshots/external_texture_with_paint_transform.png +++ b/vello_tests/snapshots/external_texture_with_paint_transform.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d913f32e0b85273b0a8861464006fc2af4932f382da7742387c94f085544dd43 -size 10591 +oid sha256:96934985fbb886c92c35efb3f2f412574a8a16ca598d9cf1994a17ccc1a4f436 +size 10590 diff --git a/vello_tests/snapshots/external_texture_with_scene_transform.png b/vello_tests/snapshots/external_texture_with_scene_transform.png index 086e1b6ec..f818329d5 100644 --- a/vello_tests/snapshots/external_texture_with_scene_transform.png +++ b/vello_tests/snapshots/external_texture_with_scene_transform.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e53972600365cacdffd3e6df1b479637ff96e290740b391b65de825a3b46d5de -size 8784 +oid sha256:03659efeb9bdd5d6c977d26ff6775178040622f132349d3c4e38f4d74f7d4604 +size 8680 diff --git a/vello_tests/snapshots/external_texture_with_scene_transform_2.png b/vello_tests/snapshots/external_texture_with_scene_transform_2.png index 4ca670b93..b7e88bf29 100644 --- a/vello_tests/snapshots/external_texture_with_scene_transform_2.png +++ b/vello_tests/snapshots/external_texture_with_scene_transform_2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f835051b66e43aeca0579ce31145e4507d3e95ce5ba1ade542be44b8d2d1fcb3 -size 1207 +oid sha256:5f909833810e2c1bfa4eea87006b2674484b67e5666a037d3e64d8c12d6a64bf +size 1813 diff --git a/vello_tests/snapshots/image_bicubic_repeat_x_pad_y.png b/vello_tests/snapshots/image_bicubic_repeat_x_pad_y.png new file mode 100644 index 000000000..265332b1c --- /dev/null +++ b/vello_tests/snapshots/image_bicubic_repeat_x_pad_y.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d0d7edf12944b746f563e35eb09ae3dc9daa0e2ac2c1b5ae64fdc700d92459c +size 755 diff --git a/vello_tests/src/renderer.rs b/vello_tests/src/renderer.rs index f1b586934..a7ca00bb9 100644 --- a/vello_tests/src/renderer.rs +++ b/vello_tests/src/renderer.rs @@ -1,7 +1,6 @@ // Copyright 2025 the Vello Authors // SPDX-License-Identifier: Apache-2.0 OR MIT -#[cfg(not(all(target_arch = "wasm32", feature = "webgl")))] use std::collections::HashMap; use std::sync::Arc; @@ -100,6 +99,8 @@ pub trait Renderer: Sized { pub struct CpuRenderer { ctx: RenderContext, resources: Resources, + external_textures: HashMap>, + next_external_texture_id: u64, render_mode: RenderMode, target: Pixmap, target_init: GpuTargetInit<'static>, @@ -119,6 +120,8 @@ impl Renderer for CpuRenderer { Self { ctx: RenderContext::new_with(width, height, settings), resources: Resources::new(), + external_textures: HashMap::new(), + next_external_texture_id: 1, render_mode, target: Pixmap::new(width, height), target_init: GpuTargetInit::Clear(ClearSettings::default()), @@ -214,6 +217,35 @@ impl Renderer for CpuRenderer { } fn set_paint(&mut self, paint: impl Into) { + let mut paint = paint.into(); + if let PaintType::Image(image) = &mut paint + && let ImageSource::ExternalTexture { + id, source_region, .. + } = &image.image + { + let pixmap = self + .external_textures + .get(id) + .unwrap_or_else(|| panic!("External texture {id:?} not found in test registry")); + + if *source_region == RectU16::new(0, 0, pixmap.width(), pixmap.height()) { + image.image = ImageSource::Pixmap(Arc::clone(pixmap)); + } else { + let mut cropped = Pixmap::new(source_region.width(), source_region.height()); + let width = usize::from(source_region.width()); + for (row, y) in cropped + .data_mut() + .chunks_exact_mut(width) + .zip(source_region.y0..source_region.y1) + { + let start = usize::from(y) * usize::from(pixmap.width()) + + usize::from(source_region.x0); + row.copy_from_slice(&pixmap.data()[start..start + width]); + } + cropped.set_may_have_transparency(pixmap.may_have_transparency()); + image.image = ImageSource::Pixmap(Arc::new(cropped)); + } + } self.ctx.set_paint(paint); } @@ -283,8 +315,11 @@ impl Renderer for CpuRenderer { self.target.clone() } - fn register_external_texture(&mut self, _: Arc) -> TextureId { - unimplemented!("external textures are only supported by GPU renderer tests") + fn register_external_texture(&mut self, pixmap: Arc) -> TextureId { + let texture_id = TextureId(self.next_external_texture_id); + self.next_external_texture_id += 1; + self.external_textures.insert(texture_id, pixmap); + texture_id } fn get_image_source(&mut self, pixmap: Arc) -> ImageSource { diff --git a/vello_tests/tests/external_texture.rs b/vello_tests/tests/external_texture.rs index 29bcdaa3f..052ab45aa 100644 --- a/vello_tests/tests/external_texture.rs +++ b/vello_tests/tests/external_texture.rs @@ -119,7 +119,7 @@ mod tests { ctx.fill_path(&circle.to_path(0.1)); } - #[vello_test(width = 96, height = 96, gpu_only)] + #[vello_test(width = 96, height = 96, gpu_tolerance = 1)] fn external_texture_composite(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto")); ctx.set_paint_transform(Affine::translate((12., 15.))); @@ -130,7 +130,7 @@ mod tests { fill_sprite_rect(ctx, texture_id, SPRITES[3], 25., 25., ImageQuality::Low); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_circle(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("color_grid_16x16")); @@ -142,7 +142,7 @@ mod tests { ctx.fill_path(&Circle::new((50., 50.), 40.).to_path(0.1)); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_repeat(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("color_grid_16x16")); @@ -158,7 +158,30 @@ mod tests { ctx.fill_rect(&Rect::new(5., 5., 95., 95.)); } - #[vello_test(gpu_only)] + #[vello_test] + fn external_texture_reflect_with_offset(ctx: &mut impl Renderer) { + let pixmap = Arc::new(Pixmap::from_parts( + vec![0, 0, 0, 255, 255, 0, 0, 255, 0, 255, 0, 255], + 3, + 1, + PixelMetadata::new(ImageAlphaType::AlphaPremultiplied, false), + )); + let texture_id = ctx.register_external_texture(pixmap); + + ctx.set_paint_transform(Affine::scale(3.0)); + ctx.set_paint(Image { + image: ImageSource::external_texture(texture_id, RectU16::new(1, 0, 3, 1), false), + sampler: ImageSampler { + x_extend: Extend::Reflect, + y_extend: Extend::Pad, + quality: ImageQuality::Medium, + alpha: 1.0, + }, + }); + ctx.fill_rect(&Rect::new(10.0, 10.0, 90.0, 90.0)); + } + + #[vello_test] fn external_texture_with_paint_transform(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("color_grid_16x16")); @@ -175,7 +198,7 @@ mod tests { ctx.fill_rect(&Rect::new(5., 5., 95., 95.)); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_with_scene_transform_2(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("color_grid_16x16")); @@ -192,7 +215,7 @@ mod tests { ctx.fill_rect(&Rect::new(0., 0., 16., 16.)); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_with_cropped_source(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("color_grid_16x16")); @@ -209,7 +232,7 @@ mod tests { ctx.fill_rect(&Rect::new(5., 5., 95., 95.)); } - #[vello_test(width = 96, height = 96, gpu_only, gpu_no_depth)] + #[vello_test(width = 96, height = 96, gpu_no_depth)] fn external_texture_opaque_interleaving(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(solid_pixmap(192, 0, 0, 192)); @@ -222,7 +245,7 @@ mod tests { ctx.fill_rect(&Rect::new(48., 16., 88., 56.)); } - #[vello_test(width = 96, height = 96, gpu_only, gpu_no_depth)] + #[vello_test(width = 96, height = 96, gpu_no_depth)] fn external_texture_runs_with_opaque_prefix(ctx: &mut impl Renderer) { let red_texture = ctx.register_external_texture(solid_pixmap(255, 0, 0, 255)); let green_texture = ctx.register_external_texture(solid_pixmap(0, 255, 0, 255)); @@ -239,7 +262,7 @@ mod tests { fill_texture_rect(ctx, red_texture, 48., 48., 40., 40., true); } - #[vello_test(gpu_only, gpu_no_depth)] + #[vello_test(gpu_no_depth)] fn external_texture_root_painter_order(ctx: &mut impl Renderer) { let coral = ctx.register_external_texture(solid_pixmap(225, 87, 89, 255)); let teal = ctx.register_external_texture(solid_pixmap(42, 157, 143, 255)); @@ -264,7 +287,7 @@ mod tests { fill_texture_rect(ctx, coral, 44., 44., 12., 12., false); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_atlas_interleaving(ctx: &mut impl Renderer) { let atlas_red = ctx.get_image_source(solid_pixmap(254, 0, 0, 254)); let external_green = ctx.register_external_texture(solid_pixmap(0, 254, 0, 254)); @@ -279,7 +302,7 @@ mod tests { draw_atlas_rect(ctx, atlas_magenta, Rect::new(66., 66., 90., 90.)); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_layer_before_external(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(solid_pixmap(128, 0, 0, 128)); @@ -293,7 +316,7 @@ mod tests { ctx.pop_layer(); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_external_before_layer(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(solid_pixmap(0, 255, 0, 255)); @@ -307,7 +330,7 @@ mod tests { ctx.pop_layer(); } - #[vello_test(gpu_only)] + #[vello_test] fn external_texture_layer_circle_orders(ctx: &mut impl Renderer) { let blue_texture = ctx.register_external_texture(solid_pixmap(0, 0, 255, 255)); let red_texture = ctx.register_external_texture(solid_pixmap(255, 0, 0, 255)); @@ -384,7 +407,7 @@ mod tests { ); } - #[vello_test(width = 96, height = 96, gpu_only, gpu_tolerance = 1)] + #[vello_test(width = 96, height = 96, gpu_tolerance = 1)] fn external_texture_skewed(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto")); let source_region = SPRITES[0]; @@ -402,7 +425,7 @@ mod tests { )); } - #[vello_test(width = 96, height = 96, gpu_only)] + #[vello_test(width = 96, height = 96)] fn external_texture_clipped(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto")); let clip = Circle::new((48., 48.), 24.).to_path(0.1); @@ -415,7 +438,7 @@ mod tests { ctx.pop_layer(); } - #[vello_test(width = 96, height = 96, gpu_only, gpu_tolerance = 2)] + #[vello_test(width = 96, height = 96, skip_multithreaded, gpu_tolerance = 2)] fn external_texture_blurred(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto")); let blur = Filter::from_primitive(FilterPrimitive::GaussianBlur { @@ -429,7 +452,7 @@ mod tests { ctx.pop_layer(); } - #[vello_test(width = 192, height = 132, gpu_only)] + #[vello_test(width = 192, height = 132)] fn external_texture_many_sprites(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto")); let placements = [ @@ -453,7 +476,7 @@ mod tests { } } - #[vello_test(width = 96, height = 96, gpu_only, gpu_tolerance = 2)] + #[vello_test(width = 96, height = 96, cpu_u8_tolerance = 2, gpu_tolerance = 2)] fn external_texture_with_scene_transform(ctx: &mut impl Renderer) { let texture_id = ctx.register_external_texture(load_image!("glyphs_colr_noto")); diff --git a/vello_tests/tests/image.rs b/vello_tests/tests/image.rs index fa33cc2dd..46f33bb44 100644 --- a/vello_tests/tests/image.rs +++ b/vello_tests/tests/image.rs @@ -89,9 +89,7 @@ fn image_pad_x_pad_y(ctx: &mut impl Renderer) { repeat(ctx, Extend::Pad, Extend::Pad); } -// TODO: Doesn't work correctly in the GPU renderer due to a difference in sampling behavior. -#[vello_test(skip_gpu)] -fn image_bilinear_repeat_x_pad_y(ctx: &mut impl Renderer) { +fn filtered_repeat_x_pad_y(ctx: &mut impl Renderer, quality: ImageQuality) { let rect = Rect::new(10.0, 10.0, 90.0, 90.0); let image_source = rgb_img_2x2(ctx); @@ -101,13 +99,23 @@ fn image_bilinear_repeat_x_pad_y(ctx: &mut impl Renderer) { sampler: ImageSampler { x_extend: Extend::Repeat, y_extend: Extend::Pad, - quality: ImageQuality::Medium, + quality, alpha: 1.0, }, }); ctx.fill_rect(&rect); } +#[vello_test] +fn image_bilinear_repeat_x_pad_y(ctx: &mut impl Renderer) { + filtered_repeat_x_pad_y(ctx, ImageQuality::Medium); +} + +#[vello_test] +fn image_bicubic_repeat_x_pad_y(ctx: &mut impl Renderer) { + filtered_repeat_x_pad_y(ctx, ImageQuality::High); +} + fn transform(ctx: &mut impl Renderer, transform: Affine, l: f64, t: f64, r: f64, b: f64) { let rect = Rect::new(l, t, r, b); let image_source = rgb_img_10x10(ctx); diff --git a/vello_tests/tests/issues.rs b/vello_tests/tests/issues.rs index 487aee94b..4cff169f0 100644 --- a/vello_tests/tests/issues.rs +++ b/vello_tests/tests/issues.rs @@ -863,12 +863,12 @@ fn image_filtering_at_extend_boundary( ctx.fill_rect(&Rect::new(10.0, 10.0, 90.0, 90.0)); } -#[vello_test(skip_gpu)] +#[vello_test] fn issue_image_filtering_at_reflect_boundary(ctx: &mut impl Renderer) { image_filtering_at_extend_boundary(ctx, 2, Extend::Reflect, (-1.0_f32).next_down()); } -#[vello_test(skip_gpu)] +#[vello_test] fn issue_image_filtering_at_repeat_boundary(ctx: &mut impl Renderer) { image_filtering_at_extend_boundary(ctx, 3, Extend::Repeat, 15.0_f32.next_down()); } diff --git a/vello_tests/webdriver.json b/vello_tests/webdriver.json index 345b6818a..808bebfdb 100644 --- a/vello_tests/webdriver.json +++ b/vello_tests/webdriver.json @@ -1,5 +1,6 @@ { "timeouts": { + "pageLoad": 1200000, "script": 1200000 } }