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