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
5use api::units::*;
6use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, CommandBufferBuilder, CommandBufferIndex};
7use crate::internal_types::FastHashMap;
8use crate::picture::{SurfaceInfo, SurfaceIndex, TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES};
9use crate::prim_store::PictureIndex;
10use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
11use crate::render_target::ResolveOp;
12use crate::render_task::{RenderTask, RenderTaskKind, RenderTaskLocation};
13use crate::visibility::{VisibilityState, PrimitiveVisibility};
14
15/*
16 Contains functionality to help building the render task graph from a series of off-screen
17 surfaces that are created during the prepare pass. For now, it maintains existing behavior.
18 A future patch will add support for surface sub-graphs, while ensuring the render task
19 graph itself is built correctly with dependencies regardless of the surface kind (chained,
20 tiled, simple).
21 */
22
23// Information about the render task(s) for a given tile
24#[cfg_attr(feature = "capture", derive(Serialize))]
25#[cfg_attr(feature = "replay", derive(Deserialize))]
26pub struct SurfaceTileDescriptor {
27    /// Target render task for commands added to this tile. This is changed
28    /// each time a sub-graph is encountered on this tile
29    pub current_task_id: RenderTaskId,
30    /// The compositing task for this tile, if required. This is only needed
31    /// when a tile contains one or more sub-graphs.
32    pub composite_task_id: Option<RenderTaskId>,
33    /// Dirty rect for this tile
34    pub dirty_rect: PictureRect,
35}
36
37// Details of how a surface is rendered
38pub enum SurfaceDescriptorKind {
39    // Picture cache tiles
40    Tiled {
41        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
42    },
43    // A single surface (e.g. for an opacity filter)
44    Simple {
45        render_task_id: RenderTaskId,
46        dirty_rect: PictureRect,
47    },
48    // A surface with 1+ intermediate tasks (e.g. blur)
49    Chained {
50        render_task_id: RenderTaskId,
51        root_task_id: RenderTaskId,
52        dirty_rect: PictureRect,
53    },
54}
55
56// Describes how a surface is rendered
57pub struct SurfaceDescriptor {
58    kind: SurfaceDescriptorKind,
59}
60
61impl SurfaceDescriptor {
62    // Create a picture cache tiled surface
63    pub fn new_tiled(
64        tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
65    ) -> Self {
66        SurfaceDescriptor {
67            kind: SurfaceDescriptorKind::Tiled {
68                tiles,
69            },
70        }
71    }
72
73    // Create a chained surface (e.g. blur)
74    pub fn new_chained(
75        render_task_id: RenderTaskId,
76        root_task_id: RenderTaskId,
77        dirty_rect: PictureRect,
78    ) -> Self {
79        SurfaceDescriptor {
80            kind: SurfaceDescriptorKind::Chained {
81                render_task_id,
82                root_task_id,
83                dirty_rect,
84            },
85        }
86    }
87
88    // Create a simple surface (e.g. opacity)
89    pub fn new_simple(
90        render_task_id: RenderTaskId,
91        dirty_rect: PictureRect,
92    ) -> Self {
93        SurfaceDescriptor {
94            kind: SurfaceDescriptorKind::Simple {
95                render_task_id,
96                dirty_rect,
97            },
98        }
99    }
100}
101
102// Describes a list of command buffers that we are adding primitives to
103// for a given surface. These are created from a command buffer builder
104// as an optimization - skipping the indirection pic_task -> cmd_buffer_index
105struct CommandBufferTargets {
106    available_cmd_buffers: Vec<Vec<(PictureRect, CommandBufferIndex)>>,
107}
108
109impl CommandBufferTargets {
110    fn new() -> Self {
111        CommandBufferTargets {
112            available_cmd_buffers: vec![Vec::new(); MAX_COMPOSITOR_SURFACES+1],
113        }
114    }
115
116    fn init(
117        &mut self,
118        cb: &CommandBufferBuilder,
119        rg_builder: &RenderTaskGraphBuilder,
120    ) {
121        for available_cmd_buffers in &mut self.available_cmd_buffers {
122            available_cmd_buffers.clear();
123        }
124
125        match cb.kind {
126            CommandBufferBuilderKind::Tiled { ref tiles, .. } => {
127                for (key, desc) in tiles {
128                    let task = rg_builder.get_task(desc.current_task_id);
129                    match task.kind {
130                        RenderTaskKind::Picture(ref info) => {
131                            let available_cmd_buffers = &mut self.available_cmd_buffers[key.sub_slice_index.as_usize()];
132                            available_cmd_buffers.push((desc.dirty_rect, info.cmd_buffer_index));
133                        }
134                        _ => unreachable!("bug: not a picture"),
135                    }
136                }
137            }
138            CommandBufferBuilderKind::Simple { render_task_id, dirty_rect, .. } => {
139                let task = rg_builder.get_task(render_task_id);
140                match task.kind {
141                    RenderTaskKind::Picture(ref info) => {
142                        for sub_slice_buffer in &mut self.available_cmd_buffers {
143                            sub_slice_buffer.push((dirty_rect, info.cmd_buffer_index));
144                        }
145                    }
146                    _ => unreachable!("bug: not a picture"),
147                }
148            }
149            CommandBufferBuilderKind::Invalid => {}
150        };
151    }
152
153    /// For a given rect and sub-slice, get a list of command buffers to write commands to
154    fn get_cmd_buffer_targets_for_rect(
155        &mut self,
156        rect: &PictureRect,
157        sub_slice_index: SubSliceIndex,
158        targets: &mut Vec<CommandBufferIndex>,
159    ) -> bool {
160
161        for (dirty_rect, cmd_buffer_index) in &self.available_cmd_buffers[sub_slice_index.as_usize()] {
162            if dirty_rect.intersects(rect) {
163                targets.push(*cmd_buffer_index);
164            }
165        }
166
167        !targets.is_empty()
168    }
169}
170
171// Main helper interface to build a graph of surfaces. In future patches this
172// will support building sub-graphs.
173pub struct SurfaceBuilder {
174    // The currently set cmd buffer targets (updated during push/pop)
175    current_cmd_buffers: CommandBufferTargets,
176    // Stack of surfaces that are parents to the current targets
177    builder_stack: Vec<CommandBufferBuilder>,
178    // A map of the output render tasks from any sub-graphs that haven't
179    // been consumed by BackdropRender prims yet
180    pub sub_graph_output_map: FastHashMap<PictureIndex, RenderTaskId>,
181}
182
183impl SurfaceBuilder {
184    pub fn new() -> Self {
185        SurfaceBuilder {
186            current_cmd_buffers: CommandBufferTargets::new(),
187            builder_stack: Vec::new(),
188            sub_graph_output_map: FastHashMap::default(),
189        }
190    }
191
192    /// Register the current surface as the source of a resolve for the task sub-graph that
193    /// is currently on the surface builder stack.
194    pub fn register_resolve_source(
195        &mut self,
196    ) {
197        let surface_task_id = match self.builder_stack.last().unwrap().kind {
198            CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
199                panic!("bug: only supported for non-tiled surfaces");
200            }
201            CommandBufferBuilderKind::Simple { render_task_id, .. } => render_task_id,
202        };
203
204        for builder in self.builder_stack.iter_mut().rev() {
205            if builder.establishes_sub_graph {
206                assert_eq!(builder.resolve_source, None);
207                builder.resolve_source = Some(surface_task_id);
208                return;
209            }
210        }
211
212        unreachable!("bug: resolve source with no sub-graph");
213    }
214
215    pub fn push_surface(
216        &mut self,
217        surface_index: SurfaceIndex,
218        is_sub_graph: bool,
219        clipping_rect: PictureRect,
220        descriptor: Option<SurfaceDescriptor>,
221        surfaces: &mut [SurfaceInfo],
222        rg_builder: &RenderTaskGraphBuilder,
223    ) {
224        // Init the surface
225        surfaces[surface_index.0].clipping_rect = clipping_rect;
226
227        let builder = if let Some(descriptor) = descriptor {
228            match descriptor.kind {
229                SurfaceDescriptorKind::Tiled { tiles } => {
230                    CommandBufferBuilder::new_tiled(
231                        tiles,
232                    )
233                }
234                SurfaceDescriptorKind::Simple { render_task_id, dirty_rect, .. } => {
235                    CommandBufferBuilder::new_simple(
236                        render_task_id,
237                        is_sub_graph,
238                        None,
239                        dirty_rect,
240                    )
241                }
242                SurfaceDescriptorKind::Chained { render_task_id, root_task_id, dirty_rect, .. } => {
243                    CommandBufferBuilder::new_simple(
244                        render_task_id,
245                        is_sub_graph,
246                        Some(root_task_id),
247                        dirty_rect,
248                    )
249                }
250            }
251        } else {
252            CommandBufferBuilder::empty()
253        };
254
255        self.current_cmd_buffers.init(&builder, rg_builder);
256        self.builder_stack.push(builder);
257    }
258
259    // Add a child render task (e.g. a render task cache item, or a clip mask) as a
260    // dependency of the current surface
261    pub fn add_child_render_task(
262        &mut self,
263        child_task_id: RenderTaskId,
264        rg_builder: &mut RenderTaskGraphBuilder,
265    ) {
266        let builder = self.builder_stack.last().unwrap();
267
268        match builder.kind {
269            CommandBufferBuilderKind::Tiled { ref tiles } => {
270                for (_, descriptor) in tiles {
271                    rg_builder.add_dependency(
272                        descriptor.current_task_id,
273                        child_task_id,
274                    );
275                }
276            }
277            CommandBufferBuilderKind::Simple { render_task_id, .. } => {
278                rg_builder.add_dependency(
279                    render_task_id,
280                    child_task_id,
281                );
282            }
283            CommandBufferBuilderKind::Invalid { .. } => {}
284        }
285    }
286
287    // Add a picture render task as a dependency of the parent surface. This is a
288    // special case with extra complexity as the root of the surface may change
289    // when inside a sub-graph. It's currently only needed for drop-shadow effects.
290    pub fn add_picture_render_task(
291        &mut self,
292        child_task_id: RenderTaskId,
293    ) {
294        self.builder_stack
295            .last_mut()
296            .unwrap()
297            .extra_dependencies
298            .push(child_task_id);
299    }
300
301    // Get a list of command buffer indices that primitives should be pushed
302    // to for a given current visbility / dirty state
303    pub fn get_cmd_buffer_targets_for_prim(
304        &mut self,
305        vis: &PrimitiveVisibility,
306        targets: &mut Vec<CommandBufferIndex>,
307    ) -> bool {
308        targets.clear();
309
310        match vis.state {
311            VisibilityState::Unset => {
312                panic!("bug: invalid vis state");
313            }
314            VisibilityState::Culled => {
315                false
316            }
317            VisibilityState::Visible { sub_slice_index, .. } => {
318                self.current_cmd_buffers.get_cmd_buffer_targets_for_rect(
319                    &vis.clip_chain.pic_coverage_rect,
320                    sub_slice_index,
321                    targets,
322                )
323            }
324            VisibilityState::PassThrough => {
325                true
326            }
327        }
328    }
329
330    pub fn pop_empty_surface(&mut self) {
331        let builder = self.builder_stack.pop().unwrap();
332        assert!(!builder.establishes_sub_graph);
333    }
334
335    // Finish adding primitives and child tasks to a surface and pop it off the stack
336    pub fn pop_surface(
337        &mut self,
338        pic_index: PictureIndex,
339        rg_builder: &mut RenderTaskGraphBuilder,
340        cmd_buffers: &mut CommandBufferList,
341    ) {
342        let builder = self.builder_stack.pop().unwrap();
343
344        if builder.establishes_sub_graph {
345            // If we are popping a sub-graph off the stack the dependency setup is rather more complex...
346            match builder.kind {
347                CommandBufferBuilderKind::Tiled { .. } | CommandBufferBuilderKind::Invalid => {
348                    unreachable!("bug: sub-graphs can only be simple surfaces");
349                }
350                CommandBufferBuilderKind::Simple { render_task_id: child_render_task_id, root_task_id: child_root_task_id, .. } => {
351                    // Get info about the resolve operation to copy from parent surface or tiles to the picture cache task
352                    if let Some(resolve_task_id) = builder.resolve_source {
353                        let mut src_task_ids = Vec::new();
354
355                        // Make the output of the sub-graph a dependency of the new replacement tile task
356                        let _old = self.sub_graph_output_map.insert(
357                            pic_index,
358                            child_root_task_id.unwrap_or(child_render_task_id),
359                        );
360                        debug_assert!(_old.is_none());
361
362                        // Set up dependencies for the sub-graph. The basic concepts below are the same, but for
363                        // tiled surfaces are a little more complex as there are multiple tasks to set up.
364                        //  (a) Set up new task(s) on parent surface that write to the same location
365                        //  (b) Set up a resolve target to copy from parent surface tasks(s) to the resolve target
366                        //  (c) Make the old parent surface tasks input dependencies of the resolve target
367                        //  (d) Make the sub-graph output an input dependency of the new task(s).
368
369                        match self.builder_stack.last_mut().unwrap().kind {
370                            CommandBufferBuilderKind::Tiled { ref mut tiles } => {
371                                let keys: Vec<TileKey> = tiles.keys().cloned().collect();
372
373                                // For each tile in parent surface
374                                for key in keys {
375                                    let descriptor = tiles.remove(&key).unwrap();
376                                    let parent_task_id = descriptor.current_task_id;
377                                    let parent_task = rg_builder.get_task_mut(parent_task_id);
378
379                                    match parent_task.location {
380                                        RenderTaskLocation::Unallocated { .. } | RenderTaskLocation::Existing { .. } => {
381                                            // Get info about the parent tile task location and params
382                                            let location = RenderTaskLocation::Existing {
383                                                parent_task_id,
384                                                size: parent_task.location.size(),
385                                            };
386
387                                            let pic_task = match parent_task.kind {
388                                                RenderTaskKind::Picture(ref mut pic_task) => {
389                                                    let cmd_buffer_index = cmd_buffers.create_cmd_buffer();
390                                                    let new_pic_task = pic_task.duplicate(cmd_buffer_index);
391
392                                                    // Add the resolve src to copy from tile -> picture input task
393                                                    src_task_ids.push(parent_task_id);
394
395                                                    new_pic_task
396                                                }
397                                                _ => panic!("bug: not a picture"),
398                                            };
399
400                                            // Make the existing tile an input dependency of the resolve target
401                                            rg_builder.add_dependency(
402                                                resolve_task_id,
403                                                parent_task_id,
404                                            );
405
406                                            // Create the new task to replace the tile task
407                                            let new_task_id = rg_builder.add().init(
408                                                RenderTask::new(
409                                                    location,          // draw to same place
410                                                    RenderTaskKind::Picture(pic_task),
411                                                ),
412                                            );
413
414                                            // Ensure that the parent task will get scheduled earlier during
415                                            // pass assignment since we are reusing the existing surface,
416                                            // even though it's not technically needed for rendering order.
417                                            rg_builder.add_dependency(
418                                                new_task_id,
419                                                parent_task_id,
420                                            );
421
422                                            // Update the surface builder with the now current target for future primitives
423                                            tiles.insert(
424                                                key,
425                                                SurfaceTileDescriptor {
426                                                    current_task_id: new_task_id,
427                                                    ..descriptor
428                                                },
429                                            );
430                                        }
431                                        RenderTaskLocation::Static { .. } => {
432                                            // Update the surface builder with the now current target for future primitives
433                                            tiles.insert(
434                                                key,
435                                                descriptor,
436                                            );
437                                        }
438                                        _ => {
439                                            panic!("bug: unexpected task location");
440                                        }
441                                    }
442                                }
443                            }
444                            CommandBufferBuilderKind::Simple { render_task_id: ref mut parent_task_id, .. } => {
445                                let parent_task = rg_builder.get_task_mut(*parent_task_id);
446
447                                // Get info about the parent tile task location and params
448                                let location = RenderTaskLocation::Existing {
449                                    parent_task_id: *parent_task_id,
450                                    size: parent_task.location.size(),
451                                };
452                                let pic_task = match parent_task.kind {
453                                    RenderTaskKind::Picture(ref mut pic_task) => {
454                                        let cmd_buffer_index = cmd_buffers.create_cmd_buffer();
455
456                                        let new_pic_task = pic_task.duplicate(cmd_buffer_index);
457
458                                        // Add the resolve src to copy from tile -> picture input task
459                                        src_task_ids.push(*parent_task_id);
460
461                                        new_pic_task
462                                    }
463                                    _ => panic!("bug: not a picture"),
464                                };
465
466                                // Make the existing surface an input dependency of the resolve target
467                                rg_builder.add_dependency(
468                                    resolve_task_id,
469                                    *parent_task_id,
470                                );
471
472                                // Create the new task to replace the parent surface task
473                                let new_task_id = rg_builder.add().init(
474                                    RenderTask::new(
475                                        location,          // draw to same place
476                                        RenderTaskKind::Picture(pic_task),
477                                    ),
478                                );
479
480                                // Ensure that the parent task will get scheduled earlier during
481                                // pass assignment since we are reusing the existing surface,
482                                // even though it's not technically needed for rendering order.
483                                rg_builder.add_dependency(
484                                    new_task_id,
485                                    *parent_task_id,
486                                );
487
488                                // Update the surface builder with the now current target for future primitives
489                                *parent_task_id = new_task_id;
490                            }
491                            CommandBufferBuilderKind::Invalid => {
492                                unreachable!();
493                            }
494                        }
495
496                        let dest_task = rg_builder.get_task_mut(resolve_task_id);
497
498                        match dest_task.kind {
499                            RenderTaskKind::Picture(ref mut dest_task_info) => {
500                                assert!(dest_task_info.resolve_op.is_none());
501                                dest_task_info.resolve_op = Some(ResolveOp {
502                                    src_task_ids,
503                                    dest_task_id: resolve_task_id,
504                                })
505                            }
506                            _ => {
507                                unreachable!("bug: not a picture");
508                            }
509                        }
510                    }
511
512                    // This can occur if there is an edge case where the resolve target is found
513                    // not visible even though the filter chain was (for example, in the case of
514                    // an extreme scale causing floating point inaccuracies). Adding a dependency
515                    // here is also a safety in case for some reason the backdrop render primitive
516                    // doesn't pick up the dependency, ensuring that it gets scheduled and freed
517                    // as early as possible.
518                    match self.builder_stack.last().unwrap().kind {
519                        CommandBufferBuilderKind::Tiled { ref tiles } => {
520                            // For a tiled render task, add as a dependency to every tile.
521                            for (_, descriptor) in tiles {
522                                rg_builder.add_dependency(
523                                    descriptor.current_task_id,
524                                    child_root_task_id.unwrap_or(child_render_task_id),
525                                );
526                            }
527                        }
528                        CommandBufferBuilderKind::Simple { render_task_id: parent_task_id, .. } => {
529                            rg_builder.add_dependency(
530                                parent_task_id,
531                                child_root_task_id.unwrap_or(child_render_task_id),
532                            );
533                        }
534                        CommandBufferBuilderKind::Invalid => {
535                            unreachable!();
536                        }
537                    }
538                }
539            }
540        } else {
541            match builder.kind {
542                CommandBufferBuilderKind::Tiled { ref tiles } => {
543                    for (_, descriptor) in tiles {
544                        if let Some(composite_task_id) = descriptor.composite_task_id {
545                            rg_builder.add_dependency(
546                                composite_task_id,
547                                descriptor.current_task_id,
548                            );
549
550                            let composite_task = rg_builder.get_task_mut(composite_task_id);
551                            match composite_task.kind {
552                                RenderTaskKind::TileComposite(ref mut info) => {
553                                    info.task_id = Some(descriptor.current_task_id);
554                                }
555                                _ => unreachable!("bug: not a tile composite"),
556                            }
557                        }
558                    }
559                }
560                CommandBufferBuilderKind::Simple { render_task_id: child_task_id, root_task_id: child_root_task_id, .. } => {
561                    match self.builder_stack.last().unwrap().kind {
562                        CommandBufferBuilderKind::Tiled { ref tiles } => {
563                            // For a tiled render task, add as a dependency to every tile.
564                            for (_, descriptor) in tiles {
565                                rg_builder.add_dependency(
566                                    descriptor.current_task_id,
567                                    child_root_task_id.unwrap_or(child_task_id),
568                                );
569                            }
570                        }
571                        CommandBufferBuilderKind::Simple { render_task_id: parent_task_id, .. } => {
572                            rg_builder.add_dependency(
573                                parent_task_id,
574                                child_root_task_id.unwrap_or(child_task_id),
575                            );
576                        }
577                        CommandBufferBuilderKind::Invalid => {
578                        }
579                    }
580                }
581                CommandBufferBuilderKind::Invalid => {
582                }
583            }
584        }
585
586        // Step through the dependencies for this builder and add them to the finalized
587        // render task root(s) for this surface
588        match builder.kind {
589            CommandBufferBuilderKind::Tiled { ref tiles } => {
590                for (_, descriptor) in tiles {
591                    for task_id in &builder.extra_dependencies {
592                        rg_builder.add_dependency(
593                            descriptor.current_task_id,
594                            *task_id,
595                        );
596                    }
597                }
598            }
599            CommandBufferBuilderKind::Simple { render_task_id, .. } => {
600                for task_id in &builder.extra_dependencies {
601                    rg_builder.add_dependency(
602                        render_task_id,
603                        *task_id,
604                    );
605                }
606            }
607            CommandBufferBuilderKind::Invalid { .. } => {}
608        }
609
610        // Set up the cmd-buffer targets to write prims into the popped surface
611        self.current_cmd_buffers.init(
612            self.builder_stack.last().unwrap_or(&CommandBufferBuilder::empty()), rg_builder
613        );
614    }
615
616    pub fn finalize(self) {
617        assert!(self.builder_stack.is_empty());
618    }
619}