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