Bug 1624468 - Add a fast path for more gradient types in WR r=gw

Differential Revision: https://phabricator.services.mozilla.com/D68945

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Bert Peers 2020-04-06 07:16:25 +00:00
Родитель 3b3f1af25e
Коммит 7f2e1717d9
16 изменённых файлов: 408 добавлений и 96 удалений

Просмотреть файл

@ -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);
if cache_item.texture_id == TextureSource::Invalid {
return;
}
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 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 prim_header_index = prim_headers.push(
&segment_prim_header,
z_id,
prim_user_data,
);
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,
);
}
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 prim_header_index = prim_headers.push(
&prim_header,
z_id,
prim_user_data,
);
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,

Просмотреть файл

@ -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<LinearGradientKey> 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<LinearGradientKey> 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<GradientStop> = 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<RenderTaskCacheEntryHandle>,
pub cache_segments: Vec<CachedGradientSegment>,
pub visible_tiles_range: GradientTileRange,
}

Просмотреть файл

@ -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() {

Просмотреть файл

@ -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]]

Просмотреть файл

@ -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]]

Просмотреть файл

@ -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]]

Просмотреть файл

@ -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]]

Просмотреть файл

@ -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]

Просмотреть файл

@ -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]

Просмотреть файл

@ -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]]

Просмотреть файл

@ -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]

Просмотреть файл

@ -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]

Просмотреть файл

@ -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]]

Просмотреть файл

@ -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

Просмотреть файл

@ -20,9 +20,10 @@ fuzzy-if(winWidget,0-1,0-37800) fuzzy-if(skiaContent,0-1,0-45000) == linear-size
== linear-stops-1a.html linear-stops-1-ref.html
== linear-stops-1b.html linear-stops-1-ref.html
== linear-stops-1c.html linear-stops-1-ref.html
== linear-stops-1d.html linear-stops-1-ref.html
== linear-stops-1e.html linear-stops-1-ref.html
== linear-stops-1f.html linear-stops-1-ref.html
# these gradients will be cached (in WebRender) and thus exhibit off-by-1 texture interpolation differences:
fuzzy(0-1,0-35100) == linear-stops-1d.html linear-stops-1-ref.html
fuzzy(0-1,0-35100) == linear-stops-1e.html linear-stops-1-ref.html
fuzzy(0-1,0-35100) == linear-stops-1f.html linear-stops-1-ref.html
fuzzy-if(!contentSameGfxBackendAsCanvas,0-3,0-88500) fuzzy-if(azureSkiaGL||skiaContent,0-3,0-89700) == linear-vertical-1a.html linear-vertical-1-ref.html
fuzzy-if(!contentSameGfxBackendAsCanvas,0-3,0-88500) fuzzy-if(azureSkiaGL||skiaContent,0-3,0-89700) == linear-vertical-1b.html linear-vertical-1-ref.html
fuzzy-if(!contentSameGfxBackendAsCanvas,0-3,0-88500) fuzzy-if(azureSkiaGL||skiaContent,0-3,0-89700) == linear-vertical-1c.html linear-vertical-1-ref.html

Просмотреть файл

@ -1,6 +1,6 @@
[gradient-move-stops.html]
expected:
if webrender: FAIL
if webrender and (os == "win"): FAIL
[border-image-repeat-round-2.html]
fuzzy: