1use api::{AlphaType, ClipMode, ImageBufferKind};
6use api::{FontInstanceFlags, YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF};
7use api::units::*;
8use crate::clip::{clamped_radius, ClipNodeFlags, ClipNodeRange, ClipItemKind, ClipStore};
9use crate::command_buffer::PrimitiveCommand;
10use crate::composite::CompositorSurfaceKind;
11use crate::pattern::PatternKind;
12use crate::spatial_tree::{SpatialTree, SpatialNodeIndex, CoordinateSystemId};
13use glyph_rasterizer::{GlyphFormat, SubpixelDirection};
14use crate::gpu_types::{BrushFlags, BrushInstance, ImageSource, PrimitiveHeaders, UvRectKind, ZBufferId, ZBufferIdGenerator};
15use crate::gpu_types::SplitCompositeInstance;
16use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
17use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex};
18use crate::gpu_types::{ImageBrushUserData, get_shader_opacity, MaskInstance};
19use crate::gpu_types::{ClipMaskInstanceCommon, ClipMaskInstanceRect};
20use crate::internal_types::{FastHashMap, Filter, FrameAllocator, FrameMemory, FrameVec, Swizzle, TextureSource};
21use crate::picture::{Picture3DContext, PictureCompositeMode, calculate_screen_uv};
22use crate::prim_store::{PrimitiveKind, ClipData};
23use crate::prim_store::{PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
24use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex};
25use crate::prim_store::VECS_PER_SEGMENT;
26use crate::quad;
27use crate::render_target::RenderTargetContext;
28use crate::render_task_graph::{RenderTaskId, RenderTaskGraph};
29use crate::render_task::{RenderTaskAddress, RenderTaskKind};
30use crate::renderer::{BlendMode, GpuBufferAddress, GpuBufferBlockF, GpuBufferBuilder, ShaderColorMode};
31use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH;
32use crate::resource_cache::{GlyphFetchResult, ImageProperties};
33use crate::space::SpaceMapper;
34use crate::transform::{GpuTransformId, TransformPalette, TransformMetadata};
35use crate::visibility::{PrimitiveVisibilityFlags, DrawState};
36use smallvec::SmallVec;
37use std::{f32, i32, usize};
38use crate::util::{project_rect, MaxRect, ScaleOffset};
39use crate::segment::EdgeMask;
40
41
42const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fffffff);
45
46pub const INVALID_SEGMENT_INDEX: i32 = 0xffff;
48
49const CLIP_RECTANGLE_TILE_SIZE: i32 = 128;
51
52const CLIP_RECTANGLE_AREA_THRESHOLD: f32 = (CLIP_RECTANGLE_TILE_SIZE * CLIP_RECTANGLE_TILE_SIZE * 4) as f32;
54
55#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
56#[cfg_attr(feature = "capture", derive(Serialize))]
57#[cfg_attr(feature = "replay", derive(Deserialize))]
58pub enum BrushBatchKind {
59 Solid,
60 Image(ImageBufferKind),
61 Blend,
62 MixBlend {
63 task_id: RenderTaskId,
64 backdrop_id: RenderTaskId,
65 },
66 YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange),
67 Opacity,
68}
69
70#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
71#[cfg_attr(feature = "capture", derive(Serialize))]
72#[cfg_attr(feature = "replay", derive(Deserialize))]
73pub enum BatchKind {
74 SplitComposite,
75 TextRun(GlyphFormat),
76 Brush(BrushBatchKind),
77 Quad(PatternKind),
78}
79
80#[derive(Copy, Clone, Debug)]
82#[cfg_attr(feature = "capture", derive(Serialize))]
83#[cfg_attr(feature = "replay", derive(Deserialize))]
84pub struct TextureSet {
85 pub colors: [TextureSource; 3],
86}
87
88impl TextureSet {
89 const UNTEXTURED: TextureSet = TextureSet {
90 colors: [
91 TextureSource::Invalid,
92 TextureSource::Invalid,
93 TextureSource::Invalid,
94 ],
95 };
96
97 fn prim_textured(
99 color: TextureSource,
100 ) -> Self {
101 TextureSet {
102 colors: [
103 color,
104 TextureSource::Invalid,
105 TextureSource::Invalid,
106 ],
107 }
108 }
109
110 fn is_compatible_with(&self, other: &TextureSet) -> bool {
111 self.colors[0].is_compatible(&other.colors[0]) &&
112 self.colors[1].is_compatible(&other.colors[1]) &&
113 self.colors[2].is_compatible(&other.colors[2])
114 }
115}
116
117impl TextureSource {
118 fn combine(&self, other: TextureSource) -> TextureSource {
119 if other == TextureSource::Invalid {
120 *self
121 } else {
122 other
123 }
124 }
125}
126
127#[derive(Copy, Clone, Debug)]
130#[cfg_attr(feature = "capture", derive(Serialize))]
131#[cfg_attr(feature = "replay", derive(Deserialize))]
132pub struct BatchTextures {
133 pub input: TextureSet,
134 pub clip_mask: TextureSource,
135}
136
137impl BatchTextures {
138 pub fn empty() -> BatchTextures {
140 BatchTextures {
141 input: TextureSet::UNTEXTURED,
142 clip_mask: TextureSource::Invalid,
143 }
144 }
145
146 pub fn prim_textured(
148 color: TextureSource,
149 clip_mask: TextureSource,
150 ) -> BatchTextures {
151 BatchTextures {
152 input: TextureSet::prim_textured(color),
153 clip_mask,
154 }
155 }
156
157 pub fn prim_untextured(
159 clip_mask: TextureSource,
160 ) -> BatchTextures {
161 BatchTextures {
162 input: TextureSet::UNTEXTURED,
163 clip_mask,
164 }
165 }
166
167 pub fn composite_rgb(
169 texture: TextureSource,
170 ) -> BatchTextures {
171 BatchTextures {
172 input: TextureSet {
173 colors: [
174 texture,
175 TextureSource::Invalid,
176 TextureSource::Invalid,
177 ],
178 },
179 clip_mask: TextureSource::Invalid,
180 }
181 }
182
183 pub fn composite_yuv(
185 color0: TextureSource,
186 color1: TextureSource,
187 color2: TextureSource,
188 ) -> BatchTextures {
189 BatchTextures {
190 input: TextureSet {
191 colors: [color0, color1, color2],
192 },
193 clip_mask: TextureSource::Invalid,
194 }
195 }
196
197 pub fn is_compatible_with(&self, other: &BatchTextures) -> bool {
198 if !self.clip_mask.is_compatible(&other.clip_mask) {
199 return false;
200 }
201
202 self.input.is_compatible_with(&other.input)
203 }
204
205 pub fn combine_textures(&self, other: BatchTextures) -> Option<BatchTextures> {
206 if !self.is_compatible_with(&other) {
207 return None;
208 }
209
210 let mut new_textures = BatchTextures::empty();
211
212 new_textures.clip_mask = self.clip_mask.combine(other.clip_mask);
213
214 for i in 0 .. 3 {
215 new_textures.input.colors[i] = self.input.colors[i].combine(other.input.colors[i]);
216 }
217
218 Some(new_textures)
219 }
220
221 fn merge(&mut self, other: &BatchTextures) {
222 self.clip_mask = self.clip_mask.combine(other.clip_mask);
223
224 for (s, o) in self.input.colors.iter_mut().zip(other.input.colors.iter()) {
225 *s = s.combine(*o);
226 }
227 }
228}
229
230#[derive(Copy, Clone, Debug)]
231#[cfg_attr(feature = "capture", derive(Serialize))]
232#[cfg_attr(feature = "replay", derive(Deserialize))]
233pub struct BatchKey {
234 pub kind: BatchKind,
235 pub blend_mode: BlendMode,
236 pub textures: BatchTextures,
237}
238
239impl BatchKey {
240 pub fn new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self {
241 BatchKey {
242 kind,
243 blend_mode,
244 textures,
245 }
246 }
247
248 pub fn is_compatible_with(&self, other: &BatchKey) -> bool {
249 self.kind == other.kind && self.blend_mode == other.blend_mode && self.textures.is_compatible_with(&other.textures)
250 }
251}
252
253pub struct BatchRects {
254 batch: PictureRect,
259 items: Option<FrameVec<PictureRect>>,
262 allocator: FrameAllocator,
266}
267
268impl BatchRects {
269 fn new(allocator: FrameAllocator) -> Self {
270 BatchRects {
271 batch: PictureRect::zero(),
272 items: None,
273 allocator,
274 }
275 }
276
277 #[inline]
278 fn add_rect(&mut self, rect: &PictureRect) {
279 let union = self.batch.union(rect);
280 if let Some(items) = &mut self.items {
284 items.push(*rect);
285 } else if self.batch.area() + rect.area() < union.area() {
286 let mut items = self.allocator.clone().new_vec_with_capacity(16);
287 items.push(self.batch);
288 items.push(*rect);
289 self.items = Some(items);
290 }
291
292 self.batch = union;
293 }
294
295 #[inline]
296 fn intersects(&mut self, rect: &PictureRect) -> bool {
297 if !self.batch.intersects(rect) {
298 return false;
299 }
300
301 if let Some(items) = &self.items {
302 items.iter().any(|item| item.intersects(rect))
303 } else {
304 true
307 }
308 }
309}
310
311
312pub struct AlphaBatchList {
313 pub batches: FrameVec<PrimitiveBatch>,
314 pub batch_rects: FrameVec<BatchRects>,
315 current_batch_index: usize,
316 current_z_id: ZBufferId,
317 break_advanced_blend_batches: bool,
318}
319
320impl AlphaBatchList {
321 fn new(break_advanced_blend_batches: bool, preallocate: usize, memory: &FrameMemory) -> Self {
322 AlphaBatchList {
323 batches: memory.new_vec_with_capacity(preallocate),
324 batch_rects: memory.new_vec_with_capacity(preallocate),
325 current_z_id: ZBufferId::invalid(),
326 current_batch_index: usize::MAX,
327 break_advanced_blend_batches,
328 }
329 }
330
331 fn clear(&mut self) {
335 self.current_batch_index = usize::MAX;
336 self.current_z_id = ZBufferId::invalid();
337 self.batches.clear();
338 self.batch_rects.clear();
339 }
340
341 pub fn set_params_and_get_batch(
342 &mut self,
343 key: BatchKey,
344 features: BatchFeatures,
345 z_bounding_rect: &PictureRect,
348 z_id: ZBufferId,
349 ) -> &mut FrameVec<PrimitiveInstanceData> {
350 if z_id != self.current_z_id ||
351 self.current_batch_index == usize::MAX ||
352 !self.batches[self.current_batch_index].key.is_compatible_with(&key)
353 {
354 let mut selected_batch_index = None;
355
356 match key.blend_mode {
357 BlendMode::Advanced(_) if self.break_advanced_blend_batches => {
358 }
360 _ => {
361 for (batch_index, batch) in self.batches.iter().enumerate().rev() {
362 if batch.key.is_compatible_with(&key) {
367 selected_batch_index = Some(batch_index);
368 break;
369 }
370
371 if self.batch_rects[batch_index].intersects(z_bounding_rect) {
373 break;
374 }
375 }
376 }
377 }
378
379 if selected_batch_index.is_none() {
380 let prealloc = match key.kind {
388 BatchKind::TextRun(..) => 128,
389 _ => 16,
390 };
391 let mut new_batch = PrimitiveBatch::new(key, self.batches.allocator().clone());
392 new_batch.instances.reserve(prealloc);
393 selected_batch_index = Some(self.batches.len());
394 self.batches.push(new_batch);
395 self.batch_rects.push(BatchRects::new(self.batches.allocator().clone()));
396 }
397
398 self.current_batch_index = selected_batch_index.unwrap();
399 self.batch_rects[self.current_batch_index].add_rect(z_bounding_rect);
400 self.current_z_id = z_id;
401 }
402
403 let batch = &mut self.batches[self.current_batch_index];
404 batch.features |= features;
405 batch.key.textures.merge(&key.textures);
406
407 &mut batch.instances
408 }
409}
410
411pub struct OpaqueBatchList {
412 pub pixel_area_threshold_for_new_batch: f32,
413 pub batches: FrameVec<PrimitiveBatch>,
414 pub current_batch_index: usize,
415 lookback_count: usize,
416}
417
418impl OpaqueBatchList {
419 fn new(pixel_area_threshold_for_new_batch: f32, lookback_count: usize, memory: &FrameMemory) -> Self {
420 OpaqueBatchList {
421 batches: memory.new_vec(),
422 pixel_area_threshold_for_new_batch,
423 current_batch_index: usize::MAX,
424 lookback_count,
425 }
426 }
427
428 fn clear(&mut self) {
432 self.current_batch_index = usize::MAX;
433 self.batches.clear();
434 }
435
436 pub fn set_params_and_get_batch(
437 &mut self,
438 key: BatchKey,
439 features: BatchFeatures,
440 z_bounding_rect: &PictureRect,
444 ) -> &mut FrameVec<PrimitiveInstanceData> {
445 let is_large_occluder = z_bounding_rect.area() > self.pixel_area_threshold_for_new_batch;
450 if is_large_occluder || self.current_batch_index == usize::MAX ||
455 !self.batches[self.current_batch_index].key.is_compatible_with(&key) {
456 let mut selected_batch_index = None;
457 if is_large_occluder {
458 if let Some(batch) = self.batches.last() {
459 if batch.key.is_compatible_with(&key) {
460 selected_batch_index = Some(self.batches.len() - 1);
461 }
462 }
463 } else {
464 for (batch_index, batch) in self.batches.iter().enumerate().rev().take(self.lookback_count) {
466 if batch.key.is_compatible_with(&key) {
467 selected_batch_index = Some(batch_index);
468 break;
469 }
470 }
471 }
472
473 if selected_batch_index.is_none() {
474 let new_batch = PrimitiveBatch::new(key, self.batches.allocator().clone());
475 selected_batch_index = Some(self.batches.len());
476 self.batches.push(new_batch);
477 }
478
479 self.current_batch_index = selected_batch_index.unwrap();
480 }
481
482 let batch = &mut self.batches[self.current_batch_index];
483 batch.features |= features;
484 batch.key.textures.merge(&key.textures);
485
486 &mut batch.instances
487 }
488
489 fn finalize(&mut self) {
490 for batch in &mut self.batches {
497 batch.instances.reverse();
498 }
499 }
500}
501
502#[cfg_attr(feature = "capture", derive(Serialize))]
503#[cfg_attr(feature = "replay", derive(Deserialize))]
504pub struct PrimitiveBatch {
505 pub key: BatchKey,
506 pub instances: FrameVec<PrimitiveInstanceData>,
507 pub features: BatchFeatures,
508}
509
510bitflags! {
511 #[cfg_attr(feature = "capture", derive(Serialize))]
520 #[cfg_attr(feature = "replay", derive(Deserialize))]
521 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
522 pub struct BatchFeatures: u8 {
523 const ALPHA_PASS = 1 << 0;
524 const ANTIALIASING = 1 << 1;
525 const REPETITION = 1 << 2;
526 const CLIP_MASK = 1 << 3;
528 }
529}
530
531impl PrimitiveBatch {
532 fn new(key: BatchKey, allocator: FrameAllocator) -> PrimitiveBatch {
533 PrimitiveBatch {
534 key,
535 instances: FrameVec::new_in(allocator),
536 features: BatchFeatures::empty(),
537 }
538 }
539
540 fn merge(&mut self, other: PrimitiveBatch) {
541 self.instances.extend(other.instances);
542 self.features |= other.features;
543 self.key.textures.merge(&other.key.textures);
544 }
545}
546
547#[cfg_attr(feature = "capture", derive(Serialize))]
548#[cfg_attr(feature = "replay", derive(Deserialize))]
549pub struct AlphaBatchContainer {
550 pub opaque_batches: FrameVec<PrimitiveBatch>,
551 pub alpha_batches: FrameVec<PrimitiveBatch>,
552 pub task_scissor_rect: Option<DeviceIntRect>,
555 pub task_rect: DeviceIntRect,
558}
559
560impl AlphaBatchContainer {
561 pub fn new(
562 task_scissor_rect: Option<DeviceIntRect>,
563 memory: &FrameMemory,
564 ) -> AlphaBatchContainer {
565 AlphaBatchContainer {
566 opaque_batches: memory.new_vec(),
567 alpha_batches: memory.new_vec(),
568 task_scissor_rect,
569 task_rect: DeviceIntRect::zero(),
570 }
571 }
572
573 pub fn is_empty(&self) -> bool {
574 self.opaque_batches.is_empty() &&
575 self.alpha_batches.is_empty()
576 }
577
578 fn merge(&mut self, builder: AlphaBatchBuilder, task_rect: &DeviceIntRect) {
579 self.task_rect = self.task_rect.union(task_rect);
580
581 for other_batch in builder.opaque_batch_list.batches {
582 let batch_index = self.opaque_batches.iter().position(|batch| {
583 batch.key.is_compatible_with(&other_batch.key)
584 });
585
586 match batch_index {
587 Some(batch_index) => {
588 self.opaque_batches[batch_index].merge(other_batch);
589 }
590 None => {
591 self.opaque_batches.push(other_batch);
592 }
593 }
594 }
595
596 let mut min_batch_index = 0;
597
598 for other_batch in builder.alpha_batch_list.batches {
599 let batch_index = self.alpha_batches.iter().skip(min_batch_index).position(|batch| {
600 batch.key.is_compatible_with(&other_batch.key)
601 });
602
603 match batch_index {
604 Some(batch_index) => {
605 let index = batch_index + min_batch_index;
606 self.alpha_batches[index].merge(other_batch);
607 min_batch_index = index;
608 }
609 None => {
610 self.alpha_batches.push(other_batch);
611 min_batch_index = self.alpha_batches.len();
612 }
613 }
614 }
615 }
616}
617
618#[derive(Debug, Copy, Clone)]
621struct SegmentInstanceData {
622 textures: TextureSet,
623 specific_resource_address: i32,
624}
625
626pub struct AlphaBatchBuilder {
628 pub alpha_batch_list: AlphaBatchList,
629 pub opaque_batch_list: OpaqueBatchList,
630 pub render_task_id: RenderTaskId,
631 render_task_address: RenderTaskAddress,
632}
633
634impl AlphaBatchBuilder {
635 pub fn new(
636 screen_size: DeviceIntSize,
637 break_advanced_blend_batches: bool,
638 lookback_count: usize,
639 render_task_id: RenderTaskId,
640 render_task_address: RenderTaskAddress,
641 memory: &FrameMemory,
642 ) -> Self {
643 let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0;
646
647 AlphaBatchBuilder {
648 alpha_batch_list: AlphaBatchList::new(break_advanced_blend_batches, 128, memory),
649 opaque_batch_list: OpaqueBatchList::new(batch_area_threshold, lookback_count, memory),
650 render_task_id,
651 render_task_address,
652 }
653 }
654
655 fn clear(&mut self) {
659 self.alpha_batch_list.clear();
660 self.opaque_batch_list.clear();
661 }
662
663 pub fn build(
664 mut self,
665 batch_containers: &mut FrameVec<AlphaBatchContainer>,
666 merged_batches: &mut AlphaBatchContainer,
667 task_rect: DeviceIntRect,
668 task_scissor_rect: Option<DeviceIntRect>,
669 ) {
670 self.opaque_batch_list.finalize();
671
672 if task_scissor_rect.is_none() {
673 merged_batches.merge(self, &task_rect);
674 } else {
675 batch_containers.push(AlphaBatchContainer {
676 alpha_batches: self.alpha_batch_list.batches,
677 opaque_batches: self.opaque_batch_list.batches,
678 task_scissor_rect,
679 task_rect,
680 });
681 }
682 }
683
684 pub fn push_single_instance(
685 &mut self,
686 key: BatchKey,
687 features: BatchFeatures,
688 bounding_rect: &PictureRect,
689 z_id: ZBufferId,
690 instance: PrimitiveInstanceData,
691 ) {
692 self.set_params_and_get_batch(key, features, bounding_rect, z_id)
693 .push(instance);
694 }
695
696 pub fn set_params_and_get_batch(
697 &mut self,
698 key: BatchKey,
699 features: BatchFeatures,
700 bounding_rect: &PictureRect,
701 z_id: ZBufferId,
702 ) -> &mut FrameVec<PrimitiveInstanceData> {
703 match key.blend_mode {
704 BlendMode::None => {
705 self.opaque_batch_list
706 .set_params_and_get_batch(key, features, bounding_rect)
707 }
708 BlendMode::Alpha |
709 BlendMode::PremultipliedAlpha |
710 BlendMode::PremultipliedDestOut |
711 BlendMode::SubpixelDualSource |
712 BlendMode::Advanced(_) |
713 BlendMode::MultiplyDualSource |
714 BlendMode::Screen |
715 BlendMode::Exclusion |
716 BlendMode::PlusLighter => {
717 self.alpha_batch_list
718 .set_params_and_get_batch(key, features, bounding_rect, z_id)
719 }
720 }
721 }
722}
723
724pub struct BatchBuilder {
728 glyph_fetch_buffer: Vec<GlyphFetchResult>,
731
732 batcher: AlphaBatchBuilder,
733}
734
735impl BatchBuilder {
736 pub fn new(batcher: AlphaBatchBuilder) -> Self {
737 BatchBuilder {
738 glyph_fetch_buffer: Vec::new(),
739 batcher,
740 }
741 }
742
743 pub fn finalize(self) -> AlphaBatchBuilder {
744 self.batcher
745 }
746
747 fn add_brush_instance_to_batches(
748 &mut self,
749 batch_key: BatchKey,
750 features: BatchFeatures,
751 bounding_rect: &PictureRect,
752 z_id: ZBufferId,
753 segment_index: i32,
754 edge_flags: EdgeMask,
755 clip_task_address: RenderTaskAddress,
756 brush_flags: BrushFlags,
757 prim_header_index: PrimitiveHeaderIndex,
758 resource_address: i32,
759 ) {
760 assert!(
761 !(brush_flags.contains(BrushFlags::NORMALIZED_UVS)
762 && features.contains(BatchFeatures::REPETITION)),
763 "Normalized UVs are not supported with repetition."
764 );
765 let instance = BrushInstance {
766 segment_index,
767 edge_flags,
768 clip_task_address,
769 brush_flags,
770 prim_header_index,
771 resource_address,
772 };
773
774 self.batcher.push_single_instance(
775 batch_key,
776 features,
777 bounding_rect,
778 z_id,
779 PrimitiveInstanceData::from(instance),
780 );
781 }
782
783 fn add_split_composite_instance_to_batches(
784 &mut self,
785 batch_key: BatchKey,
786 features: BatchFeatures,
787 bounding_rect: &PictureRect,
788 z_id: ZBufferId,
789 prim_header_index: PrimitiveHeaderIndex,
790 polygons_address: i32,
791 ) {
792 let render_task_address = self.batcher.render_task_address;
793
794 self.batcher.push_single_instance(
795 batch_key,
796 features,
797 bounding_rect,
798 z_id,
799 PrimitiveInstanceData::from(SplitCompositeInstance {
800 prim_header_index,
801 render_task_address,
802 polygons_address,
803 z: z_id,
804 }),
805 );
806 }
807
808 fn clear_batches(&mut self) {
811 self.batcher.clear();
812 }
813
814 pub fn add_prim_to_batch(
819 &mut self,
820 cmd: &PrimitiveCommand,
821 prim_spatial_node_index: SpatialNodeIndex,
822 ctx: &RenderTargetContext,
823 render_tasks: &RenderTaskGraph,
824 prim_headers: &mut PrimitiveHeaders,
825 transforms: &mut TransformPalette,
826 root_spatial_node_index: SpatialNodeIndex,
827 surface_spatial_node_index: SpatialNodeIndex,
828 z_generator: &mut ZBufferIdGenerator,
829 prim_instances: &[PrimitiveInstance],
830 gpu_buffer_builder: &mut GpuBufferBuilder,
831 segments: &[RenderTaskId],
832 ) {
833 let (draw_index, extra_prim_gpu_address) = match cmd {
834 PrimitiveCommand::Simple { draw_index } => {
835 (draw_index, None)
836 }
837 PrimitiveCommand::Complex { draw_index, gpu_address } => {
838 (draw_index, Some(gpu_address.as_int()))
839 }
840 PrimitiveCommand::Instance { draw_index, gpu_buffer_address } => {
841 (draw_index, Some(gpu_buffer_address.as_int()))
842 }
843 PrimitiveCommand::Quad { pattern, pattern_input, draw_index, gpu_buffer_address, quad_flags, edge_flags, transform_id, src_color_task_id, blend_mode } => {
844 let prim_info = &ctx.scratch.frame.draws[draw_index.0 as usize];
845 let bounding_rect = &prim_info.clip_chain.pic_coverage_rect;
846 let render_task_address = self.batcher.render_task_address;
847
848 if segments.is_empty() {
849 let z_id = z_generator.next();
850
851 quad::add_to_batch(
852 *pattern,
853 *pattern_input,
854 render_task_address,
855 *transform_id,
856 *gpu_buffer_address,
857 *quad_flags,
858 *edge_flags,
859 INVALID_SEGMENT_INDEX as u8,
860 *src_color_task_id,
861 z_id,
862 *blend_mode,
863 render_tasks,
864 gpu_buffer_builder,
865 |key, instance| {
866 let batch = self.batcher.set_params_and_get_batch(
867 key,
868 BatchFeatures::empty(),
869 bounding_rect,
870 z_id,
871 );
872 batch.push(instance);
873 },
874 );
875 } else {
876 for (i, task_id) in segments.iter().enumerate() {
877 debug_assert!(edge_flags.is_empty());
879
880 let z_id = z_generator.next();
881
882 quad::add_to_batch(
883 *pattern,
884 *pattern_input,
885 render_task_address,
886 *transform_id,
887 *gpu_buffer_address,
888 *quad_flags,
889 *edge_flags,
890 i as u8,
891 *task_id,
892 z_id,
893 *blend_mode,
894 render_tasks,
895 gpu_buffer_builder,
896 |key, instance| {
897 let batch = self.batcher.set_params_and_get_batch(
898 key,
899 BatchFeatures::empty(),
900 bounding_rect,
901 z_id,
902 );
903 batch.push(instance);
904 },
905 );
906 }
907 }
908
909 return;
910 }
911 };
912
913 let prim_instance = &prim_instances[draw_index.0 as usize];
914 let is_anti_aliased = ctx.data_stores.prim_has_anti_aliasing(prim_instance);
915
916 let brush_flags = if is_anti_aliased {
917 BrushFlags::FORCE_AA
918 } else {
919 BrushFlags::empty()
920 };
921
922 let vis_flags = match ctx.scratch.frame.draws[draw_index.0 as usize].state {
923 DrawState::Culled => {
924 return;
925 }
926 DrawState::PassThrough |
927 DrawState::Unset => {
928 panic!("bug: invalid visibility state");
929 }
930 DrawState::Visible { vis_flags, .. } => {
931 vis_flags
932 }
933 };
934
935 if vis_flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) {
941 self.clear_batches();
942 return;
943 }
944
945 let transform_id = transforms.gpu.get_id(
946 prim_spatial_node_index,
947 root_spatial_node_index,
948 ctx.spatial_tree,
949 );
950
951 let transform_metadata = transform_id.metadata();
955 let prim_info = &ctx.scratch.frame.draws[draw_index.0 as usize];
956 let bounding_rect = &prim_info.clip_chain.pic_coverage_rect;
957
958 let mut z_id = z_generator.next();
959
960 let prim_rect = ctx.data_stores.get_local_prim_rect(
961 prim_instance,
962 prim_info.snapped_local_rect,
963 &ctx.prim_store.pictures,
964 ctx.surfaces,
965 );
966
967 let mut batch_features = BatchFeatures::empty();
968 let may_need_repetition = match prim_instance.kind {
969 PrimitiveKind::Image { .. } => {
970 let idx = prim_info.kind_scratch.unwrap_image();
971 ctx.scratch.frame.images[idx].may_need_repetition
972 }
973 PrimitiveKind::NormalBorder { .. } => {
974 let idx = prim_info.kind_scratch.unwrap_normal_border();
975 ctx.scratch.frame.normal_border[idx].may_need_repetition
976 }
977 PrimitiveKind::ImageBorder { .. } => true,
981 PrimitiveKind::LineDecoration { .. } => true,
987 _ => false,
991 };
992 if may_need_repetition {
993 batch_features |= BatchFeatures::REPETITION;
994 }
995
996 if !transform_id.is_2d_axis_aligned() || is_anti_aliased {
997 batch_features |= BatchFeatures::ANTIALIASING;
998 }
999
1000 if prim_info.clip_task_index != ClipTaskIndex::INVALID {
1002 batch_features |= BatchFeatures::CLIP_MASK;
1003 }
1004
1005 if !bounding_rect.is_empty() {
1006 debug_assert_eq!(prim_info.clip_chain.pic_spatial_node_index, surface_spatial_node_index,
1007 "The primitive's bounding box is specified in a different coordinate system from the current batch!");
1008 }
1009
1010 if let PrimitiveKind::Picture { pic_index, .. } = prim_instance.kind {
1011 let pic_scratch_handle = ctx.scratch.frame.draws[draw_index.0 as usize].kind_scratch.unwrap_picture();
1012 let picture = &ctx.prim_store.pictures[pic_index.0];
1013 let picture_scratch = &ctx.scratch.frame.pictures[pic_scratch_handle];
1014 if let Some(snapshot) = picture.snapshot {
1015 if snapshot.detached {
1016 return;
1017 }
1018 }
1019
1020 let blend_mode = BlendMode::PremultipliedAlpha;
1021 let prim_cache_address = ctx.globals.default_image_data;
1022
1023 match picture.raster_config {
1024 Some(ref raster_config) => {
1025 let brush_flags = brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION;
1028
1029 let surface = &ctx.surfaces[raster_config.surface_index.0];
1030 let mut local_clip_rect = prim_info.clip_chain.local_clip_rect;
1031
1032 let transform_id = if surface.surface_spatial_node_index == surface.raster_spatial_node_index {
1037 transform_id
1038 } else {
1039 let map_local_to_raster = SpaceMapper::new_with_target(
1040 root_spatial_node_index,
1041 surface.surface_spatial_node_index,
1042 LayoutRect::max_rect(),
1043 ctx.spatial_tree,
1044 );
1045
1046 let raster_rect = map_local_to_raster
1047 .map(&prim_rect)
1048 .unwrap();
1049
1050 let sx = (raster_rect.max.x - raster_rect.min.x) / (prim_rect.max.x - prim_rect.min.x);
1051 let sy = (raster_rect.max.y - raster_rect.min.y) / (prim_rect.max.y - prim_rect.min.y);
1052
1053 let tx = raster_rect.min.x - sx * prim_rect.min.x;
1054 let ty = raster_rect.min.y - sy * prim_rect.min.y;
1055
1056 let transform = ScaleOffset::new(sx, sy, tx, ty);
1057
1058 let raster_clip_rect = map_local_to_raster
1059 .map(&prim_info.clip_chain.local_clip_rect)
1060 .unwrap();
1061 local_clip_rect = transform.unmap_rect(&raster_clip_rect);
1062
1063 transforms.gpu.get_custom(transform.to_transform())
1064 };
1065
1066 let picture_prim_header = PrimitiveHeader {
1067 local_rect: prim_rect,
1068 local_clip_rect,
1069 specific_prim_address: prim_cache_address.as_int(),
1070 transform_id,
1071 z: z_id,
1072 render_task_address: self.batcher.render_task_address,
1073 user_data: [0; 4], };
1075
1076 let mut is_opaque = prim_info.clip_task_index == ClipTaskIndex::INVALID
1077 && surface.is_opaque
1078 && transform_id.is_2d_axis_aligned()
1079 && !is_anti_aliased
1080 && !prim_info.clip_chain.needs_mask;
1081
1082 match raster_config.composite_mode {
1083 PictureCompositeMode::TileCache { .. } => {
1084 return;
1089 }
1090 PictureCompositeMode::IntermediateSurface { .. } => {
1091 return;
1095 }
1096 _=>{}
1097 }
1098
1099 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1100 prim_info.clip_task_index,
1101 render_tasks,
1102 ).unwrap();
1103
1104 let pic_task_id = picture_scratch.primary_render_task_id.unwrap();
1105
1106 let (uv_rect_address, texture) = render_tasks.resolve_location(
1107 pic_task_id,
1108
1109 ).unwrap();
1110
1111 let textures = BatchTextures::prim_textured(
1114 texture,
1115 clip_mask_texture_id,
1116 );
1117
1118 let (key, prim_user_data, resource_address) = match raster_config.composite_mode {
1119 PictureCompositeMode::TileCache { .. }
1120 | PictureCompositeMode::IntermediateSurface { .. }
1121 => return,
1122 PictureCompositeMode::Filter(ref filter) => {
1123 assert!(filter.is_visible());
1124 match filter {
1125 Filter::Blur { .. } => {
1126 let kind = BatchKind::Brush(
1127 BrushBatchKind::Image(ImageBufferKind::Texture2D)
1128 );
1129
1130 let key = BatchKey::new(
1131 kind,
1132 blend_mode,
1133 textures,
1134 );
1135
1136 let prim_user_data = ImageBrushUserData {
1137 color_mode: ShaderColorMode::Image,
1138 alpha_type: AlphaType::PremultipliedAlpha,
1139 raster_space: RasterizationSpace::Screen,
1140 opacity: 1.0,
1141 }.encode();
1142
1143 (key, prim_user_data, uv_rect_address.as_int())
1144 }
1145 Filter::DropShadows(shadows) => {
1146 let kind = BatchKind::Brush(
1150 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1151 );
1152
1153 let secondary_id = picture_scratch.secondary_render_task_id.expect("no secondary!?");
1156 let content_source = {
1157 let secondary_task = &render_tasks[secondary_id];
1158 let texture_id = secondary_task.get_target_texture();
1159 TextureSource::TextureCache(
1160 texture_id,
1161 Swizzle::default(),
1162 )
1163 };
1164
1165 let shadow_uv_rect_address = uv_rect_address;
1167 let shadow_textures = textures;
1168
1169 let content_uv_rect_address = render_tasks[secondary_id]
1170 .get_texture_address()
1171 .as_int();
1172
1173 let content_textures = BatchTextures::prim_textured(
1175 content_source,
1176 clip_mask_texture_id,
1177 );
1178
1179 let shadow_key = BatchKey::new(kind, blend_mode, shadow_textures);
1181 let content_key = BatchKey::new(kind, blend_mode, content_textures);
1182
1183 for (shadow, shadow_prim_address) in shadows.iter().zip(picture_scratch.extra_gpu_data.iter()) {
1184 let shadow_rect = picture_prim_header.local_rect.translate(shadow.offset);
1185
1186 let shadow_prim_header = PrimitiveHeader {
1187 local_rect: shadow_rect,
1188 specific_prim_address: shadow_prim_address.as_int(),
1189 z: z_id,
1190 user_data: ImageBrushUserData {
1191 color_mode: ShaderColorMode::Alpha,
1192 alpha_type: AlphaType::PremultipliedAlpha,
1193 raster_space: RasterizationSpace::Screen,
1194 opacity: 1.0,
1195 }.encode(),
1196 ..picture_prim_header
1197 };
1198 let shadow_prim_header_index = prim_headers.push(&shadow_prim_header);
1199
1200 self.add_brush_instance_to_batches(
1201 shadow_key,
1202 batch_features,
1203 bounding_rect,
1204 z_id,
1205 INVALID_SEGMENT_INDEX,
1206 EdgeMask::all(),
1207 clip_task_address,
1208 brush_flags,
1209 shadow_prim_header_index,
1210 shadow_uv_rect_address.as_int(),
1211 );
1212 }
1213
1214 z_id = z_generator.next();
1216
1217 let prim_user_data = ImageBrushUserData {
1218 color_mode: ShaderColorMode::Image,
1219 alpha_type: AlphaType::PremultipliedAlpha,
1220 raster_space: RasterizationSpace::Screen,
1221 opacity: 1.0,
1222 }.encode();
1223
1224 (content_key, prim_user_data, content_uv_rect_address)
1225 }
1226 Filter::Opacity(_, amount) => {
1227 let amount = (amount * 65536.0) as i32;
1228
1229 let key = BatchKey::new(
1230 BatchKind::Brush(BrushBatchKind::Opacity),
1231 BlendMode::PremultipliedAlpha,
1232 textures,
1233 );
1234
1235 let prim_user_data = [
1236 uv_rect_address.as_int(),
1237 amount,
1238 0,
1239 0,
1240 ];
1241
1242 (key, prim_user_data, 0)
1243 }
1244 _ => {
1245 let filter_mode = filter.as_int();
1247
1248 let user_data = match filter {
1249 Filter::Identity => 0x10000i32, Filter::Contrast(amount) |
1251 Filter::Grayscale(amount) |
1252 Filter::Invert(amount) |
1253 Filter::Saturate(amount) |
1254 Filter::Sepia(amount) |
1255 Filter::Brightness(amount) => {
1256 (amount * 65536.0) as i32
1257 }
1258 Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
1259 Filter::HueRotate(angle) => {
1260 (0.01745329251 * angle * 65536.0) as i32
1261 }
1262 Filter::ColorMatrix(_) => {
1263 picture_scratch.extra_gpu_data[0].as_int()
1264 }
1265 Filter::Flood(_) => {
1266 picture_scratch.extra_gpu_data[0].as_int()
1267 }
1268
1269 Filter::ComponentTransfer |
1271 Filter::Blur { .. } |
1272 Filter::DropShadows(..) |
1273 Filter::Opacity(..) |
1274 Filter::SVGGraphNode(..) => unreachable!(),
1275 };
1276
1277 if let Filter::ColorMatrix(..) = filter {
1280 is_opaque = false;
1281 }
1282
1283 let blend_mode = if is_opaque {
1284 BlendMode::None
1285 } else {
1286 BlendMode::PremultipliedAlpha
1287 };
1288
1289 let key = BatchKey::new(
1290 BatchKind::Brush(BrushBatchKind::Blend),
1291 blend_mode,
1292 textures,
1293 );
1294
1295 let prim_user_data = [
1296 uv_rect_address.as_int(),
1297 filter_mode,
1298 user_data,
1299 0,
1300 ];
1301
1302 (key, prim_user_data, 0)
1303 }
1304 }
1305 }
1306 PictureCompositeMode::ComponentTransferFilter(handle) => {
1307 let filter_data = &ctx.data_stores.filter_data[handle];
1311 let filter_mode : i32 = Filter::ComponentTransfer.as_int() |
1312 ((filter_data.data.r_func.to_int() << 28 |
1313 filter_data.data.g_func.to_int() << 24 |
1314 filter_data.data.b_func.to_int() << 20 |
1315 filter_data.data.a_func.to_int() << 16) as i32);
1316
1317 let user_data = filter_data.gpu_buffer_address.as_int();
1318
1319 let key = BatchKey::new(
1320 BatchKind::Brush(BrushBatchKind::Blend),
1321 BlendMode::PremultipliedAlpha,
1322 textures,
1323 );
1324
1325 let prim_user_data = [
1326 uv_rect_address.as_int(),
1327 filter_mode,
1328 user_data,
1329 0,
1330 ];
1331
1332 (key, prim_user_data, 0)
1333 }
1334 PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
1335 mode,
1336 ctx.use_advanced_blending,
1337 !ctx.break_advanced_blend_batches,
1338 ctx.use_dual_source_blending,
1339 ).is_some() => {
1340 let key = BatchKey::new(
1341 BatchKind::Brush(
1342 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1343 ),
1344 BlendMode::from_mix_blend_mode(
1345 mode,
1346 ctx.use_advanced_blending,
1347 !ctx.break_advanced_blend_batches,
1348 ctx.use_dual_source_blending,
1349 ).unwrap(),
1350 textures,
1351 );
1352
1353 let prim_user_data = ImageBrushUserData {
1354 color_mode: match key.blend_mode {
1355 BlendMode::MultiplyDualSource => ShaderColorMode::MultiplyDualSource,
1356 _ => ShaderColorMode::Image,
1357 },
1358 alpha_type: AlphaType::PremultipliedAlpha,
1359 raster_space: RasterizationSpace::Screen,
1360 opacity: 1.0,
1361 }.encode();
1362
1363 (key, prim_user_data, uv_rect_address.as_int())
1364 }
1365 PictureCompositeMode::MixBlend(mode) => {
1366 let backdrop_id = picture_scratch.secondary_render_task_id.expect("no backdrop!?");
1367
1368 let color0 = render_tasks[backdrop_id].get_target_texture();
1369 let color1 = render_tasks[pic_task_id].get_target_texture();
1370
1371 let batch_key = BatchKey::new(
1380 BatchKind::Brush(
1381 BrushBatchKind::MixBlend {
1382 task_id: self.batcher.render_task_id,
1383 backdrop_id,
1384 },
1385 ),
1386 BlendMode::PremultipliedAlpha,
1387 BatchTextures {
1388 input: TextureSet {
1389 colors: [
1390 TextureSource::TextureCache(
1391 color0,
1392 Swizzle::default(),
1393 ),
1394 TextureSource::TextureCache(
1395 color1,
1396 Swizzle::default(),
1397 ),
1398 TextureSource::Invalid,
1399 ],
1400 },
1401 clip_mask: clip_mask_texture_id,
1402 },
1403 );
1404 let src_uv_address = render_tasks[pic_task_id].get_texture_address();
1405 let readback_uv_address = render_tasks[backdrop_id].get_texture_address();
1406 let prim_header = PrimitiveHeader {
1407 user_data: [
1408 mode as u32 as i32,
1409 readback_uv_address.as_int(),
1410 src_uv_address.as_int(),
1411 0,
1412 ],
1413 ..picture_prim_header
1414 };
1415 let prim_header_index = prim_headers.push(&prim_header);
1416
1417 let instance = BrushInstance {
1418 segment_index: INVALID_SEGMENT_INDEX,
1419 edge_flags: EdgeMask::all(),
1420 clip_task_address,
1421 brush_flags,
1422 prim_header_index,
1423 resource_address: 0,
1424 };
1425
1426 self.batcher.push_single_instance(
1427 batch_key,
1428 batch_features,
1429 bounding_rect,
1430 z_id,
1431 PrimitiveInstanceData::from(instance),
1432 );
1433
1434 return;
1435 }
1436 PictureCompositeMode::Blit(_) => {
1437 match picture.context_3d {
1438 Picture3DContext::In { root_data: Some(_), .. } => {
1439 unreachable!("bug: should not have a raster_config");
1440 }
1441 Picture3DContext::In { root_data: None, .. } => {
1442 let extra_prim_gpu_address = match extra_prim_gpu_address {
1449 Some(prim_address) => prim_address,
1450 None => return,
1451 };
1452
1453 let z_id = z_generator.next();
1456
1457 let prim_header = PrimitiveHeader {
1458 z: z_id,
1459 transform_id: transforms.gpu.get_id(
1460 prim_spatial_node_index,
1461 root_spatial_node_index,
1462 ctx.spatial_tree,
1463 ),
1464 user_data: [
1465 uv_rect_address.as_int(),
1466 BrushFlags::PERSPECTIVE_INTERPOLATION.bits() as i32,
1467 0,
1468 clip_task_address.0 as i32,
1469 ],
1470 ..picture_prim_header
1471 };
1472 let prim_header_index = prim_headers.push(&prim_header);
1473
1474 let key = BatchKey::new(
1475 BatchKind::SplitComposite,
1476 BlendMode::PremultipliedAlpha,
1477 textures,
1478 );
1479
1480 self.add_split_composite_instance_to_batches(
1481 key,
1482 BatchFeatures::CLIP_MASK,
1483 &prim_info.clip_chain.pic_coverage_rect,
1484 z_id,
1485 prim_header_index,
1486 extra_prim_gpu_address,
1487 );
1488
1489 return;
1490 }
1491 Picture3DContext::Out { .. } => {
1492 let textures = TextureSet {
1493 colors: [
1494 texture,
1495 TextureSource::Invalid,
1496 TextureSource::Invalid,
1497 ],
1498 };
1499 let batch_params = BrushBatchParameters::shared(
1500 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1501 textures,
1502 ImageBrushUserData {
1503 color_mode: ShaderColorMode::Image,
1504 alpha_type: AlphaType::PremultipliedAlpha,
1505 raster_space: RasterizationSpace::Screen,
1506 opacity: 1.0,
1507 }.encode(),
1508 uv_rect_address.as_int(),
1509 );
1510
1511 let prim_header = PrimitiveHeader {
1512 specific_prim_address: prim_cache_address.as_int(),
1513 user_data: batch_params.prim_user_data,
1514 ..picture_prim_header
1515 };
1516 let prim_header_index = prim_headers.push(&prim_header);
1517
1518 let (opacity, blend_mode) = if is_opaque {
1519 (PrimitiveOpacity::opaque(), BlendMode::None)
1520 } else {
1521 (PrimitiveOpacity::translucent(), BlendMode::PremultipliedAlpha)
1522 };
1523
1524 self.add_segmented_prim_to_batch(
1525 None,
1526 opacity,
1527 &batch_params,
1528 blend_mode,
1529 batch_features,
1530 brush_flags,
1531 EdgeMask::all(),
1532 prim_header_index,
1533 bounding_rect,
1534 transform_metadata,
1535 z_id,
1536 prim_info.clip_task_index,
1537 ctx,
1538 render_tasks,
1539 );
1540
1541 return;
1542 }
1543 }
1544 }
1545 PictureCompositeMode::SVGFEGraph(..) => {
1546 let kind = BatchKind::Brush(
1547 BrushBatchKind::Image(ImageBufferKind::Texture2D)
1548 );
1549 let key = BatchKey::new(
1550 kind,
1551 blend_mode,
1552 textures,
1553 );
1554
1555 let prim_user_data = ImageBrushUserData {
1556 color_mode: ShaderColorMode::Image,
1557 alpha_type: AlphaType::PremultipliedAlpha,
1558 raster_space: RasterizationSpace::Screen,
1559 opacity: 1.0,
1560 }.encode();
1561
1562 (key, prim_user_data, uv_rect_address.as_int())
1563 }
1564 };
1565
1566 let prim_header = PrimitiveHeader {
1567 z: z_id,
1568 user_data: prim_user_data,
1569 ..picture_prim_header
1570 };
1571 let prim_header_index = prim_headers.push(&prim_header);
1572
1573 self.add_brush_instance_to_batches(
1574 key,
1575 batch_features,
1576 bounding_rect,
1577 z_id,
1578 INVALID_SEGMENT_INDEX,
1579 EdgeMask::all(),
1580 clip_task_address,
1581 brush_flags,
1582 prim_header_index,
1583 resource_address,
1584 );
1585 }
1586 None => {
1587 unreachable!();
1588 }
1589 }
1590
1591 return;
1592 }
1593
1594 let base_prim_header = PrimitiveHeader {
1595 local_rect: prim_rect,
1596 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1597 transform_id,
1598 z: z_id,
1599 render_task_address: self.batcher.render_task_address,
1600 specific_prim_address: GpuBufferAddress::INVALID.as_int(), user_data: [0; 4], };
1603
1604 let common_data = ctx.data_stores.as_common_data(prim_instance);
1605
1606 let needs_blending = !common_data.opacity.is_opaque ||
1607 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1608 !transform_metadata.is_2d_axis_aligned ||
1609 is_anti_aliased;
1610
1611 let blend_mode = if needs_blending {
1612 BlendMode::PremultipliedAlpha
1613 } else {
1614 BlendMode::None
1615 };
1616
1617 let segment_instance_index = match prim_instance.kind {
1618 PrimitiveKind::Rectangle { .. }
1619 | PrimitiveKind::YuvImage { .. } => prim_info.segment_instance_index,
1620 _ => SegmentInstanceIndex::UNUSED,
1621 };
1622
1623 let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
1624 (common_data.gpu_buffer_address, None)
1625 } else {
1626 let segment_instance = &ctx.scratch.frame.segment_instances[segment_instance_index];
1627 let segments = Some(&ctx.scratch.frame.segments[segment_instance.segments_range]);
1628 (segment_instance.gpu_data, segments)
1629 };
1630
1631 let mut prim_cache_address = prim_cache_address;
1636 let img_brush_data = match prim_instance.kind {
1637 PrimitiveKind::RadialGradient { .. } => {
1638 unreachable!("BUG: radial gradients should always use quad path");
1639 }
1640 PrimitiveKind::ConicGradient { .. } => {
1641 unreachable!("BUG: conic gradients should always use quad path");
1642 }
1643 PrimitiveKind::ImageBorder { data_handle, .. } => {
1644 let prim_data = &ctx.data_stores.image_border[data_handle];
1645 let ib_handle = prim_info.kind_scratch.unwrap_image_border();
1646 let ib_scratch = ctx.scratch.frame.image_border[ib_handle];
1647 prim_cache_address = ib_scratch.gpu_address;
1648 let brush_segments = &ctx.scratch.frame.segments[ib_scratch.brush_segments_range];
1649 Some((prim_data.kind.src_color, brush_segments))
1650 }
1651 _ => None,
1652 };
1653
1654 if let Some((src_color, brush_segments)) = img_brush_data {
1655 let src_color = render_tasks.resolve_location(src_color);
1656
1657 let (uv_rect_address, texture_source) = match src_color {
1658 Some(src) => src,
1659 None => {
1660 return;
1661 }
1662 };
1663
1664 let textures = TextureSet::prim_textured(texture_source);
1665
1666 let prim_user_data = ImageBrushUserData {
1667 color_mode: ShaderColorMode::Image,
1668 alpha_type: AlphaType::PremultipliedAlpha,
1669 raster_space: RasterizationSpace::Local,
1670 opacity: 1.0,
1671 }.encode();
1672
1673 let prim_header = PrimitiveHeader {
1674 specific_prim_address: prim_cache_address.as_int(),
1675 user_data: prim_user_data,
1676 ..base_prim_header
1677 };
1678
1679 let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
1680
1681 let batch_params = BrushBatchParameters::shared(
1682 batch_kind,
1683 textures,
1684 prim_user_data,
1685 uv_rect_address.as_int(),
1686 );
1687
1688 let segments = if brush_segments.is_empty() {
1689 None
1690 } else {
1691 Some(&brush_segments[..])
1692 };
1693
1694 let prim_header_index = prim_headers.push(&prim_header);
1695
1696 self.add_segmented_prim_to_batch(
1697 segments,
1698 common_data.opacity,
1699 &batch_params,
1700 blend_mode,
1701 batch_features,
1702 brush_flags,
1703 common_data.transformed_aa_edges,
1704 prim_header_index,
1705 bounding_rect,
1706 transform_metadata,
1707 z_id,
1708 prim_info.clip_task_index,
1709 ctx,
1710 render_tasks,
1711 );
1712
1713 return;
1714 }
1715
1716 match prim_instance.kind {
1717 PrimitiveKind::Picture { .. } => {}
1719 PrimitiveKind::RadialGradient { .. } => { }
1720 PrimitiveKind::ConicGradient { .. } => { }
1721 PrimitiveKind::ImageBorder { .. } => {}
1722 PrimitiveKind::BoxShadow { .. } => {
1723 unreachable!("BUG: Should not hit box-shadow here as they are handled by quad infra");
1724 }
1725 PrimitiveKind::NormalBorder { .. } => {
1726 let scratch_handle = prim_info.kind_scratch.unwrap_normal_border();
1727 let nb_scratch = ctx.scratch.frame.normal_border[scratch_handle];
1728 let prim_cache_address = nb_scratch.gpu_address;
1729 let task_ids = &ctx.scratch.frame.border_task_ids[nb_scratch.task_ids];
1730 let mut segment_data: SmallVec<[SegmentInstanceData; 8]> = SmallVec::new();
1731
1732 for task_id in task_ids {
1736 if let Some((uv_rect_address, texture)) = render_tasks.resolve_location(*task_id) {
1737 segment_data.push(
1738 SegmentInstanceData {
1739 textures: TextureSet::prim_textured(texture),
1740 specific_resource_address: uv_rect_address.as_int(),
1741 }
1742 );
1743 }
1744 }
1745
1746 let image_buffer_kind = ImageBufferKind::Texture2D;
1748
1749 let batch_params = BrushBatchParameters::instanced(
1750 BrushBatchKind::Image(image_buffer_kind),
1751 ImageBrushUserData {
1752 color_mode: ShaderColorMode::Image,
1753 alpha_type: AlphaType::PremultipliedAlpha,
1754 raster_space: RasterizationSpace::Local,
1755 opacity: 1.0,
1756 }.encode(),
1757 segment_data,
1758 );
1759
1760 let prim_header = PrimitiveHeader {
1761 specific_prim_address: prim_cache_address.as_int(),
1762 user_data: batch_params.prim_user_data,
1763 ..base_prim_header
1764 };
1765 let prim_header_index = prim_headers.push(&prim_header);
1766
1767 let brush_segments = &ctx.scratch.frame.segments[nb_scratch.brush_segments_range];
1768 self.add_segmented_prim_to_batch(
1769 Some(brush_segments),
1770 common_data.opacity,
1771 &batch_params,
1772 blend_mode,
1773 batch_features,
1774 brush_flags,
1775 common_data.transformed_aa_edges,
1776 prim_header_index,
1777 bounding_rect,
1778 transform_metadata,
1779 z_id,
1780 prim_info.clip_task_index,
1781 ctx,
1782 render_tasks,
1783 );
1784 }
1785 PrimitiveKind::TextRun { data_handle, .. } => {
1786 let text_run_scratch_handle = prim_info.kind_scratch.unwrap_text_run();
1787 let run_scratch = &ctx.scratch.frame.text_runs[text_run_scratch_handle];
1788 let subpx_dir = run_scratch.used_font.get_subpx_dir();
1789 let prim_data = &ctx.data_stores.text_run[data_handle];
1790
1791 let glyph_keys = &ctx.scratch.frame.glyph_keys[run_scratch.glyph_keys_range];
1792
1793 let prim_header = PrimitiveHeader {
1800 local_rect: run_scratch.local_rect,
1801 specific_prim_address: run_scratch.gpu_address.as_int(),
1802 user_data: [
1803 (run_scratch.raster_scale * 65535.0).round() as i32,
1804 run_scratch.local_raster as i32,
1805 0,
1806 0,
1807 ],
1808 ..base_prim_header
1809 };
1810 let prim_header_index = prim_headers.push(&prim_header);
1811 let base_instance = GlyphInstance::new(
1812 prim_header_index,
1813 );
1814 let batcher = &mut self.batcher;
1815
1816 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1817 prim_info.clip_task_index,
1818 render_tasks,
1819 ).unwrap();
1820
1821 let font = run_scratch.used_font.clone();
1825 ctx.resource_cache.fetch_glyphs(
1826 font,
1827 &glyph_keys,
1828 &gpu_buffer_builder.f32,
1829 &mut self.glyph_fetch_buffer,
1830 |texture_id, glyph_format, glyphs| {
1831 debug_assert_ne!(texture_id, TextureSource::Invalid);
1832
1833 let subpx_dir = subpx_dir.limit_by(glyph_format);
1834
1835 let textures = BatchTextures::prim_textured(
1836 texture_id,
1837 clip_mask_texture_id,
1838 );
1839
1840 let kind = BatchKind::TextRun(glyph_format);
1841
1842 let (blend_mode, color_mode) = match glyph_format {
1843 GlyphFormat::Subpixel |
1844 GlyphFormat::TransformedSubpixel => {
1845 debug_assert!(ctx.use_dual_source_blending);
1846 (
1847 BlendMode::SubpixelDualSource,
1848 ShaderColorMode::SubpixelDualSource,
1849 )
1850 }
1851 GlyphFormat::Alpha |
1852 GlyphFormat::TransformedAlpha |
1853 GlyphFormat::Bitmap => {
1854 (
1855 BlendMode::PremultipliedAlpha,
1856 ShaderColorMode::Alpha,
1857 )
1858 }
1859 GlyphFormat::ColorBitmap => {
1860 (
1861 BlendMode::PremultipliedAlpha,
1862 if prim_data.shadow {
1863 ShaderColorMode::BitmapShadow
1865 } else {
1866 ShaderColorMode::ColorBitmap
1867 },
1868 )
1869 }
1870 };
1871
1872 let tight_bounding_rect = {
1886 let snap_bias = match subpx_dir {
1887 SubpixelDirection::None => DeviceVector2D::new(0.5, 0.5),
1888 SubpixelDirection::Horizontal => DeviceVector2D::new(0.125, 0.5),
1889 SubpixelDirection::Vertical => DeviceVector2D::new(0.5, 0.125),
1890 };
1891 let text_offset = LayoutVector2D::zero();
1892
1893 let pic_bounding_rect = if run_scratch.used_font.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS) {
1894 let mut device_bounding_rect = DeviceRect::default();
1895
1896 let glyph_transform = ctx.spatial_tree.get_relative_transform(
1897 prim_spatial_node_index,
1898 root_spatial_node_index,
1899 ).into_transform()
1900 .with_destination::<WorldPixel>()
1901 .then(&euclid::Transform3D::from_scale(ctx.global_device_pixel_scale));
1902
1903 let glyph_translation = DeviceVector2D::new(glyph_transform.m41, glyph_transform.m42);
1904
1905 let mut use_tight_bounding_rect = true;
1906 for glyph in glyphs {
1907 let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
1908
1909 let transformed_offset = match glyph_transform.transform_point2d(glyph_offset) {
1910 Some(transformed_offset) => transformed_offset,
1911 None => {
1912 use_tight_bounding_rect = false;
1913 break;
1914 }
1915 };
1916 let raster_glyph_offset = (transformed_offset + snap_bias).floor();
1917 let raster_text_offset = (
1918 glyph_transform.transform_vector2d(text_offset) +
1919 glyph_translation +
1920 DeviceVector2D::new(0.5, 0.5)
1921 ).floor() - glyph_translation;
1922
1923 let device_glyph_rect = DeviceRect::from_origin_and_size(
1924 glyph.offset + raster_glyph_offset.to_vector() + raster_text_offset,
1925 glyph.size.to_f32(),
1926 );
1927
1928 device_bounding_rect = device_bounding_rect.union(&device_glyph_rect);
1929 }
1930
1931 if use_tight_bounding_rect {
1932 let map_device_to_surface: SpaceMapper<PicturePixel, DevicePixel> = SpaceMapper::new_with_target(
1933 root_spatial_node_index,
1934 surface_spatial_node_index,
1935 device_bounding_rect,
1936 ctx.spatial_tree,
1937 );
1938
1939 match map_device_to_surface.unmap(&device_bounding_rect) {
1940 Some(r) => r.intersection(bounding_rect),
1941 None => Some(*bounding_rect),
1942 }
1943 } else {
1944 Some(*bounding_rect)
1945 }
1946 } else {
1947 let mut local_bounding_rect = LayoutRect::default();
1948
1949 let glyph_raster_scale = run_scratch.raster_scale * ctx.global_device_pixel_scale.get();
1950
1951 for glyph in glyphs {
1952 let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
1953 let glyph_scale = LayoutToDeviceScale::new(glyph_raster_scale / glyph.scale);
1954 let raster_glyph_offset = (glyph_offset * LayoutToDeviceScale::new(glyph_raster_scale) + snap_bias).floor() / glyph.scale;
1955 let local_glyph_rect = LayoutRect::from_origin_and_size(
1956 (glyph.offset + raster_glyph_offset.to_vector()) / glyph_scale + text_offset,
1957 glyph.size.to_f32() / glyph_scale,
1958 );
1959
1960 local_bounding_rect = local_bounding_rect.union(&local_glyph_rect);
1961 }
1962
1963 let map_prim_to_surface: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target(
1964 surface_spatial_node_index,
1965 prim_spatial_node_index,
1966 *bounding_rect,
1967 ctx.spatial_tree,
1968 );
1969 map_prim_to_surface.map(&local_bounding_rect)
1970 };
1971
1972 let intersected = match pic_bounding_rect {
1973 Some(rect) => rect.intersection(bounding_rect).unwrap_or_else(PictureRect::zero),
1976 None => *bounding_rect,
1979 };
1980
1981 intersected
1982 };
1983
1984 let key = BatchKey::new(kind, blend_mode, textures);
1985
1986 let batch = batcher.alpha_batch_list.set_params_and_get_batch(
1987 key,
1988 batch_features,
1989 &tight_bounding_rect,
1990 z_id,
1991 );
1992
1993 batch.reserve(glyphs.len());
1994 for glyph in glyphs {
1995 batch.push(base_instance.build(
1996 clip_task_address,
1997 subpx_dir,
1998 glyph.index_in_text_run,
1999 glyph.uv_rect_address,
2000 color_mode,
2001 glyph.subpx_offset_x,
2002 glyph.subpx_offset_y,
2003 glyph.is_packed_glyph,
2004 ));
2005 }
2006 },
2007 );
2008 }
2009 PrimitiveKind::LineDecoration { .. } => {
2010 let scratch_handle = prim_info.kind_scratch.unwrap_line_decoration();
2011 let line_dec_scratch = ctx.scratch.frame.line_decoration[scratch_handle];
2012 let render_task_id = line_dec_scratch.task_id;
2013 let prim_cache_address = line_dec_scratch.gpu_address;
2014
2015 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2016 prim_info.clip_task_index,
2017 render_tasks,
2018 ).unwrap();
2019
2020 let (batch_kind, textures, prim_user_data, specific_resource_address) = if render_task_id != RenderTaskId::INVALID {
2021 let (uv_rect_address, texture) = render_tasks.resolve_location(render_task_id).unwrap();
2022 let textures = BatchTextures::prim_textured(
2023 texture,
2024 clip_mask_texture_id,
2025 );
2026 (
2027 BrushBatchKind::Image(texture.image_buffer_kind()),
2028 textures,
2029 ImageBrushUserData {
2030 color_mode: ShaderColorMode::Image,
2031 alpha_type: AlphaType::PremultipliedAlpha,
2032 raster_space: RasterizationSpace::Local,
2033 opacity: 1.0,
2034 }.encode(),
2035 uv_rect_address.as_int(),
2036 )
2037 } else {
2038 (
2039 BrushBatchKind::Solid,
2040 BatchTextures::prim_untextured(clip_mask_texture_id),
2041 [get_shader_opacity(1.0), 0, 0, 0],
2042 0,
2043 )
2044 };
2045
2046 let prim_header = PrimitiveHeader {
2047 specific_prim_address: prim_cache_address.as_int(),
2048 user_data: prim_user_data,
2049 ..base_prim_header
2050 };
2051 let prim_header_index = prim_headers.push(&prim_header);
2052
2053 let batch_key = BatchKey {
2054 blend_mode,
2055 kind: BatchKind::Brush(batch_kind),
2056 textures,
2057 };
2058
2059 self.add_brush_instance_to_batches(
2060 batch_key,
2061 batch_features,
2062 bounding_rect,
2063 z_id,
2064 INVALID_SEGMENT_INDEX,
2065 common_data.transformed_aa_edges,
2066 clip_task_address,
2067 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2068 prim_header_index,
2069 specific_resource_address,
2070 );
2071 }
2072 PrimitiveKind::Rectangle { .. } => {
2073 let batch_params = BrushBatchParameters::shared(
2074 BrushBatchKind::Solid,
2075 TextureSet::UNTEXTURED,
2076 [get_shader_opacity(1.0), 0, 0, 0],
2077 0,
2078 );
2079
2080 let prim_header = PrimitiveHeader {
2081 specific_prim_address: prim_cache_address.as_int(),
2082 user_data: batch_params.prim_user_data,
2083 ..base_prim_header
2084 };
2085 let prim_header_index = prim_headers.push(&prim_header);
2086
2087 self.add_segmented_prim_to_batch(
2088 segments,
2089 common_data.opacity,
2090 &batch_params,
2091 blend_mode,
2092 batch_features,
2093 brush_flags,
2094 common_data.transformed_aa_edges,
2095 prim_header_index,
2096 bounding_rect,
2097 transform_metadata,
2098 z_id,
2099 prim_info.clip_task_index,
2100 ctx,
2101 render_tasks,
2102 );
2103 }
2104 PrimitiveKind::YuvImage { data_handle, .. } => {
2105 let segment_instance_index = prim_info.segment_instance_index;
2106 if prim_info.compositor_surface_kind.needs_cutout() {
2107 self.add_compositor_surface_cutout(
2108 prim_rect,
2109 prim_info.clip_chain.local_clip_rect,
2110 prim_info.clip_task_index,
2111 transform_id,
2112 z_id,
2113 bounding_rect,
2114 ctx,
2115 render_tasks,
2116 prim_headers,
2117 );
2118
2119 return;
2120 }
2121
2122 let yuv_image_data = &ctx.data_stores.yuv_image[data_handle].kind;
2123 let mut textures = TextureSet::UNTEXTURED;
2124 let mut uv_rect_addresses = [0; 3];
2125
2126 let channel_count = yuv_image_data.format.get_plane_num();
2128 debug_assert!(channel_count <= 3);
2129 for channel in 0 .. channel_count {
2130
2131 let src_channel = render_tasks.resolve_location(yuv_image_data.src_yuv[channel]);
2132
2133 let (uv_rect_address, texture_source) = match src_channel {
2134 Some(src) => src,
2135 None => {
2136 warn!("Warnings: skip a PrimitiveKind::YuvImage");
2137 return;
2138 }
2139 };
2140
2141 textures.colors[channel] = texture_source;
2142 uv_rect_addresses[channel] = uv_rect_address.as_int();
2143 }
2144
2145 let buffer_kind = textures.colors[0].image_buffer_kind();
2147 assert!(
2148 textures.colors[1 .. yuv_image_data.format.get_plane_num()]
2149 .iter()
2150 .all(|&tid| buffer_kind == tid.image_buffer_kind())
2151 );
2152
2153 let kind = BrushBatchKind::YuvImage(
2154 buffer_kind,
2155 yuv_image_data.format,
2156 yuv_image_data.color_depth,
2157 yuv_image_data.color_space,
2158 yuv_image_data.color_range,
2159 );
2160
2161 let batch_params = BrushBatchParameters::shared(
2162 kind,
2163 textures,
2164 [
2165 uv_rect_addresses[0],
2166 uv_rect_addresses[1],
2167 uv_rect_addresses[2],
2168 0,
2169 ],
2170 0,
2171 );
2172
2173 debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID);
2174
2175 let prim_header = PrimitiveHeader {
2176 specific_prim_address: prim_cache_address.as_int(),
2177 user_data: batch_params.prim_user_data,
2178 ..base_prim_header
2179 };
2180 let prim_header_index = prim_headers.push(&prim_header);
2181
2182 self.add_segmented_prim_to_batch(
2183 segments,
2184 common_data.opacity,
2185 &batch_params,
2186 blend_mode,
2187 batch_features,
2188 brush_flags,
2189 common_data.transformed_aa_edges,
2190 prim_header_index,
2191 bounding_rect,
2192 transform_metadata,
2193 z_id,
2194 prim_info.clip_task_index,
2195 ctx,
2196 render_tasks,
2197 );
2198 }
2199 PrimitiveKind::Image { data_handle, .. } => {
2200 let img_scratch_handle = prim_info.kind_scratch.unwrap_image();
2201 if prim_info.compositor_surface_kind.needs_cutout() {
2202 self.add_compositor_surface_cutout(
2203 prim_rect,
2204 prim_info.clip_chain.local_clip_rect,
2205 prim_info.clip_task_index,
2206 transform_id,
2207 z_id,
2208 bounding_rect,
2209 ctx,
2210 render_tasks,
2211 prim_headers,
2212 );
2213
2214 return;
2215 }
2216
2217 let image_data = &ctx.data_stores.image[data_handle].kind;
2218 let image_scratch = &ctx.scratch.frame.images[img_scratch_handle];
2219 let visible_tiles = &ctx.scratch.frame.visible_image_tiles[image_scratch.visible_tiles];
2220 let prim_user_data = ImageBrushUserData {
2221 color_mode: ShaderColorMode::Image,
2222 alpha_type: image_data.alpha_type,
2223 raster_space: RasterizationSpace::Local,
2224 opacity: 1.0,
2225 }.encode();
2226
2227 let blend_mode = if needs_blending {
2228 match image_data.alpha_type {
2229 AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
2230 AlphaType::Alpha => BlendMode::Alpha,
2231 }
2232 } else {
2233 BlendMode::None
2234 };
2235
2236 if visible_tiles.is_empty() {
2237 if cfg!(debug_assertions) {
2238 match ctx.resource_cache.get_image_properties(image_data.key) {
2239 Some(ImageProperties { tiling: None, .. }) | None => (),
2240 other => panic!("Non-tiled image with no visible images detected! Properties {:?}", other),
2241 }
2242 }
2243
2244 let src_color = render_tasks.resolve_location(image_scratch.src_color);
2245
2246 let (uv_rect_address, texture_source) = match src_color {
2247 Some(src) => src,
2248 None => {
2249 return;
2250 }
2251 };
2252
2253 let batch_params = BrushBatchParameters::shared(
2254 BrushBatchKind::Image(texture_source.image_buffer_kind()),
2255 TextureSet::prim_textured(texture_source),
2256 prim_user_data,
2257 uv_rect_address.as_int(),
2258 );
2259
2260 let (prim_cache_address, segments) = if prim_info.segment_instance_index == SegmentInstanceIndex::UNUSED {
2261 (image_scratch.gpu_address, None)
2262 } else {
2263 let segment_instance = &ctx.scratch.frame.segment_instances[prim_info.segment_instance_index];
2264 let segments = Some(&ctx.scratch.frame.segments[segment_instance.segments_range]);
2265 (segment_instance.gpu_data, segments)
2266 };
2267
2268 let local_rect = image_scratch.adjustment.map_local_rect(&prim_rect);
2269 let local_clip_rect = image_scratch.tight_local_clip_rect
2270 .intersection_unchecked(&local_rect);
2271
2272 let prim_header = PrimitiveHeader {
2273 local_rect,
2274 local_clip_rect,
2275 specific_prim_address: prim_cache_address.as_int(),
2276 user_data: batch_params.prim_user_data,
2277 ..base_prim_header
2278 };
2279
2280 let prim_header_index = prim_headers.push(&prim_header);
2281
2282 let brush_flags = match image_scratch.normalized_uvs {
2283 true => brush_flags | BrushFlags::NORMALIZED_UVS,
2284 false => brush_flags,
2285 };
2286
2287 self.add_segmented_prim_to_batch(
2288 segments,
2289 common_data.opacity,
2290 &batch_params,
2291 blend_mode,
2292 batch_features,
2293 brush_flags,
2294 common_data.transformed_aa_edges,
2295 prim_header_index,
2296 bounding_rect,
2297 transform_metadata,
2298 z_id,
2299 prim_info.clip_task_index,
2300 ctx,
2301 render_tasks,
2302 );
2303 } else {
2304 const VECS_PER_SPECIFIC_BRUSH: usize = 3;
2305 let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT;
2306
2307 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2308 prim_info.clip_task_index,
2309 render_tasks,
2310 ).unwrap();
2311
2312 let mut gpu_blocks = Vec::<GpuBufferBlockF>::with_capacity(3 + max_tiles_per_header * 2);
2314 for chunk in visible_tiles.chunks(max_tiles_per_header) {
2315 gpu_blocks.clear();
2316 gpu_blocks.push(image_data.color.premultiplied().into()); gpu_blocks.push(PremultipliedColorF::WHITE.into()); gpu_blocks.push([-1.0, 0.0, 0.0, 0.0].into()); for tile in chunk {
2321 let tile_rect = tile.local_rect.translate(-prim_rect.min.to_vector());
2322 gpu_blocks.push(tile_rect.into());
2323 gpu_blocks.push([0.0; 4].into());
2324 }
2325
2326 let mut writer = gpu_buffer_builder.f32.write_blocks(gpu_blocks.len());
2327 for block in &gpu_blocks {
2328 writer.push_one(*block);
2329 }
2330 let specific_prim_address = writer.finish();
2331
2332 let prim_header = PrimitiveHeader {
2333 local_clip_rect: image_scratch.tight_local_clip_rect,
2334 specific_prim_address: specific_prim_address.as_int(),
2335 user_data: prim_user_data,
2336 ..base_prim_header
2337 };
2338 let prim_header_index = prim_headers.push(&prim_header);
2339
2340 for (i, tile) in chunk.iter().enumerate() {
2341 let (uv_rect_address, texture) = match render_tasks.resolve_location(tile.src_color) {
2342 Some(result) => result,
2343 None => {
2344 return;
2345 }
2346 };
2347
2348 let textures = BatchTextures::prim_textured(
2349 texture,
2350 clip_mask_texture_id,
2351 );
2352
2353 let batch_key = BatchKey {
2354 blend_mode,
2355 kind: BatchKind::Brush(BrushBatchKind::Image(texture.image_buffer_kind())),
2356 textures,
2357 };
2358
2359 self.add_brush_instance_to_batches(
2360 batch_key,
2361 batch_features,
2362 bounding_rect,
2363 z_id,
2364 i as i32,
2365 tile.edge_flags,
2366 clip_task_address,
2367 brush_flags | BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION,
2368 prim_header_index,
2369 uv_rect_address.as_int(),
2370 );
2371 }
2372 }
2373 }
2374 }
2375 PrimitiveKind::LinearGradient { .. } => {
2376 unreachable!("BUG: linear gradients should always use quad path");
2377 }
2378 PrimitiveKind::BackdropCapture { .. } => {}
2379 PrimitiveKind::BackdropRender { .. } => {
2380 let scratch_handle = prim_info.kind_scratch.unwrap_backdrop_render();
2381 let blend_mode = BlendMode::PremultipliedAlpha;
2382 let pic_task_id = Some(ctx.scratch.frame.backdrop_render[scratch_handle].src_task_id);
2383
2384 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2385 prim_info.clip_task_index,
2386 render_tasks,
2387 ).unwrap();
2388
2389 let kind = BatchKind::Brush(
2390 BrushBatchKind::Image(ImageBufferKind::Texture2D)
2391 );
2392 let (_, texture) = render_tasks.resolve_location(pic_task_id).unwrap();
2393 let textures = BatchTextures::prim_textured(
2394 texture,
2395 clip_mask_texture_id,
2396 );
2397 let key = BatchKey::new(
2398 kind,
2399 blend_mode,
2400 textures,
2401 );
2402
2403 let prim_header = PrimitiveHeader {
2404 specific_prim_address: ctx.globals.default_image_data.as_int(),
2405 user_data: ImageBrushUserData {
2406 color_mode: ShaderColorMode::Image,
2407 alpha_type: AlphaType::PremultipliedAlpha,
2408 raster_space: RasterizationSpace::Screen,
2409 opacity: 1.0,
2410 }.encode(),
2411 ..base_prim_header
2412 };
2413 let prim_header_index = prim_headers.push(&prim_header);
2414
2415 let pic_task = &render_tasks[pic_task_id.unwrap()];
2416 let pic_info = match pic_task.kind {
2417 RenderTaskKind::Picture(ref info) => info,
2418 _ => panic!("bug: not a picture"),
2419 };
2420 let target_rect = pic_task.get_target_rect();
2421
2422 let backdrop_rect = DeviceRect::from_origin_and_size(
2423 pic_info.content_origin,
2424 target_rect.size().to_f32(),
2425 );
2426
2427 let map_prim_to_backdrop = SpaceMapper::new_with_target(
2428 pic_info.surface_spatial_node_index,
2429 prim_spatial_node_index,
2430 WorldRect::max_rect(),
2431 ctx.spatial_tree,
2432 );
2433
2434 let points = [
2435 map_prim_to_backdrop.map_point(prim_rect.top_left()),
2436 map_prim_to_backdrop.map_point(prim_rect.top_right()),
2437 map_prim_to_backdrop.map_point(prim_rect.bottom_left()),
2438 map_prim_to_backdrop.map_point(prim_rect.bottom_right()),
2439 ];
2440
2441 if points.iter().any(|p| p.is_none()) {
2442 return;
2443 }
2444
2445 let uvs = [
2446 calculate_screen_uv(points[0].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2447 calculate_screen_uv(points[1].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2448 calculate_screen_uv(points[2].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2449 calculate_screen_uv(points[3].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2450 ];
2451
2452 let source = ImageSource {
2453 p0: target_rect.min.to_f32(),
2454 p1: target_rect.max.to_f32(),
2455 user_data: [0.0; 4],
2456 uv_rect_kind: UvRectKind::Quad {
2457 top_left: uvs[0],
2458 top_right: uvs[1],
2459 bottom_left: uvs[2],
2460 bottom_right: uvs[3],
2461 },
2462 };
2463
2464 let uv_rect_handle = source.write_gpu_blocks(&mut gpu_buffer_builder.f32);
2465 let uv_rect_address = gpu_buffer_builder.f32.resolve_handle(uv_rect_handle);
2466
2467 self.add_brush_instance_to_batches(
2468 key,
2469 batch_features,
2470 bounding_rect,
2471 z_id,
2472 INVALID_SEGMENT_INDEX,
2473 EdgeMask::all(),
2474 clip_task_address,
2475 brush_flags,
2476 prim_header_index,
2477 uv_rect_address.as_int(),
2478 );
2479 }
2480 }
2481 }
2482
2483 fn add_compositor_surface_cutout(
2486 &mut self,
2487 prim_rect: LayoutRect,
2488 local_clip_rect: LayoutRect,
2489 clip_task_index: ClipTaskIndex,
2490 transform_id: GpuTransformId,
2491 z_id: ZBufferId,
2492 bounding_rect: &PictureRect,
2493 ctx: &RenderTargetContext,
2494 render_tasks: &RenderTaskGraph,
2495 prim_headers: &mut PrimitiveHeaders,
2496 ) {
2497 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2498 clip_task_index,
2499 render_tasks,
2500 ).unwrap();
2501
2502 let prim_header = PrimitiveHeader {
2503 local_rect: prim_rect,
2504 local_clip_rect,
2505 specific_prim_address: ctx.globals.default_black_rect_address.as_int(),
2506 transform_id,
2507 z: z_id,
2508 render_task_address: self.batcher.render_task_address,
2509 user_data: [get_shader_opacity(1.0), 0, 0, 0],
2510 };
2511 let prim_header_index = prim_headers.push(&prim_header);
2512
2513 let batch_key = BatchKey {
2514 blend_mode: BlendMode::PremultipliedDestOut,
2515 kind: BatchKind::Brush(BrushBatchKind::Solid),
2516 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2517 };
2518
2519 self.add_brush_instance_to_batches(
2520 batch_key,
2521 BatchFeatures::ALPHA_PASS | BatchFeatures::CLIP_MASK,
2522 bounding_rect,
2523 z_id,
2524 INVALID_SEGMENT_INDEX,
2525 EdgeMask::empty(),
2526 clip_task_address,
2527 BrushFlags::empty(),
2528 prim_header_index,
2529 0,
2530 );
2531 }
2532
2533 fn add_segment_to_batch(
2540 &mut self,
2541 segment: &BrushSegment,
2542 segment_data: &SegmentInstanceData,
2543 segment_index: i32,
2544 batch_kind: BrushBatchKind,
2545 prim_header_index: PrimitiveHeaderIndex,
2546 alpha_blend_mode: BlendMode,
2547 features: BatchFeatures,
2548 brush_flags: BrushFlags,
2549 edge_aa_mask: EdgeMask,
2550 bounding_rect: &PictureRect,
2551 transform_metadata: TransformMetadata,
2552 z_id: ZBufferId,
2553 prim_opacity: PrimitiveOpacity,
2554 clip_task_index: ClipTaskIndex,
2555 ctx: &RenderTargetContext,
2556 render_tasks: &RenderTaskGraph,
2557 ) {
2558 debug_assert!(clip_task_index != ClipTaskIndex::INVALID);
2559
2560 if let Some((clip_task_address, clip_mask)) = ctx.get_clip_task_and_texture(
2563 clip_task_index,
2564 segment_index,
2565 render_tasks,
2566 ) {
2567 let is_inner = segment.edge_flags.is_empty();
2569 let needs_blending = !prim_opacity.is_opaque ||
2570 clip_task_address != OPAQUE_TASK_ADDRESS ||
2571 (!is_inner && !transform_metadata.is_2d_axis_aligned) ||
2572 brush_flags.contains(BrushFlags::FORCE_AA);
2573
2574 let textures = BatchTextures {
2575 input: segment_data.textures,
2576 clip_mask,
2577 };
2578
2579 let batch_key = BatchKey {
2580 blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
2581 kind: BatchKind::Brush(batch_kind),
2582 textures,
2583 };
2584
2585 self.add_brush_instance_to_batches(
2586 batch_key,
2587 features,
2588 bounding_rect,
2589 z_id,
2590 segment_index,
2591 segment.edge_flags & edge_aa_mask,
2592 clip_task_address,
2593 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags,
2594 prim_header_index,
2595 segment_data.specific_resource_address,
2596 );
2597 }
2598 }
2599
2600 fn add_segmented_prim_to_batch(
2607 &mut self,
2608 brush_segments: Option<&[BrushSegment]>,
2609 prim_opacity: PrimitiveOpacity,
2610 params: &BrushBatchParameters,
2611 blend_mode: BlendMode,
2612 features: BatchFeatures,
2613 brush_flags: BrushFlags,
2614 edge_aa_mask: EdgeMask,
2615 prim_header_index: PrimitiveHeaderIndex,
2616 bounding_rect: &PictureRect,
2617 transform_metadata: TransformMetadata,
2618 z_id: ZBufferId,
2619 clip_task_index: ClipTaskIndex,
2620 ctx: &RenderTargetContext,
2621 render_tasks: &RenderTaskGraph,
2622 ) {
2623 match (brush_segments, ¶ms.segment_data) {
2624 (Some(ref brush_segments), SegmentDataKind::Instanced(ref segment_data)) => {
2625 debug_assert_eq!(brush_segments.len(), segment_data.len());
2628 for (segment_index, (segment, segment_data)) in brush_segments
2629 .iter()
2630 .zip(segment_data.iter())
2631 .enumerate()
2632 {
2633 self.add_segment_to_batch(
2634 segment,
2635 segment_data,
2636 segment_index as i32,
2637 params.batch_kind,
2638 prim_header_index,
2639 blend_mode,
2640 features,
2641 brush_flags,
2642 edge_aa_mask,
2643 bounding_rect,
2644 transform_metadata,
2645 z_id,
2646 prim_opacity,
2647 clip_task_index,
2648 ctx,
2649 render_tasks,
2650 );
2651 }
2652 }
2653 (Some(ref brush_segments), SegmentDataKind::Shared(ref segment_data)) => {
2654 for (segment_index, segment) in brush_segments
2657 .iter()
2658 .enumerate()
2659 {
2660 self.add_segment_to_batch(
2661 segment,
2662 segment_data,
2663 segment_index as i32,
2664 params.batch_kind,
2665 prim_header_index,
2666 blend_mode,
2667 features,
2668 brush_flags,
2669 edge_aa_mask,
2670 bounding_rect,
2671 transform_metadata,
2672 z_id,
2673 prim_opacity,
2674 clip_task_index,
2675 ctx,
2676 render_tasks,
2677 );
2678 }
2679 }
2680 (None, SegmentDataKind::Shared(ref segment_data)) => {
2681 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
2685 clip_task_index,
2686 render_tasks,
2687 ).unwrap();
2688
2689 let textures = BatchTextures {
2690 input: segment_data.textures,
2691 clip_mask,
2692 };
2693
2694 let batch_key = BatchKey {
2695 blend_mode,
2696 kind: BatchKind::Brush(params.batch_kind),
2697 textures,
2698 };
2699
2700 self.add_brush_instance_to_batches(
2701 batch_key,
2702 features,
2703 bounding_rect,
2704 z_id,
2705 INVALID_SEGMENT_INDEX,
2706 edge_aa_mask,
2707 clip_task_address,
2708 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2709 prim_header_index,
2710 segment_data.specific_resource_address,
2711 );
2712 }
2713 (None, SegmentDataKind::Instanced(..)) => {
2714 unreachable!();
2717 }
2718 }
2719 }
2720}
2721
2722enum SegmentDataKind {
2725 Shared(SegmentInstanceData),
2726 Instanced(SmallVec<[SegmentInstanceData; 8]>),
2727}
2728
2729struct BrushBatchParameters {
2732 batch_kind: BrushBatchKind,
2733 prim_user_data: [i32; 4],
2734 segment_data: SegmentDataKind,
2735}
2736
2737impl BrushBatchParameters {
2738 fn instanced(
2741 batch_kind: BrushBatchKind,
2742 prim_user_data: [i32; 4],
2743 segment_data: SmallVec<[SegmentInstanceData; 8]>,
2744 ) -> Self {
2745 BrushBatchParameters {
2746 batch_kind,
2747 prim_user_data,
2748 segment_data: SegmentDataKind::Instanced(segment_data),
2749 }
2750 }
2751
2752 fn shared(
2755 batch_kind: BrushBatchKind,
2756 textures: TextureSet,
2757 prim_user_data: [i32; 4],
2758 specific_resource_address: i32,
2759 ) -> Self {
2760 BrushBatchParameters {
2761 batch_kind,
2762 prim_user_data,
2763 segment_data: SegmentDataKind::Shared(
2764 SegmentInstanceData {
2765 textures,
2766 specific_resource_address,
2767 }
2768 ),
2769 }
2770 }
2771}
2772
2773#[cfg_attr(feature = "capture", derive(Serialize))]
2775#[cfg_attr(feature = "replay", derive(Deserialize))]
2776pub struct ClipMaskInstanceList {
2777 pub mask_instances_fast: FrameVec<MaskInstance>,
2778 pub mask_instances_slow: FrameVec<MaskInstance>,
2779
2780 pub mask_instances_fast_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
2781 pub mask_instances_slow_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
2782
2783 pub image_mask_instances: FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>,
2784 pub image_mask_instances_with_scissor: FastHashMap<(DeviceIntRect, TextureSource), FrameVec<PrimitiveInstanceData>>,
2785}
2786
2787impl ClipMaskInstanceList {
2788 pub fn new(memory: &FrameMemory) -> Self {
2789 ClipMaskInstanceList {
2790 mask_instances_fast: memory.new_vec(),
2791 mask_instances_slow: memory.new_vec(),
2792 mask_instances_fast_with_scissor: FastHashMap::default(),
2793 mask_instances_slow_with_scissor: FastHashMap::default(),
2794 image_mask_instances: FastHashMap::default(),
2795 image_mask_instances_with_scissor: FastHashMap::default(),
2796 }
2797 }
2798
2799 pub fn is_empty(&self) -> bool {
2800 let ClipMaskInstanceList {
2803 mask_instances_fast,
2804 mask_instances_slow,
2805 mask_instances_fast_with_scissor,
2806 mask_instances_slow_with_scissor,
2807 image_mask_instances,
2808 image_mask_instances_with_scissor,
2809 } = self;
2810
2811 mask_instances_fast.is_empty()
2812 && mask_instances_slow.is_empty()
2813 && mask_instances_fast_with_scissor.is_empty()
2814 && mask_instances_slow_with_scissor.is_empty()
2815 && image_mask_instances.is_empty()
2816 && image_mask_instances_with_scissor.is_empty()
2817 }
2818}
2819
2820#[derive(Debug)]
2822#[cfg_attr(feature = "capture", derive(Serialize))]
2823#[cfg_attr(feature = "replay", derive(Deserialize))]
2824pub struct ClipBatchList {
2825 pub slow_rectangles: FrameVec<ClipMaskInstanceRect>,
2827 pub fast_rectangles: FrameVec<ClipMaskInstanceRect>,
2828}
2829
2830impl ClipBatchList {
2831 fn new(memory: &FrameMemory) -> Self {
2832 ClipBatchList {
2833 slow_rectangles: memory.new_vec(),
2834 fast_rectangles: memory.new_vec(),
2835 }
2836 }
2837
2838 pub fn is_empty(&self) -> bool {
2839 self.slow_rectangles.is_empty()
2840 && self.fast_rectangles.is_empty()
2841 }
2842}
2843
2844#[derive(Debug)]
2846#[cfg_attr(feature = "capture", derive(Serialize))]
2847#[cfg_attr(feature = "replay", derive(Deserialize))]
2848pub struct ClipBatcher {
2849 pub primary_clips: ClipBatchList,
2853 pub secondary_clips: ClipBatchList,
2856
2857 gpu_supports_fast_clears: bool,
2858}
2859
2860impl ClipBatcher {
2861 pub fn new(
2862 gpu_supports_fast_clears: bool,
2863 memory: &FrameMemory,
2864 ) -> Self {
2865 ClipBatcher {
2866 primary_clips: ClipBatchList::new(memory),
2867 secondary_clips: ClipBatchList::new(memory),
2868 gpu_supports_fast_clears,
2869 }
2870 }
2871
2872 pub fn add_clip_region(
2873 &mut self,
2874 local_pos: LayoutPoint,
2875 sub_rect: DeviceRect,
2876 clip_data: ClipData,
2877 task_origin: DevicePoint,
2878 screen_origin: DevicePoint,
2879 device_pixel_scale: f32,
2880 ) {
2881 let instance = ClipMaskInstanceRect {
2882 common: ClipMaskInstanceCommon {
2883 clip_transform_id: GpuTransformId::IDENTITY,
2884 prim_transform_id: GpuTransformId::IDENTITY,
2885 sub_rect,
2886 task_origin,
2887 screen_origin,
2888 device_pixel_scale,
2889 },
2890 local_pos,
2891 clip_data,
2892 };
2893
2894 self.primary_clips.slow_rectangles.push(instance);
2895 }
2896
2897 fn add_tiled_clip_mask(
2900 &mut self,
2901 mask_screen_rect: DeviceRect,
2902 local_clip_rect: LayoutRect,
2903 clip_spatial_node_index: SpatialNodeIndex,
2904 spatial_tree: &SpatialTree,
2905 world_rect: &WorldRect,
2906 global_device_pixel_scale: DevicePixelScale,
2907 common: &ClipMaskInstanceCommon,
2908 is_first_clip: bool,
2909 ) -> bool {
2910 if mask_screen_rect.area() < CLIP_RECTANGLE_AREA_THRESHOLD {
2912 return false;
2913 }
2914
2915 let mask_screen_rect_size = mask_screen_rect.size().to_i32();
2916 let clip_spatial_node = spatial_tree.get_spatial_node(clip_spatial_node_index);
2917
2918 if clip_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
2922 return false;
2923 }
2924
2925 let transform = spatial_tree.get_world_transform(
2928 clip_spatial_node_index,
2929 );
2930 let world_clip_rect = match project_rect(
2931 &transform.into_transform(),
2932 &local_clip_rect,
2933 &world_rect,
2934 ) {
2935 Some(rect) => rect,
2936 None => return false,
2937 };
2938
2939 let world_device_rect = world_clip_rect * global_device_pixel_scale;
2942 let x_tiles = (mask_screen_rect_size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
2943 let y_tiles = (mask_screen_rect_size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
2944
2945 let mask_origin = mask_screen_rect.min.to_vector();
2949 let clip_list = self.get_batch_list(is_first_clip);
2950
2951 for y in 0 .. y_tiles {
2952 for x in 0 .. x_tiles {
2953 let p0 = DeviceIntPoint::new(
2954 x * CLIP_RECTANGLE_TILE_SIZE,
2955 y * CLIP_RECTANGLE_TILE_SIZE,
2956 );
2957 let p1 = DeviceIntPoint::new(
2958 (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.width),
2959 (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.height),
2960 );
2961 let normalized_sub_rect = DeviceIntRect {
2962 min: p0,
2963 max: p1,
2964 }.to_f32();
2965 let world_sub_rect = normalized_sub_rect.translate(mask_origin);
2966
2967 if !world_device_rect.contains_box(&world_sub_rect) {
2971 clip_list.slow_rectangles.push(ClipMaskInstanceRect {
2972 common: ClipMaskInstanceCommon {
2973 sub_rect: normalized_sub_rect,
2974 ..*common
2975 },
2976 local_pos: local_clip_rect.min,
2977 clip_data: ClipData::uniform(local_clip_rect.size(), 0.0, ClipMode::Clip),
2978 });
2979 }
2980 }
2981 }
2982
2983 true
2984 }
2985
2986 fn get_batch_list(
2989 &mut self,
2990 is_first_clip: bool,
2991 ) -> &mut ClipBatchList {
2992 if is_first_clip && !self.gpu_supports_fast_clears {
2993 &mut self.primary_clips
2994 } else {
2995 &mut self.secondary_clips
2996 }
2997 }
2998
2999 pub fn add(
3000 &mut self,
3001 clip_node_range: ClipNodeRange,
3002 root_spatial_node_index: SpatialNodeIndex,
3003 clip_store: &ClipStore,
3004 transforms: &mut TransformPalette,
3005 actual_rect: DeviceRect,
3006 surface_device_pixel_scale: DevicePixelScale,
3007 task_origin: DevicePoint,
3008 screen_origin: DevicePoint,
3009 ctx: &RenderTargetContext,
3010 ) -> bool {
3011 let mut is_first_clip = true;
3012 let mut clear_to_one = false;
3013
3014 for i in 0 .. clip_node_range.count {
3015 let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
3016 let clip_node = &ctx.data_stores.clip[clip_instance.handle];
3017
3018 let clip_transform_id = transforms.gpu.get_id(
3019 clip_instance.spatial_node_index,
3020 ctx.root_spatial_node_index,
3021 ctx.spatial_tree,
3022 );
3023
3024 let prim_transform_id = transforms.gpu.get_id(
3025 root_spatial_node_index,
3026 ctx.root_spatial_node_index,
3027 ctx.spatial_tree,
3028 );
3029
3030 let common = ClipMaskInstanceCommon {
3031 sub_rect: DeviceRect::from_size(actual_rect.size()),
3032 task_origin,
3033 screen_origin,
3034 device_pixel_scale: surface_device_pixel_scale.0,
3035 clip_transform_id,
3036 prim_transform_id,
3037 };
3038
3039 let added_clip = match clip_node.item.kind {
3040 ClipItemKind::Image { .. } => {
3041 unreachable!();
3042 }
3043 ClipItemKind::Rectangle { mode: ClipMode::ClipOut } => {
3044 self.get_batch_list(is_first_clip)
3045 .slow_rectangles
3046 .push(ClipMaskInstanceRect {
3047 common,
3048 local_pos: clip_instance.clip_rect.min,
3049 clip_data: ClipData::uniform(clip_instance.clip_rect.size(), 0.0, ClipMode::ClipOut),
3050 });
3051
3052 true
3053 }
3054 ClipItemKind::Rectangle { mode: ClipMode::Clip } => {
3055 if clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
3056 false
3057 } else {
3058 if self.add_tiled_clip_mask(
3059 actual_rect,
3060 clip_instance.clip_rect,
3061 clip_instance.spatial_node_index,
3062 ctx.spatial_tree,
3063 &ctx.screen_world_rect,
3064 ctx.global_device_pixel_scale,
3065 &common,
3066 is_first_clip,
3067 ) {
3068 clear_to_one |= is_first_clip;
3069 } else {
3070 self.get_batch_list(is_first_clip)
3071 .slow_rectangles
3072 .push(ClipMaskInstanceRect {
3073 common,
3074 local_pos: clip_instance.clip_rect.min,
3075 clip_data: ClipData::uniform(clip_instance.clip_rect.size(), 0.0, ClipMode::Clip),
3076 });
3077 }
3078
3079 true
3080 }
3081 }
3082 ClipItemKind::RoundedRectangle { ref radius, mode, .. } => {
3083 let size = clip_instance.clip_rect.size();
3084 let radius = clamped_radius(radius, size);
3085 let batch_list = self.get_batch_list(is_first_clip);
3086 let instance = ClipMaskInstanceRect {
3087 common,
3088 local_pos: clip_instance.clip_rect.min,
3089 clip_data: ClipData::rounded_rect(size, &radius, mode),
3090 };
3091 if clip_instance.flags.contains(ClipNodeFlags::USE_FAST_PATH) {
3092 batch_list.fast_rectangles.push(instance);
3093 } else {
3094 batch_list.slow_rectangles.push(instance);
3095 }
3096
3097 true
3098 }
3099 };
3100
3101 is_first_clip &= !added_clip;
3102 }
3103
3104 clear_to_one
3105 }
3106}
3107
3108impl<'a, 'rc> RenderTargetContext<'a, 'rc> {
3109 fn get_clip_task_and_texture(
3114 &self,
3115 clip_task_index: ClipTaskIndex,
3116 offset: i32,
3117 render_tasks: &RenderTaskGraph,
3118 ) -> Option<(RenderTaskAddress, TextureSource)> {
3119 match self.scratch.frame.clip_mask_instances[clip_task_index.0 as usize + offset as usize] {
3120 ClipMaskKind::Mask(task_id) => {
3121 Some((
3122 task_id.into(),
3123 TextureSource::TextureCache(
3124 render_tasks[task_id].get_target_texture(),
3125 Swizzle::default(),
3126 )
3127 ))
3128 }
3129 ClipMaskKind::None => {
3130 Some((OPAQUE_TASK_ADDRESS, TextureSource::Invalid))
3131 }
3132 ClipMaskKind::Clipped => {
3133 None
3134 }
3135 }
3136 }
3137
3138 fn get_prim_clip_task_and_texture(
3141 &self,
3142 clip_task_index: ClipTaskIndex,
3143 render_tasks: &RenderTaskGraph,
3144 ) -> Option<(RenderTaskAddress, TextureSource)> {
3145 self.get_clip_task_and_texture(
3146 clip_task_index,
3147 0,
3148 render_tasks,
3149 )
3150 }
3151}
3152
3153impl CompositorSurfaceKind {
3154 fn needs_cutout(&self) -> bool {
3156 match self {
3157 CompositorSurfaceKind::Underlay => true,
3158 CompositorSurfaceKind::Overlay | CompositorSurfaceKind::Blit => false,
3159 }
3160 }
3161}