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::{BorderRadius, ClipMode, ColorF, ColorU, RasterSpace};
6 use api::{ImageRendering, RepeatMode, PrimitiveFlags};
7 use api::{PremultipliedColorF, PropertyBinding, Shadow};
8 use api::{PrimitiveKeyKind, FillRule, POLYGON_CLIP_VERTEX_MAX};
9 use api::units::*;
10 use euclid::{SideOffsets2D, Size2D};
11 use malloc_size_of::MallocSizeOf;
12 use crate::segment::EdgeAaSegmentMask;
13 use crate::border::BorderSegmentCacheKey;
14 use crate::clip::{ClipChainId, ClipSet};
15 use crate::debug_item::{DebugItem, DebugMessage};
16 use crate::debug_colors;
17 use crate::scene_building::{CreateShadow, IsVisible};
18 use crate::frame_builder::FrameBuildingState;
19 use crate::glyph_rasterizer::GlyphKey;
20 use crate::gpu_cache::{GpuCacheAddress, GpuCacheHandle, GpuDataRequest};
21 use crate::gpu_types::{BrushFlags};
22 use crate::intern;
23 use crate::picture::PicturePrimitive;
24 #[cfg(debug_assertions)]
25 use crate::render_backend::{FrameId};
26 use crate::render_task_graph::RenderTaskId;
27 use crate::resource_cache::ImageProperties;
28 use crate::scene::SceneProperties;
29 use std::{hash, ops, u32, usize};
30 #[cfg(debug_assertions)]
31 use std::sync::atomic::{AtomicUsize, Ordering};
32 use crate::util::Recycler;
33 use crate::internal_types::LayoutPrimitiveInfo;
34 use crate::visibility::PrimitiveVisibility;
35
36 pub mod backdrop;
37 pub mod borders;
38 pub mod gradient;
39 pub mod image;
40 pub mod line_dec;
41 pub mod picture;
42 pub mod text_run;
43 pub mod interned;
44
45 mod storage;
46
47 use backdrop::BackdropDataHandle;
48 use borders::{ImageBorderDataHandle, NormalBorderDataHandle};
49 use gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle};
50 use image::{ImageDataHandle, ImageInstance, YuvImageDataHandle};
51 use line_dec::LineDecorationDataHandle;
52 use picture::PictureDataHandle;
53 use text_run::{TextRunDataHandle, TextRunPrimitive};
54
55 pub const VECS_PER_SEGMENT: usize = 2;
56
57 /// Counter for unique primitive IDs for debug tracing.
58 #[cfg(debug_assertions)]
59 static NEXT_PRIM_ID: AtomicUsize = AtomicUsize::new(0);
60
61 #[cfg(debug_assertions)]
62 static PRIM_CHASE_ID: AtomicUsize = AtomicUsize::new(usize::MAX);
63
64 #[cfg(debug_assertions)]
register_prim_chase_id(id: PrimitiveDebugId)65 pub fn register_prim_chase_id(id: PrimitiveDebugId) {
66 PRIM_CHASE_ID.store(id.0, Ordering::SeqCst);
67 }
68
69 #[cfg(not(debug_assertions))]
register_prim_chase_id(_: PrimitiveDebugId)70 pub fn register_prim_chase_id(_: PrimitiveDebugId) {
71 }
72
73 #[cfg_attr(feature = "capture", derive(Serialize))]
74 #[cfg_attr(feature = "replay", derive(Deserialize))]
75 #[derive(Debug, Copy, Clone, MallocSizeOf)]
76 pub struct PrimitiveOpacity {
77 pub is_opaque: bool,
78 }
79
80 impl PrimitiveOpacity {
opaque() -> PrimitiveOpacity81 pub fn opaque() -> PrimitiveOpacity {
82 PrimitiveOpacity { is_opaque: true }
83 }
84
translucent() -> PrimitiveOpacity85 pub fn translucent() -> PrimitiveOpacity {
86 PrimitiveOpacity { is_opaque: false }
87 }
88
from_alpha(alpha: f32) -> PrimitiveOpacity89 pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
90 PrimitiveOpacity {
91 is_opaque: alpha >= 1.0,
92 }
93 }
94
combine(self, other: PrimitiveOpacity) -> PrimitiveOpacity95 pub fn combine(self, other: PrimitiveOpacity) -> PrimitiveOpacity {
96 PrimitiveOpacity{
97 is_opaque: self.is_opaque && other.is_opaque
98 }
99 }
100 }
101
102 /// For external images, it's not possible to know the
103 /// UV coords of the image (or the image data itself)
104 /// until the render thread receives the frame and issues
105 /// callbacks to the client application. For external
106 /// images that are visible, a DeferredResolve is created
107 /// that is stored in the frame. This allows the render
108 /// thread to iterate this list and update any changed
109 /// texture data and update the UV rect. Any filtering
110 /// is handled externally for NativeTexture external
111 /// images.
112 #[cfg_attr(feature = "capture", derive(Serialize))]
113 #[cfg_attr(feature = "replay", derive(Deserialize))]
114 pub struct DeferredResolve {
115 pub address: GpuCacheAddress,
116 pub image_properties: ImageProperties,
117 pub rendering: ImageRendering,
118 }
119
120 #[derive(Debug, Copy, Clone, PartialEq)]
121 #[cfg_attr(feature = "capture", derive(Serialize))]
122 pub struct ClipTaskIndex(pub u16);
123
124 impl ClipTaskIndex {
125 pub const INVALID: ClipTaskIndex = ClipTaskIndex(0);
126 }
127
128 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, MallocSizeOf, Ord, PartialOrd)]
129 #[cfg_attr(feature = "capture", derive(Serialize))]
130 #[cfg_attr(feature = "replay", derive(Deserialize))]
131 pub struct PictureIndex(pub usize);
132
133 #[cfg_attr(feature = "capture", derive(Serialize))]
134 #[cfg_attr(feature = "replay", derive(Deserialize))]
135 #[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
136 pub struct RectangleKey {
137 pub x0: f32,
138 pub y0: f32,
139 pub x1: f32,
140 pub y1: f32,
141 }
142
143 impl RectangleKey {
intersects(&self, other: &Self) -> bool144 pub fn intersects(&self, other: &Self) -> bool {
145 self.x0 < other.x1
146 && other.x0 < self.x1
147 && self.y0 < other.y1
148 && other.y0 < self.y1
149 }
150 }
151
152 impl Eq for RectangleKey {}
153
154 impl hash::Hash for RectangleKey {
hash<H: hash::Hasher>(&self, state: &mut H)155 fn hash<H: hash::Hasher>(&self, state: &mut H) {
156 self.x0.to_bits().hash(state);
157 self.y0.to_bits().hash(state);
158 self.x1.to_bits().hash(state);
159 self.y1.to_bits().hash(state);
160 }
161 }
162
163 impl From<RectangleKey> for LayoutRect {
from(key: RectangleKey) -> LayoutRect164 fn from(key: RectangleKey) -> LayoutRect {
165 LayoutRect {
166 min: LayoutPoint::new(key.x0, key.y0),
167 max: LayoutPoint::new(key.x1, key.y1),
168 }
169 }
170 }
171
172 impl From<RectangleKey> for WorldRect {
from(key: RectangleKey) -> WorldRect173 fn from(key: RectangleKey) -> WorldRect {
174 WorldRect {
175 min: WorldPoint::new(key.x0, key.y0),
176 max: WorldPoint::new(key.x1, key.y1),
177 }
178 }
179 }
180
181 impl From<LayoutRect> for RectangleKey {
from(rect: LayoutRect) -> RectangleKey182 fn from(rect: LayoutRect) -> RectangleKey {
183 RectangleKey {
184 x0: rect.min.x,
185 y0: rect.min.y,
186 x1: rect.max.x,
187 y1: rect.max.y,
188 }
189 }
190 }
191
192 impl From<PictureRect> for RectangleKey {
from(rect: PictureRect) -> RectangleKey193 fn from(rect: PictureRect) -> RectangleKey {
194 RectangleKey {
195 x0: rect.min.x,
196 y0: rect.min.y,
197 x1: rect.max.x,
198 y1: rect.max.y,
199 }
200 }
201 }
202
203 impl From<WorldRect> for RectangleKey {
from(rect: WorldRect) -> RectangleKey204 fn from(rect: WorldRect) -> RectangleKey {
205 RectangleKey {
206 x0: rect.min.x,
207 y0: rect.min.y,
208 x1: rect.max.x,
209 y1: rect.max.y,
210 }
211 }
212 }
213
214 /// To create a fixed-size representation of a polygon, we use a fixed
215 /// number of points. Our initialization method restricts us to values
216 /// <= 32. If our constant POLYGON_CLIP_VERTEX_MAX is > 32, the Rust
217 /// compiler will complain.
218 #[cfg_attr(feature = "capture", derive(Serialize))]
219 #[cfg_attr(feature = "replay", derive(Deserialize))]
220 #[derive(Copy, Debug, Clone, Hash, MallocSizeOf, PartialEq)]
221 pub struct PolygonKey {
222 pub point_count: u8,
223 pub points: [PointKey; POLYGON_CLIP_VERTEX_MAX],
224 pub fill_rule: FillRule,
225 }
226
227 impl PolygonKey {
new( points_layout: &Vec<LayoutPoint>, fill_rule: FillRule, ) -> Self228 pub fn new(
229 points_layout: &Vec<LayoutPoint>,
230 fill_rule: FillRule,
231 ) -> Self {
232 // We have to fill fixed-size arrays with data from a Vec.
233 // We'll do this by initializing the arrays to known-good
234 // values then overwriting those values as long as our
235 // iterator provides values.
236 let mut points: [PointKey; POLYGON_CLIP_VERTEX_MAX] = [PointKey { x: 0.0, y: 0.0}; POLYGON_CLIP_VERTEX_MAX];
237
238 let mut point_count: u8 = 0;
239 for (src, dest) in points_layout.iter().zip(points.iter_mut()) {
240 *dest = (*src as LayoutPoint).into();
241 point_count = point_count + 1;
242 }
243
244 PolygonKey {
245 point_count,
246 points,
247 fill_rule,
248 }
249 }
250 }
251
252 impl Eq for PolygonKey {}
253
254 /// A hashable SideOffset2D that can be used in primitive keys.
255 #[cfg_attr(feature = "capture", derive(Serialize))]
256 #[cfg_attr(feature = "replay", derive(Deserialize))]
257 #[derive(Debug, Clone, MallocSizeOf, PartialEq)]
258 pub struct SideOffsetsKey {
259 pub top: f32,
260 pub right: f32,
261 pub bottom: f32,
262 pub left: f32,
263 }
264
265 impl Eq for SideOffsetsKey {}
266
267 impl hash::Hash for SideOffsetsKey {
hash<H: hash::Hasher>(&self, state: &mut H)268 fn hash<H: hash::Hasher>(&self, state: &mut H) {
269 self.top.to_bits().hash(state);
270 self.right.to_bits().hash(state);
271 self.bottom.to_bits().hash(state);
272 self.left.to_bits().hash(state);
273 }
274 }
275
276 impl From<SideOffsetsKey> for LayoutSideOffsets {
from(key: SideOffsetsKey) -> LayoutSideOffsets277 fn from(key: SideOffsetsKey) -> LayoutSideOffsets {
278 LayoutSideOffsets::new(
279 key.top,
280 key.right,
281 key.bottom,
282 key.left,
283 )
284 }
285 }
286
287 impl<U> From<SideOffsets2D<f32, U>> for SideOffsetsKey {
from(offsets: SideOffsets2D<f32, U>) -> SideOffsetsKey288 fn from(offsets: SideOffsets2D<f32, U>) -> SideOffsetsKey {
289 SideOffsetsKey {
290 top: offsets.top,
291 right: offsets.right,
292 bottom: offsets.bottom,
293 left: offsets.left,
294 }
295 }
296 }
297
298 /// A hashable size for using as a key during primitive interning.
299 #[cfg_attr(feature = "capture", derive(Serialize))]
300 #[cfg_attr(feature = "replay", derive(Deserialize))]
301 #[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
302 pub struct SizeKey {
303 w: f32,
304 h: f32,
305 }
306
307 impl Eq for SizeKey {}
308
309 impl hash::Hash for SizeKey {
hash<H: hash::Hasher>(&self, state: &mut H)310 fn hash<H: hash::Hasher>(&self, state: &mut H) {
311 self.w.to_bits().hash(state);
312 self.h.to_bits().hash(state);
313 }
314 }
315
316 impl From<SizeKey> for LayoutSize {
from(key: SizeKey) -> LayoutSize317 fn from(key: SizeKey) -> LayoutSize {
318 LayoutSize::new(key.w, key.h)
319 }
320 }
321
322 impl<U> From<Size2D<f32, U>> for SizeKey {
from(size: Size2D<f32, U>) -> SizeKey323 fn from(size: Size2D<f32, U>) -> SizeKey {
324 SizeKey {
325 w: size.width,
326 h: size.height,
327 }
328 }
329 }
330
331 /// A hashable vec for using as a key during primitive interning.
332 #[cfg_attr(feature = "capture", derive(Serialize))]
333 #[cfg_attr(feature = "replay", derive(Deserialize))]
334 #[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)]
335 pub struct VectorKey {
336 pub x: f32,
337 pub y: f32,
338 }
339
340 impl Eq for VectorKey {}
341
342 impl hash::Hash for VectorKey {
hash<H: hash::Hasher>(&self, state: &mut H)343 fn hash<H: hash::Hasher>(&self, state: &mut H) {
344 self.x.to_bits().hash(state);
345 self.y.to_bits().hash(state);
346 }
347 }
348
349 impl From<VectorKey> for LayoutVector2D {
from(key: VectorKey) -> LayoutVector2D350 fn from(key: VectorKey) -> LayoutVector2D {
351 LayoutVector2D::new(key.x, key.y)
352 }
353 }
354
355 impl From<VectorKey> for WorldVector2D {
from(key: VectorKey) -> WorldVector2D356 fn from(key: VectorKey) -> WorldVector2D {
357 WorldVector2D::new(key.x, key.y)
358 }
359 }
360
361 impl From<LayoutVector2D> for VectorKey {
from(vec: LayoutVector2D) -> VectorKey362 fn from(vec: LayoutVector2D) -> VectorKey {
363 VectorKey {
364 x: vec.x,
365 y: vec.y,
366 }
367 }
368 }
369
370 impl From<WorldVector2D> for VectorKey {
from(vec: WorldVector2D) -> VectorKey371 fn from(vec: WorldVector2D) -> VectorKey {
372 VectorKey {
373 x: vec.x,
374 y: vec.y,
375 }
376 }
377 }
378
379 /// A hashable point for using as a key during primitive interning.
380 #[cfg_attr(feature = "capture", derive(Serialize))]
381 #[cfg_attr(feature = "replay", derive(Deserialize))]
382 #[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
383 pub struct PointKey {
384 pub x: f32,
385 pub y: f32,
386 }
387
388 impl Eq for PointKey {}
389
390 impl hash::Hash for PointKey {
hash<H: hash::Hasher>(&self, state: &mut H)391 fn hash<H: hash::Hasher>(&self, state: &mut H) {
392 self.x.to_bits().hash(state);
393 self.y.to_bits().hash(state);
394 }
395 }
396
397 impl From<PointKey> for LayoutPoint {
from(key: PointKey) -> LayoutPoint398 fn from(key: PointKey) -> LayoutPoint {
399 LayoutPoint::new(key.x, key.y)
400 }
401 }
402
403 impl From<LayoutPoint> for PointKey {
from(p: LayoutPoint) -> PointKey404 fn from(p: LayoutPoint) -> PointKey {
405 PointKey {
406 x: p.x,
407 y: p.y,
408 }
409 }
410 }
411
412 impl From<PicturePoint> for PointKey {
from(p: PicturePoint) -> PointKey413 fn from(p: PicturePoint) -> PointKey {
414 PointKey {
415 x: p.x,
416 y: p.y,
417 }
418 }
419 }
420
421 impl From<WorldPoint> for PointKey {
from(p: WorldPoint) -> PointKey422 fn from(p: WorldPoint) -> PointKey {
423 PointKey {
424 x: p.x,
425 y: p.y,
426 }
427 }
428 }
429
430 /// A hashable float for using as a key during primitive interning.
431 #[cfg_attr(feature = "capture", derive(Serialize))]
432 #[cfg_attr(feature = "replay", derive(Deserialize))]
433 #[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
434 pub struct FloatKey(f32);
435
436 impl Eq for FloatKey {}
437
438 impl hash::Hash for FloatKey {
hash<H: hash::Hasher>(&self, state: &mut H)439 fn hash<H: hash::Hasher>(&self, state: &mut H) {
440 self.0.to_bits().hash(state);
441 }
442 }
443
444
445 #[cfg_attr(feature = "capture", derive(Serialize))]
446 #[cfg_attr(feature = "replay", derive(Deserialize))]
447 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
448 pub struct PrimKeyCommonData {
449 pub flags: PrimitiveFlags,
450 pub prim_rect: RectangleKey,
451 }
452
453 impl From<&LayoutPrimitiveInfo> for PrimKeyCommonData {
from(info: &LayoutPrimitiveInfo) -> Self454 fn from(info: &LayoutPrimitiveInfo) -> Self {
455 PrimKeyCommonData {
456 flags: info.flags,
457 prim_rect: info.rect.into(),
458 }
459 }
460 }
461
462 #[cfg_attr(feature = "capture", derive(Serialize))]
463 #[cfg_attr(feature = "replay", derive(Deserialize))]
464 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
465 pub struct PrimKey<T: MallocSizeOf> {
466 pub common: PrimKeyCommonData,
467 pub kind: T,
468 }
469
470 #[cfg_attr(feature = "capture", derive(Serialize))]
471 #[cfg_attr(feature = "replay", derive(Deserialize))]
472 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
473 pub struct PrimitiveKey {
474 pub common: PrimKeyCommonData,
475 pub kind: PrimitiveKeyKind,
476 }
477
478 impl PrimitiveKey {
new( info: &LayoutPrimitiveInfo, kind: PrimitiveKeyKind, ) -> Self479 pub fn new(
480 info: &LayoutPrimitiveInfo,
481 kind: PrimitiveKeyKind,
482 ) -> Self {
483 PrimitiveKey {
484 common: info.into(),
485 kind,
486 }
487 }
488 }
489
490 impl intern::InternDebug for PrimitiveKey {}
491
492 /// The shared information for a given primitive. This is interned and retained
493 /// both across frames and display lists, by comparing the matching PrimitiveKey.
494 #[cfg_attr(feature = "capture", derive(Serialize))]
495 #[cfg_attr(feature = "replay", derive(Deserialize))]
496 #[derive(MallocSizeOf)]
497 pub enum PrimitiveTemplateKind {
498 Rectangle {
499 color: PropertyBinding<ColorF>,
500 },
501 Clear,
502 }
503
504 impl PrimitiveTemplateKind {
505 /// Write any GPU blocks for the primitive template to the given request object.
write_prim_gpu_blocks( &self, request: &mut GpuDataRequest, scene_properties: &SceneProperties, )506 pub fn write_prim_gpu_blocks(
507 &self,
508 request: &mut GpuDataRequest,
509 scene_properties: &SceneProperties,
510 ) {
511 match *self {
512 PrimitiveTemplateKind::Clear => {
513 // Opaque black with operator dest out
514 request.push(PremultipliedColorF::BLACK);
515 }
516 PrimitiveTemplateKind::Rectangle { ref color, .. } => {
517 request.push(scene_properties.resolve_color(color).premultiplied())
518 }
519 }
520 }
521 }
522
523 /// Construct the primitive template data from a primitive key. This
524 /// is invoked when a primitive key is created and the interner
525 /// doesn't currently contain a primitive with this key.
526 impl From<PrimitiveKeyKind> for PrimitiveTemplateKind {
from(kind: PrimitiveKeyKind) -> Self527 fn from(kind: PrimitiveKeyKind) -> Self {
528 match kind {
529 PrimitiveKeyKind::Clear => {
530 PrimitiveTemplateKind::Clear
531 }
532 PrimitiveKeyKind::Rectangle { color, .. } => {
533 PrimitiveTemplateKind::Rectangle {
534 color: color.into(),
535 }
536 }
537 }
538 }
539 }
540
541 #[cfg_attr(feature = "capture", derive(Serialize))]
542 #[cfg_attr(feature = "replay", derive(Deserialize))]
543 #[derive(MallocSizeOf)]
544 #[derive(Debug)]
545 pub struct PrimTemplateCommonData {
546 pub flags: PrimitiveFlags,
547 pub may_need_repetition: bool,
548 pub prim_rect: LayoutRect,
549 pub opacity: PrimitiveOpacity,
550 /// The GPU cache handle for a primitive template. Since this structure
551 /// is retained across display lists by interning, this GPU cache handle
552 /// also remains valid, which reduces the number of updates to the GPU
553 /// cache when a new display list is processed.
554 pub gpu_cache_handle: GpuCacheHandle,
555 }
556
557 impl PrimTemplateCommonData {
with_key_common(common: PrimKeyCommonData) -> Self558 pub fn with_key_common(common: PrimKeyCommonData) -> Self {
559 PrimTemplateCommonData {
560 flags: common.flags,
561 may_need_repetition: true,
562 prim_rect: common.prim_rect.into(),
563 gpu_cache_handle: GpuCacheHandle::new(),
564 opacity: PrimitiveOpacity::translucent(),
565 }
566 }
567 }
568
569 #[cfg_attr(feature = "capture", derive(Serialize))]
570 #[cfg_attr(feature = "replay", derive(Deserialize))]
571 #[derive(MallocSizeOf)]
572 pub struct PrimTemplate<T> {
573 pub common: PrimTemplateCommonData,
574 pub kind: T,
575 }
576
577 #[cfg_attr(feature = "capture", derive(Serialize))]
578 #[cfg_attr(feature = "replay", derive(Deserialize))]
579 #[derive(MallocSizeOf)]
580 pub struct PrimitiveTemplate {
581 pub common: PrimTemplateCommonData,
582 pub kind: PrimitiveTemplateKind,
583 }
584
585 impl ops::Deref for PrimitiveTemplate {
586 type Target = PrimTemplateCommonData;
deref(&self) -> &Self::Target587 fn deref(&self) -> &Self::Target {
588 &self.common
589 }
590 }
591
592 impl ops::DerefMut for PrimitiveTemplate {
deref_mut(&mut self) -> &mut Self::Target593 fn deref_mut(&mut self) -> &mut Self::Target {
594 &mut self.common
595 }
596 }
597
598 impl From<PrimitiveKey> for PrimitiveTemplate {
from(item: PrimitiveKey) -> Self599 fn from(item: PrimitiveKey) -> Self {
600 PrimitiveTemplate {
601 common: PrimTemplateCommonData::with_key_common(item.common),
602 kind: item.kind.into(),
603 }
604 }
605 }
606
607 impl PrimitiveTemplate {
608 /// Update the GPU cache for a given primitive template. This may be called multiple
609 /// times per frame, by each primitive reference that refers to this interned
610 /// template. The initial request call to the GPU cache ensures that work is only
611 /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, frame_state: &mut FrameBuildingState, scene_properties: &SceneProperties, )612 pub fn update(
613 &mut self,
614 frame_state: &mut FrameBuildingState,
615 scene_properties: &SceneProperties,
616 ) {
617 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
618 self.kind.write_prim_gpu_blocks(&mut request, scene_properties);
619 }
620
621 self.opacity = match self.kind {
622 PrimitiveTemplateKind::Clear => {
623 PrimitiveOpacity::translucent()
624 }
625 PrimitiveTemplateKind::Rectangle { ref color, .. } => {
626 PrimitiveOpacity::from_alpha(scene_properties.resolve_color(color).a)
627 }
628 };
629 }
630 }
631
632 type PrimitiveDataHandle = intern::Handle<PrimitiveKeyKind>;
633
634 impl intern::Internable for PrimitiveKeyKind {
635 type Key = PrimitiveKey;
636 type StoreData = PrimitiveTemplate;
637 type InternData = ();
638 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_PRIMITIVES;
639 }
640
641 impl InternablePrimitive for PrimitiveKeyKind {
into_key( self, info: &LayoutPrimitiveInfo, ) -> PrimitiveKey642 fn into_key(
643 self,
644 info: &LayoutPrimitiveInfo,
645 ) -> PrimitiveKey {
646 PrimitiveKey::new(info, self)
647 }
648
make_instance_kind( key: PrimitiveKey, data_handle: PrimitiveDataHandle, prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind649 fn make_instance_kind(
650 key: PrimitiveKey,
651 data_handle: PrimitiveDataHandle,
652 prim_store: &mut PrimitiveStore,
653 _reference_frame_relative_offset: LayoutVector2D,
654 ) -> PrimitiveInstanceKind {
655 match key.kind {
656 PrimitiveKeyKind::Clear => {
657 PrimitiveInstanceKind::Clear {
658 data_handle
659 }
660 }
661 PrimitiveKeyKind::Rectangle { color, .. } => {
662 let color_binding_index = match color {
663 PropertyBinding::Binding(..) => {
664 prim_store.color_bindings.push(color)
665 }
666 PropertyBinding::Value(..) => ColorBindingIndex::INVALID,
667 };
668 PrimitiveInstanceKind::Rectangle {
669 data_handle,
670 segment_instance_index: SegmentInstanceIndex::INVALID,
671 color_binding_index,
672 }
673 }
674 }
675 }
676 }
677
678 #[derive(Debug, MallocSizeOf)]
679 #[cfg_attr(feature = "capture", derive(Serialize))]
680 #[cfg_attr(feature = "replay", derive(Deserialize))]
681 pub struct VisibleMaskImageTile {
682 pub tile_offset: TileOffset,
683 pub tile_rect: LayoutRect,
684 }
685
686 #[derive(Debug)]
687 #[cfg_attr(feature = "capture", derive(Serialize))]
688 pub struct VisibleGradientTile {
689 pub handle: GpuCacheHandle,
690 pub local_rect: LayoutRect,
691 pub local_clip_rect: LayoutRect,
692 }
693
694 /// Information about how to cache a border segment,
695 /// along with the current render task cache entry.
696 #[cfg_attr(feature = "capture", derive(Serialize))]
697 #[cfg_attr(feature = "replay", derive(Deserialize))]
698 #[derive(Debug, MallocSizeOf)]
699 pub struct BorderSegmentInfo {
700 pub local_task_size: LayoutSize,
701 pub cache_key: BorderSegmentCacheKey,
702 }
703
704 /// Represents the visibility state of a segment (wrt clip masks).
705 #[cfg_attr(feature = "capture", derive(Serialize))]
706 #[derive(Debug, Clone)]
707 pub enum ClipMaskKind {
708 /// The segment has a clip mask, specified by the render task.
709 Mask(RenderTaskId),
710 /// The segment has no clip mask.
711 None,
712 /// The segment is made invisible / clipped completely.
713 Clipped,
714 }
715
716 #[cfg_attr(feature = "capture", derive(Serialize))]
717 #[cfg_attr(feature = "replay", derive(Deserialize))]
718 #[derive(Debug, Clone, MallocSizeOf)]
719 pub struct BrushSegment {
720 pub local_rect: LayoutRect,
721 pub may_need_clip_mask: bool,
722 pub edge_flags: EdgeAaSegmentMask,
723 pub extra_data: [f32; 4],
724 pub brush_flags: BrushFlags,
725 }
726
727 impl BrushSegment {
new( local_rect: LayoutRect, may_need_clip_mask: bool, edge_flags: EdgeAaSegmentMask, extra_data: [f32; 4], brush_flags: BrushFlags, ) -> Self728 pub fn new(
729 local_rect: LayoutRect,
730 may_need_clip_mask: bool,
731 edge_flags: EdgeAaSegmentMask,
732 extra_data: [f32; 4],
733 brush_flags: BrushFlags,
734 ) -> Self {
735 Self {
736 local_rect,
737 may_need_clip_mask,
738 edge_flags,
739 extra_data,
740 brush_flags,
741 }
742 }
743 }
744
745 #[derive(Debug, Clone)]
746 #[repr(C)]
747 #[cfg_attr(feature = "capture", derive(Serialize))]
748 #[cfg_attr(feature = "replay", derive(Deserialize))]
749 struct ClipRect {
750 rect: LayoutRect,
751 mode: f32,
752 }
753
754 #[derive(Debug, Clone)]
755 #[repr(C)]
756 #[cfg_attr(feature = "capture", derive(Serialize))]
757 #[cfg_attr(feature = "replay", derive(Deserialize))]
758 struct ClipCorner {
759 rect: LayoutRect,
760 outer_radius_x: f32,
761 outer_radius_y: f32,
762 inner_radius_x: f32,
763 inner_radius_y: f32,
764 }
765
766 impl ClipCorner {
uniform(rect: LayoutRect, outer_radius: f32, inner_radius: f32) -> ClipCorner767 fn uniform(rect: LayoutRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
768 ClipCorner {
769 rect,
770 outer_radius_x: outer_radius,
771 outer_radius_y: outer_radius,
772 inner_radius_x: inner_radius,
773 inner_radius_y: inner_radius,
774 }
775 }
776 }
777
778 #[derive(Debug, Clone)]
779 #[repr(C)]
780 #[cfg_attr(feature = "capture", derive(Serialize))]
781 #[cfg_attr(feature = "replay", derive(Deserialize))]
782 pub struct ClipData {
783 rect: ClipRect,
784 top_left: ClipCorner,
785 top_right: ClipCorner,
786 bottom_left: ClipCorner,
787 bottom_right: ClipCorner,
788 }
789
790 impl ClipData {
rounded_rect(size: LayoutSize, radii: &BorderRadius, mode: ClipMode) -> ClipData791 pub fn rounded_rect(size: LayoutSize, radii: &BorderRadius, mode: ClipMode) -> ClipData {
792 // TODO(gw): For simplicity, keep most of the clip GPU structs the
793 // same as they were, even though the origin is now always
794 // zero, since they are in the clip's local space. In future,
795 // we could reduce the GPU cache size of ClipData.
796 let rect = LayoutRect::from_size(size);
797
798 ClipData {
799 rect: ClipRect {
800 rect,
801 mode: mode as u32 as f32,
802 },
803 top_left: ClipCorner {
804 rect: LayoutRect::from_origin_and_size(
805 LayoutPoint::new(rect.min.x, rect.min.y),
806 LayoutSize::new(radii.top_left.width, radii.top_left.height),
807 ),
808 outer_radius_x: radii.top_left.width,
809 outer_radius_y: radii.top_left.height,
810 inner_radius_x: 0.0,
811 inner_radius_y: 0.0,
812 },
813 top_right: ClipCorner {
814 rect: LayoutRect::from_origin_and_size(
815 LayoutPoint::new(
816 rect.max.x - radii.top_right.width,
817 rect.min.y,
818 ),
819 LayoutSize::new(radii.top_right.width, radii.top_right.height),
820 ),
821 outer_radius_x: radii.top_right.width,
822 outer_radius_y: radii.top_right.height,
823 inner_radius_x: 0.0,
824 inner_radius_y: 0.0,
825 },
826 bottom_left: ClipCorner {
827 rect: LayoutRect::from_origin_and_size(
828 LayoutPoint::new(
829 rect.min.x,
830 rect.max.y - radii.bottom_left.height,
831 ),
832 LayoutSize::new(radii.bottom_left.width, radii.bottom_left.height),
833 ),
834 outer_radius_x: radii.bottom_left.width,
835 outer_radius_y: radii.bottom_left.height,
836 inner_radius_x: 0.0,
837 inner_radius_y: 0.0,
838 },
839 bottom_right: ClipCorner {
840 rect: LayoutRect::from_origin_and_size(
841 LayoutPoint::new(
842 rect.max.x - radii.bottom_right.width,
843 rect.max.y - radii.bottom_right.height,
844 ),
845 LayoutSize::new(radii.bottom_right.width, radii.bottom_right.height),
846 ),
847 outer_radius_x: radii.bottom_right.width,
848 outer_radius_y: radii.bottom_right.height,
849 inner_radius_x: 0.0,
850 inner_radius_y: 0.0,
851 },
852 }
853 }
854
uniform(size: LayoutSize, radius: f32, mode: ClipMode) -> ClipData855 pub fn uniform(size: LayoutSize, radius: f32, mode: ClipMode) -> ClipData {
856 // TODO(gw): For simplicity, keep most of the clip GPU structs the
857 // same as they were, even though the origin is now always
858 // zero, since they are in the clip's local space. In future,
859 // we could reduce the GPU cache size of ClipData.
860 let rect = LayoutRect::from_size(size);
861
862 ClipData {
863 rect: ClipRect {
864 rect,
865 mode: mode as u32 as f32,
866 },
867 top_left: ClipCorner::uniform(
868 LayoutRect::from_origin_and_size(
869 LayoutPoint::new(rect.min.x, rect.min.y),
870 LayoutSize::new(radius, radius),
871 ),
872 radius,
873 0.0,
874 ),
875 top_right: ClipCorner::uniform(
876 LayoutRect::from_origin_and_size(
877 LayoutPoint::new(rect.max.x - radius, rect.min.y),
878 LayoutSize::new(radius, radius),
879 ),
880 radius,
881 0.0,
882 ),
883 bottom_left: ClipCorner::uniform(
884 LayoutRect::from_origin_and_size(
885 LayoutPoint::new(rect.min.x, rect.max.y - radius),
886 LayoutSize::new(radius, radius),
887 ),
888 radius,
889 0.0,
890 ),
891 bottom_right: ClipCorner::uniform(
892 LayoutRect::from_origin_and_size(
893 LayoutPoint::new(
894 rect.max.x - radius,
895 rect.max.y - radius,
896 ),
897 LayoutSize::new(radius, radius),
898 ),
899 radius,
900 0.0,
901 ),
902 }
903 }
904 }
905
906 /// A hashable descriptor for nine-patches, used by image and
907 /// gradient borders.
908 #[derive(Debug, Clone, PartialEq, Eq, Hash, MallocSizeOf)]
909 #[cfg_attr(feature = "capture", derive(Serialize))]
910 #[cfg_attr(feature = "replay", derive(Deserialize))]
911 pub struct NinePatchDescriptor {
912 pub width: i32,
913 pub height: i32,
914 pub slice: DeviceIntSideOffsets,
915 pub fill: bool,
916 pub repeat_horizontal: RepeatMode,
917 pub repeat_vertical: RepeatMode,
918 pub outset: SideOffsetsKey,
919 pub widths: SideOffsetsKey,
920 }
921
922 impl IsVisible for PrimitiveKeyKind {
923 // Return true if the primary primitive is visible.
924 // Used to trivially reject non-visible primitives.
925 // TODO(gw): Currently, primitives other than those
926 // listed here are handled before the
927 // add_primitive() call. In the future
928 // we should move the logic for all other
929 // primitive types to use this.
is_visible(&self) -> bool930 fn is_visible(&self) -> bool {
931 match *self {
932 PrimitiveKeyKind::Clear => {
933 true
934 }
935 PrimitiveKeyKind::Rectangle { ref color, .. } => {
936 match *color {
937 PropertyBinding::Value(value) => value.a > 0,
938 PropertyBinding::Binding(..) => true,
939 }
940 }
941 }
942 }
943 }
944
945 impl CreateShadow for PrimitiveKeyKind {
946 // Create a clone of this PrimitiveContainer, applying whatever
947 // changes are necessary to the primitive to support rendering
948 // it as part of the supplied shadow.
create_shadow( &self, shadow: &Shadow, _: bool, _: RasterSpace, ) -> PrimitiveKeyKind949 fn create_shadow(
950 &self,
951 shadow: &Shadow,
952 _: bool,
953 _: RasterSpace,
954 ) -> PrimitiveKeyKind {
955 match *self {
956 PrimitiveKeyKind::Rectangle { .. } => {
957 PrimitiveKeyKind::Rectangle {
958 color: PropertyBinding::Value(shadow.color.into()),
959 }
960 }
961 PrimitiveKeyKind::Clear => {
962 panic!("bug: this prim is not supported in shadow contexts");
963 }
964 }
965 }
966 }
967
968 #[derive(Clone, Copy, Debug, PartialEq)]
969 #[cfg_attr(feature = "capture", derive(Serialize))]
970 #[cfg_attr(feature = "replay", derive(Deserialize))]
971 pub struct PrimitiveDebugId(pub usize);
972
973 #[derive(Debug)]
974 #[cfg_attr(feature = "capture", derive(Serialize))]
975 pub enum PrimitiveInstanceKind {
976 /// Direct reference to a Picture
977 Picture {
978 /// Handle to the common interned data for this primitive.
979 data_handle: PictureDataHandle,
980 pic_index: PictureIndex,
981 segment_instance_index: SegmentInstanceIndex,
982 },
983 /// A run of glyphs, with associated font parameters.
984 TextRun {
985 /// Handle to the common interned data for this primitive.
986 data_handle: TextRunDataHandle,
987 /// Index to the per instance scratch data for this primitive.
988 run_index: TextRunIndex,
989 },
990 /// A line decoration. cache_handle refers to a cached render
991 /// task handle, if this line decoration is not a simple solid.
992 LineDecoration {
993 /// Handle to the common interned data for this primitive.
994 data_handle: LineDecorationDataHandle,
995 // TODO(gw): For now, we need to store some information in
996 // the primitive instance that is created during
997 // prepare_prims and read during the batching pass.
998 // Once we unify the prepare_prims and batching to
999 // occur at the same time, we can remove most of
1000 // the things we store here in the instance, and
1001 // use them directly. This will remove cache_handle,
1002 // but also the opacity, clip_task_id etc below.
1003 render_task: Option<RenderTaskId>,
1004 },
1005 NormalBorder {
1006 /// Handle to the common interned data for this primitive.
1007 data_handle: NormalBorderDataHandle,
1008 render_task_ids: storage::Range<RenderTaskId>,
1009 },
1010 ImageBorder {
1011 /// Handle to the common interned data for this primitive.
1012 data_handle: ImageBorderDataHandle,
1013 },
1014 Rectangle {
1015 /// Handle to the common interned data for this primitive.
1016 data_handle: PrimitiveDataHandle,
1017 segment_instance_index: SegmentInstanceIndex,
1018 color_binding_index: ColorBindingIndex,
1019 },
1020 YuvImage {
1021 /// Handle to the common interned data for this primitive.
1022 data_handle: YuvImageDataHandle,
1023 segment_instance_index: SegmentInstanceIndex,
1024 is_compositor_surface: bool,
1025 },
1026 Image {
1027 /// Handle to the common interned data for this primitive.
1028 data_handle: ImageDataHandle,
1029 image_instance_index: ImageInstanceIndex,
1030 is_compositor_surface: bool,
1031 },
1032 /// Always rendered directly into the picture. This tends to be
1033 /// faster with SWGL.
1034 LinearGradient {
1035 /// Handle to the common interned data for this primitive.
1036 data_handle: LinearGradientDataHandle,
1037 visible_tiles_range: GradientTileRange,
1038 },
1039 /// Always rendered via a cached render task. Usually faster with
1040 /// a GPU.
1041 CachedLinearGradient {
1042 /// Handle to the common interned data for this primitive.
1043 data_handle: LinearGradientDataHandle,
1044 visible_tiles_range: GradientTileRange,
1045 },
1046 RadialGradient {
1047 /// Handle to the common interned data for this primitive.
1048 data_handle: RadialGradientDataHandle,
1049 visible_tiles_range: GradientTileRange,
1050 },
1051 ConicGradient {
1052 /// Handle to the common interned data for this primitive.
1053 data_handle: ConicGradientDataHandle,
1054 visible_tiles_range: GradientTileRange,
1055 },
1056 /// Clear out a rect, used for special effects.
1057 Clear {
1058 /// Handle to the common interned data for this primitive.
1059 data_handle: PrimitiveDataHandle,
1060 },
1061 /// Render a portion of a specified backdrop.
1062 Backdrop {
1063 data_handle: BackdropDataHandle,
1064 },
1065 }
1066
1067 #[derive(Debug)]
1068 #[cfg_attr(feature = "capture", derive(Serialize))]
1069 pub struct PrimitiveInstance {
1070 /// Identifies the kind of primitive this
1071 /// instance is, and references to where
1072 /// the relevant information for the primitive
1073 /// can be found.
1074 pub kind: PrimitiveInstanceKind,
1075
1076 #[cfg(debug_assertions)]
1077 pub id: PrimitiveDebugId,
1078
1079 /// The last frame ID (of the `RenderTaskGraph`) this primitive
1080 /// was prepared for rendering in.
1081 #[cfg(debug_assertions)]
1082 pub prepared_frame_id: FrameId,
1083
1084 /// All information and state related to clip(s) for this primitive
1085 pub clip_set: ClipSet,
1086
1087 /// Information related to the current visibility state of this
1088 /// primitive.
1089 // TODO(gw): Currently built each frame, but can be retained.
1090 // TODO(gw): Remove clipped_world_rect (use tile bounds to determine vis flags)
1091 pub vis: PrimitiveVisibility,
1092 }
1093
1094 impl PrimitiveInstance {
new( local_clip_rect: LayoutRect, kind: PrimitiveInstanceKind, clip_chain_id: ClipChainId, ) -> Self1095 pub fn new(
1096 local_clip_rect: LayoutRect,
1097 kind: PrimitiveInstanceKind,
1098 clip_chain_id: ClipChainId,
1099 ) -> Self {
1100 PrimitiveInstance {
1101 kind,
1102 #[cfg(debug_assertions)]
1103 prepared_frame_id: FrameId::INVALID,
1104 #[cfg(debug_assertions)]
1105 id: PrimitiveDebugId(NEXT_PRIM_ID.fetch_add(1, Ordering::Relaxed)),
1106 vis: PrimitiveVisibility::new(),
1107 clip_set: ClipSet {
1108 local_clip_rect,
1109 clip_chain_id,
1110 },
1111 }
1112 }
1113
1114 // Reset any pre-frame state for this primitive.
reset(&mut self)1115 pub fn reset(&mut self) {
1116 self.vis.reset();
1117 }
1118
clear_visibility(&mut self)1119 pub fn clear_visibility(&mut self) {
1120 self.vis.reset();
1121 }
1122
1123 #[cfg(debug_assertions)]
is_chased(&self) -> bool1124 pub fn is_chased(&self) -> bool {
1125 PRIM_CHASE_ID.load(Ordering::SeqCst) == self.id.0
1126 }
1127
1128 #[cfg(not(debug_assertions))]
is_chased(&self) -> bool1129 pub fn is_chased(&self) -> bool {
1130 false
1131 }
1132
uid(&self) -> intern::ItemUid1133 pub fn uid(&self) -> intern::ItemUid {
1134 match &self.kind {
1135 PrimitiveInstanceKind::Clear { data_handle, .. } |
1136 PrimitiveInstanceKind::Rectangle { data_handle, .. } => {
1137 data_handle.uid()
1138 }
1139 PrimitiveInstanceKind::Image { data_handle, .. } => {
1140 data_handle.uid()
1141 }
1142 PrimitiveInstanceKind::ImageBorder { data_handle, .. } => {
1143 data_handle.uid()
1144 }
1145 PrimitiveInstanceKind::LineDecoration { data_handle, .. } => {
1146 data_handle.uid()
1147 }
1148 PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
1149 data_handle.uid()
1150 }
1151 PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => {
1152 data_handle.uid()
1153 }
1154 PrimitiveInstanceKind::NormalBorder { data_handle, .. } => {
1155 data_handle.uid()
1156 }
1157 PrimitiveInstanceKind::Picture { data_handle, .. } => {
1158 data_handle.uid()
1159 }
1160 PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
1161 data_handle.uid()
1162 }
1163 PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
1164 data_handle.uid()
1165 }
1166 PrimitiveInstanceKind::TextRun { data_handle, .. } => {
1167 data_handle.uid()
1168 }
1169 PrimitiveInstanceKind::YuvImage { data_handle, .. } => {
1170 data_handle.uid()
1171 }
1172 PrimitiveInstanceKind::Backdrop { data_handle, .. } => {
1173 data_handle.uid()
1174 }
1175 }
1176 }
1177 }
1178
1179 #[cfg_attr(feature = "capture", derive(Serialize))]
1180 #[derive(Debug)]
1181 pub struct SegmentedInstance {
1182 pub gpu_cache_handle: GpuCacheHandle,
1183 pub segments_range: SegmentsRange,
1184 }
1185
1186 pub type GlyphKeyStorage = storage::Storage<GlyphKey>;
1187 pub type TextRunIndex = storage::Index<TextRunPrimitive>;
1188 pub type TextRunStorage = storage::Storage<TextRunPrimitive>;
1189 pub type ColorBindingIndex = storage::Index<PropertyBinding<ColorU>>;
1190 pub type ColorBindingStorage = storage::Storage<PropertyBinding<ColorU>>;
1191 pub type BorderHandleStorage = storage::Storage<RenderTaskId>;
1192 pub type SegmentStorage = storage::Storage<BrushSegment>;
1193 pub type SegmentsRange = storage::Range<BrushSegment>;
1194 pub type SegmentInstanceStorage = storage::Storage<SegmentedInstance>;
1195 pub type SegmentInstanceIndex = storage::Index<SegmentedInstance>;
1196 pub type ImageInstanceStorage = storage::Storage<ImageInstance>;
1197 pub type ImageInstanceIndex = storage::Index<ImageInstance>;
1198 pub type GradientTileStorage = storage::Storage<VisibleGradientTile>;
1199 pub type GradientTileRange = storage::Range<VisibleGradientTile>;
1200 pub type LinearGradientStorage = storage::Storage<LinearGradientPrimitive>;
1201
1202 /// Contains various vecs of data that is used only during frame building,
1203 /// where we want to recycle the memory each new display list, to avoid constantly
1204 /// re-allocating and moving memory around. Written during primitive preparation,
1205 /// and read during batching.
1206 #[cfg_attr(feature = "capture", derive(Serialize))]
1207 pub struct PrimitiveScratchBuffer {
1208 /// Contains a list of clip mask instance parameters
1209 /// per segment generated.
1210 pub clip_mask_instances: Vec<ClipMaskKind>,
1211
1212 /// List of glyphs keys that are allocated by each
1213 /// text run instance.
1214 pub glyph_keys: GlyphKeyStorage,
1215
1216 /// List of render task handles for border segment instances
1217 /// that have been added this frame.
1218 pub border_cache_handles: BorderHandleStorage,
1219
1220 /// A list of brush segments that have been built for this scene.
1221 pub segments: SegmentStorage,
1222
1223 /// A list of segment ranges and GPU cache handles for prim instances
1224 /// that have opted into segment building. In future, this should be
1225 /// removed in favor of segment building during primitive interning.
1226 pub segment_instances: SegmentInstanceStorage,
1227
1228 /// A list of visible tiles that tiled gradients use to store
1229 /// per-tile information.
1230 pub gradient_tiles: GradientTileStorage,
1231
1232 /// List of debug display items for rendering.
1233 pub debug_items: Vec<DebugItem>,
1234
1235 /// List of current debug messages to log on screen
1236 messages: Vec<DebugMessage>,
1237 }
1238
1239 impl Default for PrimitiveScratchBuffer {
default() -> Self1240 fn default() -> Self {
1241 PrimitiveScratchBuffer {
1242 clip_mask_instances: Vec::new(),
1243 glyph_keys: GlyphKeyStorage::new(0),
1244 border_cache_handles: BorderHandleStorage::new(0),
1245 segments: SegmentStorage::new(0),
1246 segment_instances: SegmentInstanceStorage::new(0),
1247 gradient_tiles: GradientTileStorage::new(0),
1248 debug_items: Vec::new(),
1249 messages: Vec::new(),
1250 }
1251 }
1252 }
1253
1254 impl PrimitiveScratchBuffer {
recycle(&mut self, recycler: &mut Recycler)1255 pub fn recycle(&mut self, recycler: &mut Recycler) {
1256 recycler.recycle_vec(&mut self.clip_mask_instances);
1257 self.glyph_keys.recycle(recycler);
1258 self.border_cache_handles.recycle(recycler);
1259 self.segments.recycle(recycler);
1260 self.segment_instances.recycle(recycler);
1261 self.gradient_tiles.recycle(recycler);
1262 recycler.recycle_vec(&mut self.debug_items);
1263 }
1264
begin_frame(&mut self)1265 pub fn begin_frame(&mut self) {
1266 // Clear the clip mask tasks for the beginning of the frame. Append
1267 // a single kind representing no clip mask, at the ClipTaskIndex::INVALID
1268 // location.
1269 self.clip_mask_instances.clear();
1270 self.clip_mask_instances.push(ClipMaskKind::None);
1271
1272 self.border_cache_handles.clear();
1273
1274 // TODO(gw): As in the previous code, the gradient tiles store GPU cache
1275 // handles that are cleared (and thus invalidated + re-uploaded)
1276 // every frame. This maintains the existing behavior, but we
1277 // should fix this in the future to retain handles.
1278 self.gradient_tiles.clear();
1279
1280 self.debug_items.clear();
1281 }
1282
end_frame(&mut self)1283 pub fn end_frame(&mut self) {
1284 const MSGS_TO_RETAIN: usize = 32;
1285 const TIME_TO_RETAIN: u64 = 2000000000;
1286 const LINE_HEIGHT: f32 = 20.0;
1287 const X0: f32 = 32.0;
1288 const Y0: f32 = 32.0;
1289 let now = time::precise_time_ns();
1290
1291 let msgs_to_remove = self.messages.len().max(MSGS_TO_RETAIN) - MSGS_TO_RETAIN;
1292 let mut msgs_removed = 0;
1293
1294 self.messages.retain(|msg| {
1295 if msgs_removed < msgs_to_remove {
1296 msgs_removed += 1;
1297 return false;
1298 }
1299
1300 if msg.timestamp + TIME_TO_RETAIN < now {
1301 return false;
1302 }
1303
1304 true
1305 });
1306
1307 let mut y = Y0 + self.messages.len() as f32 * LINE_HEIGHT;
1308 let shadow_offset = 1.0;
1309
1310 for msg in &self.messages {
1311 self.debug_items.push(DebugItem::Text {
1312 position: DevicePoint::new(X0 + shadow_offset, y + shadow_offset),
1313 color: debug_colors::BLACK,
1314 msg: msg.msg.clone(),
1315 });
1316
1317 self.debug_items.push(DebugItem::Text {
1318 position: DevicePoint::new(X0, y),
1319 color: debug_colors::RED,
1320 msg: msg.msg.clone(),
1321 });
1322
1323 y -= LINE_HEIGHT;
1324 }
1325 }
1326
1327 #[allow(dead_code)]
push_debug_rect( &mut self, rect: DeviceRect, outer_color: ColorF, inner_color: ColorF, )1328 pub fn push_debug_rect(
1329 &mut self,
1330 rect: DeviceRect,
1331 outer_color: ColorF,
1332 inner_color: ColorF,
1333 ) {
1334 self.debug_items.push(DebugItem::Rect {
1335 rect,
1336 outer_color,
1337 inner_color,
1338 });
1339 }
1340
1341 #[allow(dead_code)]
push_debug_string( &mut self, position: DevicePoint, color: ColorF, msg: String, )1342 pub fn push_debug_string(
1343 &mut self,
1344 position: DevicePoint,
1345 color: ColorF,
1346 msg: String,
1347 ) {
1348 self.debug_items.push(DebugItem::Text {
1349 position,
1350 color,
1351 msg,
1352 });
1353 }
1354
1355 #[allow(dead_code)]
log( &mut self, msg: String, )1356 pub fn log(
1357 &mut self,
1358 msg: String,
1359 ) {
1360 self.messages.push(DebugMessage {
1361 msg,
1362 timestamp: time::precise_time_ns(),
1363 })
1364 }
1365 }
1366
1367 #[cfg_attr(feature = "capture", derive(Serialize))]
1368 #[cfg_attr(feature = "replay", derive(Deserialize))]
1369 #[derive(Clone, Debug)]
1370 pub struct PrimitiveStoreStats {
1371 picture_count: usize,
1372 text_run_count: usize,
1373 image_count: usize,
1374 linear_gradient_count: usize,
1375 color_binding_count: usize,
1376 }
1377
1378 impl PrimitiveStoreStats {
empty() -> Self1379 pub fn empty() -> Self {
1380 PrimitiveStoreStats {
1381 picture_count: 0,
1382 text_run_count: 0,
1383 image_count: 0,
1384 linear_gradient_count: 0,
1385 color_binding_count: 0,
1386 }
1387 }
1388 }
1389
1390 #[cfg_attr(feature = "capture", derive(Serialize))]
1391 pub struct PrimitiveStore {
1392 pub pictures: Vec<PicturePrimitive>,
1393 pub text_runs: TextRunStorage,
1394 pub linear_gradients: LinearGradientStorage,
1395
1396 /// A list of image instances. These are stored separately as
1397 /// storing them inline in the instance makes the structure bigger
1398 /// for other types.
1399 pub images: ImageInstanceStorage,
1400
1401 /// animated color bindings for this primitive.
1402 pub color_bindings: ColorBindingStorage,
1403 }
1404
1405 impl PrimitiveStore {
new(stats: &PrimitiveStoreStats) -> PrimitiveStore1406 pub fn new(stats: &PrimitiveStoreStats) -> PrimitiveStore {
1407 PrimitiveStore {
1408 pictures: Vec::with_capacity(stats.picture_count),
1409 text_runs: TextRunStorage::new(stats.text_run_count),
1410 images: ImageInstanceStorage::new(stats.image_count),
1411 color_bindings: ColorBindingStorage::new(stats.color_binding_count),
1412 linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count),
1413 }
1414 }
1415
get_stats(&self) -> PrimitiveStoreStats1416 pub fn get_stats(&self) -> PrimitiveStoreStats {
1417 PrimitiveStoreStats {
1418 picture_count: self.pictures.len(),
1419 text_run_count: self.text_runs.len(),
1420 image_count: self.images.len(),
1421 linear_gradient_count: self.linear_gradients.len(),
1422 color_binding_count: self.color_bindings.len(),
1423 }
1424 }
1425
1426 #[allow(unused)]
print_picture_tree(&self, root: PictureIndex)1427 pub fn print_picture_tree(&self, root: PictureIndex) {
1428 use crate::print_tree::PrintTree;
1429 let mut pt = PrintTree::new("picture tree");
1430 self.pictures[root.0].print(&self.pictures, root, &mut pt);
1431 }
1432
1433 /// Returns the total count of primitive instances contained in pictures.
prim_count(&self) -> usize1434 pub fn prim_count(&self) -> usize {
1435 let mut prim_count = 0;
1436 for pic in &self.pictures {
1437 prim_count += pic.prim_list.prim_instances.len();
1438 }
1439 prim_count
1440 }
1441 }
1442
1443 /// Trait for primitives that are directly internable.
1444 /// see SceneBuilder::add_primitive<P>
1445 pub trait InternablePrimitive: intern::Internable<InternData = ()> + Sized {
1446 /// Build a new key from self with `info`.
into_key( self, info: &LayoutPrimitiveInfo, ) -> Self::Key1447 fn into_key(
1448 self,
1449 info: &LayoutPrimitiveInfo,
1450 ) -> Self::Key;
1451
make_instance_kind( key: Self::Key, data_handle: intern::Handle<Self>, prim_store: &mut PrimitiveStore, reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind1452 fn make_instance_kind(
1453 key: Self::Key,
1454 data_handle: intern::Handle<Self>,
1455 prim_store: &mut PrimitiveStore,
1456 reference_frame_relative_offset: LayoutVector2D,
1457 ) -> PrimitiveInstanceKind;
1458 }
1459
1460
1461 #[test]
1462 #[cfg(target_pointer_width = "64")]
test_struct_sizes()1463 fn test_struct_sizes() {
1464 use std::mem;
1465 // The sizes of these structures are critical for performance on a number of
1466 // talos stress tests. If you get a failure here on CI, there's two possibilities:
1467 // (a) You made a structure smaller than it currently is. Great work! Update the
1468 // test expectations and move on.
1469 // (b) You made a structure larger. This is not necessarily a problem, but should only
1470 // be done with care, and after checking if talos performance regresses badly.
1471 assert_eq!(mem::size_of::<PrimitiveInstance>(), 152, "PrimitiveInstance size changed");
1472 assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 24, "PrimitiveInstanceKind size changed");
1473 assert_eq!(mem::size_of::<PrimitiveTemplate>(), 56, "PrimitiveTemplate size changed");
1474 assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 28, "PrimitiveTemplateKind size changed");
1475 assert_eq!(mem::size_of::<PrimitiveKey>(), 36, "PrimitiveKey size changed");
1476 assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 16, "PrimitiveKeyKind size changed");
1477 }
1478