Skip to main content

webrender/
picture_composite_mode.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::{ColorF, ColorU, PremultipliedColorF, PropertyBinding, PropertyBindingId, SnapshotInfo};
6use api::units::*;
7use crate::prim_store::image::AdjustedImageSource;
8use crate::{render_task_graph::RenderTaskGraphBuilder, renderer::GpuBufferBuilderF};
9use crate::box_shadow::BLUR_SAMPLE_SCALE;
10use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
11use crate::gpu_types::{BlurEdgeMode, BrushSegmentGpuData, ImageBrushPrimitiveData, UvRectKind};
12use crate::intern::ItemUid;
13use crate::render_backend::DataStores;
14use crate::render_task_graph::RenderTaskId;
15use crate::render_target::RenderTargetKind;
16use crate::render_task::{BlurTask, RenderTask, BlurTaskCache};
17use crate::render_task::RenderTaskKind;
18use crate::renderer::{BlendMode, GpuBufferAddress, GpuBufferBuilder};
19use crate::space::SpaceMapper;
20use crate::spatial_tree::SpatialTree;
21use crate::surface::{SurfaceDescriptor, SurfaceInfo, calculate_screen_uv};
22use crate::surface::SurfaceIndex;
23use crate::svg_filter::{get_coverage_source_svgfe, FilterGraphNodeKey, FilterGraphOpKey};
24use crate::util::MaxRect;
25use smallvec::SmallVec;
26use crate::internal_types::Filter;
27use crate::profiler;
28use core::time::Duration;
29use euclid::Scale;
30use api::MixBlendMode;
31use crate::filterdata::FilterDataHandle;
32use crate::tile_cache::SliceId;
33use crate::svg_filter::{FilterGraphNode, FilterGraphOp, get_coverage_target_svgfe};
34use crate::picture::BlitReason;
35use crate::prim_store::VectorKey;
36#[cfg(feature = "capture")]
37use serde::Serialize;
38
39/// Specifies how this Picture should be composited
40/// onto the target it belongs to.
41#[allow(dead_code)]
42#[derive(Debug, Clone)]
43#[cfg_attr(feature = "capture", derive(Serialize))]
44pub enum PictureCompositeMode {
45    /// Apply CSS mix-blend-mode effect.
46    MixBlend(MixBlendMode),
47    /// Apply a CSS filter (except component transfer).
48    Filter(Filter),
49    /// Apply a component transfer filter.
50    ComponentTransferFilter(FilterDataHandle),
51    /// Draw to intermediate surface, copy straight across. This
52    /// is used for CSS isolation, and plane splitting.
53    Blit(BlitReason),
54    /// Used to cache a picture as a series of tiles.
55    TileCache {
56        slice_id: SliceId,
57    },
58    /// Apply an SVG filter graph
59    SVGFEGraph(Vec<(FilterGraphNode, FilterGraphOp)>),
60    /// A surface that is used as an input to another primitive
61    IntermediateSurface,
62}
63
64impl PictureCompositeMode {
65    pub fn get_rect(
66        &self,
67        surface: &SurfaceInfo,
68        sub_rect: Option<LayoutRect>,
69    ) -> LayoutRect {
70        let surface_rect = match sub_rect {
71            Some(sub_rect) => sub_rect,
72            None => surface.clipped_local_rect.cast_unit(),
73        };
74
75        match self {
76            PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
77                if *should_inflate {
78                    let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
79
80                    surface_rect.inflate(
81                        width_factor.ceil() * BLUR_SAMPLE_SCALE,
82                        height_factor.ceil() * BLUR_SAMPLE_SCALE,
83                    )
84                } else {
85                    surface_rect
86                }
87            }
88            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
89                let mut max_blur_radius = 0.0;
90                for shadow in shadows {
91                    max_blur_radius = f32::max(max_blur_radius, shadow.blur_radius);
92                }
93
94                let (max_blur_radius_x, max_blur_radius_y) = surface.clamp_blur_radius(
95                    max_blur_radius,
96                    max_blur_radius,
97                );
98                let blur_inflation_x = max_blur_radius_x * BLUR_SAMPLE_SCALE;
99                let blur_inflation_y = max_blur_radius_y * BLUR_SAMPLE_SCALE;
100
101                surface_rect.inflate(blur_inflation_x, blur_inflation_y)
102            }
103            PictureCompositeMode::SVGFEGraph(ref filters) => {
104                // Return prim_subregion for use in get_local_prim_rect, which
105                // is the polygon size.
106                // This must match surface_rects.unclipped_local.
107                get_coverage_target_svgfe(filters, surface_rect.cast_unit())
108            }
109            _ => {
110                surface_rect
111            }
112        }
113    }
114
115    pub fn get_coverage(
116        &self,
117        surface: &SurfaceInfo,
118        sub_rect: Option<LayoutRect>,
119    ) -> LayoutRect {
120        let surface_rect = match sub_rect {
121            Some(sub_rect) => sub_rect,
122            None => surface.clipped_local_rect.cast_unit(),
123        };
124
125        match self {
126            PictureCompositeMode::Filter(Filter::Blur { width, height, should_inflate, .. }) => {
127                if *should_inflate {
128                    let (width_factor, height_factor) = surface.clamp_blur_radius(*width, *height);
129
130                    surface_rect.inflate(
131                        width_factor.ceil() * BLUR_SAMPLE_SCALE,
132                        height_factor.ceil() * BLUR_SAMPLE_SCALE,
133                    )
134                } else {
135                    surface_rect
136                }
137            }
138            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
139                let mut rect = surface_rect;
140
141                for shadow in shadows {
142                    let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
143                        shadow.blur_radius,
144                        shadow.blur_radius,
145                    );
146                    let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
147                    let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
148
149                    let shadow_rect = surface_rect
150                        .translate(shadow.offset)
151                        .inflate(blur_inflation_x, blur_inflation_y);
152                    rect = rect.union(&shadow_rect);
153                }
154
155                rect
156            }
157            PictureCompositeMode::SVGFEGraph(ref filters) => {
158                // surface_rect may be for source or target, so invalidate based
159                // on both interpretations
160                let target_subregion = get_coverage_source_svgfe(filters, surface_rect.cast());
161                let source_subregion = get_coverage_target_svgfe(filters, surface_rect.cast());
162                target_subregion.union(&source_subregion)
163            }
164            _ => {
165                surface_rect
166            }
167        }
168    }
169
170    pub fn write_gpu_blocks(
171        &self,
172        surface: &SurfaceInfo,
173        gpu_buffers: &mut GpuBufferBuilder,
174        data_stores: &mut DataStores,
175        extra_gpu_data: &mut SmallVec<[GpuBufferAddress; 1]>,
176    ) {
177        // TODO(gw): Almost all of the composite modes below use extra_gpu_data
178        //           to store the same type of data. The exception is the filter
179        //           with a ColorMatrix, which stores the color matrix here. It's
180        //           probably worth tidying this code up to be a bit more consistent.
181        //           Perhaps store the color matrix after the common data, even though
182        //           it's not used by that shader.
183
184        match *self {
185            PictureCompositeMode::TileCache { .. } => {}
186            PictureCompositeMode::Filter(Filter::Blur { .. }) => {}
187            PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
188                extra_gpu_data.resize(shadows.len(), GpuBufferAddress::INVALID);
189                for (shadow, extra_handle) in shadows.iter().zip(extra_gpu_data.iter_mut()) {
190                    let mut writer = gpu_buffers.f32.write_blocks(5);
191                    let prim_rect = surface.clipped_local_rect.cast_unit();
192
193                    // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
194                    //  [brush specific data]
195                    //  [segment_rect, segment data]
196                    let (blur_inflation_x, blur_inflation_y) = surface.clamp_blur_radius(
197                        shadow.blur_radius,
198                        shadow.blur_radius,
199                    );
200
201                    let shadow_rect = prim_rect.inflate(
202                        blur_inflation_x * BLUR_SAMPLE_SCALE,
203                        blur_inflation_y * BLUR_SAMPLE_SCALE,
204                    ).translate(shadow.offset);
205
206                    // ImageBrush colors
207                    writer.push(&ImageBrushPrimitiveData {
208                        color: shadow.color.premultiplied(),
209                        background_color: PremultipliedColorF::WHITE,
210                        stretch_size: shadow_rect.size(),
211                    });
212
213                    writer.push(&BrushSegmentGpuData {
214                        local_rect: shadow_rect,
215                        extra_data: [0.0; 4],
216                    });
217
218                    *extra_handle = writer.finish();
219                }
220            }
221            PictureCompositeMode::Filter(ref filter) => {
222                match *filter {
223                    Filter::ColorMatrix(ref m) => {
224                        if extra_gpu_data.is_empty() {
225                            extra_gpu_data.push(GpuBufferAddress::INVALID);
226                        }
227                        let mut writer = gpu_buffers.f32.write_blocks(5);
228                        for i in 0..5 {
229                            writer.push_one([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
230                        }
231                        extra_gpu_data[0] = writer.finish();
232                    }
233                    Filter::Flood(ref color) => {
234                        if extra_gpu_data.is_empty() {
235                            extra_gpu_data.push(GpuBufferAddress::INVALID);
236                        }
237                        let mut writer = gpu_buffers.f32.write_blocks(1);
238                        writer.push_one(color.to_array());
239                        extra_gpu_data[0] = writer.finish();
240                    }
241                    _ => {}
242                }
243            }
244            PictureCompositeMode::ComponentTransferFilter(handle) => {
245                let filter_data = &mut data_stores.filter_data[handle];
246                filter_data.write_gpu_blocks(&mut gpu_buffers.f32);
247            }
248            PictureCompositeMode::MixBlend(..) |
249            PictureCompositeMode::Blit(_) |
250            PictureCompositeMode::IntermediateSurface => {}
251            PictureCompositeMode::SVGFEGraph(ref filters) => {
252                // Update interned filter data
253                for (_node, op) in filters {
254                    match op {
255                        FilterGraphOp::SVGFEComponentTransferInterned { handle, creates_pixels: _ } => {
256                            let filter_data = &mut data_stores.filter_data[*handle];
257                            filter_data.write_gpu_blocks(&mut gpu_buffers.f32);
258                        }
259                        _ => {}
260                    }
261                }
262            }
263        }
264    }
265
266    /// Returns a static str describing the type of PictureCompositeMode (and
267    /// filter type if applicable)
268    pub fn kind(&self) -> &'static str {
269        match *self {
270            PictureCompositeMode::Blit(..) => "Blit",
271            PictureCompositeMode::ComponentTransferFilter(..) => "ComponentTransferFilter",
272            PictureCompositeMode::IntermediateSurface => "IntermediateSurface",
273            PictureCompositeMode::MixBlend(..) => "MixBlend",
274            PictureCompositeMode::SVGFEGraph(..) => "SVGFEGraph",
275            PictureCompositeMode::TileCache{..} => "TileCache",
276            PictureCompositeMode::Filter(Filter::Blur{..}) => "Filter::Blur",
277            PictureCompositeMode::Filter(Filter::Brightness(..)) => "Filter::Brightness",
278            PictureCompositeMode::Filter(Filter::ColorMatrix(..)) => "Filter::ColorMatrix",
279            PictureCompositeMode::Filter(Filter::ComponentTransfer) => "Filter::ComponentTransfer",
280            PictureCompositeMode::Filter(Filter::Contrast(..)) => "Filter::Contrast",
281            PictureCompositeMode::Filter(Filter::DropShadows(..)) => "Filter::DropShadows",
282            PictureCompositeMode::Filter(Filter::Flood(..)) => "Filter::Flood",
283            PictureCompositeMode::Filter(Filter::Grayscale(..)) => "Filter::Grayscale",
284            PictureCompositeMode::Filter(Filter::HueRotate(..)) => "Filter::HueRotate",
285            PictureCompositeMode::Filter(Filter::Identity) => "Filter::Identity",
286            PictureCompositeMode::Filter(Filter::Invert(..)) => "Filter::Invert",
287            PictureCompositeMode::Filter(Filter::LinearToSrgb) => "Filter::LinearToSrgb",
288            PictureCompositeMode::Filter(Filter::Opacity(..)) => "Filter::Opacity",
289            PictureCompositeMode::Filter(Filter::Saturate(..)) => "Filter::Saturate",
290            PictureCompositeMode::Filter(Filter::Sepia(..)) => "Filter::Sepia",
291            PictureCompositeMode::Filter(Filter::SrgbToLinear) => "Filter::SrgbToLinear",
292            PictureCompositeMode::Filter(Filter::SVGGraphNode(..)) => "Filter::SVGGraphNode",
293        }
294    }
295}
296
297pub fn prepare_composite_mode(
298    composite_mode: &PictureCompositeMode,
299    surface_index: SurfaceIndex,
300    parent_surface_index: SurfaceIndex,
301    surface_rects: &SurfaceAllocInfo,
302    snapshot: &Option<SnapshotInfo>,
303    can_use_shared_surface: bool,
304    frame_context: &FrameBuildingContext,
305    frame_state: &mut FrameBuildingState,
306    data_stores: &mut DataStores,
307    extra_gpu_data: &mut SmallVec<[GpuBufferAddress; 1]>,
308) -> (SurfaceDescriptor, [Option<RenderTaskId>; 2]) {
309    let surface = &frame_state.surfaces[surface_index.0];
310    let surface_spatial_node_index = surface.surface_spatial_node_index;
311    let raster_spatial_node_index = surface.raster_spatial_node_index;
312    let device_pixel_scale = surface.device_pixel_scale;
313
314    let primary_render_task_id;
315    let surface_descriptor;
316    let mut secondary_render_task_id = None;
317    match *composite_mode {
318        PictureCompositeMode::TileCache { .. } => {
319            unreachable!("handled above");
320        }
321        PictureCompositeMode::Filter(Filter::Blur { width, height, edge_mode, .. }) => {
322            let (width, height) = surface.clamp_blur_radius(width, height);
323
324            let width_std_deviation = width * surface.local_scale.0 * device_pixel_scale.0;
325            let height_std_deviation = height * surface.local_scale.1 * device_pixel_scale.0;
326            let blur_std_deviation = DeviceSize::new(
327                width_std_deviation,
328                height_std_deviation,
329            );
330
331            let original_size = surface_rects.clipped.size();
332
333            let adjusted_size = BlurTask::adjusted_blur_source_size(
334                original_size,
335                blur_std_deviation,
336            );
337
338            let clear_color = if adjusted_size == original_size {
339                None
340            } else {
341                Some(ColorF::TRANSPARENT)
342            };
343
344            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
345            let adjusted_size = adjusted_size.to_i32();
346
347            let uv_rect_kind = calculate_uv_rect_kind(
348                DeviceRect::from_origin_and_size(surface_rects.clipped.min, adjusted_size.to_f32()),
349                surface_rects.unclipped,
350            );
351
352            let picture_task_id = frame_state.rg_builder.add().init(
353                RenderTask::new_dynamic(
354                    adjusted_size,
355                    RenderTaskKind::new_picture(
356                        adjusted_size,
357                        surface_rects.needs_scissor_rect,
358                        surface_rects.clipped.min,
359                        surface_spatial_node_index,
360                        raster_spatial_node_index,
361                        device_pixel_scale,
362                        None,
363                        None,
364                        clear_color,
365                        cmd_buffer_index,
366                        can_use_shared_surface,
367                        Some(original_size.round().to_i32()),
368                    )
369                ).with_uv_rect_kind(uv_rect_kind)
370            );
371
372
373            let blur_render_task_id = request_render_task(
374                frame_state,
375                snapshot,
376                &surface_rects,
377                false,
378                &mut|rg_builder, _| {
379                    RenderTask::new_blur(
380                        blur_std_deviation,
381                        picture_task_id,
382                        rg_builder,
383                        RenderTargetKind::Color,
384                        None,
385                        original_size.to_i32(),
386                        edge_mode,
387                    )
388                }
389            );
390            primary_render_task_id = blur_render_task_id;
391
392            surface_descriptor = SurfaceDescriptor::new_chained(
393                picture_task_id,
394                blur_render_task_id,
395                surface_rects.clipped_local,
396            );
397        }
398        PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
399            let surface = &frame_state.surfaces[surface_index.0];
400
401            let device_rect = surface_rects.clipped;
402
403            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
404
405            let picture_task_id = frame_state.rg_builder.add().init(
406                RenderTask::new_dynamic(
407                    surface_rects.task_size,
408                    RenderTaskKind::new_picture(
409                        surface_rects.task_size,
410                        surface_rects.needs_scissor_rect,
411                        device_rect.min,
412                        surface_spatial_node_index,
413                        raster_spatial_node_index,
414                        device_pixel_scale,
415                        None,
416                        None,
417                        None,
418                        cmd_buffer_index,
419                        can_use_shared_surface,
420                        None,
421                    ),
422                ).with_uv_rect_kind(surface_rects.uv_rect_kind)
423            );
424
425            let mut blur_tasks = BlurTaskCache::default();
426
427            extra_gpu_data.resize(shadows.len(), GpuBufferAddress::INVALID);
428
429            let mut blur_render_task_id = picture_task_id;
430            for shadow in shadows {
431                let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
432                    shadow.blur_radius,
433                    shadow.blur_radius,
434                );
435
436                blur_render_task_id = RenderTask::new_blur(
437                    DeviceSize::new(
438                        blur_radius_x * surface.local_scale.0 * device_pixel_scale.0,
439                        blur_radius_y * surface.local_scale.1 * device_pixel_scale.0,
440                    ),
441                    picture_task_id,
442                    frame_state.rg_builder,
443                    RenderTargetKind::Color,
444                    Some(&mut blur_tasks),
445                    device_rect.size().to_i32(),
446                    BlurEdgeMode::Duplicate,
447                );
448            }
449
450            frame_state.surface_builder.add_picture_render_task(picture_task_id);
451
452            primary_render_task_id = blur_render_task_id;
453            secondary_render_task_id = Some(picture_task_id);
454
455            surface_descriptor = SurfaceDescriptor::new_chained(
456                picture_task_id,
457                blur_render_task_id,
458                surface_rects.clipped_local,
459            );
460        }
461        PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
462            mode,
463            frame_context.fb_config.gpu_supports_advanced_blend,
464            frame_context.fb_config.advanced_blend_is_coherent,
465            frame_context.fb_config.dual_source_blending_is_supported,
466        ).is_none() => {
467            let parent_surface = &frame_state.surfaces[parent_surface_index.0];
468
469            let map_pic_to_parent = SpaceMapper::new_with_target(
470                parent_surface.surface_spatial_node_index,
471                surface_spatial_node_index,
472                parent_surface.clipping_rect,
473                frame_context.spatial_tree,
474            );
475            let pic_rect = surface.clipped_local_rect;
476            let pic_in_raster_space = map_pic_to_parent
477                .map(&pic_rect)
478                .expect("bug: unable to map mix-blend content into parent");
479
480            let backdrop_rect = pic_in_raster_space;
481            let parent_surface_rect = parent_surface.clipping_rect;
482
483            let readback_task_id = match backdrop_rect.intersection(&parent_surface_rect) {
484                Some(available_rect) => {
485                    let backdrop_rect = parent_surface.map_to_device_rect(
486                        &backdrop_rect,
487                        frame_context.spatial_tree,
488                    );
489
490                    let available_rect = parent_surface.map_to_device_rect(
491                        &available_rect,
492                        frame_context.spatial_tree,
493                    ).round_out();
494
495                    let backdrop_uv = calculate_uv_rect_kind(
496                        available_rect,
497                        backdrop_rect,
498                    );
499
500                    frame_state.rg_builder.add().init(
501                        RenderTask::new_dynamic(
502                            available_rect.size().to_i32(),
503                            RenderTaskKind::new_readback(Some(available_rect.min)),
504                        ).with_uv_rect_kind(backdrop_uv)
505                    )
506                }
507                None => {
508                    frame_state.rg_builder.add().init(
509                        RenderTask::new_dynamic(
510                            DeviceIntSize::new(16, 16),
511                            RenderTaskKind::new_readback(None),
512                        )
513                    )
514                }
515            };
516
517            frame_state.surface_builder.add_child_render_task(
518                readback_task_id,
519                frame_state.rg_builder,
520            );
521
522            secondary_render_task_id = Some(readback_task_id);
523
524            let task_size = surface_rects.clipped.size().to_i32();
525
526            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
527
528            let is_opaque = false;
529            let render_task_id = request_render_task(
530                frame_state,
531                &snapshot,
532                &surface_rects,
533                is_opaque,
534                &mut|rg_builder, _| {
535                    rg_builder.add().init(
536                        RenderTask::new_dynamic(
537                            task_size,
538                            RenderTaskKind::new_picture(
539                                task_size,
540                                surface_rects.needs_scissor_rect,
541                                surface_rects.clipped.min,
542                                surface_spatial_node_index,
543                                raster_spatial_node_index,
544                                device_pixel_scale,
545                                None,
546                                None,
547                                None,
548                                cmd_buffer_index,
549                                can_use_shared_surface,
550                                None,
551                            )
552                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
553                    )
554                }
555            );
556
557            primary_render_task_id = render_task_id;
558
559            surface_descriptor = SurfaceDescriptor::new_simple(
560                render_task_id,
561                surface_rects.clipped_local,
562            );
563        }
564        PictureCompositeMode::Filter(..) => {
565            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
566
567            let is_opaque = false;
568            let render_task_id = request_render_task(
569                frame_state,
570                snapshot,
571                &surface_rects,
572                is_opaque,
573                &mut|rg_builder, _| {
574                    rg_builder.add().init(
575                        RenderTask::new_dynamic(
576                            surface_rects.task_size,
577                            RenderTaskKind::new_picture(
578                                surface_rects.task_size,
579                                surface_rects.needs_scissor_rect,
580                                surface_rects.clipped.min,
581                                surface_spatial_node_index,
582                                raster_spatial_node_index,
583                                device_pixel_scale,
584                                None,
585                                None,
586                                None,
587                                cmd_buffer_index,
588                                can_use_shared_surface,
589                                None,
590                            )
591                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
592                    )
593                },
594            );
595
596            primary_render_task_id = render_task_id;
597
598            surface_descriptor = SurfaceDescriptor::new_simple(
599                render_task_id,
600                surface_rects.clipped_local,
601            );
602        }
603        PictureCompositeMode::ComponentTransferFilter(..) => {
604            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
605
606            let is_opaque = false;
607            let render_task_id = request_render_task(
608                frame_state,
609                snapshot,
610                &surface_rects,
611                is_opaque,
612                &mut|rg_builder, _| {
613                    rg_builder.add().init(
614                        RenderTask::new_dynamic(
615                            surface_rects.task_size,
616                            RenderTaskKind::new_picture(
617                                surface_rects.task_size,
618                                surface_rects.needs_scissor_rect,
619                                surface_rects.clipped.min,
620                                surface_spatial_node_index,
621                                raster_spatial_node_index,
622                                device_pixel_scale,
623                                None,
624                                None,
625                                None,
626                                cmd_buffer_index,
627                                can_use_shared_surface,
628                                None,
629                            )
630                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
631                    )
632                }
633            );
634
635            primary_render_task_id = render_task_id;
636
637            surface_descriptor = SurfaceDescriptor::new_simple(
638                render_task_id,
639                surface_rects.clipped_local,
640            );
641        }
642        PictureCompositeMode::MixBlend(..) |
643        PictureCompositeMode::Blit(_) => {
644            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
645
646            let is_opaque = false;
647            let render_task_id = request_render_task(
648                frame_state,
649                snapshot,
650                &surface_rects,
651                is_opaque,
652                &mut|rg_builder, _| {
653                    rg_builder.add().init(
654                        RenderTask::new_dynamic(
655                            surface_rects.task_size,
656                            RenderTaskKind::new_picture(
657                                surface_rects.task_size,
658                                surface_rects.needs_scissor_rect,
659                                surface_rects.clipped.min,
660                                surface_spatial_node_index,
661                                raster_spatial_node_index,
662                                device_pixel_scale,
663                                None,
664                                None,
665                                None,
666                                cmd_buffer_index,
667                                can_use_shared_surface,
668                                None,
669                            )
670                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
671                    )
672                }
673            );
674
675            primary_render_task_id = render_task_id;
676
677            surface_descriptor = SurfaceDescriptor::new_simple(
678                render_task_id,
679                surface_rects.clipped_local,
680            );
681        }
682        PictureCompositeMode::IntermediateSurface => {
683            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
684
685            let is_opaque = false;
686            let render_task_id = request_render_task(
687                frame_state,
688                snapshot,
689                &surface_rects,
690                is_opaque,
691                &mut|rg_builder, _| {
692                    rg_builder.add().init(
693                        RenderTask::new_dynamic(
694                            surface_rects.task_size,
695                            RenderTaskKind::new_picture(
696                                surface_rects.task_size,
697                                surface_rects.needs_scissor_rect,
698                                surface_rects.clipped.min,
699                                surface_spatial_node_index,
700                                raster_spatial_node_index,
701                                device_pixel_scale,
702                                None,
703                                None,
704                                None,
705                                cmd_buffer_index,
706                                can_use_shared_surface,
707                                None,
708                            )
709                        ).with_uv_rect_kind(surface_rects.uv_rect_kind)
710                    )
711                }
712            );
713
714            primary_render_task_id = render_task_id;
715
716            surface_descriptor = SurfaceDescriptor::new_simple(
717                render_task_id,
718                surface_rects.clipped_local,
719            );
720        }
721        PictureCompositeMode::SVGFEGraph(ref filters) => {
722            let cmd_buffer_index = frame_state.cmd_buffers.create_cmd_buffer();
723
724            let prim_subregion = surface_rects.unclipped;
725            let target_subregion = surface_rects.clipped;
726            let source_subregion = surface_rects.source;
727
728            let source_task_size = source_subregion.round_out().size().to_i32();
729            let source_task_size = if source_task_size.width > 0 && source_task_size.height > 0 {
730                source_task_size
731            } else {
732                DeviceIntSize::new(1,1)
733            };
734            let picture_task_id = frame_state.rg_builder.add().init(
735                RenderTask::new_dynamic(
736                    source_task_size,
737                    RenderTaskKind::new_picture(
738                        source_task_size,
739                        surface_rects.needs_scissor_rect,
740                        source_subregion.min,
741                        surface_spatial_node_index,
742                        raster_spatial_node_index,
743                        device_pixel_scale,
744                        None,
745                        None,
746                        None,
747                        cmd_buffer_index,
748                        can_use_shared_surface,
749                        None,
750                    )
751                )
752            );
753
754            let subregion_to_device_scale_x = surface_rects.clipped_notsnapped.width() / surface_rects.clipped_local.width();
755            let subregion_to_device_scale_y = surface_rects.clipped_notsnapped.height() / surface_rects.clipped_local.height();
756            let subregion_to_device_offset_x = surface_rects.clipped_notsnapped.min.x - (surface_rects.clipped_local.min.x * subregion_to_device_scale_x).floor();
757            let subregion_to_device_offset_y = surface_rects.clipped_notsnapped.min.y - (surface_rects.clipped_local.min.y * subregion_to_device_scale_y).floor();
758
759            let filter_task_id = request_render_task(
760                frame_state,
761                snapshot,
762                &surface_rects,
763                false,
764                &mut|rg_builder, gpu_buffer| {
765                    RenderTask::new_svg_filter_graph(
766                        filters,
767                        rg_builder,
768                        gpu_buffer,
769                        data_stores,
770                        surface_rects.uv_rect_kind,
771                        picture_task_id,
772                        source_subregion.cast_unit(),
773                        target_subregion.cast_unit(),
774                        prim_subregion.cast_unit(),
775                        subregion_to_device_scale_x,
776                        subregion_to_device_scale_y,
777                        subregion_to_device_offset_x,
778                        subregion_to_device_offset_y,
779                    )
780                }
781            );
782
783            primary_render_task_id = filter_task_id;
784
785            surface_descriptor = SurfaceDescriptor::new_chained(
786                picture_task_id,
787                filter_task_id,
788                surface_rects.clipped_local,
789            );
790        }
791    }
792
793    (
794        surface_descriptor,
795        [
796            Some(primary_render_task_id),
797            secondary_render_task_id,
798        ]
799    )
800}
801
802fn request_render_task(
803    frame_state: &mut FrameBuildingState,
804    snapshot: &Option<SnapshotInfo>,
805    surface_rects: &SurfaceAllocInfo,
806    is_opaque: bool,
807    f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId,
808) -> RenderTaskId {
809
810    let task_id = match snapshot {
811        Some(info) => {
812            let adjustment = AdjustedImageSource::from_rects(
813                &info.area,
814                &surface_rects.clipped_local.cast_unit()
815            );
816            let task_id = frame_state.resource_cache.render_as_image(
817                info.key.as_image(),
818                surface_rects.task_size,
819                frame_state.rg_builder,
820                &mut frame_state.frame_gpu_data.f32,
821                is_opaque,
822                &adjustment,
823                f
824            );
825
826            // TODO(bug 1929809): adding the dependency in the other branch causes
827            // a panic in reftests/blend/backdrop-filter-blend-container.yaml.
828            // Presumably if we use backdrop filters with snapshotting it will
829            // trigger the panic as well.
830            frame_state.surface_builder.add_child_render_task(
831                task_id,
832                frame_state.rg_builder,
833            );
834
835            frame_state.image_dependencies.insert(info.key.as_image(), task_id);
836
837            task_id
838        }
839        None => {
840            f(
841                frame_state.rg_builder,
842                &mut frame_state.frame_gpu_data.f32,
843            )
844        }
845    };
846
847    task_id
848}
849
850/// Information from `get_surface_rects` about the allocated size, UV sampling
851/// parameters etc for an off-screen surface
852#[derive(Debug)]
853pub struct SurfaceAllocInfo {
854    pub task_size: DeviceIntSize,
855    pub needs_scissor_rect: bool,
856    pub clipped: DeviceRect,
857    pub unclipped: DeviceRect,
858    // Only used for SVGFEGraph currently, this is the source pixels needed to
859    // render the pixels in clipped.
860    pub source: DeviceRect,
861    // Only used for SVGFEGraph, this is the same as clipped before rounding.
862    pub clipped_notsnapped: DeviceRect,
863    pub clipped_local: PictureRect,
864    pub uv_rect_kind: UvRectKind,
865}
866
867pub fn get_surface_rects(
868    surface_index: SurfaceIndex,
869    composite_mode: &PictureCompositeMode,
870    parent_surface_index: SurfaceIndex,
871    surfaces: &mut [SurfaceInfo],
872    spatial_tree: &SpatialTree,
873    max_surface_size: f32,
874    force_scissor_rect: bool,
875) -> Option<SurfaceAllocInfo> {
876    let parent_surface = &surfaces[parent_surface_index.0];
877
878    let local_to_parent = SpaceMapper::new_with_target(
879        parent_surface.surface_spatial_node_index,
880        surfaces[surface_index.0].surface_spatial_node_index,
881        parent_surface.clipping_rect,
882        spatial_tree,
883    );
884
885    let local_clip_rect = local_to_parent
886        .unmap(&parent_surface.clipping_rect)
887        .unwrap_or(PictureRect::max_rect())
888        .cast_unit();
889
890    let surface = &mut surfaces[surface_index.0];
891
892    let (clipped_local, unclipped_local, source_local) = match composite_mode {
893        PictureCompositeMode::SVGFEGraph(ref filters) => {
894            let prim_subregion = composite_mode.get_rect(surface, None);
895
896            let visible_subregion: LayoutRect =
897                prim_subregion.cast_unit()
898                .intersection(&local_clip_rect)
899                .unwrap_or(PictureRect::zero())
900                .cast_unit();
901
902            if visible_subregion.is_empty() {
903                return None;
904            }
905
906            let source_potential_subregion = get_coverage_source_svgfe(
907                filters, visible_subregion.cast_unit());
908            let source_subregion =
909                source_potential_subregion
910                .intersection(&surface.unclipped_local_rect.cast_unit())
911                .unwrap_or(LayoutRect::zero());
912
913            let coverage_subregion = source_subregion.union(&visible_subregion);
914
915            (coverage_subregion.cast_unit(), prim_subregion.cast_unit(), source_subregion.cast_unit())
916        }
917        PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
918            let local_prim_rect = surface.clipped_local_rect;
919
920            // Track max blur inflation across all shadows so the content-side
921            // source region is inflated by blur margin (matching the default
922            // branch's `get_rect(Some(sub_rect))` step for Blur). Without this,
923            // the picture_task texture's edges land on image content and the
924            // content quad's blur margin samples UVs > 1 → image edge bleed.
925            let mut max_blur_inflation_x: f32 = 0.0;
926            let mut max_blur_inflation_y: f32 = 0.0;
927            for shadow in shadows {
928                let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
929                    shadow.blur_radius,
930                    shadow.blur_radius,
931                );
932                max_blur_inflation_x = max_blur_inflation_x.max(blur_radius_x * BLUR_SAMPLE_SCALE);
933                max_blur_inflation_y = max_blur_inflation_y.max(blur_radius_y * BLUR_SAMPLE_SCALE);
934            }
935
936            let mut required_local_rect = local_prim_rect
937                .intersection(&local_clip_rect)
938                .map(|r| r.inflate(max_blur_inflation_x, max_blur_inflation_y))
939                .unwrap_or(PictureRect::zero());
940
941            for shadow in shadows {
942                let (blur_radius_x, blur_radius_y) = surface.clamp_blur_radius(
943                    shadow.blur_radius,
944                    shadow.blur_radius,
945                );
946                let blur_inflation_x = blur_radius_x * BLUR_SAMPLE_SCALE;
947                let blur_inflation_y = blur_radius_y * BLUR_SAMPLE_SCALE;
948
949                let local_shadow_rect = local_prim_rect
950                    .translate(shadow.offset.cast_unit())
951                    .inflate(blur_inflation_x, blur_inflation_y);
952
953                if let Some(clipped_shadow_rect) = local_clip_rect.intersection(&local_shadow_rect) {
954                    let required_shadow_rect = clipped_shadow_rect.inflate(blur_inflation_x, blur_inflation_y);
955
956                    let local_clipped_shadow_rect = required_shadow_rect.translate(-shadow.offset.cast_unit());
957
958                    required_local_rect = required_local_rect.union(&local_clipped_shadow_rect);
959                }
960            }
961
962            let unclipped = composite_mode.get_rect(surface, None);
963            let clipped = required_local_rect;
964
965            let clipped = match clipped.intersection(&unclipped.cast_unit()) {
966                Some(rect) => rect,
967                None => return None,
968            };
969
970            (clipped, unclipped, clipped)
971        }
972        _ => {
973            let surface_origin = surface.clipped_local_rect.min.to_vector().cast_unit();
974
975            let normalized_prim_rect = composite_mode
976                .get_rect(surface, None)
977                .translate(-surface_origin);
978
979            let normalized_clip_rect = local_clip_rect
980                .cast_unit()
981                .translate(-surface_origin);
982
983            let norm_clipped_rect = match normalized_prim_rect.intersection(&normalized_clip_rect) {
984                Some(rect) => rect,
985                None => return None,
986            };
987
988            let norm_clipped_rect = composite_mode.get_rect(surface, Some(norm_clipped_rect));
989
990            let norm_clipped_rect = match norm_clipped_rect.intersection(&normalized_prim_rect) {
991                Some(rect) => rect,
992                None => return None,
993            };
994
995            let unclipped = normalized_prim_rect.translate(surface_origin);
996            let clipped = norm_clipped_rect.translate(surface_origin);
997
998            (clipped.cast_unit(), unclipped.cast_unit(), clipped.cast_unit())
999        }
1000    };
1001
1002    let (mut clipped, mut unclipped, mut source) = if surface.raster_spatial_node_index != surface.surface_spatial_node_index {
1003        assert_eq!(surface.device_pixel_scale.0, 1.0);
1004
1005        let local_to_world = SpaceMapper::new_with_target(
1006            spatial_tree.root_reference_frame_index(),
1007            surface.surface_spatial_node_index,
1008            WorldRect::max_rect(),
1009            spatial_tree,
1010        );
1011
1012        let clipped = local_to_world.map(&clipped_local.cast_unit()).unwrap() * surface.device_pixel_scale;
1013        let unclipped = local_to_world.map(&unclipped_local).unwrap() * surface.device_pixel_scale;
1014        let source = local_to_world.map(&source_local.cast_unit()).unwrap() * surface.device_pixel_scale;
1015
1016        (clipped, unclipped, source)
1017    } else {
1018        let clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
1019        let unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
1020        let source = source_local.cast_unit() * surface.device_pixel_scale;
1021
1022        (clipped, unclipped, source)
1023    };
1024    let mut clipped_snapped = clipped.round_out();
1025    let mut source_snapped = source.round_out();
1026
1027    let max_dimension =
1028        clipped_snapped.width().max(
1029            clipped_snapped.height().max(
1030                source_snapped.width().max(
1031                    source_snapped.height()
1032                ))).ceil();
1033    if max_dimension > max_surface_size {
1034        let max_dimension =
1035            clipped_local.width().max(
1036                clipped_local.height().max(
1037                    source_local.width().max(
1038                        source_local.height()
1039                    ))).ceil();
1040        surface.raster_spatial_node_index = surface.surface_spatial_node_index;
1041        surface.device_pixel_scale = Scale::new(max_surface_size / max_dimension);
1042        surface.local_scale = (1.0, 1.0);
1043
1044        let add_markers = profiler::thread_is_being_profiled();
1045        if add_markers {
1046            let new_clipped = (clipped_local.cast_unit() * surface.device_pixel_scale).round();
1047            let new_source = (source_local.cast_unit() * surface.device_pixel_scale).round();
1048            profiler::add_text_marker("SurfaceSizeLimited",
1049                format!("Surface for {:?} reduced from raster {:?} (source {:?}) to local {:?} (source {:?})",
1050                    composite_mode.kind(),
1051                    clipped.size(), source.size(),
1052                    new_clipped, new_source).as_str(),
1053                Duration::from_secs_f32(new_clipped.width() * new_clipped.height() / 1000000000.0));
1054        }
1055
1056        clipped = clipped_local.cast_unit() * surface.device_pixel_scale;
1057        unclipped = unclipped_local.cast_unit() * surface.device_pixel_scale;
1058        source = source_local.cast_unit() * surface.device_pixel_scale;
1059        clipped_snapped = clipped.round();
1060        source_snapped = source.round();
1061    }
1062
1063    let task_size = clipped_snapped.size().to_i32();
1064    let task_size = task_size.min(DeviceIntSize::new(max_surface_size as i32, max_surface_size as i32));
1065    debug_assert!(
1066        task_size.width <= max_surface_size as i32 &&
1067        task_size.height <= max_surface_size as i32,
1068        "task_size {:?} for {:?} must be within max_surface_size {}",
1069        task_size,
1070        composite_mode.kind(),
1071        max_surface_size);
1072
1073    let uv_rect_kind = calculate_uv_rect_kind(
1074        clipped_snapped,
1075        unclipped,
1076    );
1077
1078    if task_size.width == 0 || task_size.height == 0 {
1079        return None;
1080    }
1081
1082    let needs_scissor_rect = force_scissor_rect || !clipped_local.contains_box(&surface.unclipped_local_rect);
1083
1084    Some(SurfaceAllocInfo {
1085        task_size,
1086        needs_scissor_rect,
1087        clipped: clipped_snapped,
1088        unclipped,
1089        source: source_snapped,
1090        clipped_notsnapped: clipped,
1091        clipped_local,
1092        uv_rect_kind,
1093    })
1094}
1095
1096pub fn calculate_uv_rect_kind(
1097    clipped: DeviceRect,
1098    unclipped: DeviceRect,
1099) -> UvRectKind {
1100    let top_left = calculate_screen_uv(
1101        unclipped.top_left().cast_unit(),
1102        clipped,
1103    );
1104
1105    let top_right = calculate_screen_uv(
1106        unclipped.top_right().cast_unit(),
1107        clipped,
1108    );
1109
1110    let bottom_left = calculate_screen_uv(
1111        unclipped.bottom_left().cast_unit(),
1112        clipped,
1113    );
1114
1115    let bottom_right = calculate_screen_uv(
1116        unclipped.bottom_right().cast_unit(),
1117        clipped,
1118    );
1119
1120    UvRectKind::Quad {
1121        top_left,
1122        top_right,
1123        bottom_left,
1124        bottom_right,
1125    }
1126}
1127
1128/// Represents a hashable description of how a picture primitive
1129/// will be composited into its parent.
1130#[cfg_attr(feature = "capture", derive(Serialize))]
1131#[cfg_attr(feature = "replay", derive(Deserialize))]
1132#[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
1133pub enum PictureCompositeKey {
1134    // No visual compositing effect
1135    Identity,
1136
1137    // FilterOp
1138    Blur(Au, Au, bool, BlurEdgeMode),
1139    Brightness(Au),
1140    Contrast(Au),
1141    Grayscale(Au),
1142    HueRotate(Au),
1143    Invert(Au),
1144    Opacity(Au),
1145    OpacityBinding(PropertyBindingId, Au),
1146    Saturate(Au),
1147    Sepia(Au),
1148    DropShadows(Vec<(VectorKey, Au, ColorU)>),
1149    ColorMatrix([Au; 20]),
1150    SrgbToLinear,
1151    LinearToSrgb,
1152    ComponentTransfer(ItemUid),
1153    Flood(ColorU),
1154    SVGFEGraph(Vec<(FilterGraphNodeKey, FilterGraphOpKey)>),
1155
1156    // MixBlendMode
1157    Multiply,
1158    Screen,
1159    Overlay,
1160    Darken,
1161    Lighten,
1162    ColorDodge,
1163    ColorBurn,
1164    HardLight,
1165    SoftLight,
1166    Difference,
1167    Exclusion,
1168    Hue,
1169    Saturation,
1170    Color,
1171    Luminosity,
1172    PlusLighter,
1173}
1174
1175impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
1176    fn from(mode: Option<PictureCompositeMode>) -> Self {
1177        match mode {
1178            Some(PictureCompositeMode::MixBlend(mode)) => {
1179                match mode {
1180                    MixBlendMode::Normal => PictureCompositeKey::Identity,
1181                    MixBlendMode::Multiply => PictureCompositeKey::Multiply,
1182                    MixBlendMode::Screen => PictureCompositeKey::Screen,
1183                    MixBlendMode::Overlay => PictureCompositeKey::Overlay,
1184                    MixBlendMode::Darken => PictureCompositeKey::Darken,
1185                    MixBlendMode::Lighten => PictureCompositeKey::Lighten,
1186                    MixBlendMode::ColorDodge => PictureCompositeKey::ColorDodge,
1187                    MixBlendMode::ColorBurn => PictureCompositeKey::ColorBurn,
1188                    MixBlendMode::HardLight => PictureCompositeKey::HardLight,
1189                    MixBlendMode::SoftLight => PictureCompositeKey::SoftLight,
1190                    MixBlendMode::Difference => PictureCompositeKey::Difference,
1191                    MixBlendMode::Exclusion => PictureCompositeKey::Exclusion,
1192                    MixBlendMode::Hue => PictureCompositeKey::Hue,
1193                    MixBlendMode::Saturation => PictureCompositeKey::Saturation,
1194                    MixBlendMode::Color => PictureCompositeKey::Color,
1195                    MixBlendMode::Luminosity => PictureCompositeKey::Luminosity,
1196                    MixBlendMode::PlusLighter => PictureCompositeKey::PlusLighter,
1197                }
1198            }
1199            Some(PictureCompositeMode::Filter(op)) => {
1200                match op {
1201                    Filter::Blur { width, height, should_inflate, edge_mode } => {
1202                        PictureCompositeKey::Blur(
1203                            Au::from_f32_px(width),
1204                            Au::from_f32_px(height),
1205                            should_inflate,
1206                            edge_mode,
1207                        )
1208                    }
1209                    Filter::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
1210                    Filter::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
1211                    Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
1212                    Filter::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
1213                    Filter::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
1214                    Filter::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
1215                    Filter::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
1216                    Filter::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
1217                    Filter::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
1218                    Filter::Identity => PictureCompositeKey::Identity,
1219                    Filter::DropShadows(ref shadows) => {
1220                        PictureCompositeKey::DropShadows(
1221                            shadows.iter().map(|shadow| {
1222                                (shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
1223                            }).collect()
1224                        )
1225                    }
1226                    Filter::Opacity(binding, _) => {
1227                        match binding {
1228                            PropertyBinding::Value(value) => {
1229                                PictureCompositeKey::Opacity(Au::from_f32_px(value))
1230                            }
1231                            PropertyBinding::Binding(key, default) => {
1232                                PictureCompositeKey::OpacityBinding(key.id, Au::from_f32_px(default))
1233                            }
1234                        }
1235                    }
1236                    Filter::ColorMatrix(values) => {
1237                        let mut quantized_values: [Au; 20] = [Au(0); 20];
1238                        for (value, result) in values.iter().zip(quantized_values.iter_mut()) {
1239                            *result = Au::from_f32_px(*value);
1240                        }
1241                        PictureCompositeKey::ColorMatrix(quantized_values)
1242                    }
1243                    Filter::ComponentTransfer => unreachable!(),
1244                    Filter::Flood(color) => PictureCompositeKey::Flood(color.into()),
1245                    Filter::SVGGraphNode(_node, _op) => unreachable!(),
1246                }
1247            }
1248            Some(PictureCompositeMode::ComponentTransferFilter(handle)) => {
1249                PictureCompositeKey::ComponentTransfer(handle.uid())
1250            }
1251            Some(PictureCompositeMode::SVGFEGraph(filter_nodes)) => {
1252                PictureCompositeKey::SVGFEGraph(
1253                    filter_nodes.into_iter().map(|(node, op)| {
1254                        (node.into(), op.into())
1255                    }).collect())
1256            }
1257            Some(PictureCompositeMode::Blit(_)) |
1258            Some(PictureCompositeMode::TileCache { .. }) |
1259            Some(PictureCompositeMode::IntermediateSurface) |
1260            None => {
1261                PictureCompositeKey::Identity
1262            }
1263        }
1264    }
1265}