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