From 3bf8ad7884ef6b10676fddd74ce2a93cae17a779 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Sun, 19 Jan 2020 19:45:37 +0000 Subject: [PATCH] Bug 1609805 - Support a new reftest kind, for verifying rasterizer accuracy. r=nical,Bert This patch introduces a new reftest (syntax ** or !* in reftest files). This type of test renders a single input file multiple times, at a range of different picture cache tile sizes. It then verifies that each of the images matches (or doesn't). This can be used to verify rasterizer accuracy when drawing primitives with different tile sizes and/or dirty rect update strategies. One of the included tests in this patch fails the accuracy test - the intent is to fix this inaccuracy in a follow up patch and then be able to mark it pixel exact. Differential Revision: https://phabricator.services.mozilla.com/D60185 --HG-- extra : moz-landing-system : lando --- gfx/wr/webrender/src/frame_builder.rs | 8 +- gfx/wr/webrender/src/picture.rs | 74 ++++-- gfx/wr/webrender/src/render_backend.rs | 9 + gfx/wr/webrender/src/renderer.rs | 6 +- gfx/wr/webrender/src/scene.rs | 1 + gfx/wr/webrender_api/src/api.rs | 2 + gfx/wr/wrench/reftests/border/reftest.list | 2 +- gfx/wr/wrench/reftests/reftest.list | 1 + gfx/wr/wrench/reftests/text/reftest.list | 4 +- gfx/wr/wrench/reftests/tiles/prim-suite.yaml | 45 ++++ gfx/wr/wrench/reftests/tiles/rect.yaml | 6 + gfx/wr/wrench/reftests/tiles/reftest.list | 4 + .../reftests/tiles/simple-gradient.yaml | 9 + gfx/wr/wrench/src/reftest.rs | 226 ++++++++++++++---- 14 files changed, 318 insertions(+), 79 deletions(-) create mode 100644 gfx/wr/wrench/reftests/tiles/prim-suite.yaml create mode 100644 gfx/wr/wrench/reftests/tiles/rect.yaml create mode 100644 gfx/wr/wrench/reftests/tiles/reftest.list create mode 100644 gfx/wr/wrench/reftests/tiles/simple-gradient.yaml diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs index cd02e500285d..9c5fa07f4faa 100644 --- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -65,6 +65,7 @@ pub struct FrameBuilderConfig { pub batch_lookback_count: usize, pub background_color: Option, pub compositor_kind: CompositorKind, + pub tile_size_override: Option, } /// A set of common / global resources that are retained between @@ -117,7 +118,7 @@ pub struct FrameVisibilityContext<'a> { pub surfaces: &'a [SurfaceInfo], pub debug_flags: DebugFlags, pub scene_properties: &'a SceneProperties, - pub config: &'a FrameBuilderConfig, + pub config: FrameBuilderConfig, } pub struct FrameVisibilityState<'a> { @@ -242,6 +243,7 @@ impl FrameBuilder { texture_cache_profile: &mut TextureCacheProfileCounters, composite_state: &mut CompositeState, tile_cache_logger: &mut TileCacheLogger, + config: FrameBuilderConfig, ) -> Option { profile_scope!("cull"); @@ -327,7 +329,7 @@ impl FrameBuilder { surfaces, debug_flags, scene_properties, - config: &scene.config, + config, }; let mut visibility_state = FrameVisibilityState { @@ -472,6 +474,7 @@ impl FrameBuilder { render_task_counters: &mut RenderTaskGraphCounters, debug_flags: DebugFlags, tile_cache_logger: &mut TileCacheLogger, + config: FrameBuilderConfig, ) -> Frame { profile_scope!("build"); profile_marker!("BuildFrame"); @@ -542,6 +545,7 @@ impl FrameBuilder { &mut resource_profile.texture_cache, &mut composite_state, tile_cache_logger, + config, ); let mut passes; diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs index 1f6d288d855b..8893746c3557 100644 --- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -278,13 +278,38 @@ pub const TILE_SIZE_SCROLLBAR_VERTICAL: DeviceIntSize = DeviceIntSize { _unit: marker::PhantomData, }; +const TILE_SIZE_FOR_TESTS: [DeviceIntSize; 6] = [ + DeviceIntSize { + width: 128, + height: 128, + _unit: marker::PhantomData, + }, + DeviceIntSize { + width: 256, + height: 256, + _unit: marker::PhantomData, + }, + DeviceIntSize { + width: 512, + height: 512, + _unit: marker::PhantomData, + }, + TILE_SIZE_DEFAULT, + TILE_SIZE_SCROLLBAR_VERTICAL, + TILE_SIZE_SCROLLBAR_HORIZONTAL, +]; + // Return the list of tile sizes for the renderer to allocate texture arrays for. -pub fn tile_cache_sizes() -> &'static [DeviceIntSize] { - &[ - TILE_SIZE_DEFAULT, - TILE_SIZE_SCROLLBAR_HORIZONTAL, - TILE_SIZE_SCROLLBAR_VERTICAL, - ] +pub fn tile_cache_sizes(testing: bool) -> &'static [DeviceIntSize] { + if testing { + &TILE_SIZE_FOR_TESTS + } else { + &[ + TILE_SIZE_DEFAULT, + TILE_SIZE_SCROLLBAR_HORIZONTAL, + TILE_SIZE_SCROLLBAR_VERTICAL, + ] + } } /// The maximum size per axis of a surface, @@ -1654,6 +1679,9 @@ pub struct TileCacheInstance { pub device_position: DevicePoint, /// True if the entire picture cache surface is opaque. is_opaque: bool, + /// The currently considered tile size override. Used to check if we should + /// re-evaluate tile size, even if the frame timer hasn't expired. + tile_size_override: Option, } impl TileCacheInstance { @@ -1704,6 +1732,7 @@ impl TileCacheInstance { native_surface_id: None, device_position: DevicePoint::zero(), is_opaque: true, + tile_size_override: None, } } @@ -1861,22 +1890,28 @@ impl TileCacheInstance { // Only evaluate what tile size to use fairly infrequently, so that we don't end // up constantly invalidating and reallocating tiles if the picture rect size is // changing near a threshold value. - if self.frames_until_size_eval == 0 { + if self.frames_until_size_eval == 0 || + self.tile_size_override != frame_context.config.tile_size_override { const TILE_SIZE_TINY: f32 = 32.0; // Work out what size tile is appropriate for this picture cache. - let desired_tile_size; - - // There's no need to check the other dimension. If we encounter a picture - // that is small on one dimension, it's a reasonable choice to use a scrollbar - // sized tile configuration regardless of the other dimension. - if pic_rect.size.width <= TILE_SIZE_TINY { - desired_tile_size = TILE_SIZE_SCROLLBAR_VERTICAL; - } else if pic_rect.size.height <= TILE_SIZE_TINY { - desired_tile_size = TILE_SIZE_SCROLLBAR_HORIZONTAL; - } else { - desired_tile_size = TILE_SIZE_DEFAULT; - } + let desired_tile_size = match frame_context.config.tile_size_override { + Some(tile_size_override) => { + tile_size_override + } + None => { + // There's no need to check the other dimension. If we encounter a picture + // that is small on one dimension, it's a reasonable choice to use a scrollbar + // sized tile configuration regardless of the other dimension. + if pic_rect.size.width <= TILE_SIZE_TINY { + TILE_SIZE_SCROLLBAR_VERTICAL + } else if pic_rect.size.height <= TILE_SIZE_TINY { + TILE_SIZE_SCROLLBAR_HORIZONTAL + } else { + TILE_SIZE_DEFAULT + } + } + }; // If the desired tile size has changed, then invalidate and drop any // existing tiles. @@ -1893,6 +1928,7 @@ impl TileCacheInstance { // Reset counter until next evaluating the desired tile size. This is an // arbitrary value. self.frames_until_size_eval = 120; + self.tile_size_override = frame_context.config.tile_size_override; } // Map an arbitrary point in picture space to world space, to work out diff --git a/gfx/wr/webrender/src/render_backend.rs b/gfx/wr/webrender/src/render_backend.rs index f997299570f7..e54874f6e702 100644 --- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -541,6 +541,7 @@ impl Document { resource_profile: &mut ResourceProfileCounters, debug_flags: DebugFlags, tile_cache_logger: &mut TileCacheLogger, + config: FrameBuilderConfig, ) -> RenderedDocument { let accumulated_scale_factor = self.view.accumulated_scale_factor(); let pan = self.view.pan.to_f32() / accumulated_scale_factor; @@ -568,6 +569,7 @@ impl Document { &mut self.render_task_counters, debug_flags, tile_cache_logger, + config, ); self.hit_tester = Some(self.scene.create_hit_tester(&self.data_stores.clip)); frame @@ -1142,6 +1144,11 @@ impl RenderBackend { // We don't want to forward this message to the renderer. return RenderBackendStatus::Continue; } + DebugCommand::SetPictureTileSize(tile_size) => { + self.frame_config.tile_size_override = tile_size; + + return RenderBackendStatus::Continue; + } DebugCommand::FetchDocuments => { // Ask SceneBuilderThread to send JSON presentation of the documents, // that will be forwarded to Renderer. @@ -1534,6 +1541,7 @@ impl RenderBackend { &mut profile_counters.resources, self.debug_flags, &mut self.tile_cache_logger, + self.frame_config, ); debug!("generated frame for document {:?} with {} passes", @@ -1712,6 +1720,7 @@ impl RenderBackend { &mut profile_counters.resources, self.debug_flags, &mut self.tile_cache_logger, + self.frame_config, ); // After we rendered the frames, there are pending updates to both // GPU cache and resources. Instead of serializing them, we are going to make sure diff --git a/gfx/wr/webrender/src/renderer.rs b/gfx/wr/webrender/src/renderer.rs index 6ae8094ca476..3bd8d490aac2 100644 --- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -2185,6 +2185,7 @@ impl Renderer { batch_lookback_count: options.batch_lookback_count, background_color: options.clear_color, compositor_kind, + tile_size_override: None, }; info!("WR {:?}", config); @@ -2292,7 +2293,7 @@ impl Renderer { max_texture_size, max_texture_layers, if config.global_enable_picture_caching { - tile_cache_sizes() + tile_cache_sizes(config.testing) } else { &[] }, @@ -2845,7 +2846,8 @@ impl Renderer { fn handle_debug_command(&mut self, command: DebugCommand) { match command { DebugCommand::EnableDualSourceBlending(_) | - DebugCommand::SetTransactionLogging(_) => { + DebugCommand::SetTransactionLogging(_) | + DebugCommand::SetPictureTileSize(_) => { panic!("Should be handled by render backend"); } DebugCommand::FetchDocuments | diff --git a/gfx/wr/webrender/src/scene.rs b/gfx/wr/webrender/src/scene.rs index a7c8aa779efc..64788eecbc2f 100644 --- a/gfx/wr/webrender/src/scene.rs +++ b/gfx/wr/webrender/src/scene.rs @@ -258,6 +258,7 @@ impl BuiltScene { batch_lookback_count: 0, background_color: None, compositor_kind: CompositorKind::default(), + tile_size_override: None, }, } } diff --git a/gfx/wr/webrender_api/src/api.rs b/gfx/wr/webrender_api/src/api.rs index 5f1dfaeea204..e6863e5b4667 100644 --- a/gfx/wr/webrender_api/src/api.rs +++ b/gfx/wr/webrender_api/src/api.rs @@ -977,6 +977,8 @@ pub enum DebugCommand { SimulateLongLowPrioritySceneBuild(u32), /// Logs transactions to a file for debugging purposes SetTransactionLogging(bool), + /// Set an override tile size to use for picture caches + SetPictureTileSize(Option), } /// Message sent by the `RenderApi` to the render backend thread. diff --git a/gfx/wr/wrench/reftests/border/reftest.list b/gfx/wr/wrench/reftests/border/reftest.list index 5d41a3f5b9a8..ec6a1af070bf 100644 --- a/gfx/wr/wrench/reftests/border/reftest.list +++ b/gfx/wr/wrench/reftests/border/reftest.list @@ -28,5 +28,5 @@ platform(linux,mac) == small-dotted-border.yaml small-dotted-border.png platform(linux,mac) == border-dashed-dotted-caching.yaml border-dashed-dotted-caching.png != small-inset-outset.yaml small-inset-outset-notref.yaml fuzzy(1,16) == no-aa.yaml green-square.yaml -skip_on(android,device) border-double-1px.yaml border-double-1px-ref.yaml # Fails on Pixel2 +skip_on(android,device) == border-double-1px.yaml border-double-1px-ref.yaml # Fails on Pixel2 == max-scale.yaml max-scale-ref.yaml diff --git a/gfx/wr/wrench/reftests/reftest.list b/gfx/wr/wrench/reftests/reftest.list index ebbe4db4e4b5..a31943320218 100644 --- a/gfx/wr/wrench/reftests/reftest.list +++ b/gfx/wr/wrench/reftests/reftest.list @@ -15,3 +15,4 @@ include snap/reftest.list include split/reftest.list include text/reftest.list include transforms/reftest.list +include tiles/reftest.list diff --git a/gfx/wr/wrench/reftests/text/reftest.list b/gfx/wr/wrench/reftests/text/reftest.list index d805141171d7..3f045bc4a6ca 100644 --- a/gfx/wr/wrench/reftests/text/reftest.list +++ b/gfx/wr/wrench/reftests/text/reftest.list @@ -73,5 +73,5 @@ options(disable-aa) == snap-clip.yaml snap-clip-ref.yaml platform(linux) == perspective-clip.yaml perspective-clip.png fuzzy(1,39) options(disable-subpixel) == raster-space-snap.yaml raster-space-snap-ref.yaml # == intermediate-transform.yaml intermediate-transform-ref.yaml # fails because of AA inavailable with an intermediate surface -platform(linux) allow_sacrificing_subpixel_aa(false) text-fixed-slice.yaml text-fixed-slice-slow.png -platform(linux) allow_sacrificing_subpixel_aa(true) text-fixed-slice.yaml text-fixed-slice-fast.png +platform(linux) allow_sacrificing_subpixel_aa(false) == text-fixed-slice.yaml text-fixed-slice-slow.png +platform(linux) allow_sacrificing_subpixel_aa(true) == text-fixed-slice.yaml text-fixed-slice-fast.png diff --git a/gfx/wr/wrench/reftests/tiles/prim-suite.yaml b/gfx/wr/wrench/reftests/tiles/prim-suite.yaml new file mode 100644 index 000000000000..2a170f8fc90d --- /dev/null +++ b/gfx/wr/wrench/reftests/tiles/prim-suite.yaml @@ -0,0 +1,45 @@ +--- +root: + items: + - type: stacking-context + bounds: [50, 50, 100, 100] + transform: rotate(30) + items: + - type: rect + bounds: [ 10, 10, 80, 80 ] + color: [0, 255, 0] + - type: box-shadow + bounds: [ 10, 10, 80, 80 ] + blur-radius: 25 + clip-mode: inset + + - type: rect + bounds: [ 140, 10, 80, 80 ] + color: [0, 255, 0] + - type: box-shadow + bounds: [ 140, 10, 80, 80 ] + blur-radius: 25 + clip-mode: outset + + - type: border + bounds: [ 250, 10, 100, 100 ] + width: [ 10, 10, 10, 10 ] + border-type: normal + style: solid + color: [ red, green, blue, black ] + radius: { + top-left: [20, 20], + top-right: [10, 10], + bottom-left: [25, 25], + bottom-right: [0, 0], + } + + - bounds: [150, 150, 128, 128] + image: checkerboard(4, 15, 8) + stretch-size: 128 128 + + - type: radial-gradient + bounds: 300 150 100 100 + center: 50 50 + radius: 50 50 + stops: [0, red, 1, blue] diff --git a/gfx/wr/wrench/reftests/tiles/rect.yaml b/gfx/wr/wrench/reftests/tiles/rect.yaml new file mode 100644 index 000000000000..e3f71ac9ddbc --- /dev/null +++ b/gfx/wr/wrench/reftests/tiles/rect.yaml @@ -0,0 +1,6 @@ +--- +root: + items: + - type: rect + bounds: 50 50 200 200 + color: red diff --git a/gfx/wr/wrench/reftests/tiles/reftest.list b/gfx/wr/wrench/reftests/tiles/reftest.list new file mode 100644 index 000000000000..7c04207b1839 --- /dev/null +++ b/gfx/wr/wrench/reftests/tiles/reftest.list @@ -0,0 +1,4 @@ +** rect.yaml +** simple-gradient.yaml +# TODO: Fix rasterizer inaccuracies so this is the same regardless of tile size! +!* prim-suite.yaml diff --git a/gfx/wr/wrench/reftests/tiles/simple-gradient.yaml b/gfx/wr/wrench/reftests/tiles/simple-gradient.yaml new file mode 100644 index 000000000000..0879b2442f0c --- /dev/null +++ b/gfx/wr/wrench/reftests/tiles/simple-gradient.yaml @@ -0,0 +1,9 @@ +--- +root: + items: + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false diff --git a/gfx/wr/wrench/src/reftest.rs b/gfx/wr/wrench/src/reftest.rs index 9aae9551013f..ab8dcf9af516 100644 --- a/gfx/wr/wrench/src/reftest.rs +++ b/gfx/wr/wrench/src/reftest.rs @@ -44,9 +44,16 @@ impl ReftestOptions { } } +#[derive(Debug, Copy, Clone)] pub enum ReftestOp { + /// Expect that the images match the reference Equal, + /// Expect that the images *don't* match the reference NotEqual, + /// Expect that drawing the reference at different tiles sizes gives the same pixel exact result. + Accurate, + /// Expect that drawing the reference at different tiles sizes gives a *different* pixel exact result. + Inaccurate, } impl Display for ReftestOp { @@ -57,6 +64,8 @@ impl Display for ReftestOp { match *self { ReftestOp::Equal => "==".to_owned(), ReftestOp::NotEqual => "!=".to_owned(), + ReftestOp::Accurate => "**".to_owned(), + ReftestOp::Inaccurate => "!*".to_owned(), } ) } @@ -102,6 +111,62 @@ pub struct Reftest { allow_sacrificing_subpixel_aa: Option, } +impl Reftest { + /// Check the positive case (expecting equality) and report details if different + fn check_and_report_equality_failure( + &self, + comparison: ReftestImageComparison, + test: &ReftestImage, + reference: &ReftestImage, + ) -> bool { + match comparison { + ReftestImageComparison::Equal => { + true + } + ReftestImageComparison::NotEqual { max_difference, count_different } => { + if max_difference > self.max_difference || count_different > self.num_differences { + println!( + "{} | {} | {}: {}, {}: {}", + "REFTEST TEST-UNEXPECTED-FAIL", + self, + "image comparison, max difference", + max_difference, + "number of differing pixels", + count_different + ); + println!("REFTEST IMAGE 1 (TEST): {}", test.clone().create_data_uri()); + println!( + "REFTEST IMAGE 2 (REFERENCE): {}", + reference.clone().create_data_uri() + ); + println!("REFTEST TEST-END | {}", self); + + false + } else { + true + } + } + } + } + + /// Check the negative case (expecting inequality) and report details if same + fn check_and_report_inequality_failure( + &self, + comparison: ReftestImageComparison, + ) -> bool { + match comparison { + ReftestImageComparison::Equal => { + println!("REFTEST TEST-UNEXPECTED-FAIL | {} | image comparison", self); + println!("REFTEST TEST-END | {}", self); + false + } + ReftestImageComparison::NotEqual { .. } => { + true + } + } + } +} + impl Display for Reftest { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { let paths: Vec = self.test.iter().map(|t| t.display().to_string()).collect(); @@ -115,10 +180,13 @@ impl Display for Reftest { } } +#[derive(Clone)] pub struct ReftestImage { pub data: Vec, pub size: DeviceIntSize, } + +#[derive(Debug, Copy, Clone)] pub enum ReftestImageComparison { Equal, NotEqual { @@ -212,7 +280,7 @@ impl ReftestManifest { let mut max_difference = 0; let mut max_count = 0; - let mut op = ReftestOp::Equal; + let mut op = None; let mut font_render_mode = None; let mut extra_checks = vec![]; let mut disable_dual_source_blending = false; @@ -299,10 +367,16 @@ impl ReftestManifest { } } "==" => { - op = ReftestOp::Equal; + op = Some(ReftestOp::Equal); } "!=" => { - op = ReftestOp::NotEqual; + op = Some(ReftestOp::NotEqual); + } + "**" => { + op = Some(ReftestOp::Accurate); + } + "!*" => { + op = Some(ReftestOp::Inaccurate); } _ => { paths.push(dir.join(*token)); @@ -311,10 +385,13 @@ impl ReftestManifest { } // Don't try to add tests for include lines. - if paths.len() < 2 { - assert_eq!(paths.len(), 0, "Only one path provided: {:?}", paths[0]); - continue; - } + let op = match op { + Some(op) => op, + None => { + assert!(paths.is_empty(), format!("paths = {:?}", paths)); + continue; + } + }; // The reference is the last path provided. If multiple paths are // passed for the test, they render sequentially before being @@ -530,15 +607,52 @@ impl<'a> ReftestHarness<'a> { let mut images = vec![]; let mut results = vec![]; - for filename in t.test.iter() { - let output = self.render_yaml( - &filename, - test_size, - t.font_render_mode, - t.allow_mipmaps, - ); - images.push(output.image); - results.push(output.results); + match t.op { + ReftestOp::Equal | ReftestOp::NotEqual => { + // For equality tests, render each test image and store result + for filename in t.test.iter() { + let output = self.render_yaml( + &filename, + test_size, + t.font_render_mode, + t.allow_mipmaps, + ); + images.push(output.image); + results.push(output.results); + } + } + ReftestOp::Accurate | ReftestOp::Inaccurate => { + // For accuracy tests, render the reference yaml at an arbitrary series + // of tile sizes, and compare to the reference drawn at normal tile size. + let tile_sizes = [ + DeviceIntSize::new(128, 128), + DeviceIntSize::new(256, 256), + DeviceIntSize::new(512, 512), + ]; + + for tile_size in &tile_sizes { + self.wrench + .api + .send_debug_cmd( + DebugCommand::SetPictureTileSize(Some(*tile_size)) + ); + + let output = self.render_yaml( + &t.reference, + test_size, + t.font_render_mode, + t.allow_mipmaps, + ); + images.push(output.image); + results.push(output.results); + } + + self.wrench + .api + .send_debug_cmd( + DebugCommand::SetPictureTileSize(None) + ); + } } let reference = match reference_image { @@ -575,44 +689,50 @@ impl<'a> ReftestHarness<'a> { } } - let test = images.pop().unwrap(); - let comparison = test.compare(&reference); - match (&t.op, comparison) { - (&ReftestOp::Equal, ReftestImageComparison::Equal) => true, - ( - &ReftestOp::Equal, - ReftestImageComparison::NotEqual { - max_difference, - count_different, - }, - ) => if max_difference > t.max_difference || count_different > t.num_differences { - println!( - "{} | {} | {}: {}, {}: {}", - "REFTEST TEST-UNEXPECTED-FAIL", - t, - "image comparison, max difference", - max_difference, - "number of differing pixels", - count_different - ); - println!("REFTEST IMAGE 1 (TEST): {}", test.create_data_uri()); - println!( - "REFTEST IMAGE 2 (REFERENCE): {}", - reference.create_data_uri() - ); - println!("REFTEST TEST-END | {}", t); - - false - } else { - true - }, - (&ReftestOp::NotEqual, ReftestImageComparison::Equal) => { - println!("REFTEST TEST-UNEXPECTED-FAIL | {} | image comparison", t); - println!("REFTEST TEST-END | {}", t); - - false + match t.op { + ReftestOp::Equal => { + // Ensure that the final image matches the reference + let test = images.pop().unwrap(); + let comparison = test.compare(&reference); + t.check_and_report_equality_failure( + comparison, + &test, + &reference, + ) + } + ReftestOp::NotEqual => { + // Ensure that the final image *doesn't* match the reference + let test = images.pop().unwrap(); + let comparison = test.compare(&reference); + t.check_and_report_inequality_failure(comparison) + } + ReftestOp::Accurate => { + // Ensure that *all* images match the reference + for test in images.drain(..) { + let comparison = test.compare(&reference); + + if !t.check_and_report_equality_failure( + comparison, + &test, + &reference, + ) { + return false; + } + } + + true + } + ReftestOp::Inaccurate => { + // Ensure that at least one of the images doesn't match the reference + let mut found_mismatch = false; + + for test in images.drain(..) { + let comparison = test.compare(&reference); + found_mismatch |= t.check_and_report_inequality_failure(comparison); + } + + found_mismatch } - (&ReftestOp::NotEqual, ReftestImageComparison::NotEqual { .. }) => true, } }