1use api::{BoxShadowClipMode, ColorF, DebugFlags, ExtendMode, GradientStop};
10use api::ClipMode;
11use crate::pattern::cutout::Cutout;
12use crate::util::clamp_to_scale_factor;
13use crate::box_shadow::{BoxShadowCacheKey, BLUR_SAMPLE_SCALE};
14use crate::pattern::box_shadow::BoxShadowPatternData;
15use crate::pattern::gradient::linear_gradient_pattern;
16use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState};
17use crate::prim_store::gradient::{decompose_axis_aligned_gradient, linear_gradient_decomposes};
18use crate::segment::EdgeMask;
19use api::units::*;
20use euclid::Scale;
21use smallvec::SmallVec;
22use crate::composite::CompositorSurfaceKind;
23use crate::command_buffer::{CommandBufferIndex, PrimitiveCommand};
24use crate::border;
25use crate::clip::{ClipStore, ClipNodeRange};
26use crate::renderer::{GpuBufferAddress, GpuBufferWriterF};
27use crate::spatial_tree::SpatialNodeIndex;
28use crate::clip::{clamped_radius, ClipNodeFlags, ClipChainInstance, ClipItemKind};
29use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
30use crate::gpu_types::{BrushFlags, BlurEdgeMode};
31use crate::render_target::RenderTargetKind;
32use crate::internal_types::{FastHashMap, PlaneSplitAnchor, Filter};
33use crate::picture::{ClusterFlags, PictureCompositeMode, PictureInstance, PictureScratch};
34use crate::picture::{PrimitiveList, PrimitiveCluster, SurfaceIndex, SubpixelMode, Picture3DContext};
35use crate::tile_cache::{SliceId, TileCacheInstance};
36use crate::prim_store::*;
37use crate::prim_store::backdrop::BackdropRenderScratch;
38use crate::prim_store::borders::{ImageBorderScratch, NormalBorderScratch};
39use crate::prim_store::line_dec::LineDecorationScratch;
40use crate::quad::{self, QuadTransformState};
41use crate::render_backend::DataStores;
42use crate::render_task_cache::RenderTaskCacheKeyKind;
43use crate::render_task_cache::{RenderTaskCacheKey, to_cache_size, RenderTaskParent};
44use crate::render_task::{EmptyTask, RenderTask, RenderTaskKind, MAX_BLUR_STD_DEVIATION};
45use crate::segment::SegmentBuilder;
46use crate::space::SpaceSnapper;
47use crate::visibility::{DrawState, KindScratchHandle};
48
49
50const MAX_MASK_SIZE: i32 = 4096;
51
52const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
53
54pub fn prepare_picture(
56 pic_index: PictureIndex,
57 store: &mut PrimitiveStore,
58 surface_index: Option<SurfaceIndex>,
59 subpixel_mode: SubpixelMode,
60 frame_context: &FrameBuildingContext,
61 frame_state: &mut FrameBuildingState,
62 data_stores: &mut DataStores,
63 scratch: &mut PrimitiveScratchBuffer,
64 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
65 prim_instances: &mut Vec<PrimitiveInstance>,
66) -> Option<storage::Index<PictureScratch>> {
67 if let Some(handle) = frame_state.picture_scratch_handles[pic_index.0] {
68 return Some(handle);
69 }
70
71 let pic = &mut store.pictures[pic_index.0];
72 let Some((pic_context, mut pic_state, mut prim_list, scratch_handle)) = pic.take_context(
73 pic_index,
74 surface_index,
75 subpixel_mode,
76 frame_state,
77 frame_context,
78 data_stores,
79 scratch,
80 tile_caches,
81 ) else {
82 frame_state.picture_scratch_handles[pic_index.0] = Some(storage::Index::INVALID);
86 return None;
87 };
88
89 frame_state.picture_scratch_handles[pic_index.0] = Some(scratch_handle);
90
91 prepare_primitives(
92 store,
93 &mut prim_list,
94 &pic_context,
95 &mut pic_state,
96 frame_context,
97 frame_state,
98 data_stores,
99 scratch,
100 tile_caches,
101 prim_instances,
102 );
103
104 store.pictures[pic_context.pic_index.0].restore_context(
106 pic_context.pic_index,
107 prim_list,
108 pic_context,
109 frame_context,
110 frame_state,
111 scratch,
112 );
113
114 Some(scratch_handle)
115}
116
117fn prepare_primitives(
118 store: &mut PrimitiveStore,
119 prim_list: &mut PrimitiveList,
120 pic_context: &PictureContext,
121 pic_state: &mut PictureState,
122 frame_context: &FrameBuildingContext,
123 frame_state: &mut FrameBuildingState,
124 data_stores: &mut DataStores,
125 scratch: &mut PrimitiveScratchBuffer,
126 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
127 prim_instances: &mut Vec<PrimitiveInstance>,
128) {
129 profile_scope!("prepare_primitives");
130 let mut cmd_buffer_targets = Vec::new();
131
132 let mut quad_transform = QuadTransformState::new();
133
134 for cluster in &mut prim_list.clusters {
135 if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) {
136 continue;
137 }
138 profile_scope!("cluster");
139 pic_state.map_local_to_pic.set_target_spatial_node(
140 cluster.spatial_node_index,
141 frame_context.spatial_tree,
142 );
143
144 let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale;
145 quad_transform.set(
146 cluster.spatial_node_index,
147 pic_context.raster_spatial_node_index,
148 frame_context.spatial_tree,
149 device_pixel_scale,
150 );
151
152 for prim_instance_index in cluster.prim_range() {
153 if frame_state.surface_builder.get_cmd_buffer_targets_for_prim(
154 &scratch.frame.draws[prim_instance_index],
155 &mut cmd_buffer_targets,
156 ) {
157 let plane_split_anchor = PlaneSplitAnchor::new(
158 cluster.spatial_node_index,
159 PrimitiveInstanceIndex(prim_instance_index as u32),
160 );
161
162 prepare_prim_for_render(
163 store,
164 prim_instance_index,
165 cluster,
166 &mut quad_transform,
167 pic_context,
168 pic_state,
169 frame_context,
170 frame_state,
171 plane_split_anchor,
172 data_stores,
173 scratch,
174 tile_caches,
175 prim_instances,
176 &cmd_buffer_targets,
177 );
178
179 frame_state.num_visible_primitives += 1;
180 continue;
181 }
182
183 scratch.frame.draws[prim_instance_index].reset();
187 }
188 }
189}
190
191fn can_use_clip_chain_for_quad_path(
192 clip_chain: &ClipChainInstance,
193 clip_store: &ClipStore,
194 data_stores: &DataStores,
195) -> bool {
196 if !clip_chain.needs_mask {
197 return true;
198 }
199
200 for i in 0 .. clip_chain.clips_range.count {
201 let clip_instance = clip_store.get_instance_from_range(&clip_chain.clips_range, i);
202 let clip_node = &data_stores.clip[clip_instance.handle];
203
204 match clip_node.item.kind {
205 ClipItemKind::RoundedRectangle { .. } | ClipItemKind::Rectangle { .. } => {}
206 ClipItemKind::Image { .. } => {
207 panic!("bug: image-masks not expected on rect/quads");
208 }
209 }
210 }
211
212 true
213}
214
215fn prepare_prim_for_render(
216 store: &mut PrimitiveStore,
217 prim_instance_index: usize,
218 cluster: &mut PrimitiveCluster,
219 quad_transform: &mut QuadTransformState,
220 pic_context: &PictureContext,
221 pic_state: &mut PictureState,
222 frame_context: &FrameBuildingContext,
223 frame_state: &mut FrameBuildingState,
224 plane_split_anchor: PlaneSplitAnchor,
225 data_stores: &mut DataStores,
226 scratch: &mut PrimitiveScratchBuffer,
227 tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
228 prim_instances: &mut Vec<PrimitiveInstance>,
229 targets: &[CommandBufferIndex],
230) {
231 profile_scope!("prepare_prim_for_render");
232
233 let mut is_passthrough = false;
239 if let PrimitiveKind::Picture { pic_index, .. } = prim_instances[prim_instance_index].kind {
240 let Some(scratch_handle) = prepare_picture(
241 pic_index,
242 store,
243 Some(pic_context.surface_index),
244 pic_context.subpixel_mode,
245 frame_context,
246 frame_state,
247 data_stores,
248 scratch,
249 tile_caches,
250 prim_instances,
251 ) else {
252 return;
253 };
254
255 scratch.frame.draws[prim_instance_index].kind_scratch =
256 KindScratchHandle::Picture(scratch_handle);
257
258 is_passthrough = store
259 .pictures[pic_index.0]
260 .composite_mode
261 .is_none();
262 }
263
264 let prim_instance = &mut prim_instances[prim_instance_index];
265 let mut use_legacy_path = true;
266 if !is_passthrough {
267 match &prim_instance.kind {
268 PrimitiveKind::Rectangle { .. }
269 | PrimitiveKind::RadialGradient { .. }
270 | PrimitiveKind::ConicGradient { .. }
271 | PrimitiveKind::LinearGradient { .. }
272 | PrimitiveKind::Image { .. }
273 => {
274 use_legacy_path = false;
275 }
276 PrimitiveKind::YuvImage { .. } => {
277 let prim_info = scratch.frame.draws[prim_instance_index];
278 use_legacy_path = prim_info.compositor_surface_kind != CompositorSurfaceKind::Underlay;
279 }
280 _ => {}
281 };
282
283 let should_update_clip_task = match &mut prim_instance.kind {
289 PrimitiveKind::Rectangle { .. }
290 | PrimitiveKind::RadialGradient { .. }
291 | PrimitiveKind::ConicGradient { .. }
292 | PrimitiveKind::LinearGradient { .. }
293 | PrimitiveKind::Image { .. }
294 | PrimitiveKind::YuvImage { .. }
295 => {
296 use_legacy_path |= !can_use_clip_chain_for_quad_path(
297 &scratch.frame.draws[prim_instance_index].clip_chain,
298 frame_state.clip_store,
299 data_stores,
300 );
301
302 use_legacy_path
303 }
304 PrimitiveKind::BoxShadow { .. } |
305 PrimitiveKind::Picture { .. } => false,
306 _ => true,
307 };
308
309 let snapped_local_rect = scratch.frame.draws[prim_instance_index].snapped_local_rect;
313 match prim_instance.kind {
314 PrimitiveKind::NormalBorder { data_handle } => {
315 NormalBorderScratch::build_for_prim(
316 data_handle,
317 PrimitiveInstanceIndex(prim_instance_index as u32),
318 snapped_local_rect.size(),
319 data_stores,
320 scratch,
321 );
322 }
323 PrimitiveKind::ImageBorder { data_handle } => {
324 ImageBorderScratch::build_for_prim(
325 data_handle,
326 PrimitiveInstanceIndex(prim_instance_index as u32),
327 snapped_local_rect.size(),
328 data_stores,
329 scratch,
330 );
331 }
332 _ => {}
333 }
334
335 if should_update_clip_task {
336 let prim_rect = data_stores.get_local_prim_rect(
337 prim_instance,
338 scratch.frame.draws[prim_instance_index].snapped_local_rect,
339 &store.pictures,
340 frame_state.surfaces,
341 );
342
343 if !update_clip_task(
344 prim_instance,
345 PrimitiveInstanceIndex(prim_instance_index as u32),
346 &prim_rect.min,
347 cluster.spatial_node_index,
348 pic_context.raster_spatial_node_index,
349 pic_context.visibility_spatial_node_index,
350 pic_context,
351 pic_state,
352 frame_context,
353 frame_state,
354 store,
355 data_stores,
356 scratch,
357 ) {
358 return;
359 }
360 }
361 }
362
363 prepare_interned_prim_for_render(
364 store,
365 use_legacy_path,
366 PrimitiveInstanceIndex(prim_instance_index as u32),
367 prim_instance,
368 cluster,
369 plane_split_anchor,
370 quad_transform,
371 pic_context,
372 pic_state,
373 frame_context,
374 frame_state,
375 data_stores,
376 scratch,
377 targets,
378 )
379}
380
381fn prepare_interned_prim_for_render(
385 store: &mut PrimitiveStore,
386 use_legacy_path: bool,
387 prim_instance_index: PrimitiveInstanceIndex,
388 prim_instance: &mut PrimitiveInstance,
389 cluster: &mut PrimitiveCluster,
390 plane_split_anchor: PlaneSplitAnchor,
391 quad_transform: &mut QuadTransformState,
392 pic_context: &PictureContext,
393 pic_state: &mut PictureState,
394 frame_context: &FrameBuildingContext,
395 frame_state: &mut FrameBuildingState,
396 data_stores: &mut DataStores,
397 scratch: &mut PrimitiveScratchBuffer,
398 targets: &[CommandBufferIndex],
399) {
400 let prim_spatial_node_index = cluster.spatial_node_index;
401 let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale;
402 let prim_info = scratch.frame.draws[prim_instance_index.0 as usize];
407
408 match &mut prim_instance.kind {
409 PrimitiveKind::BoxShadow { data_handle, .. } => {
410 profile_scope!("BoxShadow");
411
412 let prim_data = &data_stores.box_shadow[*data_handle];
413 let shadow_data = &prim_data.kind;
414 let blur_radius = shadow_data.blur_radius;
415
416 let blur_offset = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
433 let unsnapped_element_rect = match shadow_data.clip_mode {
434 BoxShadowClipMode::Outset => prim_instance.unsnapped_prim_rect
435 .inflate(-blur_offset, -blur_offset)
436 .inflate(-shadow_data.spread_amount, -shadow_data.spread_amount)
437 .translate(-shadow_data.box_offset),
438 BoxShadowClipMode::Inset => prim_instance.unsnapped_prim_rect,
439 };
440 let element_rect = {
441 let mut snapper = SpaceSnapper::new(
442 frame_context.spatial_tree.root_reference_frame_index(),
443 RasterPixelScale::new(1.0),
444 );
445 snapper.set_target_spatial_node(prim_spatial_node_index, frame_context.spatial_tree);
446 snapper.snap_rect(&unsnapped_element_rect)
447 };
448 let inner_shadow_rect = element_rect
449 .translate(shadow_data.box_offset)
450 .inflate(shadow_data.spread_amount, shadow_data.spread_amount);
451 let outer_shadow_rect = inner_shadow_rect.inflate(blur_offset, blur_offset);
452 let prim_rect = match shadow_data.clip_mode {
458 BoxShadowClipMode::Outset => outer_shadow_rect,
459 BoxShadowClipMode::Inset => element_rect,
460 };
461
462 let shadow_rect_size = inner_shadow_rect.size();
463 let mut shadow_radius = shadow_data.shadow_radius;
464 border::ensure_no_corner_overlap(&mut shadow_radius, shadow_rect_size);
465
466 let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil();
467
468 let max_corner_width = shadow_radius.top_left.width
469 .max(shadow_radius.bottom_left.width)
470 .max(shadow_radius.top_right.width)
471 .max(shadow_radius.bottom_right.width);
472 let max_corner_height = shadow_radius.top_left.height
473 .max(shadow_radius.bottom_left.height)
474 .max(shadow_radius.top_right.height)
475 .max(shadow_radius.bottom_right.height);
476
477 let used_corner_width = max_corner_width.max(blur_region);
478 let used_corner_height = max_corner_height.max(blur_region);
479
480 let min_shadow_rect_size = LayoutSize::new(
481 2.0 * used_corner_width + blur_region,
482 2.0 * used_corner_height + blur_region,
483 );
484
485 let src_rect_size = LayoutSize::new(
488 if shadow_rect_size.width >= min_shadow_rect_size.width {
489 min_shadow_rect_size.width
490 } else {
491 shadow_rect_size.width
492 },
493 if shadow_rect_size.height >= min_shadow_rect_size.height {
494 min_shadow_rect_size.height
495 } else {
496 shadow_rect_size.height
497 },
498 );
499
500 let shadow_rect_alloc_size = LayoutSize::new(
504 2.0 * blur_region + src_rect_size.width,
505 2.0 * blur_region + src_rect_size.height,
506 );
507
508 let blur_radius_dp = blur_radius * 0.5;
510 let mut content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale;
511 content_scale.0 = clamp_to_scale_factor(content_scale.0, false);
512
513 let sigma_rounded = (blur_radius_dp * content_scale.0).round();
521 let sigma_for_n = if sigma_rounded == 0.0 { blur_radius_dp * content_scale.0 } else { sigma_rounded };
522 let n_downscales = if sigma_for_n > MAX_BLUR_STD_DEVIATION {
523 (sigma_for_n / MAX_BLUR_STD_DEVIATION).log2().ceil() as u32
524 } else {
525 0
526 };
527 content_scale.0 /= (1u32 << n_downscales) as f32;
528
529 let cache_size = to_cache_size(shadow_rect_alloc_size, &mut content_scale);
532
533 let blur_std_dev = if sigma_rounded == 0.0 {
537 blur_radius_dp * content_scale.0
538 } else {
539 sigma_rounded / (1u32 << n_downscales) as f32
540 };
541 debug_assert!(
542 blur_std_dev <= MAX_BLUR_STD_DEVIATION + 1e-3,
543 "BoxShadow sigma {blur_std_dev} exceeds MAX_BLUR_STD_DEVIATION after Opt B \
544 (n_downscales={n_downscales}, content_scale={})",
545 content_scale.0,
546 );
547
548 let bs_cache_key = BoxShadowCacheKey {
549 blur_radius_dp: Au::from_f32_px(blur_std_dev),
550 clip_mode: shadow_data.clip_mode,
551 original_alloc_size: (shadow_rect_alloc_size * content_scale).round().to_i32(),
552 br_top_left: (shadow_radius.top_left * content_scale).round().to_i32(),
553 br_top_right: (shadow_radius.top_right * content_scale).round().to_i32(),
554 br_bottom_right: (shadow_radius.bottom_right * content_scale).round().to_i32(),
555 br_bottom_left: (shadow_radius.bottom_left * content_scale).round().to_i32(),
556 device_pixel_scale: Au::from_f32_px(content_scale.0),
557 };
558
559 let clip_data = ClipData::rounded_rect(
560 src_rect_size,
561 &shadow_radius,
562 ClipMode::Clip,
563 );
564
565 let minimal_shadow_rect_origin = LayoutPoint::new(blur_region, blur_region);
568 let device_pixel_scale_for_task = DevicePixelScale::new(content_scale.0);
569
570 let task_id = frame_state.resource_cache.request_render_task(
571 Some(RenderTaskCacheKey {
572 origin: DeviceIntPoint::zero(),
573 size: cache_size,
574 kind: RenderTaskCacheKeyKind::BoxShadow(bs_cache_key),
575 }),
576 false,
577 RenderTaskParent::Surface,
578 &mut frame_state.frame_gpu_data.f32,
579 frame_state.rg_builder,
580 &mut frame_state.surface_builder,
581 &mut |rg_builder, _| {
582 let mask_task_id = rg_builder.add().init(RenderTask::new_dynamic(
583 cache_size,
584 RenderTaskKind::new_rounded_rect_mask(
585 minimal_shadow_rect_origin,
586 clip_data.clone(),
587 device_pixel_scale_for_task,
588 frame_context.fb_config,
589 ),
590 ));
591
592 RenderTask::new_blur(
593 DeviceSize::new(blur_std_dev, blur_std_dev),
594 mask_task_id,
595 rg_builder,
596 RenderTargetKind::Alpha,
597 None,
598 cache_size,
599 BlurEdgeMode::Duplicate,
600 )
601 }
602 );
603
604 let prim_min_rounded = match quad_transform.as_2d_scale_offset() {
615 Some(local_to_device) => {
616 let dev: DevicePoint = local_to_device.map_point(&prim_rect.min);
624 local_to_device.unmap_point::<DevicePixel, LayoutPixel>(&dev.round())
625 }
626 None => prim_rect.min,
627 };
628
629 let dest_rect = outer_shadow_rect;
633 let dest_rect_offset = LayoutVector2D::new(
634 dest_rect.min.x - prim_min_rounded.x,
635 dest_rect.min.y - prim_min_rounded.y,
636 );
637 let dest_rect_size = dest_rect.size();
638
639 let mut element_radius = shadow_data.element_radius;
640 border::ensure_no_corner_overlap(&mut element_radius, element_rect.size());
641 let element_offset_rel_prim = LayoutVector2D::new(
642 element_rect.min.x - prim_min_rounded.x,
643 element_rect.min.y - prim_min_rounded.y,
644 );
645
646 let pattern = BoxShadowPatternData {
647 color: shadow_data.color,
648 render_task: task_id,
649 shadow_rect_alloc_size,
650 dest_rect_size,
651 dest_rect_offset,
652 clip_mode: shadow_data.clip_mode,
653 element_offset_rel_prim,
654 element_size: element_rect.size(),
655 element_radius,
656 };
657
658 quad::prepare_quad(
659 &pattern,
660 &prim_rect,
661 &prim_info.clip_chain.local_clip_rect,
662 prim_data.common.aligned_aa_edges,
663 prim_data.common.transformed_aa_edges,
664 prim_instance_index,
665 &None,
666 &prim_info.clip_chain,
667 quad_transform,
668 frame_context,
669 pic_context,
670 targets,
671 &data_stores.clip,
672 frame_state,
673 scratch,
674 );
675
676 return;
677 }
678 PrimitiveKind::LineDecoration { data_handle } => {
679 profile_scope!("LineDecoration");
680 let prim_data = &data_stores.line_decoration[*data_handle];
681
682 let (task_id, gpu_address) = prim_data.kind.prepare(
683 prim_info.snapped_local_rect.size(),
684 prim_spatial_node_index,
685 frame_context,
686 frame_state,
687 );
688
689 let line_dec_handle = scratch.frame.line_decoration.push(LineDecorationScratch {
690 task_id,
691 gpu_address,
692 });
693 scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
694 KindScratchHandle::LineDecoration(line_dec_handle);
695 }
696 PrimitiveKind::TextRun { data_handle } => {
697 profile_scope!("TextRun");
698
699 let prim_data = &data_stores.text_run[*data_handle];
700
701 let transform = frame_context.spatial_tree
706 .get_relative_transform(
707 prim_spatial_node_index,
708 pic_context.raster_spatial_node_index,
709 )
710 .into_fast_transform();
711
712 let local_rect = prim_instance.unsnapped_prim_rect;
717
718 let surface = &frame_state.surfaces[pic_context.surface_index.0];
719
720 let allow_subpixel = match prim_info.state {
724 DrawState::Culled |
725 DrawState::Unset |
726 DrawState::PassThrough => {
727 panic!("bug: invalid visibility state");
728 }
729 DrawState::Visible { sub_slice_index, .. } => {
730 if sub_slice_index.is_primary() {
733 match pic_context.subpixel_mode {
734 SubpixelMode::Allow => true,
735 SubpixelMode::Deny => false,
736 SubpixelMode::Conditional { allowed_rect, prohibited_rect } => {
737 allowed_rect.contains_box(&prim_info.clip_chain.pic_coverage_rect) &&
740 !prohibited_rect.intersects(&prim_info.clip_chain.pic_coverage_rect)
741 }
742 }
743 } else {
744 false
745 }
746 }
747 };
748
749 let text_run_handle = prim_data.request_resources(
750 local_rect,
751 &transform.to_transform().with_destination::<_>(),
752 surface,
753 prim_spatial_node_index,
754 allow_subpixel,
755 frame_context.fb_config.low_quality_pinch_zoom,
756 frame_state.resource_cache,
757 &mut frame_state.frame_gpu_data.f32,
758 frame_context.spatial_tree,
759 scratch,
760 );
761 scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
762 KindScratchHandle::TextRun(text_run_handle);
763 }
764 PrimitiveKind::NormalBorder { data_handle } => {
765 profile_scope!("NormalBorder");
766 let prim_data = &mut data_stores.normal_border[*data_handle];
767 let common_data = &mut prim_data.common;
768 let border_data = &mut prim_data.kind;
769
770 let nb_handle = scratch.frame.draws[prim_instance_index.0 as usize]
775 .kind_scratch
776 .unwrap_normal_border();
777 let nb_scratch = scratch.frame.normal_border[nb_handle];
778
779 let brush_segments = &scratch.frame.segments[nb_scratch.brush_segments_range];
780 let gpu_address = border_data.write_brush_gpu_blocks(
781 common_data,
782 prim_info.snapped_local_rect.size(),
783 brush_segments,
784 frame_state,
785 );
786 scratch.frame.normal_border[nb_handle].gpu_address = gpu_address;
787
788 let PrimitiveFrameScratch {
792 ref border_segments,
793 ref mut border_task_ids,
794 ..
795 } = scratch.frame;
796 border_data.update(
797 &border_segments[nb_scratch.border_segments_range],
798 prim_spatial_node_index,
799 device_pixel_scale,
800 frame_context,
801 frame_state,
802 &mut border_task_ids[nb_scratch.task_ids],
803 );
804 }
805 PrimitiveKind::ImageBorder { data_handle, .. } => {
806 profile_scope!("ImageBorder");
807 let prim_data = &mut data_stores.image_border[*data_handle];
808
809 let ib_handle = scratch.frame.draws[prim_instance_index.0 as usize]
814 .kind_scratch
815 .unwrap_image_border();
816 let brush_segments_range =
817 scratch.frame.image_border[ib_handle].brush_segments_range;
818 let brush_segments = &scratch.frame.segments[brush_segments_range];
819
820 let gpu_address = prim_data.kind.update(
823 &mut prim_data.common,
824 prim_info.snapped_local_rect.size(),
825 brush_segments,
826 frame_state,
827 );
828 scratch.frame.image_border[ib_handle].gpu_address = gpu_address;
829 }
830 PrimitiveKind::Rectangle { data_handle, .. } => {
831 profile_scope!("Rectangle");
832
833 if use_legacy_path {
834 let prim_data = &mut data_stores.prim[*data_handle];
835
836 prim_data.update(
839 frame_state,
840 frame_context.scene_properties,
841 );
842
843 write_segment(
844 prim_info.segment_instance_index,
845 frame_state,
846 &mut scratch.frame.segments,
847 &mut scratch.frame.segment_instances,
848 |request| {
849 request.push_one(frame_context.scene_properties.resolve_color(&prim_data.kind.color).premultiplied());
850 }
851 );
852 } else {
853 let prim_data = &data_stores.prim[*data_handle];
854 let prim_rect = prim_info.snapped_local_rect;
855 let color = prim_data.resolve(frame_context.scene_properties);
856
857 quad::prepare_quad(
858 &color,
859 &prim_rect,
860 &prim_info.clip_chain.local_clip_rect,
861 prim_data.common.aligned_aa_edges,
862 prim_data.common.transformed_aa_edges,
863 prim_instance_index,
864 &None,
865 &prim_info.clip_chain,
866 quad_transform,
867 frame_context,
868 pic_context,
869 targets,
870 &data_stores.clip,
871 frame_state,
872 scratch,
873 );
874
875 return;
876 }
877 }
878 PrimitiveKind::YuvImage { data_handle, .. } => {
879 profile_scope!("YuvImage");
880 let prim_data = &mut data_stores.yuv_image[*data_handle];
881 let common_data = &mut prim_data.common;
882 let yuv_image_data = &mut prim_data.kind;
883
884 if !use_legacy_path {
885 if prim_info.compositor_surface_kind == CompositorSurfaceKind::Underlay {
886 quad::prepare_quad(
887 &Cutout,
888 &prim_info.snapped_local_rect,
889 &prim_info.clip_chain.local_clip_rect,
890 common_data.aligned_aa_edges,
891 common_data.transformed_aa_edges,
892 prim_instance_index,
893 &None,
894 &prim_info.clip_chain,
895 quad_transform,
896 frame_context,
897 pic_context,
898 targets,
899 &data_stores.clip,
900 frame_state,
901 scratch,
902 );
903
904 return;
905 }
906 }
907
908 yuv_image_data.update(
911 common_data,
912 prim_info.compositor_surface_kind.is_composited(),
913 frame_state,
914 );
915
916 write_segment(
917 prim_info.segment_instance_index,
918 frame_state,
919 &mut scratch.frame.segments,
920 &mut scratch.frame.segment_instances,
921 |writer| {
922 yuv_image_data.write_prim_gpu_blocks(writer);
923 }
924 );
925 }
926 PrimitiveKind::Image { data_handle, .. } => {
927 profile_scope!("Image");
928
929 let prim_data = &mut data_stores.image[*data_handle];
930 let common_data = &mut prim_data.common;
931 let image_data = &mut prim_data.kind;
932
933 if !use_legacy_path {
934 let prim_rect = prim_info.snapped_local_rect;
935
936 if prim_info.compositor_surface_kind == CompositorSurfaceKind::Underlay {
937 quad::prepare_quad(
938 &Cutout,
939 &prim_rect,
940 &prim_info.clip_chain.local_clip_rect,
941 common_data.aligned_aa_edges,
942 common_data.transformed_aa_edges,
943 prim_instance_index,
944 &None,
945 &prim_info.clip_chain,
946 quad_transform,
947 frame_context,
948 pic_context,
949 targets,
950 &data_stores.clip,
951 frame_state,
952 scratch,
953 );
954
955 return;
956 }
957
958 crate::prim_store::image::prepare_image_quads(
959 &prim_rect,
960 common_data,
961 image_data,
962 &prim_info.clip_chain,
963 prim_instance_index,
964 quad_transform,
965 frame_context,
966 pic_context,
967 targets,
968 &data_stores.clip,
969 frame_state,
970 scratch,
971 );
972
973 return;
974 }
975
976 let img_scratch_handle = image_data.update(
979 common_data,
980 prim_instance_index,
981 prim_spatial_node_index,
982 frame_state,
983 frame_context,
984 prim_info.snapped_local_rect,
985 scratch,
986 );
987 scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
988 KindScratchHandle::Image(img_scratch_handle);
989 let image_adjustment = scratch.frame.images[img_scratch_handle].adjustment;
990 let effective_stretch_size =
991 image_data.stretch_size.resolve(&prim_info.snapped_local_rect);
992
993 write_segment(
994 prim_info.segment_instance_index,
995 frame_state,
996 &mut scratch.frame.segments,
997 &mut scratch.frame.segment_instances,
998 |request| {
999 image_data.write_prim_gpu_blocks(&image_adjustment, effective_stretch_size, request);
1000 },
1001 );
1002 }
1003 PrimitiveKind::LinearGradient { data_handle, .. } => {
1004 profile_scope!("LinearGradient");
1005 let prim_data = &data_stores.linear_grad[*data_handle];
1006 let prim_rect = prim_info.snapped_local_rect;
1007 let stretch_size = LayoutSize::new(
1008 prim_data.stretch_ratio.width * prim_rect.size().width,
1009 prim_data.stretch_ratio.height * prim_rect.size().height,
1010 );
1011
1012 if let Some(nine_patch) = &prim_data.border_nine_patch {
1013 quad::prepare_border_image_nine_patch(
1014 &*nine_patch,
1015 prim_data,
1016 &prim_rect,
1017 stretch_size,
1018 prim_data.common.aligned_aa_edges,
1019 prim_data.common.transformed_aa_edges,
1020 prim_instance_index,
1021 &prim_info.clip_chain,
1022 quad_transform,
1023 frame_context,
1024 pic_context,
1025 targets,
1026 &data_stores.clip,
1027 frame_state,
1028 scratch,
1029 );
1030 return;
1031 }
1032
1033 let (effective_start, effective_end) = if prim_data.reverse_stops {
1051 (prim_data.end_point, prim_data.start_point)
1052 } else {
1053 (prim_data.start_point, prim_data.end_point)
1054 };
1055 if linear_gradient_decomposes(
1056 &prim_rect,
1057 stretch_size,
1058 prim_data.tile_spacing,
1059 effective_start,
1060 effective_end,
1061 prim_data.extend_mode,
1062 &prim_data.stops,
1063 frame_context.fb_config.enable_dithering,
1064 ) {
1065 decompose_axis_aligned_gradient(
1066 &prim_rect,
1067 stretch_size,
1068 effective_start,
1069 effective_end,
1070 &prim_data.stops,
1071 &prim_info.clip_chain.local_clip_rect,
1072 |seg_rect, seg_start, seg_end, seg_stops, edge_aa_mask| {
1073 let pattern = LinearGradientSegmentPattern {
1074 start: seg_start,
1075 end: seg_end,
1076 stops: seg_stops,
1077 };
1078 quad::prepare_quad(
1079 &pattern,
1080 seg_rect,
1081 &prim_info.clip_chain.local_clip_rect,
1082 EdgeMask::empty(),
1083 edge_aa_mask,
1084 prim_instance_index,
1085 &None,
1086 &prim_info.clip_chain,
1087 quad_transform,
1088 frame_context,
1089 pic_context,
1090 targets,
1091 &data_stores.clip,
1092 frame_state,
1093 scratch,
1094 );
1095 },
1096 );
1097 return;
1098 }
1099
1100 let mut should_cache = !frame_context.fb_config.is_software
1102 && frame_state.resource_cache.texture_cache.allocated_color_bytes() < 10_000_000;
1103 if should_cache {
1104 let surface = &frame_state.surfaces[pic_context.surface_index.0];
1105 let clipped_surface_rect = surface.get_surface_rect(
1106 &prim_info.clip_chain.pic_coverage_rect,
1107 frame_context.spatial_tree,
1108 );
1109
1110 should_cache = if let Some(rect) = clipped_surface_rect {
1111 rect.width() < 512 && rect.height() < 512
1112 } else {
1113 false
1114 };
1115 }
1116
1117 let cache_key = if should_cache {
1118 quad::cache_key(
1119 data_handle.uid(),
1120 quad_transform,
1121 &prim_info.clip_chain,
1122 frame_state.clip_store,
1123 )
1124 } else {
1125 None
1126 };
1127
1128 let local_rect = prim_info.snapped_local_rect;
1129 quad::prepare_repeatable_quad(
1130 prim_data,
1131 &local_rect,
1132 &prim_info.clip_chain.local_clip_rect,
1133 stretch_size,
1134 prim_data.tile_spacing,
1135 prim_data.common.aligned_aa_edges,
1136 prim_data.common.transformed_aa_edges,
1137 prim_instance_index,
1138 &cache_key,
1139 &prim_info.clip_chain,
1140 quad_transform,
1141 frame_context,
1142 pic_context,
1143 targets,
1144 &data_stores.clip,
1145 frame_state,
1146 scratch,
1147 );
1148
1149 return;
1150 }
1151 PrimitiveKind::RadialGradient { data_handle, .. } => {
1152 profile_scope!("RadialGradient");
1153 let prim_data = &mut data_stores.radial_grad[*data_handle];
1154 let local_rect = prim_info.snapped_local_rect;
1155 let stretch_size = LayoutSize::new(
1156 prim_data.stretch_ratio.width * local_rect.size().width,
1157 prim_data.stretch_ratio.height * local_rect.size().height,
1158 );
1159
1160 if let Some(nine_patch) = &prim_data.border_nine_patch {
1161 quad::prepare_border_image_nine_patch(
1162 &*nine_patch,
1163 prim_data,
1164 &local_rect,
1165 stretch_size,
1166 prim_data.common.aligned_aa_edges,
1167 prim_data.common.transformed_aa_edges,
1168 prim_instance_index,
1169 &prim_info.clip_chain,
1170 quad_transform,
1171 frame_context,
1172 pic_context,
1173 targets,
1174 &data_stores.clip,
1175 frame_state,
1176 scratch,
1177 );
1178 return;
1179 }
1180
1181 quad::prepare_repeatable_quad(
1182 prim_data,
1183 &local_rect,
1184 &prim_info.clip_chain.local_clip_rect,
1185 stretch_size,
1186 prim_data.tile_spacing,
1187 prim_data.common.aligned_aa_edges,
1188 prim_data.common.transformed_aa_edges,
1189 prim_instance_index,
1190 &None,
1191 &prim_info.clip_chain,
1192 quad_transform,
1193 frame_context,
1194 pic_context,
1195 targets,
1196 &data_stores.clip,
1197 frame_state,
1198 scratch,
1199 );
1200 return;
1201 }
1202 PrimitiveKind::ConicGradient { data_handle, .. } => {
1203 profile_scope!("ConicGradient");
1204 let prim_data = &mut data_stores.conic_grad[*data_handle];
1205 let prim_rect = prim_info.snapped_local_rect;
1206 let stretch_size = LayoutSize::new(
1207 prim_data.stretch_ratio.width * prim_rect.size().width,
1208 prim_data.stretch_ratio.height * prim_rect.size().height,
1209 );
1210
1211 if let Some(nine_patch) = &prim_data.border_nine_patch {
1212 quad::prepare_border_image_nine_patch(
1213 &*nine_patch,
1214 prim_data,
1215 &prim_rect,
1216 stretch_size,
1217 prim_data.common.aligned_aa_edges,
1218 prim_data.common.transformed_aa_edges,
1219 prim_instance_index,
1220 &prim_info.clip_chain,
1221 quad_transform,
1222 frame_context,
1223 pic_context,
1224 targets,
1225 &data_stores.clip,
1226 frame_state,
1227 scratch,
1228 );
1229 return;
1230 }
1231
1232 let mut should_cache = frame_context.fb_config.is_software
1239 && frame_state.resource_cache.texture_cache.allocated_color_bytes() < 30_000_000;
1240 if should_cache {
1241 let surface = &frame_state.surfaces[pic_context.surface_index.0];
1242 let clipped_surface_rect = surface.get_surface_rect(
1243 &prim_info.clip_chain.pic_coverage_rect,
1244 frame_context.spatial_tree,
1245 );
1246
1247 should_cache = if let Some(rect) = clipped_surface_rect {
1248 rect.width() < 4096 && rect.height() < 4096
1249 } else {
1250 false
1251 };
1252 }
1253
1254 let cache_key = if should_cache {
1255 quad::cache_key(
1256 data_handle.uid(),
1257 quad_transform,
1258 &prim_info.clip_chain,
1259 frame_state.clip_store,
1260 )
1261 } else {
1262 None
1263 };
1264
1265 let local_rect = prim_info.snapped_local_rect;
1266 quad::prepare_repeatable_quad(
1267 prim_data,
1268 &local_rect,
1269 &prim_info.clip_chain.local_clip_rect,
1270 stretch_size,
1271 prim_data.tile_spacing,
1272 prim_data.common.aligned_aa_edges,
1273 prim_data.common.transformed_aa_edges,
1274 prim_instance_index,
1275 &cache_key,
1276 &prim_info.clip_chain,
1277 quad_transform,
1278 frame_context,
1279 pic_context,
1280 targets,
1281 &data_stores.clip,
1282 frame_state,
1283 scratch,
1284 );
1285 return;
1286 }
1287 PrimitiveKind::Picture { pic_index, .. } => {
1288 profile_scope!("Picture");
1289 let pic_scratch_handle = prim_info.kind_scratch.unwrap_picture();
1290 let pic = &mut store.pictures[pic_index.0];
1291
1292 if prim_info.clip_chain.needs_mask {
1293 let mut source_masks = Vec::new();
1300 let mut target_masks = Vec::new();
1301
1302 let force_target_mask = match pic.composite_mode {
1306 Some(PictureCompositeMode::Filter(Filter::Blur { .. })) |
1310 Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) |
1311 Some(PictureCompositeMode::SVGFEGraph( .. )) => {
1312 true
1313 }
1314 _ => {
1315 false
1316 }
1317 };
1318
1319 for i in 0 .. prim_info.clip_chain.clips_range.count {
1321 let clip_instance = frame_state.clip_store.get_instance_from_range(&prim_info.clip_chain.clips_range, i);
1322
1323 if !force_target_mask && clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
1324 source_masks.push(i);
1325 } else {
1326 target_masks.push(i);
1327 }
1328 }
1329
1330 let pic_surface_index = pic.raster_config.as_ref().unwrap().surface_index;
1331 let prim_local_rect: LayoutRect = frame_state
1332 .surfaces[pic_surface_index.0]
1333 .clipped_local_rect
1334 .cast_unit();
1335
1336 if !source_masks.is_empty() {
1340 let first_clip_node_index = frame_state.clip_store.clip_node_instances.len() as u32;
1341 let parent_task_id = scratch.frame.pictures[pic_scratch_handle].primary_render_task_id.expect("bug: no composite mode");
1342
1343 for instance in source_masks {
1345 let clip_instance = frame_state.clip_store.get_instance_from_range(&prim_info.clip_chain.clips_range, instance);
1346
1347 for tile in frame_state.clip_store.visible_mask_tiles(clip_instance) {
1348 frame_state.rg_builder.add_dependency(
1349 parent_task_id,
1350 tile.task_id,
1351 );
1352 }
1353
1354 frame_state.clip_store.clip_node_instances.push(clip_instance.clone());
1355 }
1356
1357 let clip_node_range = ClipNodeRange {
1358 first: first_clip_node_index,
1359 count: frame_state.clip_store.clip_node_instances.len() as u32 - first_clip_node_index,
1360 };
1361
1362 let pic_task_id = scratch.frame.pictures[pic_scratch_handle].primary_render_task_id.expect("uh oh");
1364 let pic_task = frame_state.rg_builder.get_task_mut(pic_task_id);
1365
1366 let RenderTaskKind::Picture(info) = &pic_task.kind else { unreachable!() };
1367
1368 let task_rect = DeviceRect::from_origin_and_size(
1369 info.content_origin,
1370 pic_task.get_target_size().to_f32(),
1371 );
1372
1373 quad::prepare_clip_range(
1374 clip_node_range,
1375 pic_task_id,
1376 &task_rect,
1377 &prim_local_rect,
1378 prim_spatial_node_index,
1379 info.raster_spatial_node_index,
1380 info.device_pixel_scale,
1381 &data_stores.clip,
1382 frame_state.clip_store,
1383 frame_context.spatial_tree,
1384 frame_state.rg_builder,
1385 &mut frame_state.frame_gpu_data.f32,
1386 frame_state.transforms,
1387 );
1388 }
1389
1390 if !target_masks.is_empty() {
1393 let surface = &frame_state.surfaces[pic_context.surface_index.0];
1394 let coverage_rect = prim_info.clip_chain.pic_coverage_rect;
1395
1396 let device_pixel_scale = surface.device_pixel_scale;
1397 let raster_spatial_node_index = surface.raster_spatial_node_index;
1398
1399 let Some(clipped_surface_rect) = surface.get_surface_rect(
1400 &coverage_rect,
1401 frame_context.spatial_tree,
1402 ) else {
1403 return;
1404 };
1405
1406 let empty_task = EmptyTask {
1409 content_origin: clipped_surface_rect.min.to_f32(),
1410 device_pixel_scale,
1411 raster_spatial_node_index,
1412 };
1413
1414 let task_size = clipped_surface_rect.size();
1415
1416 let clip_task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic(
1417 task_size,
1418 RenderTaskKind::Empty(empty_task),
1419 ));
1420
1421 let first_clip_node_index = frame_state.clip_store.clip_node_instances.len() as u32;
1423 for instance in target_masks {
1424 let clip_instance = frame_state.clip_store.get_instance_from_range(&prim_info.clip_chain.clips_range, instance);
1425
1426 for tile in frame_state.clip_store.visible_mask_tiles(clip_instance) {
1427 frame_state.rg_builder.add_dependency(
1428 clip_task_id,
1429 tile.task_id,
1430 );
1431 }
1432
1433 frame_state.clip_store.clip_node_instances.push(clip_instance.clone());
1434 }
1435
1436 let clip_node_range = ClipNodeRange {
1437 first: first_clip_node_index,
1438 count: frame_state.clip_store.clip_node_instances.len() as u32 - first_clip_node_index,
1439 };
1440
1441 let task_rect = clipped_surface_rect.to_f32();
1442
1443 quad::prepare_clip_range(
1444 clip_node_range,
1445 clip_task_id,
1446 &task_rect,
1447 &prim_local_rect,
1448 prim_spatial_node_index,
1449 raster_spatial_node_index,
1450 device_pixel_scale,
1451 &data_stores.clip,
1452 frame_state.clip_store,
1453 frame_context.spatial_tree,
1454 frame_state.rg_builder,
1455 &mut frame_state.frame_gpu_data.f32,
1456 frame_state.transforms,
1457 );
1458
1459 let clip_task_index = ClipTaskIndex(scratch.frame.clip_mask_instances.len() as _);
1460 scratch.frame.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id));
1461 scratch.frame.draws[prim_instance_index.0 as usize].clip_task_index = clip_task_index;
1462 frame_state.surface_builder.add_child_render_task(
1463 clip_task_id,
1464 frame_state.rg_builder,
1465 );
1466 }
1467 }
1468
1469 pic.write_gpu_blocks(
1470 frame_state,
1471 data_stores,
1472 &mut scratch.frame.pictures[pic_scratch_handle],
1473 );
1474
1475 if let Picture3DContext::In { root_data: None, plane_splitter_index, ancestor_index, .. } = pic.context_3d {
1476 let dirty_rect = frame_state.current_dirty_region().combined;
1477 let visibility_spatial_node = frame_state.current_dirty_region().visibility_spatial_node;
1478
1479 let splitter = &mut frame_state.plane_splitters[plane_splitter_index.0];
1480 let surface_index = pic.raster_config.as_ref().unwrap().surface_index;
1481 let surface = &frame_state.surfaces[surface_index.0];
1482 let local_prim_rect = surface.clipped_local_rect.cast_unit();
1483
1484 PictureInstance::add_split_plane(
1485 splitter,
1486 frame_context.spatial_tree,
1487 prim_spatial_node_index,
1488 ancestor_index,
1489 visibility_spatial_node,
1490 local_prim_rect,
1491 &prim_info.clip_chain.local_clip_rect,
1492 dirty_rect,
1493 plane_split_anchor,
1494 );
1495 }
1496 }
1497 PrimitiveKind::BackdropCapture { .. } => {
1498 frame_state.surface_builder.register_resolve_source();
1501
1502 if frame_context.debug_flags.contains(DebugFlags::HIGHLIGHT_BACKDROP_FILTERS) {
1503 if let Some(world_rect) = pic_state.map_pic_to_vis.map(&prim_info.clip_chain.pic_coverage_rect) {
1504 scratch.push_debug_rect(
1505 world_rect.cast_unit(),
1506 2,
1507 crate::debug_colors::MAGENTA,
1508 ColorF::TRANSPARENT,
1509 );
1510 }
1511 }
1512 }
1513 PrimitiveKind::BackdropRender { pic_index, .. } => {
1514 match frame_state.surface_builder.sub_graph_output_map.get(pic_index).cloned() {
1515 Some(sub_graph_output_id) => {
1516 frame_state.surface_builder.add_child_render_task(
1517 sub_graph_output_id,
1518 frame_state.rg_builder,
1519 );
1520 let backdrop_handle = scratch.frame.backdrop_render.push(BackdropRenderScratch {
1521 src_task_id: sub_graph_output_id,
1522 });
1523 scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
1524 KindScratchHandle::BackdropRender(backdrop_handle);
1525 }
1526 None => {
1527 scratch.frame.draws[prim_instance_index.0 as usize].reset();
1530 }
1531 }
1532 }
1533 }
1534
1535 match prim_info.state {
1536 DrawState::Unset => {
1537 panic!("bug: invalid vis state");
1538 }
1539 DrawState::Visible { .. } => {
1540 frame_state.push_prim(
1541 &PrimitiveCommand::simple(storage::Index::from_u32(prim_instance_index.0)),
1542 prim_spatial_node_index,
1543 targets,
1544 );
1545 }
1546 DrawState::PassThrough | DrawState::Culled => {}
1547 }
1548}
1549
1550
1551fn write_segment<F>(
1552 segment_instance_index: SegmentInstanceIndex,
1553 frame_state: &mut FrameBuildingState,
1554 segments: &mut SegmentStorage,
1555 segment_instances: &mut SegmentInstanceStorage,
1556 f: F,
1557) where F: Fn(&mut GpuBufferWriterF) {
1558 debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID);
1559 if segment_instance_index != SegmentInstanceIndex::UNUSED {
1560 let segment_instance = &mut segment_instances[segment_instance_index];
1561
1562 let segments = &segments[segment_instance.segments_range];
1563 let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3 + segments.len() * VECS_PER_SEGMENT);
1564
1565 f(&mut writer);
1566
1567 for segment in segments {
1568 segment.write_gpu_blocks(&mut writer);
1569 }
1570
1571 segment_instance.gpu_data = writer.finish();
1572 }
1573}
1574
1575fn update_clip_task_for_brush(
1576 instance: &PrimitiveInstance,
1577 prim_segment_instance_index: SegmentInstanceIndex,
1578 prim_brush_segments_range: storage::Range<BrushSegment>,
1579 prim_clip_chain: &ClipChainInstance,
1580 prim_origin: &LayoutPoint,
1581 prim_spatial_node_index: SpatialNodeIndex,
1582 root_spatial_node_index: SpatialNodeIndex,
1583 visibility_spatial_node_index: SpatialNodeIndex,
1584 pic_context: &PictureContext,
1585 pic_state: &mut PictureState,
1586 frame_context: &FrameBuildingContext,
1587 frame_state: &mut FrameBuildingState,
1588 data_stores: &mut DataStores,
1589 segments_store: &mut SegmentStorage,
1590 segment_instances_store: &mut SegmentInstanceStorage,
1591 clip_mask_instances: &mut Vec<ClipMaskKind>,
1592 device_pixel_scale: DevicePixelScale,
1593) -> Option<ClipTaskIndex> {
1594 let segments = match instance.kind {
1595 PrimitiveKind::BoxShadow { .. } => {
1596 unreachable!("BUG: box-shadows should not hit legacy brush clip path");
1597 }
1598 PrimitiveKind::Picture { .. } |
1599 PrimitiveKind::TextRun { .. } |
1600 PrimitiveKind::LineDecoration { .. } |
1601 PrimitiveKind::BackdropCapture { .. } |
1602 PrimitiveKind::BackdropRender { .. } => {
1603 return None;
1604 }
1605 PrimitiveKind::Image { .. } |
1606 PrimitiveKind::YuvImage { .. } |
1607 PrimitiveKind::Rectangle { .. } => {
1608 if prim_segment_instance_index == SegmentInstanceIndex::UNUSED {
1609 return None;
1610 }
1611
1612 let segment_instance = &segment_instances_store[prim_segment_instance_index];
1613
1614 &segments_store[segment_instance.segments_range]
1615 }
1616 PrimitiveKind::NormalBorder { .. } |
1617 PrimitiveKind::ImageBorder { .. } => {
1618 if prim_brush_segments_range.is_empty() {
1623 return None;
1624 }
1625 &segments_store[prim_brush_segments_range]
1626 }
1627 PrimitiveKind::LinearGradient { .. } => {
1628 unreachable!("BUG: linear gradients should always use quad path");
1629 }
1630 PrimitiveKind::RadialGradient { .. } => {
1631 unreachable!("BUG: radial gradients should always use quad path");
1632 }
1633 PrimitiveKind::ConicGradient { .. } => {
1634 unreachable!("BUG: conic gradients should always use quad path");
1635 }
1636 };
1637
1638 if segments.is_empty() {
1641 return None;
1642 }
1643
1644 let clip_task_index = ClipTaskIndex(clip_mask_instances.len() as _);
1648
1649 if segments.len() == 1 {
1654 let clip_mask_kind = update_brush_segment_clip_task(
1655 &segments[0],
1656 Some(prim_clip_chain),
1657 root_spatial_node_index,
1658 pic_context.surface_index,
1659 frame_context,
1660 frame_state,
1661 device_pixel_scale,
1662 );
1663 clip_mask_instances.push(clip_mask_kind);
1664 } else {
1665 let dirty_rect = frame_state.current_dirty_region().combined;
1666
1667 for segment in segments {
1668 frame_state.clip_store.set_active_clips_from_clip_chain(
1672 prim_clip_chain,
1673 prim_spatial_node_index,
1674 visibility_spatial_node_index,
1675 &frame_context.spatial_tree,
1676 );
1677
1678 let segment_clip_chain = frame_state
1679 .clip_store
1680 .build_clip_chain_instance(
1681 segment.local_rect.translate(prim_origin.to_vector()),
1682 &pic_state.map_local_to_pic,
1683 &pic_state.map_pic_to_vis,
1684 &frame_context.spatial_tree,
1685 &mut frame_state.frame_gpu_data.f32,
1686 frame_state.resource_cache,
1687 &dirty_rect,
1688 &mut data_stores.clip,
1689 frame_state.rg_builder,
1690 false,
1691 );
1692
1693 let clip_mask_kind = update_brush_segment_clip_task(
1694 &segment,
1695 segment_clip_chain.as_ref(),
1696 root_spatial_node_index,
1697 pic_context.surface_index,
1698 frame_context,
1699 frame_state,
1700 device_pixel_scale,
1701 );
1702 clip_mask_instances.push(clip_mask_kind);
1703 }
1704 }
1705
1706 Some(clip_task_index)
1707}
1708
1709pub fn update_clip_task(
1710 instance: &mut PrimitiveInstance,
1711 prim_instance_index: PrimitiveInstanceIndex,
1712 prim_origin: &LayoutPoint,
1713 prim_spatial_node_index: SpatialNodeIndex,
1714 root_spatial_node_index: SpatialNodeIndex,
1715 visibility_spatial_node_index: SpatialNodeIndex,
1716 pic_context: &PictureContext,
1717 pic_state: &mut PictureState,
1718 frame_context: &FrameBuildingContext,
1719 frame_state: &mut FrameBuildingState,
1720 prim_store: &mut PrimitiveStore,
1721 data_stores: &mut DataStores,
1722 scratch: &mut PrimitiveScratchBuffer,
1723) -> bool {
1724 let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale;
1725
1726 let clip_chain_snapshot = scratch.frame.draws[prim_instance_index.0 as usize].clip_chain;
1727 build_segments_if_needed(
1728 instance,
1729 prim_instance_index,
1730 &clip_chain_snapshot,
1731 frame_state,
1732 prim_store,
1733 data_stores,
1734 scratch,
1735 );
1736
1737 let prim_segment_instance_index = scratch.frame.draws[prim_instance_index.0 as usize].segment_instance_index;
1739 let prim_brush_segments_range = match instance.kind {
1744 PrimitiveKind::NormalBorder { .. } => {
1745 let nb_handle = scratch.frame.draws[prim_instance_index.0 as usize]
1746 .kind_scratch
1747 .unwrap_normal_border();
1748 scratch.frame.normal_border[nb_handle].brush_segments_range
1749 }
1750 PrimitiveKind::ImageBorder { .. } => {
1751 let ib_handle = scratch.frame.draws[prim_instance_index.0 as usize]
1752 .kind_scratch
1753 .unwrap_image_border();
1754 scratch.frame.image_border[ib_handle].brush_segments_range
1755 }
1756 _ => storage::Range::empty(),
1757 };
1758 let new_clip_task_index = if let Some(clip_task_index) = update_clip_task_for_brush(
1759 instance,
1760 prim_segment_instance_index,
1761 prim_brush_segments_range,
1762 &clip_chain_snapshot,
1763 prim_origin,
1764 prim_spatial_node_index,
1765 root_spatial_node_index,
1766 visibility_spatial_node_index,
1767 pic_context,
1768 pic_state,
1769 frame_context,
1770 frame_state,
1771 data_stores,
1772 &mut scratch.frame.segments,
1773 &mut scratch.frame.segment_instances,
1774 &mut scratch.frame.clip_mask_instances,
1775 device_pixel_scale,
1776 ) {
1777 clip_task_index
1778 } else if scratch.frame.draws[prim_instance_index.0 as usize].clip_chain.needs_mask {
1779 let unadjusted_device_rect = match frame_state.surfaces[pic_context.surface_index.0].get_surface_rect(
1783 &scratch.frame.draws[prim_instance_index.0 as usize].clip_chain.pic_coverage_rect,
1784 frame_context.spatial_tree,
1785 ) {
1786 Some(rect) => rect,
1787 None => return false,
1788 };
1789
1790 let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(
1791 unadjusted_device_rect,
1792 device_pixel_scale,
1793 );
1794
1795 if device_rect.size().to_i32().is_empty() {
1796 log::warn!("Bad adjusted clip task size {:?} (was {:?})", device_rect.size(), unadjusted_device_rect.size());
1797 return false;
1798 }
1799
1800 let clip_task_id = RenderTaskKind::new_mask(
1801 device_rect,
1802 scratch.frame.draws[prim_instance_index.0 as usize].clip_chain.clips_range,
1803 root_spatial_node_index,
1804 frame_state.rg_builder,
1805 device_pixel_scale,
1806 frame_context.fb_config,
1807 );
1808 let clip_task_index = ClipTaskIndex(scratch.frame.clip_mask_instances.len() as _);
1810 scratch.frame.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id));
1811 frame_state.surface_builder.add_child_render_task(
1812 clip_task_id,
1813 frame_state.rg_builder,
1814 );
1815 clip_task_index
1816 } else {
1817 ClipTaskIndex::INVALID
1818 };
1819 scratch.frame.draws[prim_instance_index.0 as usize].clip_task_index = new_clip_task_index;
1820
1821 true
1822}
1823
1824pub fn update_brush_segment_clip_task(
1827 segment: &BrushSegment,
1828 clip_chain: Option<&ClipChainInstance>,
1829 root_spatial_node_index: SpatialNodeIndex,
1830 surface_index: SurfaceIndex,
1831 frame_context: &FrameBuildingContext,
1832 frame_state: &mut FrameBuildingState,
1833 device_pixel_scale: DevicePixelScale,
1834) -> ClipMaskKind {
1835 let clip_chain = match clip_chain {
1836 Some(chain) => chain,
1837 None => return ClipMaskKind::Clipped,
1838 };
1839 if !clip_chain.needs_mask ||
1840 (!segment.may_need_clip_mask && !clip_chain.has_non_local_clips) {
1841 return ClipMaskKind::None;
1842 }
1843
1844 let unadjusted_device_rect = match frame_state.surfaces[surface_index.0].get_surface_rect(
1845 &clip_chain.pic_coverage_rect,
1846 frame_context.spatial_tree,
1847 ) {
1848 Some(rect) => rect,
1849 None => return ClipMaskKind::Clipped,
1850 };
1851
1852 let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(unadjusted_device_rect, device_pixel_scale);
1853
1854 if device_rect.size().to_i32().is_empty() {
1855 log::warn!("Bad adjusted mask size {:?} (was {:?})", device_rect.size(), unadjusted_device_rect.size());
1856 return ClipMaskKind::Clipped;
1857 }
1858
1859 let clip_task_id = RenderTaskKind::new_mask(
1860 device_rect,
1861 clip_chain.clips_range,
1862 root_spatial_node_index,
1863 frame_state.rg_builder,
1864 device_pixel_scale,
1865 frame_context.fb_config,
1866 );
1867
1868 frame_state.surface_builder.add_child_render_task(
1869 clip_task_id,
1870 frame_state.rg_builder,
1871 );
1872 ClipMaskKind::Mask(clip_task_id)
1873}
1874
1875
1876fn write_brush_segment_description(
1877 prim_local_rect: LayoutRect,
1878 prim_local_clip_rect: LayoutRect,
1879 clip_chain: &ClipChainInstance,
1880 segment_builder: &mut SegmentBuilder,
1881 clip_store: &ClipStore,
1882 data_stores: &DataStores,
1883) -> bool {
1884 if prim_local_rect.area() < MIN_BRUSH_SPLIT_AREA {
1887 return false;
1888 }
1889
1890 segment_builder.initialize(
1898 prim_local_rect,
1899 None,
1900 prim_local_clip_rect,
1901 );
1902
1903 for i in 0 .. clip_chain.clips_range.count {
1905 let clip_instance = clip_store
1906 .get_instance_from_range(&clip_chain.clips_range, i);
1907 let clip_node = &data_stores.clip[clip_instance.handle];
1908
1909 if !clip_instance.flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) {
1915 continue;
1916 }
1917
1918 let (local_clip_rect, radius, mode) = match clip_node.item.kind {
1919 ClipItemKind::RoundedRectangle { radius, mode } => {
1920 let radius = clamped_radius(&radius, clip_instance.clip_rect.size());
1921 (clip_instance.clip_rect, Some(radius), mode)
1922 }
1923 ClipItemKind::Rectangle { mode } => {
1924 (clip_instance.clip_rect, None, mode)
1925 }
1926 ClipItemKind::Image { .. } => {
1927 panic!("bug: masks not supported on old segment path");
1928 }
1929 };
1930
1931 segment_builder.push_clip_rect(local_clip_rect, radius, mode);
1932 }
1933
1934 true
1935}
1936
1937fn build_segments_if_needed(
1938 instance: &mut PrimitiveInstance,
1939 prim_instance_index: PrimitiveInstanceIndex,
1940 prim_clip_chain: &ClipChainInstance,
1941 frame_state: &mut FrameBuildingState,
1942 prim_store: &mut PrimitiveStore,
1943 data_stores: &DataStores,
1944 scratch: &mut PrimitiveScratchBuffer,
1945) {
1946
1947 let prim_local_rect = data_stores.get_local_prim_rect(
1950 instance,
1951 scratch.frame.draws[prim_instance_index.0 as usize].snapped_local_rect,
1952 &prim_store.pictures,
1953 frame_state.surfaces,
1954 );
1955
1956 match instance.kind {
1960 PrimitiveKind::Rectangle { .. } => {
1961 }
1963 PrimitiveKind::YuvImage { .. } => {
1964 let csk = scratch.frame.draws[prim_instance_index.0 as usize].compositor_surface_kind;
1966 if !csk.supports_segments() {
1967 return;
1968 }
1969 }
1970 PrimitiveKind::Image { data_handle, .. } => {
1971 let image_data = &data_stores.image[data_handle].kind;
1972 let csk = scratch.frame.draws[prim_instance_index.0 as usize].compositor_surface_kind;
1973
1974 if !csk.supports_segments() ||
1977 frame_state.resource_cache
1978 .get_image_properties(image_data.key)
1979 .and_then(|properties| properties.tiling)
1980 .is_some()
1981 {
1982 return;
1983 }
1984 }
1985 PrimitiveKind::Picture { .. } |
1986 PrimitiveKind::TextRun { .. } |
1987 PrimitiveKind::NormalBorder { .. } |
1988 PrimitiveKind::ImageBorder { .. } |
1989 PrimitiveKind::LinearGradient { .. } |
1990 PrimitiveKind::RadialGradient { .. } |
1991 PrimitiveKind::ConicGradient { .. } |
1992 PrimitiveKind::LineDecoration { .. } |
1993 PrimitiveKind::BackdropCapture { .. } |
1994 PrimitiveKind::BackdropRender { .. } => {
1995 return;
1997 }
1998 PrimitiveKind::BoxShadow { .. } => {
1999 unreachable!("BUG: box-shadows should not hit legacy brush clip path");
2000 }
2001 };
2002
2003 let mut segments: SmallVec<[BrushSegment; 8]> = SmallVec::new();
2008 let clip_leaf = frame_state.clip_tree.get_leaf(instance.clip_leaf_id);
2009
2010 if write_brush_segment_description(
2011 prim_local_rect,
2012 clip_leaf.snapped_local_clip_rect,
2013 prim_clip_chain,
2014 &mut frame_state.segment_builder,
2015 frame_state.clip_store,
2016 data_stores,
2017 ) {
2018 frame_state.segment_builder.build(|segment| {
2019 segments.push(
2020 BrushSegment::new(
2021 segment.rect.translate(-prim_local_rect.min.to_vector()),
2022 segment.has_mask,
2023 segment.edge_flags,
2024 [0.0; 4],
2025 BrushFlags::PERSPECTIVE_INTERPOLATION,
2026 ),
2027 );
2028 });
2029 }
2030
2031 if segments.len() <= 1 {
2043 return;
2045 }
2046
2047 let segments_range = scratch.frame.segments.extend(segments);
2048 let new_index = scratch.frame.segment_instances.push(BrushSegmentation {
2049 segments_range,
2050 gpu_data: GpuBufferAddress::INVALID,
2051 });
2052 scratch.frame.draws[prim_instance_index.0 as usize].segment_instance_index = new_index;
2053}
2054
2055fn adjust_mask_scale_for_max_size(device_rect: DeviceIntRect, device_pixel_scale: DevicePixelScale) -> (DeviceIntRect, DevicePixelScale) {
2057 if device_rect.width() > MAX_MASK_SIZE || device_rect.height() > MAX_MASK_SIZE {
2058 let device_rect_f = device_rect.to_f32();
2061 let scale = (MAX_MASK_SIZE - 1) as f32 /
2062 f32::max(device_rect_f.width(), device_rect_f.height());
2063 let new_device_pixel_scale = device_pixel_scale * Scale::new(scale);
2064 let new_device_rect = (device_rect_f * Scale::new(scale))
2065 .round_out()
2066 .to_i32();
2067 (new_device_rect, new_device_pixel_scale)
2068 } else {
2069 (device_rect, device_pixel_scale)
2070 }
2071}
2072
2073impl CompositorSurfaceKind {
2074 fn supports_segments(&self) -> bool {
2076 match self {
2077 CompositorSurfaceKind::Underlay | CompositorSurfaceKind::Overlay => false,
2078 CompositorSurfaceKind::Blit => true,
2079 }
2080 }
2081}
2082
2083struct LinearGradientSegmentPattern {
2088 start: LayoutPoint,
2089 end: LayoutPoint,
2090 stops: [GradientStop; 2],
2091}
2092
2093impl PatternBuilder for LinearGradientSegmentPattern {
2094 fn build(
2095 &self,
2096 _sub_rect: Option<DeviceRect>,
2097 offset: LayoutVector2D,
2098 ctx: &PatternBuilderContext,
2099 state: &mut PatternBuilderState,
2100 ) -> Pattern {
2101 let prim_offset = offset + ctx.prim_origin.to_vector();
2102 linear_gradient_pattern(
2103 self.start + prim_offset,
2104 self.end + prim_offset,
2105 ExtendMode::Clamp,
2106 &self.stops,
2107 ctx.fb_config.is_software,
2108 state.frame_gpu_data,
2109 )
2110 }
2111}