webrender/surface.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! Contains functionality to help building the render task graph from a series of off-screen
6//! surfaces that are created during the prepare pass, and other surface related types and
7//! helpers.
8
9use api::units::*;
10use crate::box_shadow::BLUR_SAMPLE_SCALE;
11use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, CommandBufferBuilder, CommandBufferIndex};
12use crate::internal_types::{FastHashMap, Filter};
13use crate::picture::PictureCompositeMode;
14use crate::tile_cache::{TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES};
15use crate::prim_store::PictureIndex;
16use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
17use crate::render_target::ResolveOp;
18use crate::render_task::{RenderTask, RenderTaskKind, RenderTaskLocation};
19use crate::space::SpaceMapper;
20use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
21use crate::util::MaxRect;
22use crate::visibility::{DrawState, PrimitiveDrawHeader, FrameVisibilityContext};
23pub use crate::picture_composite_mode::get_surface_rects;
24
25
26/// Maximum blur radius for blur filter
27const MAX_BLUR_RADIUS: f32 = 100.;
28
29/// An index into the surface array
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31#[cfg_attr(feature = "capture", derive(Serialize))]
32#[cfg_attr(feature = "replay", derive(Deserialize))]
33pub struct SurfaceIndex(pub usize);
34
35/// Specify whether a surface allows subpixel AA text rendering.
36#[derive(Debug, Copy, Clone)]
37pub enum SubpixelMode {
38 /// This surface allows subpixel AA text
39 Allow,
40 /// Subpixel AA text cannot be drawn on this surface
41 Deny,
42 /// Subpixel AA can be drawn on this surface, if not intersecting
43 /// with the excluded regions, and inside the allowed rect.
44 Conditional {
45 allowed_rect: PictureRect,
46 prohibited_rect: PictureRect,
47 },
48}
49
50/// Information about an offscreen surface. For now,
51/// it contains information about the size and coordinate
52/// system of the surface. In the future, it will contain
53/// information about the contents of the surface, which
54/// will allow surfaces to be cached / retained between
55/// frames and display lists.
56pub struct SurfaceInfo {
57 /// A local rect defining the size of this surface, in the
58 /// coordinate system of the parent surface. This contains
59 /// the unclipped bounding rect of child primitives.
60 ///
61 /// SNAPTODO: This rect is built by mapping per-cluster bounding
62 /// rects (and child-surface coverage rects) into this surface's
63 /// picture space via `map_local_to_picture`. Even once the source
64 /// cluster bound is a true union of per-prim *snapped* local
65 /// rects, the resulting `unclipped_local_rect` is not guaranteed
66 /// to be snapped: any 2D transform that isn't an axis-aligned,
67 /// integer-pixel translation between the cluster/child-surface
68 /// spatial node and this surface's spatial node will produce
69 /// sub-pixel edges in picture space. Float blur-margin inflation
70 /// inside `composite_mode.get_coverage` can also break the snap.
71 /// Consumers that need a snapped value will either need to
72 /// re-snap in surface space or restrict the snap path to surfaces
73 /// where the cross-space mapping preserves grid alignment (see
74 /// `SurfaceInfo.allow_snapping`).
75 pub unclipped_local_rect: PictureRect,
76 /// The local space coverage of child primitives after they are
77 /// are clipped to their owning clip-chain.
78 pub clipped_local_rect: PictureRect,
79 /// The (conservative) valid part of this surface rect. Used
80 /// to reduce the size of render target allocation.
81 pub clipping_rect: PictureRect,
82 /// The rectangle to use for culling and clipping.
83 pub culling_rect: VisRect,
84 /// Helper structs for mapping local rects in different
85 /// coordinate systems into the picture coordinates.
86 pub map_local_to_picture: SpaceMapper<LayoutPixel, PicturePixel>,
87 /// The positioning node for the surface itself,
88 pub surface_spatial_node_index: SpatialNodeIndex,
89 /// The rasterization root for this surface.
90 pub raster_spatial_node_index: SpatialNodeIndex,
91 /// The spatial node for culling and clipping (anything using VisPixel).
92 /// TODO: Replace with the raster spatial node.
93 pub visibility_spatial_node_index: SpatialNodeIndex,
94 /// The device pixel ratio specific to this surface.
95 pub device_pixel_scale: DevicePixelScale,
96 /// The scale factors of the surface to world transform.
97 pub world_scale_factors: (f32, f32),
98 /// Local scale factors surface to raster transform
99 pub local_scale: (f32, f32),
100 /// If true, we know this surface is completely opaque.
101 pub is_opaque: bool,
102 /// If true, allow snapping on this and child surfaces
103 pub allow_snapping: bool,
104 /// If true, the scissor rect must be set when drawing this surface
105 pub force_scissor_rect: bool,
106}
107
108impl SurfaceInfo {
109 pub fn new(
110 surface_spatial_node_index: SpatialNodeIndex,
111 raster_spatial_node_index: SpatialNodeIndex,
112 world_rect: WorldRect,
113 spatial_tree: &SpatialTree,
114 device_pixel_scale: DevicePixelScale,
115 world_scale_factors: (f32, f32),
116 local_scale: (f32, f32),
117 allow_snapping: bool,
118 force_scissor_rect: bool,
119 ) -> Self {
120 let map_surface_to_world = SpaceMapper::new_with_target(
121 spatial_tree.root_reference_frame_index(),
122 surface_spatial_node_index,
123 world_rect,
124 spatial_tree,
125 );
126
127 let pic_bounds = map_surface_to_world
128 .unmap(&map_surface_to_world.bounds)
129 .unwrap_or_else(PictureRect::max_rect);
130
131 let map_local_to_picture = SpaceMapper::new(
132 surface_spatial_node_index,
133 pic_bounds,
134 );
135
136 // TODO: replace the root with raster space.
137 let visibility_spatial_node_index = spatial_tree.root_reference_frame_index();
138
139 SurfaceInfo {
140 unclipped_local_rect: PictureRect::zero(),
141 clipped_local_rect: PictureRect::zero(),
142 is_opaque: false,
143 clipping_rect: PictureRect::zero(),
144 map_local_to_picture,
145 raster_spatial_node_index,
146 surface_spatial_node_index,
147 visibility_spatial_node_index,
148 device_pixel_scale,
149 world_scale_factors,
150 local_scale,
151 allow_snapping,
152 force_scissor_rect,
153 // TODO: At the moment all culling is done in world space but
154 // but the plan is to move it to raster space.
155 culling_rect: world_rect.cast_unit(),
156 }
157 }
158
159 /// Clamps the blur radius depending on scale factors.
160 pub fn clamp_blur_radius(
161 &self,
162 x_blur_radius: f32,
163 y_blur_radius: f32,
164 ) -> (f32, f32) {
165 // Clamping must occur after scale factors are applied, but scale factors are not applied
166 // until later on. To clamp the blur radius, we first apply the scale factors and then clamp
167 // and finally revert the scale factors.
168
169 let sx_blur_radius = x_blur_radius * self.local_scale.0;
170 let sy_blur_radius = y_blur_radius * self.local_scale.1;
171
172 let largest_scaled_blur_radius = f32::max(
173 sx_blur_radius * self.world_scale_factors.0,
174 sy_blur_radius * self.world_scale_factors.1,
175 );
176
177 if largest_scaled_blur_radius > MAX_BLUR_RADIUS {
178 let sf = MAX_BLUR_RADIUS / largest_scaled_blur_radius;
179 (x_blur_radius * sf, y_blur_radius * sf)
180 } else {
181 // Return the original blur radius to avoid any rounding errors
182 (x_blur_radius, y_blur_radius)
183 }
184 }
185
186 pub fn update_culling_rect(
187 &mut self,
188 parent_culling_rect: VisRect,
189 composite_mode: &PictureCompositeMode,
190 frame_context: &FrameVisibilityContext,
191 ) {
192 // Set the default culling rect to be the parent, in case we fail
193 // any mappings below due to weird perspective or invalid transforms.
194 self.culling_rect = parent_culling_rect;
195
196 if let PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) = composite_mode {
197 if *should_inflate {
198 // Space mapping vis <-> picture space
199 let map_surface_to_vis = SpaceMapper::new_with_target(
200 // TODO: switch from root to raster space.
201 frame_context.root_spatial_node_index,
202 self.surface_spatial_node_index,
203 parent_culling_rect,
204 frame_context.spatial_tree,
205 );
206
207 // Unmap the parent culling rect to surface space. Note that this may be
208 // quite conservative in the case of a complex transform, especially perspective.
209 if let Some(local_parent_culling_rect) = map_surface_to_vis.unmap(&parent_culling_rect) {
210 let (width_factor, height_factor) = self.clamp_blur_radius(*width, *height);
211
212 // Inflate by the local-space amount this surface extends.
213 let expanded_rect: PictureBox2D = local_parent_culling_rect.inflate(
214 width_factor.ceil() * BLUR_SAMPLE_SCALE,
215 height_factor.ceil() * BLUR_SAMPLE_SCALE,
216 );
217
218 // Map back to the expected vis-space culling rect
219 if let Some(rect) = map_surface_to_vis.map(&expanded_rect) {
220 self.culling_rect = rect;
221 }
222 }
223 }
224 }
225 }
226
227 pub fn map_to_device_rect(
228 &self,
229 picture_rect: &PictureRect,
230 spatial_tree: &SpatialTree,
231 ) -> DeviceRect {
232 let raster_rect = if self.raster_spatial_node_index != self.surface_spatial_node_index {
233 // Currently, the surface's spatial node can be different from its raster node only
234 // for surfaces in the root coordinate system for snapping reasons.
235 // See `PictureInstance::assign_surface`.
236 assert_eq!(self.device_pixel_scale.0, 1.0);
237 assert_eq!(self.raster_spatial_node_index, spatial_tree.root_reference_frame_index());
238
239 let pic_to_raster = SpaceMapper::new_with_target(
240 self.raster_spatial_node_index,
241 self.surface_spatial_node_index,
242 WorldRect::max_rect(),
243 spatial_tree,
244 );
245
246 pic_to_raster.map(&picture_rect).unwrap()
247 } else {
248 picture_rect.cast_unit()
249 };
250
251 raster_rect * self.device_pixel_scale
252 }
253
254 /// Clip and transform a local rect to a device rect suitable for allocating
255 /// a child off-screen surface of this surface (e.g. for clip-masks)
256 pub fn get_surface_rect(
257 &self,
258 local_rect: &PictureRect,
259 spatial_tree: &SpatialTree,
260 ) -> Option<DeviceIntRect> {
261 let local_rect = match local_rect.intersection(&self.clipping_rect) {
262 Some(rect) => rect,
263 None => return None,
264 };
265
266 let raster_rect = if self.raster_spatial_node_index != self.surface_spatial_node_index {
267 assert_eq!(self.device_pixel_scale.0, 1.0);
268
269 let local_to_world = SpaceMapper::new_with_target(
270 spatial_tree.root_reference_frame_index(),
271 self.surface_spatial_node_index,
272 WorldRect::max_rect(),
273 spatial_tree,
274 );
275
276 local_to_world.map(&local_rect).unwrap()
277 } else {
278 // The content should have been culled out earlier.
279 assert!(self.device_pixel_scale.0 > 0.0);
280
281 local_rect.cast_unit()
282 };
283
284 let surface_rect = (raster_rect * self.device_pixel_scale).round_out().to_i32();
285 if surface_rect.is_empty() {
286 // The local_rect computed above may have non-empty size that is very
287 // close to zero. Due to limited arithmetic precision, the SpaceMapper
288 // might transform the near-zero-sized rect into a zero-sized one.
289 return None;
290 }
291
292 Some(surface_rect)
293 }
294}
295
296// Information about the render task(s) for a given tile
297#[cfg_attr(feature = "capture", derive(Serialize))]
298#[cfg_attr(feature = "replay", derive(Deserialize))]
299pub struct SurfaceTileDescriptor {
300 /// Target render task for commands added to this tile. This is changed
301 /// each time a sub-graph is encountered on this tile
302 pub current_task_id: RenderTaskId,
303 /// The compositing task for this tile, if required. This is only needed
304 /// when a tile contains one or more sub-graphs.
305 pub composite_task_id: Option<RenderTaskId>,
306 /// Dirty rect for this tile
307 pub dirty_rect: PictureRect,
308}
309
310// Details of how a surface is rendered
311pub enum SurfaceDescriptorKind {
312 // Picture cache tiles
313 Tiled {
314 tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
315 },
316 // A single surface (e.g. for an opacity filter)
317 Simple {
318 render_task_id: RenderTaskId,
319 dirty_rect: PictureRect,
320 },
321 // A surface with 1+ intermediate tasks (e.g. blur)
322 Chained {
323 render_task_id: RenderTaskId,
324 root_task_id: RenderTaskId,
325 dirty_rect: PictureRect,
326 },
327}
328
329// Describes how a surface is rendered
330pub struct SurfaceDescriptor {
331 kind: SurfaceDescriptorKind,
332}
333
334impl SurfaceDescriptor {
335 // Create a picture cache tiled surface
336 pub fn new_tiled(
337 tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
338 ) -> Self {
339 SurfaceDescriptor {
340 kind: SurfaceDescriptorKind::Tiled {
341 tiles,
342 },
343 }
344 }
345
346 // Create a chained surface (e.g. blur)
347 pub fn new_chained(
348 render_task_id: RenderTaskId,
349 root_task_id: RenderTaskId,
350 dirty_rect: PictureRect,
351 ) -> Self {
352 SurfaceDescriptor {
353 kind: SurfaceDescriptorKind::Chained {
354 render_task_id,
355 root_task_id,
356 dirty_rect,
357 },
358 }
359 }
360
361 // Create a simple surface (e.g. opacity)
362 pub fn new_simple(
363 render_task_id: RenderTaskId,
364 dirty_rect: PictureRect,
365 ) -> Self {
366 SurfaceDescriptor {
367 kind: SurfaceDescriptorKind::Simple {
368 render_task_id,
369 dirty_rect,
370 },
371 }
372 }
373}
374
375// Describes a list of command buffers that we are adding primitives to
376// for a given surface. These are created from a command buffer builder
377// as an optimization - skipping the indirection pic_task -> cmd_buffer_index
378struct CommandBufferTargets {
379 available_cmd_buffers: Vec<Vec<(PictureRect, CommandBufferIndex)>>,
380}
381
382impl CommandBufferTargets {
383 fn new() -> Self {
384 CommandBufferTargets {
385 available_cmd_buffers: vec![Vec::new(); MAX_COMPOSITOR_SURFACES+1],
386 }
387 }
388
389 fn init(
390 &mut self,
391 cb: &CommandBufferBuilder,
392 rg_builder: &RenderTaskGraphBuilder,
393 ) {
394 for available_cmd_buffers in &mut self.available_cmd_buffers {
395 available_cmd_buffers.clear();
396 }
397
398 match cb.kind {
399 CommandBufferBuilderKind::Tiled { ref tiles, .. } => {
400 for (key, desc) in tiles {
401 let task = rg_builder.get_task(desc.current_task_id);
402 match task.kind {
403 RenderTaskKind::Picture(ref info) => {
404 let available_cmd_buffers = &mut self.available_cmd_buffers[key.sub_slice_index.as_usize()];
405 available_cmd_buffers.push((desc.dirty_rect, info.cmd_buffer_index));
406 }
407 _ => unreachable!("bug: not a picture"),
408 }
409 }
410 }
411 CommandBufferBuilderKind::Simple { render_task_id, dirty_rect, .. } => {
412 let task = rg_builder.get_task(render_task_id);
413 match task.kind {
414 RenderTaskKind::Picture(ref info) => {
415 for sub_slice_buffer in &mut self.available_cmd_buffers {
416 sub_slice_buffer.push((dirty_rect, info.cmd_buffer_index));
417 }
418 }
419 _ => unreachable!("bug: not a picture"),
420 }
421 }
422 CommandBufferBuilderKind::Invalid => {}
423 };
424 }
425
426 /// For a given rect and sub-slice, get a list of command buffers to write commands to
427 fn get_cmd_buffer_targets_for_rect(
428 &mut self,
429 rect: &PictureRect,
430 sub_slice_index: SubSliceIndex,
431 targets: &mut Vec<CommandBufferIndex>,
432 ) -> bool {
433
434 for (dirty_rect, cmd_buffer_index) in &self.available_cmd_buffers[sub_slice_index.as_usize()] {
435 if dirty_rect.intersects(rect) {
436 targets.push(*cmd_buffer_index);
437 }
438 }
439
440 !targets.is_empty()
441 }
442}
443
444// Main helper interface to build a graph of surfaces. In future patches this
445// will support building sub-graphs.
446pub struct SurfaceBuilder {
447 // The currently set cmd buffer targets (updated during push/pop)
448 current_cmd_buffers: CommandBufferTargets,
449 // Stack of surfaces that are parents to the current targets
450 builder_stack: Vec<CommandBufferBuilder>,
451 // A map of the output render tasks from any sub-graphs that haven't
452 // been consumed by BackdropRender prims yet
453 pub sub_graph_output_map: FastHashMap<PictureIndex, RenderTaskId>,
454}
455
456impl SurfaceBuilder {
457 pub fn new() -> Self {
458 SurfaceBuilder {
459 current_cmd_buffers: CommandBufferTargets::new(),
460 builder_stack: Vec::new(),
461 sub_graph_output_map: FastHashMap::default(),
462 }
463 }
464
465 /// Register the current surface as the source of a resolve for the task sub-graph that
466 /// is currently on the surface builder stack.
467 pub fn register_resolve_source(
468 &mut self,
469 ) {
470 let surface_task_id = match self.builder_stack.last().unwrap().kind {
471 CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
472 panic!("bug: only supported for non-tiled surfaces");
473 }
474 CommandBufferBuilderKind::Simple { render_task_id, .. } => render_task_id,
475 };
476
477 for builder in self.builder_stack.iter_mut().rev() {
478 if builder.establishes_sub_graph {
479 assert_eq!(builder.resolve_source, None);
480 builder.resolve_source = Some(surface_task_id);
481 return;
482 }
483 }
484
485 unreachable!("bug: resolve source with no sub-graph");
486 }
487
488 pub fn push_surface(
489 &mut self,
490 surface_index: SurfaceIndex,
491 is_sub_graph: bool,
492 clipping_rect: PictureRect,
493 descriptor: Option<SurfaceDescriptor>,
494 surfaces: &mut [SurfaceInfo],
495 rg_builder: &RenderTaskGraphBuilder,
496 ) {
497 // Init the surface
498 surfaces[surface_index.0].clipping_rect = clipping_rect;
499
500 let builder = if let Some(descriptor) = descriptor {
501 match descriptor.kind {
502 SurfaceDescriptorKind::Tiled { tiles } => {
503 CommandBufferBuilder::new_tiled(
504 tiles,
505 )
506 }
507 SurfaceDescriptorKind::Simple { render_task_id, dirty_rect, .. } => {
508 CommandBufferBuilder::new_simple(
509 render_task_id,
510 is_sub_graph,
511 None,
512 dirty_rect,
513 )
514 }
515 SurfaceDescriptorKind::Chained { render_task_id, root_task_id, dirty_rect, .. } => {
516 CommandBufferBuilder::new_simple(
517 render_task_id,
518 is_sub_graph,
519 Some(root_task_id),
520 dirty_rect,
521 )
522 }
523 }
524 } else {
525 CommandBufferBuilder::empty()
526 };
527
528 self.current_cmd_buffers.init(&builder, rg_builder);
529 self.builder_stack.push(builder);
530 }
531
532 // Add a child render task (e.g. a render task cache item, or a clip mask) as a
533 // dependency of the current surface
534 pub fn add_child_render_task(
535 &mut self,
536 child_task_id: RenderTaskId,
537 rg_builder: &mut RenderTaskGraphBuilder,
538 ) {
539 let builder = self.builder_stack.last().unwrap();
540
541 match builder.kind {
542 CommandBufferBuilderKind::Tiled { ref tiles } => {
543 for (_, descriptor) in tiles {
544 rg_builder.add_dependency(
545 descriptor.current_task_id,
546 child_task_id,
547 );
548 }
549 }
550 CommandBufferBuilderKind::Simple { render_task_id, .. } => {
551 rg_builder.add_dependency(
552 render_task_id,
553 child_task_id,
554 );
555 }
556 CommandBufferBuilderKind::Invalid { .. } => {}
557 }
558 }
559
560 // Add a picture render task as a dependency of the parent surface. This is a
561 // special case with extra complexity as the root of the surface may change
562 // when inside a sub-graph. It's currently only needed for drop-shadow effects.
563 pub fn add_picture_render_task(
564 &mut self,
565 child_task_id: RenderTaskId,
566 ) {
567 self.builder_stack
568 .last_mut()
569 .unwrap()
570 .extra_dependencies
571 .push(child_task_id);
572 }
573
574 // Get a list of command buffer indices that primitives should be pushed
575 // to for a given current visbility / dirty state
576 pub fn get_cmd_buffer_targets_for_prim(
577 &mut self,
578 vis: &PrimitiveDrawHeader,
579 targets: &mut Vec<CommandBufferIndex>,
580 ) -> bool {
581 targets.clear();
582
583 match vis.state {
584 DrawState::Unset => {
585 panic!("bug: invalid vis state");
586 }
587 DrawState::Culled => {
588 false
589 }
590 DrawState::Visible { sub_slice_index, .. } => {
591 self.current_cmd_buffers.get_cmd_buffer_targets_for_rect(
592 &vis.clip_chain.pic_coverage_rect,
593 sub_slice_index,
594 targets,
595 )
596 }
597 DrawState::PassThrough => {
598 true
599 }
600 }
601 }
602
603 pub fn pop_empty_surface(&mut self) {
604 let builder = self.builder_stack.pop().unwrap();
605 assert!(!builder.establishes_sub_graph);
606 }
607
608 // Finish adding primitives and child tasks to a surface and pop it off the stack
609 pub fn pop_surface(
610 &mut self,
611 pic_index: PictureIndex,
612 rg_builder: &mut RenderTaskGraphBuilder,
613 cmd_buffers: &mut CommandBufferList,
614 ) {
615 let builder = self.builder_stack.pop().unwrap();
616
617 if builder.establishes_sub_graph {
618 // If we are popping a sub-graph off the stack the dependency setup is rather more complex...
619 match builder.kind {
620 CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
621 unreachable!("bug: sub-graphs can only be simple surfaces");
622 }
623 CommandBufferBuilderKind::Simple { render_task_id: child_render_task_id, root_task_id: child_root_task_id, .. } => {
624 // Get info about the resolve operation to copy from parent surface or tiles to the picture cache task
625 if let Some(resolve_task_id) = builder.resolve_source {
626 let mut src_task_ids = Vec::new();
627
628 // Make the output of the sub-graph a dependency of the new replacement tile task
629 let _old = self.sub_graph_output_map.insert(
630 pic_index,
631 child_root_task_id.unwrap_or(child_render_task_id),
632 );
633 debug_assert!(_old.is_none());
634
635 // Set up dependencies for the sub-graph. The basic concepts below are the same, but for
636 // tiled surfaces are a little more complex as there are multiple tasks to set up.
637 // (a) Set up new task(s) on parent surface that write to the same location
638 // (b) Set up a resolve target to copy from parent surface tasks(s) to the resolve target
639 // (c) Make the old parent surface tasks input dependencies of the resolve target
640 // (d) Make the sub-graph output an input dependency of the new task(s).
641
642 match self.builder_stack.last_mut().unwrap().kind {
643 CommandBufferBuilderKind::Tiled { ref mut tiles } => {
644 let keys: Vec<TileKey> = tiles.keys().cloned().collect();
645
646 // For each tile in parent surface
647 for key in keys {
648 let descriptor = tiles.remove(&key).unwrap();
649 let parent_task_id = descriptor.current_task_id;
650 let parent_task = rg_builder.get_task_mut(parent_task_id);
651
652 match parent_task.location {
653 RenderTaskLocation::Unallocated { .. } | RenderTaskLocation::Existing { .. } => {
654 // Get info about the parent tile task location and params
655 let location = RenderTaskLocation::Existing {
656 parent_task_id,
657 size: parent_task.location.size(),
658 };
659
660 let pic_task = match parent_task.kind {
661 RenderTaskKind::Picture(ref mut pic_task) => {
662 let cmd_buffer_index = cmd_buffers.create_cmd_buffer();
663 let new_pic_task = pic_task.duplicate(cmd_buffer_index);
664
665 // Add the resolve src to copy from tile -> picture input task
666 src_task_ids.push(parent_task_id);
667
668 new_pic_task
669 }
670 _ => panic!("bug: not a picture"),
671 };
672
673 // Make the existing tile an input dependency of the resolve target
674 rg_builder.add_dependency(
675 resolve_task_id,
676 parent_task_id,
677 );
678
679 // Create the new task to replace the tile task
680 let new_task_id = rg_builder.add().init(
681 RenderTask::new(
682 location, // draw to same place
683 RenderTaskKind::Picture(pic_task),
684 ),
685 );
686
687 // Ensure that the parent task will get scheduled earlier during
688 // pass assignment since we are reusing the existing surface,
689 // even though it's not technically needed for rendering order.
690 rg_builder.add_dependency(
691 new_task_id,
692 parent_task_id,
693 );
694
695 // Update the surface builder with the now current target for future primitives
696 tiles.insert(
697 key,
698 SurfaceTileDescriptor {
699 current_task_id: new_task_id,
700 ..descriptor
701 },
702 );
703 }
704 RenderTaskLocation::Static { .. } => {
705 // Update the surface builder with the now current target for future primitives
706 tiles.insert(
707 key,
708 descriptor,
709 );
710 }
711 _ => {
712 panic!("bug: unexpected task location");
713 }
714 }
715 }
716 }
717 CommandBufferBuilderKind::Simple { render_task_id: ref mut parent_task_id, root_task_id: ref parent_root_task_id, .. } => {
718 let parent_task = rg_builder.get_task_mut(*parent_task_id);
719
720 // Get info about the parent tile task location and params
721 let location = RenderTaskLocation::Existing {
722 parent_task_id: *parent_task_id,
723 size: parent_task.location.size(),
724 };
725 let pic_task = match parent_task.kind {
726 RenderTaskKind::Picture(ref mut pic_task) => {
727 let cmd_buffer_index = cmd_buffers.create_cmd_buffer();
728
729 let new_pic_task = pic_task.duplicate(cmd_buffer_index);
730
731 // Add the resolve src to copy from tile -> picture input task
732 src_task_ids.push(*parent_task_id);
733
734 new_pic_task
735 }
736 _ => panic!("bug: not a picture"),
737 };
738
739 // Make the existing surface an input dependency of the resolve target
740 rg_builder.add_dependency(
741 resolve_task_id,
742 *parent_task_id,
743 );
744
745 // Create the new task to replace the parent surface task
746 let new_task_id = rg_builder.add().init(
747 RenderTask::new(
748 location, // draw to same place
749 RenderTaskKind::Picture(pic_task),
750 ),
751 );
752
753 // Ensure that the parent task will get scheduled earlier during
754 // pass assignment since we are reusing the existing surface,
755 // even though it's not technically needed for rendering order.
756 rg_builder.add_dependency(
757 new_task_id,
758 *parent_task_id,
759 );
760
761 // If the parent is a chained surface (e.g. a CSS blur or drop-shadow
762 // filter), its filter pass (root_task_id) reads from the same texture
763 // as parent_task_id. Ensure it executes after new_task_id has written
764 // post-backdrop-capture content (e.g. backdrop-filter children) to
765 // that texture, otherwise those primitives will be missing from the
766 // filter output.
767 if let Some(root_task_id) = *parent_root_task_id {
768 rg_builder.add_dependency(
769 root_task_id,
770 new_task_id,
771 );
772 }
773
774 // Update the surface builder with the now current target for future primitives
775 *parent_task_id = new_task_id;
776 }
777 CommandBufferBuilderKind::Invalid => {
778 unreachable!();
779 }
780 }
781
782 let dest_task = rg_builder.get_task_mut(resolve_task_id);
783
784 match dest_task.kind {
785 RenderTaskKind::Picture(ref mut dest_task_info) => {
786 assert!(dest_task_info.resolve_op.is_none());
787 dest_task_info.resolve_op = Some(ResolveOp {
788 src_task_ids,
789 dest_task_id: resolve_task_id,
790 })
791 }
792 _ => {
793 unreachable!("bug: not a picture");
794 }
795 }
796 }
797
798 // This can occur if there is an edge case where the resolve target is found
799 // not visible even though the filter chain was (for example, in the case of
800 // an extreme scale causing floating point inaccuracies). Adding a dependency
801 // here is also a safety in case for some reason the backdrop render primitive
802 // doesn't pick up the dependency, ensuring that it gets scheduled and freed
803 // as early as possible.
804 match self.builder_stack.last().unwrap().kind {
805 CommandBufferBuilderKind::Tiled { ref tiles } => {
806 // For a tiled render task, add as a dependency to every tile.
807 for (_, descriptor) in tiles {
808 rg_builder.add_dependency(
809 descriptor.current_task_id,
810 child_root_task_id.unwrap_or(child_render_task_id),
811 );
812 }
813 }
814 CommandBufferBuilderKind::Simple { render_task_id: parent_task_id, .. } => {
815 rg_builder.add_dependency(
816 parent_task_id,
817 child_root_task_id.unwrap_or(child_render_task_id),
818 );
819 }
820 CommandBufferBuilderKind::Invalid => {
821 unreachable!();
822 }
823 }
824 }
825 }
826 } else {
827 match builder.kind {
828 CommandBufferBuilderKind::Tiled { ref tiles } => {
829 for (_, descriptor) in tiles {
830 if let Some(composite_task_id) = descriptor.composite_task_id {
831 rg_builder.add_dependency(
832 composite_task_id,
833 descriptor.current_task_id,
834 );
835
836 let composite_task = rg_builder.get_task_mut(composite_task_id);
837 match composite_task.kind {
838 RenderTaskKind::TileComposite(ref mut info) => {
839 info.task_id = Some(descriptor.current_task_id);
840 }
841 _ => unreachable!("bug: not a tile composite"),
842 }
843 }
844 }
845 }
846 CommandBufferBuilderKind::Simple { render_task_id: child_task_id, root_task_id: child_root_task_id, .. } => {
847 match self.builder_stack.last().unwrap().kind {
848 CommandBufferBuilderKind::Tiled { ref tiles } => {
849 // For a tiled render task, add as a dependency to every tile.
850 for (_, descriptor) in tiles {
851 rg_builder.add_dependency(
852 descriptor.current_task_id,
853 child_root_task_id.unwrap_or(child_task_id),
854 );
855 }
856 }
857 CommandBufferBuilderKind::Simple { render_task_id: parent_task_id, .. } => {
858 rg_builder.add_dependency(
859 parent_task_id,
860 child_root_task_id.unwrap_or(child_task_id),
861 );
862 }
863 CommandBufferBuilderKind::Invalid => {
864 }
865 }
866 }
867 CommandBufferBuilderKind::Invalid => {
868 }
869 }
870 }
871
872 // Step through the dependencies for this builder and add them to the finalized
873 // render task root(s) for this surface
874 match builder.kind {
875 CommandBufferBuilderKind::Tiled { ref tiles } => {
876 for (_, descriptor) in tiles {
877 for task_id in &builder.extra_dependencies {
878 rg_builder.add_dependency(
879 descriptor.current_task_id,
880 *task_id,
881 );
882 }
883 }
884 }
885 CommandBufferBuilderKind::Simple { render_task_id, .. } => {
886 for task_id in &builder.extra_dependencies {
887 rg_builder.add_dependency(
888 render_task_id,
889 *task_id,
890 );
891 }
892 }
893 CommandBufferBuilderKind::Invalid { .. } => {}
894 }
895
896 // Set up the cmd-buffer targets to write prims into the popped surface
897 self.current_cmd_buffers.init(
898 self.builder_stack.last().unwrap_or(&CommandBufferBuilder::empty()), rg_builder
899 );
900 }
901
902 pub fn finalize(self) {
903 assert!(self.builder_stack.is_empty());
904 }
905}
906
907
908pub fn calculate_screen_uv(
909 p: DevicePoint,
910 clipped: DeviceRect,
911) -> DeviceHomogeneousVector {
912 // TODO(gw): Switch to a simple mix, no bilerp / homogeneous vec needed anymore
913 DeviceHomogeneousVector::new(
914 (p.x - clipped.min.x) / (clipped.max.x - clipped.min.x),
915 (p.y - clipped.min.y) / (clipped.max.y - clipped.min.y),
916 0.0,
917 1.0,
918 )
919}