tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mgfx/wr/webrender/src/lib.rs | 1+
Mgfx/wr/webrender/src/picture.rs | 782++-----------------------------------------------------------------------------
Agfx/wr/webrender/src/picture_composite_mode.rs | 1011+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/wr/webrender/src/surface.rs | 324+------------------------------------------------------------------------------
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, - } -}