render_task.rs (109644B)
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::{LineStyle, LineOrientation, ClipMode, ColorF, FilterOpGraphPictureBufferId}; 6 use api::{MAX_RENDER_TASK_SIZE, SVGFE_GRAPH_MAX}; 7 use api::units::*; 8 use std::time::Duration; 9 use crate::box_shadow::BLUR_SAMPLE_SCALE; 10 use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange}; 11 use crate::command_buffer::{CommandBufferIndex, QuadFlags}; 12 use crate::pattern::{PatternKind, PatternShaderInput}; 13 use crate::profiler::{add_text_marker}; 14 use crate::spatial_tree::SpatialNodeIndex; 15 use crate::frame_builder::FrameBuilderConfig; 16 use crate::gpu_types::{BorderInstance, UvRectKind, TransformPaletteId, BlurEdgeMode}; 17 use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle}; 18 use crate::svg_filter::{FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, SVGFE_CONVOLVE_VALUES_LIMIT}; 19 use crate::picture::ResolvedSurfaceTexture; 20 use crate::tile_cache::MAX_SURFACE_SIZE; 21 use crate::prim_store::ClipData; 22 use crate::prim_store::gradient::{ 23 FastLinearGradientTask, RadialGradientTask, 24 ConicGradientTask, LinearGradientTask, 25 }; 26 use crate::resource_cache::{ResourceCache, ImageRequest}; 27 use std::{usize, f32, i32, u32}; 28 use crate::renderer::{GpuBufferAddress, GpuBufferBuilder, GpuBufferBuilderF}; 29 use crate::render_backend::DataStores; 30 use crate::render_target::{ResolveOp, RenderTargetKind}; 31 use crate::render_task_graph::{PassId, RenderTaskId, RenderTaskGraphBuilder}; 32 use crate::render_task_cache::{RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent}; 33 use crate::segment::EdgeAaSegmentMask; 34 use crate::surface::SurfaceBuilder; 35 use smallvec::SmallVec; 36 37 const FLOATS_PER_RENDER_TASK_INFO: usize = 8; 38 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; 39 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8; 40 41 fn render_task_sanity_check(size: &DeviceIntSize) { 42 if size.width > MAX_RENDER_TASK_SIZE || 43 size.height > MAX_RENDER_TASK_SIZE { 44 error!("Attempting to create a render task of size {}x{}", size.width, size.height); 45 panic!(); 46 } 47 } 48 49 #[derive(Debug, Copy, Clone, PartialEq)] 50 #[repr(C)] 51 #[cfg_attr(feature = "capture", derive(Serialize))] 52 #[cfg_attr(feature = "replay", derive(Deserialize))] 53 pub struct RenderTaskAddress(pub i32); 54 55 impl Into<RenderTaskAddress> for RenderTaskId { 56 fn into(self) -> RenderTaskAddress { 57 RenderTaskAddress(self.index as i32) 58 } 59 } 60 61 /// A render task location that targets a persistent output buffer which 62 /// will be retained over multiple frames. 63 #[derive(Clone, Debug, Eq, PartialEq, Hash)] 64 #[cfg_attr(feature = "capture", derive(Serialize))] 65 #[cfg_attr(feature = "replay", derive(Deserialize))] 66 pub enum StaticRenderTaskSurface { 67 /// The output of the `RenderTask` will be persisted beyond this frame, and 68 /// thus should be drawn into the `TextureCache`. 69 TextureCache { 70 /// Which texture in the texture cache should be drawn into. 71 texture: CacheTextureId, 72 /// What format this texture cache surface is 73 target_kind: RenderTargetKind, 74 }, 75 /// Only used as a source for render tasks, can be any texture including an 76 /// external one. 77 ReadOnly { 78 source: TextureSource, 79 }, 80 /// This render task will be drawn to a picture cache texture that is 81 /// persisted between both frames and scenes, if the content remains valid. 82 PictureCache { 83 /// Describes either a WR texture or a native OS compositor target 84 surface: ResolvedSurfaceTexture, 85 }, 86 } 87 88 /// Identifies the output buffer location for a given `RenderTask`. 89 #[derive(Clone, Debug)] 90 #[cfg_attr(feature = "capture", derive(Serialize))] 91 #[cfg_attr(feature = "replay", derive(Deserialize))] 92 pub enum RenderTaskLocation { 93 // Towards the beginning of the frame, most task locations are typically not 94 // known yet, in which case they are set to one of the following variants: 95 96 /// A dynamic task that has not yet been allocated a texture and rect. 97 Unallocated { 98 /// Requested size of this render task 99 size: DeviceIntSize, 100 }, 101 /// Will be replaced by a Static location after the texture cache update. 102 CacheRequest { 103 size: DeviceIntSize, 104 }, 105 /// Same allocation as an existing task deeper in the dependency graph 106 Existing { 107 parent_task_id: RenderTaskId, 108 /// Requested size of this render task 109 size: DeviceIntSize, 110 }, 111 112 // Before batching begins, we expect that locations have been resolved to 113 // one of the following variants: 114 115 /// The `RenderTask` should be drawn to a target provided by the atlas 116 /// allocator. This is the most common case. 117 Dynamic { 118 /// Texture that this task was allocated to render on 119 texture_id: CacheTextureId, 120 /// Rectangle in the texture this task occupies 121 rect: DeviceIntRect, 122 }, 123 /// A task that is output to a persistent / retained target. 124 Static { 125 /// Target to draw to 126 surface: StaticRenderTaskSurface, 127 /// Rectangle in the texture this task occupies 128 rect: DeviceIntRect, 129 }, 130 } 131 132 impl RenderTaskLocation { 133 /// Returns true if this is a dynamic location. 134 pub fn is_dynamic(&self) -> bool { 135 match *self { 136 RenderTaskLocation::Dynamic { .. } => true, 137 _ => false, 138 } 139 } 140 141 pub fn size(&self) -> DeviceIntSize { 142 match self { 143 RenderTaskLocation::Unallocated { size } => *size, 144 RenderTaskLocation::Dynamic { rect, .. } => rect.size(), 145 RenderTaskLocation::Static { rect, .. } => rect.size(), 146 RenderTaskLocation::CacheRequest { size } => *size, 147 RenderTaskLocation::Existing { size, .. } => *size, 148 } 149 } 150 } 151 152 #[derive(Debug)] 153 #[cfg_attr(feature = "capture", derive(Serialize))] 154 #[cfg_attr(feature = "replay", derive(Deserialize))] 155 pub struct CachedTask { 156 pub target_kind: RenderTargetKind, 157 } 158 159 #[derive(Debug)] 160 #[cfg_attr(feature = "capture", derive(Serialize))] 161 #[cfg_attr(feature = "replay", derive(Deserialize))] 162 pub struct ImageRequestTask { 163 pub request: ImageRequest, 164 pub is_composited: bool, 165 } 166 167 #[derive(Debug)] 168 #[cfg_attr(feature = "capture", derive(Serialize))] 169 #[cfg_attr(feature = "replay", derive(Deserialize))] 170 pub struct CacheMaskTask { 171 pub actual_rect: DeviceRect, 172 pub root_spatial_node_index: SpatialNodeIndex, 173 pub clip_node_range: ClipNodeRange, 174 pub device_pixel_scale: DevicePixelScale, 175 pub clear_to_one: bool, 176 } 177 178 #[derive(Debug)] 179 #[cfg_attr(feature = "capture", derive(Serialize))] 180 #[cfg_attr(feature = "replay", derive(Deserialize))] 181 pub struct ClipRegionTask { 182 pub local_pos: LayoutPoint, 183 pub device_pixel_scale: DevicePixelScale, 184 pub clip_data: ClipData, 185 pub clear_to_one: bool, 186 } 187 188 #[cfg_attr(feature = "capture", derive(Serialize))] 189 #[cfg_attr(feature = "replay", derive(Deserialize))] 190 pub struct EmptyTask { 191 pub content_origin: DevicePoint, 192 pub device_pixel_scale: DevicePixelScale, 193 pub raster_spatial_node_index: SpatialNodeIndex, 194 } 195 196 #[cfg_attr(feature = "capture", derive(Serialize))] 197 #[cfg_attr(feature = "replay", derive(Deserialize))] 198 pub struct PrimTask { 199 pub pattern: PatternKind, 200 pub pattern_input: PatternShaderInput, 201 pub device_pixel_scale: DevicePixelScale, 202 pub content_origin: DevicePoint, 203 pub prim_address_f: GpuBufferAddress, 204 pub raster_spatial_node_index: SpatialNodeIndex, 205 pub transform_id: TransformPaletteId, 206 pub edge_flags: EdgeAaSegmentMask, 207 pub quad_flags: QuadFlags, 208 pub prim_needs_scissor_rect: bool, 209 pub texture_input: RenderTaskId, 210 } 211 212 #[cfg_attr(feature = "capture", derive(Serialize))] 213 #[cfg_attr(feature = "replay", derive(Deserialize))] 214 pub struct TileCompositeTask { 215 pub clear_color: ColorF, 216 pub scissor_rect: DeviceIntRect, 217 pub valid_rect: DeviceIntRect, 218 pub task_id: Option<RenderTaskId>, 219 pub sub_rect_offset: DeviceIntVector2D, 220 } 221 222 #[cfg_attr(feature = "capture", derive(Serialize))] 223 #[cfg_attr(feature = "replay", derive(Deserialize))] 224 pub struct PictureTask { 225 pub can_merge: bool, 226 pub content_origin: DevicePoint, 227 pub surface_spatial_node_index: SpatialNodeIndex, 228 pub raster_spatial_node_index: SpatialNodeIndex, 229 pub device_pixel_scale: DevicePixelScale, 230 pub clear_color: Option<ColorF>, 231 pub scissor_rect: Option<DeviceIntRect>, 232 pub valid_rect: Option<DeviceIntRect>, 233 pub cmd_buffer_index: CommandBufferIndex, 234 pub resolve_op: Option<ResolveOp>, 235 pub content_size: DeviceIntSize, 236 pub can_use_shared_surface: bool, 237 } 238 239 impl PictureTask { 240 /// Copy an existing picture task, but set a new command buffer for it to build in to. 241 /// Used for pictures that are split between render tasks (e.g. pre/post a backdrop 242 /// filter). Subsequent picture tasks never have a clear color as they are by definition 243 /// going to write to an existing target 244 pub fn duplicate( 245 &self, 246 cmd_buffer_index: CommandBufferIndex, 247 ) -> Self { 248 assert_eq!(self.resolve_op, None); 249 250 PictureTask { 251 clear_color: None, 252 cmd_buffer_index, 253 resolve_op: None, 254 can_use_shared_surface: false, 255 ..*self 256 } 257 } 258 } 259 260 #[derive(Debug)] 261 #[cfg_attr(feature = "capture", derive(Serialize))] 262 #[cfg_attr(feature = "replay", derive(Deserialize))] 263 pub struct BlurTask { 264 pub blur_std_deviation: f32, 265 pub target_kind: RenderTargetKind, 266 pub blur_region: DeviceIntSize, 267 pub edge_mode: BlurEdgeMode, 268 } 269 270 impl BlurTask { 271 // In order to do the blur down-scaling passes without introducing errors, we need the 272 // source of each down-scale pass to be a multuple of two. If need be, this inflates 273 // the source size so that each down-scale pass will sample correctly. 274 pub fn adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize { 275 let mut adjusted_size = original_size; 276 let mut scale_factor = 1.0; 277 while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION { 278 if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE as f32 || 279 adjusted_size.height < MIN_DOWNSCALING_RT_SIZE as f32 { 280 break; 281 } 282 std_dev = std_dev * 0.5; 283 scale_factor *= 2.0; 284 adjusted_size = (original_size.to_f32() / scale_factor).ceil(); 285 } 286 287 (adjusted_size * scale_factor).round() 288 } 289 } 290 291 #[derive(Debug)] 292 #[cfg_attr(feature = "capture", derive(Serialize))] 293 #[cfg_attr(feature = "replay", derive(Deserialize))] 294 pub struct ScalingTask { 295 pub target_kind: RenderTargetKind, 296 pub padding: DeviceIntSideOffsets, 297 } 298 299 #[derive(Debug)] 300 #[cfg_attr(feature = "capture", derive(Serialize))] 301 #[cfg_attr(feature = "replay", derive(Deserialize))] 302 pub struct BorderTask { 303 pub instances: Vec<BorderInstance>, 304 } 305 306 #[derive(Debug)] 307 #[cfg_attr(feature = "capture", derive(Serialize))] 308 #[cfg_attr(feature = "replay", derive(Deserialize))] 309 pub struct BlitTask { 310 pub source: RenderTaskId, 311 // Normalized rect within the source task to blit from 312 pub source_rect: DeviceIntRect, 313 } 314 315 #[derive(Debug)] 316 #[cfg_attr(feature = "capture", derive(Serialize))] 317 #[cfg_attr(feature = "replay", derive(Deserialize))] 318 pub struct LineDecorationTask { 319 pub wavy_line_thickness: f32, 320 pub style: LineStyle, 321 pub orientation: LineOrientation, 322 pub local_size: LayoutSize, 323 } 324 325 #[derive(Debug)] 326 #[cfg_attr(feature = "capture", derive(Serialize))] 327 #[cfg_attr(feature = "replay", derive(Deserialize))] 328 pub struct SVGFEFilterTask { 329 pub node: FilterGraphNode, 330 pub op: FilterGraphOp, 331 pub content_origin: DevicePoint, 332 pub extra_gpu_data: Option<GpuBufferAddress>, 333 } 334 335 #[cfg_attr(feature = "capture", derive(Serialize))] 336 #[cfg_attr(feature = "replay", derive(Deserialize))] 337 pub struct ReadbackTask { 338 // The offset of the rect that needs to be read back, in the 339 // device space of the surface that will be read back from. 340 // If this is None, there is no readback surface available 341 // and this is a dummy (empty) readback. 342 pub readback_origin: Option<DevicePoint>, 343 } 344 345 #[derive(Debug)] 346 #[cfg_attr(feature = "capture", derive(Serialize))] 347 #[cfg_attr(feature = "replay", derive(Deserialize))] 348 pub struct RenderTaskData { 349 pub data: [f32; FLOATS_PER_RENDER_TASK_INFO], 350 } 351 352 #[cfg_attr(feature = "capture", derive(Serialize))] 353 #[cfg_attr(feature = "replay", derive(Deserialize))] 354 pub enum RenderTaskKind { 355 Image(ImageRequestTask), 356 Cached(CachedTask), 357 Picture(PictureTask), 358 CacheMask(CacheMaskTask), 359 ClipRegion(ClipRegionTask), 360 VerticalBlur(BlurTask), 361 HorizontalBlur(BlurTask), 362 Readback(ReadbackTask), 363 Scaling(ScalingTask), 364 Blit(BlitTask), 365 Border(BorderTask), 366 LineDecoration(LineDecorationTask), 367 FastLinearGradient(FastLinearGradientTask), 368 LinearGradient(LinearGradientTask), 369 RadialGradient(RadialGradientTask), 370 ConicGradient(ConicGradientTask), 371 SVGFENode(SVGFEFilterTask), 372 TileComposite(TileCompositeTask), 373 Prim(PrimTask), 374 Empty(EmptyTask), 375 #[cfg(test)] 376 Test(RenderTargetKind), 377 } 378 379 impl RenderTaskKind { 380 pub fn is_a_rendering_operation(&self) -> bool { 381 match self { 382 &RenderTaskKind::Image(..) => false, 383 &RenderTaskKind::Cached(..) => false, 384 _ => true, 385 } 386 } 387 388 /// Whether this task can be allocated on a shared render target surface 389 pub fn can_use_shared_surface(&self) -> bool { 390 match self { 391 &RenderTaskKind::Picture(ref info) => info.can_use_shared_surface, 392 _ => true, 393 } 394 } 395 396 pub fn should_advance_pass(&self) -> bool { 397 match self { 398 &RenderTaskKind::Image(..) => false, 399 &RenderTaskKind::Cached(..) => false, 400 _ => true, 401 } 402 } 403 404 pub fn as_str(&self) -> &'static str { 405 match *self { 406 RenderTaskKind::Image(..) => "Image", 407 RenderTaskKind::Cached(..) => "Cached", 408 RenderTaskKind::Picture(..) => "Picture", 409 RenderTaskKind::CacheMask(..) => "CacheMask", 410 RenderTaskKind::ClipRegion(..) => "ClipRegion", 411 RenderTaskKind::VerticalBlur(..) => "VerticalBlur", 412 RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur", 413 RenderTaskKind::Readback(..) => "Readback", 414 RenderTaskKind::Scaling(..) => "Scaling", 415 RenderTaskKind::Blit(..) => "Blit", 416 RenderTaskKind::Border(..) => "Border", 417 RenderTaskKind::LineDecoration(..) => "LineDecoration", 418 RenderTaskKind::FastLinearGradient(..) => "FastLinearGradient", 419 RenderTaskKind::LinearGradient(..) => "LinearGradient", 420 RenderTaskKind::RadialGradient(..) => "RadialGradient", 421 RenderTaskKind::ConicGradient(..) => "ConicGradient", 422 RenderTaskKind::SVGFENode(..) => "SVGFENode", 423 RenderTaskKind::TileComposite(..) => "TileComposite", 424 RenderTaskKind::Prim(..) => "Prim", 425 RenderTaskKind::Empty(..) => "Empty", 426 #[cfg(test)] 427 RenderTaskKind::Test(..) => "Test", 428 } 429 } 430 431 pub fn target_kind(&self) -> RenderTargetKind { 432 match *self { 433 RenderTaskKind::Image(..) | 434 RenderTaskKind::LineDecoration(..) | 435 RenderTaskKind::Readback(..) | 436 RenderTaskKind::Border(..) | 437 RenderTaskKind::FastLinearGradient(..) | 438 RenderTaskKind::LinearGradient(..) | 439 RenderTaskKind::RadialGradient(..) | 440 RenderTaskKind::ConicGradient(..) | 441 RenderTaskKind::Picture(..) | 442 RenderTaskKind::Blit(..) | 443 RenderTaskKind::TileComposite(..) | 444 RenderTaskKind::Prim(..) => { 445 RenderTargetKind::Color 446 } 447 RenderTaskKind::SVGFENode(..) => { 448 RenderTargetKind::Color 449 } 450 451 RenderTaskKind::ClipRegion(..) | 452 RenderTaskKind::CacheMask(..) | 453 RenderTaskKind::Empty(..) => { 454 RenderTargetKind::Alpha 455 } 456 457 RenderTaskKind::VerticalBlur(ref task_info) | 458 RenderTaskKind::HorizontalBlur(ref task_info) => { 459 task_info.target_kind 460 } 461 462 RenderTaskKind::Scaling(ref task_info) => { 463 task_info.target_kind 464 } 465 466 RenderTaskKind::Cached(ref task_info) => { 467 task_info.target_kind 468 } 469 470 #[cfg(test)] 471 RenderTaskKind::Test(kind) => kind, 472 } 473 } 474 475 pub fn new_tile_composite( 476 sub_rect_offset: DeviceIntVector2D, 477 scissor_rect: DeviceIntRect, 478 valid_rect: DeviceIntRect, 479 clear_color: ColorF, 480 ) -> Self { 481 RenderTaskKind::TileComposite(TileCompositeTask { 482 task_id: None, 483 sub_rect_offset, 484 scissor_rect, 485 valid_rect, 486 clear_color, 487 }) 488 } 489 490 pub fn new_picture( 491 size: DeviceIntSize, 492 needs_scissor_rect: bool, 493 content_origin: DevicePoint, 494 surface_spatial_node_index: SpatialNodeIndex, 495 raster_spatial_node_index: SpatialNodeIndex, 496 device_pixel_scale: DevicePixelScale, 497 scissor_rect: Option<DeviceIntRect>, 498 valid_rect: Option<DeviceIntRect>, 499 clear_color: Option<ColorF>, 500 cmd_buffer_index: CommandBufferIndex, 501 can_use_shared_surface: bool, 502 content_size: Option<DeviceIntSize>, 503 ) -> Self { 504 render_task_sanity_check(&size); 505 506 RenderTaskKind::Picture(PictureTask { 507 content_origin, 508 can_merge: !needs_scissor_rect, 509 surface_spatial_node_index, 510 raster_spatial_node_index, 511 device_pixel_scale, 512 scissor_rect, 513 valid_rect, 514 clear_color, 515 cmd_buffer_index, 516 resolve_op: None, 517 can_use_shared_surface, 518 content_size: content_size.unwrap_or(size), 519 }) 520 } 521 522 pub fn new_prim( 523 pattern: PatternKind, 524 pattern_input: PatternShaderInput, 525 raster_spatial_node_index: SpatialNodeIndex, 526 device_pixel_scale: DevicePixelScale, 527 content_origin: DevicePoint, 528 prim_address_f: GpuBufferAddress, 529 transform_id: TransformPaletteId, 530 edge_flags: EdgeAaSegmentMask, 531 quad_flags: QuadFlags, 532 prim_needs_scissor_rect: bool, 533 texture_input: RenderTaskId, 534 ) -> Self { 535 RenderTaskKind::Prim(PrimTask { 536 pattern, 537 pattern_input, 538 raster_spatial_node_index, 539 device_pixel_scale, 540 content_origin, 541 prim_address_f, 542 transform_id, 543 edge_flags, 544 quad_flags, 545 prim_needs_scissor_rect, 546 texture_input, 547 }) 548 } 549 550 pub fn new_readback( 551 readback_origin: Option<DevicePoint>, 552 ) -> Self { 553 RenderTaskKind::Readback( 554 ReadbackTask { 555 readback_origin, 556 } 557 ) 558 } 559 560 pub fn new_line_decoration( 561 style: LineStyle, 562 orientation: LineOrientation, 563 wavy_line_thickness: f32, 564 local_size: LayoutSize, 565 ) -> Self { 566 RenderTaskKind::LineDecoration(LineDecorationTask { 567 style, 568 orientation, 569 wavy_line_thickness, 570 local_size, 571 }) 572 } 573 574 pub fn new_border_segment( 575 instances: Vec<BorderInstance>, 576 ) -> Self { 577 RenderTaskKind::Border(BorderTask { 578 instances, 579 }) 580 } 581 582 pub fn new_rounded_rect_mask( 583 local_pos: LayoutPoint, 584 clip_data: ClipData, 585 device_pixel_scale: DevicePixelScale, 586 fb_config: &FrameBuilderConfig, 587 ) -> Self { 588 RenderTaskKind::ClipRegion(ClipRegionTask { 589 local_pos, 590 device_pixel_scale, 591 clip_data, 592 clear_to_one: fb_config.gpu_supports_fast_clears, 593 }) 594 } 595 596 pub fn new_mask( 597 outer_rect: DeviceIntRect, 598 clip_node_range: ClipNodeRange, 599 root_spatial_node_index: SpatialNodeIndex, 600 clip_store: &mut ClipStore, 601 gpu_buffer_builder: &mut GpuBufferBuilderF, 602 resource_cache: &mut ResourceCache, 603 rg_builder: &mut RenderTaskGraphBuilder, 604 clip_data_store: &mut ClipDataStore, 605 device_pixel_scale: DevicePixelScale, 606 fb_config: &FrameBuilderConfig, 607 surface_builder: &mut SurfaceBuilder, 608 ) -> RenderTaskId { 609 // Step through the clip sources that make up this mask. If we find 610 // any box-shadow clip sources, request that image from the render 611 // task cache. This allows the blurred box-shadow rect to be cached 612 // in the texture cache across frames. 613 // TODO(gw): Consider moving this logic outside this function, especially 614 // as we add more clip sources that depend on render tasks. 615 // TODO(gw): If this ever shows up in a profile, we could pre-calculate 616 // whether a ClipSources contains any box-shadows and skip 617 // this iteration for the majority of cases. 618 let task_size = outer_rect.size(); 619 620 // If we have a potentially tiled clip mask, clear the mask area first. Otherwise, 621 // the first (primary) clip mask will overwrite all the clip mask pixels with 622 // blending disabled to set to the initial value. 623 624 let clip_task_id = rg_builder.add().init( 625 RenderTask::new_dynamic( 626 task_size, 627 RenderTaskKind::CacheMask(CacheMaskTask { 628 actual_rect: outer_rect.to_f32(), 629 clip_node_range, 630 root_spatial_node_index, 631 device_pixel_scale, 632 clear_to_one: fb_config.gpu_supports_fast_clears, 633 }), 634 ) 635 ); 636 637 for i in 0 .. clip_node_range.count { 638 let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i); 639 let clip_node = &mut clip_data_store[clip_instance.handle]; 640 match clip_node.item.kind { 641 ClipItemKind::BoxShadow { ref mut source } => { 642 let (cache_size, cache_key) = source.cache_key 643 .as_ref() 644 .expect("bug: no cache key set") 645 .clone(); 646 let blur_radius_dp = cache_key.blur_radius_dp as f32; 647 let device_pixel_scale = DevicePixelScale::new(cache_key.device_pixel_scale.to_f32_px()); 648 649 // Request a cacheable render task with a blurred, minimal 650 // sized box-shadow rect. 651 source.render_task = Some(resource_cache.request_render_task( 652 Some(RenderTaskCacheKey { 653 size: cache_size, 654 kind: RenderTaskCacheKeyKind::BoxShadow(cache_key), 655 }), 656 false, 657 RenderTaskParent::RenderTask(clip_task_id), 658 gpu_buffer_builder, 659 rg_builder, 660 surface_builder, 661 &mut |rg_builder, _| { 662 let clip_data = ClipData::rounded_rect( 663 source.minimal_shadow_rect.size(), 664 &source.shadow_radius, 665 ClipMode::Clip, 666 ); 667 668 // Draw the rounded rect. 669 let mask_task_id = rg_builder.add().init(RenderTask::new_dynamic( 670 cache_size, 671 RenderTaskKind::new_rounded_rect_mask( 672 source.minimal_shadow_rect.min, 673 clip_data, 674 device_pixel_scale, 675 fb_config, 676 ), 677 )); 678 679 // Blur it 680 RenderTask::new_blur( 681 DeviceSize::new(blur_radius_dp, blur_radius_dp), 682 mask_task_id, 683 rg_builder, 684 RenderTargetKind::Alpha, 685 None, 686 cache_size, 687 BlurEdgeMode::Duplicate, 688 ) 689 } 690 )); 691 } 692 ClipItemKind::Rectangle { .. } | 693 ClipItemKind::RoundedRectangle { .. } | 694 ClipItemKind::Image { .. } => {} 695 } 696 } 697 698 clip_task_id 699 } 700 701 // Write (up to) 8 floats of data specific to the type 702 // of render task that is provided to the GPU shaders 703 // via a vertex texture. 704 pub fn write_task_data( 705 &self, 706 target_rect: DeviceIntRect, 707 ) -> RenderTaskData { 708 // NOTE: The ordering and layout of these structures are 709 // required to match both the GPU structures declared 710 // in prim_shared.glsl, and also the uses in submit_batch() 711 // in renderer.rs. 712 // TODO(gw): Maybe there's a way to make this stuff a bit 713 // more type-safe. Although, it will always need 714 // to be kept in sync with the GLSL code anyway. 715 716 let data = match self { 717 RenderTaskKind::Picture(ref task) => { 718 // Note: has to match `PICTURE_TYPE_*` in shaders 719 [ 720 task.device_pixel_scale.0, 721 task.content_origin.x, 722 task.content_origin.y, 723 0.0, 724 ] 725 } 726 RenderTaskKind::Prim(ref task) => { 727 [ 728 // NOTE: This must match the render task data format for Picture tasks currently 729 task.device_pixel_scale.0, 730 task.content_origin.x, 731 task.content_origin.y, 732 0.0, 733 ] 734 } 735 RenderTaskKind::Empty(ref task) => { 736 [ 737 // NOTE: This must match the render task data format for Picture tasks currently 738 task.device_pixel_scale.0, 739 task.content_origin.x, 740 task.content_origin.y, 741 0.0, 742 ] 743 } 744 RenderTaskKind::CacheMask(ref task) => { 745 [ 746 task.device_pixel_scale.0, 747 task.actual_rect.min.x, 748 task.actual_rect.min.y, 749 0.0, 750 ] 751 } 752 RenderTaskKind::ClipRegion(ref task) => { 753 [ 754 task.device_pixel_scale.0, 755 0.0, 756 0.0, 757 0.0, 758 ] 759 } 760 RenderTaskKind::VerticalBlur(_) | 761 RenderTaskKind::HorizontalBlur(_) => { 762 // TODO(gw): Make this match Picture tasks so that we can draw 763 // sub-passes on them to apply box-shadow masks. 764 [ 765 0.0, 766 0.0, 767 0.0, 768 0.0, 769 ] 770 } 771 RenderTaskKind::Image(..) | 772 RenderTaskKind::Cached(..) | 773 RenderTaskKind::Readback(..) | 774 RenderTaskKind::Scaling(..) | 775 RenderTaskKind::Border(..) | 776 RenderTaskKind::LineDecoration(..) | 777 RenderTaskKind::FastLinearGradient(..) | 778 RenderTaskKind::LinearGradient(..) | 779 RenderTaskKind::RadialGradient(..) | 780 RenderTaskKind::ConicGradient(..) | 781 RenderTaskKind::TileComposite(..) | 782 RenderTaskKind::Blit(..) => { 783 [0.0; 4] 784 } 785 786 RenderTaskKind::SVGFENode(_task) => { 787 // we don't currently use this for SVGFE filters. 788 // see SVGFEFilterInstance instead 789 [0.0; 4] 790 } 791 792 #[cfg(test)] 793 RenderTaskKind::Test(..) => { 794 [0.0; 4] 795 } 796 }; 797 798 RenderTaskData { 799 data: [ 800 target_rect.min.x as f32, 801 target_rect.min.y as f32, 802 target_rect.max.x as f32, 803 target_rect.max.y as f32, 804 data[0], 805 data[1], 806 data[2], 807 data[3], 808 ] 809 } 810 } 811 812 pub fn write_gpu_blocks( 813 &mut self, 814 gpu_buffer: &mut GpuBufferBuilder, 815 ) { 816 match self { 817 RenderTaskKind::SVGFENode(ref mut filter_task) => { 818 match filter_task.op { 819 FilterGraphOp::SVGFEBlendDarken => {} 820 FilterGraphOp::SVGFEBlendLighten => {} 821 FilterGraphOp::SVGFEBlendMultiply => {} 822 FilterGraphOp::SVGFEBlendNormal => {} 823 FilterGraphOp::SVGFEBlendScreen => {} 824 FilterGraphOp::SVGFEBlendOverlay => {} 825 FilterGraphOp::SVGFEBlendColorDodge => {} 826 FilterGraphOp::SVGFEBlendColorBurn => {} 827 FilterGraphOp::SVGFEBlendHardLight => {} 828 FilterGraphOp::SVGFEBlendSoftLight => {} 829 FilterGraphOp::SVGFEBlendDifference => {} 830 FilterGraphOp::SVGFEBlendExclusion => {} 831 FilterGraphOp::SVGFEBlendHue => {} 832 FilterGraphOp::SVGFEBlendSaturation => {} 833 FilterGraphOp::SVGFEBlendColor => {} 834 FilterGraphOp::SVGFEBlendLuminosity => {} 835 FilterGraphOp::SVGFEColorMatrix { values: matrix } => { 836 let mut writer = gpu_buffer.f32.write_blocks(5); 837 for i in 0..5 { 838 writer.push_one([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]); 839 } 840 filter_task.extra_gpu_data = Some(writer.finish()); 841 } 842 FilterGraphOp::SVGFEComponentTransfer => unreachable!(), 843 FilterGraphOp::SVGFEComponentTransferInterned{..} => {} 844 FilterGraphOp::SVGFECompositeArithmetic{k1, k2, k3, k4} => { 845 let mut writer = gpu_buffer.f32.write_blocks(1); 846 writer.push_one([k1, k2, k3, k4]); 847 filter_task.extra_gpu_data = Some(writer.finish()); 848 } 849 FilterGraphOp::SVGFECompositeATop => {} 850 FilterGraphOp::SVGFECompositeIn => {} 851 FilterGraphOp::SVGFECompositeLighter => {} 852 FilterGraphOp::SVGFECompositeOut => {} 853 FilterGraphOp::SVGFECompositeOver => {} 854 FilterGraphOp::SVGFECompositeXOR => {} 855 FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} | 856 FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} | 857 FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{order_x, order_y, kernel, divisor, bias, target_x, target_y, kernel_unit_length_x, kernel_unit_length_y, preserve_alpha} => { 858 let mut writer = gpu_buffer.f32.write_blocks(8); 859 assert!(SVGFE_CONVOLVE_VALUES_LIMIT == 25); 860 writer.push_one([-target_x as f32, -target_y as f32, order_x as f32, order_y as f32]); 861 writer.push_one([kernel_unit_length_x as f32, kernel_unit_length_y as f32, 1.0 / divisor, bias]); 862 writer.push_one([kernel[0], kernel[1], kernel[2], kernel[3]]); 863 writer.push_one([kernel[4], kernel[5], kernel[6], kernel[7]]); 864 writer.push_one([kernel[8], kernel[9], kernel[10], kernel[11]]); 865 writer.push_one([kernel[12], kernel[13], kernel[14], kernel[15]]); 866 writer.push_one([kernel[16], kernel[17], kernel[18], kernel[19]]); 867 writer.push_one([kernel[20], 0.0, 0.0, preserve_alpha as f32]); 868 filter_task.extra_gpu_data = Some(writer.finish()); 869 } 870 FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {} 871 FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {} 872 FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {} 873 FilterGraphOp::SVGFEDisplacementMap{scale, x_channel_selector, y_channel_selector} => { 874 let mut writer = gpu_buffer.f32.write_blocks(1); 875 writer.push_one([x_channel_selector as f32, y_channel_selector as f32, scale, 0.0]); 876 filter_task.extra_gpu_data = Some(writer.finish()); 877 } 878 FilterGraphOp::SVGFEDropShadow { color, .. } | 879 FilterGraphOp::SVGFEFlood { color } => { 880 let mut writer = gpu_buffer.f32.write_blocks(1); 881 writer.push_one(color.to_array()); 882 filter_task.extra_gpu_data = Some(writer.finish()); 883 } 884 FilterGraphOp::SVGFEGaussianBlur{..} => {} 885 FilterGraphOp::SVGFEIdentity => {} 886 FilterGraphOp::SVGFEImage {..} => {} 887 FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } | 888 FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => { 889 let mut writer = gpu_buffer.f32.write_blocks(1); 890 writer.push_one([radius_x, radius_y, 0.0, 0.0]); 891 filter_task.extra_gpu_data = Some(writer.finish()); 892 } 893 FilterGraphOp::SVGFEOpacity{..} => {} 894 FilterGraphOp::SVGFESourceAlpha => {} 895 FilterGraphOp::SVGFESourceGraphic => {} 896 FilterGraphOp::SVGFESpecularLightingDistant{..} => {} 897 FilterGraphOp::SVGFESpecularLightingPoint{..} => {} 898 FilterGraphOp::SVGFESpecularLightingSpot{..} => {} 899 FilterGraphOp::SVGFETile => {} 900 FilterGraphOp::SVGFEToAlpha{..} => {} 901 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {} 902 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {} 903 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {} 904 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {} 905 } 906 } 907 _ => {} 908 } 909 } 910 } 911 912 /// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs, 913 /// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive 914 /// in a single frame. 915 pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>; 916 917 /// Since we only use it within a single primitive, the key only needs to contain the down-scaling level 918 /// and the blur std deviation. 919 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 920 pub enum BlurTaskKey { 921 DownScale(u32), 922 Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 }, 923 } 924 925 impl BlurTaskKey { 926 fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self { 927 // Quantise the std deviations and store it as integers to work around 928 // Eq and Hash's f32 allergy. 929 // The blur radius is rounded before RenderTask::new_blur so we don't need 930 // a lot of precision. 931 const QUANTIZATION_FACTOR: f32 = 1024.0; 932 let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32; 933 let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32; 934 BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y } 935 } 936 } 937 938 // The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that 939 // typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements 940 // avoids many tiny heap allocations in pages with a lot of text shadows and other 941 // types of render tasks. 942 pub type TaskDependencies = SmallVec<[RenderTaskId;2]>; 943 944 #[cfg_attr(feature = "capture", derive(Serialize))] 945 #[cfg_attr(feature = "replay", derive(Deserialize))] 946 pub struct MaskSubPass { 947 pub clip_node_range: ClipNodeRange, 948 pub prim_spatial_node_index: SpatialNodeIndex, 949 pub prim_address_f: GpuBufferAddress, 950 } 951 952 #[cfg_attr(feature = "capture", derive(Serialize))] 953 #[cfg_attr(feature = "replay", derive(Deserialize))] 954 pub enum SubPass { 955 Masks { 956 masks: MaskSubPass, 957 }, 958 } 959 960 #[cfg_attr(feature = "capture", derive(Serialize))] 961 #[cfg_attr(feature = "replay", derive(Deserialize))] 962 pub struct RenderTask { 963 pub location: RenderTaskLocation, 964 pub children: TaskDependencies, 965 pub kind: RenderTaskKind, 966 pub sub_pass: Option<SubPass>, 967 968 // TODO(gw): These fields and perhaps others can become private once the 969 // frame_graph / render_task source files are unified / cleaned up. 970 pub free_after: PassId, 971 pub render_on: PassId, 972 973 /// The gpu cache handle for the render task's destination rect. 974 /// 975 /// Will be set to None if the render task is cached, in which case the texture cache 976 /// manages the handle. 977 pub uv_rect_handle: GpuBufferAddress, 978 pub cache_handle: Option<RenderTaskCacheEntryHandle>, 979 pub uv_rect_kind: UvRectKind, 980 } 981 982 impl RenderTask { 983 pub fn new( 984 location: RenderTaskLocation, 985 kind: RenderTaskKind, 986 ) -> Self { 987 render_task_sanity_check(&location.size()); 988 989 RenderTask { 990 location, 991 children: TaskDependencies::new(), 992 kind, 993 free_after: PassId::MAX, 994 render_on: PassId::MIN, 995 uv_rect_handle: GpuBufferAddress::INVALID, 996 uv_rect_kind: UvRectKind::Rect, 997 cache_handle: None, 998 sub_pass: None, 999 } 1000 } 1001 1002 pub fn new_dynamic( 1003 size: DeviceIntSize, 1004 kind: RenderTaskKind, 1005 ) -> Self { 1006 assert!(!size.is_empty(), "Bad {} render task size: {:?}", kind.as_str(), size); 1007 RenderTask::new( 1008 RenderTaskLocation::Unallocated { size }, 1009 kind, 1010 ) 1011 } 1012 1013 pub fn with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self { 1014 self.uv_rect_kind = uv_rect_kind; 1015 self 1016 } 1017 1018 pub fn new_image( 1019 size: DeviceIntSize, 1020 request: ImageRequest, 1021 is_composited: bool, 1022 ) -> Self { 1023 // Note: this is a special constructor for image render tasks that does not 1024 // do the render task size sanity check. This is because with SWGL we purposefully 1025 // avoid tiling large images. There is no upload with SWGL so whatever was 1026 // successfully allocated earlier will be what shaders read, regardless of the size 1027 // and copying into tiles would only slow things down. 1028 // As a result we can run into very large images being added to the frame graph 1029 // (this is covered by a few reftests on the CI). 1030 1031 RenderTask { 1032 location: RenderTaskLocation::CacheRequest { size, }, 1033 children: TaskDependencies::new(), 1034 kind: RenderTaskKind::Image(ImageRequestTask { 1035 request, 1036 is_composited, 1037 }), 1038 free_after: PassId::MAX, 1039 render_on: PassId::MIN, 1040 uv_rect_handle: GpuBufferAddress::INVALID, 1041 uv_rect_kind: UvRectKind::Rect, 1042 cache_handle: None, 1043 sub_pass: None, 1044 } 1045 } 1046 1047 1048 #[cfg(test)] 1049 pub fn new_test( 1050 location: RenderTaskLocation, 1051 target: RenderTargetKind, 1052 ) -> Self { 1053 RenderTask { 1054 location, 1055 children: TaskDependencies::new(), 1056 kind: RenderTaskKind::Test(target), 1057 free_after: PassId::MAX, 1058 render_on: PassId::MIN, 1059 uv_rect_handle: GpuBufferAddress::INVALID, 1060 uv_rect_kind: UvRectKind::Rect, 1061 cache_handle: None, 1062 sub_pass: None, 1063 } 1064 } 1065 1066 pub fn new_blit( 1067 size: DeviceIntSize, 1068 source: RenderTaskId, 1069 source_rect: DeviceIntRect, 1070 rg_builder: &mut RenderTaskGraphBuilder, 1071 ) -> RenderTaskId { 1072 // If this blit uses a render task as a source, 1073 // ensure it's added as a child task. This will 1074 // ensure it gets allocated in the correct pass 1075 // and made available as an input when this task 1076 // executes. 1077 1078 let blit_task_id = rg_builder.add().init(RenderTask::new_dynamic( 1079 size, 1080 RenderTaskKind::Blit(BlitTask { source, source_rect }), 1081 )); 1082 1083 rg_builder.add_dependency(blit_task_id, source); 1084 1085 blit_task_id 1086 } 1087 1088 // Construct a render task to apply a blur to a primitive. 1089 // The render task chain that is constructed looks like: 1090 // 1091 // PrimitiveCacheTask: Draw the primitives. 1092 // ^ 1093 // | 1094 // DownscalingTask(s): Each downscaling task reduces the size of render target to 1095 // ^ half. Also reduce the std deviation to half until the std 1096 // | deviation less than 4.0. 1097 // | 1098 // | 1099 // VerticalBlurTask: Apply the separable vertical blur to the primitive. 1100 // ^ 1101 // | 1102 // HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur. 1103 // | 1104 // +---- This is stored as the input task to the primitive shader. 1105 // 1106 pub fn new_blur( 1107 blur_std_deviation: DeviceSize, 1108 src_task_id: RenderTaskId, 1109 rg_builder: &mut RenderTaskGraphBuilder, 1110 target_kind: RenderTargetKind, 1111 mut blur_cache: Option<&mut BlurTaskCache>, 1112 blur_region: DeviceIntSize, 1113 edge_mode: BlurEdgeMode, 1114 ) -> RenderTaskId { 1115 // Adjust large std deviation value. 1116 let mut adjusted_blur_std_deviation = blur_std_deviation; 1117 let (blur_target_size, uv_rect_kind) = { 1118 let src_task = rg_builder.get_task(src_task_id); 1119 (src_task.location.size(), src_task.uv_rect_kind()) 1120 }; 1121 let mut adjusted_blur_target_size = blur_target_size; 1122 let mut downscaling_src_task_id = src_task_id; 1123 let mut scale_factor = 1.0; 1124 let mut n_downscales = 1; 1125 while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION && 1126 adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION { 1127 if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE || 1128 adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE { 1129 break; 1130 } 1131 adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5; 1132 scale_factor *= 2.0; 1133 adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32(); 1134 1135 let cached_task = match blur_cache { 1136 Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(), 1137 None => None, 1138 }; 1139 1140 downscaling_src_task_id = cached_task.unwrap_or_else(|| { 1141 RenderTask::new_scaling( 1142 downscaling_src_task_id, 1143 rg_builder, 1144 target_kind, 1145 adjusted_blur_target_size, 1146 ) 1147 }); 1148 1149 if let Some(ref mut cache) = blur_cache { 1150 cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id); 1151 } 1152 1153 n_downscales += 1; 1154 } 1155 1156 1157 let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation); 1158 1159 let cached_task = match blur_cache { 1160 Some(ref mut cache) => cache.get(&blur_key).cloned(), 1161 None => None, 1162 }; 1163 1164 let blur_region = blur_region / (scale_factor as i32); 1165 1166 let blur_task_id = cached_task.unwrap_or_else(|| { 1167 let blur_task_v = rg_builder.add().init(RenderTask::new_dynamic( 1168 adjusted_blur_target_size, 1169 RenderTaskKind::VerticalBlur(BlurTask { 1170 blur_std_deviation: adjusted_blur_std_deviation.height, 1171 target_kind, 1172 blur_region, 1173 edge_mode, 1174 }), 1175 ).with_uv_rect_kind(uv_rect_kind)); 1176 rg_builder.add_dependency(blur_task_v, downscaling_src_task_id); 1177 1178 let task_id = rg_builder.add().init(RenderTask::new_dynamic( 1179 adjusted_blur_target_size, 1180 RenderTaskKind::HorizontalBlur(BlurTask { 1181 blur_std_deviation: adjusted_blur_std_deviation.width, 1182 target_kind, 1183 blur_region, 1184 edge_mode, 1185 }), 1186 ).with_uv_rect_kind(uv_rect_kind)); 1187 rg_builder.add_dependency(task_id, blur_task_v); 1188 1189 task_id 1190 }); 1191 1192 if let Some(ref mut cache) = blur_cache { 1193 cache.insert(blur_key, blur_task_id); 1194 } 1195 1196 blur_task_id 1197 } 1198 1199 pub fn new_scaling( 1200 src_task_id: RenderTaskId, 1201 rg_builder: &mut RenderTaskGraphBuilder, 1202 target_kind: RenderTargetKind, 1203 size: DeviceIntSize, 1204 ) -> RenderTaskId { 1205 Self::new_scaling_with_padding( 1206 src_task_id, 1207 rg_builder, 1208 target_kind, 1209 size, 1210 DeviceIntSideOffsets::zero(), 1211 ) 1212 } 1213 1214 pub fn new_scaling_with_padding( 1215 source: RenderTaskId, 1216 rg_builder: &mut RenderTaskGraphBuilder, 1217 target_kind: RenderTargetKind, 1218 padded_size: DeviceIntSize, 1219 padding: DeviceIntSideOffsets, 1220 ) -> RenderTaskId { 1221 let uv_rect_kind = rg_builder.get_task(source).uv_rect_kind(); 1222 1223 let task_id = rg_builder.add().init( 1224 RenderTask::new_dynamic( 1225 padded_size, 1226 RenderTaskKind::Scaling(ScalingTask { 1227 target_kind, 1228 padding, 1229 }), 1230 ).with_uv_rect_kind(uv_rect_kind) 1231 ); 1232 1233 rg_builder.add_dependency(task_id, source); 1234 1235 task_id 1236 } 1237 1238 pub fn add_sub_pass( 1239 &mut self, 1240 sub_pass: SubPass, 1241 ) { 1242 assert!(self.sub_pass.is_none(), "multiple sub-passes are not supported for now"); 1243 self.sub_pass = Some(sub_pass); 1244 } 1245 1246 /// Creates render tasks from PictureCompositeMode::SVGFEGraph. 1247 /// 1248 /// The interesting parts of the handling of SVG filters are: 1249 /// * scene_building.rs : wrap_prim_with_filters 1250 /// * picture.rs : get_coverage_svgfe 1251 /// * render_task.rs : new_svg_filter_graph (you are here) 1252 /// * render_target.rs : add_svg_filter_node_instances 1253 pub fn new_svg_filter_graph( 1254 filter_nodes: &[(FilterGraphNode, FilterGraphOp)], 1255 rg_builder: &mut RenderTaskGraphBuilder, 1256 gpu_buffer: &mut GpuBufferBuilderF, 1257 data_stores: &mut DataStores, 1258 _uv_rect_kind: UvRectKind, 1259 original_task_id: RenderTaskId, 1260 source_subregion: LayoutRect, 1261 target_subregion: LayoutRect, 1262 prim_subregion: LayoutRect, 1263 subregion_to_device_scale_x: f32, 1264 subregion_to_device_scale_y: f32, 1265 subregion_to_device_offset_x: f32, 1266 subregion_to_device_offset_y: f32, 1267 ) -> RenderTaskId { 1268 const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX; 1269 let mut task_by_buffer_id: [RenderTaskId; BUFFER_LIMIT] = [RenderTaskId::INVALID; BUFFER_LIMIT]; 1270 let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = [LayoutRect::zero(); BUFFER_LIMIT]; 1271 // If nothing replaces this value (all node subregions are empty), we 1272 // can just return the original picture 1273 let mut output_task_id = original_task_id; 1274 1275 // By this point we assume the following about the graph: 1276 // * BUFFER_LIMIT here should be >= BUFFER_LIMIT in the scene_building.rs code. 1277 // * input buffer id < output buffer id 1278 // * output buffer id between 0 and BUFFER_LIMIT 1279 // * the number of filter_datas matches the number of kept nodes with op 1280 // SVGFEComponentTransfer. 1281 // 1282 // These assumptions are verified with asserts in this function as 1283 // appropriate. 1284 1285 // Make a UvRectKind::Quad that represents a task for a node, which may 1286 // have an inflate border, must be a Quad because the surface_rects 1287 // compositing shader expects it to be one, we don't actually use this 1288 // internally as we use subregions, see calculate_uv_rect_kind for how 1289 // this works, it projects from clipped rect to unclipped rect, where 1290 // our clipped rect is simply task_size minus the inflate, and unclipped 1291 // is our full task_size 1292 fn uv_rect_kind_for_task_size(clipped: DeviceRect, unclipped: DeviceRect) -> UvRectKind { 1293 let scale_x = 1.0 / clipped.width(); 1294 let scale_y = 1.0 / clipped.height(); 1295 UvRectKind::Quad{ 1296 top_left: DeviceHomogeneousVector::new( 1297 (unclipped.min.x - clipped.min.x) * scale_x, 1298 (unclipped.min.y - clipped.min.y) * scale_y, 1299 0.0, 1.0), 1300 top_right: DeviceHomogeneousVector::new( 1301 (unclipped.max.x - clipped.min.x) * scale_x, 1302 (unclipped.min.y - clipped.min.y) * scale_y, 1303 0.0, 1.0), 1304 bottom_left: DeviceHomogeneousVector::new( 1305 (unclipped.min.x - clipped.min.x) * scale_x, 1306 (unclipped.max.y - clipped.min.y) * scale_y, 1307 0.0, 1.0), 1308 bottom_right: DeviceHomogeneousVector::new( 1309 (unclipped.max.x - clipped.min.x) * scale_x, 1310 (unclipped.max.y - clipped.min.y) * scale_y, 1311 0.0, 1.0), 1312 } 1313 } 1314 1315 // Iterate the filter nodes and create tasks 1316 let mut made_dependency_on_source = false; 1317 for (filter_index, (filter_node, op)) in filter_nodes.iter().enumerate() { 1318 let node = &filter_node; 1319 let is_output = filter_index == filter_nodes.len() - 1; 1320 1321 // Note that this is never set on the final output by design. 1322 if !node.kept_by_optimizer { 1323 continue; 1324 } 1325 1326 // Certain ops have parameters that need to be scaled to device 1327 // space. 1328 let op = match op { 1329 FilterGraphOp::SVGFEBlendColor => op.clone(), 1330 FilterGraphOp::SVGFEBlendColorBurn => op.clone(), 1331 FilterGraphOp::SVGFEBlendColorDodge => op.clone(), 1332 FilterGraphOp::SVGFEBlendDarken => op.clone(), 1333 FilterGraphOp::SVGFEBlendDifference => op.clone(), 1334 FilterGraphOp::SVGFEBlendExclusion => op.clone(), 1335 FilterGraphOp::SVGFEBlendHardLight => op.clone(), 1336 FilterGraphOp::SVGFEBlendHue => op.clone(), 1337 FilterGraphOp::SVGFEBlendLighten => op.clone(), 1338 FilterGraphOp::SVGFEBlendLuminosity => op.clone(), 1339 FilterGraphOp::SVGFEBlendMultiply => op.clone(), 1340 FilterGraphOp::SVGFEBlendNormal => op.clone(), 1341 FilterGraphOp::SVGFEBlendOverlay => op.clone(), 1342 FilterGraphOp::SVGFEBlendSaturation => op.clone(), 1343 FilterGraphOp::SVGFEBlendScreen => op.clone(), 1344 FilterGraphOp::SVGFEBlendSoftLight => op.clone(), 1345 FilterGraphOp::SVGFEColorMatrix{..} => op.clone(), 1346 FilterGraphOp::SVGFEComponentTransfer => unreachable!(), 1347 FilterGraphOp::SVGFEComponentTransferInterned{..} => op.clone(), 1348 FilterGraphOp::SVGFECompositeArithmetic{..} => op.clone(), 1349 FilterGraphOp::SVGFECompositeATop => op.clone(), 1350 FilterGraphOp::SVGFECompositeIn => op.clone(), 1351 FilterGraphOp::SVGFECompositeLighter => op.clone(), 1352 FilterGraphOp::SVGFECompositeOut => op.clone(), 1353 FilterGraphOp::SVGFECompositeOver => op.clone(), 1354 FilterGraphOp::SVGFECompositeXOR => op.clone(), 1355 FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{ 1356 kernel_unit_length_x, kernel_unit_length_y, order_x, 1357 order_y, kernel, divisor, bias, target_x, target_y, 1358 preserve_alpha} => { 1359 FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{ 1360 kernel_unit_length_x: 1361 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1362 kernel_unit_length_y: 1363 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1364 order_x: *order_x, order_y: *order_y, kernel: *kernel, 1365 divisor: *divisor, bias: *bias, target_x: *target_x, 1366 target_y: *target_y, preserve_alpha: *preserve_alpha} 1367 }, 1368 FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{ 1369 kernel_unit_length_x, kernel_unit_length_y, order_x, 1370 order_y, kernel, divisor, bias, target_x, target_y, 1371 preserve_alpha} => { 1372 FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{ 1373 kernel_unit_length_x: 1374 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1375 kernel_unit_length_y: 1376 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1377 order_x: *order_x, order_y: *order_y, kernel: *kernel, 1378 divisor: *divisor, bias: *bias, target_x: *target_x, 1379 target_y: *target_y, preserve_alpha: *preserve_alpha} 1380 }, 1381 FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{ 1382 kernel_unit_length_x, kernel_unit_length_y, order_x, 1383 order_y, kernel, divisor, bias, target_x, target_y, 1384 preserve_alpha} => { 1385 FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{ 1386 kernel_unit_length_x: 1387 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1388 kernel_unit_length_y: 1389 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1390 order_x: *order_x, order_y: *order_y, kernel: *kernel, 1391 divisor: *divisor, bias: *bias, target_x: *target_x, 1392 target_y: *target_y, preserve_alpha: *preserve_alpha} 1393 }, 1394 FilterGraphOp::SVGFEDiffuseLightingDistant{ 1395 surface_scale, diffuse_constant, kernel_unit_length_x, 1396 kernel_unit_length_y, azimuth, elevation} => { 1397 FilterGraphOp::SVGFEDiffuseLightingDistant{ 1398 surface_scale: *surface_scale, 1399 diffuse_constant: *diffuse_constant, 1400 kernel_unit_length_x: 1401 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1402 kernel_unit_length_y: 1403 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1404 azimuth: *azimuth, elevation: *elevation} 1405 }, 1406 FilterGraphOp::SVGFEDiffuseLightingPoint{ 1407 surface_scale, diffuse_constant, kernel_unit_length_x, 1408 kernel_unit_length_y, x, y, z} => { 1409 FilterGraphOp::SVGFEDiffuseLightingPoint{ 1410 surface_scale: *surface_scale, 1411 diffuse_constant: *diffuse_constant, 1412 kernel_unit_length_x: 1413 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1414 kernel_unit_length_y: 1415 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1416 x: x * subregion_to_device_scale_x + subregion_to_device_offset_x, 1417 y: y * subregion_to_device_scale_y + subregion_to_device_offset_y, 1418 z: *z} 1419 }, 1420 FilterGraphOp::SVGFEDiffuseLightingSpot{ 1421 surface_scale, diffuse_constant, kernel_unit_length_x, 1422 kernel_unit_length_y, x, y, z, points_at_x, points_at_y, 1423 points_at_z, cone_exponent, limiting_cone_angle} => { 1424 FilterGraphOp::SVGFEDiffuseLightingSpot{ 1425 surface_scale: *surface_scale, 1426 diffuse_constant: *diffuse_constant, 1427 kernel_unit_length_x: 1428 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1429 kernel_unit_length_y: 1430 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1431 x: x * subregion_to_device_scale_x + subregion_to_device_offset_x, 1432 y: y * subregion_to_device_scale_y + subregion_to_device_offset_y, 1433 z: *z, 1434 points_at_x: points_at_x * subregion_to_device_scale_x + subregion_to_device_offset_x, 1435 points_at_y: points_at_y * subregion_to_device_scale_y + subregion_to_device_offset_y, 1436 points_at_z: *points_at_z, 1437 cone_exponent: *cone_exponent, 1438 limiting_cone_angle: *limiting_cone_angle} 1439 }, 1440 FilterGraphOp::SVGFEFlood{..} => op.clone(), 1441 FilterGraphOp::SVGFEDisplacementMap{ 1442 scale, x_channel_selector, y_channel_selector} => { 1443 FilterGraphOp::SVGFEDisplacementMap{ 1444 scale: scale * subregion_to_device_scale_x, 1445 x_channel_selector: *x_channel_selector, 1446 y_channel_selector: *y_channel_selector} 1447 }, 1448 FilterGraphOp::SVGFEDropShadow{ 1449 color, dx, dy, std_deviation_x, std_deviation_y} => { 1450 FilterGraphOp::SVGFEDropShadow{ 1451 color: *color, 1452 dx: dx * subregion_to_device_scale_x, 1453 dy: dy * subregion_to_device_scale_y, 1454 std_deviation_x: std_deviation_x * subregion_to_device_scale_x, 1455 std_deviation_y: std_deviation_y * subregion_to_device_scale_y} 1456 }, 1457 FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => { 1458 let std_deviation_x = std_deviation_x * subregion_to_device_scale_x; 1459 let std_deviation_y = std_deviation_y * subregion_to_device_scale_y; 1460 // For blurs that effectively have no radius in display 1461 // space, we can convert to identity. 1462 if std_deviation_x + std_deviation_y >= 0.125 { 1463 FilterGraphOp::SVGFEGaussianBlur{ 1464 std_deviation_x, 1465 std_deviation_y} 1466 } else { 1467 FilterGraphOp::SVGFEIdentity 1468 } 1469 }, 1470 FilterGraphOp::SVGFEIdentity => op.clone(), 1471 FilterGraphOp::SVGFEImage{..} => op.clone(), 1472 FilterGraphOp::SVGFEMorphologyDilate{radius_x, radius_y} => { 1473 FilterGraphOp::SVGFEMorphologyDilate{ 1474 radius_x: (radius_x * subregion_to_device_scale_x).round(), 1475 radius_y: (radius_y * subregion_to_device_scale_y).round()} 1476 }, 1477 FilterGraphOp::SVGFEMorphologyErode{radius_x, radius_y} => { 1478 FilterGraphOp::SVGFEMorphologyErode{ 1479 radius_x: (radius_x * subregion_to_device_scale_x).round(), 1480 radius_y: (radius_y * subregion_to_device_scale_y).round()} 1481 }, 1482 FilterGraphOp::SVGFEOpacity{..} => op.clone(), 1483 FilterGraphOp::SVGFESourceAlpha => op.clone(), 1484 FilterGraphOp::SVGFESourceGraphic => op.clone(), 1485 FilterGraphOp::SVGFESpecularLightingDistant{ 1486 surface_scale, specular_constant, specular_exponent, 1487 kernel_unit_length_x, kernel_unit_length_y, azimuth, 1488 elevation} => { 1489 FilterGraphOp::SVGFESpecularLightingDistant{ 1490 surface_scale: *surface_scale, 1491 specular_constant: *specular_constant, 1492 specular_exponent: *specular_exponent, 1493 kernel_unit_length_x: 1494 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1495 kernel_unit_length_y: 1496 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1497 azimuth: *azimuth, elevation: *elevation} 1498 }, 1499 FilterGraphOp::SVGFESpecularLightingPoint{ 1500 surface_scale, specular_constant, specular_exponent, 1501 kernel_unit_length_x, kernel_unit_length_y, x, y, z } => { 1502 FilterGraphOp::SVGFESpecularLightingPoint{ 1503 surface_scale: *surface_scale, 1504 specular_constant: *specular_constant, 1505 specular_exponent: *specular_exponent, 1506 kernel_unit_length_x: 1507 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1508 kernel_unit_length_y: 1509 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1510 x: x * subregion_to_device_scale_x + subregion_to_device_offset_x, 1511 y: y * subregion_to_device_scale_y + subregion_to_device_offset_y, 1512 z: *z } 1513 }, 1514 FilterGraphOp::SVGFESpecularLightingSpot{ 1515 surface_scale, specular_constant, specular_exponent, 1516 kernel_unit_length_x, kernel_unit_length_y, x, y, z, 1517 points_at_x, points_at_y, points_at_z, cone_exponent, 1518 limiting_cone_angle} => { 1519 FilterGraphOp::SVGFESpecularLightingSpot{ 1520 surface_scale: *surface_scale, 1521 specular_constant: *specular_constant, 1522 specular_exponent: *specular_exponent, 1523 kernel_unit_length_x: 1524 (kernel_unit_length_x * subregion_to_device_scale_x).round(), 1525 kernel_unit_length_y: 1526 (kernel_unit_length_y * subregion_to_device_scale_y).round(), 1527 x: x * subregion_to_device_scale_x + subregion_to_device_offset_x, 1528 y: y * subregion_to_device_scale_y + subregion_to_device_offset_y, 1529 z: *z, 1530 points_at_x: points_at_x * subregion_to_device_scale_x + subregion_to_device_offset_x, 1531 points_at_y: points_at_y * subregion_to_device_scale_y + subregion_to_device_offset_y, 1532 points_at_z: *points_at_z, 1533 cone_exponent: *cone_exponent, 1534 limiting_cone_angle: *limiting_cone_angle} 1535 }, 1536 FilterGraphOp::SVGFETile => op.clone(), 1537 FilterGraphOp::SVGFEToAlpha => op.clone(), 1538 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ 1539 base_frequency_x, base_frequency_y, num_octaves, seed} => { 1540 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ 1541 base_frequency_x: 1542 base_frequency_x * subregion_to_device_scale_x, 1543 base_frequency_y: 1544 base_frequency_y * subregion_to_device_scale_y, 1545 num_octaves: *num_octaves, seed: *seed} 1546 }, 1547 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{ 1548 base_frequency_x, base_frequency_y, num_octaves, seed} => { 1549 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ 1550 base_frequency_x: 1551 base_frequency_x * subregion_to_device_scale_x, 1552 base_frequency_y: 1553 base_frequency_y * subregion_to_device_scale_y, 1554 num_octaves: *num_octaves, seed: *seed} 1555 }, 1556 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{ 1557 base_frequency_x, base_frequency_y, num_octaves, seed} => { 1558 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ 1559 base_frequency_x: 1560 base_frequency_x * subregion_to_device_scale_x, 1561 base_frequency_y: 1562 base_frequency_y * subregion_to_device_scale_y, 1563 num_octaves: *num_octaves, seed: *seed} 1564 }, 1565 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{ 1566 base_frequency_x, base_frequency_y, num_octaves, seed} => { 1567 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{ 1568 base_frequency_x: 1569 base_frequency_x * subregion_to_device_scale_x, 1570 base_frequency_y: 1571 base_frequency_y * subregion_to_device_scale_y, 1572 num_octaves: *num_octaves, seed: *seed} 1573 }, 1574 }; 1575 1576 // Process the inputs and figure out their new subregion, because 1577 // the SourceGraphic subregion is smaller than it was in scene build 1578 // now that it reflects the invalidation rect 1579 // 1580 // Also look up the child tasks while we are here. 1581 let mut used_subregion = LayoutRect::zero(); 1582 let mut combined_input_subregion = LayoutRect::zero(); 1583 let node_inputs: Vec<(FilterGraphPictureReference, RenderTaskId)> = node.inputs.iter().map(|input| { 1584 let (subregion, task) = 1585 match input.buffer_id { 1586 FilterOpGraphPictureBufferId::BufferId(id) => { 1587 (subregion_by_buffer_id[id as usize], task_by_buffer_id[id as usize]) 1588 } 1589 FilterOpGraphPictureBufferId::None => { 1590 // Task must resolve so we use the SourceGraphic as 1591 // a placeholder for these, they don't actually 1592 // contribute anything to the output 1593 (LayoutRect::zero(), original_task_id) 1594 } 1595 }; 1596 // Convert offset to device coordinates. 1597 let offset = LayoutVector2D::new( 1598 (input.offset.x * subregion_to_device_scale_x).round(), 1599 (input.offset.y * subregion_to_device_scale_y).round(), 1600 ); 1601 // To figure out the portion of the node subregion used by this 1602 // source image we need to apply the target padding. Note that 1603 // this does not affect the subregion of the input, as that 1604 // can't be modified as it is used for placement (offset). 1605 let target_padding = input.target_padding 1606 .scale(subregion_to_device_scale_x, subregion_to_device_scale_y) 1607 .round(); 1608 let target_subregion = 1609 LayoutRect::new( 1610 LayoutPoint::new( 1611 subregion.min.x + target_padding.min.x, 1612 subregion.min.y + target_padding.min.y, 1613 ), 1614 LayoutPoint::new( 1615 subregion.max.x + target_padding.max.x, 1616 subregion.max.y + target_padding.max.y, 1617 ), 1618 ); 1619 used_subregion = used_subregion.union(&target_subregion); 1620 combined_input_subregion = combined_input_subregion.union(&subregion); 1621 (FilterGraphPictureReference{ 1622 buffer_id: input.buffer_id, 1623 // Apply offset to the placement of the input subregion. 1624 subregion: subregion.translate(offset), 1625 offset: LayoutVector2D::zero(), 1626 inflate: input.inflate, 1627 // Nothing past this point uses the padding. 1628 source_padding: LayoutRect::zero(), 1629 target_padding: LayoutRect::zero(), 1630 }, task) 1631 }).collect(); 1632 1633 // Convert subregion from PicturePixels to DevicePixels and round. 1634 let full_subregion = node.subregion 1635 .scale(subregion_to_device_scale_x, subregion_to_device_scale_y) 1636 .translate(LayoutVector2D::new(subregion_to_device_offset_x, subregion_to_device_offset_y)) 1637 .round(); 1638 1639 // Clip the used subregion we calculated from the inputs to fit 1640 // within the node's specified subregion, but we want to keep a copy 1641 // of the combined input subregion for sizing tasks that involve 1642 // blurs as their intermediate stages will have to be downscaled if 1643 // very large, and we want that to be at the same alignment as the 1644 // node output itself. 1645 used_subregion = used_subregion 1646 .intersection(&full_subregion) 1647 .unwrap_or(LayoutRect::zero()) 1648 .round(); 1649 1650 // Certain filters need to override the used_subregion directly. 1651 match op { 1652 FilterGraphOp::SVGFEBlendColor => {}, 1653 FilterGraphOp::SVGFEBlendColorBurn => {}, 1654 FilterGraphOp::SVGFEBlendColorDodge => {}, 1655 FilterGraphOp::SVGFEBlendDarken => {}, 1656 FilterGraphOp::SVGFEBlendDifference => {}, 1657 FilterGraphOp::SVGFEBlendExclusion => {}, 1658 FilterGraphOp::SVGFEBlendHardLight => {}, 1659 FilterGraphOp::SVGFEBlendHue => {}, 1660 FilterGraphOp::SVGFEBlendLighten => {}, 1661 FilterGraphOp::SVGFEBlendLuminosity => {}, 1662 FilterGraphOp::SVGFEBlendMultiply => {}, 1663 FilterGraphOp::SVGFEBlendNormal => {}, 1664 FilterGraphOp::SVGFEBlendOverlay => {}, 1665 FilterGraphOp::SVGFEBlendSaturation => {}, 1666 FilterGraphOp::SVGFEBlendScreen => {}, 1667 FilterGraphOp::SVGFEBlendSoftLight => {}, 1668 FilterGraphOp::SVGFEColorMatrix{values} => { 1669 if values[19] > 0.0 { 1670 // Manipulating alpha offset can easily create new 1671 // pixels outside of input subregions 1672 used_subregion = full_subregion; 1673 } 1674 }, 1675 FilterGraphOp::SVGFEComponentTransfer => unreachable!(), 1676 FilterGraphOp::SVGFEComponentTransferInterned{handle: _, creates_pixels} => { 1677 // Check if the value of alpha[0] is modified, if so 1678 // the whole subregion is used because it will be 1679 // creating new pixels outside of input subregions 1680 if creates_pixels { 1681 used_subregion = full_subregion; 1682 } 1683 }, 1684 FilterGraphOp::SVGFECompositeArithmetic { k1, k2, k3, k4 } => { 1685 // Optimize certain cases of Arithmetic operator 1686 // 1687 // See logic for SVG_FECOMPOSITE_OPERATOR_ARITHMETIC 1688 // in FilterSupport.cpp for more information. 1689 // 1690 // Any other case uses the union of input subregions 1691 if k4 > 0.0 { 1692 // Can produce pixels anywhere in the subregion. 1693 used_subregion = full_subregion; 1694 } else if k1 > 0.0 && k2 == 0.0 && k3 == 0.0 { 1695 // Can produce pixels where both exist. 1696 used_subregion = full_subregion 1697 .intersection(&node_inputs[0].0.subregion) 1698 .unwrap_or(LayoutRect::zero()) 1699 .intersection(&node_inputs[1].0.subregion) 1700 .unwrap_or(LayoutRect::zero()); 1701 } 1702 else if k2 > 0.0 && k3 == 0.0 { 1703 // Can produce pixels where source exists. 1704 used_subregion = full_subregion 1705 .intersection(&node_inputs[0].0.subregion) 1706 .unwrap_or(LayoutRect::zero()); 1707 } 1708 else if k2 == 0.0 && k3 > 0.0 { 1709 // Can produce pixels where background exists. 1710 used_subregion = full_subregion 1711 .intersection(&node_inputs[1].0.subregion) 1712 .unwrap_or(LayoutRect::zero()); 1713 } 1714 }, 1715 FilterGraphOp::SVGFECompositeATop => { 1716 // Can only produce pixels where background exists. 1717 used_subregion = full_subregion 1718 .intersection(&node_inputs[1].0.subregion) 1719 .unwrap_or(LayoutRect::zero()); 1720 }, 1721 FilterGraphOp::SVGFECompositeIn => { 1722 // Can only produce pixels where both exist. 1723 used_subregion = used_subregion 1724 .intersection(&node_inputs[0].0.subregion) 1725 .unwrap_or(LayoutRect::zero()) 1726 .intersection(&node_inputs[1].0.subregion) 1727 .unwrap_or(LayoutRect::zero()); 1728 }, 1729 FilterGraphOp::SVGFECompositeLighter => {}, 1730 FilterGraphOp::SVGFECompositeOut => { 1731 // Can only produce pixels where source exists. 1732 used_subregion = full_subregion 1733 .intersection(&node_inputs[0].0.subregion) 1734 .unwrap_or(LayoutRect::zero()); 1735 }, 1736 FilterGraphOp::SVGFECompositeOver => {}, 1737 FilterGraphOp::SVGFECompositeXOR => {}, 1738 FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {}, 1739 FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {}, 1740 FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {}, 1741 FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {}, 1742 FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {}, 1743 FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {}, 1744 FilterGraphOp::SVGFEDisplacementMap{..} => {}, 1745 FilterGraphOp::SVGFEDropShadow{..} => {}, 1746 FilterGraphOp::SVGFEFlood { color } => { 1747 // Subregion needs to be set to the full node 1748 // subregion for fills (unless the fill is a no-op), 1749 // we know at this point that it has no inputs, so the 1750 // used_region is empty unless we set it here. 1751 if color.a > 0.0 { 1752 used_subregion = full_subregion; 1753 } 1754 }, 1755 FilterGraphOp::SVGFEIdentity => {}, 1756 FilterGraphOp::SVGFEImage { sampling_filter: _sampling_filter, matrix: _matrix } => { 1757 // TODO: calculate the actual subregion 1758 used_subregion = full_subregion; 1759 }, 1760 FilterGraphOp::SVGFEGaussianBlur{..} => {}, 1761 FilterGraphOp::SVGFEMorphologyDilate{..} => {}, 1762 FilterGraphOp::SVGFEMorphologyErode{..} => {}, 1763 FilterGraphOp::SVGFEOpacity{valuebinding: _valuebinding, value} => { 1764 // If fully transparent, we can ignore this node 1765 if value <= 0.0 { 1766 used_subregion = LayoutRect::zero(); 1767 } 1768 }, 1769 FilterGraphOp::SVGFESourceAlpha | 1770 FilterGraphOp::SVGFESourceGraphic => { 1771 used_subregion = source_subregion 1772 .intersection(&full_subregion) 1773 .unwrap_or(LayoutRect::zero()); 1774 }, 1775 FilterGraphOp::SVGFESpecularLightingDistant{..} => {}, 1776 FilterGraphOp::SVGFESpecularLightingPoint{..} => {}, 1777 FilterGraphOp::SVGFESpecularLightingSpot{..} => {}, 1778 FilterGraphOp::SVGFETile => { 1779 if !used_subregion.is_empty() { 1780 // This fills the entire target, at least if there are 1781 // any input pixels to work with. 1782 used_subregion = full_subregion; 1783 } 1784 }, 1785 FilterGraphOp::SVGFEToAlpha => {}, 1786 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} | 1787 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} | 1788 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} | 1789 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => { 1790 // Turbulence produces pixel values throughout the 1791 // node subregion. 1792 used_subregion = full_subregion; 1793 }, 1794 } 1795 1796 add_text_marker( 1797 "SVGFEGraph", 1798 &format!("{}({})", op.kind(), filter_index), 1799 Duration::from_micros((used_subregion.width() * used_subregion.height() / 1000.0) as u64), 1800 ); 1801 1802 // SVG spec requires that a later node sampling pixels outside 1803 // this node's subregion will receive a transparent black color 1804 // for those samples, we achieve this by adding a 1 pixel inflate 1805 // around the target rect, which works fine with the 1806 // edgemode=duplicate behavior of the texture fetch in the shader, 1807 // all of the out of bounds reads are transparent black. 1808 // 1809 // If this is the output node, we don't apply the inflate, knowing 1810 // that the pixels outside of the invalidation rect will not be used 1811 // so it is okay if they duplicate outside the view. 1812 let mut node_inflate = node.inflate; 1813 if is_output { 1814 // Use the provided target subregion (invalidation rect) 1815 used_subregion = target_subregion; 1816 node_inflate = 0; 1817 } 1818 1819 // We can't render tasks larger than a certain size, if this node 1820 // is too large (particularly with blur padding), we need to render 1821 // at a reduced resolution, later nodes can still be full resolution 1822 // but for example blurs are not significantly harmed by reduced 1823 // resolution in most cases. 1824 let mut device_to_render_scale = 1.0; 1825 let mut render_to_device_scale = 1.0; 1826 let mut subregion = used_subregion; 1827 let padded_subregion = match op { 1828 FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} | 1829 FilterGraphOp::SVGFEDropShadow{std_deviation_x, std_deviation_y, ..} => { 1830 used_subregion 1831 .inflate( 1832 std_deviation_x.ceil() * BLUR_SAMPLE_SCALE, 1833 std_deviation_y.ceil() * BLUR_SAMPLE_SCALE) 1834 } 1835 _ => used_subregion, 1836 }.union(&combined_input_subregion); 1837 while 1838 padded_subregion.scale(device_to_render_scale, device_to_render_scale).round().width() + node_inflate as f32 * 2.0 > MAX_SURFACE_SIZE as f32 || 1839 padded_subregion.scale(device_to_render_scale, device_to_render_scale).round().height() + node_inflate as f32 * 2.0 > MAX_SURFACE_SIZE as f32 { 1840 device_to_render_scale *= 0.5; 1841 render_to_device_scale *= 2.0; 1842 // If the rendering was scaled, we need to snap used_subregion 1843 // to the correct granularity or we'd have misaligned sampling 1844 // when this is used as an input later. 1845 subregion = used_subregion 1846 .scale(device_to_render_scale, device_to_render_scale) 1847 .round() 1848 .scale(render_to_device_scale, render_to_device_scale); 1849 } 1850 1851 // This is the rect we will be actually producing as a render task, 1852 // it is sometimes the case that subregion is empty, but we 1853 // must make a task or else the earlier tasks would not be properly 1854 // linked into the frametree, causing a leak. 1855 let node_task_rect: DeviceRect = 1856 subregion 1857 .scale(device_to_render_scale, device_to_render_scale) 1858 .round() 1859 .inflate(node_inflate as f32, node_inflate as f32) 1860 .cast_unit(); 1861 let node_task_size = node_task_rect.to_i32().size(); 1862 let node_task_size = 1863 if node_task_size.width < 1 || node_task_size.height < 1 { 1864 DeviceIntSize::new(1, 1) 1865 } else { 1866 node_task_size 1867 }; 1868 1869 // Make the uv_rect_kind for this node's task to use, this matters 1870 // only on the final node because we don't use it internally 1871 let node_uv_rect_kind = uv_rect_kind_for_task_size( 1872 subregion 1873 .scale(device_to_render_scale, device_to_render_scale) 1874 .round() 1875 .inflate(node_inflate as f32, node_inflate as f32) 1876 .cast_unit(), 1877 prim_subregion 1878 .scale(device_to_render_scale, device_to_render_scale) 1879 .round() 1880 .inflate(node_inflate as f32, node_inflate as f32) 1881 .cast_unit(), 1882 ); 1883 1884 // Create task for this node 1885 let task_id; 1886 match op { 1887 FilterGraphOp::SVGFEGaussianBlur { std_deviation_x, std_deviation_y } => { 1888 // Note: wrap_prim_with_filters copies the SourceGraphic to 1889 // a node to apply the transparent border around the image, 1890 // we rely on that behavior here as the Blur filter is a 1891 // different shader without awareness of the subregion 1892 // rules in the SVG spec. 1893 1894 // Find the input task id 1895 assert!(node_inputs.len() == 1); 1896 let blur_input = &node_inputs[0].0; 1897 let source_task_id = node_inputs[0].1; 1898 1899 // We have to make a copy of the input that is padded with 1900 // transparent black for the area outside the subregion, so 1901 // that the blur task does not duplicate at the edges 1902 let adjusted_blur_std_deviation = DeviceSize::new( 1903 std_deviation_x.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale, 1904 std_deviation_y.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale, 1905 ); 1906 let blur_subregion = blur_input.subregion 1907 .scale(device_to_render_scale, device_to_render_scale) 1908 .inflate( 1909 adjusted_blur_std_deviation.width * BLUR_SAMPLE_SCALE, 1910 adjusted_blur_std_deviation.height * BLUR_SAMPLE_SCALE) 1911 .round_out(); 1912 let blur_task_size = blur_subregion 1913 .size() 1914 .cast_unit() 1915 .max(DeviceSize::new(1.0, 1.0)); 1916 // Adjust task size to prevent potential sampling errors 1917 let adjusted_blur_task_size = 1918 BlurTask::adjusted_blur_source_size( 1919 blur_task_size, 1920 adjusted_blur_std_deviation, 1921 ).max(DeviceSize::new(1.0, 1.0)); 1922 // Now change the subregion to match the revised task size, 1923 // keeping it centered should keep animated radius smooth. 1924 let corner = LayoutPoint::new( 1925 blur_subregion.min.x.floor() + (( 1926 blur_task_size.width - 1927 adjusted_blur_task_size.width) * 0.5).floor(), 1928 blur_subregion.min.y.floor() + (( 1929 blur_task_size.height - 1930 adjusted_blur_task_size.height) * 0.5).floor(), 1931 ); 1932 // Recalculate the blur_subregion to match, and if render 1933 // scale is used, undo that so it is in the same subregion 1934 // coordinate system as the node 1935 let blur_subregion = 1936 LayoutRect::new( 1937 corner, 1938 LayoutPoint::new( 1939 corner.x + adjusted_blur_task_size.width, 1940 corner.y + adjusted_blur_task_size.height, 1941 ), 1942 ) 1943 .scale(render_to_device_scale, render_to_device_scale); 1944 1945 let input_subregion_task_id = rg_builder.add().init(RenderTask::new_dynamic( 1946 adjusted_blur_task_size.to_i32(), 1947 RenderTaskKind::SVGFENode( 1948 SVGFEFilterTask{ 1949 node: FilterGraphNode{ 1950 kept_by_optimizer: true, 1951 linear: false, 1952 inflate: 0, 1953 inputs: [blur_input.clone()].to_vec(), 1954 subregion: blur_subregion, 1955 }, 1956 op: FilterGraphOp::SVGFEIdentity, 1957 content_origin: DevicePoint::zero(), 1958 extra_gpu_data: None, 1959 } 1960 ), 1961 ).with_uv_rect_kind(UvRectKind::Rect)); 1962 // Adding the dependencies sets the inputs for this task 1963 rg_builder.add_dependency(input_subregion_task_id, source_task_id); 1964 1965 // TODO: We should do this blur in the correct 1966 // colorspace, linear=true is the default in SVG and 1967 // new_blur does not currently support it. If the nodes 1968 // that consume the result only use the alpha channel, it 1969 // does not matter, but when they use the RGB it matters. 1970 let blur_task_id = 1971 RenderTask::new_blur( 1972 adjusted_blur_std_deviation, 1973 input_subregion_task_id, 1974 rg_builder, 1975 RenderTargetKind::Color, 1976 None, 1977 adjusted_blur_task_size.to_i32(), 1978 BlurEdgeMode::Duplicate, 1979 ); 1980 1981 task_id = rg_builder.add().init(RenderTask::new_dynamic( 1982 node_task_size, 1983 RenderTaskKind::SVGFENode( 1984 SVGFEFilterTask{ 1985 node: FilterGraphNode{ 1986 kept_by_optimizer: true, 1987 linear: node.linear, 1988 inflate: node_inflate, 1989 inputs: [ 1990 FilterGraphPictureReference{ 1991 buffer_id: blur_input.buffer_id, 1992 subregion: blur_subregion, 1993 inflate: 0, 1994 offset: LayoutVector2D::zero(), 1995 source_padding: LayoutRect::zero(), 1996 target_padding: LayoutRect::zero(), 1997 }].to_vec(), 1998 subregion, 1999 }, 2000 op: FilterGraphOp::SVGFEIdentity, 2001 content_origin: node_task_rect.min, 2002 extra_gpu_data: None, 2003 } 2004 ), 2005 ).with_uv_rect_kind(node_uv_rect_kind)); 2006 // Adding the dependencies sets the inputs for this task 2007 rg_builder.add_dependency(task_id, blur_task_id); 2008 } 2009 FilterGraphOp::SVGFEDropShadow { color, dx, dy, std_deviation_x, std_deviation_y } => { 2010 // Note: wrap_prim_with_filters copies the SourceGraphic to 2011 // a node to apply the transparent border around the image, 2012 // we rely on that behavior here as the Blur filter is a 2013 // different shader without awareness of the subregion 2014 // rules in the SVG spec. 2015 2016 // Find the input task id 2017 assert!(node_inputs.len() == 1); 2018 let blur_input = &node_inputs[0].0; 2019 let source_task_id = node_inputs[0].1; 2020 2021 // We have to make a copy of the input that is padded with 2022 // transparent black for the area outside the subregion, so 2023 // that the blur task does not duplicate at the edges 2024 let adjusted_blur_std_deviation = DeviceSize::new( 2025 std_deviation_x.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale, 2026 std_deviation_y.clamp(0.0, (i32::MAX / 2) as f32) * device_to_render_scale, 2027 ); 2028 let blur_subregion = blur_input.subregion 2029 .scale(device_to_render_scale, device_to_render_scale) 2030 .inflate( 2031 adjusted_blur_std_deviation.width * BLUR_SAMPLE_SCALE, 2032 adjusted_blur_std_deviation.height * BLUR_SAMPLE_SCALE) 2033 .round_out(); 2034 let blur_task_size = blur_subregion 2035 .size() 2036 .cast_unit() 2037 .max(DeviceSize::new(1.0, 1.0)); 2038 // Adjust task size to prevent potential sampling errors 2039 let adjusted_blur_task_size = 2040 BlurTask::adjusted_blur_source_size( 2041 blur_task_size, 2042 adjusted_blur_std_deviation, 2043 ).max(DeviceSize::new(1.0, 1.0)); 2044 // Now change the subregion to match the revised task size, 2045 // keeping it centered should keep animated radius smooth. 2046 let corner = LayoutPoint::new( 2047 blur_subregion.min.x.floor() + (( 2048 blur_task_size.width - 2049 adjusted_blur_task_size.width) * 0.5).floor(), 2050 blur_subregion.min.y.floor() + (( 2051 blur_task_size.height - 2052 adjusted_blur_task_size.height) * 0.5).floor(), 2053 ); 2054 // Recalculate the blur_subregion to match, and if render 2055 // scale is used, undo that so it is in the same subregion 2056 // coordinate system as the node 2057 let blur_subregion = 2058 LayoutRect::new( 2059 corner, 2060 LayoutPoint::new( 2061 corner.x + adjusted_blur_task_size.width, 2062 corner.y + adjusted_blur_task_size.height, 2063 ), 2064 ) 2065 .scale(render_to_device_scale, render_to_device_scale); 2066 2067 let input_subregion_task_id = rg_builder.add().init(RenderTask::new_dynamic( 2068 adjusted_blur_task_size.to_i32(), 2069 RenderTaskKind::SVGFENode( 2070 SVGFEFilterTask{ 2071 node: FilterGraphNode{ 2072 kept_by_optimizer: true, 2073 linear: false, 2074 inputs: [ 2075 FilterGraphPictureReference{ 2076 buffer_id: blur_input.buffer_id, 2077 subregion: blur_input.subregion, 2078 offset: LayoutVector2D::zero(), 2079 inflate: blur_input.inflate, 2080 source_padding: LayoutRect::zero(), 2081 target_padding: LayoutRect::zero(), 2082 }].to_vec(), 2083 subregion: blur_subregion, 2084 inflate: 0, 2085 }, 2086 op: FilterGraphOp::SVGFEIdentity, 2087 content_origin: node_task_rect.min, 2088 extra_gpu_data: None, 2089 } 2090 ), 2091 ).with_uv_rect_kind(UvRectKind::Rect)); 2092 // Adding the dependencies sets the inputs for this task 2093 rg_builder.add_dependency(input_subregion_task_id, source_task_id); 2094 2095 // The shadow compositing only cares about alpha channel 2096 // which is always linear, so we can blur this in sRGB or 2097 // linear color space and the result is the same as we will 2098 // be replacing the rgb completely. 2099 let blur_task_id = 2100 RenderTask::new_blur( 2101 adjusted_blur_std_deviation, 2102 input_subregion_task_id, 2103 rg_builder, 2104 RenderTargetKind::Color, 2105 None, 2106 adjusted_blur_task_size.to_i32(), 2107 BlurEdgeMode::Duplicate, 2108 ); 2109 2110 // Now we make the compositing task, for this we need to put 2111 // the blurred shadow image at the correct subregion offset 2112 let blur_subregion_translated = blur_subregion 2113 .translate(LayoutVector2D::new(dx, dy)); 2114 task_id = rg_builder.add().init(RenderTask::new_dynamic( 2115 node_task_size, 2116 RenderTaskKind::SVGFENode( 2117 SVGFEFilterTask{ 2118 node: FilterGraphNode{ 2119 kept_by_optimizer: true, 2120 linear: node.linear, 2121 inflate: node_inflate, 2122 inputs: [ 2123 // Original picture 2124 *blur_input, 2125 // Shadow picture 2126 FilterGraphPictureReference{ 2127 buffer_id: blur_input.buffer_id, 2128 subregion: blur_subregion_translated, 2129 inflate: 0, 2130 offset: LayoutVector2D::zero(), 2131 source_padding: LayoutRect::zero(), 2132 target_padding: LayoutRect::zero(), 2133 }].to_vec(), 2134 subregion, 2135 }, 2136 op: FilterGraphOp::SVGFEDropShadow{ 2137 color, 2138 // These parameters don't matter here 2139 dx: 0.0, dy: 0.0, 2140 std_deviation_x: 0.0, std_deviation_y: 0.0, 2141 }, 2142 content_origin: node_task_rect.min, 2143 extra_gpu_data: None, 2144 } 2145 ), 2146 ).with_uv_rect_kind(node_uv_rect_kind)); 2147 // Adding the dependencies sets the inputs for this task 2148 rg_builder.add_dependency(task_id, source_task_id); 2149 rg_builder.add_dependency(task_id, blur_task_id); 2150 } 2151 FilterGraphOp::SVGFESourceAlpha | 2152 FilterGraphOp::SVGFESourceGraphic => { 2153 // These copy from the original task, we have to synthesize 2154 // a fake input binding to make the shader do the copy. In 2155 // the case of SourceAlpha the shader will zero the RGB but 2156 // we don't have to care about that distinction here. 2157 task_id = rg_builder.add().init(RenderTask::new_dynamic( 2158 node_task_size, 2159 RenderTaskKind::SVGFENode( 2160 SVGFEFilterTask{ 2161 node: FilterGraphNode{ 2162 kept_by_optimizer: true, 2163 linear: node.linear, 2164 inflate: node_inflate, 2165 inputs: [ 2166 FilterGraphPictureReference{ 2167 buffer_id: FilterOpGraphPictureBufferId::None, 2168 // This is what makes the mapping 2169 // actually work. 2170 subregion: source_subregion.cast_unit(), 2171 offset: LayoutVector2D::zero(), 2172 inflate: 0, 2173 source_padding: LayoutRect::zero(), 2174 target_padding: LayoutRect::zero(), 2175 } 2176 ].to_vec(), 2177 subregion: source_subregion.cast_unit(), 2178 }, 2179 op: op.clone(), 2180 content_origin: source_subregion.min.cast_unit(), 2181 extra_gpu_data: None, 2182 } 2183 ), 2184 ).with_uv_rect_kind(node_uv_rect_kind)); 2185 rg_builder.add_dependency(task_id, original_task_id); 2186 made_dependency_on_source = true; 2187 } 2188 FilterGraphOp::SVGFEComponentTransferInterned { handle, creates_pixels: _ } => { 2189 // FIXME: Doing this in prepare_interned_prim_for_render 2190 // doesn't seem to be enough, where should it be done? 2191 let filter_data = &mut data_stores.filter_data[handle]; 2192 filter_data.write_gpu_blocks(gpu_buffer); 2193 // ComponentTransfer has a gpu buffer address that we need to 2194 // pass along 2195 task_id = rg_builder.add().init(RenderTask::new_dynamic( 2196 node_task_size, 2197 RenderTaskKind::SVGFENode( 2198 SVGFEFilterTask { 2199 node: FilterGraphNode{ 2200 kept_by_optimizer: true, 2201 linear: node.linear, 2202 inputs: node_inputs.iter().map(|input| {input.0}).collect(), 2203 subregion, 2204 inflate: node_inflate, 2205 }, 2206 op: op.clone(), 2207 content_origin: node_task_rect.min, 2208 extra_gpu_data: Some(filter_data.gpu_buffer_address), 2209 } 2210 ), 2211 ).with_uv_rect_kind(node_uv_rect_kind)); 2212 2213 // Add the dependencies for inputs of this node, which will 2214 // be used by add_svg_filter_node_instances later 2215 for (_input, input_task) in &node_inputs { 2216 if *input_task == original_task_id { 2217 made_dependency_on_source = true; 2218 } 2219 if *input_task != RenderTaskId::INVALID { 2220 rg_builder.add_dependency(task_id, *input_task); 2221 } 2222 } 2223 } 2224 _ => { 2225 // This is the usual case - zero, one or two inputs that 2226 // reference earlier node results. 2227 task_id = rg_builder.add().init(RenderTask::new_dynamic( 2228 node_task_size, 2229 RenderTaskKind::SVGFENode( 2230 SVGFEFilterTask{ 2231 node: FilterGraphNode{ 2232 kept_by_optimizer: true, 2233 linear: node.linear, 2234 inputs: node_inputs.iter().map(|input| {input.0}).collect(), 2235 subregion, 2236 inflate: node_inflate, 2237 }, 2238 op: op.clone(), 2239 content_origin: node_task_rect.min, 2240 extra_gpu_data: None, 2241 } 2242 ), 2243 ).with_uv_rect_kind(node_uv_rect_kind)); 2244 2245 // Add the dependencies for inputs of this node, which will 2246 // be used by add_svg_filter_node_instances later 2247 for (_input, input_task) in &node_inputs { 2248 if *input_task == original_task_id { 2249 made_dependency_on_source = true; 2250 } 2251 if *input_task != RenderTaskId::INVALID { 2252 rg_builder.add_dependency(task_id, *input_task); 2253 } 2254 } 2255 } 2256 } 2257 2258 // We track the tasks we created by output buffer id to make it easy 2259 // to look them up quickly, since nodes can only depend on previous 2260 // nodes in the same list 2261 task_by_buffer_id[filter_index] = task_id; 2262 subregion_by_buffer_id[filter_index] = subregion; 2263 2264 // The final task we create is the output picture. 2265 output_task_id = task_id; 2266 } 2267 2268 // If no tasks referenced the SourceGraphic, we actually have to create 2269 // a fake dependency so that it does not leak. 2270 if !made_dependency_on_source && output_task_id != original_task_id { 2271 rg_builder.add_dependency(output_task_id, original_task_id); 2272 } 2273 2274 output_task_id 2275 } 2276 2277 pub fn uv_rect_kind(&self) -> UvRectKind { 2278 self.uv_rect_kind 2279 } 2280 2281 pub fn get_texture_address(&self) -> GpuBufferAddress { 2282 self.uv_rect_handle 2283 } 2284 2285 pub fn get_target_texture(&self) -> CacheTextureId { 2286 match self.location { 2287 RenderTaskLocation::Dynamic { texture_id, .. } => { 2288 assert_ne!(texture_id, CacheTextureId::INVALID); 2289 texture_id 2290 } 2291 RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => { 2292 texture 2293 } 2294 _ => { 2295 unreachable!(); 2296 } 2297 } 2298 } 2299 2300 pub fn get_texture_source(&self) -> TextureSource { 2301 match self.location { 2302 RenderTaskLocation::Dynamic { texture_id, .. } => { 2303 assert_ne!(texture_id, CacheTextureId::INVALID); 2304 TextureSource::TextureCache(texture_id, Swizzle::default()) 2305 } 2306 RenderTaskLocation::Static { surface: StaticRenderTaskSurface::ReadOnly { source }, .. } => { 2307 source 2308 } 2309 RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => { 2310 TextureSource::TextureCache(texture, Swizzle::default()) 2311 } 2312 RenderTaskLocation::Existing { .. } | 2313 RenderTaskLocation::Static { .. } | 2314 RenderTaskLocation::CacheRequest { .. } | 2315 RenderTaskLocation::Unallocated { .. } => { 2316 unreachable!(); 2317 } 2318 } 2319 } 2320 2321 pub fn get_target_rect(&self) -> DeviceIntRect { 2322 match self.location { 2323 // Previously, we only added render tasks after the entire 2324 // primitive chain was determined visible. This meant that 2325 // we could assert any render task in the list was also 2326 // allocated (assigned to passes). Now, we add render 2327 // tasks earlier, and the picture they belong to may be 2328 // culled out later, so we can't assert that the task 2329 // has been allocated. 2330 // Render tasks that are created but not assigned to 2331 // passes consume a row in the render task texture, but 2332 // don't allocate any space in render targets nor 2333 // draw any pixels. 2334 // TODO(gw): Consider some kind of tag or other method 2335 // to mark a task as unused explicitly. This 2336 // would allow us to restore this debug check. 2337 RenderTaskLocation::Dynamic { rect, .. } => rect, 2338 RenderTaskLocation::Static { rect, .. } => rect, 2339 RenderTaskLocation::Existing { .. } | 2340 RenderTaskLocation::CacheRequest { .. } | 2341 RenderTaskLocation::Unallocated { .. } => { 2342 panic!("bug: get_target_rect called before allocating"); 2343 } 2344 } 2345 } 2346 2347 pub fn get_target_size(&self) -> DeviceIntSize { 2348 match self.location { 2349 RenderTaskLocation::Dynamic { rect, .. } => rect.size(), 2350 RenderTaskLocation::Static { rect, .. } => rect.size(), 2351 RenderTaskLocation::Existing { size, .. } => size, 2352 RenderTaskLocation::CacheRequest { size } => size, 2353 RenderTaskLocation::Unallocated { size } => size, 2354 } 2355 } 2356 2357 pub fn target_kind(&self) -> RenderTargetKind { 2358 self.kind.target_kind() 2359 } 2360 2361 /// Called by the render task cache. 2362 /// 2363 /// Tells the render task that it is cached (which means its gpu cache 2364 /// handle is managed by the texture cache). 2365 pub fn mark_cached(&mut self, handle: RenderTaskCacheEntryHandle) { 2366 self.cache_handle = Some(handle); 2367 } 2368 }