1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use api::{AlphaType, DeviceIntRect, DeviceIntSize, LayerToWorldScale};
6 use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
7 use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
8 use api::{LayerToWorldTransform, WorldPixel};
9 use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
10 use clip::{ClipSource, ClipStore, ClipWorkItem};
11 use clip_scroll_tree::{CoordinateSystemId};
12 use euclid::{TypedTransform3D, vec3};
13 use glyph_rasterizer::GlyphFormat;
14 use gpu_cache::{GpuCache, GpuCacheAddress};
15 use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex};
16 use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex};
17 use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
18 use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
19 use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive};
20 use plane_split::{BspSplitter, Polygon, Splitter};
21 use prim_store::{CachedGradient, ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
22 use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
23 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
24 use renderer::{BlendMode, ImageBufferKind};
25 use renderer::BLOCKS_PER_UV_RECT;
26 use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
27 use std::{usize, f32, i32};
28 use tiling::{RenderTargetContext};
29 use util::{MatrixHelpers, TransformedRectKind};
30
31 // Special sentinel value recognized by the shader. It is considered to be
32 // a dummy task that doesn't mask out anything.
33 const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
34
35 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
36 #[cfg_attr(feature = "capture", derive(Serialize))]
37 #[cfg_attr(feature = "replay", derive(Deserialize))]
38 pub enum TransformBatchKind {
39 TextRun(GlyphFormat),
40 Image(ImageBufferKind),
41 BorderCorner,
42 BorderEdge,
43 }
44
45 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
46 #[cfg_attr(feature = "capture", derive(Serialize))]
47 #[cfg_attr(feature = "replay", derive(Deserialize))]
48 pub enum BrushImageSourceKind {
49 Color = 0,
50 //Alpha = 1, // Unused for now, but left here as shaders need to match.
51 ColorAlphaMask = 2,
52 }
53
54 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
55 #[cfg_attr(feature = "capture", derive(Serialize))]
56 #[cfg_attr(feature = "replay", derive(Deserialize))]
57 pub enum BrushBatchKind {
58 Solid,
59 Line,
60 Image(ImageBufferKind),
61 Blend,
62 MixBlend {
63 task_id: RenderTaskId,
64 source_id: RenderTaskId,
65 backdrop_id: RenderTaskId,
66 },
67 YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
68 RadialGradient,
69 LinearGradient,
70 }
71
72 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
73 #[cfg_attr(feature = "capture", derive(Serialize))]
74 #[cfg_attr(feature = "replay", derive(Deserialize))]
75 pub enum BatchKind {
76 HardwareComposite,
77 SplitComposite,
78 Transformable(TransformedRectKind, TransformBatchKind),
79 Brush(BrushBatchKind),
80 }
81
82 /// Optional textures that can be used as a source in the shaders.
83 /// Textures that are not used by the batch are equal to TextureId::invalid().
84 #[derive(Copy, Clone, Debug)]
85 #[cfg_attr(feature = "capture", derive(Serialize))]
86 #[cfg_attr(feature = "replay", derive(Deserialize))]
87 pub struct BatchTextures {
88 pub colors: [SourceTexture; 3],
89 }
90
91 impl BatchTextures {
no_texture() -> Self92 pub fn no_texture() -> Self {
93 BatchTextures {
94 colors: [SourceTexture::Invalid; 3],
95 }
96 }
97
render_target_cache() -> Self98 pub fn render_target_cache() -> Self {
99 BatchTextures {
100 colors: [
101 SourceTexture::CacheRGBA8,
102 SourceTexture::CacheA8,
103 SourceTexture::Invalid,
104 ],
105 }
106 }
107
color(texture: SourceTexture) -> Self108 pub fn color(texture: SourceTexture) -> Self {
109 BatchTextures {
110 colors: [texture, texture, SourceTexture::Invalid],
111 }
112 }
113 }
114
115 #[derive(Copy, Clone, Debug)]
116 #[cfg_attr(feature = "capture", derive(Serialize))]
117 #[cfg_attr(feature = "replay", derive(Deserialize))]
118 pub struct BatchKey {
119 pub kind: BatchKind,
120 pub blend_mode: BlendMode,
121 pub textures: BatchTextures,
122 }
123
124 impl BatchKey {
new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self125 pub fn new(kind: BatchKind, blend_mode: BlendMode, textures: BatchTextures) -> Self {
126 BatchKey {
127 kind,
128 blend_mode,
129 textures,
130 }
131 }
132
is_compatible_with(&self, other: &BatchKey) -> bool133 pub fn is_compatible_with(&self, other: &BatchKey) -> bool {
134 self.kind == other.kind && self.blend_mode == other.blend_mode &&
135 textures_compatible(self.textures.colors[0], other.textures.colors[0]) &&
136 textures_compatible(self.textures.colors[1], other.textures.colors[1]) &&
137 textures_compatible(self.textures.colors[2], other.textures.colors[2])
138 }
139 }
140
141 #[inline]
textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool142 fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
143 t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2
144 }
145
146 pub struct AlphaBatchList {
147 pub batches: Vec<PrimitiveBatch>,
148 pub item_rects: Vec<Vec<DeviceIntRect>>,
149 }
150
151 impl AlphaBatchList {
new() -> Self152 fn new() -> Self {
153 AlphaBatchList {
154 batches: Vec::new(),
155 item_rects: Vec::new(),
156 }
157 }
158
get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec<PrimitiveInstance>159 pub fn get_suitable_batch(
160 &mut self,
161 key: BatchKey,
162 task_relative_bounding_rect: &DeviceIntRect,
163 ) -> &mut Vec<PrimitiveInstance> {
164 let mut selected_batch_index = None;
165
166 match (key.kind, key.blend_mode) {
167 (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelWithBgColor) |
168 (BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelVariableTextColor) => {
169 'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
170 // Subpixel text is drawn in two passes. Because of this, we need
171 // to check for overlaps with every batch (which is a bit different
172 // than the normal batching below).
173 for item_rect in &self.item_rects[batch_index] {
174 if item_rect.intersects(task_relative_bounding_rect) {
175 break 'outer_text;
176 }
177 }
178
179 if batch.key.is_compatible_with(&key) {
180 selected_batch_index = Some(batch_index);
181 break;
182 }
183 }
184 }
185 _ => {
186 'outer_default: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
187 // For normal batches, we only need to check for overlaps for batches
188 // other than the first batch we consider. If the first batch
189 // is compatible, then we know there isn't any potential overlap
190 // issues to worry about.
191 if batch.key.is_compatible_with(&key) {
192 selected_batch_index = Some(batch_index);
193 break;
194 }
195
196 // check for intersections
197 for item_rect in &self.item_rects[batch_index] {
198 if item_rect.intersects(task_relative_bounding_rect) {
199 break 'outer_default;
200 }
201 }
202 }
203 }
204 }
205
206 if selected_batch_index.is_none() {
207 let new_batch = PrimitiveBatch::new(key);
208 selected_batch_index = Some(self.batches.len());
209 self.batches.push(new_batch);
210 self.item_rects.push(Vec::new());
211 }
212
213 let selected_batch_index = selected_batch_index.unwrap();
214 self.item_rects[selected_batch_index].push(*task_relative_bounding_rect);
215 &mut self.batches[selected_batch_index].instances
216 }
217 }
218
219 pub struct OpaqueBatchList {
220 pub pixel_area_threshold_for_new_batch: f32,
221 pub batches: Vec<PrimitiveBatch>,
222 }
223
224 impl OpaqueBatchList {
new(pixel_area_threshold_for_new_batch: f32) -> Self225 fn new(pixel_area_threshold_for_new_batch: f32) -> Self {
226 OpaqueBatchList {
227 batches: Vec::new(),
228 pixel_area_threshold_for_new_batch,
229 }
230 }
231
get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect ) -> &mut Vec<PrimitiveInstance>232 pub fn get_suitable_batch(
233 &mut self,
234 key: BatchKey,
235 task_relative_bounding_rect: &DeviceIntRect
236 ) -> &mut Vec<PrimitiveInstance> {
237 let mut selected_batch_index = None;
238 let item_area = task_relative_bounding_rect.size.to_f32().area();
239
240 // If the area of this primitive is larger than the given threshold,
241 // then it is large enough to warrant breaking a batch for. In this
242 // case we just see if it can be added to the existing batch or
243 // create a new one.
244 if item_area > self.pixel_area_threshold_for_new_batch {
245 if let Some(ref batch) = self.batches.last() {
246 if batch.key.is_compatible_with(&key) {
247 selected_batch_index = Some(self.batches.len() - 1);
248 }
249 }
250 } else {
251 // Otherwise, look back through a reasonable number of batches.
252 for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
253 if batch.key.is_compatible_with(&key) {
254 selected_batch_index = Some(batch_index);
255 break;
256 }
257 }
258 }
259
260 if selected_batch_index.is_none() {
261 let new_batch = PrimitiveBatch::new(key);
262 selected_batch_index = Some(self.batches.len());
263 self.batches.push(new_batch);
264 }
265
266 let batch = &mut self.batches[selected_batch_index.unwrap()];
267
268 &mut batch.instances
269 }
270
finalize(&mut self)271 fn finalize(&mut self) {
272 // Reverse the instance arrays in the opaque batches
273 // to get maximum z-buffer efficiency by drawing
274 // front-to-back.
275 // TODO(gw): Maybe we can change the batch code to
276 // build these in reverse and avoid having
277 // to reverse the instance array here.
278 for batch in &mut self.batches {
279 batch.instances.reverse();
280 }
281 }
282 }
283
284 pub struct BatchList {
285 pub alpha_batch_list: AlphaBatchList,
286 pub opaque_batch_list: OpaqueBatchList,
287 pub combined_bounding_rect: DeviceIntRect,
288 }
289
290 impl BatchList {
new(screen_size: DeviceIntSize) -> Self291 pub fn new(screen_size: DeviceIntSize) -> Self {
292 // The threshold for creating a new batch is
293 // one quarter the screen size.
294 let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0;
295
296 BatchList {
297 alpha_batch_list: AlphaBatchList::new(),
298 opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
299 combined_bounding_rect: DeviceIntRect::zero(),
300 }
301 }
302
add_bounding_rect( &mut self, task_relative_bounding_rect: &DeviceIntRect, )303 fn add_bounding_rect(
304 &mut self,
305 task_relative_bounding_rect: &DeviceIntRect,
306 ) {
307 self.combined_bounding_rect = self.combined_bounding_rect.union(task_relative_bounding_rect);
308 }
309
get_suitable_batch( &mut self, key: BatchKey, task_relative_bounding_rect: &DeviceIntRect, ) -> &mut Vec<PrimitiveInstance>310 pub fn get_suitable_batch(
311 &mut self,
312 key: BatchKey,
313 task_relative_bounding_rect: &DeviceIntRect,
314 ) -> &mut Vec<PrimitiveInstance> {
315 self.add_bounding_rect(task_relative_bounding_rect);
316
317 match key.blend_mode {
318 BlendMode::None => {
319 self.opaque_batch_list
320 .get_suitable_batch(key, task_relative_bounding_rect)
321 }
322 BlendMode::Alpha |
323 BlendMode::PremultipliedAlpha |
324 BlendMode::PremultipliedDestOut |
325 BlendMode::SubpixelConstantTextColor(..) |
326 BlendMode::SubpixelVariableTextColor |
327 BlendMode::SubpixelWithBgColor |
328 BlendMode::SubpixelDualSource => {
329 self.alpha_batch_list
330 .get_suitable_batch(key, task_relative_bounding_rect)
331 }
332 }
333 }
334
finalize(&mut self)335 fn finalize(&mut self) {
336 self.opaque_batch_list.finalize()
337 }
338 }
339
340 #[cfg_attr(feature = "capture", derive(Serialize))]
341 #[cfg_attr(feature = "replay", derive(Deserialize))]
342 pub struct PrimitiveBatch {
343 pub key: BatchKey,
344 pub instances: Vec<PrimitiveInstance>,
345 }
346
347 impl PrimitiveBatch {
new(key: BatchKey) -> PrimitiveBatch348 fn new(key: BatchKey) -> PrimitiveBatch {
349 PrimitiveBatch {
350 key,
351 instances: Vec::new(),
352 }
353 }
354 }
355
356 #[cfg_attr(feature = "capture", derive(Serialize))]
357 #[cfg_attr(feature = "replay", derive(Deserialize))]
358 pub struct AlphaBatchContainer {
359 pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
360 pub opaque_batches: Vec<PrimitiveBatch>,
361 pub alpha_batches: Vec<PrimitiveBatch>,
362 pub target_rect: Option<DeviceIntRect>,
363 }
364
365 impl AlphaBatchContainer {
new(target_rect: Option<DeviceIntRect>) -> AlphaBatchContainer366 pub fn new(target_rect: Option<DeviceIntRect>) -> AlphaBatchContainer {
367 AlphaBatchContainer {
368 text_run_cache_prims: FastHashMap::default(),
369 opaque_batches: Vec::new(),
370 alpha_batches: Vec::new(),
371 target_rect,
372 }
373 }
374
merge(&mut self, builder: AlphaBatchBuilder)375 fn merge(&mut self, builder: AlphaBatchBuilder) {
376 self.text_run_cache_prims.extend(builder.text_run_cache_prims);
377
378 for other_batch in builder.batch_list.opaque_batch_list.batches {
379 let batch_index = self.opaque_batches.iter().position(|batch| {
380 batch.key.is_compatible_with(&other_batch.key)
381 });
382
383 match batch_index {
384 Some(batch_index) => {
385 self.opaque_batches[batch_index].instances.extend(other_batch.instances);
386 }
387 None => {
388 self.opaque_batches.push(other_batch);
389 }
390 }
391 }
392
393 let mut min_batch_index = 0;
394
395 for other_batch in builder.batch_list.alpha_batch_list.batches {
396 let batch_index = self.alpha_batches.iter().skip(min_batch_index).position(|batch| {
397 batch.key.is_compatible_with(&other_batch.key)
398 });
399
400 match batch_index {
401 Some(batch_index) => {
402 let batch_index = batch_index + min_batch_index;
403 self.alpha_batches[batch_index].instances.extend(other_batch.instances);
404 min_batch_index = batch_index;
405 }
406 None => {
407 self.alpha_batches.push(other_batch);
408 min_batch_index = self.alpha_batches.len();
409 }
410 }
411 }
412 }
413 }
414
415 /// Encapsulates the logic of building batches for items that are blended.
416 pub struct AlphaBatchBuilder {
417 pub batch_list: BatchList,
418 pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
419 glyph_fetch_buffer: Vec<GlyphFetchResult>,
420 target_rect: DeviceIntRect,
421 }
422
423 impl AlphaBatchBuilder {
new( screen_size: DeviceIntSize, target_rect: DeviceIntRect, ) -> Self424 pub fn new(
425 screen_size: DeviceIntSize,
426 target_rect: DeviceIntRect,
427 ) -> Self {
428 AlphaBatchBuilder {
429 batch_list: BatchList::new(screen_size),
430 glyph_fetch_buffer: Vec::new(),
431 text_run_cache_prims: FastHashMap::default(),
432 target_rect,
433 }
434 }
435
build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer>436 pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer> {
437 self.batch_list.finalize();
438
439 let task_relative_target_rect = DeviceIntRect::new(
440 DeviceIntPoint::zero(),
441 self.target_rect.size,
442 );
443
444 let can_merge = task_relative_target_rect.contains_rect(&self.batch_list.combined_bounding_rect);
445
446 if can_merge {
447 merged_batches.merge(self);
448 None
449 } else {
450 Some(AlphaBatchContainer {
451 alpha_batches: self.batch_list.alpha_batch_list.batches,
452 opaque_batches: self.batch_list.opaque_batch_list.batches,
453 target_rect: Some(self.target_rect),
454 text_run_cache_prims: self.text_run_cache_prims,
455 })
456 }
457 }
458
add_pic_to_batch( &mut self, pic: &PicturePrimitive, task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, deferred_resolves: &mut Vec<DeferredResolve>, )459 pub fn add_pic_to_batch(
460 &mut self,
461 pic: &PicturePrimitive,
462 task_id: RenderTaskId,
463 ctx: &RenderTargetContext,
464 gpu_cache: &mut GpuCache,
465 render_tasks: &RenderTaskTree,
466 deferred_resolves: &mut Vec<DeferredResolve>,
467 ) {
468 let task_address = render_tasks.get_task_address(task_id);
469
470 let task = &render_tasks[task_id];
471 let content_origin = match task.kind {
472 RenderTaskKind::Picture(ref pic_task) => {
473 pic_task.content_origin
474 }
475 _ => {
476 panic!("todo: tidy this up");
477 }
478 };
479
480 // Even though most of the time a splitter isn't used or needed,
481 // they are cheap to construct so we will always pass one down.
482 let mut splitter = BspSplitter::new();
483
484 // Add each run in this picture to the batch.
485 for run in &pic.runs {
486 let scroll_node = &ctx.clip_scroll_tree.nodes[run.clip_and_scroll.scroll_node_id.0];
487 let scroll_id = scroll_node.node_data_index;
488 self.add_run_to_batch(
489 run,
490 scroll_id,
491 ctx,
492 gpu_cache,
493 render_tasks,
494 task_id,
495 task_address,
496 deferred_resolves,
497 &mut splitter,
498 pic,
499 content_origin,
500 );
501 }
502
503 // Flush the accumulated plane splits onto the task tree.
504 // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
505 for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
506 let prim_index = PrimitiveIndex(poly.anchor);
507 debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
508 let pp = &poly.points;
509 let gpu_blocks = [
510 [pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
511 [pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
512 [pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
513 ];
514 let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
515 let key = BatchKey::new(
516 BatchKind::SplitComposite,
517 BlendMode::PremultipliedAlpha,
518 BatchTextures::no_texture(),
519 );
520 let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
521 let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
522 let batch = self.batch_list.get_suitable_batch(key, &pic_metadata.screen_rect.as_ref().expect("bug").clipped);
523
524 let render_task_id = pic.surface.expect("BUG: unexpected surface in splitting");
525 let source_task_address = render_tasks.get_task_address(render_task_id);
526 let gpu_address = gpu_handle.as_int(gpu_cache);
527
528 let instance = CompositePrimitiveInstance::new(
529 task_address,
530 source_task_address,
531 RenderTaskAddress(0),
532 gpu_address,
533 0,
534 prim_index.0 as i32,
535 0,
536 0,
537 );
538
539 batch.push(PrimitiveInstance::from(instance));
540 }
541 }
542
543 // Helper to add an entire primitive run to a batch list.
544 // TODO(gw): Restructure this so the param list isn't quite
545 // so daunting!
add_run_to_batch( &mut self, run: &PrimitiveRun, scroll_id: ClipScrollNodeIndex, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, task_id: RenderTaskId, task_address: RenderTaskAddress, deferred_resolves: &mut Vec<DeferredResolve>, splitter: &mut BspSplitter<f64, WorldPixel>, pic: &PicturePrimitive, content_origin: ContentOrigin, )546 fn add_run_to_batch(
547 &mut self,
548 run: &PrimitiveRun,
549 scroll_id: ClipScrollNodeIndex,
550 ctx: &RenderTargetContext,
551 gpu_cache: &mut GpuCache,
552 render_tasks: &RenderTaskTree,
553 task_id: RenderTaskId,
554 task_address: RenderTaskAddress,
555 deferred_resolves: &mut Vec<DeferredResolve>,
556 splitter: &mut BspSplitter<f64, WorldPixel>,
557 pic: &PicturePrimitive,
558 content_origin: ContentOrigin,
559 ) {
560 for i in 0 .. run.count {
561 let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
562
563 let metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
564
565 // Now that we walk the primitive runs in order to add
566 // items to batches, we need to check if they are
567 // visible here.
568 // We currently only support culling on normal (Image)
569 // picture types.
570 // TODO(gw): Support culling on shadow image types.
571 let is_image = match pic.kind {
572 PictureKind::Image { .. } => true,
573 PictureKind::TextShadow { .. } => false,
574 };
575
576 if !is_image || metadata.screen_rect.is_some() {
577 self.add_prim_to_batch(
578 metadata.clip_chain_rect_index,
579 scroll_id,
580 prim_index,
581 ctx,
582 gpu_cache,
583 render_tasks,
584 task_id,
585 task_address,
586 deferred_resolves,
587 splitter,
588 content_origin,
589 pic,
590 );
591 }
592 }
593 }
594
595 // Adds a primitive to a batch.
596 // It can recursively call itself in some situations, for
597 // example if it encounters a picture where the items
598 // in that picture are being drawn into the same target.
add_prim_to_batch( &mut self, clip_chain_rect_index: ClipChainRectIndex, scroll_id: ClipScrollNodeIndex, prim_index: PrimitiveIndex, ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, task_id: RenderTaskId, task_address: RenderTaskAddress, deferred_resolves: &mut Vec<DeferredResolve>, splitter: &mut BspSplitter<f64, WorldPixel>, content_origin: ContentOrigin, pic: &PicturePrimitive, )599 fn add_prim_to_batch(
600 &mut self,
601 clip_chain_rect_index: ClipChainRectIndex,
602 scroll_id: ClipScrollNodeIndex,
603 prim_index: PrimitiveIndex,
604 ctx: &RenderTargetContext,
605 gpu_cache: &mut GpuCache,
606 render_tasks: &RenderTaskTree,
607 task_id: RenderTaskId,
608 task_address: RenderTaskAddress,
609 deferred_resolves: &mut Vec<DeferredResolve>,
610 splitter: &mut BspSplitter<f64, WorldPixel>,
611 content_origin: ContentOrigin,
612 pic: &PicturePrimitive,
613 ) {
614 let z = prim_index.0 as i32;
615 let prim_metadata = ctx.prim_store.get_metadata(prim_index);
616 let scroll_node = &ctx.node_data[scroll_id.0 as usize];
617 // TODO(gw): Calculating this for every primitive is a bit
618 // wasteful. We should probably cache this in
619 // the scroll node...
620 let transform_kind = scroll_node.transform.transform_kind();
621
622 let task_relative_bounding_rect = match content_origin {
623 ContentOrigin::Screen(point) => {
624 // translate by content-origin
625 let screen_rect = prim_metadata.screen_rect.expect("bug");
626 DeviceIntRect::new(
627 DeviceIntPoint::new(
628 screen_rect.unclipped.origin.x - point.x,
629 screen_rect.unclipped.origin.y - point.y,
630 ),
631 screen_rect.unclipped.size,
632 )
633 }
634 ContentOrigin::Local(point) => {
635 // scale local rect by device pixel ratio
636 let content_rect = LayerRect::new(
637 LayerPoint::new(
638 prim_metadata.local_rect.origin.x - point.x,
639 prim_metadata.local_rect.origin.y - point.y,
640 ),
641 prim_metadata.local_rect.size,
642 );
643 (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round().to_i32()
644 }
645 };
646
647 let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location);
648 let no_textures = BatchTextures::no_texture();
649 let clip_task_address = prim_metadata
650 .clip_task_id
651 .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
652 let base_instance = SimplePrimitiveInstance::new(
653 prim_cache_address,
654 task_address,
655 clip_task_address,
656 clip_chain_rect_index,
657 scroll_id,
658 z,
659 );
660
661 let specified_blend_mode = ctx.prim_store.get_blend_mode(prim_metadata);
662
663 let non_segmented_blend_mode = if !prim_metadata.opacity.is_opaque ||
664 prim_metadata.clip_task_id.is_some() ||
665 transform_kind == TransformedRectKind::Complex {
666 specified_blend_mode
667 } else {
668 BlendMode::None
669 };
670
671 match prim_metadata.prim_kind {
672 PrimitiveKind::Brush => {
673 let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
674 if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
675 ctx.resource_cache,
676 gpu_cache,
677 deferred_resolves,
678 &ctx.cached_gradients,
679 ) {
680 self.add_brush_to_batch(
681 brush,
682 prim_metadata,
683 batch_kind,
684 specified_blend_mode,
685 non_segmented_blend_mode,
686 textures,
687 clip_chain_rect_index,
688 clip_task_address,
689 &task_relative_bounding_rect,
690 prim_cache_address,
691 scroll_id,
692 task_address,
693 transform_kind,
694 z,
695 render_tasks,
696 user_data,
697 );
698 }
699 }
700 PrimitiveKind::Border => {
701 let border_cpu =
702 &ctx.prim_store.cpu_borders[prim_metadata.cpu_prim_index.0];
703 // TODO(gw): Select correct blend mode for edges and corners!!
704 let corner_kind = BatchKind::Transformable(
705 transform_kind,
706 TransformBatchKind::BorderCorner,
707 );
708 let corner_key = BatchKey::new(corner_kind, non_segmented_blend_mode, no_textures);
709 let edge_kind = BatchKind::Transformable(
710 transform_kind,
711 TransformBatchKind::BorderEdge,
712 );
713 let edge_key = BatchKey::new(edge_kind, non_segmented_blend_mode, no_textures);
714
715 // Work around borrow ck on borrowing batch_list twice.
716 {
717 let batch =
718 self.batch_list.get_suitable_batch(corner_key, &task_relative_bounding_rect);
719 for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate()
720 {
721 let sub_index = i as i32;
722 match *instance_kind {
723 BorderCornerInstance::None => {}
724 BorderCornerInstance::Single => {
725 batch.push(base_instance.build(
726 sub_index,
727 BorderCornerSide::Both as i32,
728 0,
729 ));
730 }
731 BorderCornerInstance::Double => {
732 batch.push(base_instance.build(
733 sub_index,
734 BorderCornerSide::First as i32,
735 0,
736 ));
737 batch.push(base_instance.build(
738 sub_index,
739 BorderCornerSide::Second as i32,
740 0,
741 ));
742 }
743 }
744 }
745 }
746
747 let batch = self.batch_list.get_suitable_batch(edge_key, &task_relative_bounding_rect);
748 for (border_segment, instance_kind) in border_cpu.edges.iter().enumerate() {
749 match *instance_kind {
750 BorderEdgeKind::None => {},
751 _ => {
752 batch.push(base_instance.build(border_segment as i32, 0, 0));
753 }
754 }
755 }
756 }
757 PrimitiveKind::Image => {
758 let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0];
759
760 let cache_item = match image_cpu.source {
761 ImageSource::Default => {
762 resolve_image(
763 image_cpu.key.request,
764 ctx.resource_cache,
765 gpu_cache,
766 deferred_resolves,
767 )
768 }
769 ImageSource::Cache { ref item, .. } => {
770 item.clone()
771 }
772 };
773
774 if cache_item.texture_id == SourceTexture::Invalid {
775 warn!("Warnings: skip a PrimitiveKind::Image");
776 debug!("at {:?}.", task_relative_bounding_rect);
777 return;
778 }
779
780 let batch_kind = TransformBatchKind::Image(get_buffer_kind(cache_item.texture_id));
781 let key = BatchKey::new(
782 BatchKind::Transformable(transform_kind, batch_kind),
783 non_segmented_blend_mode,
784 BatchTextures {
785 colors: [
786 cache_item.texture_id,
787 SourceTexture::Invalid,
788 SourceTexture::Invalid,
789 ],
790 },
791 );
792 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
793 batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0));
794 }
795 PrimitiveKind::TextRun => {
796 let text_cpu =
797 &ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
798 let is_shadow = match pic.kind {
799 PictureKind::TextShadow { .. } => true,
800 PictureKind::Image { .. } => false,
801 };
802
803 // TODO(gw): It probably makes sense to base this decision on the content
804 // origin field in the future (once that's configurable).
805 let font_transform = if is_shadow {
806 None
807 } else {
808 Some(scroll_node.transform)
809 };
810
811 let font = text_cpu.get_font(
812 ctx.device_pixel_scale,
813 font_transform,
814 );
815
816 let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
817 let batch_list = &mut self.batch_list;
818 let text_run_cache_prims = &mut self.text_run_cache_prims;
819
820 ctx.resource_cache.fetch_glyphs(
821 font,
822 &text_cpu.glyph_keys,
823 glyph_fetch_buffer,
824 gpu_cache,
825 |texture_id, mut glyph_format, glyphs| {
826 debug_assert_ne!(texture_id, SourceTexture::Invalid);
827
828 // Ignore color and only sample alpha when shadowing.
829 if text_cpu.shadow {
830 glyph_format = glyph_format.ignore_color();
831 }
832
833 let subpx_dir = match glyph_format {
834 GlyphFormat::Bitmap |
835 GlyphFormat::ColorBitmap => SubpixelDirection::None,
836 _ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
837 };
838
839 let batch = if is_shadow {
840 text_run_cache_prims
841 .entry(texture_id)
842 .or_insert(Vec::new())
843 } else {
844 let textures = BatchTextures {
845 colors: [
846 texture_id,
847 SourceTexture::Invalid,
848 SourceTexture::Invalid,
849 ],
850 };
851
852 let kind = BatchKind::Transformable(
853 transform_kind,
854 TransformBatchKind::TextRun(glyph_format),
855 );
856
857 let blend_mode = match glyph_format {
858 GlyphFormat::Subpixel |
859 GlyphFormat::TransformedSubpixel => {
860 if text_cpu.font.bg_color.a != 0 {
861 BlendMode::SubpixelWithBgColor
862 } else if ctx.use_dual_source_blending {
863 BlendMode::SubpixelDualSource
864 } else {
865 BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
866 }
867 }
868 GlyphFormat::Alpha |
869 GlyphFormat::TransformedAlpha |
870 GlyphFormat::Bitmap |
871 GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
872 };
873
874 let key = BatchKey::new(kind, blend_mode, textures);
875 batch_list.get_suitable_batch(key, &task_relative_bounding_rect)
876 };
877
878 for glyph in glyphs {
879 batch.push(base_instance.build(
880 glyph.index_in_text_run,
881 glyph.uv_rect_address.as_int(),
882 subpx_dir as u32 as i32,
883 ));
884 }
885 },
886 );
887 }
888 PrimitiveKind::Picture => {
889 let picture =
890 &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
891
892 match picture.surface {
893 Some(cache_task_id) => {
894 let cache_task_address = render_tasks.get_task_address(cache_task_id);
895 let textures = BatchTextures::render_target_cache();
896
897 match picture.kind {
898 PictureKind::TextShadow { .. } => {
899 let kind = BatchKind::Brush(
900 BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
901 );
902 let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
903 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
904
905 let uv_rect_address = render_tasks[cache_task_id]
906 .get_texture_handle()
907 .as_int(gpu_cache);
908
909 let instance = BrushInstance {
910 picture_address: task_address,
911 prim_address: prim_cache_address,
912 clip_chain_rect_index,
913 scroll_id,
914 clip_task_address,
915 z,
916 segment_index: 0,
917 edge_flags: EdgeAaSegmentMask::empty(),
918 brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
919 user_data: [
920 uv_rect_address,
921 BrushImageSourceKind::Color as i32,
922 0,
923 ],
924 };
925 batch.push(PrimitiveInstance::from(instance));
926 }
927 PictureKind::Image {
928 composite_mode,
929 secondary_render_task_id,
930 is_in_3d_context,
931 reference_frame_index,
932 real_local_rect,
933 ref extra_gpu_data_handle,
934 ..
935 } => {
936 // If this picture is participating in a 3D rendering context,
937 // then don't add it to any batches here. Instead, create a polygon
938 // for it and add it to the current plane splitter.
939 if is_in_3d_context {
940 // Push into parent plane splitter.
941
942 let real_xf = &ctx.clip_scroll_tree
943 .nodes[reference_frame_index.0]
944 .world_content_transform
945 .into();
946 let polygon = make_polygon(
947 real_local_rect,
948 &real_xf,
949 prim_index.0,
950 );
951
952 splitter.add(polygon);
953
954 return;
955 }
956
957 // Depending on the composite mode of the picture, we generate the
958 // old style Composite primitive instances. In the future, we'll
959 // remove these and pass them through the brush batching pipeline.
960 // This will allow us to unify some of the shaders, apply clip masks
961 // when compositing pictures, and also correctly apply pixel snapping
962 // to picture compositing operations.
963 let source_id = cache_task_id;
964
965 match composite_mode.expect("bug: only composites here") {
966 PictureCompositeMode::Filter(filter) => {
967 match filter {
968 FilterOp::Blur(..) => {
969 let src_task_address = render_tasks.get_task_address(source_id);
970 let key = BatchKey::new(
971 BatchKind::HardwareComposite,
972 BlendMode::PremultipliedAlpha,
973 BatchTextures::render_target_cache(),
974 );
975 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
976 let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped;
977 let instance = CompositePrimitiveInstance::new(
978 task_address,
979 src_task_address,
980 RenderTaskAddress(0),
981 item_bounding_rect.origin.x,
982 item_bounding_rect.origin.y,
983 z,
984 item_bounding_rect.size.width,
985 item_bounding_rect.size.height,
986 );
987
988 batch.push(PrimitiveInstance::from(instance));
989 }
990 FilterOp::DropShadow(offset, _, _) => {
991 let kind = BatchKind::Brush(
992 BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
993 );
994 let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
995
996 let uv_rect_address = render_tasks[cache_task_id]
997 .get_texture_handle()
998 .as_int(gpu_cache);
999
1000 let instance = BrushInstance {
1001 picture_address: task_address,
1002 prim_address: prim_cache_address,
1003 clip_chain_rect_index,
1004 scroll_id,
1005 clip_task_address,
1006 z,
1007 segment_index: 0,
1008 edge_flags: EdgeAaSegmentMask::empty(),
1009 brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
1010 user_data: [
1011 uv_rect_address,
1012 BrushImageSourceKind::ColorAlphaMask as i32,
1013 0,
1014 ],
1015 };
1016
1017 {
1018 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1019 batch.push(PrimitiveInstance::from(instance));
1020 }
1021
1022 let secondary_id = secondary_render_task_id.expect("no secondary!?");
1023 let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
1024 debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
1025 let secondary_task_address = render_tasks.get_task_address(secondary_id);
1026 let secondary_textures = BatchTextures {
1027 colors: [
1028 SourceTexture::RenderTaskCache(saved_index),
1029 SourceTexture::Invalid,
1030 SourceTexture::Invalid,
1031 ],
1032 };
1033 let key = BatchKey::new(
1034 BatchKind::HardwareComposite,
1035 BlendMode::PremultipliedAlpha,
1036 secondary_textures,
1037 );
1038 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1039 let content_rect = prim_metadata.local_rect.translate(&-offset);
1040 let rect =
1041 (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round()
1042 .to_i32();
1043
1044 let instance = CompositePrimitiveInstance::new(
1045 task_address,
1046 secondary_task_address,
1047 RenderTaskAddress(0),
1048 rect.origin.x,
1049 rect.origin.y,
1050 z,
1051 rect.size.width,
1052 rect.size.height,
1053 );
1054
1055 batch.push(PrimitiveInstance::from(instance));
1056 }
1057 _ => {
1058 let key = BatchKey::new(
1059 BatchKind::Brush(BrushBatchKind::Blend),
1060 BlendMode::PremultipliedAlpha,
1061 BatchTextures::render_target_cache(),
1062 );
1063
1064 let (filter_mode, extra_cache_address) = match filter {
1065 FilterOp::Blur(..) => (0, 0),
1066 FilterOp::Contrast(..) => (1, 0),
1067 FilterOp::Grayscale(..) => (2, 0),
1068 FilterOp::HueRotate(..) => (3, 0),
1069 FilterOp::Invert(..) => (4, 0),
1070 FilterOp::Saturate(..) => (5, 0),
1071 FilterOp::Sepia(..) => (6, 0),
1072 FilterOp::Brightness(..) => (7, 0),
1073 FilterOp::Opacity(..) => (8, 0),
1074 FilterOp::DropShadow(..) => (9, 0),
1075 FilterOp::ColorMatrix(..) => {
1076 (10, extra_gpu_data_handle.as_int(gpu_cache))
1077 }
1078 };
1079
1080 let instance = BrushInstance {
1081 picture_address: task_address,
1082 prim_address: prim_cache_address,
1083 clip_chain_rect_index,
1084 scroll_id,
1085 clip_task_address,
1086 z,
1087 segment_index: 0,
1088 edge_flags: EdgeAaSegmentMask::empty(),
1089 brush_flags: BrushFlags::empty(),
1090 user_data: [
1091 cache_task_address.0 as i32,
1092 filter_mode,
1093 extra_cache_address,
1094 ],
1095 };
1096
1097 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1098 batch.push(PrimitiveInstance::from(instance));
1099 }
1100 }
1101 }
1102 PictureCompositeMode::MixBlend(mode) => {
1103 let backdrop_id = secondary_render_task_id.expect("no backdrop!?");
1104
1105 let key = BatchKey::new(
1106 BatchKind::Brush(
1107 BrushBatchKind::MixBlend {
1108 task_id,
1109 source_id,
1110 backdrop_id,
1111 },
1112 ),
1113 BlendMode::PremultipliedAlpha,
1114 BatchTextures::no_texture(),
1115 );
1116 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1117 let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
1118 let source_task_address = render_tasks.get_task_address(source_id);
1119
1120 let instance = BrushInstance {
1121 picture_address: task_address,
1122 prim_address: prim_cache_address,
1123 clip_chain_rect_index,
1124 scroll_id,
1125 clip_task_address,
1126 z,
1127 segment_index: 0,
1128 edge_flags: EdgeAaSegmentMask::empty(),
1129 brush_flags: BrushFlags::empty(),
1130 user_data: [
1131 mode as u32 as i32,
1132 backdrop_task_address.0 as i32,
1133 source_task_address.0 as i32,
1134 ],
1135 };
1136
1137 batch.push(PrimitiveInstance::from(instance));
1138 }
1139 PictureCompositeMode::Blit => {
1140 let src_task_address = render_tasks.get_task_address(source_id);
1141 let key = BatchKey::new(
1142 BatchKind::HardwareComposite,
1143 BlendMode::PremultipliedAlpha,
1144 BatchTextures::render_target_cache(),
1145 );
1146 let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
1147 let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped;
1148 let instance = CompositePrimitiveInstance::new(
1149 task_address,
1150 src_task_address,
1151 RenderTaskAddress(0),
1152 item_bounding_rect.origin.x,
1153 item_bounding_rect.origin.y,
1154 z,
1155 item_bounding_rect.size.width,
1156 item_bounding_rect.size.height,
1157 );
1158
1159 batch.push(PrimitiveInstance::from(instance));
1160 }
1161 }
1162 }
1163 }
1164 }
1165 None => {
1166 // If this picture is being drawn into an existing target (i.e. with
1167 // no composition operation), recurse and add to the current batch list.
1168 self.add_pic_to_batch(
1169 picture,
1170 task_id,
1171 ctx,
1172 gpu_cache,
1173 render_tasks,
1174 deferred_resolves,
1175 );
1176 }
1177 }
1178 }
1179 }
1180 }
1181
add_brush_to_batch( &mut self, brush: &BrushPrimitive, prim_metadata: &PrimitiveMetadata, batch_kind: BrushBatchKind, alpha_blend_mode: BlendMode, non_segmented_blend_mode: BlendMode, textures: BatchTextures, clip_chain_rect_index: ClipChainRectIndex, clip_task_address: RenderTaskAddress, task_relative_bounding_rect: &DeviceIntRect, prim_cache_address: GpuCacheAddress, scroll_id: ClipScrollNodeIndex, task_address: RenderTaskAddress, transform_kind: TransformedRectKind, z: i32, render_tasks: &RenderTaskTree, user_data: [i32; 3], )1182 fn add_brush_to_batch(
1183 &mut self,
1184 brush: &BrushPrimitive,
1185 prim_metadata: &PrimitiveMetadata,
1186 batch_kind: BrushBatchKind,
1187 alpha_blend_mode: BlendMode,
1188 non_segmented_blend_mode: BlendMode,
1189 textures: BatchTextures,
1190 clip_chain_rect_index: ClipChainRectIndex,
1191 clip_task_address: RenderTaskAddress,
1192 task_relative_bounding_rect: &DeviceIntRect,
1193 prim_cache_address: GpuCacheAddress,
1194 scroll_id: ClipScrollNodeIndex,
1195 task_address: RenderTaskAddress,
1196 transform_kind: TransformedRectKind,
1197 z: i32,
1198 render_tasks: &RenderTaskTree,
1199 user_data: [i32; 3],
1200 ) {
1201 let base_instance = BrushInstance {
1202 picture_address: task_address,
1203 prim_address: prim_cache_address,
1204 clip_chain_rect_index,
1205 scroll_id,
1206 clip_task_address,
1207 z,
1208 segment_index: 0,
1209 edge_flags: EdgeAaSegmentMask::all(),
1210 brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
1211 user_data,
1212 };
1213
1214 self.batch_list.add_bounding_rect(task_relative_bounding_rect);
1215
1216 match brush.segment_desc {
1217 Some(ref segment_desc) => {
1218 let alpha_batch_key = BatchKey {
1219 blend_mode: alpha_blend_mode,
1220 kind: BatchKind::Brush(batch_kind),
1221 textures,
1222 };
1223
1224 let alpha_batch = self.batch_list.alpha_batch_list.get_suitable_batch(
1225 alpha_batch_key,
1226 task_relative_bounding_rect
1227 );
1228
1229 let opaque_batch_key = BatchKey {
1230 blend_mode: BlendMode::None,
1231 kind: BatchKind::Brush(batch_kind),
1232 textures,
1233 };
1234
1235 let opaque_batch = self.batch_list.opaque_batch_list.get_suitable_batch(
1236 opaque_batch_key,
1237 task_relative_bounding_rect
1238 );
1239
1240 for (i, segment) in segment_desc.segments.iter().enumerate() {
1241 let is_inner = segment.edge_flags.is_empty();
1242 let needs_blending = !prim_metadata.opacity.is_opaque ||
1243 segment.clip_task_id.is_some() ||
1244 (!is_inner && transform_kind == TransformedRectKind::Complex);
1245
1246 let clip_task_address = segment
1247 .clip_task_id
1248 .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
1249
1250 let instance = PrimitiveInstance::from(BrushInstance {
1251 segment_index: i as i32,
1252 edge_flags: segment.edge_flags,
1253 clip_task_address,
1254 ..base_instance
1255 });
1256
1257 if needs_blending {
1258 alpha_batch.push(instance);
1259 } else {
1260 opaque_batch.push(instance);
1261 }
1262 }
1263 }
1264 None => {
1265 let batch_key = BatchKey {
1266 blend_mode: non_segmented_blend_mode,
1267 kind: BatchKind::Brush(batch_kind),
1268 textures,
1269 };
1270 let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect);
1271 batch.push(PrimitiveInstance::from(base_instance));
1272 }
1273 }
1274 }
1275 }
1276
1277 impl BrushPrimitive {
get_batch_params( &self, resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec<DeferredResolve>, cached_gradients: &[CachedGradient], ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])>1278 fn get_batch_params(
1279 &self,
1280 resource_cache: &ResourceCache,
1281 gpu_cache: &mut GpuCache,
1282 deferred_resolves: &mut Vec<DeferredResolve>,
1283 cached_gradients: &[CachedGradient],
1284 ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> {
1285 match self.kind {
1286 BrushKind::Line { .. } => {
1287 Some((
1288 BrushBatchKind::Line,
1289 BatchTextures::no_texture(),
1290 [0; 3],
1291 ))
1292 }
1293 BrushKind::Image { request, .. } => {
1294 let cache_item = resolve_image(
1295 request,
1296 resource_cache,
1297 gpu_cache,
1298 deferred_resolves,
1299 );
1300
1301 if cache_item.texture_id == SourceTexture::Invalid {
1302 None
1303 } else {
1304 let textures = BatchTextures::color(cache_item.texture_id);
1305
1306 Some((
1307 BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
1308 textures,
1309 [
1310 cache_item.uv_rect_handle.as_int(gpu_cache),
1311 BrushImageSourceKind::Color as i32,
1312 0,
1313 ],
1314 ))
1315 }
1316 }
1317 BrushKind::Picture => {
1318 panic!("bug: get_batch_key is handled at higher level for pictures");
1319 }
1320 BrushKind::Solid { .. } => {
1321 Some((
1322 BrushBatchKind::Solid,
1323 BatchTextures::no_texture(),
1324 [0; 3],
1325 ))
1326 }
1327 BrushKind::Clear => {
1328 Some((
1329 BrushBatchKind::Solid,
1330 BatchTextures::no_texture(),
1331 [0; 3],
1332 ))
1333 }
1334 BrushKind::RadialGradient { gradient_index, .. } => {
1335 let stops_handle = &cached_gradients[gradient_index.0].handle;
1336 Some((
1337 BrushBatchKind::RadialGradient,
1338 BatchTextures::no_texture(),
1339 [
1340 stops_handle.as_int(gpu_cache),
1341 0,
1342 0,
1343 ],
1344 ))
1345 }
1346 BrushKind::LinearGradient { gradient_index, .. } => {
1347 let stops_handle = &cached_gradients[gradient_index.0].handle;
1348 Some((
1349 BrushBatchKind::LinearGradient,
1350 BatchTextures::no_texture(),
1351 [
1352 stops_handle.as_int(gpu_cache),
1353 0,
1354 0,
1355 ],
1356 ))
1357 }
1358 BrushKind::YuvImage { format, yuv_key, image_rendering, color_space } => {
1359 let mut textures = BatchTextures::no_texture();
1360 let mut uv_rect_addresses = [0; 3];
1361
1362 //yuv channel
1363 let channel_count = format.get_plane_num();
1364 debug_assert!(channel_count <= 3);
1365 for channel in 0 .. channel_count {
1366 let image_key = yuv_key[channel];
1367
1368 let cache_item = resolve_image(
1369 ImageRequest {
1370 key: image_key,
1371 rendering: image_rendering,
1372 tile: None,
1373 },
1374 resource_cache,
1375 gpu_cache,
1376 deferred_resolves,
1377 );
1378
1379 if cache_item.texture_id == SourceTexture::Invalid {
1380 warn!("Warnings: skip a PrimitiveKind::YuvImage");
1381 return None;
1382 }
1383
1384 textures.colors[channel] = cache_item.texture_id;
1385 uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
1386 }
1387
1388 // All yuv textures should be the same type.
1389 let buffer_kind = get_buffer_kind(textures.colors[0]);
1390 assert!(
1391 textures.colors[1 .. format.get_plane_num()]
1392 .iter()
1393 .all(|&tid| buffer_kind == get_buffer_kind(tid))
1394 );
1395
1396 let kind = BrushBatchKind::YuvImage(
1397 buffer_kind,
1398 format,
1399 color_space,
1400 );
1401
1402 Some((
1403 kind,
1404 textures,
1405 [
1406 uv_rect_addresses[0],
1407 uv_rect_addresses[1],
1408 uv_rect_addresses[2],
1409 ],
1410 ))
1411 }
1412 }
1413 }
1414 }
1415
1416 trait AlphaBatchHelpers {
get_blend_mode( &self, metadata: &PrimitiveMetadata, ) -> BlendMode1417 fn get_blend_mode(
1418 &self,
1419 metadata: &PrimitiveMetadata,
1420 ) -> BlendMode;
1421 }
1422
1423 impl AlphaBatchHelpers for PrimitiveStore {
get_blend_mode(&self, metadata: &PrimitiveMetadata) -> BlendMode1424 fn get_blend_mode(&self, metadata: &PrimitiveMetadata) -> BlendMode {
1425 match metadata.prim_kind {
1426 // Can only resolve the TextRun's blend mode once glyphs are fetched.
1427 PrimitiveKind::TextRun => {
1428 BlendMode::PremultipliedAlpha
1429 }
1430
1431 PrimitiveKind::Border |
1432 PrimitiveKind::Picture => {
1433 BlendMode::PremultipliedAlpha
1434 }
1435
1436 PrimitiveKind::Brush => {
1437 let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
1438 match brush.kind {
1439 BrushKind::Clear => {
1440 BlendMode::PremultipliedDestOut
1441 }
1442 BrushKind::Image { alpha_type, .. } => {
1443 match alpha_type {
1444 AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
1445 AlphaType::Alpha => BlendMode::Alpha,
1446 }
1447 }
1448 BrushKind::Solid { .. } |
1449 BrushKind::Line { .. } |
1450 BrushKind::YuvImage { .. } |
1451 BrushKind::RadialGradient { .. } |
1452 BrushKind::LinearGradient { .. } |
1453 BrushKind::Picture => {
1454 BlendMode::PremultipliedAlpha
1455 }
1456 }
1457 }
1458 PrimitiveKind::Image => {
1459 let image_cpu = &self.cpu_images[metadata.cpu_prim_index.0];
1460 match image_cpu.alpha_type {
1461 AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
1462 AlphaType::Alpha => BlendMode::Alpha,
1463 }
1464 }
1465 }
1466 }
1467 }
1468
resolve_image( request: ImageRequest, resource_cache: &ResourceCache, gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec<DeferredResolve>, ) -> CacheItem1469 pub fn resolve_image(
1470 request: ImageRequest,
1471 resource_cache: &ResourceCache,
1472 gpu_cache: &mut GpuCache,
1473 deferred_resolves: &mut Vec<DeferredResolve>,
1474 ) -> CacheItem {
1475 match resource_cache.get_image_properties(request.key) {
1476 Some(image_properties) => {
1477 // Check if an external image that needs to be resolved
1478 // by the render thread.
1479 match image_properties.external_image {
1480 Some(external_image) => {
1481 // This is an external texture - we will add it to
1482 // the deferred resolves list to be patched by
1483 // the render thread...
1484 let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT);
1485 let cache_item = CacheItem {
1486 texture_id: SourceTexture::External(external_image),
1487 uv_rect_handle: cache_handle,
1488 uv_rect: DeviceUintRect::new(
1489 DeviceUintPoint::zero(),
1490 DeviceUintSize::new(
1491 image_properties.descriptor.width,
1492 image_properties.descriptor.height,
1493 )
1494 ),
1495 texture_layer: 0,
1496 };
1497
1498 deferred_resolves.push(DeferredResolve {
1499 image_properties,
1500 address: gpu_cache.get_address(&cache_handle),
1501 });
1502
1503 cache_item
1504 }
1505 None => {
1506 if let Ok(cache_item) = resource_cache.get_cached_image(request) {
1507 cache_item
1508 } else {
1509 // There is no usable texture entry for the image key. Just return an invalid texture here.
1510 CacheItem::invalid()
1511 }
1512 }
1513 }
1514 }
1515 None => {
1516 CacheItem::invalid()
1517 }
1518 }
1519 }
1520
1521 /// Construct a polygon from stacking context boundaries.
1522 /// `anchor` here is an index that's going to be preserved in all the
1523 /// splits of the polygon.
make_polygon( rect: LayerRect, transform: &LayerToWorldTransform, anchor: usize, ) -> Polygon<f64, WorldPixel>1524 fn make_polygon(
1525 rect: LayerRect,
1526 transform: &LayerToWorldTransform,
1527 anchor: usize,
1528 ) -> Polygon<f64, WorldPixel> {
1529 let mat = TypedTransform3D::row_major(
1530 transform.m11 as f64,
1531 transform.m12 as f64,
1532 transform.m13 as f64,
1533 transform.m14 as f64,
1534 transform.m21 as f64,
1535 transform.m22 as f64,
1536 transform.m23 as f64,
1537 transform.m24 as f64,
1538 transform.m31 as f64,
1539 transform.m32 as f64,
1540 transform.m33 as f64,
1541 transform.m34 as f64,
1542 transform.m41 as f64,
1543 transform.m42 as f64,
1544 transform.m43 as f64,
1545 transform.m44 as f64);
1546 Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor)
1547 }
1548
1549 /// Batcher managing draw calls into the clip mask (in the RT cache).
1550 #[derive(Debug)]
1551 #[cfg_attr(feature = "capture", derive(Serialize))]
1552 #[cfg_attr(feature = "replay", derive(Deserialize))]
1553 pub struct ClipBatcher {
1554 /// Rectangle draws fill up the rectangles with rounded corners.
1555 pub rectangles: Vec<ClipMaskInstance>,
1556 /// Image draws apply the image masking.
1557 pub images: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
1558 pub border_clears: Vec<ClipMaskInstance>,
1559 pub borders: Vec<ClipMaskInstance>,
1560 pub box_shadows: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
1561 }
1562
1563 impl ClipBatcher {
new() -> Self1564 pub fn new() -> Self {
1565 ClipBatcher {
1566 rectangles: Vec::new(),
1567 images: FastHashMap::default(),
1568 border_clears: Vec::new(),
1569 borders: Vec::new(),
1570 box_shadows: FastHashMap::default(),
1571 }
1572 }
1573
add_clip_region( &mut self, task_address: RenderTaskAddress, clip_data_address: GpuCacheAddress, )1574 pub fn add_clip_region(
1575 &mut self,
1576 task_address: RenderTaskAddress,
1577 clip_data_address: GpuCacheAddress,
1578 ) {
1579 let instance = ClipMaskInstance {
1580 render_task_address: task_address,
1581 scroll_node_data_index: ClipScrollNodeIndex(0),
1582 segment: 0,
1583 clip_data_address,
1584 resource_address: GpuCacheAddress::invalid(),
1585 };
1586
1587 self.rectangles.push(instance);
1588 }
1589
add( &mut self, task_address: RenderTaskAddress, clips: &[ClipWorkItem], coordinate_system_id: CoordinateSystemId, resource_cache: &ResourceCache, gpu_cache: &GpuCache, clip_store: &ClipStore, )1590 pub fn add(
1591 &mut self,
1592 task_address: RenderTaskAddress,
1593 clips: &[ClipWorkItem],
1594 coordinate_system_id: CoordinateSystemId,
1595 resource_cache: &ResourceCache,
1596 gpu_cache: &GpuCache,
1597 clip_store: &ClipStore,
1598 ) {
1599 let mut coordinate_system_id = coordinate_system_id;
1600 for work_item in clips.iter() {
1601 let instance = ClipMaskInstance {
1602 render_task_address: task_address,
1603 scroll_node_data_index: work_item.scroll_node_data_index,
1604 segment: 0,
1605 clip_data_address: GpuCacheAddress::invalid(),
1606 resource_address: GpuCacheAddress::invalid(),
1607 };
1608 let info = clip_store
1609 .get_opt(&work_item.clip_sources)
1610 .expect("bug: clip handle should be valid");
1611
1612 for &(ref source, ref handle) in &info.clips {
1613 let gpu_address = gpu_cache.get_address(handle);
1614
1615 match *source {
1616 ClipSource::Image(ref mask) => {
1617 if let Ok(cache_item) = resource_cache.get_cached_image(
1618 ImageRequest {
1619 key: mask.image,
1620 rendering: ImageRendering::Auto,
1621 tile: None,
1622 }
1623 ) {
1624 self.images
1625 .entry(cache_item.texture_id)
1626 .or_insert(Vec::new())
1627 .push(ClipMaskInstance {
1628 clip_data_address: gpu_address,
1629 resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
1630 ..instance
1631 });
1632 } else {
1633 warn!("Warnings: skip a image mask");
1634 debug!("Key:{:?} Rect::{:?}", mask.image, mask.rect);
1635 continue;
1636 }
1637 }
1638 ClipSource::BoxShadow(ref info) => {
1639 debug_assert_ne!(info.cache_item.texture_id, SourceTexture::Invalid);
1640
1641 self.box_shadows
1642 .entry(info.cache_item.texture_id)
1643 .or_insert(Vec::new())
1644 .push(ClipMaskInstance {
1645 clip_data_address: gpu_address,
1646 resource_address: gpu_cache.get_address(&info.cache_item.uv_rect_handle),
1647 ..instance
1648 });
1649 }
1650 ClipSource::Rectangle(..) => {
1651 if work_item.coordinate_system_id != coordinate_system_id {
1652 self.rectangles.push(ClipMaskInstance {
1653 clip_data_address: gpu_address,
1654 ..instance
1655 });
1656 coordinate_system_id = work_item.coordinate_system_id;
1657 }
1658 }
1659 ClipSource::RoundedRectangle(..) => {
1660 self.rectangles.push(ClipMaskInstance {
1661 clip_data_address: gpu_address,
1662 ..instance
1663 });
1664 }
1665 ClipSource::BorderCorner(ref source) => {
1666 self.border_clears.push(ClipMaskInstance {
1667 clip_data_address: gpu_address,
1668 segment: 0,
1669 ..instance
1670 });
1671 for clip_index in 0 .. source.actual_clip_count {
1672 self.borders.push(ClipMaskInstance {
1673 clip_data_address: gpu_address,
1674 segment: 1 + clip_index as i32,
1675 ..instance
1676 })
1677 }
1678 }
1679 }
1680 }
1681 }
1682 }
1683 }
1684
get_buffer_kind(texture: SourceTexture) -> ImageBufferKind1685 fn get_buffer_kind(texture: SourceTexture) -> ImageBufferKind {
1686 match texture {
1687 SourceTexture::External(ext_image) => {
1688 match ext_image.image_type {
1689 ExternalImageType::TextureHandle(target) => {
1690 target.into()
1691 }
1692 ExternalImageType::Buffer => {
1693 // The ExternalImageType::Buffer should be handled by resource_cache.
1694 // It should go through the non-external case.
1695 panic!("Unexpected non-texture handle type");
1696 }
1697 }
1698 }
1699 _ => ImageBufferKind::Texture2DArray,
1700 }
1701 }
1702