Skip to main content

webrender/
render_target.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
6use api::units::*;
7use api::{ColorF, LineOrientation, BorderStyle};
8use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures};
9use crate::batch::{ClipBatcher, BatchBuilder, INVALID_SEGMENT_INDEX, ClipMaskInstanceList};
10use crate::render_task::{SubTask, RectangleClipSubTask, ImageClipSubTask};
11use crate::command_buffer::{CommandBufferList, QuadFlags};
12use crate::pattern::{Pattern, PatternKind, PatternShaderInput};
13use crate::segment::EdgeMask;
14use crate::spatial_tree::SpatialTree;
15use crate::clip::ClipStore;
16use crate::frame_builder::FrameGlobalResources;
17use crate::gpu_types::{BorderInstance, SVGFEFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
18use crate::gpu_types::{ZBufferIdGenerator, MaskInstance, BlurEdgeMode};
19use crate::gpu_types::{ZBufferId, PrimitiveInstanceData};
20use crate::internal_types::{CacheTextureId, FastHashMap, FrameAllocator, FrameMemory, FrameVec, TextureSource};
21use crate::svg_filter::FilterGraphOp;
22use crate::picture::{SurfaceInfo, ResolvedSurfaceTexture};
23use crate::tile_cache::{SliceId, TileCacheInstance};
24use crate::transform::TransformPalette;
25use crate::quad;
26use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PrimitiveScratchBuffer};
27use crate::renderer::{BlendMode, GpuBufferAddress, GpuBufferBuilder};
28use crate::render_backend::DataStores;
29use crate::render_task::{RenderTaskKind, RenderTaskAddress};
30use crate::render_task::{RenderTask, ScalingTask, SVGFEFilterTask};
31use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
32use crate::resource_cache::ResourceCache;
33use crate::spatial_tree::SpatialNodeIndex;
34
35
36const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16);
37const STYLE_MASK: i32 = 0x00FF_FF00;
38
39/// A tag used to identify the output format of a `RenderTarget`.
40#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
41#[cfg_attr(feature = "capture", derive(Serialize))]
42#[cfg_attr(feature = "replay", derive(Deserialize))]
43pub enum RenderTargetKind {
44    Color, // RGBA8
45    Alpha, // R8
46}
47
48pub struct RenderTargetContext<'a, 'rc> {
49    pub global_device_pixel_scale: DevicePixelScale,
50    pub prim_store: &'a PrimitiveStore,
51    pub resource_cache: &'rc mut ResourceCache,
52    pub use_dual_source_blending: bool,
53    pub use_advanced_blending: bool,
54    pub break_advanced_blend_batches: bool,
55    pub batch_lookback_count: usize,
56    pub spatial_tree: &'a SpatialTree,
57    pub data_stores: &'a DataStores,
58    pub surfaces: &'a [SurfaceInfo],
59    pub scratch: &'a PrimitiveScratchBuffer,
60    pub screen_world_rect: WorldRect,
61    pub globals: &'a FrameGlobalResources,
62    pub tile_caches: &'a FastHashMap<SliceId, Box<TileCacheInstance>>,
63    pub root_spatial_node_index: SpatialNodeIndex,
64    pub frame_memory: &'a mut FrameMemory,
65}
66
67/// A series of `RenderTarget` instances, serving as the high-level container
68/// into which `RenderTasks` are assigned.
69///
70/// During the build phase, we iterate over the tasks in each `RenderPass`. For
71/// each task, we invoke `allocate()` on the `RenderTargetList`, which in turn
72/// attempts to allocate an output region in the last `RenderTarget` in the
73/// list. If allocation fails (or if the list is empty), a new `RenderTarget` is
74/// created and appended to the list. The build phase then assign the task into
75/// the target associated with the final allocation.
76///
77/// The result is that each `RenderPass` is associated with one or two
78/// `RenderTargetLists`, depending on whether we have all our tasks have the
79/// same `RenderTargetKind`. The lists are then shipped to the `Renderer`, which
80/// allocates a device texture array, with one slice per render target in the
81/// list.
82///
83/// The upshot of this scheme is that it maximizes batching. In a given pass,
84/// we need to do a separate batch for each individual render target. But with
85/// the texture array, we can expose the entirety of the previous pass to each
86/// task in the current pass in a single batch, which generally allows each
87/// task to be drawn in a single batch regardless of how many results from the
88/// previous pass it depends on.
89///
90/// Note that in some cases (like drop-shadows), we can depend on the output of
91/// a pass earlier than the immediately-preceding pass.
92#[cfg_attr(feature = "capture", derive(Serialize))]
93#[cfg_attr(feature = "replay", derive(Deserialize))]
94pub struct RenderTargetList {
95    pub targets: FrameVec<RenderTarget>,
96}
97
98impl RenderTargetList {
99    pub fn new(allocator: FrameAllocator) -> Self {
100        RenderTargetList {
101            targets: allocator.new_vec(),
102        }
103    }
104
105    pub fn build(
106        &mut self,
107        ctx: &mut RenderTargetContext,
108        render_tasks: &RenderTaskGraph,
109        prim_headers: &mut PrimitiveHeaders,
110        transforms: &mut TransformPalette,
111        z_generator: &mut ZBufferIdGenerator,
112        prim_instances: &[PrimitiveInstance],
113        cmd_buffers: &CommandBufferList,
114        gpu_buffer_builder: &mut GpuBufferBuilder,
115    ) {
116        if self.targets.is_empty() {
117            return;
118        }
119
120        for target in &mut self.targets {
121            target.build(
122                ctx,
123                render_tasks,
124                prim_headers,
125                transforms,
126                z_generator,
127                prim_instances,
128                cmd_buffers,
129                gpu_buffer_builder,
130            );
131        }
132    }
133}
134
135const NUM_PATTERNS: usize = crate::pattern::NUM_PATTERNS as usize;
136
137/// Contains the work (in the form of instance arrays) needed to fill a color
138/// color (RGBA8) or alpha output surface.
139///
140/// In graphics parlance, a "render target" usually means "a surface (texture or
141/// framebuffer) bound to the output of a shader". This struct has a slightly
142/// different meaning, in that it represents the operations on that surface
143/// _before_ it's actually bound and rendered. So a `RenderTarget` is built by
144/// the `RenderBackend` by inserting tasks, and then shipped over to the
145/// `Renderer` where a device surface is resolved and the tasks are transformed
146/// into draw commands on that surface.
147#[cfg_attr(feature = "capture", derive(Serialize))]
148#[cfg_attr(feature = "replay", derive(Deserialize))]
149pub struct RenderTarget {
150    pub target_kind: RenderTargetKind,
151    pub cached: bool,
152    screen_size: DeviceIntSize,
153    pub texture_id: CacheTextureId,
154
155    pub alpha_batch_containers: FrameVec<AlphaBatchContainer>,
156    // List of blur operations to apply for this render target.
157    pub vertical_blurs: FastHashMap<TextureSource, FrameVec<BlurInstance>>,
158    pub horizontal_blurs: FastHashMap<TextureSource, FrameVec<BlurInstance>>,
159    pub scalings: FastHashMap<TextureSource, FrameVec<ScalingInstance>>,
160    pub svg_nodes: FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>,
161    pub blits: FrameVec<BlitJob>,
162    alpha_tasks: FrameVec<RenderTaskId>,
163    pub resolve_ops: FrameVec<ResolveOp>,
164
165    pub prim_instances: [FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>; NUM_PATTERNS],
166    pub prim_instances_with_scissor: FastHashMap<(DeviceIntRect, PatternKind), FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>>,
167
168    pub clip_masks: ClipMaskInstanceList,
169
170    pub border_segments_complex: FrameVec<BorderInstance>,
171    pub border_segments_solid: FrameVec<BorderInstance>,
172    pub line_decorations: FrameVec<LineDecorationJob>,
173
174    pub clip_batcher: ClipBatcher,
175
176    // Clearing render targets has a fair amount of special cases.
177    // The general rules are:
178    // - Depth (for at least the used potion of the target) is always cleared if it
179    //   is used by the target. The rest of this explaination focuses on clearing
180    //   color/alpha textures.
181    // - For non-cached targets we either clear the entire target or the used portion
182    //   (unless clear_color is None).
183    // - Cached render targets require precise partial clears which are specified
184    //   via the vectors below (if clearing is needed at all).
185    //
186    // See also: Renderer::clear_render_target
187
188    // Areas that *must* be cleared.
189    // Even if a global target clear is done, we try to honor clearing the rects that
190    // have a different color than the global clear color.
191    pub clears: FrameVec<(DeviceIntRect, ColorF)>,
192
193    // Optionally track the used rect of the render target, to give the renderer
194    // an opportunity to only clear the used portion of the target as an optimization.
195    // Note: We make the simplifying assumption that if clear vectors AND used_rect
196    // are specified, then the rects from the clear vectors are contained in
197    // used_rect.
198    pub used_rect: Option<DeviceIntRect>,
199    // The global clear color is Some(TRANSPARENT) by default. If we are drawing
200    // a single render task in this target, it can be set to something else.
201    // If clear_color is None, only the clears/zero_clears/one_clears are done.
202    pub clear_color: Option<ColorF>,
203}
204
205impl RenderTarget {
206    pub fn new(
207        target_kind: RenderTargetKind,
208        cached: bool,
209        texture_id: CacheTextureId,
210        screen_size: DeviceIntSize,
211        gpu_supports_fast_clears: bool,
212        used_rect: Option<DeviceIntRect>,
213        memory: &FrameMemory,
214    ) -> Self {
215        RenderTarget {
216            target_kind,
217            cached,
218            screen_size,
219            texture_id,
220            alpha_batch_containers: memory.new_vec(),
221            vertical_blurs: FastHashMap::default(),
222            horizontal_blurs: FastHashMap::default(),
223            scalings: FastHashMap::default(),
224            svg_nodes: memory.new_vec(),
225            blits: memory.new_vec(),
226            alpha_tasks: memory.new_vec(),
227            used_rect,
228            resolve_ops: memory.new_vec(),
229            clear_color: Some(ColorF::TRANSPARENT),
230            prim_instances: [
231                FastHashMap::default(),
232                FastHashMap::default(),
233                FastHashMap::default(),
234                FastHashMap::default(),
235                FastHashMap::default(),
236                FastHashMap::default(),
237                FastHashMap::default(),
238                FastHashMap::default(),
239            ],
240            prim_instances_with_scissor: FastHashMap::default(),
241            clip_masks: ClipMaskInstanceList::new(memory),
242            clip_batcher: ClipBatcher::new(gpu_supports_fast_clears, memory),
243            border_segments_complex: memory.new_vec(),
244            border_segments_solid: memory.new_vec(),
245            clears: memory.new_vec(),
246            line_decorations: memory.new_vec(),
247        }
248    }
249
250    pub fn build(
251        &mut self,
252        ctx: &mut RenderTargetContext,
253        render_tasks: &RenderTaskGraph,
254        prim_headers: &mut PrimitiveHeaders,
255        transforms: &mut TransformPalette,
256        z_generator: &mut ZBufferIdGenerator,
257        prim_instances: &[PrimitiveInstance],
258        cmd_buffers: &CommandBufferList,
259        gpu_buffer_builder: &mut GpuBufferBuilder,
260    ) {
261        profile_scope!("build");
262        let mut merged_batches = AlphaBatchContainer::new(None, &ctx.frame_memory);
263
264        for task_id in &self.alpha_tasks {
265            profile_scope!("alpha_task");
266            let task = &render_tasks[*task_id];
267
268            match task.kind {
269                RenderTaskKind::Picture(ref pic_task) => {
270                    let target_rect = task.get_target_rect();
271
272                    let scissor_rect = if pic_task.can_merge {
273                        None
274                    } else {
275                        Some(target_rect)
276                    };
277
278                    if !pic_task.can_use_shared_surface {
279                        self.clear_color = pic_task.clear_color;
280                    }
281                    if let Some(clear_color) = pic_task.clear_color {
282                        self.clears.push((target_rect, clear_color));
283                    } else if self.cached {
284                        self.clears.push((target_rect, ColorF::TRANSPARENT));
285                    }
286
287                    // TODO(gw): The type names of AlphaBatchBuilder and BatchBuilder
288                    //           are still confusing. Once more of the picture caching
289                    //           improvement code lands, the AlphaBatchBuilder and
290                    //           AlphaBatchList types will be collapsed into one, which
291                    //           should simplify coming up with better type names.
292                    let alpha_batch_builder = AlphaBatchBuilder::new(
293                        self.screen_size,
294                        ctx.break_advanced_blend_batches,
295                        ctx.batch_lookback_count,
296                        *task_id,
297                        (*task_id).into(),
298                        &ctx.frame_memory,
299                    );
300
301                    let mut batch_builder = BatchBuilder::new(alpha_batch_builder);
302                    let cmd_buffer = cmd_buffers.get(pic_task.cmd_buffer_index);
303
304                    cmd_buffer.iter_prims(&mut |cmd, spatial_node_index, segments| {
305                        batch_builder.add_prim_to_batch(
306                            cmd,
307                            spatial_node_index,
308                            ctx,
309                            render_tasks,
310                            prim_headers,
311                            transforms,
312                            pic_task.raster_spatial_node_index,
313                            pic_task.surface_spatial_node_index,
314                            z_generator,
315                            prim_instances,
316                            gpu_buffer_builder,
317                            segments,
318                        );
319                    });
320
321                    let alpha_batch_builder = batch_builder.finalize();
322
323                    alpha_batch_builder.build(
324                        &mut self.alpha_batch_containers,
325                        &mut merged_batches,
326                        target_rect,
327                        scissor_rect,
328                    );
329                }
330                _ => {
331                    unreachable!();
332                }
333            }
334        }
335
336        if !merged_batches.is_empty() {
337            self.alpha_batch_containers.push(merged_batches);
338        }
339    }
340
341    pub fn texture_id(&self) -> CacheTextureId {
342        self.texture_id
343    }
344
345    pub fn add_task(
346        &mut self,
347        task_id: RenderTaskId,
348        ctx: &RenderTargetContext,
349        gpu_buffer_builder: &mut GpuBufferBuilder,
350        render_tasks: &RenderTaskGraph,
351        clip_store: &ClipStore,
352        transforms: &mut TransformPalette,
353    ) {
354        profile_scope!("add_task");
355        let task = &render_tasks[task_id];
356        let target_rect = task.get_target_rect();
357
358        match task.kind {
359            RenderTaskKind::Prim(ref info) => {
360                // If the transform is not axis aligned, we may not be covering all of the
361                // pixels in the render task, so we need to clear it.
362                if !info.transform_id.is_2d_axis_aligned() {
363                    self.clears.push((target_rect, ColorF::TRANSPARENT));
364                }
365                // Note: For single-primitive tasks like this we always want to disable blending
366                // and overwrite whatever pixel data is in the target, even if the primitive is
367                // note entirely opaque. This is why we unconditionally set the opaque quad flag.
368                let render_task_address = task_id.into();
369                quad::add_to_batch(
370                    info.pattern,
371                    info.pattern_input,
372                    render_task_address,
373                    info.transform_id,
374                    info.prim_address_f,
375                    info.quad_flags | QuadFlags::IS_OPAQUE,
376                    info.edge_flags,
377                    INVALID_SEGMENT_INDEX as u8,
378                    info.texture_input,
379                    ZBufferId(0),
380                    BlendMode::None, // This parameter is ignored
381                    render_tasks,
382                    gpu_buffer_builder,
383                    |key, instance| {
384                        if info.prim_needs_scissor_rect {
385                            self.prim_instances_with_scissor
386                                .entry((target_rect, info.pattern))
387                                .or_insert(FastHashMap::default())
388                                .entry(key.textures.input.colors[0])
389                                .or_insert_with(|| ctx.frame_memory.new_vec())
390                                .push(instance);
391                        } else {
392                            self.prim_instances[info.pattern as usize]
393                                .entry(key.textures.input.colors[0])
394                                .or_insert_with(|| ctx.frame_memory.new_vec())
395                                .push(instance);
396                        }
397                    }
398                );
399            }
400            RenderTaskKind::VerticalBlur(ref info) => {
401                if self.target_kind == RenderTargetKind::Alpha {
402                    self.clears.push((target_rect, ColorF::TRANSPARENT));
403                }
404                add_blur_instances(
405                    &mut self.vertical_blurs,
406                    BlurDirection::Vertical,
407                    info.blur_std_deviation,
408                    info.blur_region,
409                    info.edge_mode,
410                    task_id.into(),
411                    task.children[0],
412                    render_tasks,
413                    &ctx.frame_memory,
414                );
415            }
416            RenderTaskKind::HorizontalBlur(ref info) => {
417                if self.target_kind == RenderTargetKind::Alpha {
418                    self.clears.push((target_rect, ColorF::TRANSPARENT));
419                }
420                add_blur_instances(
421                    &mut self.horizontal_blurs,
422                    BlurDirection::Horizontal,
423                    info.blur_std_deviation,
424                    info.blur_region,
425                    info.edge_mode,
426                    task_id.into(),
427                    task.children[0],
428                    render_tasks,
429                    &ctx.frame_memory,
430                );
431            }
432            RenderTaskKind::Picture(ref pic_task) => {
433                if let Some(ref resolve_op) = pic_task.resolve_op {
434                    self.resolve_ops.push(resolve_op.clone());
435                }
436                self.alpha_tasks.push(task_id);
437            }
438            RenderTaskKind::SVGFENode(ref task_info) => {
439                add_svg_filter_node_instances(
440                    &mut self.svg_nodes,
441                    render_tasks,
442                    &task_info,
443                    task,
444                    task.children.get(0).cloned(),
445                    task.children.get(1).cloned(),
446                    task_info.extra_gpu_data,
447                    &ctx.frame_memory,
448                )
449            }
450            RenderTaskKind::Empty(..) => {
451                // TODO(gw): Could likely be more efficient by choosing to clear to 0 or 1
452                //           based on the clip chain, or even skipping clear and masking the
453                //           prim region with blend disabled.
454                self.clears.push((target_rect, ColorF::WHITE));
455            }
456            RenderTaskKind::CacheMask(ref task_info) => {
457                let clear_to_one = self.clip_batcher.add(
458                    task_info.clip_node_range,
459                    task_info.root_spatial_node_index,
460                    clip_store,
461                    transforms,
462                    task_info.actual_rect,
463                    task_info.device_pixel_scale,
464                    target_rect.min.to_f32(),
465                    task_info.actual_rect.min,
466                    ctx,
467                );
468                if task_info.clear_to_one || clear_to_one {
469                    self.clears.push((target_rect, ColorF::WHITE));
470                }
471            }
472            RenderTaskKind::ClipRegion(ref region_task) => {
473                if region_task.clear_to_one {
474                    self.clears.push((target_rect, ColorF::WHITE));
475                }
476                let device_rect = DeviceRect::from_size(
477                    target_rect.size().to_f32(),
478                );
479                self.clip_batcher.add_clip_region(
480                    region_task.local_pos,
481                    device_rect,
482                    region_task.clip_data.clone(),
483                    target_rect.min.to_f32(),
484                    DevicePoint::zero(),
485                    region_task.device_pixel_scale.0,
486                );
487            }
488            RenderTaskKind::Scaling(ref info) => {
489                add_scaling_instances(
490                    info,
491                    &mut self.scalings,
492                    task,
493                    task.children.first().map(|&child| &render_tasks[child]),
494                    &ctx.frame_memory,
495                );
496            }
497            RenderTaskKind::Blit(ref task_info) => {
498                let target_rect = task.get_target_rect();
499                self.blits.push(BlitJob {
500                    source: task_info.source,
501                    source_rect: task_info.source_rect,
502                    target_rect,
503                });
504            }
505            RenderTaskKind::LineDecoration(ref info) => {
506                self.clears.push((target_rect, ColorF::TRANSPARENT));
507
508                self.line_decorations.push(LineDecorationJob {
509                    task_rect: target_rect.to_f32(),
510                    local_size: info.local_size,
511                    style: info.style as i32,
512                    axis_select: match info.orientation {
513                        LineOrientation::Horizontal => 0.0,
514                        LineOrientation::Vertical => 1.0,
515                    },
516                    wavy_line_thickness: info.wavy_line_thickness,
517                });
518            }
519            RenderTaskKind::Border(ref task_info) => {
520                self.clears.push((target_rect, ColorF::TRANSPARENT));
521
522                let task_origin = target_rect.min.to_f32();
523                // TODO(gw): Clone here instead of a move of this vec, since the frame
524                //           graph is immutable by this point. It's rare that borders
525                //           are drawn since they are persisted in the texture cache,
526                //           but perhaps this could be improved in future.
527                let instances = task_info.instances.clone();
528                for mut instance in instances {
529                    // TODO(gw): It may be better to store the task origin in
530                    //           the render task data instead of per instance.
531                    instance.task_origin = task_origin;
532                    if instance.flags & STYLE_MASK == STYLE_SOLID {
533                        self.border_segments_solid.push(instance);
534                    } else {
535                        self.border_segments_complex.push(instance);
536                    }
537                }
538            }
539            RenderTaskKind::Image(..) |
540            RenderTaskKind::Cached(..) |
541            RenderTaskKind::TileComposite(..) => {
542                panic!("Should not be added to color target!");
543            }
544            RenderTaskKind::Readback(..) => {}
545            #[cfg(test)]
546            RenderTaskKind::Test(..) => {}
547        }
548
549        let task_address = task_id.into();
550        for sub_task_id in task.sub_tasks.clone() {
551            let sub_task = &render_tasks[sub_task_id];
552            match sub_task {
553                SubTask::RectangleClip(clip_task) => {
554                    add_rect_clip_task_to_batch(
555                        clip_task,
556                        &target_rect,
557                        task_address,
558                        &ctx.frame_memory,
559                        render_tasks,
560                        gpu_buffer_builder,
561                        &mut self.clip_masks
562                    );
563                }
564                SubTask::ImageClip(clip_task) => {
565                    add_image_clip_task_to_batch(
566                        clip_task,
567                        &target_rect,
568                        task_address,
569                        &ctx.frame_memory,
570                        render_tasks,
571                        gpu_buffer_builder,
572                        &mut self.clip_masks
573                    );
574                }
575            }
576        }
577    }
578
579    pub fn needs_depth(&self) -> bool {
580        self.alpha_batch_containers.iter().any(|ab| {
581            !ab.opaque_batches.is_empty()
582        })
583    }
584}
585
586#[cfg_attr(feature = "capture", derive(Serialize))]
587#[cfg_attr(feature = "replay", derive(Deserialize))]
588#[derive(Debug, PartialEq, Clone)]
589pub struct ResolveOp {
590    pub src_task_ids: Vec<RenderTaskId>,
591    pub dest_task_id: RenderTaskId,
592}
593
594#[cfg_attr(feature = "capture", derive(Serialize))]
595#[cfg_attr(feature = "replay", derive(Deserialize))]
596pub enum PictureCacheTargetKind {
597    Draw {
598        alpha_batch_container: AlphaBatchContainer,
599    },
600    Blit {
601        task_id: RenderTaskId,
602        sub_rect_offset: DeviceIntVector2D,
603    },
604}
605
606#[cfg_attr(feature = "capture", derive(Serialize))]
607#[cfg_attr(feature = "replay", derive(Deserialize))]
608pub struct PictureCacheTarget {
609    pub surface: ResolvedSurfaceTexture,
610    pub kind: PictureCacheTargetKind,
611    pub clear_color: Option<ColorF>,
612    pub dirty_rect: DeviceIntRect,
613    pub valid_rect: DeviceIntRect,
614}
615
616fn add_blur_instances(
617    instances: &mut FastHashMap<TextureSource, FrameVec<BlurInstance>>,
618    blur_direction: BlurDirection,
619    blur_std_deviation: f32,
620    blur_region: DeviceIntSize,
621    edge_mode: BlurEdgeMode,
622    task_address: RenderTaskAddress,
623    src_task_id: RenderTaskId,
624    render_tasks: &RenderTaskGraph,
625    memory: &FrameMemory,
626) {
627    let source = render_tasks[src_task_id].get_texture_source();
628
629    let instance = BlurInstance {
630        task_address,
631        src_task_address: src_task_id.into(),
632        blur_direction: blur_direction.as_int(),
633        blur_std_deviation,
634        edge_mode: edge_mode.as_int(),
635        blur_region: blur_region.to_f32(),
636    };
637
638    instances
639        .entry(source)
640        .or_insert_with(|| memory.new_vec())
641        .push(instance);
642}
643
644fn add_scaling_instances(
645    task: &ScalingTask,
646    instances: &mut FastHashMap<TextureSource, FrameVec<ScalingInstance>>,
647    target_task: &RenderTask,
648    source_task: Option<&RenderTask>,
649    memory: &FrameMemory,
650) {
651    let target_rect = target_task
652        .get_target_rect()
653        .inner_box(task.padding)
654        .to_f32();
655
656    let source = source_task.unwrap().get_texture_source();
657
658    let source_rect = source_task.unwrap().get_target_rect().to_f32();
659
660    instances
661        .entry(source)
662        .or_insert_with(|| memory.new_vec())
663        .push(ScalingInstance::new(
664            target_rect,
665            source_rect,
666            source.uses_normalized_uvs(),
667        ));
668}
669
670/// Generates SVGFEFilterInstances from a single SVGFEFilterTask, this is what
671/// prepares vertex data for the shader, and adds it to the appropriate batch.
672///
673/// The interesting parts of the handling of SVG filters are:
674/// * scene_building.rs : wrap_prim_with_filters
675/// * picture.rs : get_coverage_svgfe
676/// * render_task.rs : new_svg_filter_graph
677/// * render_target.rs : add_svg_filter_node_instances (you are here)
678fn add_svg_filter_node_instances(
679    instances: &mut FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>,
680    render_tasks: &RenderTaskGraph,
681    task_info: &SVGFEFilterTask,
682    target_task: &RenderTask,
683    input_1_task: Option<RenderTaskId>,
684    input_2_task: Option<RenderTaskId>,
685    extra_data_address: Option<GpuBufferAddress>,
686    memory: &FrameMemory,
687) {
688    let node = &task_info.node;
689    let op = &task_info.op;
690    let mut textures = BatchTextures::empty();
691
692    // We have to undo the inflate here as the inflated target rect is meant to
693    // have a blank border
694    let target_rect = target_task
695        .get_target_rect()
696        .inner_box(DeviceIntSideOffsets::new(node.inflate as i32, node.inflate as i32, node.inflate as i32, node.inflate as i32))
697        .to_f32();
698
699    let mut instance = SVGFEFilterInstance {
700        target_rect,
701        input_1_content_scale_and_offset: [0.0; 4],
702        input_2_content_scale_and_offset: [0.0; 4],
703        input_1_task_address: RenderTaskId::INVALID.into(),
704        input_2_task_address: RenderTaskId::INVALID.into(),
705        kind: 0,
706        input_count: node.inputs.len() as u16,
707        extra_data_address: extra_data_address.unwrap_or(GpuBufferAddress::INVALID).as_int(),
708    };
709
710    // Must match FILTER_* in cs_svg_filter_node.glsl
711    instance.kind = match op {
712        // Identity does not modify color, no linear case
713        FilterGraphOp::SVGFEIdentity => 0,
714        // SourceGraphic does not have its own shader mode, it uses Identity.
715        FilterGraphOp::SVGFESourceGraphic => 0,
716        // SourceAlpha does not have its own shader mode, it uses ToAlpha.
717        FilterGraphOp::SVGFESourceAlpha => 4,
718        // Opacity scales the entire rgba color, so it does not need a linear
719        // case as the rgb / a ratio does not change (sRGB is a curve on the RGB
720        // before alpha multiply, not after)
721        FilterGraphOp::SVGFEOpacity{..} => 2,
722        FilterGraphOp::SVGFEToAlpha => 4,
723        FilterGraphOp::SVGFEBlendColor => {match node.linear {false => 6, true => 7}},
724        FilterGraphOp::SVGFEBlendColorBurn => {match node.linear {false => 8, true => 9}},
725        FilterGraphOp::SVGFEBlendColorDodge => {match node.linear {false => 10, true => 11}},
726        FilterGraphOp::SVGFEBlendDarken => {match node.linear {false => 12, true => 13}},
727        FilterGraphOp::SVGFEBlendDifference => {match node.linear {false => 14, true => 15}},
728        FilterGraphOp::SVGFEBlendExclusion => {match node.linear {false => 16, true => 17}},
729        FilterGraphOp::SVGFEBlendHardLight => {match node.linear {false => 18, true => 19}},
730        FilterGraphOp::SVGFEBlendHue => {match node.linear {false => 20, true => 21}},
731        FilterGraphOp::SVGFEBlendLighten => {match node.linear {false => 22, true => 23}},
732        FilterGraphOp::SVGFEBlendLuminosity => {match node.linear {false => 24, true => 25}},
733        FilterGraphOp::SVGFEBlendMultiply => {match node.linear {false => 26, true => 27}},
734        FilterGraphOp::SVGFEBlendNormal => {match node.linear {false => 28, true => 29}},
735        FilterGraphOp::SVGFEBlendOverlay => {match node.linear {false => 30, true => 31}},
736        FilterGraphOp::SVGFEBlendSaturation => {match node.linear {false => 32, true => 33}},
737        FilterGraphOp::SVGFEBlendScreen => {match node.linear {false => 34, true => 35}},
738        FilterGraphOp::SVGFEBlendSoftLight => {match node.linear {false => 36, true => 37}},
739        FilterGraphOp::SVGFEColorMatrix{..} => {match node.linear {false => 38, true => 39}},
740        FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
741        FilterGraphOp::SVGFEComponentTransferInterned{..} => {match node.linear {false => 40, true => 41}},
742        FilterGraphOp::SVGFECompositeArithmetic{..} => {match node.linear {false => 42, true => 43}},
743        FilterGraphOp::SVGFECompositeATop => {match node.linear {false => 44, true => 45}},
744        FilterGraphOp::SVGFECompositeIn => {match node.linear {false => 46, true => 47}},
745        FilterGraphOp::SVGFECompositeLighter => {match node.linear {false => 48, true => 49}},
746        FilterGraphOp::SVGFECompositeOut => {match node.linear {false => 50, true => 51}},
747        FilterGraphOp::SVGFECompositeOver => {match node.linear {false => 52, true => 53}},
748        FilterGraphOp::SVGFECompositeXOR => {match node.linear {false => 54, true => 55}},
749        FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {match node.linear {false => 56, true => 57}},
750        FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {match node.linear {false => 58, true => 59}},
751        FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {match node.linear {false => 60, true => 61}},
752        FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {match node.linear {false => 62, true => 63}},
753        FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {match node.linear {false => 64, true => 65}},
754        FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {match node.linear {false => 66, true => 67}},
755        FilterGraphOp::SVGFEDisplacementMap{..} => {match node.linear {false => 68, true => 69}},
756        FilterGraphOp::SVGFEDropShadow{..} => {match node.linear {false => 70, true => 71}},
757        // feFlood takes an sRGB color and does no math on it, no linear case
758        FilterGraphOp::SVGFEFlood{..} => 72,
759        FilterGraphOp::SVGFEGaussianBlur{..} => {match node.linear {false => 74, true => 75}},
760        // feImage does not meaningfully modify the color of its input, though a
761        // case could be made for gamma-correct image scaling, that's a bit out
762        // of scope for now
763        FilterGraphOp::SVGFEImage{..} => 76,
764        FilterGraphOp::SVGFEMorphologyDilate{..} => {match node.linear {false => 80, true => 81}},
765        FilterGraphOp::SVGFEMorphologyErode{..} => {match node.linear {false => 82, true => 83}},
766        FilterGraphOp::SVGFESpecularLightingDistant{..} => {match node.linear {false => 86, true => 87}},
767        FilterGraphOp::SVGFESpecularLightingPoint{..} => {match node.linear {false => 88, true => 89}},
768        FilterGraphOp::SVGFESpecularLightingSpot{..} => {match node.linear {false => 90, true => 91}},
769        // feTile does not modify color, no linear case
770        FilterGraphOp::SVGFETile => 92,
771        FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {match node.linear {false => 94, true => 95}},
772        FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {match node.linear {false => 96, true => 97}},
773        FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {match node.linear {false => 98, true => 99}},
774        FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {match node.linear {false => 100, true => 101}},
775    };
776
777    // This is a bit of an ugly way to do this, but avoids code duplication.
778    let mut resolve_input = |index: usize, src_task: Option<RenderTaskId>| -> (RenderTaskAddress, [f32; 4]) {
779        let mut src_task_id = RenderTaskId::INVALID;
780        let mut resolved_scale_and_offset: [f32; 4] = [0.0; 4];
781        if let Some(input) = node.inputs.get(index) {
782            src_task_id = src_task.unwrap();
783            let src_task = &render_tasks[src_task_id];
784
785            textures.input.colors[index] = src_task.get_texture_source();
786            let src_task_size = src_task.location.size();
787            let src_scale_x = (src_task_size.width as f32 - input.inflate as f32 * 2.0) / input.subregion.width();
788            let src_scale_y = (src_task_size.height as f32 - input.inflate as f32 * 2.0) / input.subregion.height();
789            let scale_x = src_scale_x * node.subregion.width();
790            let scale_y = src_scale_y * node.subregion.height();
791            let offset_x = src_scale_x * (node.subregion.min.x - input.subregion.min.x) + input.inflate as f32;
792            let offset_y = src_scale_y * (node.subregion.min.y - input.subregion.min.y) + input.inflate as f32;
793            resolved_scale_and_offset = [
794                scale_x,
795                scale_y,
796                offset_x,
797                offset_y];
798        }
799        let address: RenderTaskAddress = src_task_id.into();
800        (address, resolved_scale_and_offset)
801    };
802    (instance.input_1_task_address, instance.input_1_content_scale_and_offset) = resolve_input(0, input_1_task);
803    (instance.input_2_task_address, instance.input_2_content_scale_and_offset) = resolve_input(1, input_2_task);
804
805    // Additional instance modifications for certain filters
806    match op {
807        FilterGraphOp::SVGFEOpacity { valuebinding: _, value } => {
808            // opacity only has one input so we can use the other
809            // components to store the opacity value
810            instance.input_2_content_scale_and_offset = [*value, 0.0, 0.0, 0.0];
811        },
812        FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } |
813        FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => {
814            // morphology filters only use one input, so we use the
815            // second offset coord to store the radius values.
816            instance.input_2_content_scale_and_offset = [*radius_x, *radius_y, 0.0, 0.0];
817        },
818        FilterGraphOp::SVGFEFlood { color } => {
819            // flood filters don't use inputs, so we store color here.
820            // We can't do the same trick on DropShadow because it does have two
821            // inputs.
822            instance.input_2_content_scale_and_offset = [color.r, color.g, color.b, color.a];
823        },
824        _ => {},
825    }
826
827    for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
828        if let Some(combined_textures) = batch_textures.combine_textures(textures) {
829            batch.push(instance);
830            // Update the batch textures to the newly combined batch textures
831            *batch_textures = combined_textures;
832            // is this really the intended behavior?
833            return;
834        }
835    }
836
837    let mut vec = memory.new_vec();
838    vec.push(instance);
839
840    instances.push((textures, vec));
841}
842
843// Information required to do a blit from a source to a target.
844#[cfg_attr(feature = "capture", derive(Serialize))]
845#[cfg_attr(feature = "replay", derive(Deserialize))]
846pub struct BlitJob {
847    pub source: RenderTaskId,
848    // Normalized region within the source task to blit from
849    pub source_rect: DeviceIntRect,
850    pub target_rect: DeviceIntRect,
851}
852
853#[cfg_attr(feature = "capture", derive(Serialize))]
854#[cfg_attr(feature = "replay", derive(Deserialize))]
855#[repr(C)]
856#[derive(Clone, Debug)]
857pub struct LineDecorationJob {
858    pub task_rect: DeviceRect,
859    pub local_size: LayoutSize,
860    pub wavy_line_thickness: f32,
861    pub style: i32,
862    pub axis_select: f32,
863}
864
865fn add_rect_clip_task_to_batch(
866    task: &RectangleClipSubTask,
867    target_rect: &DeviceIntRect,
868    masked_task_address: RenderTaskAddress,
869    memory: &FrameMemory,
870    render_tasks: &RenderTaskGraph,
871    gpu_buffers: &mut GpuBufferBuilder,
872    results: &mut ClipMaskInstanceList,
873) {
874    quad::add_to_batch(
875        PatternKind::Mask,
876        PatternShaderInput::default(),
877        masked_task_address,
878        task.quad_transform_id,
879        task.quad_address,
880        task.quad_flags,
881        EdgeMask::empty(),
882        INVALID_SEGMENT_INDEX as u8,
883        RenderTaskId::INVALID,
884        ZBufferId(0),
885        BlendMode::None, // This parameter is ignored.
886        render_tasks,
887        gpu_buffers,
888        |_, prim| {
889            let instance = MaskInstance {
890                prim,
891                clip_transform_id: task.clip_transform_id,
892                clip_address: task.clip_address.as_int(),
893                clip_space: task.clip_space.as_int(),
894                unused: 0,
895            };
896
897            if task.needs_scissor_rect {
898                if task.rounded_rect_fast_path {
899                    results.mask_instances_fast_with_scissor
900                            .entry(*target_rect)
901                            .or_insert_with(|| memory.new_vec())
902                            .push(instance);
903                } else {
904                    results.mask_instances_slow_with_scissor
905                            .entry(*target_rect)
906                            .or_insert_with(|| memory.new_vec())
907                            .push(instance);
908                }
909            } else {
910                if task.rounded_rect_fast_path {
911                    results.mask_instances_fast.push(instance);
912                } else {
913                    results.mask_instances_slow.push(instance);
914                }
915            }
916        }
917    );
918}
919
920fn add_image_clip_task_to_batch(
921    task: &ImageClipSubTask,
922    target_rect: &DeviceIntRect,
923    masked_task_address: RenderTaskAddress,
924    memory: &FrameMemory,
925    render_tasks: &RenderTaskGraph,
926    gpu_buffers: &mut GpuBufferBuilder,
927    results: &mut ClipMaskInstanceList,
928) {
929    // A current oddity of the quads infrastructure is that image sources are encoded in
930    // quad segment data so we need to have at least one segment if we have a uv rect as
931    // input.
932    let segment_index = 0;
933
934    let pattern = Pattern::texture(task.src_task, false);
935
936    quad::add_to_batch(
937        pattern.kind,
938        pattern.shader_input,
939        masked_task_address,
940        task.quad_transform_id,
941        task.quad_address,
942        task.quad_flags,
943        EdgeMask::empty(),
944        segment_index,
945        task.src_task,
946        ZBufferId(0),
947        BlendMode::None, // This parameter is ignored.
948        render_tasks,
949        gpu_buffers,
950        |_, prim| {
951            let texture = render_tasks
952                .resolve_texture(task.src_task)
953                .expect("bug: texture not found for tile");
954
955            if task.needs_scissor_rect {
956                results
957                    .image_mask_instances_with_scissor
958                    .entry((*target_rect, texture))
959                    .or_insert_with(|| memory.new_vec())
960                    .push(prim);
961            } else {
962                results
963                    .image_mask_instances
964                    .entry(texture)
965                    .or_insert_with(|| memory.new_vec())
966                    .push(prim);
967            }
968        }
969    );
970}