commit a9e6d6b9424af46c186d168bc3b414da8dd2240e
parent 57e9cc68c2770df6275a43342494e9cd6686c40f
Author: Nicolas Silva <nical@fastmail.com>
Date: Mon, 15 Dec 2025 10:53:21 +0000
Bug 1998913 - Part 8 - Move picture composite mode related code in its own module. r=gfx-reviewers,gw
Differential Revision: https://phabricator.services.mozilla.com/D276189
Diffstat:
4 files changed, 1024 insertions(+), 1094 deletions(-)
diff --git a/gfx/wr/webrender/src/lib.rs b/gfx/wr/webrender/src/lib.rs
@@ -108,6 +108,7 @@ mod internal_types;
mod lru_cache;
mod pattern;
mod picture;
+mod picture_composite_mode;
mod picture_graph;
mod invalidation;
mod prepare;
diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs
@@ -94,11 +94,11 @@
//! blend the overlay tile (this is not always optimal right now, but will be
//! improved as a follow up).
-use api::{MixBlendMode, PremultipliedColorF, RasterSpace};
+use api::{PremultipliedColorF, RasterSpace};
use api::{DebugFlags, ColorF, PrimitiveFlags, SnapshotInfo};
use api::units::*;
-use crate::prim_store::image::AdjustedImageSource;
-use crate::{command_buffer::PrimitiveCommand, render_task_graph::RenderTaskGraphBuilder, renderer::GpuBufferBuilderF};
+use crate::command_buffer::PrimitiveCommand;
+use crate::renderer::GpuBufferBuilderF;
use crate::box_shadow::BLUR_SAMPLE_SCALE;
use crate::clip::{ClipNodeId, ClipTreeBuilder};
use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace};
@@ -109,31 +109,29 @@ use euclid::{vec3, Scale, Vector2D, Box2D};
use crate::internal_types::{FastHashMap, PlaneSplitter, Filter};
use crate::internal_types::{PlaneSplitterIndex, PlaneSplitAnchor, TextureSource};
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
-use crate::gpu_types::{BlurEdgeMode, BrushSegmentGpuData, ImageBrushPrimitiveData};
-use crate::svg_filter::{FilterGraphOp, FilterGraphNode, get_coverage_source_svgfe, get_coverage_target_svgfe};
+use crate::gpu_types::{BrushSegmentGpuData, ImageBrushPrimitiveData};
+use crate::svg_filter::FilterGraphOp;
use plane_split::{Clipper, Polygon};
use crate::prim_store::{PictureIndex, PrimitiveInstance, PrimitiveInstanceKind};
use crate::prim_store::PrimitiveScratchBuffer;
use crate::print_tree::PrintTreePrinter;
use crate::render_backend::DataStores;
use crate::render_task_graph::RenderTaskId;
-use crate::render_target::RenderTargetKind;
-use crate::render_task::{BlurTask, RenderTask, RenderTaskLocation, BlurTaskCache};
+use crate::render_task::{RenderTask, RenderTaskLocation};
use crate::render_task::{StaticRenderTaskSurface, RenderTaskKind};
-use crate::renderer::{BlendMode, GpuBufferAddress};
+use crate::renderer::GpuBufferAddress;
use crate::resource_cache::ResourceCache;
use crate::space::SpaceMapper;
use crate::scene::SceneProperties;
use crate::spatial_tree::CoordinateSystemId;
use crate::surface::{SurfaceDescriptor, SurfaceTileDescriptor, get_surface_rects};
-pub use crate::surface::{SurfaceIndex, SurfaceInfo, SubpixelMode, SurfaceAllocInfo};
+pub use crate::surface::{SurfaceIndex, SurfaceInfo, SubpixelMode};
pub use crate::surface::{calculate_screen_uv, calculate_uv_rect_kind};
use smallvec::SmallVec;
use std::{mem, u8, u32};
use std::ops::Range;
use crate::picture_textures::PictureCacheTextureHandle;
use crate::util::{MaxRect, Recycler, ScaleOffset};
-use crate::filterdata::FilterDataHandle;
use crate::tile_cache::{SliceDebugInfo, TileDebugInfo, DirtyTileDebugInfo};
use crate::tile_cache::{SliceId, TileCacheInstance, TileSurface, NativeSurface};
use crate::tile_cache::{BackdropKind, BackdropSurface, Tile};
@@ -145,9 +143,11 @@ pub use crate::invalidation::DirtyRegion;
pub use crate::invalidation::dependency::ImageDependency;
pub use crate::invalidation::quadtree::{TileNode, TileNodeKind};
+pub use crate::picture_composite_mode::{PictureCompositeMode, prepare_composite_mode};
+
// Maximum blur radius for blur filter (different than box-shadow blur).
// Taken from FilterNodeSoftware.cpp in Gecko.
-const MAX_BLUR_RADIUS: f32 = 100.;
+pub(crate) const MAX_BLUR_RADIUS: f32 = 100.;
/// Maximum size of a compositor surface.
pub const MAX_COMPOSITOR_SURFACES_SIZE: f32 = 8192.0;
@@ -273,168 +273,6 @@ bitflags! {
}
}
-/// Specifies how this Picture should be composited
-/// onto the target it belongs to.
-#[allow(dead_code)]
-#[derive(Debug, Clone)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-pub enum PictureCompositeMode {
- /// Apply CSS mix-blend-mode effect.
- MixBlend(MixBlendMode),
- /// Apply a CSS filter (except component transfer).
- Filter(Filter),
- /// Apply a component transfer filter.
- ComponentTransferFilter(FilterDataHandle),
- /// Draw to intermediate surface, copy straight across. This
- /// is used for CSS isolation, and plane splitting.
- Blit(BlitReason),
- /// Used to cache a picture as a series of tiles.
- TileCache {
- slice_id: SliceId,
- },
- /// Apply an SVG filter graph
- SVGFEGraph(Vec<(FilterGraphNode, FilterGraphOp)>),
- /// A surface that is used as an input to another primitive
- IntermediateSurface,
-}
-
-impl PictureCompositeMode {
- pub fn get_rect(
- &self,
- surface: &SurfaceInfo,
- sub_rect: Option<LayoutRect>,
- ) -> LayoutRect {
- let surface_rect = match sub_rect {
- Some(sub_rect) => sub_rect,
- None => surface.clipped_local_rect.cast_unit(),
- };
-
- match self {
- PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
- if *should_inflate {
- let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
-
- surface_rect.inflate(
- width_factor.ceil() * BLUR_SAMPLE_SCALE,
- height_factor.ceil() * BLUR_SAMPLE_SCALE,
- )
- } else {
- surface_rect
- }
- }
- PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
- let mut max_blur_radius = 0.0;
- for shadow in shadows {
- max_blur_radius = f32::max(max_blur_radius, shadow.blur_radius);
- }
-
- let (max_blur_radius_x, max_blur_radius_y) = surface.clamp_blur_radius(
- max_blur_radius,
- max_blur_radius,
- );
- let blur_inflation_x = max_blur_radius_x * BLUR_SAMPLE_SCALE;
- let blur_inflation_y = max_blur_radius_y * BLUR_SAMPLE_SCALE;
-
- surface_rect.inflate(blur_inflation_x, blur_inflation_y)
- }
- PictureCompositeMode::SVGFEGraph(ref filters) => {
- // Return prim_subregion for use in get_local_prim_rect, which
- // is the polygon size.
- // This must match surface_rects.unclipped_local.
- get_coverage_target_svgfe(filters, surface_rect.cast_unit())
- }
- _ => {
- surface_rect
- }
- }
- }
-
- pub fn get_coverage(
- &self,
- surface: &SurfaceInfo,
- sub_rect: Option<LayoutRect>,
- ) -> LayoutRect {
- let surface_rect = match sub_rect {
- Some(sub_rect) => sub_rect,
- None => surface.clipped_local_rect.cast_unit(),
- };
-
- match self {
- PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
- if *should_inflate {
- let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
-
- surface_rect.inflate(
- width_factor.ceil() * BLUR_SAMPLE_SCALE,
- height_factor.ceil() * BLUR_SAMPLE_SCALE,
- )
- } else {
- surface_rect
- }
- }
- PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
- let mut rect = surface_rect;
-
- for shadow in shadows {
- let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
- shadow.blur_radius,
- shadow.blur_radius,
- );
- let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
- let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
-
- let shadow_rect = surface_rect
- .translate(shadow.offset)
- .inflate(blur_inflation_x, blur_inflation_y);
- rect = rect.union(&shadow_rect);
- }
-
- rect
- }
- PictureCompositeMode::SVGFEGraph(ref filters) => {
- // surface_rect may be for source or target, so invalidate based
- // on both interpretations
- let target_subregion = get_coverage_source_svgfe(filters, surface_rect.cast());
- let source_subregion = get_coverage_target_svgfe(filters, surface_rect.cast());
- target_subregion.union(&source_subregion)
- }
- _ => {
- surface_rect
- }
- }
- }
-
- /// Returns a static str describing the type of PictureCompositeMode (and
- /// filter type if applicable)
- pub fn kind(&self) -> &'static str {
- match *self {
- PictureCompositeMode::Blit(..) => "Blit",
- PictureCompositeMode::ComponentTransferFilter(..) => "ComponentTransferFilter",
- PictureCompositeMode::IntermediateSurface => "IntermediateSurface",
- PictureCompositeMode::MixBlend(..) => "MixBlend",
- PictureCompositeMode::SVGFEGraph(..) => "SVGFEGraph",
- PictureCompositeMode::TileCache{..} => "TileCache",
- PictureCompositeMode::Filter(Filter::Blur{..}) => "Filter::Blur",
- PictureCompositeMode::Filter(Filter::Brightness(..)) => "Filter::Brightness",
- PictureCompositeMode::Filter(Filter::ColorMatrix(..)) => "Filter::ColorMatrix",
- PictureCompositeMode::Filter(Filter::ComponentTransfer) => "Filter::ComponentTransfer",
- PictureCompositeMode::Filter(Filter::Contrast(..)) => "Filter::Contrast",
- PictureCompositeMode::Filter(Filter::DropShadows(..)) => "Filter::DropShadows",
- PictureCompositeMode::Filter(Filter::Flood(..)) => "Filter::Flood",
- PictureCompositeMode::Filter(Filter::Grayscale(..)) => "Filter::Grayscale",
- PictureCompositeMode::Filter(Filter::HueRotate(..)) => "Filter::HueRotate",
- PictureCompositeMode::Filter(Filter::Identity) => "Filter::Identity",
- PictureCompositeMode::Filter(Filter::Invert(..)) => "Filter::Invert",
- PictureCompositeMode::Filter(Filter::LinearToSrgb) => "Filter::LinearToSrgb",
- PictureCompositeMode::Filter(Filter::Opacity(..)) => "Filter::Opacity",
- PictureCompositeMode::Filter(Filter::Saturate(..)) => "Filter::Saturate",
- PictureCompositeMode::Filter(Filter::Sepia(..)) => "Filter::Sepia",
- PictureCompositeMode::Filter(Filter::SrgbToLinear) => "Filter::SrgbToLinear",
- PictureCompositeMode::Filter(Filter::SVGGraphNode(..)) => "Filter::SVGGraphNode",
- }
- }
-}
-
/// Enum value describing the place of a picture in a 3D context.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
@@ -2447,556 +2285,6 @@ fn prepare_tiled_picture_surface(
);
}
-fn prepare_composite_mode(
- composite_mode: &PictureCompositeMode,
- surface_index: SurfaceIndex,
- parent_surface_index: SurfaceIndex,
- surface_rects: &SurfaceAllocInfo,
- snapshot: &Option<SnapshotInfo>,
- can_use_shared_surface: bool,
- frame_context: &FrameBuildingContext,
- frame_state: &mut FrameBuildingState,
- data_stores: &mut DataStores,
- extra_gpu_data: &mut SmallVec<[GpuBufferAddress; 1]>,
-) -> (SurfaceDescriptor, [Option<RenderTaskId>; 2]) {
- let surface = &frame_state.surfaces[surface_index.0];
- let surface_spatial_node_index = surface.surface_spatial_node_index;
- let raster_spatial_node_index = surface.raster_spatial_node_index;
- let device_pixel_scale = surface.device_pixel_scale;
-
- let primary_render_task_id;
- let surface_descriptor;
- let mut secondary_render_task_id = None;
- match *composite_mode {
- PictureCompositeMode::TileCache { .. } => {
- unreachable!("handled above");
- }
- PictureCompositeMode::Filter(Filter::Blur { width, height, edge_mode, .. }) => {
- //let surface = &frame_state.surfaces[raster_config.surface_index.0];
- let (width, height) = surface.clamp_blur_radius(width, height);
-
- let width_std_deviation = width * surface.local_scale.0 * device_pixel_scale.0;
- let height_std_deviation = height * surface.local_scale.1 * device_pixel_scale.0;
- let blur_std_deviation = DeviceSize::new(
- width_std_deviation,
- height_std_deviation,
- );
-
- let original_size = surface_rects.clipped.size();
-
- // Adjust the size to avoid introducing sampling errors during the down-scaling passes.
- // what would be even better is to rasterize the picture at the down-scaled size
- // directly.
- let adjusted_size = BlurTask::adjusted_blur_source_size(
- original_size,
- blur_std_deviation,
- );
-
- // If we have extended the size of the picture for blurring downscaling
- // accuracy, ensure we clear it so that any stray pixels don't affect the
- // downscaling passes. If not, the picture / resolve consumes the full
- // task size anyway, so we will clamp as usual to the task rect.
- let clear_color = if adjusted_size == original_size {
- None
- } else {
- Some(ColorF::TRANSPARENT)
- };
-
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
- let adjusted_size = adjusted_size.to_i32();
-
- // Since we (may have) adjusted the render task size for downscaling accuracy
- // above, recalculate the uv rect for tasks that may sample from this blur output
- let uv_rect_kind = calculate_uv_rect_kind(
- DeviceRect::from_origin_and_size(surface_rects.clipped.min, adjusted_size.to_f32()),
- surface_rects.unclipped,
- );
-
- let picture_task_id = frame_state.rg_builder.add().init(
- RenderTask::new_dynamic(
- adjusted_size,
- RenderTaskKind::new_picture(
- adjusted_size,
- surface_rects.needs_scissor_rect,
- surface_rects.clipped.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- clear_color,
- cmd_buffer_index,
- can_use_shared_surface,
- Some(original_size.round().to_i32()),
- )
- ).with_uv_rect_kind(uv_rect_kind)
- );
-
-
- let blur_render_task_id = request_render_task(
- frame_state,
- snapshot,
- &surface_rects,
- false,
- &mut|rg_builder, _| {
- RenderTask::new_blur(
- blur_std_deviation,
- picture_task_id,
- rg_builder,
- RenderTargetKind::Color,
- None,
- original_size.to_i32(),
- edge_mode,
- )
- }
- );
- primary_render_task_id = blur_render_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_chained(
- picture_task_id,
- blur_render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
- let surface = &frame_state.surfaces[surface_index.0];
-
- let device_rect = surface_rects.clipped;
-
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- let picture_task_id = frame_state.rg_builder.add().init(
- RenderTask::new_dynamic(
- surface_rects.task_size,
- RenderTaskKind::new_picture(
- surface_rects.task_size,
- surface_rects.needs_scissor_rect,
- device_rect.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- ),
- ).with_uv_rect_kind(surface_rects.uv_rect_kind)
- );
-
- let mut blur_tasks = BlurTaskCache::default();
-
- extra_gpu_data.resize(shadows.len(), GpuBufferAddress::INVALID);
-
- let mut blur_render_task_id = picture_task_id;
- for shadow in shadows {
- let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
- shadow.blur_radius,
- shadow.blur_radius,
- );
-
- blur_render_task_id = RenderTask::new_blur(
- DeviceSize::new(
- blur_radius_x * surface.local_scale.0 * device_pixel_scale.0,
- blur_radius_y * surface.local_scale.1 * device_pixel_scale.0,
- ),
- picture_task_id,
- frame_state.rg_builder,
- RenderTargetKind::Color,
- Some(&mut blur_tasks),
- device_rect.size().to_i32(),
- BlurEdgeMode::Duplicate,
- );
- }
-
- // TODO: Ensure that snapshots bake their shadow.
-
- // Add this content picture as a dependency of the parent surface, to
- // ensure it isn't free'd after the shadow uses it as an input.
- frame_state.surface_builder.add_picture_render_task(picture_task_id);
-
- primary_render_task_id = blur_render_task_id;
- secondary_render_task_id = Some(picture_task_id);
-
- surface_descriptor = SurfaceDescriptor::new_chained(
- picture_task_id,
- blur_render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
- mode,
- frame_context.fb_config.gpu_supports_advanced_blend,
- frame_context.fb_config.advanced_blend_is_coherent,
- frame_context.fb_config.dual_source_blending_is_supported,
- ).is_none() => {
- let parent_surface = &frame_state.surfaces[parent_surface_index.0];
-
- // Create a space mapper that will allow mapping from the local rect
- // of the mix-blend primitive into the space of the surface that we
- // need to read back from. Note that we use the parent's raster spatial
- // node here, so that we are in the correct device space of the parent
- // surface, whether it establishes a raster root or not.
- let map_pic_to_parent = SpaceMapper::new_with_target(
- parent_surface.surface_spatial_node_index,
- surface_spatial_node_index,
- parent_surface.clipping_rect,
- frame_context.spatial_tree,
- );
- let pic_rect = surface.clipped_local_rect;
- let pic_in_raster_space = map_pic_to_parent
- .map(&pic_rect)
- .expect("bug: unable to map mix-blend content into parent");
-
- // Apply device pixel ratio for parent surface to get into device
- // pixels for that surface.
- let backdrop_rect = pic_in_raster_space;
- let parent_surface_rect = parent_surface.clipping_rect;
-
- // If there is no available parent surface to read back from (for example, if
- // the parent surface is affected by a clip that doesn't affect the child
- // surface), then create a dummy 16x16 readback. In future, we could alter
- // the composite mode of this primitive to skip the mix-blend, but for simplicity
- // we just create a dummy readback for now.
-
- let readback_task_id = match backdrop_rect.intersection(&parent_surface_rect) {
- Some(available_rect) => {
- // Calculate the UV coords necessary for the shader to sampler
- // from the primitive rect within the readback region. This is
- // 0..1 for aligned surfaces, but doing it this way allows
- // accurate sampling if the primitive bounds have fractional values.
-
- let backdrop_rect = parent_surface.map_to_device_rect(
- &backdrop_rect,
- frame_context.spatial_tree,
- );
-
- let available_rect = parent_surface.map_to_device_rect(
- &available_rect,
- frame_context.spatial_tree,
- ).round_out();
-
- let backdrop_uv = calculate_uv_rect_kind(
- available_rect,
- backdrop_rect,
- );
-
- frame_state.rg_builder.add().init(
- RenderTask::new_dynamic(
- available_rect.size().to_i32(),
- RenderTaskKind::new_readback(Some(available_rect.min)),
- ).with_uv_rect_kind(backdrop_uv)
- )
- }
- None => {
- frame_state.rg_builder.add().init(
- RenderTask::new_dynamic(
- DeviceIntSize::new(16, 16),
- RenderTaskKind::new_readback(None),
- )
- )
- }
- };
-
- frame_state.surface_builder.add_child_render_task(
- readback_task_id,
- frame_state.rg_builder,
- );
-
- secondary_render_task_id = Some(readback_task_id);
-
- let task_size = surface_rects.clipped.size().to_i32();
-
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- let is_opaque = false; // TODO
- let render_task_id = request_render_task(
- frame_state,
- &snapshot,
- &surface_rects,
- is_opaque,
- &mut|rg_builder, _| {
- rg_builder.add().init(
- RenderTask::new_dynamic(
- task_size,
- RenderTaskKind::new_picture(
- task_size,
- surface_rects.needs_scissor_rect,
- surface_rects.clipped.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- )
- ).with_uv_rect_kind(surface_rects.uv_rect_kind)
- )
- }
- );
-
- primary_render_task_id = render_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_simple(
- render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::Filter(..) => {
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- let is_opaque = false; // TODO
- let render_task_id = request_render_task(
- frame_state,
- snapshot,
- &surface_rects,
- is_opaque,
- &mut|rg_builder, _| {
- rg_builder.add().init(
- RenderTask::new_dynamic(
- surface_rects.task_size,
- RenderTaskKind::new_picture(
- surface_rects.task_size,
- surface_rects.needs_scissor_rect,
- surface_rects.clipped.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- )
- ).with_uv_rect_kind(surface_rects.uv_rect_kind)
- )
- },
- );
-
- primary_render_task_id = render_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_simple(
- render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::ComponentTransferFilter(..) => {
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- let is_opaque = false; // TODO
- let render_task_id = request_render_task(
- frame_state,
- snapshot,
- &surface_rects,
- is_opaque,
- &mut|rg_builder, _| {
- rg_builder.add().init(
- RenderTask::new_dynamic(
- surface_rects.task_size,
- RenderTaskKind::new_picture(
- surface_rects.task_size,
- surface_rects.needs_scissor_rect,
- surface_rects.clipped.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- )
- ).with_uv_rect_kind(surface_rects.uv_rect_kind)
- )
- }
- );
-
- primary_render_task_id = render_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_simple(
- render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::MixBlend(..) |
- PictureCompositeMode::Blit(_) => {
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- let is_opaque = false; // TODO
- let render_task_id = request_render_task(
- frame_state,
- snapshot,
- &surface_rects,
- is_opaque,
- &mut|rg_builder, _| {
- rg_builder.add().init(
- RenderTask::new_dynamic(
- surface_rects.task_size,
- RenderTaskKind::new_picture(
- surface_rects.task_size,
- surface_rects.needs_scissor_rect,
- surface_rects.clipped.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- )
- ).with_uv_rect_kind(surface_rects.uv_rect_kind)
- )
- }
- );
-
- primary_render_task_id = render_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_simple(
- render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::IntermediateSurface => {
- // TODO(gw): Remove all the mostly duplicated code in each of these
- // match cases (they used to be quite different).
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- let is_opaque = false; // TODO
- let render_task_id = request_render_task(
- frame_state,
- snapshot,
- &surface_rects,
- is_opaque,
- &mut|rg_builder, _| {
- rg_builder.add().init(
- RenderTask::new_dynamic(
- surface_rects.task_size,
- RenderTaskKind::new_picture(
- surface_rects.task_size,
- surface_rects.needs_scissor_rect,
- surface_rects.clipped.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- )
- ).with_uv_rect_kind(surface_rects.uv_rect_kind)
- )
- }
- );
-
- primary_render_task_id = render_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_simple(
- render_task_id,
- surface_rects.clipped_local,
- );
- }
- PictureCompositeMode::SVGFEGraph(ref filters) => {
- let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
-
- // Whole target without regard to clipping.
- let prim_subregion = surface_rects.unclipped;
- // Visible (clipped) subregion within prim_subregion.
- let target_subregion = surface_rects.clipped;
- // Subregion of the SourceGraphic that we need to render
- // all pixels within target_subregion.
- let source_subregion = surface_rects.source;
-
- // Produce the source pixels, this task will be consumed
- // by the RenderTask graph we build
- let source_task_size = source_subregion.round_out().size().to_i32();
- let source_task_size = if source_task_size.width > 0 && source_task_size.height > 0 {
- source_task_size
- } else {
- DeviceIntSize::new(1,1)
- };
- let picture_task_id = frame_state.rg_builder.add().init(
- RenderTask::new_dynamic(
- source_task_size,
- RenderTaskKind::new_picture(
- source_task_size,
- surface_rects.needs_scissor_rect,
- source_subregion.min,
- surface_spatial_node_index,
- raster_spatial_node_index,
- device_pixel_scale,
- None,
- None,
- None,
- cmd_buffer_index,
- can_use_shared_surface,
- None,
- )
- )
- );
-
- // Determine the local space to device pixel scaling in the most robust
- // way, this accounts for local to device transform and
- // device_pixel_scale (if the task is shrunk in get_surface_rects).
- let subregion_to_device_scale_x = surface_rects.clipped_notsnapped.width() / surface_rects.clipped_local.width();
- let subregion_to_device_scale_y = surface_rects.clipped_notsnapped.height() / surface_rects.clipped_local.height();
- let subregion_to_device_offset_x = surface_rects.clipped_notsnapped.min.x - (surface_rects.clipped_local.min.x * subregion_to_device_scale_x).floor();
- let subregion_to_device_offset_y = surface_rects.clipped_notsnapped.min.y - (surface_rects.clipped_local.min.y * subregion_to_device_scale_y).floor();
-
- // Produce the target pixels, this is the result of the
- // composite op
- let filter_task_id = request_render_task(
- frame_state,
- snapshot,
- &surface_rects,
- false,
- &mut|rg_builder, gpu_buffer| {
- RenderTask::new_svg_filter_graph(
- filters,
- rg_builder,
- gpu_buffer,
- data_stores,
- surface_rects.uv_rect_kind,
- picture_task_id,
- source_subregion.cast_unit(),
- target_subregion.cast_unit(),
- prim_subregion.cast_unit(),
- subregion_to_device_scale_x,
- subregion_to_device_scale_y,
- subregion_to_device_offset_x,
- subregion_to_device_offset_y,
- )
- }
- );
-
- primary_render_task_id = filter_task_id;
-
- surface_descriptor = SurfaceDescriptor::new_chained(
- picture_task_id,
- filter_task_id,
- surface_rects.clipped_local,
- );
- }
- }
-
- (
- surface_descriptor,
- [
- Some(primary_render_task_id),
- secondary_render_task_id,
- ]
- )
-}
-
fn compute_subpixel_mode(
raster_config: &Option<RasterConfig>,
tile_caches: &FastHashMap<SliceId, Box<TileCacheInstance>>,
@@ -3234,51 +2522,3 @@ fn test_drop_filter_dirty_region_outside_prim() {
).expect("No surface rect");
assert_eq!(info.task_size, DeviceIntSize::new(432, 578));
}
-
-fn request_render_task(
- frame_state: &mut FrameBuildingState,
- snapshot: &Option<SnapshotInfo>,
- surface_rects: &SurfaceAllocInfo,
- is_opaque: bool,
- f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId,
-) -> RenderTaskId {
-
- let task_id = match snapshot {
- Some(info) => {
- let adjustment = AdjustedImageSource::from_rects(
- &info.area,
- &surface_rects.clipped_local.cast_unit()
- );
- let task_id = frame_state.resource_cache.render_as_image(
- info.key.as_image(),
- surface_rects.task_size,
- frame_state.rg_builder,
- &mut frame_state.frame_gpu_data.f32,
- is_opaque,
- &adjustment,
- f
- );
-
- // TODO(bug 1929809): adding the dependency in the other branch causes
- // a panic in reftests/blend/backdrop-filter-blend-container.yaml.
- // Presumably if we use backdrop filters with snapshotting it will
- // trigger the panic as well.
- frame_state.surface_builder.add_child_render_task(
- task_id,
- frame_state.rg_builder,
- );
-
- frame_state.image_dependencies.insert(info.key.as_image(), task_id);
-
- task_id
- }
- None => {
- f(
- frame_state.rg_builder,
- &mut frame_state.frame_gpu_data.f32,
- )
- }
- };
-
- task_id
-}
diff --git a/gfx/wr/webrender/src/picture_composite_mode.rs b/gfx/wr/webrender/src/picture_composite_mode.rs
@@ -0,0 +1,1011 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::{ColorF, SnapshotInfo};
+use api::units::*;
+use crate::prim_store::image::AdjustedImageSource;
+use crate::{render_task_graph::RenderTaskGraphBuilder, renderer::GpuBufferBuilderF};
+use crate::box_shadow::BLUR_SAMPLE_SCALE;
+use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
+use crate::gpu_types::{BlurEdgeMode, UvRectKind};
+use crate::render_backend::DataStores;
+use crate::render_task_graph::RenderTaskId;
+use crate::render_target::RenderTargetKind;
+use crate::render_task::{BlurTask, RenderTask, BlurTaskCache};
+use crate::render_task::RenderTaskKind;
+use crate::renderer::{BlendMode, GpuBufferAddress};
+use crate::space::SpaceMapper;
+use crate::spatial_tree::SpatialTree;
+use crate::surface::{SurfaceDescriptor, SurfaceInfo, calculate_screen_uv};
+use crate::surface::SurfaceIndex;
+use crate::svg_filter::get_coverage_source_svgfe;
+use crate::util::MaxRect;
+use smallvec::SmallVec;
+use crate::internal_types::Filter;
+use crate::profiler;
+use core::time::Duration;
+use euclid::Scale;
+use api::MixBlendMode;
+use crate::filterdata::FilterDataHandle;
+use crate::tile_cache::SliceId;
+use crate::svg_filter::{FilterGraphNode, FilterGraphOp, get_coverage_target_svgfe};
+use crate::picture::BlitReason;
+#[cfg(feature = "capture")]
+use serde::Serialize;
+
+/// Specifies how this Picture should be composited
+/// onto the target it belongs to.
+#[allow(dead_code)]
+#[derive(Debug, Clone)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+pub enum PictureCompositeMode {
+ /// Apply CSS mix-blend-mode effect.
+ MixBlend(MixBlendMode),
+ /// Apply a CSS filter (except component transfer).
+ Filter(Filter),
+ /// Apply a component transfer filter.
+ ComponentTransferFilter(FilterDataHandle),
+ /// Draw to intermediate surface, copy straight across. This
+ /// is used for CSS isolation, and plane splitting.
+ Blit(BlitReason),
+ /// Used to cache a picture as a series of tiles.
+ TileCache {
+ slice_id: SliceId,
+ },
+ /// Apply an SVG filter graph
+ SVGFEGraph(Vec<(FilterGraphNode, FilterGraphOp)>),
+ /// A surface that is used as an input to another primitive
+ IntermediateSurface,
+}
+
+impl PictureCompositeMode {
+ pub fn get_rect(
+ &self,
+ surface: &SurfaceInfo,
+ sub_rect: Option<LayoutRect>,
+ ) -> LayoutRect {
+ let surface_rect = match sub_rect {
+ Some(sub_rect) => sub_rect,
+ None => surface.clipped_local_rect.cast_unit(),
+ };
+
+ match self {
+ PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
+ if *should_inflate {
+ let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
+
+ surface_rect.inflate(
+ width_factor.ceil() * BLUR_SAMPLE_SCALE,
+ height_factor.ceil() * BLUR_SAMPLE_SCALE,
+ )
+ } else {
+ surface_rect
+ }
+ }
+ PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
+ let mut max_blur_radius = 0.0;
+ for shadow in shadows {
+ max_blur_radius = f32::max(max_blur_radius, shadow.blur_radius);
+ }
+
+ let (max_blur_radius_x, max_blur_radius_y) = surface.clamp_blur_radius(
+ max_blur_radius,
+ max_blur_radius,
+ );
+ let blur_inflation_x = max_blur_radius_x * BLUR_SAMPLE_SCALE;
+ let blur_inflation_y = max_blur_radius_y * BLUR_SAMPLE_SCALE;
+
+ surface_rect.inflate(blur_inflation_x, blur_inflation_y)
+ }
+ PictureCompositeMode::SVGFEGraph(ref filters) => {
+ // Return prim_subregion for use in get_local_prim_rect, which
+ // is the polygon size.
+ // This must match surface_rects.unclipped_local.
+ get_coverage_target_svgfe(filters, surface_rect.cast_unit())
+ }
+ _ => {
+ surface_rect
+ }
+ }
+ }
+
+ pub fn get_coverage(
+ &self,
+ surface: &SurfaceInfo,
+ sub_rect: Option<LayoutRect>,
+ ) -> LayoutRect {
+ let surface_rect = match sub_rect {
+ Some(sub_rect) => sub_rect,
+ None => surface.clipped_local_rect.cast_unit(),
+ };
+
+ match self {
+ PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
+ if *should_inflate {
+ let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
+
+ surface_rect.inflate(
+ width_factor.ceil() * BLUR_SAMPLE_SCALE,
+ height_factor.ceil() * BLUR_SAMPLE_SCALE,
+ )
+ } else {
+ surface_rect
+ }
+ }
+ PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
+ let mut rect = surface_rect;
+
+ for shadow in shadows {
+ let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
+ shadow.blur_radius,
+ shadow.blur_radius,
+ );
+ let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
+ let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
+
+ let shadow_rect = surface_rect
+ .translate(shadow.offset)
+ .inflate(blur_inflation_x, blur_inflation_y);
+ rect = rect.union(&shadow_rect);
+ }
+
+ rect
+ }
+ PictureCompositeMode::SVGFEGraph(ref filters) => {
+ // surface_rect may be for source or target, so invalidate based
+ // on both interpretations
+ let target_subregion = get_coverage_source_svgfe(filters, surface_rect.cast());
+ let source_subregion = get_coverage_target_svgfe(filters, surface_rect.cast());
+ target_subregion.union(&source_subregion)
+ }
+ _ => {
+ surface_rect
+ }
+ }
+ }
+
+ /// Returns a static str describing the type of PictureCompositeMode (and
+ /// filter type if applicable)
+ pub fn kind(&self) -> &'static str {
+ match *self {
+ PictureCompositeMode::Blit(..) => "Blit",
+ PictureCompositeMode::ComponentTransferFilter(..) => "ComponentTransferFilter",
+ PictureCompositeMode::IntermediateSurface => "IntermediateSurface",
+ PictureCompositeMode::MixBlend(..) => "MixBlend",
+ PictureCompositeMode::SVGFEGraph(..) => "SVGFEGraph",
+ PictureCompositeMode::TileCache{..} => "TileCache",
+ PictureCompositeMode::Filter(Filter::Blur{..}) => "Filter::Blur",
+ PictureCompositeMode::Filter(Filter::Brightness(..)) => "Filter::Brightness",
+ PictureCompositeMode::Filter(Filter::ColorMatrix(..)) => "Filter::ColorMatrix",
+ PictureCompositeMode::Filter(Filter::ComponentTransfer) => "Filter::ComponentTransfer",
+ PictureCompositeMode::Filter(Filter::Contrast(..)) => "Filter::Contrast",
+ PictureCompositeMode::Filter(Filter::DropShadows(..)) => "Filter::DropShadows",
+ PictureCompositeMode::Filter(Filter::Flood(..)) => "Filter::Flood",
+ PictureCompositeMode::Filter(Filter::Grayscale(..)) => "Filter::Grayscale",
+ PictureCompositeMode::Filter(Filter::HueRotate(..)) => "Filter::HueRotate",
+ PictureCompositeMode::Filter(Filter::Identity) => "Filter::Identity",
+ PictureCompositeMode::Filter(Filter::Invert(..)) => "Filter::Invert",
+ PictureCompositeMode::Filter(Filter::LinearToSrgb) => "Filter::LinearToSrgb",
+ PictureCompositeMode::Filter(Filter::Opacity(..)) => "Filter::Opacity",
+ PictureCompositeMode::Filter(Filter::Saturate(..)) => "Filter::Saturate",
+ PictureCompositeMode::Filter(Filter::Sepia(..)) => "Filter::Sepia",
+ PictureCompositeMode::Filter(Filter::SrgbToLinear) => "Filter::SrgbToLinear",
+ PictureCompositeMode::Filter(Filter::SVGGraphNode(..)) => "Filter::SVGGraphNode",
+ }
+ }
+}
+
+pub fn prepare_composite_mode(
+ composite_mode: &PictureCompositeMode,
+ surface_index: SurfaceIndex,
+ parent_surface_index: SurfaceIndex,
+ surface_rects: &SurfaceAllocInfo,
+ snapshot: &Option<SnapshotInfo>,
+ can_use_shared_surface: bool,
+ frame_context: &FrameBuildingContext,
+ frame_state: &mut FrameBuildingState,
+ data_stores: &mut DataStores,
+ extra_gpu_data: &mut SmallVec<[GpuBufferAddress; 1]>,
+) -> (SurfaceDescriptor, [Option<RenderTaskId>; 2]) {
+ let surface = &frame_state.surfaces[surface_index.0];
+ let surface_spatial_node_index = surface.surface_spatial_node_index;
+ let raster_spatial_node_index = surface.raster_spatial_node_index;
+ let device_pixel_scale = surface.device_pixel_scale;
+
+ let primary_render_task_id;
+ let surface_descriptor;
+ let mut secondary_render_task_id = None;
+ match *composite_mode {
+ PictureCompositeMode::TileCache { .. } => {
+ unreachable!("handled above");
+ }
+ PictureCompositeMode::Filter(Filter::Blur { width, height, edge_mode, .. }) => {
+ let (width, height) = surface.clamp_blur_radius(width, height);
+
+ let width_std_deviation = width * surface.local_scale.0 * device_pixel_scale.0;
+ let height_std_deviation = height * surface.local_scale.1 * device_pixel_scale.0;
+ let blur_std_deviation = DeviceSize::new(
+ width_std_deviation,
+ height_std_deviation,
+ );
+
+ let original_size = surface_rects.clipped.size();
+
+ let adjusted_size = BlurTask::adjusted_blur_source_size(
+ original_size,
+ blur_std_deviation,
+ );
+
+ let clear_color = if adjusted_size == original_size {
+ None
+ } else {
+ Some(ColorF::TRANSPARENT)
+ };
+
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+ let adjusted_size = adjusted_size.to_i32();
+
+ let uv_rect_kind = calculate_uv_rect_kind(
+ DeviceRect::from_origin_and_size(surface_rects.clipped.min, adjusted_size.to_f32()),
+ surface_rects.unclipped,
+ );
+
+ let picture_task_id = frame_state.rg_builder.add().init(
+ RenderTask::new_dynamic(
+ adjusted_size,
+ RenderTaskKind::new_picture(
+ adjusted_size,
+ surface_rects.needs_scissor_rect,
+ surface_rects.clipped.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ clear_color,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ Some(original_size.round().to_i32()),
+ )
+ ).with_uv_rect_kind(uv_rect_kind)
+ );
+
+
+ let blur_render_task_id = request_render_task(
+ frame_state,
+ snapshot,
+ &surface_rects,
+ false,
+ &mut|rg_builder, _| {
+ RenderTask::new_blur(
+ blur_std_deviation,
+ picture_task_id,
+ rg_builder,
+ RenderTargetKind::Color,
+ None,
+ original_size.to_i32(),
+ edge_mode,
+ )
+ }
+ );
+ primary_render_task_id = blur_render_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_chained(
+ picture_task_id,
+ blur_render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
+ let surface = &frame_state.surfaces[surface_index.0];
+
+ let device_rect = surface_rects.clipped;
+
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let picture_task_id = frame_state.rg_builder.add().init(
+ RenderTask::new_dynamic(
+ surface_rects.task_size,
+ RenderTaskKind::new_picture(
+ surface_rects.task_size,
+ surface_rects.needs_scissor_rect,
+ device_rect.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ ),
+ ).with_uv_rect_kind(surface_rects.uv_rect_kind)
+ );
+
+ let mut blur_tasks = BlurTaskCache::default();
+
+ extra_gpu_data.resize(shadows.len(), GpuBufferAddress::INVALID);
+
+ let mut blur_render_task_id = picture_task_id;
+ for shadow in shadows {
+ let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
+ shadow.blur_radius,
+ shadow.blur_radius,
+ );
+
+ blur_render_task_id = RenderTask::new_blur(
+ DeviceSize::new(
+ blur_radius_x * surface.local_scale.0 * device_pixel_scale.0,
+ blur_radius_y * surface.local_scale.1 * device_pixel_scale.0,
+ ),
+ picture_task_id,
+ frame_state.rg_builder,
+ RenderTargetKind::Color,
+ Some(&mut blur_tasks),
+ device_rect.size().to_i32(),
+ BlurEdgeMode::Duplicate,
+ );
+ }
+
+ frame_state.surface_builder.add_picture_render_task(picture_task_id);
+
+ primary_render_task_id = blur_render_task_id;
+ secondary_render_task_id = Some(picture_task_id);
+
+ surface_descriptor = SurfaceDescriptor::new_chained(
+ picture_task_id,
+ blur_render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
+ mode,
+ frame_context.fb_config.gpu_supports_advanced_blend,
+ frame_context.fb_config.advanced_blend_is_coherent,
+ frame_context.fb_config.dual_source_blending_is_supported,
+ ).is_none() => {
+ let parent_surface = &frame_state.surfaces[parent_surface_index.0];
+
+ let map_pic_to_parent = SpaceMapper::new_with_target(
+ parent_surface.surface_spatial_node_index,
+ surface_spatial_node_index,
+ parent_surface.clipping_rect,
+ frame_context.spatial_tree,
+ );
+ let pic_rect = surface.clipped_local_rect;
+ let pic_in_raster_space = map_pic_to_parent
+ .map(&pic_rect)
+ .expect("bug: unable to map mix-blend content into parent");
+
+ let backdrop_rect = pic_in_raster_space;
+ let parent_surface_rect = parent_surface.clipping_rect;
+
+ let readback_task_id = match backdrop_rect.intersection(&parent_surface_rect) {
+ Some(available_rect) => {
+ let backdrop_rect = parent_surface.map_to_device_rect(
+ &backdrop_rect,
+ frame_context.spatial_tree,
+ );
+
+ let available_rect = parent_surface.map_to_device_rect(
+ &available_rect,
+ frame_context.spatial_tree,
+ ).round_out();
+
+ let backdrop_uv = calculate_uv_rect_kind(
+ available_rect,
+ backdrop_rect,
+ );
+
+ frame_state.rg_builder.add().init(
+ RenderTask::new_dynamic(
+ available_rect.size().to_i32(),
+ RenderTaskKind::new_readback(Some(available_rect.min)),
+ ).with_uv_rect_kind(backdrop_uv)
+ )
+ }
+ None => {
+ frame_state.rg_builder.add().init(
+ RenderTask::new_dynamic(
+ DeviceIntSize::new(16, 16),
+ RenderTaskKind::new_readback(None),
+ )
+ )
+ }
+ };
+
+ frame_state.surface_builder.add_child_render_task(
+ readback_task_id,
+ frame_state.rg_builder,
+ );
+
+ secondary_render_task_id = Some(readback_task_id);
+
+ let task_size = surface_rects.clipped.size().to_i32();
+
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let is_opaque = false;
+ let render_task_id = request_render_task(
+ frame_state,
+ &snapshot,
+ &surface_rects,
+ is_opaque,
+ &mut|rg_builder, _| {
+ rg_builder.add().init(
+ RenderTask::new_dynamic(
+ task_size,
+ RenderTaskKind::new_picture(
+ task_size,
+ surface_rects.needs_scissor_rect,
+ surface_rects.clipped.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ )
+ ).with_uv_rect_kind(surface_rects.uv_rect_kind)
+ )
+ }
+ );
+
+ primary_render_task_id = render_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_simple(
+ render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::Filter(..) => {
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let is_opaque = false;
+ let render_task_id = request_render_task(
+ frame_state,
+ snapshot,
+ &surface_rects,
+ is_opaque,
+ &mut|rg_builder, _| {
+ rg_builder.add().init(
+ RenderTask::new_dynamic(
+ surface_rects.task_size,
+ RenderTaskKind::new_picture(
+ surface_rects.task_size,
+ surface_rects.needs_scissor_rect,
+ surface_rects.clipped.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ )
+ ).with_uv_rect_kind(surface_rects.uv_rect_kind)
+ )
+ },
+ );
+
+ primary_render_task_id = render_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_simple(
+ render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::ComponentTransferFilter(..) => {
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let is_opaque = false;
+ let render_task_id = request_render_task(
+ frame_state,
+ snapshot,
+ &surface_rects,
+ is_opaque,
+ &mut|rg_builder, _| {
+ rg_builder.add().init(
+ RenderTask::new_dynamic(
+ surface_rects.task_size,
+ RenderTaskKind::new_picture(
+ surface_rects.task_size,
+ surface_rects.needs_scissor_rect,
+ surface_rects.clipped.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ )
+ ).with_uv_rect_kind(surface_rects.uv_rect_kind)
+ )
+ }
+ );
+
+ primary_render_task_id = render_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_simple(
+ render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::MixBlend(..) |
+ PictureCompositeMode::Blit(_) => {
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let is_opaque = false;
+ let render_task_id = request_render_task(
+ frame_state,
+ snapshot,
+ &surface_rects,
+ is_opaque,
+ &mut|rg_builder, _| {
+ rg_builder.add().init(
+ RenderTask::new_dynamic(
+ surface_rects.task_size,
+ RenderTaskKind::new_picture(
+ surface_rects.task_size,
+ surface_rects.needs_scissor_rect,
+ surface_rects.clipped.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ )
+ ).with_uv_rect_kind(surface_rects.uv_rect_kind)
+ )
+ }
+ );
+
+ primary_render_task_id = render_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_simple(
+ render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::IntermediateSurface => {
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let is_opaque = false;
+ let render_task_id = request_render_task(
+ frame_state,
+ snapshot,
+ &surface_rects,
+ is_opaque,
+ &mut|rg_builder, _| {
+ rg_builder.add().init(
+ RenderTask::new_dynamic(
+ surface_rects.task_size,
+ RenderTaskKind::new_picture(
+ surface_rects.task_size,
+ surface_rects.needs_scissor_rect,
+ surface_rects.clipped.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ )
+ ).with_uv_rect_kind(surface_rects.uv_rect_kind)
+ )
+ }
+ );
+
+ primary_render_task_id = render_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_simple(
+ render_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ PictureCompositeMode::SVGFEGraph(ref filters) => {
+ let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
+
+ let prim_subregion = surface_rects.unclipped;
+ let target_subregion = surface_rects.clipped;
+ let source_subregion = surface_rects.source;
+
+ let source_task_size = source_subregion.round_out().size().to_i32();
+ let source_task_size = if source_task_size.width > 0 && source_task_size.height > 0 {
+ source_task_size
+ } else {
+ DeviceIntSize::new(1,1)
+ };
+ let picture_task_id = frame_state.rg_builder.add().init(
+ RenderTask::new_dynamic(
+ source_task_size,
+ RenderTaskKind::new_picture(
+ source_task_size,
+ surface_rects.needs_scissor_rect,
+ source_subregion.min,
+ surface_spatial_node_index,
+ raster_spatial_node_index,
+ device_pixel_scale,
+ None,
+ None,
+ None,
+ cmd_buffer_index,
+ can_use_shared_surface,
+ None,
+ )
+ )
+ );
+
+ let subregion_to_device_scale_x = surface_rects.clipped_notsnapped.width() / surface_rects.clipped_local.width();
+ let subregion_to_device_scale_y = surface_rects.clipped_notsnapped.height() / surface_rects.clipped_local.height();
+ let subregion_to_device_offset_x = surface_rects.clipped_notsnapped.min.x - (surface_rects.clipped_local.min.x * subregion_to_device_scale_x).floor();
+ let subregion_to_device_offset_y = surface_rects.clipped_notsnapped.min.y - (surface_rects.clipped_local.min.y * subregion_to_device_scale_y).floor();
+
+ let filter_task_id = request_render_task(
+ frame_state,
+ snapshot,
+ &surface_rects,
+ false,
+ &mut|rg_builder, gpu_buffer| {
+ RenderTask::new_svg_filter_graph(
+ filters,
+ rg_builder,
+ gpu_buffer,
+ data_stores,
+ surface_rects.uv_rect_kind,
+ picture_task_id,
+ source_subregion.cast_unit(),
+ target_subregion.cast_unit(),
+ prim_subregion.cast_unit(),
+ subregion_to_device_scale_x,
+ subregion_to_device_scale_y,
+ subregion_to_device_offset_x,
+ subregion_to_device_offset_y,
+ )
+ }
+ );
+
+ primary_render_task_id = filter_task_id;
+
+ surface_descriptor = SurfaceDescriptor::new_chained(
+ picture_task_id,
+ filter_task_id,
+ surface_rects.clipped_local,
+ );
+ }
+ }
+
+ (
+ surface_descriptor,
+ [
+ Some(primary_render_task_id),
+ secondary_render_task_id,
+ ]
+ )
+}
+
+fn request_render_task(
+ frame_state: &mut FrameBuildingState,
+ snapshot: &Option<SnapshotInfo>,
+ surface_rects: &SurfaceAllocInfo,
+ is_opaque: bool,
+ f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId,
+) -> RenderTaskId {
+
+ let task_id = match snapshot {
+ Some(info) => {
+ let adjustment = AdjustedImageSource::from_rects(
+ &info.area,
+ &surface_rects.clipped_local.cast_unit()
+ );
+ let task_id = frame_state.resource_cache.render_as_image(
+ info.key.as_image(),
+ surface_rects.task_size,
+ frame_state.rg_builder,
+ &mut frame_state.frame_gpu_data.f32,
+ is_opaque,
+ &adjustment,
+ f
+ );
+
+ // TODO(bug 1929809): adding the dependency in the other branch causes
+ // a panic in reftests/blend/backdrop-filter-blend-container.yaml.
+ // Presumably if we use backdrop filters with snapshotting it will
+ // trigger the panic as well.
+ frame_state.surface_builder.add_child_render_task(
+ task_id,
+ frame_state.rg_builder,
+ );
+
+ frame_state.image_dependencies.insert(info.key.as_image(), task_id);
+
+ task_id
+ }
+ None => {
+ f(
+ frame_state.rg_builder,
+ &mut frame_state.frame_gpu_data.f32,
+ )
+ }
+ };
+
+ task_id
+}
+
+/// Information from `get_surface_rects` about the allocated size, UV sampling
+/// parameters etc for an off-screen surface
+#[derive(Debug)]
+pub struct SurfaceAllocInfo {
+ pub task_size: DeviceIntSize,
+ pub needs_scissor_rect: bool,
+ pub clipped: DeviceRect,
+ pub unclipped: DeviceRect,
+ // Only used for SVGFEGraph currently, this is the source pixels needed to
+ // render the pixels in clipped.
+ pub source: DeviceRect,
+ // Only used for SVGFEGraph, this is the same as clipped before rounding.
+ pub clipped_notsnapped: DeviceRect,
+ pub clipped_local: PictureRect,
+ pub uv_rect_kind: UvRectKind,
+}
+
+pub fn get_surface_rects(
+ surface_index: SurfaceIndex,
+ composite_mode: &PictureCompositeMode,
+ parent_surface_index: SurfaceIndex,
+ surfaces: &mut [SurfaceInfo],
+ spatial_tree: &SpatialTree,
+ max_surface_size: f32,
+ force_scissor_rect: bool,
+) -> Option<SurfaceAllocInfo> {
+ let parent_surface = &surfaces[parent_surface_index.0];
+
+ let local_to_parent = SpaceMapper::new_with_target(
+ parent_surface.surface_spatial_node_index,
+ surfaces[surface_index.0].surface_spatial_node_index,
+ parent_surface.clipping_rect,
+ spatial_tree,
+ );
+
+ let local_clip_rect = local_to_parent
+ .unmap(&parent_surface.clipping_rect)
+ .unwrap_or(PictureRect::max_rect())
+ .cast_unit();
+
+ let surface = &mut surfaces[surface_index.0];
+
+ let (clipped_local, unclipped_local, source_local) = match composite_mode {
+ PictureCompositeMode::SVGFEGraph(ref filters) => {
+ let prim_subregion = composite_mode.get_rect(surface, None);
+
+ let visible_subregion: LayoutRect =
+ prim_subregion.cast_unit()
+ .intersection(&local_clip_rect)
+ .unwrap_or(PictureRect::zero())
+ .cast_unit();
+
+ if visible_subregion.is_empty() {
+ return None;
+ }
+
+ let source_potential_subregion = get_coverage_source_svgfe(
+ filters, visible_subregion.cast_unit());
+ let source_subregion =
+ source_potential_subregion
+ .intersection(&surface.unclipped_local_rect.cast_unit())
+ .unwrap_or(LayoutRect::zero());
+
+ let coverage_subregion = source_subregion.union(&visible_subregion);
+
+ (coverage_subregion.cast_unit(), prim_subregion.cast_unit(), source_subregion.cast_unit())
+ }
+ PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
+ let local_prim_rect = surface.clipped_local_rect;
+
+ let mut required_local_rect = local_prim_rect
+ .intersection(&local_clip_rect)
+ .unwrap_or(PictureRect::zero());
+
+ for shadow in shadows {
+ let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
+ shadow.blur_radius,
+ shadow.blur_radius,
+ );
+ let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
+ let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
+
+ let local_shadow_rect = local_prim_rect
+ .translate(shadow.offset.cast_unit())
+ .inflate(blur_inflation_x, blur_inflation_y);
+
+ if let Some(clipped_shadow_rect) = local_clip_rect.intersection(&local_shadow_rect) {
+ let required_shadow_rect = clipped_shadow_rect.inflate(blur_inflation_x, blur_inflation_y);
+
+ let local_clipped_shadow_rect = required_shadow_rect.translate(-shadow.offset.cast_unit());
+
+ required_local_rect = required_local_rect.union(&local_clipped_shadow_rect);
+ }
+ }
+
+ let unclipped = composite_mode.get_rect(surface, None);
+ let clipped = required_local_rect;
+
+ let clipped = match clipped.intersection(&unclipped.cast_unit()) {
+ Some(rect) => rect,
+ None => return None,
+ };
+
+ (clipped, unclipped, clipped)
+ }
+ _ => {
+ let surface_origin = surface.clipped_local_rect.min.to_vector().cast_unit();
+
+ let normalized_prim_rect = composite_mode
+ .get_rect(surface, None)
+ .translate(-surface_origin);
+
+ let normalized_clip_rect = local_clip_rect
+ .cast_unit()
+ .translate(-surface_origin);
+
+ let norm_clipped_rect = match normalized_prim_rect.intersection(&normalized_clip_rect) {
+ Some(rect) => rect,
+ None => return None,
+ };
+
+ let norm_clipped_rect = composite_mode.get_rect(surface, Some(norm_clipped_rect));
+
+ let norm_clipped_rect = match norm_clipped_rect.intersection(&normalized_prim_rect) {
+ Some(rect) => rect,
+ None => return None,
+ };
+
+ let unclipped = normalized_prim_rect.translate(surface_origin);
+ let clipped = norm_clipped_rect.translate(surface_origin);
+
+ (clipped.cast_unit(), unclipped.cast_unit(), clipped.cast_unit())
+ }
+ };
+
+ let (mut clipped, mut unclipped, mut source) = if surface.raster_spatial_node_index != surface.surface_spatial_node_index {
+ assert_eq!(surface.device_pixel_scale.0, 1.0);
+
+ let local_to_world = SpaceMapper::new_with_target(
+ spatial_tree.root_reference_frame_index(),
+ surface.surface_spatial_node_index,
+ WorldRect::max_rect(),
+ spatial_tree,
+ );
+
+ let clipped = local_to_world.map(&clipped_local.cast_unit()).unwrap() * surface.device_pixel_scale;
+ let unclipped = local_to_world.map(&unclipped_local).unwrap() * surface.device_pixel_scale;
+ let source = local_to_world.map(&source_local.cast_unit()).unwrap() * surface.device_pixel_scale;
+
+ (clipped, unclipped, source)
+ } else {
+ let clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
+ let unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
+ let source = source_local.cast_unit() * surface.device_pixel_scale;
+
+ (clipped, unclipped, source)
+ };
+ let mut clipped_snapped = clipped.round_out();
+ let mut source_snapped = source.round_out();
+
+ let max_dimension =
+ clipped_snapped.width().max(
+ clipped_snapped.height().max(
+ source_snapped.width().max(
+ source_snapped.height()
+ ))).ceil();
+ if max_dimension > max_surface_size {
+ let max_dimension =
+ clipped_local.width().max(
+ clipped_local.height().max(
+ source_local.width().max(
+ source_local.height()
+ ))).ceil();
+ surface.raster_spatial_node_index = surface.surface_spatial_node_index;
+ surface.device_pixel_scale = Scale::new(max_surface_size / max_dimension);
+ surface.local_scale = (1.0, 1.0);
+
+ let add_markers = profiler::thread_is_being_profiled();
+ if add_markers {
+ let new_clipped = (clipped_local.cast_unit() * surface.device_pixel_scale).round();
+ let new_source = (source_local.cast_unit() * surface.device_pixel_scale).round();
+ profiler::add_text_marker("SurfaceSizeLimited",
+ format!("Surface for {:?} reduced from raster {:?} (source {:?}) to local {:?} (source {:?})",
+ composite_mode.kind(),
+ clipped.size(), source.size(),
+ new_clipped, new_source).as_str(),
+ Duration::from_secs_f32(new_clipped.width() * new_clipped.height() / 1000000000.0));
+ }
+
+ clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
+ unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
+ source = source_local.cast_unit() * surface.device_pixel_scale;
+ clipped_snapped = clipped.round();
+ source_snapped = source.round();
+ }
+
+ let task_size = clipped_snapped.size().to_i32();
+ let task_size = task_size.min(DeviceIntSize::new(max_surface_size as i32, max_surface_size as i32));
+ debug_assert!(
+ task_size.width <= max_surface_size as i32 &&
+ task_size.height <= max_surface_size as i32,
+ "task_size {:?} for {:?} must be within max_surface_size {}",
+ task_size,
+ composite_mode.kind(),
+ max_surface_size);
+
+ let uv_rect_kind = calculate_uv_rect_kind(
+ clipped_snapped,
+ unclipped,
+ );
+
+ if task_size.width == 0 || task_size.height == 0 {
+ return None;
+ }
+
+ let needs_scissor_rect = force_scissor_rect || !clipped_local.contains_box(&surface.unclipped_local_rect);
+
+ Some(SurfaceAllocInfo {
+ task_size,
+ needs_scissor_rect,
+ clipped: clipped_snapped,
+ unclipped,
+ source: source_snapped,
+ clipped_notsnapped: clipped,
+ clipped_local,
+ uv_rect_kind,
+ })
+}
+
+pub fn calculate_uv_rect_kind(
+ clipped: DeviceRect,
+ unclipped: DeviceRect,
+) -> UvRectKind {
+ let top_left = calculate_screen_uv(
+ unclipped.top_left().cast_unit(),
+ clipped,
+ );
+
+ let top_right = calculate_screen_uv(
+ unclipped.top_right().cast_unit(),
+ clipped,
+ );
+
+ let bottom_left = calculate_screen_uv(
+ unclipped.bottom_left().cast_unit(),
+ clipped,
+ );
+
+ let bottom_right = calculate_screen_uv(
+ unclipped.bottom_right().cast_unit(),
+ clipped,
+ );
+
+ UvRectKind::Quad {
+ top_left,
+ top_right,
+ bottom_left,
+ bottom_right,
+ }
+}
diff --git a/gfx/wr/webrender/src/surface.rs b/gfx/wr/webrender/src/surface.rs
@@ -9,13 +9,10 @@
use api::units::*;
use crate::box_shadow::BLUR_SAMPLE_SCALE;
use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, CommandBufferBuilder, CommandBufferIndex};
-use crate::gpu_types::UvRectKind;
use crate::internal_types::{FastHashMap, Filter};
use crate::picture::PictureCompositeMode;
-use crate::svg_filter::get_coverage_source_svgfe;
use crate::tile_cache::{TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES};
use crate::prim_store::PictureIndex;
-use crate::profiler;
use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
use crate::render_target::ResolveOp;
use crate::render_task::{RenderTask, RenderTaskKind, RenderTaskLocation};
@@ -23,8 +20,7 @@ use crate::space::SpaceMapper;
use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
use crate::util::MaxRect;
use crate::visibility::{VisibilityState, PrimitiveVisibility, FrameVisibilityContext};
-use core::time::Duration;
-use euclid::Scale;
+pub use crate::picture_composite_mode::{get_surface_rects, calculate_uv_rect_kind};
/// Maximum blur radius for blur filter
@@ -282,24 +278,6 @@ impl SurfaceInfo {
}
}
-/// Information from `get_surface_rects` about the allocated size, UV sampling
-/// parameters etc for an off-screen surface
-#[derive(Debug)]
-pub struct SurfaceAllocInfo {
- pub task_size: DeviceIntSize,
- pub needs_scissor_rect: bool,
- pub clipped: DeviceRect,
- pub unclipped: DeviceRect,
- // Only used for SVGFEGraph currently, this is the source pixels needed to
- // render the pixels in clipped.
- pub source: DeviceRect,
- // Only used for SVGFEGraph, this is the same as clipped before rounding.
- pub clipped_notsnapped: DeviceRect,
- pub clipped_local: PictureRect,
- pub uv_rect_kind: UvRectKind,
-}
-
-
// Information about the render task(s) for a given tile
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -911,303 +889,3 @@ pub fn calculate_screen_uv(
1.0,
)
}
-
-pub fn get_surface_rects(
- surface_index: SurfaceIndex,
- composite_mode: &PictureCompositeMode,
- parent_surface_index: SurfaceIndex,
- surfaces: &mut [SurfaceInfo],
- spatial_tree: &SpatialTree,
- max_surface_size: f32,
- force_scissor_rect: bool,
-) -> Option<SurfaceAllocInfo> {
- let parent_surface = &surfaces[parent_surface_index.0];
-
- let local_to_parent = SpaceMapper::new_with_target(
- parent_surface.surface_spatial_node_index,
- surfaces[surface_index.0].surface_spatial_node_index,
- parent_surface.clipping_rect,
- spatial_tree,
- );
-
- let local_clip_rect = local_to_parent
- .unmap(&parent_surface.clipping_rect)
- .unwrap_or(PictureRect::max_rect())
- .cast_unit();
-
- let surface = &mut surfaces[surface_index.0];
-
- let (clipped_local, unclipped_local, source_local) = match composite_mode {
- PictureCompositeMode::SVGFEGraph(ref filters) => {
- // We need to get the primitive rect, and get_coverage_target_svgfe
- // requires the provided rect is in user space (defined in SVG spec)
- // for subregion calculations to work properly
- //
- // Calculate the target rect from source rect, note that this can
- // produce a valid target rect even with an empty source rect in the
- // case of filters like feFlood, feComponentTransfer, feColorMatrix,
- // feImage and feTurbulence which can fill their whole subregion
- // even if given empty SourceGraphic. It can also produce a smaller
- // rect than source if subregions or filter region apply clipping to
- // the intermediate pictures or the final picture.
- let prim_subregion = composite_mode.get_rect(surface, None);
-
- // Clip the prim_subregion by the clip_rect, this will be put into
- // surface_rects.clipped.
- let visible_subregion: LayoutRect =
- prim_subregion.cast_unit()
- .intersection(&local_clip_rect)
- .unwrap_or(PictureRect::zero())
- .cast_unit();
-
- // If the visible_subregion was empty to begin with, or clipped away
- // entirely, then there is nothing to do here, this is the hot path
- // for culling of composited pictures.
- if visible_subregion.is_empty() {
- return None;
- }
-
- // Calculate the subregion for how much of SourceGraphic we may need
- // to produce to satisfy the invalidation rect, then clip it by the
- // original primitive rect because we have no reason to produce any
- // out of bounds pixels; they would just be blank anyway.
- let source_potential_subregion = get_coverage_source_svgfe(
- filters, visible_subregion.cast_unit());
- let source_subregion =
- source_potential_subregion
- .intersection(&surface.unclipped_local_rect.cast_unit())
- .unwrap_or(LayoutRect::zero());
-
- // For some reason, code assumes that the clipped_local rect we make
- // here will enclose the source_subregion, and also be a valid
- // prim_subregion, so we have to union the two rects to meet those
- // expectations. This is an optimization opportunity - figure out
- // how to make just the visible_subregion work here.
- let coverage_subregion = source_subregion.union(&visible_subregion);
-
- (coverage_subregion.cast_unit(), prim_subregion.cast_unit(), source_subregion.cast_unit())
- }
- PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
- let local_prim_rect = surface.clipped_local_rect;
-
- let mut required_local_rect = local_prim_rect
- .intersection(&local_clip_rect)
- .unwrap_or(PictureRect::zero());
-
- for shadow in shadows {
- let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
- shadow.blur_radius,
- shadow.blur_radius,
- );
- let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
- let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
-
- let local_shadow_rect = local_prim_rect
- .translate(shadow.offset.cast_unit())
- .inflate(blur_inflation_x, blur_inflation_y);
-
- if let Some(clipped_shadow_rect) = local_clip_rect.intersection(&local_shadow_rect) {
- let required_shadow_rect = clipped_shadow_rect.inflate(blur_inflation_x, blur_inflation_y);
-
- let local_clipped_shadow_rect = required_shadow_rect.translate(-shadow.offset.cast_unit());
-
- required_local_rect = required_local_rect.union(&local_clipped_shadow_rect);
- }
- }
-
- let unclipped = composite_mode.get_rect(surface, None);
- let clipped = required_local_rect;
-
- let clipped = match clipped.intersection(&unclipped.cast_unit()) {
- Some(rect) => rect,
- None => return None,
- };
-
- (clipped, unclipped, clipped)
- }
- _ => {
- let surface_origin = surface.clipped_local_rect.min.to_vector().cast_unit();
-
- let normalized_prim_rect = composite_mode
- .get_rect(surface, None)
- .translate(-surface_origin);
-
- let normalized_clip_rect = local_clip_rect
- .cast_unit()
- .translate(-surface_origin);
-
- let norm_clipped_rect = match normalized_prim_rect.intersection(&normalized_clip_rect) {
- Some(rect) => rect,
- None => return None,
- };
-
- let norm_clipped_rect = composite_mode.get_rect(surface, Some(norm_clipped_rect));
-
- let norm_clipped_rect = match norm_clipped_rect.intersection(&normalized_prim_rect) {
- Some(rect) => rect,
- None => return None,
- };
-
- let unclipped = normalized_prim_rect.translate(surface_origin);
- let clipped = norm_clipped_rect.translate(surface_origin);
-
- (clipped.cast_unit(), unclipped.cast_unit(), clipped.cast_unit())
- }
- };
-
- // We need to put the clipped, unclipped and source rects in the chosen
- // raster spatial node if possible, so that it will be rendered at the
- // proper pixel scale with antialiasing, otherwise it would be blurry.
- let (mut clipped, mut unclipped, mut source) = if surface.raster_spatial_node_index != surface.surface_spatial_node_index {
- // Transform surface into the chosen raster spatial node
- assert_eq!(surface.device_pixel_scale.0, 1.0);
-
- let local_to_world = SpaceMapper::new_with_target(
- spatial_tree.root_reference_frame_index(),
- surface.surface_spatial_node_index,
- WorldRect::max_rect(),
- spatial_tree,
- );
-
- let clipped = local_to_world.map(&clipped_local.cast_unit()).unwrap() * surface.device_pixel_scale;
- let unclipped = local_to_world.map(&unclipped_local).unwrap() * surface.device_pixel_scale;
- let source = local_to_world.map(&source_local.cast_unit()).unwrap() * surface.device_pixel_scale;
-
- (clipped, unclipped, source)
- } else {
- // Surface is already in the chosen raster spatial node
- let clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
- let unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
- let source = source_local.cast_unit() * surface.device_pixel_scale;
-
- (clipped, unclipped, source)
- };
- let mut clipped_snapped = clipped.round_out();
- let mut source_snapped = source.round_out();
-
- // We need to make sure the surface size does not exceed max_surface_size,
- // if it would exceed it we actually want to keep the surface in its local
- // space and stop worrying about it being a little blurry.
- //
- // Since both clipped and source are subject to the same limit, we can just
- // pick the largest axis from all rects involved.
- //
- // Importantly, surfaces that are exactly at max_surface_size are relatively
- // common for some reason, so we don't want to use a conservative limit.
- //
- // If you change this, test with:
- // ./mach crashtest layout/svg/crashtests/387290-1.svg
- let max_dimension =
- clipped_snapped.width().max(
- clipped_snapped.height().max(
- source_snapped.width().max(
- source_snapped.height()
- ))).ceil();
- if max_dimension > max_surface_size {
- // We have to recalculate max_dimension for the local space we'll be
- // using as we're no longer rasterizing in the parent space
- let max_dimension =
- clipped_local.width().max(
- clipped_local.height().max(
- source_local.width().max(
- source_local.height()
- ))).ceil();
- surface.raster_spatial_node_index = surface.surface_spatial_node_index;
- surface.device_pixel_scale = Scale::new(max_surface_size / max_dimension);
- surface.local_scale = (1.0, 1.0);
-
- let add_markers = profiler::thread_is_being_profiled();
- if add_markers {
- let new_clipped = (clipped_local.cast_unit() * surface.device_pixel_scale).round();
- let new_source = (source_local.cast_unit() * surface.device_pixel_scale).round();
- profiler::add_text_marker("SurfaceSizeLimited",
- format!("Surface for {:?} reduced from raster {:?} (source {:?}) to local {:?} (source {:?})",
- composite_mode.kind(),
- clipped.size(), source.size(),
- new_clipped, new_source).as_str(),
- Duration::from_secs_f32(new_clipped.width() * new_clipped.height() / 1000000000.0));
- }
-
- clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
- unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
- source = source_local.cast_unit() * surface.device_pixel_scale;
- clipped_snapped = clipped.round();
- source_snapped = source.round();
- }
-
- let task_size = clipped_snapped.size().to_i32();
- // We must avoid hitting the assert here at all costs because panics here
- // will repeatedly crash the GPU Process, making the whole app unusable,
- // so make sure task_size <= max_surface_size, it's possible that we lose a
- // pixel here if the max_dimension threshold was not optimal.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1948939 for more info.
- let task_size = task_size.min(DeviceIntSize::new(max_surface_size as i32, max_surface_size as i32));
- debug_assert!(
- task_size.width <= max_surface_size as i32 &&
- task_size.height <= max_surface_size as i32,
- "task_size {:?} for {:?} must be within max_surface_size {}",
- task_size,
- composite_mode.kind(),
- max_surface_size);
-
- let uv_rect_kind = calculate_uv_rect_kind(
- clipped_snapped,
- unclipped,
- );
-
- // If the task size is zero sized, skip creation and drawing of it
- if task_size.width == 0 || task_size.height == 0 {
- return None;
- }
-
- // If the final clipped surface rect is not the same or larger as the unclipped
- // local rect of the surface, we need to enable scissor rect (which disables
- // merging batches between this and other render tasks allocated to the same
- // render target). This is conservative - we could do better in future by
- // distinguishing between clips that affect the surface itself vs. clips on
- // child primitives that don't affect this.
- let needs_scissor_rect = force_scissor_rect || !clipped_local.contains_box(&surface.unclipped_local_rect);
-
- Some(SurfaceAllocInfo {
- task_size,
- needs_scissor_rect,
- clipped: clipped_snapped,
- unclipped,
- source: source_snapped,
- clipped_notsnapped: clipped,
- clipped_local,
- uv_rect_kind,
- })
-}
-
-pub fn calculate_uv_rect_kind(
- clipped: DeviceRect,
- unclipped: DeviceRect,
-) -> UvRectKind {
- let top_left = calculate_screen_uv(
- unclipped.top_left().cast_unit(),
- clipped,
- );
-
- let top_right = calculate_screen_uv(
- unclipped.top_right().cast_unit(),
- clipped,
- );
-
- let bottom_left = calculate_screen_uv(
- unclipped.bottom_left().cast_unit(),
- clipped,
- );
-
- let bottom_right = calculate_screen_uv(
- unclipped.bottom_right().cast_unit(),
- clipped,
- );
-
- UvRectKind::Quad {
- top_left,
- top_right,
- bottom_left,
- bottom_right,
- }
-}