1use api::{AlphaType, ClipMode, ImageBufferKind};
6use api::{FontInstanceFlags, YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF};
7use api::units::*;
8use crate::clip::{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_cache::{GpuBlockData, GpuCache, GpuCacheAddress};
15use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator};
16use crate::gpu_types::SplitCompositeInstance;
17use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
18use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
19use crate::gpu_types::{ImageBrushData, get_shader_opacity, BoxShadowData, MaskInstance};
20use crate::gpu_types::{ClipMaskInstanceCommon, ClipMaskInstanceRect, ClipMaskInstanceBoxShadow};
21use crate::internal_types::{FastHashMap, Filter, FrameAllocator, FrameMemory, FrameVec, Swizzle, TextureSource};
22use crate::picture::{Picture3DContext, PictureCompositeMode, calculate_screen_uv};
23use crate::prim_store::{PrimitiveInstanceKind, ClipData};
24use crate::prim_store::{PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
25use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex};
26use crate::prim_store::VECS_PER_SEGMENT;
27use crate::quad;
28use crate::render_target::RenderTargetContext;
29use crate::render_task_graph::{RenderTaskId, RenderTaskGraph};
30use crate::render_task::{RenderTaskAddress, RenderTaskKind, SubPass};
31use crate::renderer::{BlendMode, GpuBufferBuilder, ShaderColorMode};
32use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH;
33use crate::resource_cache::{GlyphFetchResult, ImageProperties};
34use crate::space::SpaceMapper;
35use crate::visibility::{PrimitiveVisibilityFlags, VisibilityState};
36use smallvec::SmallVec;
37use std::{f32, i32, usize};
38use crate::util::{project_rect, MaxRect, TransformedRectKind, ScaleOffset};
39use crate::segment::EdgeAaSegmentMask;
40
41const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fffffff);
44
45pub const INVALID_SEGMENT_INDEX: i32 = 0xffff;
47
48const CLIP_RECTANGLE_TILE_SIZE: i32 = 128;
50
51const CLIP_RECTANGLE_AREA_THRESHOLD: f32 = (CLIP_RECTANGLE_TILE_SIZE * CLIP_RECTANGLE_TILE_SIZE * 4) as f32;
53
54#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
55#[cfg_attr(feature = "capture", derive(Serialize))]
56#[cfg_attr(feature = "replay", derive(Deserialize))]
57pub enum BrushBatchKind {
58 Solid,
59 Image(ImageBufferKind),
60 Blend,
61 MixBlend {
62 task_id: RenderTaskId,
63 backdrop_id: RenderTaskId,
64 },
65 YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange),
66 LinearGradient,
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: EdgeAaSegmentMask,
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 gpu_cache: &mut GpuCache,
824 render_tasks: &RenderTaskGraph,
825 prim_headers: &mut PrimitiveHeaders,
826 transforms: &mut TransformPalette,
827 root_spatial_node_index: SpatialNodeIndex,
828 surface_spatial_node_index: SpatialNodeIndex,
829 z_generator: &mut ZBufferIdGenerator,
830 prim_instances: &[PrimitiveInstance],
831 gpu_buffer_builder: &mut GpuBufferBuilder,
832 segments: &[RenderTaskId],
833 ) {
834 let (prim_instance_index, extra_prim_gpu_address) = match cmd {
835 PrimitiveCommand::Simple { prim_instance_index } => {
836 (prim_instance_index, None)
837 }
838 PrimitiveCommand::Complex { prim_instance_index, gpu_address } => {
839 (prim_instance_index, Some(gpu_address.as_int()))
840 }
841 PrimitiveCommand::Instance { prim_instance_index, gpu_buffer_address } => {
842 (prim_instance_index, Some(gpu_buffer_address.as_int()))
843 }
844 PrimitiveCommand::Quad { pattern, pattern_input, prim_instance_index, gpu_buffer_address, quad_flags, edge_flags, transform_id, src_color_task_id } => {
845 let prim_instance = &prim_instances[prim_instance_index.0 as usize];
846 let prim_info = &prim_instance.vis;
847 let bounding_rect = &prim_info.clip_chain.pic_coverage_rect;
848 let render_task_address = self.batcher.render_task_address;
849
850 if segments.is_empty() {
851 let z_id = z_generator.next();
852
853 quad::add_to_batch(
854 *pattern,
855 *pattern_input,
856 render_task_address,
857 *transform_id,
858 *gpu_buffer_address,
859 *quad_flags,
860 *edge_flags,
861 INVALID_SEGMENT_INDEX as u8,
862 *src_color_task_id,
863 z_id,
864 render_tasks,
865 gpu_buffer_builder,
866 |key, instance| {
867 let batch = self.batcher.set_params_and_get_batch(
868 key,
869 BatchFeatures::empty(),
870 bounding_rect,
871 z_id,
872 );
873 batch.push(instance);
874 },
875 );
876 } else {
877 for (i, task_id) in segments.iter().enumerate() {
878 debug_assert!(edge_flags.is_empty());
880
881 let z_id = z_generator.next();
882
883 quad::add_to_batch(
884 *pattern,
885 *pattern_input,
886 render_task_address,
887 *transform_id,
888 *gpu_buffer_address,
889 *quad_flags,
890 *edge_flags,
891 i as u8,
892 *task_id,
893 z_id,
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[prim_instance_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 prim_instance.vis.state {
923 VisibilityState::Culled => {
924 return;
925 }
926 VisibilityState::PassThrough |
927 VisibilityState::Unset => {
928 panic!("bug: invalid visibility state");
929 }
930 VisibilityState::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
946 .get_id(
947 prim_spatial_node_index,
948 root_spatial_node_index,
949 ctx.spatial_tree,
950 );
951
952 let transform_kind = transform_id.transform_kind();
956 let prim_info = &prim_instance.vis;
957 let bounding_rect = &prim_info.clip_chain.pic_coverage_rect;
958
959 let z_id = z_generator.next();
960
961 let prim_rect = ctx.data_stores.get_local_prim_rect(
962 prim_instance,
963 &ctx.prim_store.pictures,
964 ctx.surfaces,
965 );
966
967 let mut batch_features = BatchFeatures::empty();
968 if ctx.data_stores.prim_may_need_repetition(prim_instance) {
969 batch_features |= BatchFeatures::REPETITION;
970 }
971
972 if transform_kind != TransformedRectKind::AxisAligned || is_anti_aliased {
973 batch_features |= BatchFeatures::ANTIALIASING;
974 }
975
976 if prim_info.clip_task_index != ClipTaskIndex::INVALID {
978 batch_features |= BatchFeatures::CLIP_MASK;
979 }
980
981 if !bounding_rect.is_empty() {
982 debug_assert_eq!(prim_info.clip_chain.pic_spatial_node_index, surface_spatial_node_index,
983 "The primitive's bounding box is specified in a different coordinate system from the current batch!");
984 }
985
986 match prim_instance.kind {
987 PrimitiveInstanceKind::BoxShadow { .. } => {
988 unreachable!("BUG: Should not hit box-shadow here as they are handled by quad infra");
989 }
990 PrimitiveInstanceKind::Clear { data_handle } => {
991 let prim_data = &ctx.data_stores.prim[data_handle];
992 let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
993
994 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
995 prim_info.clip_task_index,
996 render_tasks,
997 ).unwrap();
998
999 let prim_header = PrimitiveHeader {
1004 local_rect: prim_rect,
1005 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1006 specific_prim_address: prim_cache_address,
1007 transform_id,
1008 };
1009
1010 let prim_header_index = prim_headers.push(
1011 &prim_header,
1012 z_id,
1013 self.batcher.render_task_address,
1014 [get_shader_opacity(1.0), 0, 0, 0],
1015 );
1016
1017 let batch_key = BatchKey {
1018 blend_mode: BlendMode::PremultipliedDestOut,
1019 kind: BatchKind::Brush(BrushBatchKind::Solid),
1020 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
1021 };
1022
1023 self.add_brush_instance_to_batches(
1024 batch_key,
1025 batch_features,
1026 bounding_rect,
1027 z_id,
1028 INVALID_SEGMENT_INDEX,
1029 prim_data.edge_aa_mask,
1030 clip_task_address,
1031 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1032 prim_header_index,
1033 0,
1034 );
1035 }
1036 PrimitiveInstanceKind::NormalBorder { data_handle, ref render_task_ids, .. } => {
1037 let prim_data = &ctx.data_stores.normal_border[data_handle];
1038 let common_data = &prim_data.common;
1039 let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
1040 let task_ids = &ctx.scratch.border_cache_handles[*render_task_ids];
1041 let specified_blend_mode = BlendMode::PremultipliedAlpha;
1042 let mut segment_data: SmallVec<[SegmentInstanceData; 8]> = SmallVec::new();
1043
1044 for task_id in task_ids {
1048 if let Some((uv_rect_address, texture)) = render_tasks.resolve_location(*task_id, gpu_cache) {
1049 segment_data.push(
1050 SegmentInstanceData {
1051 textures: TextureSet::prim_textured(texture),
1052 specific_resource_address: uv_rect_address.as_int(),
1053 }
1054 );
1055 }
1056 }
1057
1058 let image_buffer_kind = ImageBufferKind::Texture2D;
1060
1061 let blend_mode = if !common_data.opacity.is_opaque ||
1062 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1063 transform_kind == TransformedRectKind::Complex ||
1064 is_anti_aliased
1065 {
1066 specified_blend_mode
1067 } else {
1068 BlendMode::None
1069 };
1070
1071 let prim_header = PrimitiveHeader {
1072 local_rect: prim_rect,
1073 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1074 specific_prim_address: prim_cache_address,
1075 transform_id,
1076 };
1077
1078 let batch_params = BrushBatchParameters::instanced(
1079 BrushBatchKind::Image(image_buffer_kind),
1080 ImageBrushData {
1081 color_mode: ShaderColorMode::Image,
1082 alpha_type: AlphaType::PremultipliedAlpha,
1083 raster_space: RasterizationSpace::Local,
1084 opacity: 1.0,
1085 }.encode(),
1086 segment_data,
1087 );
1088
1089 let prim_header_index = prim_headers.push(
1090 &prim_header,
1091 z_id,
1092 self.batcher.render_task_address,
1093 batch_params.prim_user_data,
1094 );
1095
1096 let border_data = &prim_data.kind;
1097 self.add_segmented_prim_to_batch(
1098 Some(border_data.brush_segments.as_slice()),
1099 common_data.opacity,
1100 &batch_params,
1101 blend_mode,
1102 batch_features,
1103 brush_flags,
1104 common_data.edge_aa_mask,
1105 prim_header_index,
1106 bounding_rect,
1107 transform_kind,
1108 z_id,
1109 prim_info.clip_task_index,
1110 ctx,
1111 render_tasks,
1112 );
1113 }
1114 PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
1115 let run = &ctx.prim_store.text_runs[run_index];
1116 let subpx_dir = run.used_font.get_subpx_dir();
1117
1118 let prim_data = &ctx.data_stores.text_run[data_handle];
1121 let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
1122
1123 let prim_header = PrimitiveHeader {
1134 local_rect: LayoutRect {
1135 min: prim_rect.min - run.reference_frame_relative_offset,
1136 max: run.snapped_reference_frame_relative_offset.to_point(),
1137 },
1138 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1139 specific_prim_address: prim_cache_address,
1140 transform_id,
1141 };
1142
1143 let glyph_keys = &ctx.scratch.glyph_keys[run.glyph_keys_range];
1144 let prim_header_index = prim_headers.push(
1145 &prim_header,
1146 z_id,
1147 self.batcher.render_task_address,
1148 [
1149 (run.raster_scale * 65535.0).round() as i32,
1150 0,
1151 0,
1152 0,
1153 ],
1154 );
1155 let base_instance = GlyphInstance::new(
1156 prim_header_index,
1157 );
1158 let batcher = &mut self.batcher;
1159
1160 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1161 prim_info.clip_task_index,
1162 render_tasks,
1163 ).unwrap();
1164
1165 let font = run.used_font.clone();
1169 ctx.resource_cache.fetch_glyphs(
1170 font,
1171 &glyph_keys,
1172 &mut self.glyph_fetch_buffer,
1173 gpu_cache,
1174 |texture_id, glyph_format, glyphs| {
1175 debug_assert_ne!(texture_id, TextureSource::Invalid);
1176
1177 let subpx_dir = subpx_dir.limit_by(glyph_format);
1178
1179 let textures = BatchTextures::prim_textured(
1180 texture_id,
1181 clip_mask_texture_id,
1182 );
1183
1184 let kind = BatchKind::TextRun(glyph_format);
1185
1186 let (blend_mode, color_mode) = match glyph_format {
1187 GlyphFormat::Subpixel |
1188 GlyphFormat::TransformedSubpixel => {
1189 debug_assert!(ctx.use_dual_source_blending);
1190 (
1191 BlendMode::SubpixelDualSource,
1192 ShaderColorMode::SubpixelDualSource,
1193 )
1194 }
1195 GlyphFormat::Alpha |
1196 GlyphFormat::TransformedAlpha |
1197 GlyphFormat::Bitmap => {
1198 (
1199 BlendMode::PremultipliedAlpha,
1200 ShaderColorMode::Alpha,
1201 )
1202 }
1203 GlyphFormat::ColorBitmap => {
1204 (
1205 BlendMode::PremultipliedAlpha,
1206 if run.shadow {
1207 ShaderColorMode::BitmapShadow
1209 } else {
1210 ShaderColorMode::ColorBitmap
1211 },
1212 )
1213 }
1214 };
1215
1216 let tight_bounding_rect = {
1222 let snap_bias = match subpx_dir {
1223 SubpixelDirection::None => DeviceVector2D::new(0.5, 0.5),
1224 SubpixelDirection::Horizontal => DeviceVector2D::new(0.125, 0.5),
1225 SubpixelDirection::Vertical => DeviceVector2D::new(0.5, 0.125),
1226 SubpixelDirection::Mixed => DeviceVector2D::new(0.125, 0.125),
1227 };
1228 let text_offset = prim_header.local_rect.max.to_vector();
1229
1230 let pic_bounding_rect = if run.used_font.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS) {
1231 let mut device_bounding_rect = DeviceRect::default();
1232
1233 let glyph_transform = ctx.spatial_tree.get_relative_transform(
1234 prim_spatial_node_index,
1235 root_spatial_node_index,
1236 ).into_transform()
1237 .with_destination::<WorldPixel>()
1238 .then(&euclid::Transform3D::from_scale(ctx.global_device_pixel_scale));
1239
1240 let glyph_translation = DeviceVector2D::new(glyph_transform.m41, glyph_transform.m42);
1241
1242 let mut use_tight_bounding_rect = true;
1243 for glyph in glyphs {
1244 let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
1245
1246 let transformed_offset = match glyph_transform.transform_point2d(glyph_offset) {
1247 Some(transformed_offset) => transformed_offset,
1248 None => {
1249 use_tight_bounding_rect = false;
1250 break;
1251 }
1252 };
1253 let raster_glyph_offset = (transformed_offset + snap_bias).floor();
1254 let raster_text_offset = (
1255 glyph_transform.transform_vector2d(text_offset) +
1256 glyph_translation +
1257 DeviceVector2D::new(0.5, 0.5)
1258 ).floor() - glyph_translation;
1259
1260 let device_glyph_rect = DeviceRect::from_origin_and_size(
1261 glyph.offset + raster_glyph_offset.to_vector() + raster_text_offset,
1262 glyph.size.to_f32(),
1263 );
1264
1265 device_bounding_rect = device_bounding_rect.union(&device_glyph_rect);
1266 }
1267
1268 if use_tight_bounding_rect {
1269 let map_device_to_surface: SpaceMapper<PicturePixel, DevicePixel> = SpaceMapper::new_with_target(
1270 root_spatial_node_index,
1271 surface_spatial_node_index,
1272 device_bounding_rect,
1273 ctx.spatial_tree,
1274 );
1275
1276 match map_device_to_surface.unmap(&device_bounding_rect) {
1277 Some(r) => r.intersection(bounding_rect),
1278 None => Some(*bounding_rect),
1279 }
1280 } else {
1281 Some(*bounding_rect)
1282 }
1283 } else {
1284 let mut local_bounding_rect = LayoutRect::default();
1285
1286 let glyph_raster_scale = run.raster_scale * ctx.global_device_pixel_scale.get();
1287
1288 for glyph in glyphs {
1289 let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
1290 let glyph_scale = LayoutToDeviceScale::new(glyph_raster_scale / glyph.scale);
1291 let raster_glyph_offset = (glyph_offset * LayoutToDeviceScale::new(glyph_raster_scale) + snap_bias).floor() / glyph.scale;
1292 let local_glyph_rect = LayoutRect::from_origin_and_size(
1293 (glyph.offset + raster_glyph_offset.to_vector()) / glyph_scale + text_offset,
1294 glyph.size.to_f32() / glyph_scale,
1295 );
1296
1297 local_bounding_rect = local_bounding_rect.union(&local_glyph_rect);
1298 }
1299
1300 let map_prim_to_surface: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target(
1301 surface_spatial_node_index,
1302 prim_spatial_node_index,
1303 *bounding_rect,
1304 ctx.spatial_tree,
1305 );
1306 map_prim_to_surface.map(&local_bounding_rect)
1307 };
1308
1309 let intersected = match pic_bounding_rect {
1310 Some(rect) => rect.intersection(bounding_rect).unwrap_or_else(PictureRect::zero),
1313 None => *bounding_rect,
1316 };
1317
1318 intersected
1319 };
1320
1321 let key = BatchKey::new(kind, blend_mode, textures);
1322
1323 let batch = batcher.alpha_batch_list.set_params_and_get_batch(
1324 key,
1325 batch_features,
1326 &tight_bounding_rect,
1327 z_id,
1328 );
1329
1330 batch.reserve(glyphs.len());
1331 for glyph in glyphs {
1332 batch.push(base_instance.build(
1333 clip_task_address,
1334 subpx_dir,
1335 glyph.index_in_text_run,
1336 glyph.uv_rect_address,
1337 color_mode,
1338 ));
1339 }
1340 },
1341 );
1342 }
1343 PrimitiveInstanceKind::LineDecoration { data_handle, ref render_task, .. } => {
1344 let common_data = &ctx.data_stores.line_decoration[data_handle].common;
1347 let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
1348
1349 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1350 prim_info.clip_task_index,
1351 render_tasks,
1352 ).unwrap();
1353
1354 let (batch_kind, textures, prim_user_data, specific_resource_address) = match render_task {
1355 Some(task_id) => {
1356 let (uv_rect_address, texture) = render_tasks.resolve_location(*task_id, gpu_cache).unwrap();
1357 let textures = BatchTextures::prim_textured(
1358 texture,
1359 clip_mask_texture_id,
1360 );
1361 (
1362 BrushBatchKind::Image(texture.image_buffer_kind()),
1363 textures,
1364 ImageBrushData {
1365 color_mode: ShaderColorMode::Image,
1366 alpha_type: AlphaType::PremultipliedAlpha,
1367 raster_space: RasterizationSpace::Local,
1368 opacity: 1.0,
1369 }.encode(),
1370 uv_rect_address.as_int(),
1371 )
1372 }
1373 None => {
1374 (
1375 BrushBatchKind::Solid,
1376 BatchTextures::prim_untextured(clip_mask_texture_id),
1377 [get_shader_opacity(1.0), 0, 0, 0],
1378 0,
1379 )
1380 }
1381 };
1382
1383 let blend_mode = if !common_data.opacity.is_opaque ||
1387 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1388 transform_kind == TransformedRectKind::Complex ||
1389 is_anti_aliased
1390 {
1391 BlendMode::PremultipliedAlpha
1392 } else {
1393 BlendMode::None
1394 };
1395
1396 let prim_header = PrimitiveHeader {
1397 local_rect: prim_rect,
1398 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1399 specific_prim_address: prim_cache_address,
1400 transform_id,
1401 };
1402
1403 let prim_header_index = prim_headers.push(
1404 &prim_header,
1405 z_id,
1406 self.batcher.render_task_address,
1407 prim_user_data,
1408 );
1409
1410 let batch_key = BatchKey {
1411 blend_mode,
1412 kind: BatchKind::Brush(batch_kind),
1413 textures,
1414 };
1415
1416 self.add_brush_instance_to_batches(
1417 batch_key,
1418 batch_features,
1419 bounding_rect,
1420 z_id,
1421 INVALID_SEGMENT_INDEX,
1422 common_data.edge_aa_mask,
1423 clip_task_address,
1424 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1425 prim_header_index,
1426 specific_resource_address,
1427 );
1428 }
1429 PrimitiveInstanceKind::Picture { pic_index, .. } => {
1430 let picture = &ctx.prim_store.pictures[pic_index.0];
1431 if let Some(snapshot) = picture.snapshot {
1432 if snapshot.detached {
1433 return;
1434 }
1435 }
1436
1437 let blend_mode = BlendMode::PremultipliedAlpha;
1438 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
1439
1440 match picture.raster_config {
1441 Some(ref raster_config) => {
1442 let brush_flags = brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION;
1445
1446 let surface = &ctx.surfaces[raster_config.surface_index.0];
1447 let mut local_clip_rect = prim_info.clip_chain.local_clip_rect;
1448
1449 let transform_id = if surface.surface_spatial_node_index == surface.raster_spatial_node_index {
1454 transform_id
1455 } else {
1456 let map_local_to_raster = SpaceMapper::new_with_target(
1457 root_spatial_node_index,
1458 surface.surface_spatial_node_index,
1459 LayoutRect::max_rect(),
1460 ctx.spatial_tree,
1461 );
1462
1463 let raster_rect = map_local_to_raster
1464 .map(&prim_rect)
1465 .unwrap();
1466
1467 let sx = (raster_rect.max.x - raster_rect.min.x) / (prim_rect.max.x - prim_rect.min.x);
1468 let sy = (raster_rect.max.y - raster_rect.min.y) / (prim_rect.max.y - prim_rect.min.y);
1469
1470 let tx = raster_rect.min.x - sx * prim_rect.min.x;
1471 let ty = raster_rect.min.y - sy * prim_rect.min.y;
1472
1473 let transform = ScaleOffset::new(sx, sy, tx, ty);
1474
1475 let raster_clip_rect = map_local_to_raster
1476 .map(&prim_info.clip_chain.local_clip_rect)
1477 .unwrap();
1478 local_clip_rect = transform.unmap_rect(&raster_clip_rect);
1479
1480 transforms.get_custom(transform.to_transform())
1481 };
1482
1483 let prim_header = PrimitiveHeader {
1484 local_rect: prim_rect,
1485 local_clip_rect,
1486 specific_prim_address: prim_cache_address,
1487 transform_id,
1488 };
1489
1490 let mut is_opaque = prim_info.clip_task_index == ClipTaskIndex::INVALID
1491 && surface.is_opaque
1492 && transform_kind == TransformedRectKind::AxisAligned
1493 && !is_anti_aliased;
1494
1495 let pic_task_id = picture.primary_render_task_id.unwrap();
1496
1497 let pic_task = &render_tasks[pic_task_id];
1498 match pic_task.sub_pass {
1499 Some(SubPass::Masks { .. }) => {
1500 is_opaque = false;
1501 }
1502 None => {}
1503 }
1504
1505 match raster_config.composite_mode {
1506 PictureCompositeMode::TileCache { .. } => {
1507 }
1512 PictureCompositeMode::IntermediateSurface { .. } => {
1513 }
1517 PictureCompositeMode::Filter(ref filter) => {
1518 assert!(filter.is_visible());
1519 match filter {
1520 Filter::Blur { .. } => {
1521 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1522 prim_info.clip_task_index,
1523 render_tasks,
1524 ).unwrap();
1525
1526 let kind = BatchKind::Brush(
1527 BrushBatchKind::Image(ImageBufferKind::Texture2D)
1528 );
1529
1530 let (uv_rect_address, texture) = render_tasks.resolve_location(
1531 pic_task_id,
1532 gpu_cache,
1533 ).unwrap();
1534 let textures = BatchTextures::prim_textured(
1535 texture,
1536 clip_mask_texture_id,
1537 );
1538
1539 let key = BatchKey::new(
1540 kind,
1541 blend_mode,
1542 textures,
1543 );
1544 let prim_header_index = prim_headers.push(
1545 &prim_header,
1546 z_id,
1547 self.batcher.render_task_address,
1548 ImageBrushData {
1549 color_mode: ShaderColorMode::Image,
1550 alpha_type: AlphaType::PremultipliedAlpha,
1551 raster_space: RasterizationSpace::Screen,
1552 opacity: 1.0,
1553 }.encode(),
1554 );
1555
1556 self.add_brush_instance_to_batches(
1557 key,
1558 batch_features,
1559 bounding_rect,
1560 z_id,
1561 INVALID_SEGMENT_INDEX,
1562 EdgeAaSegmentMask::all(),
1563 clip_task_address,
1564 brush_flags,
1565 prim_header_index,
1566 uv_rect_address.as_int(),
1567 );
1568 }
1569 Filter::DropShadows(shadows) => {
1570 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1571 prim_info.clip_task_index,
1572 render_tasks,
1573 ).unwrap();
1574
1575 let kind = BatchKind::Brush(
1579 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1580 );
1581
1582 let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
1585 let content_source = {
1586 let secondary_task = &render_tasks[secondary_id];
1587 let texture_id = secondary_task.get_target_texture();
1588 TextureSource::TextureCache(
1589 texture_id,
1590 Swizzle::default(),
1591 )
1592 };
1593
1594 let (shadow_uv_rect_address, shadow_texture) = render_tasks.resolve_location(
1596 pic_task_id,
1597 gpu_cache,
1598 ).unwrap();
1599 let shadow_textures = BatchTextures::prim_textured(
1600 shadow_texture,
1601 clip_mask_texture_id,
1602 );
1603
1604 let content_uv_rect_address = render_tasks[secondary_id]
1605 .get_texture_address(gpu_cache)
1606 .as_int();
1607
1608 let content_textures = BatchTextures::prim_textured(
1610 content_source,
1611 clip_mask_texture_id,
1612 );
1613
1614 let shadow_key = BatchKey::new(kind, blend_mode, shadow_textures);
1616 let content_key = BatchKey::new(kind, blend_mode, content_textures);
1617
1618 for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) {
1619 let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data);
1621
1622 let shadow_rect = prim_header.local_rect.translate(shadow.offset);
1623
1624 let shadow_prim_header = PrimitiveHeader {
1625 local_rect: shadow_rect,
1626 specific_prim_address: shadow_prim_address,
1627 ..prim_header
1628 };
1629
1630 let shadow_prim_header_index = prim_headers.push(
1631 &shadow_prim_header,
1632 z_id,
1633 self.batcher.render_task_address,
1634 ImageBrushData {
1635 color_mode: ShaderColorMode::Alpha,
1636 alpha_type: AlphaType::PremultipliedAlpha,
1637 raster_space: RasterizationSpace::Screen,
1638 opacity: 1.0,
1639 }.encode(),
1640 );
1641
1642 self.add_brush_instance_to_batches(
1643 shadow_key,
1644 batch_features,
1645 bounding_rect,
1646 z_id,
1647 INVALID_SEGMENT_INDEX,
1648 EdgeAaSegmentMask::all(),
1649 clip_task_address,
1650 brush_flags,
1651 shadow_prim_header_index,
1652 shadow_uv_rect_address.as_int(),
1653 );
1654 }
1655 let z_id_content = z_generator.next();
1656
1657 let content_prim_header_index = prim_headers.push(
1658 &prim_header,
1659 z_id_content,
1660 self.batcher.render_task_address,
1661 ImageBrushData {
1662 color_mode: ShaderColorMode::Image,
1663 alpha_type: AlphaType::PremultipliedAlpha,
1664 raster_space: RasterizationSpace::Screen,
1665 opacity: 1.0,
1666 }.encode(),
1667 );
1668
1669 self.add_brush_instance_to_batches(
1670 content_key,
1671 batch_features,
1672 bounding_rect,
1673 z_id_content,
1674 INVALID_SEGMENT_INDEX,
1675 EdgeAaSegmentMask::all(),
1676 clip_task_address,
1677 brush_flags,
1678 content_prim_header_index,
1679 content_uv_rect_address,
1680 );
1681 }
1682 Filter::Opacity(_, amount) => {
1683 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1684 prim_info.clip_task_index,
1685 render_tasks,
1686 ).unwrap();
1687
1688 let amount = (amount * 65536.0) as i32;
1689
1690 let (uv_rect_address, texture) = render_tasks.resolve_location(
1691 pic_task_id,
1692 gpu_cache,
1693 ).unwrap();
1694 let textures = BatchTextures::prim_textured(
1695 texture,
1696 clip_mask_texture_id,
1697 );
1698
1699
1700 let key = BatchKey::new(
1701 BatchKind::Brush(BrushBatchKind::Opacity),
1702 BlendMode::PremultipliedAlpha,
1703 textures,
1704 );
1705
1706 let prim_header_index = prim_headers.push(
1707 &prim_header,
1708 z_id,
1709 self.batcher.render_task_address,
1710 [
1711 uv_rect_address.as_int(),
1712 amount,
1713 0,
1714 0,
1715 ]
1716 );
1717
1718 self.add_brush_instance_to_batches(
1719 key,
1720 batch_features,
1721 bounding_rect,
1722 z_id,
1723 INVALID_SEGMENT_INDEX,
1724 EdgeAaSegmentMask::all(),
1725 clip_task_address,
1726 brush_flags,
1727 prim_header_index,
1728 0,
1729 );
1730 }
1731 _ => {
1732 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1733 prim_info.clip_task_index,
1734 render_tasks,
1735 ).unwrap();
1736
1737 let filter_mode = filter.as_int();
1739
1740 let user_data = match filter {
1741 Filter::Identity => 0x10000i32, Filter::Contrast(amount) |
1743 Filter::Grayscale(amount) |
1744 Filter::Invert(amount) |
1745 Filter::Saturate(amount) |
1746 Filter::Sepia(amount) |
1747 Filter::Brightness(amount) => {
1748 (amount * 65536.0) as i32
1749 }
1750 Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
1751 Filter::HueRotate(angle) => {
1752 (0.01745329251 * angle * 65536.0) as i32
1753 }
1754 Filter::ColorMatrix(_) => {
1755 picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1756 }
1757 Filter::Flood(_) => {
1758 picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1759 }
1760
1761 Filter::ComponentTransfer |
1763 Filter::Blur { .. } |
1764 Filter::DropShadows(..) |
1765 Filter::Opacity(..) |
1766 Filter::SVGGraphNode(..) => unreachable!(),
1767 };
1768
1769 if let Filter::ColorMatrix(..) = filter {
1772 is_opaque = false;
1773 }
1774
1775 let (uv_rect_address, texture) = render_tasks.resolve_location(
1776 pic_task_id,
1777 gpu_cache,
1778 ).unwrap();
1779 let textures = BatchTextures::prim_textured(
1780 texture,
1781 clip_mask_texture_id,
1782 );
1783
1784 let blend_mode = if is_opaque {
1785 BlendMode::None
1786 } else {
1787 BlendMode::PremultipliedAlpha
1788 };
1789
1790 let key = BatchKey::new(
1791 BatchKind::Brush(BrushBatchKind::Blend),
1792 blend_mode,
1793 textures,
1794 );
1795
1796 let prim_header_index = prim_headers.push(
1797 &prim_header,
1798 z_id,
1799 self.batcher.render_task_address,
1800 [
1801 uv_rect_address.as_int(),
1802 filter_mode,
1803 user_data,
1804 0,
1805 ]
1806 );
1807
1808 self.add_brush_instance_to_batches(
1809 key,
1810 batch_features,
1811 bounding_rect,
1812 z_id,
1813 INVALID_SEGMENT_INDEX,
1814 EdgeAaSegmentMask::all(),
1815 clip_task_address,
1816 brush_flags,
1817 prim_header_index,
1818 0,
1819 );
1820 }
1821 }
1822 }
1823 PictureCompositeMode::ComponentTransferFilter(handle) => {
1824 let filter_data = &ctx.data_stores.filter_data[handle];
1828 let filter_mode : i32 = Filter::ComponentTransfer.as_int() |
1829 ((filter_data.data.r_func.to_int() << 28 |
1830 filter_data.data.g_func.to_int() << 24 |
1831 filter_data.data.b_func.to_int() << 20 |
1832 filter_data.data.a_func.to_int() << 16) as i32);
1833
1834 let user_data = filter_data.gpu_cache_handle.as_int(gpu_cache);
1835
1836 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1837 prim_info.clip_task_index,
1838 render_tasks,
1839 ).unwrap();
1840
1841 let (uv_rect_address, texture) = render_tasks.resolve_location(
1842 pic_task_id,
1843 gpu_cache,
1844 ).unwrap();
1845 let textures = BatchTextures::prim_textured(
1846 texture,
1847 clip_mask_texture_id,
1848 );
1849
1850 let key = BatchKey::new(
1851 BatchKind::Brush(BrushBatchKind::Blend),
1852 BlendMode::PremultipliedAlpha,
1853 textures,
1854 );
1855
1856 let prim_header_index = prim_headers.push(
1857 &prim_header,
1858 z_id,
1859 self.batcher.render_task_address,
1860 [
1861 uv_rect_address.as_int(),
1862 filter_mode,
1863 user_data,
1864 0,
1865 ]
1866 );
1867
1868 self.add_brush_instance_to_batches(
1869 key,
1870 batch_features,
1871 bounding_rect,
1872 z_id,
1873 INVALID_SEGMENT_INDEX,
1874 EdgeAaSegmentMask::all(),
1875 clip_task_address,
1876 brush_flags,
1877 prim_header_index,
1878 0,
1879 );
1880 }
1881 PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
1882 mode,
1883 ctx.use_advanced_blending,
1884 !ctx.break_advanced_blend_batches,
1885 ctx.use_dual_source_blending,
1886 ).is_some() => {
1887 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1888 prim_info.clip_task_index,
1889 render_tasks,
1890 ).unwrap();
1891
1892 let (uv_rect_address, texture) = render_tasks.resolve_location(
1893 pic_task_id,
1894 gpu_cache,
1895 ).unwrap();
1896 let textures = BatchTextures::prim_textured(
1897 texture,
1898 clip_mask_texture_id,
1899 );
1900
1901
1902 let key = BatchKey::new(
1903 BatchKind::Brush(
1904 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1905 ),
1906 BlendMode::from_mix_blend_mode(
1907 mode,
1908 ctx.use_advanced_blending,
1909 !ctx.break_advanced_blend_batches,
1910 ctx.use_dual_source_blending,
1911 ).unwrap(),
1912 textures,
1913 );
1914 let prim_header_index = prim_headers.push(
1915 &prim_header,
1916 z_id,
1917 self.batcher.render_task_address,
1918 ImageBrushData {
1919 color_mode: match key.blend_mode {
1920 BlendMode::MultiplyDualSource => ShaderColorMode::MultiplyDualSource,
1921 _ => ShaderColorMode::Image,
1922 },
1923 alpha_type: AlphaType::PremultipliedAlpha,
1924 raster_space: RasterizationSpace::Screen,
1925 opacity: 1.0,
1926 }.encode(),
1927 );
1928
1929 self.add_brush_instance_to_batches(
1930 key,
1931 batch_features,
1932 bounding_rect,
1933 z_id,
1934 INVALID_SEGMENT_INDEX,
1935 EdgeAaSegmentMask::all(),
1936 clip_task_address,
1937 brush_flags,
1938 prim_header_index,
1939 uv_rect_address.as_int(),
1940 );
1941 }
1942 PictureCompositeMode::MixBlend(mode) => {
1943 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1944 prim_info.clip_task_index,
1945 render_tasks,
1946 ).unwrap();
1947 let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
1948
1949 let color0 = render_tasks[backdrop_id].get_target_texture();
1950 let color1 = render_tasks[pic_task_id].get_target_texture();
1951
1952 let batch_key = BatchKey::new(
1961 BatchKind::Brush(
1962 BrushBatchKind::MixBlend {
1963 task_id: self.batcher.render_task_id,
1964 backdrop_id,
1965 },
1966 ),
1967 BlendMode::PremultipliedAlpha,
1968 BatchTextures {
1969 input: TextureSet {
1970 colors: [
1971 TextureSource::TextureCache(
1972 color0,
1973 Swizzle::default(),
1974 ),
1975 TextureSource::TextureCache(
1976 color1,
1977 Swizzle::default(),
1978 ),
1979 TextureSource::Invalid,
1980 ],
1981 },
1982 clip_mask: clip_mask_texture_id,
1983 },
1984 );
1985 let src_uv_address = render_tasks[pic_task_id].get_texture_address(gpu_cache);
1986 let readback_uv_address = render_tasks[backdrop_id].get_texture_address(gpu_cache);
1987 let prim_header_index = prim_headers.push(
1988 &prim_header,
1989 z_id,
1990 self.batcher.render_task_address,
1991 [
1992 mode as u32 as i32,
1993 readback_uv_address.as_int(),
1994 src_uv_address.as_int(),
1995 0,
1996 ]
1997 );
1998
1999 let instance = BrushInstance {
2000 segment_index: INVALID_SEGMENT_INDEX,
2001 edge_flags: EdgeAaSegmentMask::all(),
2002 clip_task_address,
2003 brush_flags,
2004 prim_header_index,
2005 resource_address: 0,
2006 };
2007
2008 self.batcher.push_single_instance(
2009 batch_key,
2010 batch_features,
2011 bounding_rect,
2012 z_id,
2013 PrimitiveInstanceData::from(instance),
2014 );
2015 }
2016 PictureCompositeMode::Blit(_) => {
2017 match picture.context_3d {
2018 Picture3DContext::In { root_data: Some(_), .. } => {
2019 unreachable!("bug: should not have a raster_config");
2020 }
2021 Picture3DContext::In { root_data: None, .. } => {
2022 let extra_prim_gpu_address = match extra_prim_gpu_address {
2029 Some(prim_address) => prim_address,
2030 None => return,
2031 };
2032
2033 let (child_clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2035 prim_info.clip_task_index,
2036 render_tasks,
2037 ).unwrap();
2038
2039 let prim_header = PrimitiveHeader {
2040 local_rect: prim_rect,
2041 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2042 specific_prim_address: GpuCacheAddress::INVALID,
2043 transform_id: transforms
2044 .get_id(
2045 prim_spatial_node_index,
2046 root_spatial_node_index,
2047 ctx.spatial_tree,
2048 ),
2049 };
2050
2051 let child_pic_task_id = picture
2052 .primary_render_task_id
2053 .unwrap();
2054
2055 let (uv_rect_address, texture) = render_tasks.resolve_location(
2056 child_pic_task_id,
2057 gpu_cache,
2058 ).unwrap();
2059 let textures = BatchTextures::prim_textured(
2060 texture,
2061 clip_mask_texture_id,
2062 );
2063
2064 let z_id = z_generator.next();
2067
2068 let prim_header_index = prim_headers.push(
2069 &prim_header,
2070 z_id,
2071 self.batcher.render_task_address,
2072 [
2073 uv_rect_address.as_int(),
2074 BrushFlags::PERSPECTIVE_INTERPOLATION.bits() as i32,
2075 0,
2076 child_clip_task_address.0 as i32,
2077 ]
2078 );
2079
2080 let key = BatchKey::new(
2081 BatchKind::SplitComposite,
2082 BlendMode::PremultipliedAlpha,
2083 textures,
2084 );
2085
2086 self.add_split_composite_instance_to_batches(
2087 key,
2088 BatchFeatures::CLIP_MASK,
2089 &prim_info.clip_chain.pic_coverage_rect,
2090 z_id,
2091 prim_header_index,
2092 extra_prim_gpu_address,
2093 );
2094 }
2095 Picture3DContext::Out { .. } => {
2096 let uv_rect_address = render_tasks[pic_task_id]
2097 .get_texture_address(gpu_cache)
2098 .as_int();
2099 let cache_render_task = &render_tasks[pic_task_id];
2100 let texture_id = cache_render_task.get_target_texture();
2101 let textures = TextureSet {
2102 colors: [
2103 TextureSource::TextureCache(
2104 texture_id,
2105 Swizzle::default(),
2106 ),
2107 TextureSource::Invalid,
2108 TextureSource::Invalid,
2109 ],
2110 };
2111 let batch_params = BrushBatchParameters::shared(
2112 BrushBatchKind::Image(ImageBufferKind::Texture2D),
2113 textures,
2114 ImageBrushData {
2115 color_mode: ShaderColorMode::Image,
2116 alpha_type: AlphaType::PremultipliedAlpha,
2117 raster_space: RasterizationSpace::Screen,
2118 opacity: 1.0,
2119 }.encode(),
2120 uv_rect_address,
2121 );
2122
2123 let prim_header = PrimitiveHeader {
2124 specific_prim_address: prim_cache_address,
2125 ..prim_header
2126 };
2127
2128 let prim_header_index = prim_headers.push(
2129 &prim_header,
2130 z_id,
2131 self.batcher.render_task_address,
2132 batch_params.prim_user_data,
2133 );
2134
2135 let (opacity, blend_mode) = if is_opaque {
2136 (PrimitiveOpacity::opaque(), BlendMode::None)
2137 } else {
2138 (PrimitiveOpacity::translucent(), BlendMode::PremultipliedAlpha)
2139 };
2140
2141 self.add_segmented_prim_to_batch(
2142 None,
2143 opacity,
2144 &batch_params,
2145 blend_mode,
2146 batch_features,
2147 brush_flags,
2148 EdgeAaSegmentMask::all(),
2149 prim_header_index,
2150 bounding_rect,
2151 transform_kind,
2152 z_id,
2153 prim_info.clip_task_index,
2154 ctx,
2155 render_tasks,
2156 );
2157 }
2158 }
2159 }
2160 PictureCompositeMode::SvgFilter(..) => {
2161 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2162 prim_info.clip_task_index,
2163 render_tasks,
2164 ).unwrap();
2165
2166 let kind = BatchKind::Brush(
2167 BrushBatchKind::Image(ImageBufferKind::Texture2D)
2168 );
2169 let (uv_rect_address, texture) = render_tasks.resolve_location(
2170 pic_task_id,
2171 gpu_cache,
2172 ).unwrap();
2173 let textures = BatchTextures::prim_textured(
2174 texture,
2175 clip_mask_texture_id,
2176 );
2177 let key = BatchKey::new(
2178 kind,
2179 blend_mode,
2180 textures,
2181 );
2182 let prim_header_index = prim_headers.push(
2183 &prim_header,
2184 z_id,
2185 self.batcher.render_task_address,
2186 ImageBrushData {
2187 color_mode: ShaderColorMode::Image,
2188 alpha_type: AlphaType::PremultipliedAlpha,
2189 raster_space: RasterizationSpace::Screen,
2190 opacity: 1.0,
2191 }.encode(),
2192 );
2193
2194 self.add_brush_instance_to_batches(
2195 key,
2196 batch_features,
2197 bounding_rect,
2198 z_id,
2199 INVALID_SEGMENT_INDEX,
2200 EdgeAaSegmentMask::all(),
2201 clip_task_address,
2202 brush_flags,
2203 prim_header_index,
2204 uv_rect_address.as_int(),
2205 );
2206 }
2207 PictureCompositeMode::SVGFEGraph(..) => {
2208 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2209 prim_info.clip_task_index,
2210 render_tasks,
2211 ).unwrap();
2212
2213 let kind = BatchKind::Brush(
2214 BrushBatchKind::Image(ImageBufferKind::Texture2D)
2215 );
2216 let (uv_rect_address, texture) = render_tasks.resolve_location(
2217 pic_task_id,
2218 gpu_cache,
2219 ).unwrap();
2220 let textures = BatchTextures::prim_textured(
2221 texture,
2222 clip_mask_texture_id,
2223 );
2224 let key = BatchKey::new(
2225 kind,
2226 blend_mode,
2227 textures,
2228 );
2229 let prim_header_index = prim_headers.push(
2230 &prim_header,
2231 z_id,
2232 self.batcher.render_task_address,
2233 ImageBrushData {
2234 color_mode: ShaderColorMode::Image,
2235 alpha_type: AlphaType::PremultipliedAlpha,
2236 raster_space: RasterizationSpace::Screen,
2237 opacity: 1.0,
2238 }.encode(),
2239 );
2240
2241 self.add_brush_instance_to_batches(
2242 key,
2243 batch_features,
2244 bounding_rect,
2245 z_id,
2246 INVALID_SEGMENT_INDEX,
2247 EdgeAaSegmentMask::all(),
2248 clip_task_address,
2249 brush_flags,
2250 prim_header_index,
2251 uv_rect_address.as_int(),
2252 );
2253 }
2254 }
2255 }
2256 None => {
2257 unreachable!();
2258 }
2259 }
2260 }
2261 PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
2262 let prim_data = &ctx.data_stores.image_border[data_handle];
2263 let common_data = &prim_data.common;
2264 let border_data = &prim_data.kind;
2265
2266 let (uv_rect_address, texture) = match render_tasks.resolve_location(border_data.src_color, gpu_cache) {
2267 Some(src) => src,
2268 None => {
2269 return;
2270 }
2271 };
2272
2273 let textures = TextureSet::prim_textured(texture);
2274 let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
2275 let blend_mode = if !common_data.opacity.is_opaque ||
2276 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2277 transform_kind == TransformedRectKind::Complex ||
2278 is_anti_aliased
2279 {
2280 BlendMode::PremultipliedAlpha
2281 } else {
2282 BlendMode::None
2283 };
2284
2285 let prim_header = PrimitiveHeader {
2286 local_rect: prim_rect,
2287 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2288 specific_prim_address: prim_cache_address,
2289 transform_id,
2290 };
2291
2292 let batch_params = BrushBatchParameters::shared(
2293 BrushBatchKind::Image(texture.image_buffer_kind()),
2294 textures,
2295 ImageBrushData {
2296 color_mode: ShaderColorMode::Image,
2297 alpha_type: AlphaType::PremultipliedAlpha,
2298 raster_space: RasterizationSpace::Local,
2299 opacity: 1.0,
2300 }.encode(),
2301 uv_rect_address.as_int(),
2302 );
2303
2304 let prim_header_index = prim_headers.push(
2305 &prim_header,
2306 z_id,
2307 self.batcher.render_task_address,
2308 batch_params.prim_user_data,
2309 );
2310
2311 self.add_segmented_prim_to_batch(
2312 Some(border_data.brush_segments.as_slice()),
2313 common_data.opacity,
2314 &batch_params,
2315 blend_mode,
2316 batch_features,
2317 brush_flags,
2318 common_data.edge_aa_mask,
2319 prim_header_index,
2320 bounding_rect,
2321 transform_kind,
2322 z_id,
2323 prim_info.clip_task_index,
2324 ctx,
2325 render_tasks,
2326 );
2327 }
2328 PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, use_legacy_path, .. } => {
2329 debug_assert!(use_legacy_path);
2330 let prim_data = &ctx.data_stores.prim[data_handle];
2331
2332 let blend_mode = if !prim_data.opacity.is_opaque ||
2333 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2334 transform_kind == TransformedRectKind::Complex ||
2335 is_anti_aliased
2336 {
2337 BlendMode::PremultipliedAlpha
2338 } else {
2339 BlendMode::None
2340 };
2341
2342 let batch_params = BrushBatchParameters::shared(
2343 BrushBatchKind::Solid,
2344 TextureSet::UNTEXTURED,
2345 [get_shader_opacity(1.0), 0, 0, 0],
2346 0,
2347 );
2348
2349 let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
2350 (gpu_cache.get_address(&prim_data.gpu_cache_handle), None)
2351 } else {
2352 let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
2353 let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2354 (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2355 };
2356
2357 let prim_header = PrimitiveHeader {
2358 local_rect: prim_rect,
2359 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2360 specific_prim_address: prim_cache_address,
2361 transform_id,
2362 };
2363
2364 let prim_header_index = prim_headers.push(
2365 &prim_header,
2366 z_id,
2367 self.batcher.render_task_address,
2368 batch_params.prim_user_data,
2369 );
2370
2371 self.add_segmented_prim_to_batch(
2372 segments,
2373 prim_data.opacity,
2374 &batch_params,
2375 blend_mode,
2376 batch_features,
2377 brush_flags,
2378 prim_data.edge_aa_mask,
2379 prim_header_index,
2380 bounding_rect,
2381 transform_kind,
2382 z_id,
2383 prim_info.clip_task_index,
2384 ctx,
2385 render_tasks,
2386 );
2387 }
2388 PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, compositor_surface_kind, .. } => {
2389 if compositor_surface_kind.needs_cutout() {
2390 self.add_compositor_surface_cutout(
2391 prim_rect,
2392 prim_info.clip_chain.local_clip_rect,
2393 prim_info.clip_task_index,
2394 transform_id,
2395 z_id,
2396 bounding_rect,
2397 ctx,
2398 gpu_cache,
2399 render_tasks,
2400 prim_headers,
2401 );
2402
2403 return;
2404 }
2405
2406 let yuv_image_data = &ctx.data_stores.yuv_image[data_handle].kind;
2407 let mut textures = TextureSet::UNTEXTURED;
2408 let mut uv_rect_addresses = [0; 3];
2409
2410 let channel_count = yuv_image_data.format.get_plane_num();
2412 debug_assert!(channel_count <= 3);
2413 for channel in 0 .. channel_count {
2414
2415 let src_channel = render_tasks.resolve_location(yuv_image_data.src_yuv[channel], gpu_cache);
2416
2417 let (uv_rect_address, texture_source) = match src_channel {
2418 Some(src) => src,
2419 None => {
2420 warn!("Warnings: skip a PrimitiveKind::YuvImage");
2421 return;
2422 }
2423 };
2424
2425 textures.colors[channel] = texture_source;
2426 uv_rect_addresses[channel] = uv_rect_address.as_int();
2427 }
2428
2429 let buffer_kind = textures.colors[0].image_buffer_kind();
2431 assert!(
2432 textures.colors[1 .. yuv_image_data.format.get_plane_num()]
2433 .iter()
2434 .all(|&tid| buffer_kind == tid.image_buffer_kind())
2435 );
2436
2437 let kind = BrushBatchKind::YuvImage(
2438 buffer_kind,
2439 yuv_image_data.format,
2440 yuv_image_data.color_depth,
2441 yuv_image_data.color_space,
2442 yuv_image_data.color_range,
2443 );
2444
2445 let batch_params = BrushBatchParameters::shared(
2446 kind,
2447 textures,
2448 [
2449 uv_rect_addresses[0],
2450 uv_rect_addresses[1],
2451 uv_rect_addresses[2],
2452 0,
2453 ],
2454 0,
2455 );
2456
2457 let prim_common_data = ctx.data_stores.as_common_data(&prim_instance);
2458
2459 let blend_mode = if !prim_common_data.opacity.is_opaque ||
2460 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2461 transform_kind == TransformedRectKind::Complex ||
2462 is_anti_aliased
2463 {
2464 BlendMode::PremultipliedAlpha
2465 } else {
2466 BlendMode::None
2467 };
2468
2469 debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID);
2470 let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
2471 (gpu_cache.get_address(&prim_common_data.gpu_cache_handle), None)
2472 } else {
2473 let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
2474 let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2475 (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2476 };
2477
2478 let prim_header = PrimitiveHeader {
2479 local_rect: prim_rect,
2480 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2481 specific_prim_address: prim_cache_address,
2482 transform_id,
2483 };
2484
2485 let prim_header_index = prim_headers.push(
2486 &prim_header,
2487 z_id,
2488 self.batcher.render_task_address,
2489 batch_params.prim_user_data,
2490 );
2491
2492 self.add_segmented_prim_to_batch(
2493 segments,
2494 prim_common_data.opacity,
2495 &batch_params,
2496 blend_mode,
2497 batch_features,
2498 brush_flags,
2499 prim_common_data.edge_aa_mask,
2500 prim_header_index,
2501 bounding_rect,
2502 transform_kind,
2503 z_id,
2504 prim_info.clip_task_index,
2505 ctx,
2506 render_tasks,
2507 );
2508 }
2509 PrimitiveInstanceKind::Image { data_handle, image_instance_index, compositor_surface_kind, .. } => {
2510 if compositor_surface_kind.needs_cutout() {
2511 self.add_compositor_surface_cutout(
2512 prim_rect,
2513 prim_info.clip_chain.local_clip_rect,
2514 prim_info.clip_task_index,
2515 transform_id,
2516 z_id,
2517 bounding_rect,
2518 ctx,
2519 gpu_cache,
2520 render_tasks,
2521 prim_headers,
2522 );
2523
2524 return;
2525 }
2526
2527 let image_data = &ctx.data_stores.image[data_handle].kind;
2528 let common_data = &ctx.data_stores.image[data_handle].common;
2529 let image_instance = &ctx.prim_store.images[image_instance_index];
2530 let prim_user_data = ImageBrushData {
2531 color_mode: ShaderColorMode::Image,
2532 alpha_type: image_data.alpha_type,
2533 raster_space: RasterizationSpace::Local,
2534 opacity: 1.0,
2535 }.encode();
2536
2537 let blend_mode = if !common_data.opacity.is_opaque ||
2538 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2539 transform_kind == TransformedRectKind::Complex ||
2540 is_anti_aliased
2541 {
2542 match image_data.alpha_type {
2543 AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
2544 AlphaType::Alpha => BlendMode::Alpha,
2545 }
2546 } else {
2547 BlendMode::None
2548 };
2549
2550 if image_instance.visible_tiles.is_empty() {
2551 if cfg!(debug_assertions) {
2552 match ctx.resource_cache.get_image_properties(image_data.key) {
2553 Some(ImageProperties { tiling: None, .. }) | None => (),
2554 other => panic!("Non-tiled image with no visible images detected! Properties {:?}", other),
2555 }
2556 }
2557
2558 let src_color = render_tasks.resolve_location(image_instance.src_color, gpu_cache);
2559
2560 let (uv_rect_address, texture_source) = match src_color {
2561 Some(src) => src,
2562 None => {
2563 return;
2564 }
2565 };
2566
2567 let batch_params = BrushBatchParameters::shared(
2568 BrushBatchKind::Image(texture_source.image_buffer_kind()),
2569 TextureSet::prim_textured(texture_source),
2570 prim_user_data,
2571 uv_rect_address.as_int(),
2572 );
2573
2574 debug_assert_ne!(image_instance.segment_instance_index, SegmentInstanceIndex::INVALID);
2575 let (prim_cache_address, segments) = if image_instance.segment_instance_index == SegmentInstanceIndex::UNUSED {
2576 (gpu_cache.get_address(&common_data.gpu_cache_handle), None)
2577 } else {
2578 let segment_instance = &ctx.scratch.segment_instances[image_instance.segment_instance_index];
2579 let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2580 (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2581 };
2582
2583 let local_rect = image_instance.adjustment.map_local_rect(&prim_rect);
2584 let local_clip_rect = image_instance.tight_local_clip_rect
2585 .intersection_unchecked(&local_rect);
2586
2587 let prim_header = PrimitiveHeader {
2588 local_rect,
2589 local_clip_rect,
2590 specific_prim_address: prim_cache_address,
2591 transform_id,
2592 };
2593
2594 let prim_header_index = prim_headers.push(
2595 &prim_header,
2596 z_id,
2597 self.batcher.render_task_address,
2598 batch_params.prim_user_data,
2599 );
2600
2601 let brush_flags = match image_instance.normalized_uvs {
2602 true => brush_flags | BrushFlags::NORMALIZED_UVS,
2603 false => brush_flags,
2604 };
2605
2606 self.add_segmented_prim_to_batch(
2607 segments,
2608 common_data.opacity,
2609 &batch_params,
2610 blend_mode,
2611 batch_features,
2612 brush_flags,
2613 common_data.edge_aa_mask,
2614 prim_header_index,
2615 bounding_rect,
2616 transform_kind,
2617 z_id,
2618 prim_info.clip_task_index,
2619 ctx,
2620 render_tasks,
2621 );
2622 } else {
2623 const VECS_PER_SPECIFIC_BRUSH: usize = 3;
2624 let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT;
2625
2626 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2627 prim_info.clip_task_index,
2628 render_tasks,
2629 ).unwrap();
2630
2631 let mut gpu_blocks = Vec::<GpuBlockData>::with_capacity(3 + max_tiles_per_header * 2);
2633 for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) {
2634 gpu_blocks.clear();
2635 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 {
2640 let tile_rect = tile.local_rect.translate(-prim_rect.min.to_vector());
2641 gpu_blocks.push(tile_rect.into());
2642 gpu_blocks.push(GpuBlockData::EMPTY);
2643 }
2644
2645 let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
2646 let prim_header = PrimitiveHeader {
2647 local_rect: prim_rect,
2648 local_clip_rect: image_instance.tight_local_clip_rect,
2649 specific_prim_address: gpu_cache.get_address(&gpu_handle),
2650 transform_id,
2651 };
2652 let prim_header_index = prim_headers.push(
2653 &prim_header,
2654 z_id,
2655 self.batcher.render_task_address,
2656 prim_user_data,
2657 );
2658
2659 for (i, tile) in chunk.iter().enumerate() {
2660 let (uv_rect_address, texture) = match render_tasks.resolve_location(tile.src_color, gpu_cache) {
2661 Some(result) => result,
2662 None => {
2663 return;
2664 }
2665 };
2666
2667 let textures = BatchTextures::prim_textured(
2668 texture,
2669 clip_mask_texture_id,
2670 );
2671
2672 let batch_key = BatchKey {
2673 blend_mode,
2674 kind: BatchKind::Brush(BrushBatchKind::Image(texture.image_buffer_kind())),
2675 textures,
2676 };
2677
2678 self.add_brush_instance_to_batches(
2679 batch_key,
2680 batch_features,
2681 bounding_rect,
2682 z_id,
2683 i as i32,
2684 tile.edge_flags,
2685 clip_task_address,
2686 brush_flags | BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION,
2687 prim_header_index,
2688 uv_rect_address.as_int(),
2689 );
2690 }
2691 }
2692 }
2693 }
2694 PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => {
2695 let prim_data = &ctx.data_stores.linear_grad[data_handle];
2696
2697 let mut prim_header = PrimitiveHeader {
2698 local_rect: prim_rect,
2699 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2700 specific_prim_address: GpuCacheAddress::INVALID,
2701 transform_id,
2702 };
2703
2704 let blend_mode = if !prim_data.opacity.is_opaque ||
2705 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2706 transform_kind == TransformedRectKind::Complex ||
2707 is_anti_aliased
2708 {
2709 BlendMode::PremultipliedAlpha
2710 } else {
2711 BlendMode::None
2712 };
2713
2714 let user_data = [extra_prim_gpu_address.unwrap(), 0, 0, 0];
2715
2716 if visible_tiles_range.is_empty() {
2717 let batch_params = BrushBatchParameters::shared(
2718 BrushBatchKind::LinearGradient,
2719 TextureSet::UNTEXTURED,
2720 user_data,
2721 0,
2722 );
2723
2724 prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
2725
2726 let prim_header_index = prim_headers.push(
2727 &prim_header,
2728 z_id,
2729 self.batcher.render_task_address,
2730 user_data,
2731 );
2732
2733 let segments = if prim_data.brush_segments.is_empty() {
2734 None
2735 } else {
2736 Some(prim_data.brush_segments.as_slice())
2737 };
2738 self.add_segmented_prim_to_batch(
2739 segments,
2740 prim_data.opacity,
2741 &batch_params,
2742 blend_mode,
2743 batch_features,
2744 brush_flags,
2745 prim_data.edge_aa_mask,
2746 prim_header_index,
2747 bounding_rect,
2748 transform_kind,
2749 z_id,
2750 prim_info.clip_task_index,
2751 ctx,
2752 render_tasks,
2753 );
2754 } else {
2755 let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
2756
2757 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2758 prim_info.clip_task_index,
2759 render_tasks,
2760 ).unwrap();
2761
2762 let key = BatchKey {
2763 blend_mode,
2764 kind: BatchKind::Brush(BrushBatchKind::LinearGradient),
2765 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2766 };
2767
2768 for tile in visible_tiles {
2769 let tile_prim_header = PrimitiveHeader {
2770 specific_prim_address: gpu_cache.get_address(&tile.handle),
2771 local_rect: tile.local_rect,
2772 local_clip_rect: tile.local_clip_rect,
2773 ..prim_header
2774 };
2775 let prim_header_index = prim_headers.push(
2776 &tile_prim_header,
2777 z_id,
2778 self.batcher.render_task_address,
2779 user_data,
2780 );
2781
2782 self.add_brush_instance_to_batches(
2783 key,
2784 batch_features,
2785 bounding_rect,
2786 z_id,
2787 INVALID_SEGMENT_INDEX,
2788 prim_data.edge_aa_mask,
2789 clip_task_address,
2790 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2791 prim_header_index,
2792 0,
2793 );
2794 }
2795 }
2796 }
2797 PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref visible_tiles_range, .. } => {
2798 let prim_data = &ctx.data_stores.linear_grad[data_handle];
2799 let common_data = &prim_data.common;
2800
2801 let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache);
2802
2803 let (uv_rect_address, texture_source) = match src_color {
2804 Some(src) => src,
2805 None => {
2806 return;
2807 }
2808 };
2809
2810 let textures = TextureSet::prim_textured(texture_source);
2811
2812 let prim_header = PrimitiveHeader {
2813 local_rect: prim_rect,
2814 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2815 specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
2816 transform_id,
2817 };
2818
2819 let prim_user_data = ImageBrushData {
2820 color_mode: ShaderColorMode::Image,
2821 alpha_type: AlphaType::PremultipliedAlpha,
2822 raster_space: RasterizationSpace::Local,
2823 opacity: 1.0,
2824 }.encode();
2825
2826 let blend_mode = if !common_data.opacity.is_opaque ||
2827 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2828 transform_kind == TransformedRectKind::Complex ||
2829 is_anti_aliased
2830 {
2831 BlendMode::PremultipliedAlpha
2832 } else {
2833 BlendMode::None
2834 };
2835
2836 let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
2837
2838 if visible_tiles_range.is_empty() {
2839 let batch_params = BrushBatchParameters::shared(
2840 batch_kind,
2841 textures,
2842 prim_user_data,
2843 uv_rect_address.as_int(),
2844 );
2845
2846 let segments = if prim_data.brush_segments.is_empty() {
2847 None
2848 } else {
2849 Some(&prim_data.brush_segments[..])
2850 };
2851
2852 let prim_header_index = prim_headers.push(
2853 &prim_header,
2854 z_id,
2855 self.batcher.render_task_address,
2856 batch_params.prim_user_data,
2857 );
2858
2859 self.add_segmented_prim_to_batch(
2860 segments,
2861 common_data.opacity,
2862 &batch_params,
2863 blend_mode,
2864 batch_features,
2865 brush_flags,
2866 common_data.edge_aa_mask,
2867 prim_header_index,
2868 bounding_rect,
2869 transform_kind,
2870 z_id,
2871 prim_info.clip_task_index,
2872 ctx,
2873 render_tasks,
2874 );
2875 } else {
2876 let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
2877
2878 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
2879 prim_info.clip_task_index,
2880 render_tasks,
2881 ).unwrap();
2882
2883 let batch_key = BatchKey {
2884 blend_mode,
2885 kind: BatchKind::Brush(batch_kind),
2886 textures: BatchTextures {
2887 input: textures,
2888 clip_mask,
2889 },
2890 };
2891
2892 for tile in visible_tiles {
2893 let tile_prim_header = PrimitiveHeader {
2894 local_rect: tile.local_rect,
2895 local_clip_rect: tile.local_clip_rect,
2896 ..prim_header
2897 };
2898 let prim_header_index = prim_headers.push(
2899 &tile_prim_header,
2900 z_id,
2901 self.batcher.render_task_address,
2902 prim_user_data,
2903 );
2904
2905 self.add_brush_instance_to_batches(
2906 batch_key,
2907 batch_features,
2908 bounding_rect,
2909 z_id,
2910 INVALID_SEGMENT_INDEX,
2911 prim_data.edge_aa_mask,
2912 clip_task_address,
2913 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2914 prim_header_index,
2915 uv_rect_address.as_int(),
2916 );
2917 }
2918 }
2919 }
2920 PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
2921 let prim_data = &ctx.data_stores.radial_grad[data_handle];
2922 let common_data = &prim_data.common;
2923
2924 let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache);
2925
2926 let (uv_rect_address, texture_source) = match src_color {
2927 Some(src) => src,
2928 None => {
2929 return;
2930 }
2931 };
2932
2933 let textures = TextureSet::prim_textured(texture_source);
2934
2935 let prim_header = PrimitiveHeader {
2936 local_rect: prim_rect,
2937 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2938 specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
2939 transform_id,
2940 };
2941
2942 let prim_user_data = ImageBrushData {
2943 color_mode: ShaderColorMode::Image,
2944 alpha_type: AlphaType::PremultipliedAlpha,
2945 raster_space: RasterizationSpace::Local,
2946 opacity: 1.0,
2947 }.encode();
2948
2949
2950 let blend_mode = if !common_data.opacity.is_opaque ||
2951 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
2952 transform_kind == TransformedRectKind::Complex ||
2953 is_anti_aliased
2954 {
2955 BlendMode::PremultipliedAlpha
2956 } else {
2957 BlendMode::None
2958 };
2959
2960 let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
2961
2962 if visible_tiles_range.is_empty() {
2963 let batch_params = BrushBatchParameters::shared(
2964 batch_kind,
2965 textures,
2966 prim_user_data,
2967 uv_rect_address.as_int(),
2968 );
2969
2970 let segments = if prim_data.brush_segments.is_empty() {
2971 None
2972 } else {
2973 Some(&prim_data.brush_segments[..])
2974 };
2975
2976 let prim_header_index = prim_headers.push(
2977 &prim_header,
2978 z_id,
2979 self.batcher.render_task_address,
2980 batch_params.prim_user_data,
2981 );
2982
2983 self.add_segmented_prim_to_batch(
2984 segments,
2985 common_data.opacity,
2986 &batch_params,
2987 blend_mode,
2988 batch_features,
2989 brush_flags,
2990 prim_data.edge_aa_mask,
2991 prim_header_index,
2992 bounding_rect,
2993 transform_kind,
2994 z_id,
2995 prim_info.clip_task_index,
2996 ctx,
2997 render_tasks,
2998 );
2999 } else {
3000 let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
3001
3002 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3003 prim_info.clip_task_index,
3004 render_tasks,
3005 ).unwrap();
3006
3007 let batch_key = BatchKey {
3008 blend_mode,
3009 kind: BatchKind::Brush(batch_kind),
3010 textures: BatchTextures {
3011 input: textures,
3012 clip_mask,
3013 },
3014 };
3015
3016 for tile in visible_tiles {
3017 let tile_prim_header = PrimitiveHeader {
3018 local_rect: tile.local_rect,
3019 local_clip_rect: tile.local_clip_rect,
3020 ..prim_header
3021 };
3022 let prim_header_index = prim_headers.push(
3023 &tile_prim_header,
3024 z_id,
3025 self.batcher.render_task_address,
3026 prim_user_data,
3027 );
3028
3029 self.add_brush_instance_to_batches(
3030 batch_key,
3031 batch_features,
3032 bounding_rect,
3033 z_id,
3034 INVALID_SEGMENT_INDEX,
3035 prim_data.edge_aa_mask,
3036 clip_task_address,
3037 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3038 prim_header_index,
3039 uv_rect_address.as_int(),
3040 );
3041 }
3042 }
3043
3044 }
3045 PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => {
3046 let prim_data = &ctx.data_stores.conic_grad[data_handle];
3047 let common_data = &prim_data.common;
3048
3049 let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache);
3050
3051 let (uv_rect_address, texture_source) = match src_color {
3052 Some(src) => src,
3053 None => {
3054 return;
3055 }
3056 };
3057
3058 let textures = TextureSet::prim_textured(texture_source);
3059
3060 let prim_header = PrimitiveHeader {
3061 local_rect: prim_rect,
3062 local_clip_rect: prim_info.clip_chain.local_clip_rect,
3063 specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
3064 transform_id,
3065 };
3066
3067 let prim_user_data = ImageBrushData {
3068 color_mode: ShaderColorMode::Image,
3069 alpha_type: AlphaType::PremultipliedAlpha,
3070 raster_space: RasterizationSpace::Local,
3071 opacity: 1.0,
3072 }.encode();
3073
3074
3075 let blend_mode = if !common_data.opacity.is_opaque ||
3076 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
3077 transform_kind == TransformedRectKind::Complex ||
3078 is_anti_aliased
3079 {
3080 BlendMode::PremultipliedAlpha
3081 } else {
3082 BlendMode::None
3083 };
3084
3085 let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
3086
3087 if visible_tiles_range.is_empty() {
3088 let batch_params = BrushBatchParameters::shared(
3089 batch_kind,
3090 textures,
3091 prim_user_data,
3092 uv_rect_address.as_int(),
3093 );
3094
3095 let segments = if prim_data.brush_segments.is_empty() {
3096 None
3097 } else {
3098 Some(&prim_data.brush_segments[..])
3099 };
3100
3101 let prim_header_index = prim_headers.push(
3102 &prim_header,
3103 z_id,
3104 self.batcher.render_task_address,
3105 batch_params.prim_user_data,
3106 );
3107
3108 self.add_segmented_prim_to_batch(
3109 segments,
3110 common_data.opacity,
3111 &batch_params,
3112 blend_mode,
3113 batch_features,
3114 brush_flags,
3115 prim_data.edge_aa_mask,
3116 prim_header_index,
3117 bounding_rect,
3118 transform_kind,
3119 z_id,
3120 prim_info.clip_task_index,
3121 ctx,
3122 render_tasks,
3123 );
3124 } else {
3125 let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
3126
3127 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3128 prim_info.clip_task_index,
3129 render_tasks,
3130 ).unwrap();
3131
3132 let batch_key = BatchKey {
3133 blend_mode,
3134 kind: BatchKind::Brush(batch_kind),
3135 textures: BatchTextures {
3136 input: textures,
3137 clip_mask,
3138 },
3139 };
3140
3141 for tile in visible_tiles {
3142 let tile_prim_header = PrimitiveHeader {
3143 local_rect: tile.local_rect,
3144 local_clip_rect: tile.local_clip_rect,
3145 ..prim_header
3146 };
3147 let prim_header_index = prim_headers.push(
3148 &tile_prim_header,
3149 z_id,
3150 self.batcher.render_task_address,
3151 prim_user_data,
3152 );
3153
3154 self.add_brush_instance_to_batches(
3155 batch_key,
3156 batch_features,
3157 bounding_rect,
3158 z_id,
3159 INVALID_SEGMENT_INDEX,
3160 prim_data.edge_aa_mask,
3161 clip_task_address,
3162 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3163 prim_header_index,
3164 uv_rect_address.as_int(),
3165 );
3166 }
3167 }
3168 }
3169 PrimitiveInstanceKind::BackdropCapture { .. } => {}
3170 PrimitiveInstanceKind::BackdropRender { pic_index, .. } => {
3171 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
3172 let blend_mode = BlendMode::PremultipliedAlpha;
3173 let pic_task_id = ctx.prim_store.pictures[pic_index.0].primary_render_task_id;
3174
3175 let prim_header = PrimitiveHeader {
3176 local_rect: prim_rect,
3177 local_clip_rect: prim_info.clip_chain.local_clip_rect,
3178 specific_prim_address: prim_cache_address,
3179 transform_id,
3180 };
3181
3182 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
3183 prim_info.clip_task_index,
3184 render_tasks,
3185 ).unwrap();
3186
3187 let kind = BatchKind::Brush(
3188 BrushBatchKind::Image(ImageBufferKind::Texture2D)
3189 );
3190 let (_, texture) = render_tasks.resolve_location(
3191 pic_task_id,
3192 gpu_cache,
3193 ).unwrap();
3194 let textures = BatchTextures::prim_textured(
3195 texture,
3196 clip_mask_texture_id,
3197 );
3198 let key = BatchKey::new(
3199 kind,
3200 blend_mode,
3201 textures,
3202 );
3203 let prim_header_index = prim_headers.push(
3204 &prim_header,
3205 z_id,
3206 self.batcher.render_task_address,
3207 ImageBrushData {
3208 color_mode: ShaderColorMode::Image,
3209 alpha_type: AlphaType::PremultipliedAlpha,
3210 raster_space: RasterizationSpace::Screen,
3211 opacity: 1.0,
3212 }.encode(),
3213 );
3214
3215 let pic_task = &render_tasks[pic_task_id.unwrap()];
3216 let pic_info = match pic_task.kind {
3217 RenderTaskKind::Picture(ref info) => info,
3218 _ => panic!("bug: not a picture"),
3219 };
3220 let target_rect = pic_task.get_target_rect();
3221
3222 let backdrop_rect = DeviceRect::from_origin_and_size(
3223 pic_info.content_origin,
3224 target_rect.size().to_f32(),
3225 );
3226
3227 let map_prim_to_backdrop = SpaceMapper::new_with_target(
3228 pic_info.surface_spatial_node_index,
3229 prim_spatial_node_index,
3230 WorldRect::max_rect(),
3231 ctx.spatial_tree,
3232 );
3233
3234 let points = [
3235 map_prim_to_backdrop.map_point(prim_rect.top_left()),
3236 map_prim_to_backdrop.map_point(prim_rect.top_right()),
3237 map_prim_to_backdrop.map_point(prim_rect.bottom_left()),
3238 map_prim_to_backdrop.map_point(prim_rect.bottom_right()),
3239 ];
3240
3241 if points.iter().any(|p| p.is_none()) {
3242 return;
3243 }
3244
3245 let uvs = [
3246 calculate_screen_uv(points[0].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3247 calculate_screen_uv(points[1].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3248 calculate_screen_uv(points[2].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3249 calculate_screen_uv(points[3].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
3250 ];
3251
3252 let gpu_blocks = &[
3256 GpuBlockData::from([
3257 target_rect.min.x as f32,
3258 target_rect.min.y as f32,
3259 target_rect.max.x as f32,
3260 target_rect.max.y as f32,
3261 ]),
3262 GpuBlockData::from([0.0; 4]),
3263 GpuBlockData::from(uvs[0]),
3264 GpuBlockData::from(uvs[1]),
3265 GpuBlockData::from(uvs[2]),
3266 GpuBlockData::from(uvs[3]),
3267 ];
3268 let uv_rect_handle = gpu_cache.push_per_frame_blocks(gpu_blocks);
3269
3270 self.add_brush_instance_to_batches(
3271 key,
3272 batch_features,
3273 bounding_rect,
3274 z_id,
3275 INVALID_SEGMENT_INDEX,
3276 EdgeAaSegmentMask::all(),
3277 clip_task_address,
3278 brush_flags,
3279 prim_header_index,
3280 uv_rect_handle.as_int(gpu_cache),
3281 );
3282 }
3283 }
3284 }
3285
3286 fn add_compositor_surface_cutout(
3289 &mut self,
3290 prim_rect: LayoutRect,
3291 local_clip_rect: LayoutRect,
3292 clip_task_index: ClipTaskIndex,
3293 transform_id: TransformPaletteId,
3294 z_id: ZBufferId,
3295 bounding_rect: &PictureRect,
3296 ctx: &RenderTargetContext,
3297 gpu_cache: &mut GpuCache,
3298 render_tasks: &RenderTaskGraph,
3299 prim_headers: &mut PrimitiveHeaders,
3300 ) {
3301 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_black_rect_handle);
3302
3303 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
3304 clip_task_index,
3305 render_tasks,
3306 ).unwrap();
3307
3308 let prim_header = PrimitiveHeader {
3309 local_rect: prim_rect,
3310 local_clip_rect,
3311 specific_prim_address: prim_cache_address,
3312 transform_id,
3313 };
3314
3315 let prim_header_index = prim_headers.push(
3316 &prim_header,
3317 z_id,
3318 self.batcher.render_task_address,
3319 [get_shader_opacity(1.0), 0, 0, 0],
3320 );
3321
3322 let batch_key = BatchKey {
3323 blend_mode: BlendMode::PremultipliedDestOut,
3324 kind: BatchKind::Brush(BrushBatchKind::Solid),
3325 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
3326 };
3327
3328 self.add_brush_instance_to_batches(
3329 batch_key,
3330 BatchFeatures::ALPHA_PASS | BatchFeatures::CLIP_MASK,
3331 bounding_rect,
3332 z_id,
3333 INVALID_SEGMENT_INDEX,
3334 EdgeAaSegmentMask::empty(),
3335 clip_task_address,
3336 BrushFlags::empty(),
3337 prim_header_index,
3338 0,
3339 );
3340 }
3341
3342 fn add_segment_to_batch(
3349 &mut self,
3350 segment: &BrushSegment,
3351 segment_data: &SegmentInstanceData,
3352 segment_index: i32,
3353 batch_kind: BrushBatchKind,
3354 prim_header_index: PrimitiveHeaderIndex,
3355 alpha_blend_mode: BlendMode,
3356 features: BatchFeatures,
3357 brush_flags: BrushFlags,
3358 edge_aa_mask: EdgeAaSegmentMask,
3359 bounding_rect: &PictureRect,
3360 transform_kind: TransformedRectKind,
3361 z_id: ZBufferId,
3362 prim_opacity: PrimitiveOpacity,
3363 clip_task_index: ClipTaskIndex,
3364 ctx: &RenderTargetContext,
3365 render_tasks: &RenderTaskGraph,
3366 ) {
3367 debug_assert!(clip_task_index != ClipTaskIndex::INVALID);
3368
3369 if let Some((clip_task_address, clip_mask)) = ctx.get_clip_task_and_texture(
3372 clip_task_index,
3373 segment_index,
3374 render_tasks,
3375 ) {
3376 let is_inner = segment.edge_flags.is_empty();
3378 let needs_blending = !prim_opacity.is_opaque ||
3379 clip_task_address != OPAQUE_TASK_ADDRESS ||
3380 (!is_inner && transform_kind == TransformedRectKind::Complex) ||
3381 brush_flags.contains(BrushFlags::FORCE_AA);
3382
3383 let textures = BatchTextures {
3384 input: segment_data.textures,
3385 clip_mask,
3386 };
3387
3388 let batch_key = BatchKey {
3389 blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
3390 kind: BatchKind::Brush(batch_kind),
3391 textures,
3392 };
3393
3394 self.add_brush_instance_to_batches(
3395 batch_key,
3396 features,
3397 bounding_rect,
3398 z_id,
3399 segment_index,
3400 segment.edge_flags & edge_aa_mask,
3401 clip_task_address,
3402 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags,
3403 prim_header_index,
3404 segment_data.specific_resource_address,
3405 );
3406 }
3407 }
3408
3409 fn add_segmented_prim_to_batch(
3416 &mut self,
3417 brush_segments: Option<&[BrushSegment]>,
3418 prim_opacity: PrimitiveOpacity,
3419 params: &BrushBatchParameters,
3420 blend_mode: BlendMode,
3421 features: BatchFeatures,
3422 brush_flags: BrushFlags,
3423 edge_aa_mask: EdgeAaSegmentMask,
3424 prim_header_index: PrimitiveHeaderIndex,
3425 bounding_rect: &PictureRect,
3426 transform_kind: TransformedRectKind,
3427 z_id: ZBufferId,
3428 clip_task_index: ClipTaskIndex,
3429 ctx: &RenderTargetContext,
3430 render_tasks: &RenderTaskGraph,
3431 ) {
3432 match (brush_segments, ¶ms.segment_data) {
3433 (Some(ref brush_segments), SegmentDataKind::Instanced(ref segment_data)) => {
3434 debug_assert_eq!(brush_segments.len(), segment_data.len());
3437 for (segment_index, (segment, segment_data)) in brush_segments
3438 .iter()
3439 .zip(segment_data.iter())
3440 .enumerate()
3441 {
3442 self.add_segment_to_batch(
3443 segment,
3444 segment_data,
3445 segment_index as i32,
3446 params.batch_kind,
3447 prim_header_index,
3448 blend_mode,
3449 features,
3450 brush_flags,
3451 edge_aa_mask,
3452 bounding_rect,
3453 transform_kind,
3454 z_id,
3455 prim_opacity,
3456 clip_task_index,
3457 ctx,
3458 render_tasks,
3459 );
3460 }
3461 }
3462 (Some(ref brush_segments), SegmentDataKind::Shared(ref segment_data)) => {
3463 for (segment_index, segment) in brush_segments
3466 .iter()
3467 .enumerate()
3468 {
3469 self.add_segment_to_batch(
3470 segment,
3471 segment_data,
3472 segment_index as i32,
3473 params.batch_kind,
3474 prim_header_index,
3475 blend_mode,
3476 features,
3477 brush_flags,
3478 edge_aa_mask,
3479 bounding_rect,
3480 transform_kind,
3481 z_id,
3482 prim_opacity,
3483 clip_task_index,
3484 ctx,
3485 render_tasks,
3486 );
3487 }
3488 }
3489 (None, SegmentDataKind::Shared(ref segment_data)) => {
3490 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3494 clip_task_index,
3495 render_tasks,
3496 ).unwrap();
3497
3498 let textures = BatchTextures {
3499 input: segment_data.textures,
3500 clip_mask,
3501 };
3502
3503 let batch_key = BatchKey {
3504 blend_mode,
3505 kind: BatchKind::Brush(params.batch_kind),
3506 textures,
3507 };
3508
3509 self.add_brush_instance_to_batches(
3510 batch_key,
3511 features,
3512 bounding_rect,
3513 z_id,
3514 INVALID_SEGMENT_INDEX,
3515 edge_aa_mask,
3516 clip_task_address,
3517 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3518 prim_header_index,
3519 segment_data.specific_resource_address,
3520 );
3521 }
3522 (None, SegmentDataKind::Instanced(..)) => {
3523 unreachable!();
3526 }
3527 }
3528 }
3529}
3530
3531enum SegmentDataKind {
3534 Shared(SegmentInstanceData),
3535 Instanced(SmallVec<[SegmentInstanceData; 8]>),
3536}
3537
3538struct BrushBatchParameters {
3541 batch_kind: BrushBatchKind,
3542 prim_user_data: [i32; 4],
3543 segment_data: SegmentDataKind,
3544}
3545
3546impl BrushBatchParameters {
3547 fn instanced(
3550 batch_kind: BrushBatchKind,
3551 prim_user_data: [i32; 4],
3552 segment_data: SmallVec<[SegmentInstanceData; 8]>,
3553 ) -> Self {
3554 BrushBatchParameters {
3555 batch_kind,
3556 prim_user_data,
3557 segment_data: SegmentDataKind::Instanced(segment_data),
3558 }
3559 }
3560
3561 fn shared(
3564 batch_kind: BrushBatchKind,
3565 textures: TextureSet,
3566 prim_user_data: [i32; 4],
3567 specific_resource_address: i32,
3568 ) -> Self {
3569 BrushBatchParameters {
3570 batch_kind,
3571 prim_user_data,
3572 segment_data: SegmentDataKind::Shared(
3573 SegmentInstanceData {
3574 textures,
3575 specific_resource_address,
3576 }
3577 ),
3578 }
3579 }
3580}
3581
3582#[cfg_attr(feature = "capture", derive(Serialize))]
3584#[cfg_attr(feature = "replay", derive(Deserialize))]
3585pub struct ClipMaskInstanceList {
3586 pub mask_instances_fast: FrameVec<MaskInstance>,
3587 pub mask_instances_slow: FrameVec<MaskInstance>,
3588
3589 pub mask_instances_fast_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3590 pub mask_instances_slow_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3591
3592 pub image_mask_instances: FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>,
3593 pub image_mask_instances_with_scissor: FastHashMap<(DeviceIntRect, TextureSource), FrameVec<PrimitiveInstanceData>>,
3594}
3595
3596impl ClipMaskInstanceList {
3597 pub fn new(memory: &FrameMemory) -> Self {
3598 ClipMaskInstanceList {
3599 mask_instances_fast: memory.new_vec(),
3600 mask_instances_slow: memory.new_vec(),
3601 mask_instances_fast_with_scissor: FastHashMap::default(),
3602 mask_instances_slow_with_scissor: FastHashMap::default(),
3603 image_mask_instances: FastHashMap::default(),
3604 image_mask_instances_with_scissor: FastHashMap::default(),
3605 }
3606 }
3607}
3608
3609#[derive(Debug)]
3611#[cfg_attr(feature = "capture", derive(Serialize))]
3612#[cfg_attr(feature = "replay", derive(Deserialize))]
3613pub struct ClipBatchList {
3614 pub slow_rectangles: FrameVec<ClipMaskInstanceRect>,
3616 pub fast_rectangles: FrameVec<ClipMaskInstanceRect>,
3617 pub box_shadows: FastHashMap<TextureSource, FrameVec<ClipMaskInstanceBoxShadow>>,
3618}
3619
3620impl ClipBatchList {
3621 fn new(memory: &FrameMemory) -> Self {
3622 ClipBatchList {
3623 slow_rectangles: memory.new_vec(),
3624 fast_rectangles: memory.new_vec(),
3625 box_shadows: FastHashMap::default(),
3626 }
3627 }
3628
3629 pub fn is_empty(&self) -> bool {
3630 self.slow_rectangles.is_empty()
3631 && self.fast_rectangles.is_empty()
3632 && self.box_shadows.is_empty()
3633 }
3634}
3635
3636#[derive(Debug)]
3638#[cfg_attr(feature = "capture", derive(Serialize))]
3639#[cfg_attr(feature = "replay", derive(Deserialize))]
3640pub struct ClipBatcher {
3641 pub primary_clips: ClipBatchList,
3645 pub secondary_clips: ClipBatchList,
3648
3649 gpu_supports_fast_clears: bool,
3650}
3651
3652impl ClipBatcher {
3653 pub fn new(
3654 gpu_supports_fast_clears: bool,
3655 memory: &FrameMemory,
3656 ) -> Self {
3657 ClipBatcher {
3658 primary_clips: ClipBatchList::new(memory),
3659 secondary_clips: ClipBatchList::new(memory),
3660 gpu_supports_fast_clears,
3661 }
3662 }
3663
3664 pub fn add_clip_region(
3665 &mut self,
3666 local_pos: LayoutPoint,
3667 sub_rect: DeviceRect,
3668 clip_data: ClipData,
3669 task_origin: DevicePoint,
3670 screen_origin: DevicePoint,
3671 device_pixel_scale: f32,
3672 ) {
3673 let instance = ClipMaskInstanceRect {
3674 common: ClipMaskInstanceCommon {
3675 clip_transform_id: TransformPaletteId::IDENTITY,
3676 prim_transform_id: TransformPaletteId::IDENTITY,
3677 sub_rect,
3678 task_origin,
3679 screen_origin,
3680 device_pixel_scale,
3681 },
3682 local_pos,
3683 clip_data,
3684 };
3685
3686 self.primary_clips.slow_rectangles.push(instance);
3687 }
3688
3689 fn add_tiled_clip_mask(
3692 &mut self,
3693 mask_screen_rect: DeviceRect,
3694 local_clip_rect: LayoutRect,
3695 clip_spatial_node_index: SpatialNodeIndex,
3696 spatial_tree: &SpatialTree,
3697 world_rect: &WorldRect,
3698 global_device_pixel_scale: DevicePixelScale,
3699 common: &ClipMaskInstanceCommon,
3700 is_first_clip: bool,
3701 ) -> bool {
3702 if mask_screen_rect.area() < CLIP_RECTANGLE_AREA_THRESHOLD {
3704 return false;
3705 }
3706
3707 let mask_screen_rect_size = mask_screen_rect.size().to_i32();
3708 let clip_spatial_node = spatial_tree.get_spatial_node(clip_spatial_node_index);
3709
3710 if clip_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
3714 return false;
3715 }
3716
3717 let transform = spatial_tree.get_world_transform(
3720 clip_spatial_node_index,
3721 );
3722 let world_clip_rect = match project_rect(
3723 &transform.into_transform(),
3724 &local_clip_rect,
3725 &world_rect,
3726 ) {
3727 Some(rect) => rect,
3728 None => return false,
3729 };
3730
3731 let world_device_rect = world_clip_rect * global_device_pixel_scale;
3734 let x_tiles = (mask_screen_rect_size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3735 let y_tiles = (mask_screen_rect_size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3736
3737 let mask_origin = mask_screen_rect.min.to_vector();
3741 let clip_list = self.get_batch_list(is_first_clip);
3742
3743 for y in 0 .. y_tiles {
3744 for x in 0 .. x_tiles {
3745 let p0 = DeviceIntPoint::new(
3746 x * CLIP_RECTANGLE_TILE_SIZE,
3747 y * CLIP_RECTANGLE_TILE_SIZE,
3748 );
3749 let p1 = DeviceIntPoint::new(
3750 (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.width),
3751 (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.height),
3752 );
3753 let normalized_sub_rect = DeviceIntRect {
3754 min: p0,
3755 max: p1,
3756 }.to_f32();
3757 let world_sub_rect = normalized_sub_rect.translate(mask_origin);
3758
3759 if !world_device_rect.contains_box(&world_sub_rect) {
3763 clip_list.slow_rectangles.push(ClipMaskInstanceRect {
3764 common: ClipMaskInstanceCommon {
3765 sub_rect: normalized_sub_rect,
3766 ..*common
3767 },
3768 local_pos: local_clip_rect.min,
3769 clip_data: ClipData::uniform(local_clip_rect.size(), 0.0, ClipMode::Clip),
3770 });
3771 }
3772 }
3773 }
3774
3775 true
3776 }
3777
3778 fn get_batch_list(
3781 &mut self,
3782 is_first_clip: bool,
3783 ) -> &mut ClipBatchList {
3784 if is_first_clip && !self.gpu_supports_fast_clears {
3785 &mut self.primary_clips
3786 } else {
3787 &mut self.secondary_clips
3788 }
3789 }
3790
3791 pub fn add(
3792 &mut self,
3793 clip_node_range: ClipNodeRange,
3794 root_spatial_node_index: SpatialNodeIndex,
3795 render_tasks: &RenderTaskGraph,
3796 gpu_cache: &GpuCache,
3797 clip_store: &ClipStore,
3798 transforms: &mut TransformPalette,
3799 actual_rect: DeviceRect,
3800 surface_device_pixel_scale: DevicePixelScale,
3801 task_origin: DevicePoint,
3802 screen_origin: DevicePoint,
3803 ctx: &RenderTargetContext,
3804 ) -> bool {
3805 let mut is_first_clip = true;
3806 let mut clear_to_one = false;
3807
3808 for i in 0 .. clip_node_range.count {
3809 let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
3810 let clip_node = &ctx.data_stores.clip[clip_instance.handle];
3811
3812 let clip_transform_id = transforms.get_id(
3813 clip_node.item.spatial_node_index,
3814 ctx.root_spatial_node_index,
3815 ctx.spatial_tree,
3816 );
3817
3818 let prim_transform_id = transforms.get_id(
3819 root_spatial_node_index,
3820 ctx.root_spatial_node_index,
3821 ctx.spatial_tree,
3822 );
3823
3824 let common = ClipMaskInstanceCommon {
3825 sub_rect: DeviceRect::from_size(actual_rect.size()),
3826 task_origin,
3827 screen_origin,
3828 device_pixel_scale: surface_device_pixel_scale.0,
3829 clip_transform_id,
3830 prim_transform_id,
3831 };
3832
3833 let added_clip = match clip_node.item.kind {
3834 ClipItemKind::Image { .. } => {
3835 unreachable!();
3836 }
3837 ClipItemKind::BoxShadow { ref source } => {
3838 let task_id = source
3839 .render_task
3840 .expect("bug: render task handle not allocated");
3841 let (uv_rect_address, texture) = render_tasks.resolve_location(task_id, gpu_cache).unwrap();
3842
3843 self.get_batch_list(is_first_clip)
3844 .box_shadows
3845 .entry(texture)
3846 .or_insert_with(|| ctx.frame_memory.new_vec())
3847 .push(ClipMaskInstanceBoxShadow {
3848 common,
3849 resource_address: uv_rect_address,
3850 shadow_data: BoxShadowData {
3851 src_rect_size: source.original_alloc_size,
3852 clip_mode: source.clip_mode as i32,
3853 stretch_mode_x: source.stretch_mode_x as i32,
3854 stretch_mode_y: source.stretch_mode_y as i32,
3855 dest_rect: source.prim_shadow_rect,
3856 },
3857 });
3858
3859 true
3860 }
3861 ClipItemKind::Rectangle { rect, mode: ClipMode::ClipOut } => {
3862 self.get_batch_list(is_first_clip)
3863 .slow_rectangles
3864 .push(ClipMaskInstanceRect {
3865 common,
3866 local_pos: rect.min,
3867 clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::ClipOut),
3868 });
3869
3870 true
3871 }
3872 ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => {
3873 if clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
3874 false
3875 } else {
3876 if self.add_tiled_clip_mask(
3877 actual_rect,
3878 rect,
3879 clip_node.item.spatial_node_index,
3880 ctx.spatial_tree,
3881 &ctx.screen_world_rect,
3882 ctx.global_device_pixel_scale,
3883 &common,
3884 is_first_clip,
3885 ) {
3886 clear_to_one |= is_first_clip;
3887 } else {
3888 self.get_batch_list(is_first_clip)
3889 .slow_rectangles
3890 .push(ClipMaskInstanceRect {
3891 common,
3892 local_pos: rect.min,
3893 clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::Clip),
3894 });
3895 }
3896
3897 true
3898 }
3899 }
3900 ClipItemKind::RoundedRectangle { rect, ref radius, mode, .. } => {
3901 let batch_list = self.get_batch_list(is_first_clip);
3902 let instance = ClipMaskInstanceRect {
3903 common,
3904 local_pos: rect.min,
3905 clip_data: ClipData::rounded_rect(rect.size(), radius, mode),
3906 };
3907 if clip_instance.flags.contains(ClipNodeFlags::USE_FAST_PATH) {
3908 batch_list.fast_rectangles.push(instance);
3909 } else {
3910 batch_list.slow_rectangles.push(instance);
3911 }
3912
3913 true
3914 }
3915 };
3916
3917 is_first_clip &= !added_clip;
3918 }
3919
3920 clear_to_one
3921 }
3922}
3923
3924impl<'a, 'rc> RenderTargetContext<'a, 'rc> {
3925 fn get_clip_task_and_texture(
3930 &self,
3931 clip_task_index: ClipTaskIndex,
3932 offset: i32,
3933 render_tasks: &RenderTaskGraph,
3934 ) -> Option<(RenderTaskAddress, TextureSource)> {
3935 match self.scratch.clip_mask_instances[clip_task_index.0 as usize + offset as usize] {
3936 ClipMaskKind::Mask(task_id) => {
3937 Some((
3938 task_id.into(),
3939 TextureSource::TextureCache(
3940 render_tasks[task_id].get_target_texture(),
3941 Swizzle::default(),
3942 )
3943 ))
3944 }
3945 ClipMaskKind::None => {
3946 Some((OPAQUE_TASK_ADDRESS, TextureSource::Invalid))
3947 }
3948 ClipMaskKind::Clipped => {
3949 None
3950 }
3951 }
3952 }
3953
3954 fn get_prim_clip_task_and_texture(
3957 &self,
3958 clip_task_index: ClipTaskIndex,
3959 render_tasks: &RenderTaskGraph,
3960 ) -> Option<(RenderTaskAddress, TextureSource)> {
3961 self.get_clip_task_and_texture(
3962 clip_task_index,
3963 0,
3964 render_tasks,
3965 )
3966 }
3967}
3968
3969impl CompositorSurfaceKind {
3970 fn needs_cutout(&self) -> bool {
3972 match self {
3973 CompositorSurfaceKind::Underlay => true,
3974 CompositorSurfaceKind::Overlay | CompositorSurfaceKind::Blit => false,
3975 }
3976 }
3977}