tor-browser

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

commit ccc5cf33fd78c4806d7a1e35cded7e3edb86e2dc
parent 2f560ef05ad942d0acb0543ab5405032988ab53e
Author: Nicolas Silva <nical@fastmail.com>
Date:   Mon, 15 Dec 2025 10:53:20 +0000

Bug 1998913 - Part 5 - Extract the composite mode prep code into its own function. r=gfx-reviewers,gw

PicturePrimitive::take_context has too much going on. After a hour looking at it I would not be able to give a short explanation of what it does. In this patch and follow ups I'm attempting to break some self-contained parts out of it.

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

Diffstat:
Mgfx/wr/webrender/src/picture.rs | 1099+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 569 insertions(+), 530 deletions(-)

diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs @@ -1560,537 +1560,28 @@ impl PicturePrimitive { None => return None, }; - let (raster_spatial_node_index, device_pixel_scale) = { - let surface = &frame_state.surfaces[surface_index.0]; - (surface.raster_spatial_node_index, surface.device_pixel_scale) - }; - let can_use_shared_surface = !self.flags.contains(PictureFlags::IS_RESOLVE_TARGET); - - let primary_render_task_id; - let surface_descriptor; - match raster_config.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, - &self.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, - ); + if let PictureCompositeMode::IntermediateSurface = raster_config.composite_mode { + if !scratch.required_sub_graphs.contains(&pic_index) { + return None; } - PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => { - let surface = &frame_state.surfaces[raster_config.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(); - - self.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; - self.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_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, - ); - - self.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, - &self.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, - &self.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, - &self.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, - &self.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 => { - if !scratch.required_sub_graphs.contains(&pic_index) { - return None; - } - - // 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, - &self.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, - &self.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; + let can_use_shared_surface = !self.flags.contains(PictureFlags::IS_RESOLVE_TARGET); + let (surface_descriptor, render_tasks) = prepare_composite_mode( + &raster_config.composite_mode, + surface_index, + parent_surface_index, + &surface_rects, + &self.snapshot, + can_use_shared_surface, + frame_context, + frame_state, + data_stores, + &mut self.extra_gpu_data, + ); - surface_descriptor = SurfaceDescriptor::new_chained( - picture_task_id, - filter_task_id, - surface_rects.clipped_local, - ); - } - } + self.primary_render_task_id = render_tasks[0]; + self.secondary_render_task_id = render_tasks[1]; let is_sub_graph = self.flags.contains(PictureFlags::IS_SUB_GRAPH); @@ -2102,8 +1593,6 @@ impl PicturePrimitive { frame_state.surfaces, frame_state.rg_builder, ); - - self.primary_render_task_id = Some(primary_render_task_id); } None => {} }; @@ -2990,6 +2479,556 @@ pub fn get_relative_scale_offset( scale_offset } +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, + ] + ) +} + #[test] fn test_large_surface_scale_1() { use crate::spatial_tree::{SceneSpatialTree, SpatialTree};