diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index cfa766029e..54635b1e45 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -2268,53 +2268,69 @@ impl BatchBuilder { BlendMode::None }; - if let Some(ref cache_handle) = gradient.cache_handle { - let rt_cache_entry = ctx.resource_cache - .get_cached_render_task(cache_handle); - let cache_item = ctx.resource_cache - .get_texture_cache_item(&rt_cache_entry.handle); + if !gradient.cache_segments.is_empty() { - if cache_item.texture_id == TextureSource::Invalid { - return; - } + for segment in &gradient.cache_segments { + let ref cache_handle = segment.handle; + let rt_cache_entry = ctx.resource_cache + .get_cached_render_task(cache_handle); + let cache_item = ctx.resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); - let textures = BatchTextures::color(cache_item.texture_id); - let batch_kind = BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)); - let prim_user_data = ImageBrushData { - color_mode: ShaderColorMode::Image, - alpha_type: AlphaType::PremultipliedAlpha, - raster_space: RasterizationSpace::Local, - opacity: 1.0, - }.encode(); + if cache_item.texture_id == TextureSource::Invalid { + return; + } - let specific_resource_address = cache_item.uv_rect_handle.as_int(gpu_cache); - prim_header.specific_prim_address = gpu_cache.get_address(&ctx.globals.default_image_handle); + let textures = BatchTextures::color(cache_item.texture_id); + let batch_kind = BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)); + let prim_user_data = ImageBrushData { + color_mode: ShaderColorMode::Image, + alpha_type: AlphaType::PremultipliedAlpha, + raster_space: RasterizationSpace::Local, + opacity: 1.0, + }.encode(); + + let specific_resource_address = cache_item.uv_rect_handle.as_int(gpu_cache); + prim_header.specific_prim_address = gpu_cache.get_address(&ctx.globals.default_image_handle); + + let segment_local_clip_rect = prim_header.local_clip_rect.intersection(&segment.local_rect); + if segment_local_clip_rect.is_none() { + continue; + } - let prim_header_index = prim_headers.push( - &prim_header, - z_id, - prim_user_data, - ); + let segment_prim_header = PrimitiveHeader { + local_rect: segment.local_rect, + local_clip_rect: segment_local_clip_rect.unwrap(), + specific_prim_address: prim_header.specific_prim_address, + transform_id: prim_header.transform_id, + }; - let batch_key = BatchKey { - blend_mode: non_segmented_blend_mode, - kind: BatchKind::Brush(batch_kind), - textures, - }; + let prim_header_index = prim_headers.push( + &segment_prim_header, + z_id, + prim_user_data, + ); - self.add_brush_instance_to_batches( - batch_key, - batch_features, - bounding_rect, - z_id, - INVALID_SEGMENT_INDEX, - EdgeAaSegmentMask::all(), - clip_task_address.unwrap(), - BrushFlags::PERSPECTIVE_INTERPOLATION, - prim_header_index, - specific_resource_address, - prim_vis_mask, - ); + let batch_key = BatchKey { + blend_mode: non_segmented_blend_mode, + kind: BatchKind::Brush(batch_kind), + textures, + }; + + self.add_brush_instance_to_batches( + batch_key, + batch_features, + bounding_rect, + z_id, + INVALID_SEGMENT_INDEX, + EdgeAaSegmentMask::all(), + clip_task_address.unwrap(), + BrushFlags::PERSPECTIVE_INTERPOLATION, + prim_header_index, + specific_resource_address, + prim_vis_mask, + ); + } } else if gradient.visible_tiles_range.is_empty() { let batch_params = BrushBatchParameters::shared( BrushBatchKind::LinearGradient, diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 2942ba2855..88f838442f 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -67,6 +67,7 @@ pub struct FrameBuilderConfig { pub compositor_kind: CompositorKind, pub tile_size_override: Option, pub max_depth_ids: i32, + pub max_target_size: i32, } /// A set of common / global resources that are retained between diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 73ce7c1a2c..9dc072f4b7 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -4630,6 +4630,7 @@ impl PicturePrimitive { /// font size, etc. need to be scaled accordingly. fn adjust_scale_for_max_surface_size( raster_config: &RasterConfig, + max_target_size: i32, pic_rect: PictureRect, map_pic_to_raster: &SpaceMapper, map_raster_to_world: &SpaceMapper, @@ -4638,13 +4639,15 @@ impl PicturePrimitive { device_rect: &mut DeviceIntRect, unclipped: &mut DeviceRect) -> Option { - if raster_config.establishes_raster_root && - (device_rect.size.width > (MAX_SURFACE_SIZE as i32) || - device_rect.size.height > (MAX_SURFACE_SIZE as i32)) - { + let limit = if raster_config.establishes_raster_root { + MAX_SURFACE_SIZE as i32 + } else { + max_target_size + }; + if device_rect.size.width > limit || device_rect.size.height > limit { // round_out will grow by 1 integer pixel if origin is on a // fractional position, so keep that margin for error with -1: - let scale = (MAX_SURFACE_SIZE as f32 - 1.0) / + let scale = (limit as f32 - 1.0) / (i32::max(device_rect.size.width, device_rect.size.height) as f32); *device_pixel_scale = *device_pixel_scale * Scale::new(scale); let new_device_rect = device_rect.to_f32() * Scale::new(scale); @@ -4717,10 +4720,11 @@ impl PicturePrimitive { ); if let Some(scale) = adjust_scale_for_max_surface_size( - raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, - clipped_prim_bounding_rect, - &mut device_pixel_scale, &mut device_rect, &mut unclipped) - { + raster_config, frame_context.fb_config.max_target_size, + pic_rect, &map_pic_to_raster, &map_raster_to_world, + clipped_prim_bounding_rect, + &mut device_pixel_scale, &mut device_rect, &mut unclipped, + ) { blur_std_deviation = blur_std_deviation * scale; original_size = (original_size.to_f32() * scale).try_cast::().unwrap(); raster_config.root_scaling_factor = scale; @@ -4791,10 +4795,11 @@ impl PicturePrimitive { ); if let Some(scale) = adjust_scale_for_max_surface_size( - raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + raster_config, frame_context.fb_config.max_target_size, + pic_rect, &map_pic_to_raster, &map_raster_to_world, clipped_prim_bounding_rect, - &mut device_pixel_scale, &mut device_rect, &mut unclipped) - { + &mut device_pixel_scale, &mut device_rect, &mut unclipped, + ) { // std_dev adjusts automatically from using device_pixel_scale raster_config.root_scaling_factor = scale; } @@ -4889,10 +4894,11 @@ impl PicturePrimitive { PictureCompositeMode::Filter(..) => { if let Some(scale) = adjust_scale_for_max_surface_size( - raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + raster_config, frame_context.fb_config.max_target_size, + pic_rect, &map_pic_to_raster, &map_raster_to_world, clipped_prim_bounding_rect, - &mut device_pixel_scale, &mut clipped, &mut unclipped) - { + &mut device_pixel_scale, &mut clipped, &mut unclipped, + ) { raster_config.root_scaling_factor = scale; } @@ -4922,10 +4928,11 @@ impl PicturePrimitive { } PictureCompositeMode::ComponentTransferFilter(..) => { if let Some(scale) = adjust_scale_for_max_surface_size( - raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + raster_config, frame_context.fb_config.max_target_size, + pic_rect, &map_pic_to_raster, &map_raster_to_world, clipped_prim_bounding_rect, - &mut device_pixel_scale, &mut clipped, &mut unclipped) - { + &mut device_pixel_scale, &mut clipped, &mut unclipped, + ) { raster_config.root_scaling_factor = scale; } @@ -4965,37 +4972,56 @@ impl PicturePrimitive { let device_clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round(); for tile in tile_cache.tiles.values_mut() { - if !tile.is_visible { - continue; - } - // Get the world space rect that this tile will actually occupy on screem - let device_draw_rect = match device_clip_rect.intersection(&tile.device_valid_rect) { - Some(rect) => rect, - None => { - tile.is_visible = false; - continue; - } - }; + // Only check for occlusion on visible tiles that are fixed position. + if tile.is_visible && tile_cache.spatial_node_index == ROOT_SPATIAL_NODE_INDEX { + // Get the world space rect that this tile will actually occupy on screem + let device_draw_rect = device_clip_rect.intersection(&tile.device_valid_rect); + + // If that draw rect is occluded by some set of tiles in front of it, + // then mark it as not visible and skip drawing. When it's not occluded + // it will fail this test, and get rasterized by the render task setup + // code below. + match device_draw_rect { + Some(device_draw_rect) => { + if frame_state.composite_state.is_tile_occluded(tile.z_id, device_draw_rect) { + // If this tile has an allocated native surface, free it, since it's completely + // occluded. We will need to re-allocate this surface if it becomes visible, + // but that's likely to be rare (e.g. when there is no content display list + // for a frame or two during a tab switch). + let surface = tile.surface.as_mut().expect("no tile surface set!"); + + if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface { + if let Some(id) = id.take() { + frame_state.resource_cache.destroy_compositor_tile(id); + } + } - // If that draw rect is occluded by some set of tiles in front of it, - // then mark it as not visible and skip drawing. When it's not occluded - // it will fail this test, and get rasterized by the render task setup - // code below. - if frame_state.composite_state.is_tile_occluded(tile.z_id, device_draw_rect) { - // If this tile has an allocated native surface, free it, since it's completely - // occluded. We will need to re-allocate this surface if it becomes visible, - // but that's likely to be rare (e.g. when there is no content display list - // for a frame or two during a tab switch). - let surface = tile.surface.as_mut().expect("no tile surface set!"); - - if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface { - if let Some(id) = id.take() { - frame_state.resource_cache.destroy_compositor_tile(id); + tile.is_visible = false; + continue; + } + } + None => { + tile.is_visible = false; } } + } + + // If we get here, we want to ensure that the surface remains valid in the texture + // cache, _even if_ it's not visible due to clipping or being scrolled off-screen. + // This ensures that we retain valid tiles that are off-screen, but still in the + // display port of this tile cache instance. + if let Some(TileSurface::Texture { descriptor, .. }) = tile.surface.as_ref() { + if let SurfaceTextureDescriptor::TextureCache { ref handle, .. } = descriptor { + frame_state.resource_cache.texture_cache.request( + handle, + frame_state.gpu_cache, + ); + } + } - tile.is_visible = false; + // If the tile has been found to be off-screen / clipped, skip any further processing. + if !tile.is_visible { continue; } @@ -5228,10 +5254,11 @@ impl PicturePrimitive { PictureCompositeMode::MixBlend(..) | PictureCompositeMode::Blit(_) => { if let Some(scale) = adjust_scale_for_max_surface_size( - raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + raster_config, frame_context.fb_config.max_target_size, + pic_rect, &map_pic_to_raster, &map_raster_to_world, clipped_prim_bounding_rect, - &mut device_pixel_scale, &mut clipped, &mut unclipped) - { + &mut device_pixel_scale, &mut clipped, &mut unclipped, + ) { raster_config.root_scaling_factor = scale; } @@ -5262,10 +5289,11 @@ impl PicturePrimitive { PictureCompositeMode::SvgFilter(ref primitives, ref filter_datas) => { if let Some(scale) = adjust_scale_for_max_surface_size( - raster_config, pic_rect, &map_pic_to_raster, &map_raster_to_world, + raster_config, frame_context.fb_config.max_target_size, + pic_rect, &map_pic_to_raster, &map_raster_to_world, clipped_prim_bounding_rect, - &mut device_pixel_scale, &mut clipped, &mut unclipped) - { + &mut device_pixel_scale, &mut clipped, &mut unclipped, + ) { raster_config.root_scaling_factor = scale; } diff --git a/webrender/src/prim_store/gradient.rs b/webrender/src/prim_store/gradient.rs index b8d59c8030..9e2c27be55 100644 --- a/webrender/src/prim_store/gradient.rs +++ b/webrender/src/prim_store/gradient.rs @@ -13,11 +13,10 @@ use crate::frame_builder::FrameBuildingState; use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest}; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; use crate::internal_types::LayoutPrimitiveInfo; -use crate::prim_store::{BrushSegment, GradientTileRange, VectorKey}; +use crate::prim_store::{BrushSegment, CachedGradientSegment, GradientTileRange, VectorKey}; use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData}; use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore}; use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive}; -use crate::render_task_cache::RenderTaskCacheEntryHandle; use std::{hash, ops::{Deref, DerefMut}}; use crate::util::pack_as_float; @@ -152,7 +151,7 @@ impl From for LinearGradientTemplate { // gradient in a smaller task, and drawing as an image. // TODO(gw): Aim to reduce the constraints on fast path gradients in future, // although this catches the vast majority of gradients on real pages. - let mut supports_caching = + let supports_caching = // No repeating support in fast path item.extend_mode == ExtendMode::Clamp && // Gradient must cover entire primitive @@ -161,28 +160,15 @@ impl From for LinearGradientTemplate { // Must be a vertical or horizontal gradient (item.start_point.x.approx_eq(&item.end_point.x) || item.start_point.y.approx_eq(&item.end_point.y)) && - // Fast path supports a limited number of stops - item.stops.len() <= GRADIENT_FP_STOPS && // Fast path not supported on segmented (border-image) gradients. item.nine_patch.is_none(); - let mut prev_offset = None; // Convert the stops to more convenient representation // for the current gradient builder. let stops: Vec = item.stops.iter().map(|stop| { let color: ColorF = stop.color.into(); min_alpha = min_alpha.min(color.a); - // The fast path doesn't support hard color stops, yet. - // Since the length of the gradient is a fixed size (512 device pixels), if there - // is a hard stop you will see bilinear interpolation with this method, instead - // of an abrupt color change. - if prev_offset == Some(stop.offset) { - supports_caching = false; - } - - prev_offset = Some(stop.offset); - GradientStop { offset: stop.offset, color, @@ -318,7 +304,7 @@ impl InternablePrimitive for LinearGradient { _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind { let gradient_index = prim_store.linear_gradients.push(LinearGradientPrimitive { - cache_handle: None, + cache_segments: Vec::new(), visible_tiles_range: GradientTileRange::empty(), }); @@ -338,7 +324,7 @@ impl IsVisible for LinearGradient { #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] pub struct LinearGradientPrimitive { - pub cache_handle: Option, + pub cache_segments: Vec, pub visible_tiles_range: GradientTileRange, } diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index c9c16df149..82d17aa441 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -970,6 +970,13 @@ pub struct VisibleGradientTile { pub local_clip_rect: LayoutRect, } +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct CachedGradientSegment { + pub handle: RenderTaskCacheEntryHandle, + pub local_rect: LayoutRect, +} + /// Information about how to cache a border segment, /// along with the current render task cache entry. #[cfg_attr(feature = "capture", derive(Serialize))] @@ -3284,7 +3291,7 @@ impl PrimitiveStore { }; // Build the cache key, including information about the stops. - let mut stops = [GradientStopKey::empty(); GRADIENT_FP_STOPS]; + let mut stops = vec![GradientStopKey::empty(); prim_data.stops.len()]; // Reverse the stops as required, same as the gradient builder does // for the slow path. @@ -3302,35 +3309,124 @@ impl PrimitiveStore { } } - let cache_key = GradientCacheKey { - orientation, - start_stop_point: VectorKey { - x: start_point, - y: end_point, - }, - stops, - }; + // To support clamping, we need to make sure that quads are emitted for the + // segments before and after the 0.0...1.0 range of offsets. The loop below + // can handle that by duplicating the first and last point if necessary: + if start_point < 0.0 { + stops.insert(0, GradientStopKey { + offset: start_point, + color : stops[0].color + }); + } - // Request the render task each frame. - gradient.cache_handle = Some(frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size, - kind: RenderTaskCacheKeyKind::Gradient(cache_key), - }, - frame_state.gpu_cache, - frame_state.render_tasks, - None, - prim_data.stops_opacity.is_opaque, - |render_tasks| { - render_tasks.add().init(RenderTask::new_gradient( - size, - stops, - orientation, - start_point, - end_point, - )) + if end_point > 1.0 { + stops.push( GradientStopKey { + offset: end_point, + color : stops[stops.len()-1].color + }); + } + + gradient.cache_segments.clear(); + + let mut first_stop = 0; + // look for an inclusive range of stops [first_stop, last_stop]. + // once first_stop points at (or past) the last stop, we're done. + while first_stop < stops.len()-1 { + + // if the entire segment starts at an offset that's past the primitive's + // end_point, we're done. + if stops[first_stop].offset > end_point { + break; } - )); + + // accumulate stops until we have GRADIENT_FP_STOPS of them, or we hit + // a hard stop: + let mut last_stop = first_stop; + let mut hard_stop = false; // did we stop on a hard stop? + while last_stop < stops.len()-1 && + last_stop - first_stop + 1 < GRADIENT_FP_STOPS + { + if stops[last_stop+1].offset == stops[last_stop].offset { + hard_stop = true; + break; + } + + last_stop = last_stop + 1; + } + + let num_stops = last_stop - first_stop + 1; + + // repeated hard stops at the same offset, skip + if num_stops == 0 { + first_stop = last_stop + 1; + continue; + } + + // if the last stop offset is before start_point, the segment's not visible: + if stops[last_stop].offset < start_point { + first_stop = if hard_stop { last_stop+1 } else { last_stop }; + continue; + } + + let segment_start_point = start_point.max(stops[first_stop].offset); + let segment_end_point = end_point .min(stops[last_stop ].offset); + + let mut segment_stops = [GradientStopKey::empty(); GRADIENT_FP_STOPS]; + for i in 0..num_stops { + segment_stops[i] = stops[first_stop + i]; + } + + let cache_key = GradientCacheKey { + orientation, + start_stop_point: VectorKey { + x: segment_start_point, + y: segment_end_point, + }, + stops: segment_stops, + }; + + let mut prim_origin = prim_instance.prim_origin; + let mut prim_size = prim_data.common.prim_size; + + let inv_length = 1.0 / ( end_point - start_point ); + if orientation == LineOrientation::Horizontal { + prim_origin.x += ( segment_start_point - start_point ) * inv_length * prim_size.width; + prim_size.width *= ( segment_end_point - segment_start_point ) * inv_length; + } else { + prim_origin.y += ( segment_start_point - start_point ) * inv_length * prim_size.height; + prim_size.height *= ( segment_end_point - segment_start_point ) * inv_length; + } + + let local_rect = LayoutRect::new( prim_origin, prim_size ); + + // Request the render task each frame. + gradient.cache_segments.push( + CachedGradientSegment { + handle: frame_state.resource_cache.request_render_task( + RenderTaskCacheKey { + size, + kind: RenderTaskCacheKeyKind::Gradient(cache_key), + }, + frame_state.gpu_cache, + frame_state.render_tasks, + None, + prim_data.stops_opacity.is_opaque, + |render_tasks| { + render_tasks.add().init(RenderTask::new_gradient( + size, + segment_stops, + orientation, + segment_start_point, + segment_end_point, + )) + }), + local_rect: local_rect, + } + ); + + // if ending on a hardstop, skip past it for the start of the next run: + first_stop = if hard_stop { last_stop + 1 } else { last_stop }; + } } if prim_data.tile_spacing != LayoutSize::zero() { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index fe17540e59..49becbd30d 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -2276,6 +2276,7 @@ impl Renderer { compositor_kind, tile_size_override: None, max_depth_ids: device.max_depth_ids(), + max_target_size: max_texture_size, }; info!("WR {:?}", config); diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index 7498f9dae5..3caf68ab60 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -309,6 +309,7 @@ impl BuiltScene { compositor_kind: CompositorKind::default(), tile_size_override: None, max_depth_ids: 0, + max_target_size: 0, }, } } diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml index 933c14b17f..d64c8d5e6f 100644 --- a/wrench/Cargo.toml +++ b/wrench/Cargo.toml @@ -57,7 +57,7 @@ font-loader = "0.7" [package.metadata.android] package_name = "org.mozilla.wrench" label = "Wrench" -android_version = 28 +android_version = 29 target_sdk_version = 18 min_sdk_version = 18 fullscreen = true diff --git a/wrench/reftests/gradient/gradient_cache_5stops.yaml b/wrench/reftests/gradient/gradient_cache_5stops.yaml new file mode 100644 index 0000000000..d448723002 --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_5stops.yaml @@ -0,0 +1,13 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 960 540 + start: 0 0 + end: 960 0 + stops: [0.0, red, + 0.25, green, + 0.5, blue, + 0.75, [40,40,40,1], + 1.0, [100,200,50,1]] + diff --git a/wrench/reftests/gradient/gradient_cache_5stops_ref.yaml b/wrench/reftests/gradient/gradient_cache_5stops_ref.yaml new file mode 100644 index 0000000000..34b6b0e01c --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_5stops_ref.yaml @@ -0,0 +1,18 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 480 540 + start: 0 0 + end: 480 0 + stops: [0.0, red, + 0.5, green, + 1.0, blue] + - type: gradient + bounds: 480 0 480 540 + start: 0 0 + end: 480 0 + stops: [ 0.0, blue, + 0.5, [40,40,40,1], + 1.0, [100,200,50,1]] + diff --git a/wrench/reftests/gradient/gradient_cache_5stops_vertical.yaml b/wrench/reftests/gradient/gradient_cache_5stops_vertical.yaml new file mode 100644 index 0000000000..dd2c8b7c9d --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_5stops_vertical.yaml @@ -0,0 +1,13 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 960 540 + start: 0 0 + end: 0 540 + stops: [0.0, red, + 0.25, green, + 0.5, blue, + 0.75, [40,40,40,1], + 1.0, [100,200,50,1]] + diff --git a/wrench/reftests/gradient/gradient_cache_5stops_vertical_ref.yaml b/wrench/reftests/gradient/gradient_cache_5stops_vertical_ref.yaml new file mode 100644 index 0000000000..704b5be2f6 --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_5stops_vertical_ref.yaml @@ -0,0 +1,18 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 960 270 + start: 0 0 + end: 0 270 + stops: [0.0, red, + 0.5, green, + 1.0, blue] + - type: gradient + bounds: 0 270 960 270 + start: 0 0 + end: 0 270 + stops: [ 0.0, blue, + 0.5, [40,40,40,1], + 1.0, [100,200,50,1]] + diff --git a/wrench/reftests/gradient/gradient_cache_clamp.yaml b/wrench/reftests/gradient/gradient_cache_clamp.yaml new file mode 100644 index 0000000000..1c55a269a1 --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_clamp.yaml @@ -0,0 +1,20 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 400 200 + start: 0 100 + end: 100 100 + stops: [0.0, blue, 1.0, blue, 1.0, red] + - type: gradient + bounds: 0 300 400 200 + start: 100 100 + end: 200 100 + stops: [0.0, blue, 1.0, blue, 1.0, red] + - type: gradient + bounds: 0 600 200 400 + start: 0 100 + end: 0 300 + stops: [ + 0.0, blue, + 1.0, red] diff --git a/wrench/reftests/gradient/gradient_cache_clamp_ref.yaml b/wrench/reftests/gradient/gradient_cache_clamp_ref.yaml new file mode 100644 index 0000000000..4631192cd8 --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_clamp_ref.yaml @@ -0,0 +1,30 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 400 200 + start: 0 100 + end: 400 100 + stops: [ + 0.0, blue, + 0.25, blue, + 0.25, red, + 1.0, red] + - type: gradient + bounds: 0 300 400 200 + start: 0 100 + end: 400 100 + stops: [ + 0.0, blue, + 0.5, blue, + 0.5, red, + 1.0, red] + - type: gradient + bounds: 0 600 200 400 + start: 0 0 + end: 0 400 + stops: [ + 0.0, blue, + 0.25, blue, + 0.75, red, + 1.0, red] diff --git a/wrench/reftests/gradient/gradient_cache_hardstop.yaml b/wrench/reftests/gradient/gradient_cache_hardstop.yaml new file mode 100644 index 0000000000..53c908fb22 --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_hardstop.yaml @@ -0,0 +1,19 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 960 540 + start: 0 0 + end: 960 0 + stops: [0.0, red, + 0.125, yellow, + 0.25, red, + 0.25, green, + 0.375, yellow, + 0.5, green, + 0.5, blue, + 0.625, yellow, + 0.75, blue, + 0.75, white, + 1.0, [100,200,50,1]] + diff --git a/wrench/reftests/gradient/gradient_cache_hardstop_clip.yaml b/wrench/reftests/gradient/gradient_cache_hardstop_clip.yaml new file mode 100644 index 0000000000..3e7a2e946f --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_hardstop_clip.yaml @@ -0,0 +1,21 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 960 540 + start: 0 0 + end: 960 0 + stops: [0.0, red, + 0.125, yellow, + 0.25, red, + 0.25, green, + 0.375, yellow, + 0.5, green, + 0.5, blue, + 0.625, yellow, + 0.75, blue, + 0.75, white, + 1.0, [100,200,50,1]] + complex-clip: + rect: [100, 100, 760, 340] + radius: [32, 32] diff --git a/wrench/reftests/gradient/gradient_cache_hardstop_clip_ref.yaml b/wrench/reftests/gradient/gradient_cache_hardstop_clip_ref.yaml new file mode 100644 index 0000000000..2b27c5649c --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_hardstop_clip_ref.yaml @@ -0,0 +1,28 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 480 540 + start: 0 0 + end: 480 0 + stops: [0.0, red, + 0.25, yellow, + 0.5, red, + 0.5, green, + 0.75, yellow, + 1.0, green] + complex-clip: + rect: [100, 100, 760, 340] + radius: [32, 32] + - type: gradient + bounds: 480 0 480 540 + start: 0 0 + end: 480 0 + stops: [0.0, blue, + 0.25, yellow, + 0.5, blue, + 0.5, white, + 1.0, [100,200,50,1]] + complex-clip: + rect: [100, 100, 760, 340] + radius: [32, 32] diff --git a/wrench/reftests/gradient/gradient_cache_hardstop_ref.yaml b/wrench/reftests/gradient/gradient_cache_hardstop_ref.yaml new file mode 100644 index 0000000000..e4b3928046 --- /dev/null +++ b/wrench/reftests/gradient/gradient_cache_hardstop_ref.yaml @@ -0,0 +1,24 @@ +--- +root: + items: + - type: gradient + bounds: 0 0 480 540 + start: 0 0 + end: 480 0 + stops: [0.0, red, + 0.25, yellow, + 0.5, red, + 0.5, green, + 0.75, yellow, + 1.0, green] + - type: gradient + bounds: 480 0 480 540 + start: 0 0 + end: 480 0 + stops: [0.0, blue, + 0.25, yellow, + 0.5, blue, + 0.5, white, + 1.0, [100,200,50,1]] + + diff --git a/wrench/reftests/gradient/reftest.list b/wrench/reftests/gradient/reftest.list index ac0b6454b4..c3b3eee9cd 100644 --- a/wrench/reftests/gradient/reftest.list +++ b/wrench/reftests/gradient/reftest.list @@ -16,7 +16,7 @@ platform(linux,mac) fuzzy(1,35000) == linear-stops.yaml linear-stops-ref.png == linear-clamp-1b.yaml linear-clamp-1-ref.yaml == linear-clamp-2.yaml linear-clamp-2-ref.yaml -== linear-hard-stop.yaml linear-hard-stop-ref.png +fuzzy-range(<=1,*4800) == linear-hard-stop.yaml linear-hard-stop-ref.png # dithering requires us to fuzz here fuzzy(1,20000) == linear.yaml linear-ref.yaml @@ -85,3 +85,12 @@ fuzzy(255,166) == conic-angle.yaml conic-angle.png fuzzy(1,1) == conic-angle-wraparound.yaml conic-angle.yaml fuzzy(1,1) == conic-angle-wraparound-negative.yaml conic-angle.yaml fuzzy(1,115) == conic-color-wheel.yaml conic-color-wheel.png + +# gradient caching tests +# replaces a computed gradient by a sampled texture, so a lot of off-by-one +# variation from interpolation, which is fine: +fuzzy-range(<=1,*195000) == gradient_cache_5stops.yaml gradient_cache_5stops_ref.yaml +fuzzy-range(<=1,*169000) == gradient_cache_5stops_vertical.yaml gradient_cache_5stops_vertical_ref.yaml +== gradient_cache_hardstop.yaml gradient_cache_hardstop_ref.yaml +== gradient_cache_hardstop_clip.yaml gradient_cache_hardstop_clip_ref.yaml +== gradient_cache_clamp.yaml gradient_cache_clamp_ref.yaml