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.rs594
1 files changed, 24 insertions, 570 deletions
diff --git a/gfx/wr/webrender/src/prepare.rs b/gfx/wr/webrender/src/prepare.rs
index a59eca0670..d9b4521cfc 100644
--- a/gfx/wr/webrender/src/prepare.rs
+++ b/gfx/wr/webrender/src/prepare.rs
@@ -6,13 +6,13 @@
//!
//! TODO: document this!
-use api::{ColorF, PremultipliedColorF, PropertyBinding};
+use api::{PremultipliedColorF, PropertyBinding};
use api::{BoxShadowClipMode, BorderStyle, ClipMode};
use api::units::*;
use euclid::Scale;
use smallvec::SmallVec;
use crate::composite::CompositorSurfaceKind;
-use crate::command_buffer::{PrimitiveCommand, QuadFlags, CommandBufferIndex};
+use crate::command_buffer::{PrimitiveCommand, CommandBufferIndex};
use crate::image_tiling::{self, Repetition};
use crate::border::{get_max_scale_for_border, build_border_instances};
use crate::clip::{ClipStore, ClipNodeRange};
@@ -20,32 +20,29 @@ use crate::spatial_tree::{SpatialNodeIndex, SpatialTree};
use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainInstance, ClipItemKind};
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest};
-use crate::gpu_types::{BrushFlags, TransformPaletteId, QuadSegment};
+use crate::gpu_types::BrushFlags;
use crate::internal_types::{FastHashMap, PlaneSplitAnchor, Filter};
use crate::picture::{PicturePrimitive, SliceId, ClusterFlags, PictureCompositeMode};
use crate::picture::{PrimitiveList, PrimitiveCluster, SurfaceIndex, TileCacheInstance, SubpixelMode, Picture3DContext};
use crate::prim_store::line_dec::MAX_LINE_DECORATION_RESOLUTION;
use crate::prim_store::*;
+use crate::quad;
+use crate::pattern::Pattern;
use crate::prim_store::gradient::GradientGpuBlockBuilder;
use crate::render_backend::DataStores;
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::{GpuBufferBuilderF, GpuBufferAddress};
-use crate::segment::{EdgeAaSegmentMask, SegmentBuilder};
-use crate::space::SpaceMapper;
-use crate::util::{clamp_to_scale_factor, pack_as_float, MaxRect};
+use crate::segment::SegmentBuilder;
+use crate::util::{clamp_to_scale_factor, pack_as_float};
use crate::visibility::{compute_conservative_visible_rect, PrimitiveVisibility, VisibilityState};
const MAX_MASK_SIZE: i32 = 4096;
-const MIN_BRUSH_SPLIT_SIZE: f32 = 256.0;
const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
-const MIN_AA_SEGMENTS_SIZE: f32 = 4.0;
-
pub fn prepare_primitives(
store: &mut PrimitiveStore,
prim_list: &mut PrimitiveList,
@@ -141,110 +138,6 @@ 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,
- }
-}
-
-fn get_prim_render_strategy(
- prim_spatial_node_index: SpatialNodeIndex,
- clip_chain: &ClipChainInstance,
- clip_store: &ClipStore,
- data_stores: &DataStores,
- can_use_nine_patch: bool,
- spatial_tree: &SpatialTree,
-) -> QuadRenderStrategy {
- if !clip_chain.needs_mask {
- return QuadRenderStrategy::Direct
- }
-
- fn tile_count_for_size(size: f32) -> u16 {
- (size / MIN_BRUSH_SPLIT_SIZE).min(4.0).max(1.0).ceil() as u16
- }
-
- 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 {
- 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,
- }
-}
-
fn prepare_prim_for_render(
store: &mut PrimitiveStore,
prim_instance_index: usize,
@@ -712,334 +605,32 @@ fn prepare_interned_prim_for_render(
}
);
} else {
- let map_prim_to_surface = frame_context.spatial_tree.get_relative_transform(
- prim_spatial_node_index,
- pic_context.raster_spatial_node_index,
- );
- let prim_is_2d_scale_translation = map_prim_to_surface.is_2d_scale_translation();
- let prim_is_2d_axis_aligned = map_prim_to_surface.is_2d_axis_aligned();
-
- let strategy = get_prim_render_strategy(
- prim_spatial_node_index,
- &prim_instance.vis.clip_chain,
- frame_state.clip_store,
- data_stores,
- prim_is_2d_scale_translation,
- frame_context.spatial_tree,
- );
-
let prim_data = &data_stores.prim[*data_handle];
- let (color, is_opaque) = match prim_data.kind {
- PrimitiveTemplateKind::Clear => {
- // Opaque black with operator dest out
- (ColorF::BLACK, false)
- }
+ let pattern = match prim_data.kind {
+ PrimitiveTemplateKind::Clear => Pattern::clear(),
PrimitiveTemplateKind::Rectangle { ref color, .. } => {
let color = frame_context.scene_properties.resolve_color(color);
-
- (color, color.a >= 1.0)
+ Pattern::color(color)
}
};
- let premul_color = color.premultiplied();
-
- let mut quad_flags = QuadFlags::empty();
-
- // Only use AA edge instances if the primitive is large enough to require it
- let prim_size = prim_data.common.prim_rect.size();
- if prim_size.width > MIN_AA_SEGMENTS_SIZE && prim_size.height > MIN_AA_SEGMENTS_SIZE {
- quad_flags |= QuadFlags::USE_AA_SEGMENTS;
- }
-
- if is_opaque {
- quad_flags |= QuadFlags::IS_OPAQUE;
- }
- let needs_scissor = !prim_is_2d_scale_translation;
- if !needs_scissor {
- quad_flags |= QuadFlags::APPLY_DEVICE_CLIP;
- }
-
- // TODO(gw): For now, we don't select per-edge AA at all if the primitive
- // has a 2d transform, which matches existing behavior. However,
- // as a follow up, we can now easily check if we have a 2d-aligned
- // primitive on a subpixel boundary, and enable AA along those edge(s).
- let aa_flags = if prim_is_2d_axis_aligned {
- EdgeAaSegmentMask::empty()
- } else {
- EdgeAaSegmentMask::all()
- };
-
- let transform_id = frame_state.transforms.get_id(
+ quad::push_quad(
+ &pattern,
+ &prim_data.common.prim_rect,
+ prim_instance_index,
prim_spatial_node_index,
- pic_context.raster_spatial_node_index,
- frame_context.spatial_tree,
- );
-
- // TODO(gw): Perhaps rather than writing untyped data here (we at least do validate
- // the written block count) to gpu-buffer, we could add a trait for
- // writing typed data?
- let main_prim_address = write_prim_blocks(
- &mut frame_state.frame_gpu_data.f32,
- prim_data.common.prim_rect,
- prim_instance.vis.clip_chain.local_clip_rect,
- premul_color,
- &[],
+ &prim_instance.vis.clip_chain,
+ device_pixel_scale,
+ frame_context,
+ pic_context,
+ targets,
+ &data_stores.clip,
+ frame_state,
+ pic_state,
+ scratch,
);
- match strategy {
- QuadRenderStrategy::Direct => {
- frame_state.push_prim(
- &PrimitiveCommand::quad(
- prim_instance_index,
- main_prim_address,
- transform_id,
- quad_flags,
- aa_flags,
- ),
- prim_spatial_node_index,
- targets,
- );
- }
- QuadRenderStrategy::Indirect => {
- let surface = &frame_state.surfaces[pic_context.surface_index.0];
- let Some(clipped_surface_rect) = surface.get_surface_rect(
- &prim_instance.vis.clip_chain.pic_coverage_rect,
- frame_context.spatial_tree,
- ) else {
- return;
- };
-
- let p0 = clipped_surface_rect.min.to_f32();
- let p1 = clipped_surface_rect.max.to_f32();
-
- let segment = add_segment(
- p0.x,
- p0.y,
- p1.x,
- p1.y,
- true,
- prim_instance,
- prim_spatial_node_index,
- pic_context.raster_spatial_node_index,
- main_prim_address,
- transform_id,
- aa_flags,
- quad_flags,
- device_pixel_scale,
- needs_scissor,
- frame_state,
- );
-
- add_composite_prim(
- prim_instance_index,
- LayoutRect::new(p0.cast_unit(), p1.cast_unit()),
- premul_color,
- quad_flags,
- frame_state,
- targets,
- &[segment],
- );
- }
- QuadRenderStrategy::Tiled { x_tiles, y_tiles } => {
- let surface = &frame_state.surfaces[pic_context.surface_index.0];
-
- let Some(clipped_surface_rect) = surface.get_surface_rect(
- &prim_instance.vis.clip_chain.pic_coverage_rect,
- frame_context.spatial_tree,
- ) else {
- return;
- };
-
- let unclipped_surface_rect = surface.map_to_device_rect(
- &prim_instance.vis.clip_chain.pic_coverage_rect,
- frame_context.spatial_tree,
- );
-
- scratch.quad_segments.clear();
-
- 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) 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 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 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);
- y_coords.push(clipped_surface_rect.max.y);
-
- for y in 0 .. y_coords.len()-1 {
- let y0 = y_coords[y];
- let y1 = y_coords[y+1];
-
- if y1 <= y0 {
- continue;
- }
-
- for x in 0 .. x_coords.len()-1 {
- let x0 = x_coords[x];
- let x1 = x_coords[x+1];
-
- if x1 <= x0 {
- continue;
- }
-
- let create_task = true;
-
- let segment = add_segment(
- x0 as f32,
- y0 as f32,
- x1 as f32,
- y1 as f32,
- create_task,
- prim_instance,
- prim_spatial_node_index,
- pic_context.raster_spatial_node_index,
- main_prim_address,
- transform_id,
- aa_flags,
- quad_flags,
- device_pixel_scale,
- needs_scissor,
- frame_state,
- );
- scratch.quad_segments.push(segment);
- }
- }
-
- add_composite_prim(
- prim_instance_index,
- unclipped_surface_rect.cast_unit(),
- premul_color,
- quad_flags,
- frame_state,
- targets,
- &scratch.quad_segments,
- );
- }
- QuadRenderStrategy::NinePatch { clip_rect, radius } => {
- let surface = &frame_state.surfaces[pic_context.surface_index.0];
- let Some(clipped_surface_rect) = surface.get_surface_rect(
- &prim_instance.vis.clip_chain.pic_coverage_rect,
- frame_context.spatial_tree,
- ) else {
- return;
- };
-
- let unclipped_surface_rect = surface.map_to_device_rect(
- &prim_instance.vis.clip_chain.pic_coverage_rect,
- frame_context.spatial_tree,
- );
-
- let local_corner_0 = LayoutRect::new(
- clip_rect.min,
- clip_rect.min + radius,
- );
-
- let local_corner_1 = LayoutRect::new(
- clip_rect.max - radius,
- clip_rect.max,
- );
-
- let pic_corner_0 = pic_state.map_local_to_pic.map(&local_corner_0).unwrap();
- let pic_corner_1 = pic_state.map_local_to_pic.map(&local_corner_1).unwrap();
-
- 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;
- 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];
-
- x_coords.sort_by(|a, b| a.partial_cmp(b).unwrap());
- y_coords.sort_by(|a, b| a.partial_cmp(b).unwrap());
-
- scratch.quad_segments.clear();
-
- for y in 0 .. y_coords.len()-1 {
- let y0 = y_coords[y];
- let y1 = y_coords[y+1];
-
- if y1 <= y0 {
- continue;
- }
-
- for x in 0 .. x_coords.len()-1 {
- let x0 = x_coords[x];
- let x1 = x_coords[x+1];
-
- if x1 <= x0 {
- continue;
- }
-
- let create_task = if x == 1 || y == 1 {
- false
- } else {
- true
- };
-
- let r = DeviceIntRect::new(
- DeviceIntPoint::new(x0, y0),
- DeviceIntPoint::new(x1, y1),
- );
-
- let r = match r.intersection(&clipped_surface_rect) {
- Some(r) => r,
- None => {
- continue;
- }
- };
-
- let segment = add_segment(
- 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,
- pic_context.raster_spatial_node_index,
- main_prim_address,
- transform_id,
- aa_flags,
- quad_flags,
- device_pixel_scale,
- false,
- frame_state,
- );
- scratch.quad_segments.push(segment);
- }
- }
-
- add_composite_prim(
- prim_instance_index,
- unclipped_surface_rect.cast_unit(),
- premul_color,
- quad_flags,
- frame_state,
- targets,
- &scratch.quad_segments,
- );
- }
- }
-
return;
}
}
@@ -1304,7 +895,7 @@ fn prepare_interned_prim_for_render(
.clipped_local_rect
.cast_unit();
- let prim_address_f = write_prim_blocks(
+ let prim_address_f = quad::write_prim_blocks(
&mut frame_state.frame_gpu_data.f32,
prim_local_rect,
prim_instance.vis.clip_chain.local_clip_rect,
@@ -2129,143 +1720,6 @@ fn adjust_mask_scale_for_max_size(device_rect: DeviceIntRect, device_pixel_scale
}
}
-pub fn write_prim_blocks(
- builder: &mut GpuBufferBuilderF,
- prim_rect: LayoutRect,
- clip_rect: LayoutRect,
- color: PremultipliedColorF,
- segments: &[QuadSegment],
-) -> GpuBufferAddress {
- let mut writer = builder.write_blocks(3 + segments.len() * 2);
-
- writer.push_one(prim_rect);
- writer.push_one(clip_rect);
- writer.push_one(color);
-
- for segment in segments {
- writer.push_one(segment.rect);
- match segment.task_id {
- RenderTaskId::INVALID => {
- writer.push_one([0.0; 4]);
- }
- task_id => {
- writer.push_render_task(task_id);
- }
- }
- }
-
- writer.finish()
-}
-
-fn add_segment(
- x0: f32,
- y0: f32,
- x1: f32,
- y1: f32,
- create_task: bool,
- prim_instance: &PrimitiveInstance,
- prim_spatial_node_index: SpatialNodeIndex,
- raster_spatial_node_index: SpatialNodeIndex,
- prim_address_f: GpuBufferAddress,
- transform_id: TransformPaletteId,
- aa_flags: EdgeAaSegmentMask,
- quad_flags: QuadFlags,
- device_pixel_scale: DevicePixelScale,
- needs_scissor_rect: bool,
- frame_state: &mut FrameBuildingState,
-) -> QuadSegment {
- let task_size = DeviceSize::new(x1 - x0, y1 - y0).round().to_i32();
- let content_origin = DevicePoint::new(x0, y0);
-
- let rect = LayoutRect::new(
- LayoutPoint::new(x0, y0),
- LayoutPoint::new(x1, y1),
- );
-
- let task_id = if create_task {
- let task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic(
- task_size,
- RenderTaskKind::new_prim(
- prim_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- content_origin,
- prim_address_f,
- transform_id,
- aa_flags,
- quad_flags,
- prim_instance.vis.clip_chain.clips_range,
- needs_scissor_rect,
- ),
- ));
-
- let masks = MaskSubPass {
- clip_node_range: prim_instance.vis.clip_chain.clips_range,
- prim_spatial_node_index,
- prim_address_f,
- };
-
- let task = frame_state.rg_builder.get_task_mut(task_id);
- task.add_sub_pass(SubPass::Masks {
- masks,
- });
-
- frame_state.surface_builder.add_child_render_task(
- task_id,
- frame_state.rg_builder,
- );
-
- task_id
- } else {
- RenderTaskId::INVALID
- };
-
- QuadSegment {
- rect,
- task_id,
- }
-}
-
-fn add_composite_prim(
- prim_instance_index: PrimitiveInstanceIndex,
- rect: LayoutRect,
- color: PremultipliedColorF,
- quad_flags: QuadFlags,
- frame_state: &mut FrameBuildingState,
- targets: &[CommandBufferIndex],
- segments: &[QuadSegment],
-) {
- let composite_prim_address = write_prim_blocks(
- &mut frame_state.frame_gpu_data.f32,
- rect,
- rect,
- color,
- segments,
- );
-
- frame_state.set_segments(
- segments,
- targets,
- );
-
- let mut composite_quad_flags = QuadFlags::IGNORE_DEVICE_PIXEL_SCALE | QuadFlags::APPLY_DEVICE_CLIP;
- if quad_flags.contains(QuadFlags::IS_OPAQUE) {
- composite_quad_flags |= QuadFlags::IS_OPAQUE;
- }
-
- frame_state.push_cmd(
- &PrimitiveCommand::quad(
- prim_instance_index,
- composite_prim_address,
- TransformPaletteId::IDENTITY,
- composite_quad_flags,
- // TODO(gw): No AA on composite, unless we use it to apply 2d clips
- EdgeAaSegmentMask::empty(),
- ),
- targets,
- );
-}
-
impl CompositorSurfaceKind {
/// Returns true if the compositor surface strategy supports segment rendering
fn supports_segments(&self) -> bool {