summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender/src/prepare.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/webrender/src/prepare.rs')
-rw-r--r--gfx/wr/webrender/src/prepare.rs313
1 files changed, 163 insertions, 150 deletions
diff --git a/gfx/wr/webrender/src/prepare.rs b/gfx/wr/webrender/src/prepare.rs
index f32c94073e..a59eca0670 100644
--- a/gfx/wr/webrender/src/prepare.rs
+++ b/gfx/wr/webrender/src/prepare.rs
@@ -28,18 +28,18 @@ use crate::prim_store::line_dec::MAX_LINE_DECORATION_RESOLUTION;
use crate::prim_store::*;
use crate::prim_store::gradient::GradientGpuBlockBuilder;
use crate::render_backend::DataStores;
-use crate::render_task_graph::{RenderTaskId};
+use crate::render_task_graph::RenderTaskId;
use crate::render_task_cache::RenderTaskCacheKeyKind;
use crate::render_task_cache::{RenderTaskCacheKey, to_cache_size, RenderTaskParent};
use crate::render_task::{RenderTaskKind, RenderTask, SubPass, MaskSubPass, EmptyTask};
-use crate::renderer::{GpuBufferBuilder, GpuBufferAddress};
+use crate::renderer::{GpuBufferBuilderF, GpuBufferAddress};
use crate::segment::{EdgeAaSegmentMask, SegmentBuilder};
use crate::space::SpaceMapper;
use crate::util::{clamp_to_scale_factor, pack_as_float, MaxRect};
use crate::visibility::{compute_conservative_visible_rect, PrimitiveVisibility, VisibilityState};
-const MAX_MASK_SIZE: f32 = 4096.0;
+const MAX_MASK_SIZE: i32 = 4096;
const MIN_BRUSH_SPLIT_SIZE: f32 = 256.0;
const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
@@ -141,14 +141,32 @@ fn can_use_clip_chain_for_quad_path(
true
}
+/// Describes how clipping affects the rendering of a quad primitive.
+///
+/// As a general rule, parts of the quad that require masking are prerendered in an
+/// intermediate target and the mask is applied using multiplicative blending to
+/// the intermediate result before compositing it into the destination target.
+///
+/// Each segment can opt in or out of masking independently.
#[derive(Debug, Copy, Clone)]
pub enum QuadRenderStrategy {
+ /// The quad is not affected by any mask and is drawn directly in the destination
+ /// target.
Direct,
+ /// The quad is drawn entirely in an intermediate target and a mask is applied
+ /// before compositing in the destination target.
Indirect,
+ /// A rounded rectangle clip is applied to the quad primitive via a nine-patch.
+ /// The segments of the nine-patch that require a mask are rendered and masked in
+ /// an intermediate target, while other segments are drawn directly in the destination
+ /// target.
NinePatch {
radius: LayoutVector2D,
clip_rect: LayoutRect,
},
+ /// Split the primitive into coarse tiles so that each tile independently
+ /// has the opportunity to be drawn directly in the destination target or
+ /// via an intermediate target if it is affected by a mask.
Tiled {
x_tiles: u16,
y_tiles: u16,
@@ -163,69 +181,67 @@ fn get_prim_render_strategy(
can_use_nine_patch: bool,
spatial_tree: &SpatialTree,
) -> QuadRenderStrategy {
- if clip_chain.needs_mask {
- fn tile_count_for_size(size: f32) -> u16 {
- (size / MIN_BRUSH_SPLIT_SIZE).min(4.0).max(1.0).ceil() as u16
- }
+ if !clip_chain.needs_mask {
+ return QuadRenderStrategy::Direct
+ }
- let prim_coverage_size = clip_chain.pic_coverage_rect.size();
- let x_tiles = tile_count_for_size(prim_coverage_size.width);
- let y_tiles = tile_count_for_size(prim_coverage_size.height);
- let try_split_prim = x_tiles > 1 || y_tiles > 1;
-
- if try_split_prim {
- if can_use_nine_patch {
- if clip_chain.clips_range.count == 1 {
- let clip_instance = clip_store.get_instance_from_range(&clip_chain.clips_range, 0);
- let clip_node = &data_stores.clip[clip_instance.handle];
-
- if let ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip, rect, .. } = clip_node.item.kind {
- let max_corner_width = radius.top_left.width
- .max(radius.bottom_left.width)
- .max(radius.top_right.width)
- .max(radius.bottom_right.width);
- let max_corner_height = radius.top_left.height
- .max(radius.bottom_left.height)
- .max(radius.top_right.height)
- .max(radius.bottom_right.height);
-
- if max_corner_width <= 0.5 * rect.size().width &&
- max_corner_height <= 0.5 * rect.size().height {
-
- let clip_prim_coords_match = spatial_tree.is_matching_coord_system(
- prim_spatial_node_index,
- clip_node.item.spatial_node_index,
- );
+ fn tile_count_for_size(size: f32) -> u16 {
+ (size / MIN_BRUSH_SPLIT_SIZE).min(4.0).max(1.0).ceil() as u16
+ }
- if clip_prim_coords_match {
- let map_clip_to_prim = SpaceMapper::new_with_target(
- prim_spatial_node_index,
- clip_node.item.spatial_node_index,
- LayoutRect::max_rect(),
- spatial_tree,
- );
+ let prim_coverage_size = clip_chain.pic_coverage_rect.size();
+ let x_tiles = tile_count_for_size(prim_coverage_size.width);
+ let y_tiles = tile_count_for_size(prim_coverage_size.height);
+ let try_split_prim = x_tiles > 1 || y_tiles > 1;
- if let Some(rect) = map_clip_to_prim.map(&rect) {
- return QuadRenderStrategy::NinePatch {
- radius: LayoutVector2D::new(max_corner_width, max_corner_height),
- clip_rect: rect,
- };
- }
- }
- }
+ if !try_split_prim {
+ return QuadRenderStrategy::Indirect;
+ }
+
+ if can_use_nine_patch && clip_chain.clips_range.count == 1 {
+ let clip_instance = clip_store.get_instance_from_range(&clip_chain.clips_range, 0);
+ let clip_node = &data_stores.clip[clip_instance.handle];
+
+ if let ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip, rect, .. } = clip_node.item.kind {
+ let max_corner_width = radius.top_left.width
+ .max(radius.bottom_left.width)
+ .max(radius.top_right.width)
+ .max(radius.bottom_right.width);
+ let max_corner_height = radius.top_left.height
+ .max(radius.bottom_left.height)
+ .max(radius.top_right.height)
+ .max(radius.bottom_right.height);
+
+ if max_corner_width <= 0.5 * rect.size().width &&
+ max_corner_height <= 0.5 * rect.size().height {
+
+ let clip_prim_coords_match = spatial_tree.is_matching_coord_system(
+ prim_spatial_node_index,
+ clip_node.item.spatial_node_index,
+ );
+
+ if clip_prim_coords_match {
+ let map_clip_to_prim = SpaceMapper::new_with_target(
+ prim_spatial_node_index,
+ clip_node.item.spatial_node_index,
+ LayoutRect::max_rect(),
+ spatial_tree,
+ );
+
+ if let Some(rect) = map_clip_to_prim.map(&rect) {
+ return QuadRenderStrategy::NinePatch {
+ radius: LayoutVector2D::new(max_corner_width, max_corner_height),
+ clip_rect: rect,
+ };
}
}
}
-
- QuadRenderStrategy::Tiled {
- x_tiles,
- y_tiles,
- }
- } else {
- QuadRenderStrategy::Indirect
}
- } else {
- QuadRenderStrategy::Direct
+ }
+
+ QuadRenderStrategy::Tiled {
+ x_tiles,
+ y_tiles,
}
}
@@ -452,7 +468,7 @@ fn prepare_interned_prim_for_render(
kind: RenderTaskCacheKeyKind::LineDecoration(cache_key.clone()),
},
frame_state.gpu_cache,
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
frame_state.rg_builder,
None,
false,
@@ -607,7 +623,7 @@ fn prepare_interned_prim_for_render(
handles.push(frame_state.resource_cache.request_render_task(
cache_key,
frame_state.gpu_cache,
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
frame_state.rg_builder,
None,
false, // TODO(gw): We don't calculate opacity for borders yet!
@@ -764,7 +780,7 @@ fn prepare_interned_prim_for_render(
// the written block count) to gpu-buffer, we could add a trait for
// writing typed data?
let main_prim_address = write_prim_blocks(
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
prim_data.common.prim_rect,
prim_instance.vis.clip_chain.local_clip_rect,
premul_color,
@@ -787,24 +803,21 @@ fn prepare_interned_prim_for_render(
}
QuadRenderStrategy::Indirect => {
let surface = &frame_state.surfaces[pic_context.surface_index.0];
- let clipped_surface_rect = surface.get_surface_rect(
+ let Some(clipped_surface_rect) = surface.get_surface_rect(
&prim_instance.vis.clip_chain.pic_coverage_rect,
frame_context.spatial_tree,
- ).expect("bug: what can cause this?");
-
- let p0 = clipped_surface_rect.min.floor();
- let p1 = clipped_surface_rect.max.ceil();
+ ) else {
+ return;
+ };
- let x0 = p0.x;
- let y0 = p0.y;
- let x1 = p1.x;
- let y1 = p1.y;
+ let p0 = clipped_surface_rect.min.to_f32();
+ let p1 = clipped_surface_rect.max.to_f32();
let segment = add_segment(
- x0,
- y0,
- x1,
- y1,
+ p0.x,
+ p0.y,
+ p1.x,
+ p1.y,
true,
prim_instance,
prim_spatial_node_index,
@@ -820,7 +833,7 @@ fn prepare_interned_prim_for_render(
add_composite_prim(
prim_instance_index,
- LayoutRect::new(LayoutPoint::new(x0, y0), LayoutPoint::new(x1, y1)),
+ LayoutRect::new(p0.cast_unit(), p1.cast_unit()),
premul_color,
quad_flags,
frame_state,
@@ -831,10 +844,12 @@ fn prepare_interned_prim_for_render(
QuadRenderStrategy::Tiled { x_tiles, y_tiles } => {
let surface = &frame_state.surfaces[pic_context.surface_index.0];
- let clipped_surface_rect = surface.get_surface_rect(
+ let Some(clipped_surface_rect) = surface.get_surface_rect(
&prim_instance.vis.clip_chain.pic_coverage_rect,
frame_context.spatial_tree,
- ).expect("bug: what can cause this?");
+ ) else {
+ return;
+ };
let unclipped_surface_rect = surface.map_to_device_rect(
&prim_instance.vis.clip_chain.pic_coverage_rect,
@@ -843,21 +858,21 @@ fn prepare_interned_prim_for_render(
scratch.quad_segments.clear();
- let mut x_coords = vec![clipped_surface_rect.min.x.round()];
- let mut y_coords = vec![clipped_surface_rect.min.y.round()];
+ let mut x_coords = vec![clipped_surface_rect.min.x];
+ let mut y_coords = vec![clipped_surface_rect.min.y];
- let dx = (clipped_surface_rect.max.x - clipped_surface_rect.min.x) / x_tiles as f32;
- let dy = (clipped_surface_rect.max.y - clipped_surface_rect.min.y) / y_tiles as f32;
+ let dx = (clipped_surface_rect.max.x - clipped_surface_rect.min.x) as f32 / x_tiles as f32;
+ let dy = (clipped_surface_rect.max.y - clipped_surface_rect.min.y) as f32 / y_tiles as f32;
- for x in 1 .. x_tiles {
- x_coords.push((clipped_surface_rect.min.x + x as f32 * dx).round());
+ for x in 1 .. (x_tiles as i32) {
+ x_coords.push((clipped_surface_rect.min.x as f32 + x as f32 * dx).round() as i32);
}
- for y in 1 .. y_tiles {
- y_coords.push((clipped_surface_rect.min.y + y as f32 * dy).round());
+ for y in 1 .. (y_tiles as i32) {
+ y_coords.push((clipped_surface_rect.min.y as f32 + y as f32 * dy).round() as i32);
}
- x_coords.push(clipped_surface_rect.max.x.round());
- y_coords.push(clipped_surface_rect.max.y.round());
+ x_coords.push(clipped_surface_rect.max.x);
+ y_coords.push(clipped_surface_rect.max.y);
for y in 0 .. y_coords.len()-1 {
let y0 = y_coords[y];
@@ -877,18 +892,11 @@ fn prepare_interned_prim_for_render(
let create_task = true;
- let r = DeviceRect::new(DevicePoint::new(x0, y0), DevicePoint::new(x1, y1));
-
- let x0 = r.min.x;
- let y0 = r.min.y;
- let x1 = r.max.x;
- let y1 = r.max.y;
-
let segment = add_segment(
- x0,
- y0,
- x1,
- y1,
+ x0 as f32,
+ y0 as f32,
+ x1 as f32,
+ y1 as f32,
create_task,
prim_instance,
prim_spatial_node_index,
@@ -917,10 +925,12 @@ fn prepare_interned_prim_for_render(
}
QuadRenderStrategy::NinePatch { clip_rect, radius } => {
let surface = &frame_state.surfaces[pic_context.surface_index.0];
- let clipped_surface_rect = surface.get_surface_rect(
+ let Some(clipped_surface_rect) = surface.get_surface_rect(
&prim_instance.vis.clip_chain.pic_coverage_rect,
frame_context.spatial_tree,
- ).expect("bug: what can cause this?");
+ ) else {
+ return;
+ };
let unclipped_surface_rect = surface.map_to_device_rect(
&prim_instance.vis.clip_chain.pic_coverage_rect,
@@ -943,17 +953,17 @@ fn prepare_interned_prim_for_render(
let surface_rect_0 = surface.map_to_device_rect(
&pic_corner_0,
frame_context.spatial_tree,
- );
+ ).round_out().to_i32();
let surface_rect_1 = surface.map_to_device_rect(
&pic_corner_1,
frame_context.spatial_tree,
- );
+ ).round_out().to_i32();
- let p0 = surface_rect_0.min.floor();
- let p1 = surface_rect_0.max.ceil();
- let p2 = surface_rect_1.min.floor();
- let p3 = surface_rect_1.max.ceil();
+ let p0 = surface_rect_0.min;
+ let p1 = surface_rect_0.max;
+ let p2 = surface_rect_1.min;
+ let p3 = surface_rect_1.max;
let mut x_coords = [p0.x, p1.x, p2.x, p3.x];
let mut y_coords = [p0.y, p1.y, p2.y, p3.y];
@@ -985,7 +995,10 @@ fn prepare_interned_prim_for_render(
true
};
- let r = DeviceRect::new(DevicePoint::new(x0, y0), DevicePoint::new(x1, y1));
+ let r = DeviceIntRect::new(
+ DeviceIntPoint::new(x0, y0),
+ DeviceIntPoint::new(x1, y1),
+ );
let r = match r.intersection(&clipped_surface_rect) {
Some(r) => r,
@@ -994,16 +1007,11 @@ fn prepare_interned_prim_for_render(
}
};
- let x0 = r.min.x;
- let y0 = r.min.y;
- let x1 = r.max.x;
- let y1 = r.max.y;
-
let segment = add_segment(
- x0,
- y0,
- x1,
- y1,
+ r.min.x as f32,
+ r.min.y as f32,
+ r.max.x as f32,
+ r.max.y as f32,
create_task,
prim_instance,
prim_spatial_node_index,
@@ -1138,7 +1146,7 @@ fn prepare_interned_prim_for_render(
let stops_address = GradientGpuBlockBuilder::build(
prim_data.reverse_stops,
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
&prim_data.stops,
);
@@ -1296,8 +1304,8 @@ fn prepare_interned_prim_for_render(
.clipped_local_rect
.cast_unit();
- let main_prim_address = write_prim_blocks(
- frame_state.frame_gpu_data,
+ let prim_address_f = write_prim_blocks(
+ &mut frame_state.frame_gpu_data.f32,
prim_local_rect,
prim_instance.vis.clip_chain.local_clip_rect,
PremultipliedColorF::WHITE,
@@ -1333,7 +1341,7 @@ fn prepare_interned_prim_for_render(
let masks = MaskSubPass {
clip_node_range,
prim_spatial_node_index,
- main_prim_address,
+ prim_address_f,
};
// Add the mask as a sub-pass of the picture
@@ -1353,30 +1361,22 @@ fn prepare_interned_prim_for_render(
let device_pixel_scale = surface.device_pixel_scale;
let raster_spatial_node_index = surface.raster_spatial_node_index;
- let clipped_surface_rect = surface.get_surface_rect(
+ let Some(clipped_surface_rect) = surface.get_surface_rect(
&coverage_rect,
frame_context.spatial_tree,
- ).expect("bug: what can cause this?");
-
- let p0 = clipped_surface_rect.min.floor();
- let x0 = p0.x;
- let y0 = p0.y;
-
- let content_origin = DevicePoint::new(x0, y0);
+ ) else {
+ return;
+ };
// Draw a normal screens-space mask to an alpha target that
// can be sampled when compositing this picture.
let empty_task = EmptyTask {
- content_origin,
+ content_origin: clipped_surface_rect.min.to_f32(),
device_pixel_scale,
raster_spatial_node_index,
};
- let p1 = clipped_surface_rect.max.ceil();
- let x1 = p1.x;
- let y1 = p1.y;
-
- let task_size = DeviceSize::new(x1 - x0, y1 - y0).round().to_i32();
+ let task_size = clipped_surface_rect.size();
let clip_task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic(
task_size,
@@ -1406,7 +1406,7 @@ fn prepare_interned_prim_for_render(
let masks = MaskSubPass {
clip_node_range,
prim_spatial_node_index,
- main_prim_address,
+ prim_address_f,
};
let clip_task = frame_state.rg_builder.get_task_mut(clip_task_id);
@@ -1814,13 +1814,19 @@ pub fn update_clip_task(
unadjusted_device_rect,
device_pixel_scale,
);
+
+ if device_rect.size().to_i32().is_empty() {
+ log::warn!("Bad adjusted clip task size {:?} (was {:?})", device_rect.size(), unadjusted_device_rect.size());
+ return false;
+ }
+
let clip_task_id = RenderTaskKind::new_mask(
device_rect,
instance.vis.clip_chain.clips_range,
root_spatial_node_index,
frame_state.clip_store,
frame_state.gpu_cache,
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
frame_state.resource_cache,
frame_state.rg_builder,
&mut data_stores.clip,
@@ -1865,7 +1871,7 @@ pub fn update_brush_segment_clip_task(
return ClipMaskKind::None;
}
- let device_rect = match frame_state.surfaces[surface_index.0].get_surface_rect(
+ let unadjusted_device_rect = match frame_state.surfaces[surface_index.0].get_surface_rect(
&clip_chain.pic_coverage_rect,
frame_context.spatial_tree,
) {
@@ -1873,7 +1879,12 @@ pub fn update_brush_segment_clip_task(
None => return ClipMaskKind::Clipped,
};
- let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(device_rect, device_pixel_scale);
+ let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(unadjusted_device_rect, device_pixel_scale);
+
+ if device_rect.size().to_i32().is_empty() {
+ log::warn!("Bad adjusted mask size {:?} (was {:?})", device_rect.size(), unadjusted_device_rect.size());
+ return ClipMaskKind::Clipped;
+ }
let clip_task_id = RenderTaskKind::new_mask(
device_rect,
@@ -1881,7 +1892,7 @@ pub fn update_brush_segment_clip_task(
root_spatial_node_index,
frame_state.clip_store,
frame_state.gpu_cache,
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
frame_state.resource_cache,
frame_state.rg_builder,
clip_data_store,
@@ -2101,15 +2112,17 @@ fn build_segments_if_needed(
}
// Ensures that the size of mask render tasks are within MAX_MASK_SIZE.
-fn adjust_mask_scale_for_max_size(device_rect: DeviceRect, device_pixel_scale: DevicePixelScale) -> (DeviceRect, DevicePixelScale) {
+fn adjust_mask_scale_for_max_size(device_rect: DeviceIntRect, device_pixel_scale: DevicePixelScale) -> (DeviceIntRect, DevicePixelScale) {
if device_rect.width() > MAX_MASK_SIZE || device_rect.height() > MAX_MASK_SIZE {
// 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_MASK_SIZE - 1.0) /
- f32::max(device_rect.width(), device_rect.height());
+ let device_rect_f = device_rect.to_f32();
+ let scale = (MAX_MASK_SIZE - 1) as f32 /
+ f32::max(device_rect_f.width(), device_rect_f.height());
let new_device_pixel_scale = device_pixel_scale * Scale::new(scale);
- let new_device_rect = (device_rect.to_f32() * Scale::new(scale))
- .round_out();
+ let new_device_rect = (device_rect_f * Scale::new(scale))
+ .round_out()
+ .to_i32();
(new_device_rect, new_device_pixel_scale)
} else {
(device_rect, device_pixel_scale)
@@ -2117,7 +2130,7 @@ fn adjust_mask_scale_for_max_size(device_rect: DeviceRect, device_pixel_scale: D
}
pub fn write_prim_blocks(
- builder: &mut GpuBufferBuilder,
+ builder: &mut GpuBufferBuilderF,
prim_rect: LayoutRect,
clip_rect: LayoutRect,
color: PremultipliedColorF,
@@ -2153,7 +2166,7 @@ fn add_segment(
prim_instance: &PrimitiveInstance,
prim_spatial_node_index: SpatialNodeIndex,
raster_spatial_node_index: SpatialNodeIndex,
- main_prim_address: GpuBufferAddress,
+ prim_address_f: GpuBufferAddress,
transform_id: TransformPaletteId,
aa_flags: EdgeAaSegmentMask,
quad_flags: QuadFlags,
@@ -2177,7 +2190,7 @@ fn add_segment(
raster_spatial_node_index,
device_pixel_scale,
content_origin,
- main_prim_address,
+ prim_address_f,
transform_id,
aa_flags,
quad_flags,
@@ -2189,7 +2202,7 @@ fn add_segment(
let masks = MaskSubPass {
clip_node_range: prim_instance.vis.clip_chain.clips_range,
prim_spatial_node_index,
- main_prim_address,
+ prim_address_f,
};
let task = frame_state.rg_builder.get_task_mut(task_id);
@@ -2223,7 +2236,7 @@ fn add_composite_prim(
segments: &[QuadSegment],
) {
let composite_prim_address = write_prim_blocks(
- frame_state.frame_gpu_data,
+ &mut frame_state.frame_gpu_data.f32,
rect,
rect,
color,