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 if let PrimitiveInstanceKind::Picture { pic_index, .. } = prim_instance.kind {
987 let picture = &ctx.prim_store.pictures[pic_index.0];
988 if let Some(snapshot) = picture.snapshot {
989 if snapshot.detached {
990 return;
991 }
992 }
993
994 let blend_mode = BlendMode::PremultipliedAlpha;
995 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
996
997 match picture.raster_config {
998 Some(ref raster_config) => {
999 let brush_flags = brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION;
1002
1003 let surface = &ctx.surfaces[raster_config.surface_index.0];
1004 let mut local_clip_rect = prim_info.clip_chain.local_clip_rect;
1005
1006 let transform_id = if surface.surface_spatial_node_index == surface.raster_spatial_node_index {
1011 transform_id
1012 } else {
1013 let map_local_to_raster = SpaceMapper::new_with_target(
1014 root_spatial_node_index,
1015 surface.surface_spatial_node_index,
1016 LayoutRect::max_rect(),
1017 ctx.spatial_tree,
1018 );
1019
1020 let raster_rect = map_local_to_raster
1021 .map(&prim_rect)
1022 .unwrap();
1023
1024 let sx = (raster_rect.max.x - raster_rect.min.x) / (prim_rect.max.x - prim_rect.min.x);
1025 let sy = (raster_rect.max.y - raster_rect.min.y) / (prim_rect.max.y - prim_rect.min.y);
1026
1027 let tx = raster_rect.min.x - sx * prim_rect.min.x;
1028 let ty = raster_rect.min.y - sy * prim_rect.min.y;
1029
1030 let transform = ScaleOffset::new(sx, sy, tx, ty);
1031
1032 let raster_clip_rect = map_local_to_raster
1033 .map(&prim_info.clip_chain.local_clip_rect)
1034 .unwrap();
1035 local_clip_rect = transform.unmap_rect(&raster_clip_rect);
1036
1037 transforms.get_custom(transform.to_transform())
1038 };
1039
1040 let prim_header = PrimitiveHeader {
1041 local_rect: prim_rect,
1042 local_clip_rect,
1043 specific_prim_address: prim_cache_address,
1044 transform_id,
1045 };
1046
1047 let mut is_opaque = prim_info.clip_task_index == ClipTaskIndex::INVALID
1048 && surface.is_opaque
1049 && transform_kind == TransformedRectKind::AxisAligned
1050 && !is_anti_aliased;
1051
1052 let pic_task_id = picture.primary_render_task_id.unwrap();
1053
1054 let pic_task = &render_tasks[pic_task_id];
1055 match pic_task.sub_pass {
1056 Some(SubPass::Masks { .. }) => {
1057 is_opaque = false;
1058 }
1059 None => {}
1060 }
1061 match raster_config.composite_mode {
1062 PictureCompositeMode::TileCache { .. } => {
1063 return;
1068 }
1069 PictureCompositeMode::IntermediateSurface { .. } => {
1070 return;
1074 }
1075 _=>{}
1076 }
1077
1078 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1079 prim_info.clip_task_index,
1080 render_tasks,
1081 ).unwrap();
1082
1083 let (uv_rect_address, texture) = render_tasks.resolve_location(
1084 pic_task_id,
1085 gpu_cache,
1086 ).unwrap();
1087
1088 let textures = BatchTextures::prim_textured(
1091 texture,
1092 clip_mask_texture_id,
1093 );
1094
1095 match raster_config.composite_mode {
1096 PictureCompositeMode::TileCache { .. } => {}
1097 PictureCompositeMode::IntermediateSurface { .. } => {}
1098 PictureCompositeMode::Filter(ref filter) => {
1099 assert!(filter.is_visible());
1100 match filter {
1101 Filter::Blur { .. } => {
1102 let kind = BatchKind::Brush(
1103 BrushBatchKind::Image(ImageBufferKind::Texture2D)
1104 );
1105
1106 let key = BatchKey::new(
1107 kind,
1108 blend_mode,
1109 textures,
1110 );
1111 let prim_header_index = prim_headers.push(
1112 &prim_header,
1113 z_id,
1114 self.batcher.render_task_address,
1115 ImageBrushData {
1116 color_mode: ShaderColorMode::Image,
1117 alpha_type: AlphaType::PremultipliedAlpha,
1118 raster_space: RasterizationSpace::Screen,
1119 opacity: 1.0,
1120 }.encode(),
1121 );
1122
1123 self.add_brush_instance_to_batches(
1124 key,
1125 batch_features,
1126 bounding_rect,
1127 z_id,
1128 INVALID_SEGMENT_INDEX,
1129 EdgeAaSegmentMask::all(),
1130 clip_task_address,
1131 brush_flags,
1132 prim_header_index,
1133 uv_rect_address.as_int(),
1134 );
1135 }
1136 Filter::DropShadows(shadows) => {
1137 let kind = BatchKind::Brush(
1141 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1142 );
1143
1144 let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
1147 let content_source = {
1148 let secondary_task = &render_tasks[secondary_id];
1149 let texture_id = secondary_task.get_target_texture();
1150 TextureSource::TextureCache(
1151 texture_id,
1152 Swizzle::default(),
1153 )
1154 };
1155
1156 let shadow_uv_rect_address = uv_rect_address;
1158 let shadow_textures = textures;
1159
1160 let content_uv_rect_address = render_tasks[secondary_id]
1161 .get_texture_address(gpu_cache)
1162 .as_int();
1163
1164 let content_textures = BatchTextures::prim_textured(
1166 content_source,
1167 clip_mask_texture_id,
1168 );
1169
1170 let shadow_key = BatchKey::new(kind, blend_mode, shadow_textures);
1172 let content_key = BatchKey::new(kind, blend_mode, content_textures);
1173
1174 for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) {
1175 let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data);
1177
1178 let shadow_rect = prim_header.local_rect.translate(shadow.offset);
1179
1180 let shadow_prim_header = PrimitiveHeader {
1181 local_rect: shadow_rect,
1182 specific_prim_address: shadow_prim_address,
1183 ..prim_header
1184 };
1185
1186 let shadow_prim_header_index = prim_headers.push(
1187 &shadow_prim_header,
1188 z_id,
1189 self.batcher.render_task_address,
1190 ImageBrushData {
1191 color_mode: ShaderColorMode::Alpha,
1192 alpha_type: AlphaType::PremultipliedAlpha,
1193 raster_space: RasterizationSpace::Screen,
1194 opacity: 1.0,
1195 }.encode(),
1196 );
1197
1198 self.add_brush_instance_to_batches(
1199 shadow_key,
1200 batch_features,
1201 bounding_rect,
1202 z_id,
1203 INVALID_SEGMENT_INDEX,
1204 EdgeAaSegmentMask::all(),
1205 clip_task_address,
1206 brush_flags,
1207 shadow_prim_header_index,
1208 shadow_uv_rect_address.as_int(),
1209 );
1210 }
1211 let z_id_content = z_generator.next();
1212
1213 let content_prim_header_index = prim_headers.push(
1214 &prim_header,
1215 z_id_content,
1216 self.batcher.render_task_address,
1217 ImageBrushData {
1218 color_mode: ShaderColorMode::Image,
1219 alpha_type: AlphaType::PremultipliedAlpha,
1220 raster_space: RasterizationSpace::Screen,
1221 opacity: 1.0,
1222 }.encode(),
1223 );
1224
1225 self.add_brush_instance_to_batches(
1226 content_key,
1227 batch_features,
1228 bounding_rect,
1229 z_id_content,
1230 INVALID_SEGMENT_INDEX,
1231 EdgeAaSegmentMask::all(),
1232 clip_task_address,
1233 brush_flags,
1234 content_prim_header_index,
1235 content_uv_rect_address,
1236 );
1237 }
1238 Filter::Opacity(_, amount) => {
1239 let amount = (amount * 65536.0) as i32;
1240
1241 let key = BatchKey::new(
1242 BatchKind::Brush(BrushBatchKind::Opacity),
1243 BlendMode::PremultipliedAlpha,
1244 textures,
1245 );
1246
1247 let prim_header_index = prim_headers.push(
1248 &prim_header,
1249 z_id,
1250 self.batcher.render_task_address,
1251 [
1252 uv_rect_address.as_int(),
1253 amount,
1254 0,
1255 0,
1256 ]
1257 );
1258
1259 self.add_brush_instance_to_batches(
1260 key,
1261 batch_features,
1262 bounding_rect,
1263 z_id,
1264 INVALID_SEGMENT_INDEX,
1265 EdgeAaSegmentMask::all(),
1266 clip_task_address,
1267 brush_flags,
1268 prim_header_index,
1269 0,
1270 );
1271 }
1272 _ => {
1273 let filter_mode = filter.as_int();
1275
1276 let user_data = match filter {
1277 Filter::Identity => 0x10000i32, Filter::Contrast(amount) |
1279 Filter::Grayscale(amount) |
1280 Filter::Invert(amount) |
1281 Filter::Saturate(amount) |
1282 Filter::Sepia(amount) |
1283 Filter::Brightness(amount) => {
1284 (amount * 65536.0) as i32
1285 }
1286 Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
1287 Filter::HueRotate(angle) => {
1288 (0.01745329251 * angle * 65536.0) as i32
1289 }
1290 Filter::ColorMatrix(_) => {
1291 picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1292 }
1293 Filter::Flood(_) => {
1294 picture.extra_gpu_data_handles[0].as_int(gpu_cache)
1295 }
1296
1297 Filter::ComponentTransfer |
1299 Filter::Blur { .. } |
1300 Filter::DropShadows(..) |
1301 Filter::Opacity(..) |
1302 Filter::SVGGraphNode(..) => unreachable!(),
1303 };
1304
1305 if let Filter::ColorMatrix(..) = filter {
1308 is_opaque = false;
1309 }
1310
1311 let blend_mode = if is_opaque {
1312 BlendMode::None
1313 } else {
1314 BlendMode::PremultipliedAlpha
1315 };
1316
1317 let key = BatchKey::new(
1318 BatchKind::Brush(BrushBatchKind::Blend),
1319 blend_mode,
1320 textures,
1321 );
1322
1323 let prim_header_index = prim_headers.push(
1324 &prim_header,
1325 z_id,
1326 self.batcher.render_task_address,
1327 [
1328 uv_rect_address.as_int(),
1329 filter_mode,
1330 user_data,
1331 0,
1332 ]
1333 );
1334
1335 self.add_brush_instance_to_batches(
1336 key,
1337 batch_features,
1338 bounding_rect,
1339 z_id,
1340 INVALID_SEGMENT_INDEX,
1341 EdgeAaSegmentMask::all(),
1342 clip_task_address,
1343 brush_flags,
1344 prim_header_index,
1345 0,
1346 );
1347 }
1348 }
1349 }
1350 PictureCompositeMode::ComponentTransferFilter(handle) => {
1351 let filter_data = &ctx.data_stores.filter_data[handle];
1355 let filter_mode : i32 = Filter::ComponentTransfer.as_int() |
1356 ((filter_data.data.r_func.to_int() << 28 |
1357 filter_data.data.g_func.to_int() << 24 |
1358 filter_data.data.b_func.to_int() << 20 |
1359 filter_data.data.a_func.to_int() << 16) as i32);
1360
1361 let user_data = filter_data.gpu_cache_handle.as_int(gpu_cache);
1362
1363 let key = BatchKey::new(
1364 BatchKind::Brush(BrushBatchKind::Blend),
1365 BlendMode::PremultipliedAlpha,
1366 textures,
1367 );
1368
1369 let prim_header_index = prim_headers.push(
1370 &prim_header,
1371 z_id,
1372 self.batcher.render_task_address,
1373 [
1374 uv_rect_address.as_int(),
1375 filter_mode,
1376 user_data,
1377 0,
1378 ]
1379 );
1380
1381 self.add_brush_instance_to_batches(
1382 key,
1383 batch_features,
1384 bounding_rect,
1385 z_id,
1386 INVALID_SEGMENT_INDEX,
1387 EdgeAaSegmentMask::all(),
1388 clip_task_address,
1389 brush_flags,
1390 prim_header_index,
1391 0,
1392 );
1393 }
1394 PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode(
1395 mode,
1396 ctx.use_advanced_blending,
1397 !ctx.break_advanced_blend_batches,
1398 ctx.use_dual_source_blending,
1399 ).is_some() => {
1400 let key = BatchKey::new(
1401 BatchKind::Brush(
1402 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1403 ),
1404 BlendMode::from_mix_blend_mode(
1405 mode,
1406 ctx.use_advanced_blending,
1407 !ctx.break_advanced_blend_batches,
1408 ctx.use_dual_source_blending,
1409 ).unwrap(),
1410 textures,
1411 );
1412 let prim_header_index = prim_headers.push(
1413 &prim_header,
1414 z_id,
1415 self.batcher.render_task_address,
1416 ImageBrushData {
1417 color_mode: match key.blend_mode {
1418 BlendMode::MultiplyDualSource => ShaderColorMode::MultiplyDualSource,
1419 _ => ShaderColorMode::Image,
1420 },
1421 alpha_type: AlphaType::PremultipliedAlpha,
1422 raster_space: RasterizationSpace::Screen,
1423 opacity: 1.0,
1424 }.encode(),
1425 );
1426
1427 self.add_brush_instance_to_batches(
1428 key,
1429 batch_features,
1430 bounding_rect,
1431 z_id,
1432 INVALID_SEGMENT_INDEX,
1433 EdgeAaSegmentMask::all(),
1434 clip_task_address,
1435 brush_flags,
1436 prim_header_index,
1437 uv_rect_address.as_int(),
1438 );
1439 }
1440 PictureCompositeMode::MixBlend(mode) => {
1441 let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");
1442
1443 let color0 = render_tasks[backdrop_id].get_target_texture();
1444 let color1 = render_tasks[pic_task_id].get_target_texture();
1445
1446 let batch_key = BatchKey::new(
1455 BatchKind::Brush(
1456 BrushBatchKind::MixBlend {
1457 task_id: self.batcher.render_task_id,
1458 backdrop_id,
1459 },
1460 ),
1461 BlendMode::PremultipliedAlpha,
1462 BatchTextures {
1463 input: TextureSet {
1464 colors: [
1465 TextureSource::TextureCache(
1466 color0,
1467 Swizzle::default(),
1468 ),
1469 TextureSource::TextureCache(
1470 color1,
1471 Swizzle::default(),
1472 ),
1473 TextureSource::Invalid,
1474 ],
1475 },
1476 clip_mask: clip_mask_texture_id,
1477 },
1478 );
1479 let src_uv_address = render_tasks[pic_task_id].get_texture_address(gpu_cache);
1480 let readback_uv_address = render_tasks[backdrop_id].get_texture_address(gpu_cache);
1481 let prim_header_index = prim_headers.push(
1482 &prim_header,
1483 z_id,
1484 self.batcher.render_task_address,
1485 [
1486 mode as u32 as i32,
1487 readback_uv_address.as_int(),
1488 src_uv_address.as_int(),
1489 0,
1490 ]
1491 );
1492
1493 let instance = BrushInstance {
1494 segment_index: INVALID_SEGMENT_INDEX,
1495 edge_flags: EdgeAaSegmentMask::all(),
1496 clip_task_address,
1497 brush_flags,
1498 prim_header_index,
1499 resource_address: 0,
1500 };
1501
1502 self.batcher.push_single_instance(
1503 batch_key,
1504 batch_features,
1505 bounding_rect,
1506 z_id,
1507 PrimitiveInstanceData::from(instance),
1508 );
1509 }
1510 PictureCompositeMode::Blit(_) => {
1511 match picture.context_3d {
1512 Picture3DContext::In { root_data: Some(_), .. } => {
1513 unreachable!("bug: should not have a raster_config");
1514 }
1515 Picture3DContext::In { root_data: None, .. } => {
1516 let extra_prim_gpu_address = match extra_prim_gpu_address {
1523 Some(prim_address) => prim_address,
1524 None => return,
1525 };
1526
1527 let prim_header = PrimitiveHeader {
1528 local_rect: prim_rect,
1529 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1530 specific_prim_address: GpuCacheAddress::INVALID,
1531 transform_id: transforms
1532 .get_id(
1533 prim_spatial_node_index,
1534 root_spatial_node_index,
1535 ctx.spatial_tree,
1536 ),
1537 };
1538
1539 let z_id = z_generator.next();
1542
1543 let prim_header_index = prim_headers.push(
1544 &prim_header,
1545 z_id,
1546 self.batcher.render_task_address,
1547 [
1548 uv_rect_address.as_int(),
1549 BrushFlags::PERSPECTIVE_INTERPOLATION.bits() as i32,
1550 0,
1551 clip_task_address.0 as i32,
1552 ]
1553 );
1554
1555 let key = BatchKey::new(
1556 BatchKind::SplitComposite,
1557 BlendMode::PremultipliedAlpha,
1558 textures,
1559 );
1560
1561 self.add_split_composite_instance_to_batches(
1562 key,
1563 BatchFeatures::CLIP_MASK,
1564 &prim_info.clip_chain.pic_coverage_rect,
1565 z_id,
1566 prim_header_index,
1567 extra_prim_gpu_address,
1568 );
1569 }
1570 Picture3DContext::Out { .. } => {
1571 let textures = TextureSet {
1572 colors: [
1573 texture,
1574 TextureSource::Invalid,
1575 TextureSource::Invalid,
1576 ],
1577 };
1578 let batch_params = BrushBatchParameters::shared(
1579 BrushBatchKind::Image(ImageBufferKind::Texture2D),
1580 textures,
1581 ImageBrushData {
1582 color_mode: ShaderColorMode::Image,
1583 alpha_type: AlphaType::PremultipliedAlpha,
1584 raster_space: RasterizationSpace::Screen,
1585 opacity: 1.0,
1586 }.encode(),
1587 uv_rect_address.as_int(),
1588 );
1589
1590 let prim_header = PrimitiveHeader {
1591 specific_prim_address: prim_cache_address,
1592 ..prim_header
1593 };
1594
1595 let prim_header_index = prim_headers.push(
1596 &prim_header,
1597 z_id,
1598 self.batcher.render_task_address,
1599 batch_params.prim_user_data,
1600 );
1601
1602 let (opacity, blend_mode) = if is_opaque {
1603 (PrimitiveOpacity::opaque(), BlendMode::None)
1604 } else {
1605 (PrimitiveOpacity::translucent(), BlendMode::PremultipliedAlpha)
1606 };
1607
1608 self.add_segmented_prim_to_batch(
1609 None,
1610 opacity,
1611 &batch_params,
1612 blend_mode,
1613 batch_features,
1614 brush_flags,
1615 EdgeAaSegmentMask::all(),
1616 prim_header_index,
1617 bounding_rect,
1618 transform_kind,
1619 z_id,
1620 prim_info.clip_task_index,
1621 ctx,
1622 render_tasks,
1623 );
1624 }
1625 }
1626 }
1627 PictureCompositeMode::SvgFilter(..) => {
1628 let kind = BatchKind::Brush(
1629 BrushBatchKind::Image(ImageBufferKind::Texture2D)
1630 );
1631 let key = BatchKey::new(
1632 kind,
1633 blend_mode,
1634 textures,
1635 );
1636 let prim_header_index = prim_headers.push(
1637 &prim_header,
1638 z_id,
1639 self.batcher.render_task_address,
1640 ImageBrushData {
1641 color_mode: ShaderColorMode::Image,
1642 alpha_type: AlphaType::PremultipliedAlpha,
1643 raster_space: RasterizationSpace::Screen,
1644 opacity: 1.0,
1645 }.encode(),
1646 );
1647
1648 self.add_brush_instance_to_batches(
1649 key,
1650 batch_features,
1651 bounding_rect,
1652 z_id,
1653 INVALID_SEGMENT_INDEX,
1654 EdgeAaSegmentMask::all(),
1655 clip_task_address,
1656 brush_flags,
1657 prim_header_index,
1658 uv_rect_address.as_int(),
1659 );
1660 }
1661 PictureCompositeMode::SVGFEGraph(..) => {
1662 let kind = BatchKind::Brush(
1663 BrushBatchKind::Image(ImageBufferKind::Texture2D)
1664 );
1665 let key = BatchKey::new(
1666 kind,
1667 blend_mode,
1668 textures,
1669 );
1670 let prim_header_index = prim_headers.push(
1671 &prim_header,
1672 z_id,
1673 self.batcher.render_task_address,
1674 ImageBrushData {
1675 color_mode: ShaderColorMode::Image,
1676 alpha_type: AlphaType::PremultipliedAlpha,
1677 raster_space: RasterizationSpace::Screen,
1678 opacity: 1.0,
1679 }.encode(),
1680 );
1681
1682 self.add_brush_instance_to_batches(
1683 key,
1684 batch_features,
1685 bounding_rect,
1686 z_id,
1687 INVALID_SEGMENT_INDEX,
1688 EdgeAaSegmentMask::all(),
1689 clip_task_address,
1690 brush_flags,
1691 prim_header_index,
1692 uv_rect_address.as_int(),
1693 );
1694 }
1695 }
1696 }
1697 None => {
1698 unreachable!();
1699 }
1700 }
1701
1702 return;
1703 }
1704
1705 let common_data = ctx.data_stores.as_common_data(prim_instance);
1706
1707 let needs_blending = !common_data.opacity.is_opaque ||
1708 prim_info.clip_task_index != ClipTaskIndex::INVALID ||
1709 transform_kind == TransformedRectKind::Complex ||
1710 is_anti_aliased;
1711
1712 let blend_mode = if needs_blending {
1713 BlendMode::PremultipliedAlpha
1714 } else {
1715 BlendMode::None
1716 };
1717
1718 let segment_instance_index = match prim_instance.kind {
1719 PrimitiveInstanceKind::Rectangle { segment_instance_index, .. }
1720 | PrimitiveInstanceKind::YuvImage { segment_instance_index, .. } => segment_instance_index,
1721 _ => SegmentInstanceIndex::UNUSED,
1722 };
1723
1724 let (prim_cache_address, segments) = if segment_instance_index == SegmentInstanceIndex::UNUSED {
1725 (gpu_cache.try_get_address(&common_data.gpu_cache_handle), None)
1726 } else {
1727 let segment_instance = &ctx.scratch.segment_instances[segment_instance_index];
1728 let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
1729 (Some(gpu_cache.get_address(&segment_instance.gpu_cache_handle)), segments)
1730 };
1731
1732 let img_brush_data = match prim_instance.kind {
1734 PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref visible_tiles_range, .. } => {
1735 let prim_data = &ctx.data_stores.linear_grad[data_handle];
1736 Some((prim_data.src_color, Some(visible_tiles_range), prim_data.brush_segments.as_slice()))
1737 }
1738 PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
1739 let prim_data = &ctx.data_stores.radial_grad[data_handle];
1740 Some((prim_data.src_color, Some(visible_tiles_range), prim_data.brush_segments.as_slice()))
1741 }
1742 PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => {
1743 let prim_data = &ctx.data_stores.conic_grad[data_handle];
1744 Some((prim_data.src_color, Some(visible_tiles_range), prim_data.brush_segments.as_slice()))
1745 }
1746 PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
1747 let prim_data = &ctx.data_stores.image_border[data_handle];
1748 Some((prim_data.kind.src_color, None, prim_data.kind.brush_segments.as_slice()))
1749 }
1750 _ => None,
1751 };
1752
1753 if let Some((src_color, visible_tiles_range, brush_segments)) = img_brush_data {
1754 let src_color = render_tasks.resolve_location(src_color, gpu_cache);
1755
1756 let (uv_rect_address, texture_source) = match src_color {
1757 Some(src) => src,
1758 None => {
1759 return;
1760 }
1761 };
1762
1763 let textures = TextureSet::prim_textured(texture_source);
1764
1765 let prim_header = PrimitiveHeader {
1766 local_rect: prim_rect,
1767 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1768 specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle),
1769 transform_id,
1770 };
1771
1772 let prim_user_data = ImageBrushData {
1773 color_mode: ShaderColorMode::Image,
1774 alpha_type: AlphaType::PremultipliedAlpha,
1775 raster_space: RasterizationSpace::Local,
1776 opacity: 1.0,
1777 }.encode();
1778
1779 let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind());
1780
1781 if visible_tiles_range.map_or(true, |r| r.is_empty()) {
1782 let batch_params = BrushBatchParameters::shared(
1783 batch_kind,
1784 textures,
1785 prim_user_data,
1786 uv_rect_address.as_int(),
1787 );
1788
1789 let segments = if brush_segments.is_empty() {
1790 None
1791 } else {
1792 Some(&brush_segments[..])
1793 };
1794
1795 let prim_header_index = prim_headers.push(
1796 &prim_header,
1797 z_id,
1798 self.batcher.render_task_address,
1799 batch_params.prim_user_data,
1800 );
1801
1802 self.add_segmented_prim_to_batch(
1803 segments,
1804 common_data.opacity,
1805 &batch_params,
1806 blend_mode,
1807 batch_features,
1808 brush_flags,
1809 common_data.edge_aa_mask,
1810 prim_header_index,
1811 bounding_rect,
1812 transform_kind,
1813 z_id,
1814 prim_info.clip_task_index,
1815 ctx,
1816 render_tasks,
1817 );
1818 } else {
1819 let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range.unwrap()];
1820
1821 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
1822 prim_info.clip_task_index,
1823 render_tasks,
1824 ).unwrap();
1825
1826 let batch_key = BatchKey {
1827 blend_mode,
1828 kind: BatchKind::Brush(batch_kind),
1829 textures: BatchTextures {
1830 input: textures,
1831 clip_mask,
1832 },
1833 };
1834
1835 for tile in visible_tiles {
1836 let tile_prim_header = PrimitiveHeader {
1837 local_rect: tile.local_rect,
1838 local_clip_rect: tile.local_clip_rect,
1839 ..prim_header
1840 };
1841 let prim_header_index = prim_headers.push(
1842 &tile_prim_header,
1843 z_id,
1844 self.batcher.render_task_address,
1845 prim_user_data,
1846 );
1847
1848 self.add_brush_instance_to_batches(
1849 batch_key,
1850 batch_features,
1851 bounding_rect,
1852 z_id,
1853 INVALID_SEGMENT_INDEX,
1854 common_data.edge_aa_mask,
1855 clip_task_address,
1856 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1857 prim_header_index,
1858 uv_rect_address.as_int(),
1859 );
1860 }
1861 }
1862
1863 return;
1864 }
1865
1866 match prim_instance.kind {
1867 PrimitiveInstanceKind::Picture { .. } => {}
1869 PrimitiveInstanceKind::CachedLinearGradient { .. } => { }
1870 PrimitiveInstanceKind::RadialGradient { .. } => { }
1871 PrimitiveInstanceKind::ConicGradient { .. } => { }
1872 PrimitiveInstanceKind::ImageBorder { .. } => {}
1873 PrimitiveInstanceKind::BoxShadow { .. } => {
1874 unreachable!("BUG: Should not hit box-shadow here as they are handled by quad infra");
1875 }
1876 PrimitiveInstanceKind::Clear { .. } => {
1877 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
1878 prim_info.clip_task_index,
1879 render_tasks,
1880 ).unwrap();
1881
1882 let prim_header = PrimitiveHeader {
1887 local_rect: prim_rect,
1888 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1889 specific_prim_address: prim_cache_address.unwrap(),
1890 transform_id,
1891 };
1892
1893 let prim_header_index = prim_headers.push(
1894 &prim_header,
1895 z_id,
1896 self.batcher.render_task_address,
1897 [get_shader_opacity(1.0), 0, 0, 0],
1898 );
1899
1900 let batch_key = BatchKey {
1901 blend_mode: BlendMode::PremultipliedDestOut,
1902 kind: BatchKind::Brush(BrushBatchKind::Solid),
1903 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
1904 };
1905
1906 self.add_brush_instance_to_batches(
1907 batch_key,
1908 batch_features,
1909 bounding_rect,
1910 z_id,
1911 INVALID_SEGMENT_INDEX,
1912 common_data.edge_aa_mask,
1913 clip_task_address,
1914 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
1915 prim_header_index,
1916 0,
1917 );
1918 }
1919 PrimitiveInstanceKind::NormalBorder { data_handle, ref render_task_ids, .. } => {
1920 let prim_data = &ctx.data_stores.normal_border[data_handle];
1921 let task_ids = &ctx.scratch.border_cache_handles[*render_task_ids];
1922 let mut segment_data: SmallVec<[SegmentInstanceData; 8]> = SmallVec::new();
1923
1924 for task_id in task_ids {
1928 if let Some((uv_rect_address, texture)) = render_tasks.resolve_location(*task_id, gpu_cache) {
1929 segment_data.push(
1930 SegmentInstanceData {
1931 textures: TextureSet::prim_textured(texture),
1932 specific_resource_address: uv_rect_address.as_int(),
1933 }
1934 );
1935 }
1936 }
1937
1938 let image_buffer_kind = ImageBufferKind::Texture2D;
1940
1941 let prim_header = PrimitiveHeader {
1942 local_rect: prim_rect,
1943 local_clip_rect: prim_info.clip_chain.local_clip_rect,
1944 specific_prim_address: prim_cache_address.unwrap(),
1945 transform_id,
1946 };
1947
1948 let batch_params = BrushBatchParameters::instanced(
1949 BrushBatchKind::Image(image_buffer_kind),
1950 ImageBrushData {
1951 color_mode: ShaderColorMode::Image,
1952 alpha_type: AlphaType::PremultipliedAlpha,
1953 raster_space: RasterizationSpace::Local,
1954 opacity: 1.0,
1955 }.encode(),
1956 segment_data,
1957 );
1958
1959 let prim_header_index = prim_headers.push(
1960 &prim_header,
1961 z_id,
1962 self.batcher.render_task_address,
1963 batch_params.prim_user_data,
1964 );
1965
1966 let border_data = &prim_data.kind;
1967 self.add_segmented_prim_to_batch(
1968 Some(border_data.brush_segments.as_slice()),
1969 common_data.opacity,
1970 &batch_params,
1971 blend_mode,
1972 batch_features,
1973 brush_flags,
1974 common_data.edge_aa_mask,
1975 prim_header_index,
1976 bounding_rect,
1977 transform_kind,
1978 z_id,
1979 prim_info.clip_task_index,
1980 ctx,
1981 render_tasks,
1982 );
1983 }
1984 PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => {
1985 let run = &ctx.prim_store.text_runs[run_index];
1986 let subpx_dir = run.used_font.get_subpx_dir();
1987
1988 let prim_data = &ctx.data_stores.text_run[data_handle];
1991
1992 let prim_header = PrimitiveHeader {
2003 local_rect: LayoutRect {
2004 min: prim_rect.min - run.reference_frame_relative_offset,
2005 max: run.snapped_reference_frame_relative_offset.to_point(),
2006 },
2007 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2008 specific_prim_address: prim_cache_address.unwrap(),
2009 transform_id,
2010 };
2011
2012 let glyph_keys = &ctx.scratch.glyph_keys[run.glyph_keys_range];
2013 let prim_header_index = prim_headers.push(
2014 &prim_header,
2015 z_id,
2016 self.batcher.render_task_address,
2017 [
2018 (run.raster_scale * 65535.0).round() as i32,
2019 0,
2020 0,
2021 0,
2022 ],
2023 );
2024 let base_instance = GlyphInstance::new(
2025 prim_header_index,
2026 );
2027 let batcher = &mut self.batcher;
2028
2029 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2030 prim_info.clip_task_index,
2031 render_tasks,
2032 ).unwrap();
2033
2034 let font = run.used_font.clone();
2038 ctx.resource_cache.fetch_glyphs(
2039 font,
2040 &glyph_keys,
2041 &mut self.glyph_fetch_buffer,
2042 gpu_cache,
2043 |texture_id, glyph_format, glyphs| {
2044 debug_assert_ne!(texture_id, TextureSource::Invalid);
2045
2046 let subpx_dir = subpx_dir.limit_by(glyph_format);
2047
2048 let textures = BatchTextures::prim_textured(
2049 texture_id,
2050 clip_mask_texture_id,
2051 );
2052
2053 let kind = BatchKind::TextRun(glyph_format);
2054
2055 let (blend_mode, color_mode) = match glyph_format {
2056 GlyphFormat::Subpixel |
2057 GlyphFormat::TransformedSubpixel => {
2058 debug_assert!(ctx.use_dual_source_blending);
2059 (
2060 BlendMode::SubpixelDualSource,
2061 ShaderColorMode::SubpixelDualSource,
2062 )
2063 }
2064 GlyphFormat::Alpha |
2065 GlyphFormat::TransformedAlpha |
2066 GlyphFormat::Bitmap => {
2067 (
2068 BlendMode::PremultipliedAlpha,
2069 ShaderColorMode::Alpha,
2070 )
2071 }
2072 GlyphFormat::ColorBitmap => {
2073 (
2074 BlendMode::PremultipliedAlpha,
2075 if run.shadow {
2076 ShaderColorMode::BitmapShadow
2078 } else {
2079 ShaderColorMode::ColorBitmap
2080 },
2081 )
2082 }
2083 };
2084
2085 let tight_bounding_rect = {
2091 let snap_bias = match subpx_dir {
2092 SubpixelDirection::None => DeviceVector2D::new(0.5, 0.5),
2093 SubpixelDirection::Horizontal => DeviceVector2D::new(0.125, 0.5),
2094 SubpixelDirection::Vertical => DeviceVector2D::new(0.5, 0.125),
2095 SubpixelDirection::Mixed => DeviceVector2D::new(0.125, 0.125),
2096 };
2097 let text_offset = prim_header.local_rect.max.to_vector();
2098
2099 let pic_bounding_rect = if run.used_font.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS) {
2100 let mut device_bounding_rect = DeviceRect::default();
2101
2102 let glyph_transform = ctx.spatial_tree.get_relative_transform(
2103 prim_spatial_node_index,
2104 root_spatial_node_index,
2105 ).into_transform()
2106 .with_destination::<WorldPixel>()
2107 .then(&euclid::Transform3D::from_scale(ctx.global_device_pixel_scale));
2108
2109 let glyph_translation = DeviceVector2D::new(glyph_transform.m41, glyph_transform.m42);
2110
2111 let mut use_tight_bounding_rect = true;
2112 for glyph in glyphs {
2113 let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
2114
2115 let transformed_offset = match glyph_transform.transform_point2d(glyph_offset) {
2116 Some(transformed_offset) => transformed_offset,
2117 None => {
2118 use_tight_bounding_rect = false;
2119 break;
2120 }
2121 };
2122 let raster_glyph_offset = (transformed_offset + snap_bias).floor();
2123 let raster_text_offset = (
2124 glyph_transform.transform_vector2d(text_offset) +
2125 glyph_translation +
2126 DeviceVector2D::new(0.5, 0.5)
2127 ).floor() - glyph_translation;
2128
2129 let device_glyph_rect = DeviceRect::from_origin_and_size(
2130 glyph.offset + raster_glyph_offset.to_vector() + raster_text_offset,
2131 glyph.size.to_f32(),
2132 );
2133
2134 device_bounding_rect = device_bounding_rect.union(&device_glyph_rect);
2135 }
2136
2137 if use_tight_bounding_rect {
2138 let map_device_to_surface: SpaceMapper<PicturePixel, DevicePixel> = SpaceMapper::new_with_target(
2139 root_spatial_node_index,
2140 surface_spatial_node_index,
2141 device_bounding_rect,
2142 ctx.spatial_tree,
2143 );
2144
2145 match map_device_to_surface.unmap(&device_bounding_rect) {
2146 Some(r) => r.intersection(bounding_rect),
2147 None => Some(*bounding_rect),
2148 }
2149 } else {
2150 Some(*bounding_rect)
2151 }
2152 } else {
2153 let mut local_bounding_rect = LayoutRect::default();
2154
2155 let glyph_raster_scale = run.raster_scale * ctx.global_device_pixel_scale.get();
2156
2157 for glyph in glyphs {
2158 let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.min.to_vector();
2159 let glyph_scale = LayoutToDeviceScale::new(glyph_raster_scale / glyph.scale);
2160 let raster_glyph_offset = (glyph_offset * LayoutToDeviceScale::new(glyph_raster_scale) + snap_bias).floor() / glyph.scale;
2161 let local_glyph_rect = LayoutRect::from_origin_and_size(
2162 (glyph.offset + raster_glyph_offset.to_vector()) / glyph_scale + text_offset,
2163 glyph.size.to_f32() / glyph_scale,
2164 );
2165
2166 local_bounding_rect = local_bounding_rect.union(&local_glyph_rect);
2167 }
2168
2169 let map_prim_to_surface: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target(
2170 surface_spatial_node_index,
2171 prim_spatial_node_index,
2172 *bounding_rect,
2173 ctx.spatial_tree,
2174 );
2175 map_prim_to_surface.map(&local_bounding_rect)
2176 };
2177
2178 let intersected = match pic_bounding_rect {
2179 Some(rect) => rect.intersection(bounding_rect).unwrap_or_else(PictureRect::zero),
2182 None => *bounding_rect,
2185 };
2186
2187 intersected
2188 };
2189
2190 let key = BatchKey::new(kind, blend_mode, textures);
2191
2192 let batch = batcher.alpha_batch_list.set_params_and_get_batch(
2193 key,
2194 batch_features,
2195 &tight_bounding_rect,
2196 z_id,
2197 );
2198
2199 batch.reserve(glyphs.len());
2200 for glyph in glyphs {
2201 batch.push(base_instance.build(
2202 clip_task_address,
2203 subpx_dir,
2204 glyph.index_in_text_run,
2205 glyph.uv_rect_address,
2206 color_mode,
2207 ));
2208 }
2209 },
2210 );
2211 }
2212 PrimitiveInstanceKind::LineDecoration { ref render_task, .. } => {
2213 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2214 prim_info.clip_task_index,
2215 render_tasks,
2216 ).unwrap();
2217
2218 let (batch_kind, textures, prim_user_data, specific_resource_address) = match render_task {
2219 Some(task_id) => {
2220 let (uv_rect_address, texture) = render_tasks.resolve_location(*task_id, gpu_cache).unwrap();
2221 let textures = BatchTextures::prim_textured(
2222 texture,
2223 clip_mask_texture_id,
2224 );
2225 (
2226 BrushBatchKind::Image(texture.image_buffer_kind()),
2227 textures,
2228 ImageBrushData {
2229 color_mode: ShaderColorMode::Image,
2230 alpha_type: AlphaType::PremultipliedAlpha,
2231 raster_space: RasterizationSpace::Local,
2232 opacity: 1.0,
2233 }.encode(),
2234 uv_rect_address.as_int(),
2235 )
2236 }
2237 None => {
2238 (
2239 BrushBatchKind::Solid,
2240 BatchTextures::prim_untextured(clip_mask_texture_id),
2241 [get_shader_opacity(1.0), 0, 0, 0],
2242 0,
2243 )
2244 }
2245 };
2246
2247 let prim_header = PrimitiveHeader {
2248 local_rect: prim_rect,
2249 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2250 specific_prim_address: prim_cache_address.unwrap(),
2251 transform_id,
2252 };
2253
2254 let prim_header_index = prim_headers.push(
2255 &prim_header,
2256 z_id,
2257 self.batcher.render_task_address,
2258 prim_user_data,
2259 );
2260
2261 let batch_key = BatchKey {
2262 blend_mode,
2263 kind: BatchKind::Brush(batch_kind),
2264 textures,
2265 };
2266
2267 self.add_brush_instance_to_batches(
2268 batch_key,
2269 batch_features,
2270 bounding_rect,
2271 z_id,
2272 INVALID_SEGMENT_INDEX,
2273 common_data.edge_aa_mask,
2274 clip_task_address,
2275 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2276 prim_header_index,
2277 specific_resource_address,
2278 );
2279 }
2280 PrimitiveInstanceKind::Rectangle { use_legacy_path, .. } => {
2281 debug_assert!(use_legacy_path);
2282 let batch_params = BrushBatchParameters::shared(
2283 BrushBatchKind::Solid,
2284 TextureSet::UNTEXTURED,
2285 [get_shader_opacity(1.0), 0, 0, 0],
2286 0,
2287 );
2288
2289 let prim_header = PrimitiveHeader {
2290 local_rect: prim_rect,
2291 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2292 specific_prim_address: prim_cache_address.unwrap(),
2293 transform_id,
2294 };
2295
2296 let prim_header_index = prim_headers.push(
2297 &prim_header,
2298 z_id,
2299 self.batcher.render_task_address,
2300 batch_params.prim_user_data,
2301 );
2302
2303 self.add_segmented_prim_to_batch(
2304 segments,
2305 common_data.opacity,
2306 &batch_params,
2307 blend_mode,
2308 batch_features,
2309 brush_flags,
2310 common_data.edge_aa_mask,
2311 prim_header_index,
2312 bounding_rect,
2313 transform_kind,
2314 z_id,
2315 prim_info.clip_task_index,
2316 ctx,
2317 render_tasks,
2318 );
2319 }
2320 PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, compositor_surface_kind, .. } => {
2321 if compositor_surface_kind.needs_cutout() {
2322 self.add_compositor_surface_cutout(
2323 prim_rect,
2324 prim_info.clip_chain.local_clip_rect,
2325 prim_info.clip_task_index,
2326 transform_id,
2327 z_id,
2328 bounding_rect,
2329 ctx,
2330 gpu_cache,
2331 render_tasks,
2332 prim_headers,
2333 );
2334
2335 return;
2336 }
2337
2338 let yuv_image_data = &ctx.data_stores.yuv_image[data_handle].kind;
2339 let mut textures = TextureSet::UNTEXTURED;
2340 let mut uv_rect_addresses = [0; 3];
2341
2342 let channel_count = yuv_image_data.format.get_plane_num();
2344 debug_assert!(channel_count <= 3);
2345 for channel in 0 .. channel_count {
2346
2347 let src_channel = render_tasks.resolve_location(yuv_image_data.src_yuv[channel], gpu_cache);
2348
2349 let (uv_rect_address, texture_source) = match src_channel {
2350 Some(src) => src,
2351 None => {
2352 warn!("Warnings: skip a PrimitiveKind::YuvImage");
2353 return;
2354 }
2355 };
2356
2357 textures.colors[channel] = texture_source;
2358 uv_rect_addresses[channel] = uv_rect_address.as_int();
2359 }
2360
2361 let buffer_kind = textures.colors[0].image_buffer_kind();
2363 assert!(
2364 textures.colors[1 .. yuv_image_data.format.get_plane_num()]
2365 .iter()
2366 .all(|&tid| buffer_kind == tid.image_buffer_kind())
2367 );
2368
2369 let kind = BrushBatchKind::YuvImage(
2370 buffer_kind,
2371 yuv_image_data.format,
2372 yuv_image_data.color_depth,
2373 yuv_image_data.color_space,
2374 yuv_image_data.color_range,
2375 );
2376
2377 let batch_params = BrushBatchParameters::shared(
2378 kind,
2379 textures,
2380 [
2381 uv_rect_addresses[0],
2382 uv_rect_addresses[1],
2383 uv_rect_addresses[2],
2384 0,
2385 ],
2386 0,
2387 );
2388
2389 debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID);
2390
2391 let prim_header = PrimitiveHeader {
2392 local_rect: prim_rect,
2393 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2394 specific_prim_address: prim_cache_address.unwrap(),
2395 transform_id,
2396 };
2397
2398 let prim_header_index = prim_headers.push(
2399 &prim_header,
2400 z_id,
2401 self.batcher.render_task_address,
2402 batch_params.prim_user_data,
2403 );
2404
2405 self.add_segmented_prim_to_batch(
2406 segments,
2407 common_data.opacity,
2408 &batch_params,
2409 blend_mode,
2410 batch_features,
2411 brush_flags,
2412 common_data.edge_aa_mask,
2413 prim_header_index,
2414 bounding_rect,
2415 transform_kind,
2416 z_id,
2417 prim_info.clip_task_index,
2418 ctx,
2419 render_tasks,
2420 );
2421 }
2422 PrimitiveInstanceKind::Image { data_handle, image_instance_index, compositor_surface_kind, .. } => {
2423 if compositor_surface_kind.needs_cutout() {
2424 self.add_compositor_surface_cutout(
2425 prim_rect,
2426 prim_info.clip_chain.local_clip_rect,
2427 prim_info.clip_task_index,
2428 transform_id,
2429 z_id,
2430 bounding_rect,
2431 ctx,
2432 gpu_cache,
2433 render_tasks,
2434 prim_headers,
2435 );
2436
2437 return;
2438 }
2439
2440 let image_data = &ctx.data_stores.image[data_handle].kind;
2441 let image_instance = &ctx.prim_store.images[image_instance_index];
2442 let prim_user_data = ImageBrushData {
2443 color_mode: ShaderColorMode::Image,
2444 alpha_type: image_data.alpha_type,
2445 raster_space: RasterizationSpace::Local,
2446 opacity: 1.0,
2447 }.encode();
2448
2449 let blend_mode = if needs_blending {
2450 match image_data.alpha_type {
2451 AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
2452 AlphaType::Alpha => BlendMode::Alpha,
2453 }
2454 } else {
2455 BlendMode::None
2456 };
2457
2458 if image_instance.visible_tiles.is_empty() {
2459 if cfg!(debug_assertions) {
2460 match ctx.resource_cache.get_image_properties(image_data.key) {
2461 Some(ImageProperties { tiling: None, .. }) | None => (),
2462 other => panic!("Non-tiled image with no visible images detected! Properties {:?}", other),
2463 }
2464 }
2465
2466 let src_color = render_tasks.resolve_location(image_instance.src_color, gpu_cache);
2467
2468 let (uv_rect_address, texture_source) = match src_color {
2469 Some(src) => src,
2470 None => {
2471 return;
2472 }
2473 };
2474
2475 let batch_params = BrushBatchParameters::shared(
2476 BrushBatchKind::Image(texture_source.image_buffer_kind()),
2477 TextureSet::prim_textured(texture_source),
2478 prim_user_data,
2479 uv_rect_address.as_int(),
2480 );
2481
2482 debug_assert_ne!(image_instance.segment_instance_index, SegmentInstanceIndex::INVALID);
2483 let (prim_cache_address, segments) = if image_instance.segment_instance_index == SegmentInstanceIndex::UNUSED {
2484 (prim_cache_address.unwrap(), None)
2485 } else {
2486 let segment_instance = &ctx.scratch.segment_instances[image_instance.segment_instance_index];
2487 let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]);
2488 (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments)
2489 };
2490
2491 let local_rect = image_instance.adjustment.map_local_rect(&prim_rect);
2492 let local_clip_rect = image_instance.tight_local_clip_rect
2493 .intersection_unchecked(&local_rect);
2494
2495 let prim_header = PrimitiveHeader {
2496 local_rect,
2497 local_clip_rect,
2498 specific_prim_address: prim_cache_address,
2499 transform_id,
2500 };
2501
2502 let prim_header_index = prim_headers.push(
2503 &prim_header,
2504 z_id,
2505 self.batcher.render_task_address,
2506 batch_params.prim_user_data,
2507 );
2508
2509 let brush_flags = match image_instance.normalized_uvs {
2510 true => brush_flags | BrushFlags::NORMALIZED_UVS,
2511 false => brush_flags,
2512 };
2513
2514 self.add_segmented_prim_to_batch(
2515 segments,
2516 common_data.opacity,
2517 &batch_params,
2518 blend_mode,
2519 batch_features,
2520 brush_flags,
2521 common_data.edge_aa_mask,
2522 prim_header_index,
2523 bounding_rect,
2524 transform_kind,
2525 z_id,
2526 prim_info.clip_task_index,
2527 ctx,
2528 render_tasks,
2529 );
2530 } else {
2531 const VECS_PER_SPECIFIC_BRUSH: usize = 3;
2532 let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT;
2533
2534 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2535 prim_info.clip_task_index,
2536 render_tasks,
2537 ).unwrap();
2538
2539 let mut gpu_blocks = Vec::<GpuBlockData>::with_capacity(3 + max_tiles_per_header * 2);
2541 for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) {
2542 gpu_blocks.clear();
2543 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 {
2548 let tile_rect = tile.local_rect.translate(-prim_rect.min.to_vector());
2549 gpu_blocks.push(tile_rect.into());
2550 gpu_blocks.push(GpuBlockData::EMPTY);
2551 }
2552
2553 let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
2554 let prim_header = PrimitiveHeader {
2555 local_rect: prim_rect,
2556 local_clip_rect: image_instance.tight_local_clip_rect,
2557 specific_prim_address: gpu_cache.get_address(&gpu_handle),
2558 transform_id,
2559 };
2560 let prim_header_index = prim_headers.push(
2561 &prim_header,
2562 z_id,
2563 self.batcher.render_task_address,
2564 prim_user_data,
2565 );
2566
2567 for (i, tile) in chunk.iter().enumerate() {
2568 let (uv_rect_address, texture) = match render_tasks.resolve_location(tile.src_color, gpu_cache) {
2569 Some(result) => result,
2570 None => {
2571 return;
2572 }
2573 };
2574
2575 let textures = BatchTextures::prim_textured(
2576 texture,
2577 clip_mask_texture_id,
2578 );
2579
2580 let batch_key = BatchKey {
2581 blend_mode,
2582 kind: BatchKind::Brush(BrushBatchKind::Image(texture.image_buffer_kind())),
2583 textures,
2584 };
2585
2586 self.add_brush_instance_to_batches(
2587 batch_key,
2588 batch_features,
2589 bounding_rect,
2590 z_id,
2591 i as i32,
2592 tile.edge_flags,
2593 clip_task_address,
2594 brush_flags | BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION,
2595 prim_header_index,
2596 uv_rect_address.as_int(),
2597 );
2598 }
2599 }
2600 }
2601 }
2602 PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => {
2603 let prim_data = &ctx.data_stores.linear_grad[data_handle];
2604
2605 let mut prim_header = PrimitiveHeader {
2606 local_rect: prim_rect,
2607 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2608 specific_prim_address: GpuCacheAddress::INVALID,
2609 transform_id,
2610 };
2611
2612 let user_data = [extra_prim_gpu_address.unwrap(), 0, 0, 0];
2613
2614 if visible_tiles_range.is_empty() {
2615 let batch_params = BrushBatchParameters::shared(
2616 BrushBatchKind::LinearGradient,
2617 TextureSet::UNTEXTURED,
2618 user_data,
2619 0,
2620 );
2621
2622 prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
2623
2624 let prim_header_index = prim_headers.push(
2625 &prim_header,
2626 z_id,
2627 self.batcher.render_task_address,
2628 user_data,
2629 );
2630
2631 let segments = if prim_data.brush_segments.is_empty() {
2632 None
2633 } else {
2634 Some(prim_data.brush_segments.as_slice())
2635 };
2636 self.add_segmented_prim_to_batch(
2637 segments,
2638 prim_data.opacity,
2639 &batch_params,
2640 blend_mode,
2641 batch_features,
2642 brush_flags,
2643 prim_data.edge_aa_mask,
2644 prim_header_index,
2645 bounding_rect,
2646 transform_kind,
2647 z_id,
2648 prim_info.clip_task_index,
2649 ctx,
2650 render_tasks,
2651 );
2652 } else {
2653 let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
2654
2655 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2656 prim_info.clip_task_index,
2657 render_tasks,
2658 ).unwrap();
2659
2660 let key = BatchKey {
2661 blend_mode,
2662 kind: BatchKind::Brush(BrushBatchKind::LinearGradient),
2663 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2664 };
2665
2666 for tile in visible_tiles {
2667 let tile_prim_header = PrimitiveHeader {
2668 specific_prim_address: gpu_cache.get_address(&tile.handle),
2669 local_rect: tile.local_rect,
2670 local_clip_rect: tile.local_clip_rect,
2671 ..prim_header
2672 };
2673 let prim_header_index = prim_headers.push(
2674 &tile_prim_header,
2675 z_id,
2676 self.batcher.render_task_address,
2677 user_data,
2678 );
2679
2680 self.add_brush_instance_to_batches(
2681 key,
2682 batch_features,
2683 bounding_rect,
2684 z_id,
2685 INVALID_SEGMENT_INDEX,
2686 prim_data.edge_aa_mask,
2687 clip_task_address,
2688 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
2689 prim_header_index,
2690 0,
2691 );
2692 }
2693 }
2694 }
2695 PrimitiveInstanceKind::BackdropCapture { .. } => {}
2696 PrimitiveInstanceKind::BackdropRender { pic_index, .. } => {
2697 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_image_handle);
2698 let blend_mode = BlendMode::PremultipliedAlpha;
2699 let pic_task_id = ctx.prim_store.pictures[pic_index.0].primary_render_task_id;
2700
2701 let prim_header = PrimitiveHeader {
2702 local_rect: prim_rect,
2703 local_clip_rect: prim_info.clip_chain.local_clip_rect,
2704 specific_prim_address: prim_cache_address,
2705 transform_id,
2706 };
2707
2708 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2709 prim_info.clip_task_index,
2710 render_tasks,
2711 ).unwrap();
2712
2713 let kind = BatchKind::Brush(
2714 BrushBatchKind::Image(ImageBufferKind::Texture2D)
2715 );
2716 let (_, texture) = render_tasks.resolve_location(
2717 pic_task_id,
2718 gpu_cache,
2719 ).unwrap();
2720 let textures = BatchTextures::prim_textured(
2721 texture,
2722 clip_mask_texture_id,
2723 );
2724 let key = BatchKey::new(
2725 kind,
2726 blend_mode,
2727 textures,
2728 );
2729 let prim_header_index = prim_headers.push(
2730 &prim_header,
2731 z_id,
2732 self.batcher.render_task_address,
2733 ImageBrushData {
2734 color_mode: ShaderColorMode::Image,
2735 alpha_type: AlphaType::PremultipliedAlpha,
2736 raster_space: RasterizationSpace::Screen,
2737 opacity: 1.0,
2738 }.encode(),
2739 );
2740
2741 let pic_task = &render_tasks[pic_task_id.unwrap()];
2742 let pic_info = match pic_task.kind {
2743 RenderTaskKind::Picture(ref info) => info,
2744 _ => panic!("bug: not a picture"),
2745 };
2746 let target_rect = pic_task.get_target_rect();
2747
2748 let backdrop_rect = DeviceRect::from_origin_and_size(
2749 pic_info.content_origin,
2750 target_rect.size().to_f32(),
2751 );
2752
2753 let map_prim_to_backdrop = SpaceMapper::new_with_target(
2754 pic_info.surface_spatial_node_index,
2755 prim_spatial_node_index,
2756 WorldRect::max_rect(),
2757 ctx.spatial_tree,
2758 );
2759
2760 let points = [
2761 map_prim_to_backdrop.map_point(prim_rect.top_left()),
2762 map_prim_to_backdrop.map_point(prim_rect.top_right()),
2763 map_prim_to_backdrop.map_point(prim_rect.bottom_left()),
2764 map_prim_to_backdrop.map_point(prim_rect.bottom_right()),
2765 ];
2766
2767 if points.iter().any(|p| p.is_none()) {
2768 return;
2769 }
2770
2771 let uvs = [
2772 calculate_screen_uv(points[0].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2773 calculate_screen_uv(points[1].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2774 calculate_screen_uv(points[2].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2775 calculate_screen_uv(points[3].unwrap() * pic_info.device_pixel_scale, backdrop_rect),
2776 ];
2777
2778 let gpu_blocks = &[
2782 GpuBlockData::from([
2783 target_rect.min.x as f32,
2784 target_rect.min.y as f32,
2785 target_rect.max.x as f32,
2786 target_rect.max.y as f32,
2787 ]),
2788 GpuBlockData::from([0.0; 4]),
2789 GpuBlockData::from(uvs[0]),
2790 GpuBlockData::from(uvs[1]),
2791 GpuBlockData::from(uvs[2]),
2792 GpuBlockData::from(uvs[3]),
2793 ];
2794 let uv_rect_handle = gpu_cache.push_per_frame_blocks(gpu_blocks);
2795
2796 self.add_brush_instance_to_batches(
2797 key,
2798 batch_features,
2799 bounding_rect,
2800 z_id,
2801 INVALID_SEGMENT_INDEX,
2802 EdgeAaSegmentMask::all(),
2803 clip_task_address,
2804 brush_flags,
2805 prim_header_index,
2806 uv_rect_handle.as_int(gpu_cache),
2807 );
2808 }
2809 }
2810 }
2811
2812 fn add_compositor_surface_cutout(
2815 &mut self,
2816 prim_rect: LayoutRect,
2817 local_clip_rect: LayoutRect,
2818 clip_task_index: ClipTaskIndex,
2819 transform_id: TransformPaletteId,
2820 z_id: ZBufferId,
2821 bounding_rect: &PictureRect,
2822 ctx: &RenderTargetContext,
2823 gpu_cache: &mut GpuCache,
2824 render_tasks: &RenderTaskGraph,
2825 prim_headers: &mut PrimitiveHeaders,
2826 ) {
2827 let prim_cache_address = gpu_cache.get_address(&ctx.globals.default_black_rect_handle);
2828
2829 let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture(
2830 clip_task_index,
2831 render_tasks,
2832 ).unwrap();
2833
2834 let prim_header = PrimitiveHeader {
2835 local_rect: prim_rect,
2836 local_clip_rect,
2837 specific_prim_address: prim_cache_address,
2838 transform_id,
2839 };
2840
2841 let prim_header_index = prim_headers.push(
2842 &prim_header,
2843 z_id,
2844 self.batcher.render_task_address,
2845 [get_shader_opacity(1.0), 0, 0, 0],
2846 );
2847
2848 let batch_key = BatchKey {
2849 blend_mode: BlendMode::PremultipliedDestOut,
2850 kind: BatchKind::Brush(BrushBatchKind::Solid),
2851 textures: BatchTextures::prim_untextured(clip_mask_texture_id),
2852 };
2853
2854 self.add_brush_instance_to_batches(
2855 batch_key,
2856 BatchFeatures::ALPHA_PASS | BatchFeatures::CLIP_MASK,
2857 bounding_rect,
2858 z_id,
2859 INVALID_SEGMENT_INDEX,
2860 EdgeAaSegmentMask::empty(),
2861 clip_task_address,
2862 BrushFlags::empty(),
2863 prim_header_index,
2864 0,
2865 );
2866 }
2867
2868 fn add_segment_to_batch(
2875 &mut self,
2876 segment: &BrushSegment,
2877 segment_data: &SegmentInstanceData,
2878 segment_index: i32,
2879 batch_kind: BrushBatchKind,
2880 prim_header_index: PrimitiveHeaderIndex,
2881 alpha_blend_mode: BlendMode,
2882 features: BatchFeatures,
2883 brush_flags: BrushFlags,
2884 edge_aa_mask: EdgeAaSegmentMask,
2885 bounding_rect: &PictureRect,
2886 transform_kind: TransformedRectKind,
2887 z_id: ZBufferId,
2888 prim_opacity: PrimitiveOpacity,
2889 clip_task_index: ClipTaskIndex,
2890 ctx: &RenderTargetContext,
2891 render_tasks: &RenderTaskGraph,
2892 ) {
2893 debug_assert!(clip_task_index != ClipTaskIndex::INVALID);
2894
2895 if let Some((clip_task_address, clip_mask)) = ctx.get_clip_task_and_texture(
2898 clip_task_index,
2899 segment_index,
2900 render_tasks,
2901 ) {
2902 let is_inner = segment.edge_flags.is_empty();
2904 let needs_blending = !prim_opacity.is_opaque ||
2905 clip_task_address != OPAQUE_TASK_ADDRESS ||
2906 (!is_inner && transform_kind == TransformedRectKind::Complex) ||
2907 brush_flags.contains(BrushFlags::FORCE_AA);
2908
2909 let textures = BatchTextures {
2910 input: segment_data.textures,
2911 clip_mask,
2912 };
2913
2914 let batch_key = BatchKey {
2915 blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None },
2916 kind: BatchKind::Brush(batch_kind),
2917 textures,
2918 };
2919
2920 self.add_brush_instance_to_batches(
2921 batch_key,
2922 features,
2923 bounding_rect,
2924 z_id,
2925 segment_index,
2926 segment.edge_flags & edge_aa_mask,
2927 clip_task_address,
2928 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags,
2929 prim_header_index,
2930 segment_data.specific_resource_address,
2931 );
2932 }
2933 }
2934
2935 fn add_segmented_prim_to_batch(
2942 &mut self,
2943 brush_segments: Option<&[BrushSegment]>,
2944 prim_opacity: PrimitiveOpacity,
2945 params: &BrushBatchParameters,
2946 blend_mode: BlendMode,
2947 features: BatchFeatures,
2948 brush_flags: BrushFlags,
2949 edge_aa_mask: EdgeAaSegmentMask,
2950 prim_header_index: PrimitiveHeaderIndex,
2951 bounding_rect: &PictureRect,
2952 transform_kind: TransformedRectKind,
2953 z_id: ZBufferId,
2954 clip_task_index: ClipTaskIndex,
2955 ctx: &RenderTargetContext,
2956 render_tasks: &RenderTaskGraph,
2957 ) {
2958 match (brush_segments, ¶ms.segment_data) {
2959 (Some(ref brush_segments), SegmentDataKind::Instanced(ref segment_data)) => {
2960 debug_assert_eq!(brush_segments.len(), segment_data.len());
2963 for (segment_index, (segment, segment_data)) in brush_segments
2964 .iter()
2965 .zip(segment_data.iter())
2966 .enumerate()
2967 {
2968 self.add_segment_to_batch(
2969 segment,
2970 segment_data,
2971 segment_index as i32,
2972 params.batch_kind,
2973 prim_header_index,
2974 blend_mode,
2975 features,
2976 brush_flags,
2977 edge_aa_mask,
2978 bounding_rect,
2979 transform_kind,
2980 z_id,
2981 prim_opacity,
2982 clip_task_index,
2983 ctx,
2984 render_tasks,
2985 );
2986 }
2987 }
2988 (Some(ref brush_segments), SegmentDataKind::Shared(ref segment_data)) => {
2989 for (segment_index, segment) in brush_segments
2992 .iter()
2993 .enumerate()
2994 {
2995 self.add_segment_to_batch(
2996 segment,
2997 segment_data,
2998 segment_index as i32,
2999 params.batch_kind,
3000 prim_header_index,
3001 blend_mode,
3002 features,
3003 brush_flags,
3004 edge_aa_mask,
3005 bounding_rect,
3006 transform_kind,
3007 z_id,
3008 prim_opacity,
3009 clip_task_index,
3010 ctx,
3011 render_tasks,
3012 );
3013 }
3014 }
3015 (None, SegmentDataKind::Shared(ref segment_data)) => {
3016 let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture(
3020 clip_task_index,
3021 render_tasks,
3022 ).unwrap();
3023
3024 let textures = BatchTextures {
3025 input: segment_data.textures,
3026 clip_mask,
3027 };
3028
3029 let batch_key = BatchKey {
3030 blend_mode,
3031 kind: BatchKind::Brush(params.batch_kind),
3032 textures,
3033 };
3034
3035 self.add_brush_instance_to_batches(
3036 batch_key,
3037 features,
3038 bounding_rect,
3039 z_id,
3040 INVALID_SEGMENT_INDEX,
3041 edge_aa_mask,
3042 clip_task_address,
3043 brush_flags | BrushFlags::PERSPECTIVE_INTERPOLATION,
3044 prim_header_index,
3045 segment_data.specific_resource_address,
3046 );
3047 }
3048 (None, SegmentDataKind::Instanced(..)) => {
3049 unreachable!();
3052 }
3053 }
3054 }
3055}
3056
3057enum SegmentDataKind {
3060 Shared(SegmentInstanceData),
3061 Instanced(SmallVec<[SegmentInstanceData; 8]>),
3062}
3063
3064struct BrushBatchParameters {
3067 batch_kind: BrushBatchKind,
3068 prim_user_data: [i32; 4],
3069 segment_data: SegmentDataKind,
3070}
3071
3072impl BrushBatchParameters {
3073 fn instanced(
3076 batch_kind: BrushBatchKind,
3077 prim_user_data: [i32; 4],
3078 segment_data: SmallVec<[SegmentInstanceData; 8]>,
3079 ) -> Self {
3080 BrushBatchParameters {
3081 batch_kind,
3082 prim_user_data,
3083 segment_data: SegmentDataKind::Instanced(segment_data),
3084 }
3085 }
3086
3087 fn shared(
3090 batch_kind: BrushBatchKind,
3091 textures: TextureSet,
3092 prim_user_data: [i32; 4],
3093 specific_resource_address: i32,
3094 ) -> Self {
3095 BrushBatchParameters {
3096 batch_kind,
3097 prim_user_data,
3098 segment_data: SegmentDataKind::Shared(
3099 SegmentInstanceData {
3100 textures,
3101 specific_resource_address,
3102 }
3103 ),
3104 }
3105 }
3106}
3107
3108#[cfg_attr(feature = "capture", derive(Serialize))]
3110#[cfg_attr(feature = "replay", derive(Deserialize))]
3111pub struct ClipMaskInstanceList {
3112 pub mask_instances_fast: FrameVec<MaskInstance>,
3113 pub mask_instances_slow: FrameVec<MaskInstance>,
3114
3115 pub mask_instances_fast_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3116 pub mask_instances_slow_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
3117
3118 pub image_mask_instances: FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>,
3119 pub image_mask_instances_with_scissor: FastHashMap<(DeviceIntRect, TextureSource), FrameVec<PrimitiveInstanceData>>,
3120}
3121
3122impl ClipMaskInstanceList {
3123 pub fn new(memory: &FrameMemory) -> Self {
3124 ClipMaskInstanceList {
3125 mask_instances_fast: memory.new_vec(),
3126 mask_instances_slow: memory.new_vec(),
3127 mask_instances_fast_with_scissor: FastHashMap::default(),
3128 mask_instances_slow_with_scissor: FastHashMap::default(),
3129 image_mask_instances: FastHashMap::default(),
3130 image_mask_instances_with_scissor: FastHashMap::default(),
3131 }
3132 }
3133
3134 pub fn is_empty(&self) -> bool {
3135 let ClipMaskInstanceList {
3138 mask_instances_fast,
3139 mask_instances_slow,
3140 mask_instances_fast_with_scissor,
3141 mask_instances_slow_with_scissor,
3142 image_mask_instances,
3143 image_mask_instances_with_scissor,
3144 } = self;
3145
3146 mask_instances_fast.is_empty()
3147 && mask_instances_slow.is_empty()
3148 && mask_instances_fast_with_scissor.is_empty()
3149 && mask_instances_slow_with_scissor.is_empty()
3150 && image_mask_instances.is_empty()
3151 && image_mask_instances_with_scissor.is_empty()
3152 }
3153}
3154
3155#[derive(Debug)]
3157#[cfg_attr(feature = "capture", derive(Serialize))]
3158#[cfg_attr(feature = "replay", derive(Deserialize))]
3159pub struct ClipBatchList {
3160 pub slow_rectangles: FrameVec<ClipMaskInstanceRect>,
3162 pub fast_rectangles: FrameVec<ClipMaskInstanceRect>,
3163 pub box_shadows: FastHashMap<TextureSource, FrameVec<ClipMaskInstanceBoxShadow>>,
3164}
3165
3166impl ClipBatchList {
3167 fn new(memory: &FrameMemory) -> Self {
3168 ClipBatchList {
3169 slow_rectangles: memory.new_vec(),
3170 fast_rectangles: memory.new_vec(),
3171 box_shadows: FastHashMap::default(),
3172 }
3173 }
3174
3175 pub fn is_empty(&self) -> bool {
3176 self.slow_rectangles.is_empty()
3177 && self.fast_rectangles.is_empty()
3178 && self.box_shadows.is_empty()
3179 }
3180}
3181
3182#[derive(Debug)]
3184#[cfg_attr(feature = "capture", derive(Serialize))]
3185#[cfg_attr(feature = "replay", derive(Deserialize))]
3186pub struct ClipBatcher {
3187 pub primary_clips: ClipBatchList,
3191 pub secondary_clips: ClipBatchList,
3194
3195 gpu_supports_fast_clears: bool,
3196}
3197
3198impl ClipBatcher {
3199 pub fn new(
3200 gpu_supports_fast_clears: bool,
3201 memory: &FrameMemory,
3202 ) -> Self {
3203 ClipBatcher {
3204 primary_clips: ClipBatchList::new(memory),
3205 secondary_clips: ClipBatchList::new(memory),
3206 gpu_supports_fast_clears,
3207 }
3208 }
3209
3210 pub fn add_clip_region(
3211 &mut self,
3212 local_pos: LayoutPoint,
3213 sub_rect: DeviceRect,
3214 clip_data: ClipData,
3215 task_origin: DevicePoint,
3216 screen_origin: DevicePoint,
3217 device_pixel_scale: f32,
3218 ) {
3219 let instance = ClipMaskInstanceRect {
3220 common: ClipMaskInstanceCommon {
3221 clip_transform_id: TransformPaletteId::IDENTITY,
3222 prim_transform_id: TransformPaletteId::IDENTITY,
3223 sub_rect,
3224 task_origin,
3225 screen_origin,
3226 device_pixel_scale,
3227 },
3228 local_pos,
3229 clip_data,
3230 };
3231
3232 self.primary_clips.slow_rectangles.push(instance);
3233 }
3234
3235 fn add_tiled_clip_mask(
3238 &mut self,
3239 mask_screen_rect: DeviceRect,
3240 local_clip_rect: LayoutRect,
3241 clip_spatial_node_index: SpatialNodeIndex,
3242 spatial_tree: &SpatialTree,
3243 world_rect: &WorldRect,
3244 global_device_pixel_scale: DevicePixelScale,
3245 common: &ClipMaskInstanceCommon,
3246 is_first_clip: bool,
3247 ) -> bool {
3248 if mask_screen_rect.area() < CLIP_RECTANGLE_AREA_THRESHOLD {
3250 return false;
3251 }
3252
3253 let mask_screen_rect_size = mask_screen_rect.size().to_i32();
3254 let clip_spatial_node = spatial_tree.get_spatial_node(clip_spatial_node_index);
3255
3256 if clip_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
3260 return false;
3261 }
3262
3263 let transform = spatial_tree.get_world_transform(
3266 clip_spatial_node_index,
3267 );
3268 let world_clip_rect = match project_rect(
3269 &transform.into_transform(),
3270 &local_clip_rect,
3271 &world_rect,
3272 ) {
3273 Some(rect) => rect,
3274 None => return false,
3275 };
3276
3277 let world_device_rect = world_clip_rect * global_device_pixel_scale;
3280 let x_tiles = (mask_screen_rect_size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3281 let y_tiles = (mask_screen_rect_size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE;
3282
3283 let mask_origin = mask_screen_rect.min.to_vector();
3287 let clip_list = self.get_batch_list(is_first_clip);
3288
3289 for y in 0 .. y_tiles {
3290 for x in 0 .. x_tiles {
3291 let p0 = DeviceIntPoint::new(
3292 x * CLIP_RECTANGLE_TILE_SIZE,
3293 y * CLIP_RECTANGLE_TILE_SIZE,
3294 );
3295 let p1 = DeviceIntPoint::new(
3296 (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.width),
3297 (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.height),
3298 );
3299 let normalized_sub_rect = DeviceIntRect {
3300 min: p0,
3301 max: p1,
3302 }.to_f32();
3303 let world_sub_rect = normalized_sub_rect.translate(mask_origin);
3304
3305 if !world_device_rect.contains_box(&world_sub_rect) {
3309 clip_list.slow_rectangles.push(ClipMaskInstanceRect {
3310 common: ClipMaskInstanceCommon {
3311 sub_rect: normalized_sub_rect,
3312 ..*common
3313 },
3314 local_pos: local_clip_rect.min,
3315 clip_data: ClipData::uniform(local_clip_rect.size(), 0.0, ClipMode::Clip),
3316 });
3317 }
3318 }
3319 }
3320
3321 true
3322 }
3323
3324 fn get_batch_list(
3327 &mut self,
3328 is_first_clip: bool,
3329 ) -> &mut ClipBatchList {
3330 if is_first_clip && !self.gpu_supports_fast_clears {
3331 &mut self.primary_clips
3332 } else {
3333 &mut self.secondary_clips
3334 }
3335 }
3336
3337 pub fn add(
3338 &mut self,
3339 clip_node_range: ClipNodeRange,
3340 root_spatial_node_index: SpatialNodeIndex,
3341 render_tasks: &RenderTaskGraph,
3342 gpu_cache: &GpuCache,
3343 clip_store: &ClipStore,
3344 transforms: &mut TransformPalette,
3345 actual_rect: DeviceRect,
3346 surface_device_pixel_scale: DevicePixelScale,
3347 task_origin: DevicePoint,
3348 screen_origin: DevicePoint,
3349 ctx: &RenderTargetContext,
3350 ) -> bool {
3351 let mut is_first_clip = true;
3352 let mut clear_to_one = false;
3353
3354 for i in 0 .. clip_node_range.count {
3355 let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
3356 let clip_node = &ctx.data_stores.clip[clip_instance.handle];
3357
3358 let clip_transform_id = transforms.get_id(
3359 clip_node.item.spatial_node_index,
3360 ctx.root_spatial_node_index,
3361 ctx.spatial_tree,
3362 );
3363
3364 let prim_transform_id = transforms.get_id(
3365 root_spatial_node_index,
3366 ctx.root_spatial_node_index,
3367 ctx.spatial_tree,
3368 );
3369
3370 let common = ClipMaskInstanceCommon {
3371 sub_rect: DeviceRect::from_size(actual_rect.size()),
3372 task_origin,
3373 screen_origin,
3374 device_pixel_scale: surface_device_pixel_scale.0,
3375 clip_transform_id,
3376 prim_transform_id,
3377 };
3378
3379 let added_clip = match clip_node.item.kind {
3380 ClipItemKind::Image { .. } => {
3381 unreachable!();
3382 }
3383 ClipItemKind::BoxShadow { ref source } => {
3384 let task_id = source
3385 .render_task
3386 .expect("bug: render task handle not allocated");
3387 let (uv_rect_address, texture) = render_tasks.resolve_location(task_id, gpu_cache).unwrap();
3388
3389 self.get_batch_list(is_first_clip)
3390 .box_shadows
3391 .entry(texture)
3392 .or_insert_with(|| ctx.frame_memory.new_vec())
3393 .push(ClipMaskInstanceBoxShadow {
3394 common,
3395 resource_address: uv_rect_address,
3396 shadow_data: BoxShadowData {
3397 src_rect_size: source.original_alloc_size,
3398 clip_mode: source.clip_mode as i32,
3399 stretch_mode_x: source.stretch_mode_x as i32,
3400 stretch_mode_y: source.stretch_mode_y as i32,
3401 dest_rect: source.prim_shadow_rect,
3402 },
3403 });
3404
3405 true
3406 }
3407 ClipItemKind::Rectangle { rect, mode: ClipMode::ClipOut } => {
3408 self.get_batch_list(is_first_clip)
3409 .slow_rectangles
3410 .push(ClipMaskInstanceRect {
3411 common,
3412 local_pos: rect.min,
3413 clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::ClipOut),
3414 });
3415
3416 true
3417 }
3418 ClipItemKind::Rectangle { rect, mode: ClipMode::Clip } => {
3419 if clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
3420 false
3421 } else {
3422 if self.add_tiled_clip_mask(
3423 actual_rect,
3424 rect,
3425 clip_node.item.spatial_node_index,
3426 ctx.spatial_tree,
3427 &ctx.screen_world_rect,
3428 ctx.global_device_pixel_scale,
3429 &common,
3430 is_first_clip,
3431 ) {
3432 clear_to_one |= is_first_clip;
3433 } else {
3434 self.get_batch_list(is_first_clip)
3435 .slow_rectangles
3436 .push(ClipMaskInstanceRect {
3437 common,
3438 local_pos: rect.min,
3439 clip_data: ClipData::uniform(rect.size(), 0.0, ClipMode::Clip),
3440 });
3441 }
3442
3443 true
3444 }
3445 }
3446 ClipItemKind::RoundedRectangle { rect, ref radius, mode, .. } => {
3447 let batch_list = self.get_batch_list(is_first_clip);
3448 let instance = ClipMaskInstanceRect {
3449 common,
3450 local_pos: rect.min,
3451 clip_data: ClipData::rounded_rect(rect.size(), radius, mode),
3452 };
3453 if clip_instance.flags.contains(ClipNodeFlags::USE_FAST_PATH) {
3454 batch_list.fast_rectangles.push(instance);
3455 } else {
3456 batch_list.slow_rectangles.push(instance);
3457 }
3458
3459 true
3460 }
3461 };
3462
3463 is_first_clip &= !added_clip;
3464 }
3465
3466 clear_to_one
3467 }
3468}
3469
3470impl<'a, 'rc> RenderTargetContext<'a, 'rc> {
3471 fn get_clip_task_and_texture(
3476 &self,
3477 clip_task_index: ClipTaskIndex,
3478 offset: i32,
3479 render_tasks: &RenderTaskGraph,
3480 ) -> Option<(RenderTaskAddress, TextureSource)> {
3481 match self.scratch.clip_mask_instances[clip_task_index.0 as usize + offset as usize] {
3482 ClipMaskKind::Mask(task_id) => {
3483 Some((
3484 task_id.into(),
3485 TextureSource::TextureCache(
3486 render_tasks[task_id].get_target_texture(),
3487 Swizzle::default(),
3488 )
3489 ))
3490 }
3491 ClipMaskKind::None => {
3492 Some((OPAQUE_TASK_ADDRESS, TextureSource::Invalid))
3493 }
3494 ClipMaskKind::Clipped => {
3495 None
3496 }
3497 }
3498 }
3499
3500 fn get_prim_clip_task_and_texture(
3503 &self,
3504 clip_task_index: ClipTaskIndex,
3505 render_tasks: &RenderTaskGraph,
3506 ) -> Option<(RenderTaskAddress, TextureSource)> {
3507 self.get_clip_task_and_texture(
3508 clip_task_index,
3509 0,
3510 render_tasks,
3511 )
3512 }
3513}
3514
3515impl CompositorSurfaceKind {
3516 fn needs_cutout(&self) -> bool {
3518 match self {
3519 CompositorSurfaceKind::Underlay => true,
3520 CompositorSurfaceKind::Overlay | CompositorSurfaceKind::Blit => false,
3521 }
3522 }
3523}