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 use api::{CompositeOperator, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
6 use api::{LineStyle, LineOrientation, ClipMode, MixBlendMode, ColorF, ColorSpace};
7 use api::units::*;
8 use crate::batch::BatchFilter;
9 use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange};
10 use crate::spatial_tree::SpatialNodeIndex;
11 use crate::filterdata::SFilterData;
12 use crate::frame_builder::FrameBuilderConfig;
13 use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
14 use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind};
15 use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle};
16 use crate::picture::{ResolvedSurfaceTexture, SurfaceInfo};
17 use crate::prim_store::{ClipData, PictureIndex};
18 use crate::prim_store::gradient::{
19 FastLinearGradientTask, RadialGradientTask,
20 ConicGradientTask, LinearGradientTask,
21 };
22 use crate::resource_cache::{ResourceCache, ImageRequest};
23 use std::{usize, f32, i32, u32};
24 use crate::render_target::RenderTargetKind;
25 use crate::render_task_graph::{PassId, RenderTaskId, RenderTaskGraphBuilder};
26 use crate::render_task_cache::{RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent};
27 use smallvec::SmallVec;
28
29 const FLOATS_PER_RENDER_TASK_INFO: usize = 8;
30 pub const MAX_RENDER_TASK_SIZE: i32 = 16384;
31 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
32 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8;
33
render_task_sanity_check(size: &DeviceIntSize)34 fn render_task_sanity_check(size: &DeviceIntSize) {
35 if size.width > MAX_RENDER_TASK_SIZE ||
36 size.height > MAX_RENDER_TASK_SIZE {
37 error!("Attempting to create a render task of size {}x{}", size.width, size.height);
38 panic!();
39 }
40 }
41
42 #[derive(Debug, Copy, Clone, PartialEq)]
43 #[repr(C)]
44 #[cfg_attr(feature = "capture", derive(Serialize))]
45 #[cfg_attr(feature = "replay", derive(Deserialize))]
46 pub struct RenderTaskAddress(pub u16);
47
48 impl Into<RenderTaskAddress> for RenderTaskId {
into(self) -> RenderTaskAddress49 fn into(self) -> RenderTaskAddress {
50 RenderTaskAddress(self.index as u16)
51 }
52 }
53
54 /// A render task location that targets a persistent output buffer which
55 /// will be retained over multiple frames.
56 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
57 #[cfg_attr(feature = "capture", derive(Serialize))]
58 #[cfg_attr(feature = "replay", derive(Deserialize))]
59 pub enum StaticRenderTaskSurface {
60 /// The output of the `RenderTask` will be persisted beyond this frame, and
61 /// thus should be drawn into the `TextureCache`.
62 TextureCache {
63 /// Which texture in the texture cache should be drawn into.
64 texture: CacheTextureId,
65 /// What format this texture cache surface is
66 target_kind: RenderTargetKind,
67 },
68 /// Only used as a source for render tasks, can be any texture including an
69 /// external one.
70 ReadOnly {
71 source: TextureSource,
72 },
73 /// This render task will be drawn to a picture cache texture that is
74 /// persisted between both frames and scenes, if the content remains valid.
75 PictureCache {
76 /// Describes either a WR texture or a native OS compositor target
77 surface: ResolvedSurfaceTexture,
78 },
79 }
80
81 /// Identifies the output buffer location for a given `RenderTask`.
82 #[derive(Clone, Debug)]
83 #[cfg_attr(feature = "capture", derive(Serialize))]
84 #[cfg_attr(feature = "replay", derive(Deserialize))]
85 pub enum RenderTaskLocation {
86 // Towards the beginning of the frame, most task locations are typically not
87 // known yet, in which case they are set to one of the following variants:
88
89 /// A dynamic task that has not yet been allocated a texture and rect.
90 Unallocated {
91 /// Requested size of this render task
92 size: DeviceIntSize,
93 },
94 /// Will be replaced by a Static location after the texture cache update.
95 CacheRequest {
96 size: DeviceIntSize,
97 },
98
99 // Before batching begins, we expect that locations have been resolved to
100 // one of the following variants:
101
102 /// The `RenderTask` should be drawn to a target provided by the atlas
103 /// allocator. This is the most common case.
104 Dynamic {
105 /// Texture that this task was allocated to render on
106 texture_id: CacheTextureId,
107 /// Rectangle in the texture this task occupies
108 rect: DeviceIntRect,
109 },
110 /// A task that is output to a persistent / retained target.
111 Static {
112 /// Target to draw to
113 surface: StaticRenderTaskSurface,
114 /// Rectangle in the texture this task occupies
115 rect: DeviceIntRect,
116 },
117 }
118
119 impl RenderTaskLocation {
120 /// Returns true if this is a dynamic location.
is_dynamic(&self) -> bool121 pub fn is_dynamic(&self) -> bool {
122 match *self {
123 RenderTaskLocation::Dynamic { .. } => true,
124 _ => false,
125 }
126 }
127
size(&self) -> DeviceIntSize128 pub fn size(&self) -> DeviceIntSize {
129 match self {
130 RenderTaskLocation::Unallocated { size } => *size,
131 RenderTaskLocation::Dynamic { rect, .. } => rect.size(),
132 RenderTaskLocation::Static { rect, .. } => rect.size(),
133 RenderTaskLocation::CacheRequest { size } => *size,
134 }
135 }
136 }
137
138 #[derive(Debug)]
139 #[cfg_attr(feature = "capture", derive(Serialize))]
140 #[cfg_attr(feature = "replay", derive(Deserialize))]
141 pub struct CachedTask {
142 pub target_kind: RenderTargetKind,
143 }
144
145 #[derive(Debug)]
146 #[cfg_attr(feature = "capture", derive(Serialize))]
147 #[cfg_attr(feature = "replay", derive(Deserialize))]
148 pub struct CacheMaskTask {
149 pub actual_rect: DeviceRect,
150 pub root_spatial_node_index: SpatialNodeIndex,
151 pub clip_node_range: ClipNodeRange,
152 pub device_pixel_scale: DevicePixelScale,
153 pub clear_to_one: bool,
154 }
155
156 #[derive(Debug)]
157 #[cfg_attr(feature = "capture", derive(Serialize))]
158 #[cfg_attr(feature = "replay", derive(Deserialize))]
159 pub struct ClipRegionTask {
160 pub local_pos: LayoutPoint,
161 pub device_pixel_scale: DevicePixelScale,
162 pub clip_data: ClipData,
163 pub clear_to_one: bool,
164 }
165
166 #[cfg_attr(feature = "capture", derive(Serialize))]
167 #[cfg_attr(feature = "replay", derive(Deserialize))]
168 pub struct PictureTask {
169 pub pic_index: PictureIndex,
170 pub can_merge: bool,
171 pub content_origin: DevicePoint,
172 pub surface_spatial_node_index: SpatialNodeIndex,
173 pub device_pixel_scale: DevicePixelScale,
174 pub batch_filter: Option<BatchFilter>,
175 pub scissor_rect: Option<DeviceIntRect>,
176 pub valid_rect: Option<DeviceIntRect>,
177 }
178
179 #[derive(Debug)]
180 #[cfg_attr(feature = "capture", derive(Serialize))]
181 #[cfg_attr(feature = "replay", derive(Deserialize))]
182 pub struct BlurTask {
183 pub blur_std_deviation: f32,
184 pub target_kind: RenderTargetKind,
185 pub blur_region: DeviceIntSize,
186 }
187
188 impl BlurTask {
189 // In order to do the blur down-scaling passes without introducing errors, we need the
190 // source of each down-scale pass to be a multuple of two. If need be, this inflates
191 // the source size so that each down-scale pass will sample correctly.
adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize192 pub fn adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize {
193 let mut adjusted_size = original_size;
194 let mut scale_factor = 1.0;
195 while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION {
196 if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE as f32 ||
197 adjusted_size.height < MIN_DOWNSCALING_RT_SIZE as f32 {
198 break;
199 }
200 std_dev = std_dev * 0.5;
201 scale_factor *= 2.0;
202 adjusted_size = (original_size.to_f32() / scale_factor).ceil();
203 }
204
205 adjusted_size * scale_factor
206 }
207 }
208
209 #[derive(Debug)]
210 #[cfg_attr(feature = "capture", derive(Serialize))]
211 #[cfg_attr(feature = "replay", derive(Deserialize))]
212 pub struct ScalingTask {
213 pub target_kind: RenderTargetKind,
214 pub padding: DeviceIntSideOffsets,
215 }
216
217 #[derive(Debug)]
218 #[cfg_attr(feature = "capture", derive(Serialize))]
219 #[cfg_attr(feature = "replay", derive(Deserialize))]
220 pub struct BorderTask {
221 pub instances: Vec<BorderInstance>,
222 }
223
224 #[derive(Debug)]
225 #[cfg_attr(feature = "capture", derive(Serialize))]
226 #[cfg_attr(feature = "replay", derive(Deserialize))]
227 pub struct BlitTask {
228 pub source: RenderTaskId,
229 }
230
231 #[derive(Debug)]
232 #[cfg_attr(feature = "capture", derive(Serialize))]
233 #[cfg_attr(feature = "replay", derive(Deserialize))]
234 pub struct LineDecorationTask {
235 pub wavy_line_thickness: f32,
236 pub style: LineStyle,
237 pub orientation: LineOrientation,
238 pub local_size: LayoutSize,
239 }
240
241 #[derive(Debug)]
242 #[cfg_attr(feature = "capture", derive(Serialize))]
243 #[cfg_attr(feature = "replay", derive(Deserialize))]
244 pub enum SvgFilterInfo {
245 Blend(MixBlendMode),
246 Flood(ColorF),
247 LinearToSrgb,
248 SrgbToLinear,
249 Opacity(f32),
250 ColorMatrix(Box<[f32; 20]>),
251 DropShadow(ColorF),
252 Offset(DeviceVector2D),
253 ComponentTransfer(SFilterData),
254 Composite(CompositeOperator),
255 // TODO: This is used as a hack to ensure that a blur task's input is always in the blur's previous pass.
256 Identity,
257 }
258
259 #[derive(Debug)]
260 #[cfg_attr(feature = "capture", derive(Serialize))]
261 #[cfg_attr(feature = "replay", derive(Deserialize))]
262 pub struct SvgFilterTask {
263 pub info: SvgFilterInfo,
264 pub extra_gpu_cache_handle: Option<GpuCacheHandle>,
265 }
266
267 #[cfg_attr(feature = "capture", derive(Serialize))]
268 #[cfg_attr(feature = "replay", derive(Deserialize))]
269 pub struct ReadbackTask {
270 // The offset of the rect that needs to be read back, in the
271 // device space of the surface that will be read back from.
272 // If this is None, there is no readback surface available
273 // and this is a dummy (empty) readback.
274 pub readback_origin: Option<DevicePoint>,
275 }
276
277 #[derive(Debug)]
278 #[cfg_attr(feature = "capture", derive(Serialize))]
279 #[cfg_attr(feature = "replay", derive(Deserialize))]
280 pub struct RenderTaskData {
281 pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
282 }
283
284 #[cfg_attr(feature = "capture", derive(Serialize))]
285 #[cfg_attr(feature = "replay", derive(Deserialize))]
286 pub enum RenderTaskKind {
287 Image(ImageRequest),
288 Cached(CachedTask),
289 Picture(PictureTask),
290 CacheMask(CacheMaskTask),
291 ClipRegion(ClipRegionTask),
292 VerticalBlur(BlurTask),
293 HorizontalBlur(BlurTask),
294 Readback(ReadbackTask),
295 Scaling(ScalingTask),
296 Blit(BlitTask),
297 Border(BorderTask),
298 LineDecoration(LineDecorationTask),
299 FastLinearGradient(FastLinearGradientTask),
300 LinearGradient(LinearGradientTask),
301 RadialGradient(RadialGradientTask),
302 ConicGradient(ConicGradientTask),
303 SvgFilter(SvgFilterTask),
304 #[cfg(test)]
305 Test(RenderTargetKind),
306 }
307
308 impl RenderTaskKind {
is_a_rendering_operation(&self) -> bool309 pub fn is_a_rendering_operation(&self) -> bool {
310 match self {
311 &RenderTaskKind::Image(..) => false,
312 &RenderTaskKind::Cached(..) => false,
313 _ => true,
314 }
315 }
316
as_str(&self) -> &'static str317 pub fn as_str(&self) -> &'static str {
318 match *self {
319 RenderTaskKind::Image(..) => "Image",
320 RenderTaskKind::Cached(..) => "Cached",
321 RenderTaskKind::Picture(..) => "Picture",
322 RenderTaskKind::CacheMask(..) => "CacheMask",
323 RenderTaskKind::ClipRegion(..) => "ClipRegion",
324 RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
325 RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
326 RenderTaskKind::Readback(..) => "Readback",
327 RenderTaskKind::Scaling(..) => "Scaling",
328 RenderTaskKind::Blit(..) => "Blit",
329 RenderTaskKind::Border(..) => "Border",
330 RenderTaskKind::LineDecoration(..) => "LineDecoration",
331 RenderTaskKind::FastLinearGradient(..) => "FastLinearGradient",
332 RenderTaskKind::LinearGradient(..) => "LinearGradient",
333 RenderTaskKind::RadialGradient(..) => "RadialGradient",
334 RenderTaskKind::ConicGradient(..) => "ConicGradient",
335 RenderTaskKind::SvgFilter(..) => "SvgFilter",
336 #[cfg(test)]
337 RenderTaskKind::Test(..) => "Test",
338 }
339 }
340
target_kind(&self) -> RenderTargetKind341 pub fn target_kind(&self) -> RenderTargetKind {
342 match *self {
343 RenderTaskKind::Image(..) |
344 RenderTaskKind::LineDecoration(..) |
345 RenderTaskKind::Readback(..) |
346 RenderTaskKind::Border(..) |
347 RenderTaskKind::FastLinearGradient(..) |
348 RenderTaskKind::LinearGradient(..) |
349 RenderTaskKind::RadialGradient(..) |
350 RenderTaskKind::ConicGradient(..) |
351 RenderTaskKind::Picture(..) |
352 RenderTaskKind::Blit(..) |
353 RenderTaskKind::SvgFilter(..) => {
354 RenderTargetKind::Color
355 }
356
357 RenderTaskKind::ClipRegion(..) |
358 RenderTaskKind::CacheMask(..) => {
359 RenderTargetKind::Alpha
360 }
361
362 RenderTaskKind::VerticalBlur(ref task_info) |
363 RenderTaskKind::HorizontalBlur(ref task_info) => {
364 task_info.target_kind
365 }
366
367 RenderTaskKind::Scaling(ref task_info) => {
368 task_info.target_kind
369 }
370
371 RenderTaskKind::Cached(ref task_info) => {
372 task_info.target_kind
373 }
374
375 #[cfg(test)]
376 RenderTaskKind::Test(kind) => kind,
377 }
378 }
379
new_picture( size: DeviceIntSize, unclipped_size: DeviceSize, pic_index: PictureIndex, content_origin: DevicePoint, surface_spatial_node_index: SpatialNodeIndex, device_pixel_scale: DevicePixelScale, batch_filter: Option<BatchFilter>, scissor_rect: Option<DeviceIntRect>, valid_rect: Option<DeviceIntRect>, ) -> Self380 pub fn new_picture(
381 size: DeviceIntSize,
382 unclipped_size: DeviceSize,
383 pic_index: PictureIndex,
384 content_origin: DevicePoint,
385 surface_spatial_node_index: SpatialNodeIndex,
386 device_pixel_scale: DevicePixelScale,
387 batch_filter: Option<BatchFilter>,
388 scissor_rect: Option<DeviceIntRect>,
389 valid_rect: Option<DeviceIntRect>,
390 ) -> Self {
391 render_task_sanity_check(&size);
392
393 let can_merge = size.width as f32 >= unclipped_size.width &&
394 size.height as f32 >= unclipped_size.height;
395
396 RenderTaskKind::Picture(PictureTask {
397 pic_index,
398 content_origin,
399 can_merge,
400 surface_spatial_node_index,
401 device_pixel_scale,
402 batch_filter,
403 scissor_rect,
404 valid_rect,
405 })
406 }
407
new_readback( readback_origin: Option<DevicePoint>, ) -> Self408 pub fn new_readback(
409 readback_origin: Option<DevicePoint>,
410 ) -> Self {
411 RenderTaskKind::Readback(
412 ReadbackTask {
413 readback_origin,
414 }
415 )
416 }
417
new_line_decoration( style: LineStyle, orientation: LineOrientation, wavy_line_thickness: f32, local_size: LayoutSize, ) -> Self418 pub fn new_line_decoration(
419 style: LineStyle,
420 orientation: LineOrientation,
421 wavy_line_thickness: f32,
422 local_size: LayoutSize,
423 ) -> Self {
424 RenderTaskKind::LineDecoration(LineDecorationTask {
425 style,
426 orientation,
427 wavy_line_thickness,
428 local_size,
429 })
430 }
431
new_border_segment( instances: Vec<BorderInstance>, ) -> Self432 pub fn new_border_segment(
433 instances: Vec<BorderInstance>,
434 ) -> Self {
435 RenderTaskKind::Border(BorderTask {
436 instances,
437 })
438 }
439
new_rounded_rect_mask( local_pos: LayoutPoint, clip_data: ClipData, device_pixel_scale: DevicePixelScale, fb_config: &FrameBuilderConfig, ) -> Self440 pub fn new_rounded_rect_mask(
441 local_pos: LayoutPoint,
442 clip_data: ClipData,
443 device_pixel_scale: DevicePixelScale,
444 fb_config: &FrameBuilderConfig,
445 ) -> Self {
446 RenderTaskKind::ClipRegion(ClipRegionTask {
447 local_pos,
448 device_pixel_scale,
449 clip_data,
450 clear_to_one: fb_config.gpu_supports_fast_clears,
451 })
452 }
453
new_mask( outer_rect: DeviceRect, clip_node_range: ClipNodeRange, root_spatial_node_index: SpatialNodeIndex, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, rg_builder: &mut RenderTaskGraphBuilder, clip_data_store: &mut ClipDataStore, device_pixel_scale: DevicePixelScale, fb_config: &FrameBuilderConfig, surfaces: &[SurfaceInfo], ) -> RenderTaskId454 pub fn new_mask(
455 outer_rect: DeviceRect,
456 clip_node_range: ClipNodeRange,
457 root_spatial_node_index: SpatialNodeIndex,
458 clip_store: &mut ClipStore,
459 gpu_cache: &mut GpuCache,
460 resource_cache: &mut ResourceCache,
461 rg_builder: &mut RenderTaskGraphBuilder,
462 clip_data_store: &mut ClipDataStore,
463 device_pixel_scale: DevicePixelScale,
464 fb_config: &FrameBuilderConfig,
465 surfaces: &[SurfaceInfo],
466 ) -> RenderTaskId {
467 // Step through the clip sources that make up this mask. If we find
468 // any box-shadow clip sources, request that image from the render
469 // task cache. This allows the blurred box-shadow rect to be cached
470 // in the texture cache across frames.
471 // TODO(gw): Consider moving this logic outside this function, especially
472 // as we add more clip sources that depend on render tasks.
473 // TODO(gw): If this ever shows up in a profile, we could pre-calculate
474 // whether a ClipSources contains any box-shadows and skip
475 // this iteration for the majority of cases.
476 let task_size = outer_rect.size().to_i32();
477
478 // If we have a potentially tiled clip mask, clear the mask area first. Otherwise,
479 // the first (primary) clip mask will overwrite all the clip mask pixels with
480 // blending disabled to set to the initial value.
481
482 let clip_task_id = rg_builder.add().init(
483 RenderTask::new_dynamic(
484 task_size,
485 RenderTaskKind::CacheMask(CacheMaskTask {
486 actual_rect: outer_rect,
487 clip_node_range,
488 root_spatial_node_index,
489 device_pixel_scale,
490 clear_to_one: fb_config.gpu_supports_fast_clears,
491 }),
492 )
493 );
494
495 for i in 0 .. clip_node_range.count {
496 let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
497 let clip_node = &mut clip_data_store[clip_instance.handle];
498 match clip_node.item.kind {
499 ClipItemKind::BoxShadow { ref mut source } => {
500 let (cache_size, cache_key) = source.cache_key
501 .as_ref()
502 .expect("bug: no cache key set")
503 .clone();
504 let blur_radius_dp = cache_key.blur_radius_dp as f32;
505 let device_pixel_scale = DevicePixelScale::new(cache_key.device_pixel_scale.to_f32_px());
506
507 // Request a cacheable render task with a blurred, minimal
508 // sized box-shadow rect.
509 source.render_task = Some(resource_cache.request_render_task(
510 RenderTaskCacheKey {
511 size: cache_size,
512 kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
513 },
514 gpu_cache,
515 rg_builder,
516 None,
517 false,
518 RenderTaskParent::RenderTask(clip_task_id),
519 surfaces,
520 |rg_builder| {
521 let clip_data = ClipData::rounded_rect(
522 source.minimal_shadow_rect.size(),
523 &source.shadow_radius,
524 ClipMode::Clip,
525 );
526
527 // Draw the rounded rect.
528 let mask_task_id = rg_builder.add().init(RenderTask::new_dynamic(
529 cache_size,
530 RenderTaskKind::new_rounded_rect_mask(
531 source.minimal_shadow_rect.min,
532 clip_data,
533 device_pixel_scale,
534 fb_config,
535 ),
536 ));
537
538 // Blur it
539 RenderTask::new_blur(
540 DeviceSize::new(blur_radius_dp, blur_radius_dp),
541 mask_task_id,
542 rg_builder,
543 RenderTargetKind::Alpha,
544 None,
545 cache_size,
546 )
547 }
548 ));
549 }
550 ClipItemKind::Rectangle { .. } |
551 ClipItemKind::RoundedRectangle { .. } |
552 ClipItemKind::Image { .. } => {}
553 }
554 }
555
556 clip_task_id
557 }
558
559 // Write (up to) 8 floats of data specific to the type
560 // of render task that is provided to the GPU shaders
561 // via a vertex texture.
write_task_data( &self, target_rect: DeviceIntRect, ) -> RenderTaskData562 pub fn write_task_data(
563 &self,
564 target_rect: DeviceIntRect,
565 ) -> RenderTaskData {
566 // NOTE: The ordering and layout of these structures are
567 // required to match both the GPU structures declared
568 // in prim_shared.glsl, and also the uses in submit_batch()
569 // in renderer.rs.
570 // TODO(gw): Maybe there's a way to make this stuff a bit
571 // more type-safe. Although, it will always need
572 // to be kept in sync with the GLSL code anyway.
573
574 let data = match self {
575 RenderTaskKind::Picture(ref task) => {
576 // Note: has to match `PICTURE_TYPE_*` in shaders
577 [
578 task.device_pixel_scale.0,
579 task.content_origin.x,
580 task.content_origin.y,
581 0.0,
582 ]
583 }
584 RenderTaskKind::CacheMask(ref task) => {
585 [
586 task.device_pixel_scale.0,
587 task.actual_rect.min.x,
588 task.actual_rect.min.y,
589 0.0,
590 ]
591 }
592 RenderTaskKind::ClipRegion(ref task) => {
593 [
594 task.device_pixel_scale.0,
595 0.0,
596 0.0,
597 0.0,
598 ]
599 }
600 RenderTaskKind::VerticalBlur(ref task) |
601 RenderTaskKind::HorizontalBlur(ref task) => {
602 [
603 task.blur_std_deviation,
604 task.blur_region.width as f32,
605 task.blur_region.height as f32,
606 0.0,
607 ]
608 }
609 RenderTaskKind::Image(..) |
610 RenderTaskKind::Cached(..) |
611 RenderTaskKind::Readback(..) |
612 RenderTaskKind::Scaling(..) |
613 RenderTaskKind::Border(..) |
614 RenderTaskKind::LineDecoration(..) |
615 RenderTaskKind::FastLinearGradient(..) |
616 RenderTaskKind::LinearGradient(..) |
617 RenderTaskKind::RadialGradient(..) |
618 RenderTaskKind::ConicGradient(..) |
619 RenderTaskKind::Blit(..) => {
620 [0.0; 4]
621 }
622
623
624 RenderTaskKind::SvgFilter(ref task) => {
625 match task.info {
626 SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0, 0.0],
627 SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0, 0.0],
628 _ => [0.0; 4]
629 }
630 }
631
632 #[cfg(test)]
633 RenderTaskKind::Test(..) => {
634 [0.0; 4]
635 }
636 };
637
638 RenderTaskData {
639 data: [
640 target_rect.min.x as f32,
641 target_rect.min.y as f32,
642 target_rect.max.x as f32,
643 target_rect.max.y as f32,
644 data[0],
645 data[1],
646 data[2],
647 data[3],
648 ]
649 }
650 }
651
write_gpu_blocks( &mut self, gpu_cache: &mut GpuCache, )652 pub fn write_gpu_blocks(
653 &mut self,
654 gpu_cache: &mut GpuCache,
655 ) {
656 if let RenderTaskKind::SvgFilter(ref mut filter_task) = self {
657 match filter_task.info {
658 SvgFilterInfo::ColorMatrix(ref matrix) => {
659 let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
660 if let Some(mut request) = gpu_cache.request(handle) {
661 for i in 0..5 {
662 request.push([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]);
663 }
664 }
665 }
666 SvgFilterInfo::DropShadow(color) |
667 SvgFilterInfo::Flood(color) => {
668 let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
669 if let Some(mut request) = gpu_cache.request(handle) {
670 request.push(color.to_array());
671 }
672 }
673 SvgFilterInfo::ComponentTransfer(ref data) => {
674 let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
675 if let Some(request) = gpu_cache.request(handle) {
676 data.update(request);
677 }
678 }
679 SvgFilterInfo::Composite(ref operator) => {
680 if let CompositeOperator::Arithmetic(k_vals) = operator {
681 let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
682 if let Some(mut request) = gpu_cache.request(handle) {
683 request.push(*k_vals);
684 }
685 }
686 }
687 _ => {},
688 }
689 }
690 }
691 }
692
693 /// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs,
694 /// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive
695 /// in a single frame.
696 pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>;
697
698 /// Since we only use it within a single primitive, the key only needs to contain the down-scaling level
699 /// and the blur std deviation.
700 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
701 pub enum BlurTaskKey {
702 DownScale(u32),
703 Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 },
704 }
705
706 impl BlurTaskKey {
downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self707 fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self {
708 // Quantise the std deviations and store it as integers to work around
709 // Eq and Hash's f32 allergy.
710 // The blur radius is rounded before RenderTask::new_blur so we don't need
711 // a lot of precision.
712 const QUANTIZATION_FACTOR: f32 = 1024.0;
713 let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32;
714 let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32;
715 BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y }
716 }
717 }
718
719 // The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that
720 // typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements
721 // avoids many tiny heap allocations in pages with a lot of text shadows and other
722 // types of render tasks.
723 pub type TaskDependencies = SmallVec<[RenderTaskId;2]>;
724
725 #[cfg_attr(feature = "capture", derive(Serialize))]
726 #[cfg_attr(feature = "replay", derive(Deserialize))]
727 pub struct RenderTask {
728 pub location: RenderTaskLocation,
729 pub children: TaskDependencies,
730 pub kind: RenderTaskKind,
731
732 // TODO(gw): These fields and perhaps others can become private once the
733 // frame_graph / render_task source files are unified / cleaned up.
734 pub free_after: PassId,
735 pub render_on: PassId,
736
737 /// The gpu cache handle for the render task's destination rect.
738 ///
739 /// Will be set to None if the render task is cached, in which case the texture cache
740 /// manages the handle.
741 pub uv_rect_handle: GpuCacheHandle,
742 pub cache_handle: Option<RenderTaskCacheEntryHandle>,
743 uv_rect_kind: UvRectKind,
744 }
745
746 impl RenderTask {
new( location: RenderTaskLocation, kind: RenderTaskKind, ) -> Self747 pub fn new(
748 location: RenderTaskLocation,
749 kind: RenderTaskKind,
750 ) -> Self {
751 render_task_sanity_check(&location.size());
752
753 RenderTask {
754 location,
755 children: TaskDependencies::new(),
756 kind,
757 free_after: PassId::MAX,
758 render_on: PassId::MIN,
759 uv_rect_handle: GpuCacheHandle::new(),
760 uv_rect_kind: UvRectKind::Rect,
761 cache_handle: None,
762 }
763 }
764
new_dynamic( size: DeviceIntSize, kind: RenderTaskKind, ) -> Self765 pub fn new_dynamic(
766 size: DeviceIntSize,
767 kind: RenderTaskKind,
768 ) -> Self {
769 RenderTask::new(
770 RenderTaskLocation::Unallocated { size },
771 kind,
772 )
773 }
774
with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self775 pub fn with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self {
776 self.uv_rect_kind = uv_rect_kind;
777 self
778 }
779
new_image( size: DeviceIntSize, request: ImageRequest, ) -> Self780 pub fn new_image(
781 size: DeviceIntSize,
782 request: ImageRequest,
783 ) -> Self {
784 // Note: this is a special constructor for image render tasks that does not
785 // do the render task size sanity check. This is because with SWGL we purposefully
786 // avoid tiling large images. There is no upload with SWGL so whatever was
787 // successfully allocated earlier will be what shaders read, regardless of the size
788 // and copying into tiles would only slow things down.
789 // As a result we can run into very large images being added to the frame graph
790 // (this is covered by a few reftests on the CI).
791
792 RenderTask {
793 location: RenderTaskLocation::CacheRequest { size, },
794 children: TaskDependencies::new(),
795 kind: RenderTaskKind::Image(request),
796 free_after: PassId::MAX,
797 render_on: PassId::MIN,
798 uv_rect_handle: GpuCacheHandle::new(),
799 uv_rect_kind: UvRectKind::Rect,
800 cache_handle: None,
801 }
802 }
803
804
805 #[cfg(test)]
new_test( location: RenderTaskLocation, target: RenderTargetKind, ) -> Self806 pub fn new_test(
807 location: RenderTaskLocation,
808 target: RenderTargetKind,
809 ) -> Self {
810 RenderTask {
811 location,
812 children: TaskDependencies::new(),
813 kind: RenderTaskKind::Test(target),
814 free_after: PassId::MAX,
815 render_on: PassId::MIN,
816 uv_rect_handle: GpuCacheHandle::new(),
817 uv_rect_kind: UvRectKind::Rect,
818 cache_handle: None,
819 }
820 }
821
new_blit( size: DeviceIntSize, source: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, ) -> RenderTaskId822 pub fn new_blit(
823 size: DeviceIntSize,
824 source: RenderTaskId,
825 rg_builder: &mut RenderTaskGraphBuilder,
826 ) -> RenderTaskId {
827 // If this blit uses a render task as a source,
828 // ensure it's added as a child task. This will
829 // ensure it gets allocated in the correct pass
830 // and made available as an input when this task
831 // executes.
832
833 let blit_task_id = rg_builder.add().init(RenderTask::new_dynamic(
834 size,
835 RenderTaskKind::Blit(BlitTask { source }),
836 ));
837
838 rg_builder.add_dependency(blit_task_id, source);
839
840 blit_task_id
841 }
842
843 // Construct a render task to apply a blur to a primitive.
844 // The render task chain that is constructed looks like:
845 //
846 // PrimitiveCacheTask: Draw the primitives.
847 // ^
848 // |
849 // DownscalingTask(s): Each downscaling task reduces the size of render target to
850 // ^ half. Also reduce the std deviation to half until the std
851 // | deviation less than 4.0.
852 // |
853 // |
854 // VerticalBlurTask: Apply the separable vertical blur to the primitive.
855 // ^
856 // |
857 // HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
858 // |
859 // +---- This is stored as the input task to the primitive shader.
860 //
new_blur( blur_std_deviation: DeviceSize, src_task_id: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, target_kind: RenderTargetKind, mut blur_cache: Option<&mut BlurTaskCache>, blur_region: DeviceIntSize, ) -> RenderTaskId861 pub fn new_blur(
862 blur_std_deviation: DeviceSize,
863 src_task_id: RenderTaskId,
864 rg_builder: &mut RenderTaskGraphBuilder,
865 target_kind: RenderTargetKind,
866 mut blur_cache: Option<&mut BlurTaskCache>,
867 blur_region: DeviceIntSize,
868 ) -> RenderTaskId {
869 // Adjust large std deviation value.
870 let mut adjusted_blur_std_deviation = blur_std_deviation;
871 let (blur_target_size, uv_rect_kind) = {
872 let src_task = rg_builder.get_task(src_task_id);
873 (src_task.location.size(), src_task.uv_rect_kind())
874 };
875 let mut adjusted_blur_target_size = blur_target_size;
876 let mut downscaling_src_task_id = src_task_id;
877 let mut scale_factor = 1.0;
878 let mut n_downscales = 1;
879 while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION &&
880 adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION {
881 if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
882 adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
883 break;
884 }
885 adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5;
886 scale_factor *= 2.0;
887 adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
888
889 let cached_task = match blur_cache {
890 Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(),
891 None => None,
892 };
893
894 downscaling_src_task_id = cached_task.unwrap_or_else(|| {
895 RenderTask::new_scaling(
896 downscaling_src_task_id,
897 rg_builder,
898 target_kind,
899 adjusted_blur_target_size,
900 )
901 });
902
903 if let Some(ref mut cache) = blur_cache {
904 cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id);
905 }
906
907 n_downscales += 1;
908 }
909
910
911 let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
912
913 let cached_task = match blur_cache {
914 Some(ref mut cache) => cache.get(&blur_key).cloned(),
915 None => None,
916 };
917
918 let blur_region = blur_region / (scale_factor as i32);
919
920 let blur_task_id = cached_task.unwrap_or_else(|| {
921 let blur_task_v = rg_builder.add().init(RenderTask::new_dynamic(
922 adjusted_blur_target_size,
923 RenderTaskKind::VerticalBlur(BlurTask {
924 blur_std_deviation: adjusted_blur_std_deviation.height,
925 target_kind,
926 blur_region,
927 }),
928 ).with_uv_rect_kind(uv_rect_kind));
929 rg_builder.add_dependency(blur_task_v, downscaling_src_task_id);
930
931 let task_id = rg_builder.add().init(RenderTask::new_dynamic(
932 adjusted_blur_target_size,
933 RenderTaskKind::HorizontalBlur(BlurTask {
934 blur_std_deviation: adjusted_blur_std_deviation.width,
935 target_kind,
936 blur_region,
937 }),
938 ).with_uv_rect_kind(uv_rect_kind));
939 rg_builder.add_dependency(task_id, blur_task_v);
940
941 task_id
942 });
943
944 if let Some(ref mut cache) = blur_cache {
945 cache.insert(blur_key, blur_task_id);
946 }
947
948 blur_task_id
949 }
950
new_scaling( src_task_id: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, target_kind: RenderTargetKind, size: DeviceIntSize, ) -> RenderTaskId951 pub fn new_scaling(
952 src_task_id: RenderTaskId,
953 rg_builder: &mut RenderTaskGraphBuilder,
954 target_kind: RenderTargetKind,
955 size: DeviceIntSize,
956 ) -> RenderTaskId {
957 Self::new_scaling_with_padding(
958 src_task_id,
959 rg_builder,
960 target_kind,
961 size,
962 DeviceIntSideOffsets::zero(),
963 )
964 }
965
new_scaling_with_padding( source: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, target_kind: RenderTargetKind, padded_size: DeviceIntSize, padding: DeviceIntSideOffsets, ) -> RenderTaskId966 pub fn new_scaling_with_padding(
967 source: RenderTaskId,
968 rg_builder: &mut RenderTaskGraphBuilder,
969 target_kind: RenderTargetKind,
970 padded_size: DeviceIntSize,
971 padding: DeviceIntSideOffsets,
972 ) -> RenderTaskId {
973 let uv_rect_kind = rg_builder.get_task(source).uv_rect_kind();
974
975 let task_id = rg_builder.add().init(
976 RenderTask::new_dynamic(
977 padded_size,
978 RenderTaskKind::Scaling(ScalingTask {
979 target_kind,
980 padding,
981 }),
982 ).with_uv_rect_kind(uv_rect_kind)
983 );
984
985 rg_builder.add_dependency(task_id, source);
986
987 task_id
988 }
989
new_svg_filter( filter_primitives: &[FilterPrimitive], filter_datas: &[SFilterData], rg_builder: &mut RenderTaskGraphBuilder, content_size: DeviceIntSize, uv_rect_kind: UvRectKind, original_task_id: RenderTaskId, device_pixel_scale: DevicePixelScale, ) -> RenderTaskId990 pub fn new_svg_filter(
991 filter_primitives: &[FilterPrimitive],
992 filter_datas: &[SFilterData],
993 rg_builder: &mut RenderTaskGraphBuilder,
994 content_size: DeviceIntSize,
995 uv_rect_kind: UvRectKind,
996 original_task_id: RenderTaskId,
997 device_pixel_scale: DevicePixelScale,
998 ) -> RenderTaskId {
999
1000 if filter_primitives.is_empty() {
1001 return original_task_id;
1002 }
1003
1004 // Resolves the input to a filter primitive
1005 let get_task_input = |
1006 input: &FilterPrimitiveInput,
1007 filter_primitives: &[FilterPrimitive],
1008 rg_builder: &mut RenderTaskGraphBuilder,
1009 cur_index: usize,
1010 outputs: &[RenderTaskId],
1011 original: RenderTaskId,
1012 color_space: ColorSpace,
1013 | {
1014 // TODO(cbrewster): Not sure we can assume that the original input is sRGB.
1015 let (mut task_id, input_color_space) = match input.to_index(cur_index) {
1016 Some(index) => (outputs[index], filter_primitives[index].color_space),
1017 None => (original, ColorSpace::Srgb),
1018 };
1019
1020 match (input_color_space, color_space) {
1021 (ColorSpace::Srgb, ColorSpace::LinearRgb) => {
1022 task_id = RenderTask::new_svg_filter_primitive(
1023 smallvec![task_id],
1024 content_size,
1025 uv_rect_kind,
1026 SvgFilterInfo::SrgbToLinear,
1027 rg_builder,
1028 );
1029 },
1030 (ColorSpace::LinearRgb, ColorSpace::Srgb) => {
1031 task_id = RenderTask::new_svg_filter_primitive(
1032 smallvec![task_id],
1033 content_size,
1034 uv_rect_kind,
1035 SvgFilterInfo::LinearToSrgb,
1036 rg_builder,
1037 );
1038 },
1039 _ => {},
1040 }
1041
1042 task_id
1043 };
1044
1045 let mut outputs = vec![];
1046 let mut cur_filter_data = 0;
1047 for (cur_index, primitive) in filter_primitives.iter().enumerate() {
1048 let render_task_id = match primitive.kind {
1049 FilterPrimitiveKind::Identity(ref identity) => {
1050 // Identity does not create a task, it provides its input's render task
1051 get_task_input(
1052 &identity.input,
1053 filter_primitives,
1054 rg_builder,
1055 cur_index,
1056 &outputs,
1057 original_task_id,
1058 primitive.color_space
1059 )
1060 }
1061 FilterPrimitiveKind::Blend(ref blend) => {
1062 let input_1_task_id = get_task_input(
1063 &blend.input1,
1064 filter_primitives,
1065 rg_builder,
1066 cur_index,
1067 &outputs,
1068 original_task_id,
1069 primitive.color_space
1070 );
1071 let input_2_task_id = get_task_input(
1072 &blend.input2,
1073 filter_primitives,
1074 rg_builder,
1075 cur_index,
1076 &outputs,
1077 original_task_id,
1078 primitive.color_space
1079 );
1080
1081 RenderTask::new_svg_filter_primitive(
1082 smallvec![input_1_task_id, input_2_task_id],
1083 content_size,
1084 uv_rect_kind,
1085 SvgFilterInfo::Blend(blend.mode),
1086 rg_builder,
1087 )
1088 },
1089 FilterPrimitiveKind::Flood(ref flood) => {
1090 RenderTask::new_svg_filter_primitive(
1091 smallvec![],
1092 content_size,
1093 uv_rect_kind,
1094 SvgFilterInfo::Flood(flood.color),
1095 rg_builder,
1096 )
1097 }
1098 FilterPrimitiveKind::Blur(ref blur) => {
1099 let width_std_deviation = blur.width * device_pixel_scale.0;
1100 let height_std_deviation = blur.height * device_pixel_scale.0;
1101 let input_task_id = get_task_input(
1102 &blur.input,
1103 filter_primitives,
1104 rg_builder,
1105 cur_index,
1106 &outputs,
1107 original_task_id,
1108 primitive.color_space
1109 );
1110
1111 RenderTask::new_blur(
1112 DeviceSize::new(width_std_deviation, height_std_deviation),
1113 // TODO: This is a hack to ensure that a blur task's input is always
1114 // in the blur's previous pass.
1115 RenderTask::new_svg_filter_primitive(
1116 smallvec![input_task_id],
1117 content_size,
1118 uv_rect_kind,
1119 SvgFilterInfo::Identity,
1120 rg_builder,
1121 ),
1122 rg_builder,
1123 RenderTargetKind::Color,
1124 None,
1125 content_size,
1126 )
1127 }
1128 FilterPrimitiveKind::Opacity(ref opacity) => {
1129 let input_task_id = get_task_input(
1130 &opacity.input,
1131 filter_primitives,
1132 rg_builder,
1133 cur_index,
1134 &outputs,
1135 original_task_id,
1136 primitive.color_space
1137 );
1138
1139 RenderTask::new_svg_filter_primitive(
1140 smallvec![input_task_id],
1141 content_size,
1142 uv_rect_kind,
1143 SvgFilterInfo::Opacity(opacity.opacity),
1144 rg_builder,
1145 )
1146 }
1147 FilterPrimitiveKind::ColorMatrix(ref color_matrix) => {
1148 let input_task_id = get_task_input(
1149 &color_matrix.input,
1150 filter_primitives,
1151 rg_builder,
1152 cur_index,
1153 &outputs,
1154 original_task_id,
1155 primitive.color_space
1156 );
1157
1158 RenderTask::new_svg_filter_primitive(
1159 smallvec![input_task_id],
1160 content_size,
1161 uv_rect_kind,
1162 SvgFilterInfo::ColorMatrix(Box::new(color_matrix.matrix)),
1163 rg_builder,
1164 )
1165 }
1166 FilterPrimitiveKind::DropShadow(ref drop_shadow) => {
1167 let input_task_id = get_task_input(
1168 &drop_shadow.input,
1169 filter_primitives,
1170 rg_builder,
1171 cur_index,
1172 &outputs,
1173 original_task_id,
1174 primitive.color_space
1175 );
1176
1177 let blur_std_deviation = drop_shadow.shadow.blur_radius * device_pixel_scale.0;
1178 let offset = drop_shadow.shadow.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
1179
1180 let offset_task_id = RenderTask::new_svg_filter_primitive(
1181 smallvec![input_task_id],
1182 content_size,
1183 uv_rect_kind,
1184 SvgFilterInfo::Offset(offset),
1185 rg_builder,
1186 );
1187
1188 let blur_task_id = RenderTask::new_blur(
1189 DeviceSize::new(blur_std_deviation, blur_std_deviation),
1190 offset_task_id,
1191 rg_builder,
1192 RenderTargetKind::Color,
1193 None,
1194 content_size,
1195 );
1196
1197 RenderTask::new_svg_filter_primitive(
1198 smallvec![input_task_id, blur_task_id],
1199 content_size,
1200 uv_rect_kind,
1201 SvgFilterInfo::DropShadow(drop_shadow.shadow.color),
1202 rg_builder,
1203 )
1204 }
1205 FilterPrimitiveKind::ComponentTransfer(ref component_transfer) => {
1206 let input_task_id = get_task_input(
1207 &component_transfer.input,
1208 filter_primitives,
1209 rg_builder,
1210 cur_index,
1211 &outputs,
1212 original_task_id,
1213 primitive.color_space
1214 );
1215
1216 let filter_data = &filter_datas[cur_filter_data];
1217 cur_filter_data += 1;
1218 if filter_data.is_identity() {
1219 input_task_id
1220 } else {
1221 RenderTask::new_svg_filter_primitive(
1222 smallvec![input_task_id],
1223 content_size,
1224 uv_rect_kind,
1225 SvgFilterInfo::ComponentTransfer(filter_data.clone()),
1226 rg_builder,
1227 )
1228 }
1229 }
1230 FilterPrimitiveKind::Offset(ref info) => {
1231 let input_task_id = get_task_input(
1232 &info.input,
1233 filter_primitives,
1234 rg_builder,
1235 cur_index,
1236 &outputs,
1237 original_task_id,
1238 primitive.color_space
1239 );
1240
1241 let offset = info.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
1242 RenderTask::new_svg_filter_primitive(
1243 smallvec![input_task_id],
1244 content_size,
1245 uv_rect_kind,
1246 SvgFilterInfo::Offset(offset),
1247 rg_builder,
1248 )
1249 }
1250 FilterPrimitiveKind::Composite(info) => {
1251 let input_1_task_id = get_task_input(
1252 &info.input1,
1253 filter_primitives,
1254 rg_builder,
1255 cur_index,
1256 &outputs,
1257 original_task_id,
1258 primitive.color_space
1259 );
1260 let input_2_task_id = get_task_input(
1261 &info.input2,
1262 filter_primitives,
1263 rg_builder,
1264 cur_index,
1265 &outputs,
1266 original_task_id,
1267 primitive.color_space
1268 );
1269
1270 RenderTask::new_svg_filter_primitive(
1271 smallvec![input_1_task_id, input_2_task_id],
1272 content_size,
1273 uv_rect_kind,
1274 SvgFilterInfo::Composite(info.operator),
1275 rg_builder,
1276 )
1277 }
1278 };
1279 outputs.push(render_task_id);
1280 }
1281
1282 // The output of a filter is the output of the last primitive in the chain.
1283 let mut render_task_id = *outputs.last().unwrap();
1284
1285 // Convert to sRGB if needed
1286 if filter_primitives.last().unwrap().color_space == ColorSpace::LinearRgb {
1287 render_task_id = RenderTask::new_svg_filter_primitive(
1288 smallvec![render_task_id],
1289 content_size,
1290 uv_rect_kind,
1291 SvgFilterInfo::LinearToSrgb,
1292 rg_builder,
1293 );
1294 }
1295
1296 render_task_id
1297 }
1298
new_svg_filter_primitive( tasks: TaskDependencies, target_size: DeviceIntSize, uv_rect_kind: UvRectKind, info: SvgFilterInfo, rg_builder: &mut RenderTaskGraphBuilder, ) -> RenderTaskId1299 pub fn new_svg_filter_primitive(
1300 tasks: TaskDependencies,
1301 target_size: DeviceIntSize,
1302 uv_rect_kind: UvRectKind,
1303 info: SvgFilterInfo,
1304 rg_builder: &mut RenderTaskGraphBuilder,
1305 ) -> RenderTaskId {
1306 let task_id = rg_builder.add().init(RenderTask::new_dynamic(
1307 target_size,
1308 RenderTaskKind::SvgFilter(SvgFilterTask {
1309 extra_gpu_cache_handle: None,
1310 info,
1311 }),
1312 ).with_uv_rect_kind(uv_rect_kind));
1313
1314 for child_id in tasks {
1315 rg_builder.add_dependency(task_id, child_id);
1316 }
1317
1318 task_id
1319 }
1320
uv_rect_kind(&self) -> UvRectKind1321 pub fn uv_rect_kind(&self) -> UvRectKind {
1322 self.uv_rect_kind
1323 }
1324
get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress1325 pub fn get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress {
1326 gpu_cache.get_address(&self.uv_rect_handle)
1327 }
1328
get_dynamic_size(&self) -> DeviceIntSize1329 pub fn get_dynamic_size(&self) -> DeviceIntSize {
1330 self.location.size()
1331 }
1332
get_target_texture(&self) -> CacheTextureId1333 pub fn get_target_texture(&self) -> CacheTextureId {
1334 match self.location {
1335 RenderTaskLocation::Dynamic { texture_id, .. } => {
1336 assert_ne!(texture_id, CacheTextureId::INVALID);
1337 texture_id
1338 }
1339 RenderTaskLocation::CacheRequest { .. } |
1340 RenderTaskLocation::Unallocated { .. } |
1341 RenderTaskLocation::Static { .. } => {
1342 unreachable!();
1343 }
1344 }
1345 }
1346
get_texture_source(&self) -> TextureSource1347 pub fn get_texture_source(&self) -> TextureSource {
1348 match self.location {
1349 RenderTaskLocation::Dynamic { texture_id, .. } => {
1350 assert_ne!(texture_id, CacheTextureId::INVALID);
1351 TextureSource::TextureCache(texture_id, Swizzle::default())
1352 }
1353 RenderTaskLocation::Static { surface: StaticRenderTaskSurface::ReadOnly { source }, .. } => {
1354 source
1355 }
1356 RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => {
1357 TextureSource::TextureCache(texture, Swizzle::default())
1358 }
1359 RenderTaskLocation::Static { .. } |
1360 RenderTaskLocation::CacheRequest { .. } |
1361 RenderTaskLocation::Unallocated { .. } => {
1362 unreachable!();
1363 }
1364 }
1365 }
1366
get_target_rect(&self) -> DeviceIntRect1367 pub fn get_target_rect(&self) -> DeviceIntRect {
1368 match self.location {
1369 // Previously, we only added render tasks after the entire
1370 // primitive chain was determined visible. This meant that
1371 // we could assert any render task in the list was also
1372 // allocated (assigned to passes). Now, we add render
1373 // tasks earlier, and the picture they belong to may be
1374 // culled out later, so we can't assert that the task
1375 // has been allocated.
1376 // Render tasks that are created but not assigned to
1377 // passes consume a row in the render task texture, but
1378 // don't allocate any space in render targets nor
1379 // draw any pixels.
1380 // TODO(gw): Consider some kind of tag or other method
1381 // to mark a task as unused explicitly. This
1382 // would allow us to restore this debug check.
1383 RenderTaskLocation::Dynamic { rect, .. } => rect,
1384 RenderTaskLocation::Static { rect, .. } => rect,
1385 RenderTaskLocation::CacheRequest { .. }
1386 | RenderTaskLocation::Unallocated { .. } => {
1387 panic!("bug: get_target_rect called before allocating");
1388 }
1389 }
1390 }
1391
target_kind(&self) -> RenderTargetKind1392 pub fn target_kind(&self) -> RenderTargetKind {
1393 self.kind.target_kind()
1394 }
1395
write_gpu_blocks( &mut self, target_rect: DeviceIntRect, gpu_cache: &mut GpuCache, )1396 pub fn write_gpu_blocks(
1397 &mut self,
1398 target_rect: DeviceIntRect,
1399 gpu_cache: &mut GpuCache,
1400 ) {
1401 profile_scope!("write_gpu_blocks");
1402
1403 self.kind.write_gpu_blocks(gpu_cache);
1404
1405 if self.cache_handle.is_some() {
1406 // The uv rect handle of cached render tasks is requested and set by the
1407 // render task cache.
1408 return;
1409 }
1410
1411 if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
1412 let p0 = target_rect.min.to_f32();
1413 let p1 = target_rect.max.to_f32();
1414 let image_source = ImageSource {
1415 p0,
1416 p1,
1417 user_data: [0.0; 4],
1418 uv_rect_kind: self.uv_rect_kind,
1419 };
1420 image_source.write_gpu_blocks(&mut request);
1421 }
1422 }
1423
1424 /// Called by the render task cache.
1425 ///
1426 /// Tells the render task that it is cached (which means its gpu cache
1427 /// handle is managed by the texture cache).
mark_cached(&mut self, handle: RenderTaskCacheEntryHandle)1428 pub fn mark_cached(&mut self, handle: RenderTaskCacheEntryHandle) {
1429 self.cache_handle = Some(handle);
1430 }
1431 }
1432