1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
6 use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
7 use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
8 use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
9 use api::{LineStyle, PremultipliedColorF, YuvColorSpace, YuvFormat};
10 use border::{BorderCornerInstance, BorderEdgeKind};
11 use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
12 use clip_scroll_node::ClipScrollNode;
13 use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
14 use clip::{ClipSourcesHandle, ClipWorkItem};
15 use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
16 use frame_builder::PrimitiveRunContext;
17 use glyph_rasterizer::{FontInstance, FontTransform};
18 use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
19 ToGpuBlocks};
20 use gpu_types::{ClipChainRectIndex};
21 use picture::{PictureKind, PicturePrimitive};
22 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind};
23 use render_task::RenderTaskId;
24 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
25 use resource_cache::{CacheItem, ImageProperties, ImageRequest, ResourceCache};
26 use segment::SegmentBuilder;
27 use std::{mem, usize};
28 use std::sync::Arc;
29 use util::{MatrixHelpers, WorldToLayerFastTransform, calculate_screen_bounding_rect};
30 use util::{pack_as_float, recycle_vec};
31
32
33 const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0;
34
35 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
36 pub struct ScrollNodeAndClipChain {
37 pub scroll_node_id: ClipScrollNodeIndex,
38 pub clip_chain_index: ClipChainIndex,
39 }
40
41 impl ScrollNodeAndClipChain {
new( scroll_node_id: ClipScrollNodeIndex, clip_chain_index: ClipChainIndex ) -> ScrollNodeAndClipChain42 pub fn new(
43 scroll_node_id: ClipScrollNodeIndex,
44 clip_chain_index: ClipChainIndex
45 ) -> ScrollNodeAndClipChain {
46 ScrollNodeAndClipChain { scroll_node_id, clip_chain_index }
47 }
48 }
49
50 #[derive(Debug)]
51 pub struct PrimitiveRun {
52 pub base_prim_index: PrimitiveIndex,
53 pub count: usize,
54 pub clip_and_scroll: ScrollNodeAndClipChain,
55 }
56
57 #[derive(Debug, Copy, Clone)]
58 pub struct PrimitiveOpacity {
59 pub is_opaque: bool,
60 }
61
62 impl PrimitiveOpacity {
opaque() -> PrimitiveOpacity63 pub fn opaque() -> PrimitiveOpacity {
64 PrimitiveOpacity { is_opaque: true }
65 }
66
translucent() -> PrimitiveOpacity67 pub fn translucent() -> PrimitiveOpacity {
68 PrimitiveOpacity { is_opaque: false }
69 }
70
from_alpha(alpha: f32) -> PrimitiveOpacity71 pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
72 PrimitiveOpacity {
73 is_opaque: alpha == 1.0,
74 }
75 }
76 }
77
78 #[derive(Debug, Copy, Clone)]
79 pub struct CachedGradientIndex(pub usize);
80
81 pub struct CachedGradient {
82 pub handle: GpuCacheHandle,
83 }
84
85 impl CachedGradient {
new() -> CachedGradient86 pub fn new() -> CachedGradient {
87 CachedGradient {
88 handle: GpuCacheHandle::new(),
89 }
90 }
91 }
92
93 // Represents the local space rect of a list of
94 // primitive runs. For most primitive runs, the
95 // primitive runs are attached to the parent they
96 // are declared in. However, when a primitive run
97 // is part of a 3d rendering context, it may get
98 // hoisted to a higher level in the picture tree.
99 // When this happens, we need to also calculate the
100 // local space rects in the original space. This
101 // allows constructing the true world space polygons
102 // for the primitive, to enable the plane splitting
103 // logic to work correctly.
104 // TODO(gw) In the future, we can probably simplify
105 // this - perhaps calculate the world space
106 // polygons directly and store internally
107 // in the picture structure.
108 #[derive(Debug)]
109 pub struct PrimitiveRunLocalRect {
110 pub local_rect_in_actual_parent_space: LayerRect,
111 pub local_rect_in_original_parent_space: LayerRect,
112 }
113
114 /// For external images, it's not possible to know the
115 /// UV coords of the image (or the image data itself)
116 /// until the render thread receives the frame and issues
117 /// callbacks to the client application. For external
118 /// images that are visible, a DeferredResolve is created
119 /// that is stored in the frame. This allows the render
120 /// thread to iterate this list and update any changed
121 /// texture data and update the UV rect.
122 #[cfg_attr(feature = "capture", derive(Serialize))]
123 #[cfg_attr(feature = "replay", derive(Deserialize))]
124 pub struct DeferredResolve {
125 pub address: GpuCacheAddress,
126 pub image_properties: ImageProperties,
127 }
128
129 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
130 pub struct SpecificPrimitiveIndex(pub usize);
131
132 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
133 #[cfg_attr(feature = "capture", derive(Serialize))]
134 #[cfg_attr(feature = "replay", derive(Deserialize))]
135 pub struct PrimitiveIndex(pub usize);
136
137 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
138 pub enum PrimitiveKind {
139 TextRun,
140 Image,
141 Border,
142 Picture,
143 Brush,
144 }
145
146 impl GpuCacheHandle {
as_int(&self, gpu_cache: &GpuCache) -> i32147 pub fn as_int(&self, gpu_cache: &GpuCache) -> i32 {
148 gpu_cache.get_address(self).as_int()
149 }
150 }
151
152 impl GpuCacheAddress {
as_int(&self) -> i32153 pub fn as_int(&self) -> i32 {
154 // TODO(gw): Temporarily encode GPU Cache addresses as a single int.
155 // In the future, we can change the PrimitiveInstance struct
156 // to use 2x u16 for the vertex attribute instead of an i32.
157 self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32
158 }
159 }
160
161 #[derive(Debug, Copy, Clone)]
162 pub struct ScreenRect {
163 pub clipped: DeviceIntRect,
164 pub unclipped: DeviceIntRect,
165 }
166
167 // TODO(gw): Pack the fields here better!
168 #[derive(Debug)]
169 pub struct PrimitiveMetadata {
170 pub opacity: PrimitiveOpacity,
171 pub clip_sources: Option<ClipSourcesHandle>,
172 pub prim_kind: PrimitiveKind,
173 pub cpu_prim_index: SpecificPrimitiveIndex,
174 pub gpu_location: GpuCacheHandle,
175 pub clip_task_id: Option<RenderTaskId>,
176
177 // TODO(gw): In the future, we should just pull these
178 // directly from the DL item, instead of
179 // storing them here.
180 pub local_rect: LayerRect,
181 pub local_clip_rect: LayerRect,
182 pub clip_chain_rect_index: ClipChainRectIndex,
183 pub is_backface_visible: bool,
184 pub screen_rect: Option<ScreenRect>,
185
186 /// A tag used to identify this primitive outside of WebRender. This is
187 /// used for returning useful data during hit testing.
188 pub tag: Option<ItemTag>,
189 }
190
191 #[derive(Debug)]
192 pub enum BrushKind {
193 Solid {
194 color: ColorF,
195 },
196 Clear,
197 Line {
198 color: PremultipliedColorF,
199 wavy_line_thickness: f32,
200 style: LineStyle,
201 orientation: LineOrientation,
202 },
203 Picture,
204 Image {
205 request: ImageRequest,
206 current_epoch: Epoch,
207 alpha_type: AlphaType,
208 },
209 YuvImage {
210 yuv_key: [ImageKey; 3],
211 format: YuvFormat,
212 color_space: YuvColorSpace,
213 image_rendering: ImageRendering,
214 },
215 RadialGradient {
216 gradient_index: CachedGradientIndex,
217 stops_range: ItemRange<GradientStop>,
218 extend_mode: ExtendMode,
219 start_center: LayerPoint,
220 end_center: LayerPoint,
221 start_radius: f32,
222 end_radius: f32,
223 ratio_xy: f32,
224 },
225 LinearGradient {
226 gradient_index: CachedGradientIndex,
227 stops_range: ItemRange<GradientStop>,
228 stops_count: usize,
229 extend_mode: ExtendMode,
230 reverse_stops: bool,
231 start_point: LayerPoint,
232 end_point: LayerPoint,
233 }
234 }
235
236 impl BrushKind {
supports_segments(&self) -> bool237 fn supports_segments(&self) -> bool {
238 match *self {
239 BrushKind::Solid { .. } |
240 BrushKind::Picture |
241 BrushKind::Image { .. } |
242 BrushKind::YuvImage { .. } |
243 BrushKind::RadialGradient { .. } |
244 BrushKind::LinearGradient { .. } => true,
245
246 BrushKind::Clear |
247 BrushKind::Line { .. } => false,
248 }
249 }
250 }
251
252 bitflags! {
253 /// Each bit of the edge AA mask is:
254 /// 0, when the edge of the primitive needs to be considered for AA
255 /// 1, when the edge of the segment needs to be considered for AA
256 ///
257 /// *Note*: the bit values have to match the shader logic in
258 /// `write_transform_vertex()` function.
259 pub struct EdgeAaSegmentMask: u8 {
260 const LEFT = 0x1;
261 const TOP = 0x2;
262 const RIGHT = 0x4;
263 const BOTTOM = 0x8;
264 }
265 }
266
267 #[derive(Debug)]
268 pub struct BrushSegment {
269 pub local_rect: LayerRect,
270 pub clip_task_id: Option<RenderTaskId>,
271 pub may_need_clip_mask: bool,
272 pub edge_flags: EdgeAaSegmentMask,
273 }
274
275 impl BrushSegment {
new( origin: LayerPoint, size: LayerSize, may_need_clip_mask: bool, edge_flags: EdgeAaSegmentMask, ) -> BrushSegment276 pub fn new(
277 origin: LayerPoint,
278 size: LayerSize,
279 may_need_clip_mask: bool,
280 edge_flags: EdgeAaSegmentMask,
281 ) -> BrushSegment {
282 BrushSegment {
283 local_rect: LayerRect::new(origin, size),
284 clip_task_id: None,
285 may_need_clip_mask,
286 edge_flags,
287 }
288 }
289 }
290
291 #[derive(Copy, Clone, Debug, PartialEq)]
292 pub enum BrushClipMaskKind {
293 Unknown,
294 Individual,
295 Global,
296 }
297
298 #[derive(Debug)]
299 pub struct BrushSegmentDescriptor {
300 pub segments: Vec<BrushSegment>,
301 pub clip_mask_kind: BrushClipMaskKind,
302 }
303
304 #[derive(Debug)]
305 pub struct BrushPrimitive {
306 pub kind: BrushKind,
307 pub segment_desc: Option<BrushSegmentDescriptor>,
308 }
309
310 impl BrushPrimitive {
new( kind: BrushKind, segment_desc: Option<BrushSegmentDescriptor>, ) -> BrushPrimitive311 pub fn new(
312 kind: BrushKind,
313 segment_desc: Option<BrushSegmentDescriptor>,
314 ) -> BrushPrimitive {
315 BrushPrimitive {
316 kind,
317 segment_desc,
318 }
319 }
320
write_gpu_blocks(&self, request: &mut GpuDataRequest)321 fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
322 // has to match VECS_PER_SPECIFIC_BRUSH
323 match self.kind {
324 BrushKind::Picture |
325 BrushKind::YuvImage { .. } => {
326 }
327 BrushKind::Image { .. } => {
328 request.push([0.0; 4]);
329 }
330 BrushKind::Solid { color } => {
331 request.push(color.premultiplied());
332 }
333 BrushKind::Clear => {
334 // Opaque black with operator dest out
335 request.push(PremultipliedColorF::BLACK);
336 }
337 BrushKind::Line { color, wavy_line_thickness, style, orientation } => {
338 request.push(color);
339 request.push([
340 wavy_line_thickness,
341 pack_as_float(style as u32),
342 pack_as_float(orientation as u32),
343 0.0,
344 ]);
345 }
346 BrushKind::LinearGradient { start_point, end_point, extend_mode, .. } => {
347 request.push([
348 start_point.x,
349 start_point.y,
350 end_point.x,
351 end_point.y,
352 ]);
353 request.push([
354 pack_as_float(extend_mode as u32),
355 0.0,
356 0.0,
357 0.0,
358 ]);
359 }
360 BrushKind::RadialGradient { start_center, end_center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
361 request.push([
362 start_center.x,
363 start_center.y,
364 end_center.x,
365 end_center.y,
366 ]);
367 request.push([
368 start_radius,
369 end_radius,
370 ratio_xy,
371 pack_as_float(extend_mode as u32),
372 ]);
373 }
374 }
375 }
376 }
377
378 // Key that identifies a unique (partial) image that is being
379 // stored in the render task cache.
380 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
381 #[cfg_attr(feature = "capture", derive(Serialize))]
382 #[cfg_attr(feature = "replay", derive(Deserialize))]
383 pub struct ImageCacheKey {
384 pub request: ImageRequest,
385 pub texel_rect: Option<DeviceIntRect>,
386 }
387
388 // Where to find the texture data for an image primitive.
389 #[derive(Debug)]
390 pub enum ImageSource {
391 // A normal image - just reference the texture cache.
392 Default,
393 // An image that is pre-rendered into the texture cache
394 // via a render task.
395 Cache {
396 size: DeviceIntSize,
397 item: CacheItem,
398 },
399 }
400
401 #[derive(Debug)]
402 pub struct ImagePrimitiveCpu {
403 pub tile_spacing: LayerSize,
404 pub alpha_type: AlphaType,
405 pub stretch_size: LayerSize,
406 pub current_epoch: Epoch,
407 pub source: ImageSource,
408 pub key: ImageCacheKey,
409 }
410
411 impl ToGpuBlocks for ImagePrimitiveCpu {
write_gpu_blocks(&self, mut request: GpuDataRequest)412 fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
413 request.push([
414 self.stretch_size.width, self.stretch_size.height,
415 self.tile_spacing.width, self.tile_spacing.height,
416 ]);
417 }
418 }
419
420 #[derive(Debug)]
421 pub struct BorderPrimitiveCpu {
422 pub corner_instances: [BorderCornerInstance; 4],
423 pub edges: [BorderEdgeKind; 4],
424 pub gpu_blocks: [GpuBlockData; 8],
425 }
426
427 impl ToGpuBlocks for BorderPrimitiveCpu {
write_gpu_blocks(&self, mut request: GpuDataRequest)428 fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
429 request.extend_from_slice(&self.gpu_blocks);
430 }
431 }
432
433 // The gradient entry index for the first color stop
434 pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
435 // The gradient entry index for the last color stop
436 pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1;
437
438 // The start of the gradient data table
439 pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1;
440 // The exclusive bound of the gradient data table
441 pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP;
442 // The number of entries in the gradient data table.
443 pub const GRADIENT_DATA_TABLE_SIZE: usize = 128;
444
445 // The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry
446 pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
447
448 #[derive(Debug)]
449 #[repr(C)]
450 // An entry in a gradient data table representing a segment of the gradient color space.
451 pub struct GradientDataEntry {
452 pub start_color: PremultipliedColorF,
453 pub end_color: PremultipliedColorF,
454 }
455
456 struct GradientGpuBlockBuilder<'a> {
457 stops_range: ItemRange<GradientStop>,
458 display_list: &'a BuiltDisplayList,
459 }
460
461 impl<'a> GradientGpuBlockBuilder<'a> {
new( stops_range: ItemRange<GradientStop>, display_list: &'a BuiltDisplayList, ) -> Self462 fn new(
463 stops_range: ItemRange<GradientStop>,
464 display_list: &'a BuiltDisplayList,
465 ) -> Self {
466 GradientGpuBlockBuilder {
467 stops_range,
468 display_list,
469 }
470 }
471
472 /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
473 /// from start_color to end_color.
fill_colors( &self, start_idx: usize, end_idx: usize, start_color: &PremultipliedColorF, end_color: &PremultipliedColorF, entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE], )474 fn fill_colors(
475 &self,
476 start_idx: usize,
477 end_idx: usize,
478 start_color: &PremultipliedColorF,
479 end_color: &PremultipliedColorF,
480 entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
481 ) {
482 // Calculate the color difference for individual steps in the ramp.
483 let inv_steps = 1.0 / (end_idx - start_idx) as f32;
484 let step_r = (end_color.r - start_color.r) * inv_steps;
485 let step_g = (end_color.g - start_color.g) * inv_steps;
486 let step_b = (end_color.b - start_color.b) * inv_steps;
487 let step_a = (end_color.a - start_color.a) * inv_steps;
488
489 let mut cur_color = *start_color;
490
491 // Walk the ramp writing start and end colors for each entry.
492 for index in start_idx .. end_idx {
493 let entry = &mut entries[index];
494 entry.start_color = cur_color;
495 cur_color.r += step_r;
496 cur_color.g += step_g;
497 cur_color.b += step_b;
498 cur_color.a += step_a;
499 entry.end_color = cur_color;
500 }
501 }
502
503 /// Compute an index into the gradient entry table based on a gradient stop offset. This
504 /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END].
505 #[inline]
get_index(offset: f32) -> usize506 fn get_index(offset: f32) -> usize {
507 (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 +
508 GRADIENT_DATA_TABLE_BEGIN as f32)
509 .round() as usize
510 }
511
512 // Build the gradient data from the supplied stops, reversing them if necessary.
build(&self, reverse_stops: bool, request: &mut GpuDataRequest)513 fn build(&self, reverse_stops: bool, request: &mut GpuDataRequest) {
514 let src_stops = self.display_list.get(self.stops_range);
515
516 // Preconditions (should be ensured by DisplayListBuilder):
517 // * we have at least two stops
518 // * first stop has offset 0.0
519 // * last stop has offset 1.0
520
521 let mut src_stops = src_stops.into_iter();
522 let first = src_stops.next().unwrap();
523 let mut cur_color = first.color.premultiplied();
524 debug_assert_eq!(first.offset, 0.0);
525
526 // A table of gradient entries, with two colors per entry, that specify the start and end color
527 // within the segment of the gradient space represented by that entry. To lookup a gradient result,
528 // first the entry index is calculated to determine which two colors to interpolate between, then
529 // the offset within that entry bucket is used to interpolate between the two colors in that entry.
530 // This layout preserves hard stops, as the end color for a given entry can differ from the start
531 // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
532 // format for texture upload. This table requires the gradient color stops to be normalized to the
533 // range [0, 1]. The first and last entries hold the first and last color stop colors respectively,
534 // while the entries in between hold the interpolated color stop values for the range [0, 1].
535 let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() };
536
537 if reverse_stops {
538 // Fill in the first entry (for reversed stops) with the first color stop
539 self.fill_colors(
540 GRADIENT_DATA_LAST_STOP,
541 GRADIENT_DATA_LAST_STOP + 1,
542 &cur_color,
543 &cur_color,
544 &mut entries,
545 );
546
547 // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
548 // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The
549 // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
550 let mut cur_idx = GRADIENT_DATA_TABLE_END;
551 for next in src_stops {
552 let next_color = next.color.premultiplied();
553 let next_idx = Self::get_index(1.0 - next.offset);
554
555 if next_idx < cur_idx {
556 self.fill_colors(next_idx, cur_idx, &next_color, &cur_color, &mut entries);
557 cur_idx = next_idx;
558 }
559
560 cur_color = next_color;
561 }
562 debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_BEGIN);
563
564 // Fill in the last entry (for reversed stops) with the last color stop
565 self.fill_colors(
566 GRADIENT_DATA_FIRST_STOP,
567 GRADIENT_DATA_FIRST_STOP + 1,
568 &cur_color,
569 &cur_color,
570 &mut entries,
571 );
572 } else {
573 // Fill in the first entry with the first color stop
574 self.fill_colors(
575 GRADIENT_DATA_FIRST_STOP,
576 GRADIENT_DATA_FIRST_STOP + 1,
577 &cur_color,
578 &cur_color,
579 &mut entries,
580 );
581
582 // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
583 // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The
584 // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
585 let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN;
586 for next in src_stops {
587 let next_color = next.color.premultiplied();
588 let next_idx = Self::get_index(next.offset);
589
590 if next_idx > cur_idx {
591 self.fill_colors(cur_idx, next_idx, &cur_color, &next_color, &mut entries);
592 cur_idx = next_idx;
593 }
594
595 cur_color = next_color;
596 }
597 debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_END);
598
599 // Fill in the last entry with the last color stop
600 self.fill_colors(
601 GRADIENT_DATA_LAST_STOP,
602 GRADIENT_DATA_LAST_STOP + 1,
603 &cur_color,
604 &cur_color,
605 &mut entries,
606 );
607 }
608
609 for entry in entries.iter() {
610 request.push(entry.start_color);
611 request.push(entry.end_color);
612 }
613 }
614 }
615
616 #[derive(Debug, Clone)]
617 pub struct TextRunPrimitiveCpu {
618 pub font: FontInstance,
619 pub offset: LayerVector2D,
620 pub glyph_range: ItemRange<GlyphInstance>,
621 pub glyph_count: usize,
622 pub glyph_keys: Vec<GlyphKey>,
623 pub glyph_gpu_blocks: Vec<GpuBlockData>,
624 pub shadow: bool,
625 }
626
627 impl TextRunPrimitiveCpu {
get_font( &self, device_pixel_scale: DevicePixelScale, transform: Option<LayerToWorldTransform>, ) -> FontInstance628 pub fn get_font(
629 &self,
630 device_pixel_scale: DevicePixelScale,
631 transform: Option<LayerToWorldTransform>,
632 ) -> FontInstance {
633 let mut font = self.font.clone();
634 font.size = font.size.scale_by(device_pixel_scale.0);
635 if let Some(transform) = transform {
636 if transform.has_perspective_component() || !transform.has_2d_inverse() {
637 font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
638 } else {
639 font.transform = FontTransform::from(&transform).quantize();
640 }
641 }
642 font
643 }
644
prepare_for_render( &mut self, resource_cache: &mut ResourceCache, device_pixel_scale: DevicePixelScale, transform: Option<LayerToWorldTransform>, display_list: &BuiltDisplayList, gpu_cache: &mut GpuCache, )645 fn prepare_for_render(
646 &mut self,
647 resource_cache: &mut ResourceCache,
648 device_pixel_scale: DevicePixelScale,
649 transform: Option<LayerToWorldTransform>,
650 display_list: &BuiltDisplayList,
651 gpu_cache: &mut GpuCache,
652 ) {
653 let font = self.get_font(device_pixel_scale, transform);
654
655 // Cache the glyph positions, if not in the cache already.
656 // TODO(gw): In the future, remove `glyph_instances`
657 // completely, and just reference the glyphs
658 // directly from the display list.
659 if self.glyph_keys.is_empty() {
660 let subpx_dir = font.subpx_dir.limit_by(font.render_mode);
661 let src_glyphs = display_list.get(self.glyph_range);
662
663 // TODO(gw): If we support chunks() on AuxIter
664 // in the future, this code below could
665 // be much simpler...
666 let mut gpu_block = [0.0; 4];
667 for (i, src) in src_glyphs.enumerate() {
668 let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir);
669 self.glyph_keys.push(key);
670
671 // Two glyphs are packed per GPU block.
672
673 if (i & 1) == 0 {
674 gpu_block[0] = src.point.x;
675 gpu_block[1] = src.point.y;
676 } else {
677 gpu_block[2] = src.point.x;
678 gpu_block[3] = src.point.y;
679 self.glyph_gpu_blocks.push(gpu_block.into());
680 }
681 }
682
683 // Ensure the last block is added in the case
684 // of an odd number of glyphs.
685 if (self.glyph_keys.len() & 1) != 0 {
686 self.glyph_gpu_blocks.push(gpu_block.into());
687 }
688 }
689
690 resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
691 }
692
write_gpu_blocks(&self, request: &mut GpuDataRequest)693 fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
694 request.push(ColorF::from(self.font.color).premultiplied());
695 // this is the only case where we need to provide plain color to GPU
696 let bg_color = ColorF::from(self.font.bg_color);
697 request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
698 request.push([
699 self.offset.x,
700 self.offset.y,
701 0.0,
702 0.0,
703 ]);
704 request.extend_from_slice(&self.glyph_gpu_blocks);
705
706 assert!(request.current_used_block_num() <= MAX_VERTEX_TEXTURE_WIDTH);
707 }
708 }
709
710 #[derive(Debug)]
711 #[repr(C)]
712 struct ClipRect {
713 rect: LayerRect,
714 mode: f32,
715 }
716
717 #[derive(Debug)]
718 #[repr(C)]
719 struct ClipCorner {
720 rect: LayerRect,
721 outer_radius_x: f32,
722 outer_radius_y: f32,
723 inner_radius_x: f32,
724 inner_radius_y: f32,
725 }
726
727 impl ToGpuBlocks for ClipCorner {
write_gpu_blocks(&self, mut request: GpuDataRequest)728 fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
729 self.write(&mut request)
730 }
731 }
732
733 impl ClipCorner {
write(&self, request: &mut GpuDataRequest)734 fn write(&self, request: &mut GpuDataRequest) {
735 request.push(self.rect);
736 request.push([
737 self.outer_radius_x,
738 self.outer_radius_y,
739 self.inner_radius_x,
740 self.inner_radius_y,
741 ]);
742 }
743
uniform(rect: LayerRect, outer_radius: f32, inner_radius: f32) -> ClipCorner744 fn uniform(rect: LayerRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
745 ClipCorner {
746 rect,
747 outer_radius_x: outer_radius,
748 outer_radius_y: outer_radius,
749 inner_radius_x: inner_radius,
750 inner_radius_y: inner_radius,
751 }
752 }
753 }
754
755 #[derive(Debug)]
756 #[repr(C)]
757 pub struct ImageMaskData {
758 pub local_rect: LayerRect,
759 }
760
761 impl ToGpuBlocks for ImageMaskData {
write_gpu_blocks(&self, mut request: GpuDataRequest)762 fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
763 request.push(self.local_rect);
764 }
765 }
766
767 #[derive(Debug)]
768 pub struct ClipData {
769 rect: ClipRect,
770 top_left: ClipCorner,
771 top_right: ClipCorner,
772 bottom_left: ClipCorner,
773 bottom_right: ClipCorner,
774 }
775
776 impl ClipData {
rounded_rect(rect: &LayerRect, radii: &BorderRadius, mode: ClipMode) -> ClipData777 pub fn rounded_rect(rect: &LayerRect, radii: &BorderRadius, mode: ClipMode) -> ClipData {
778 ClipData {
779 rect: ClipRect {
780 rect: *rect,
781 mode: mode as u32 as f32,
782 },
783 top_left: ClipCorner {
784 rect: LayerRect::new(
785 LayerPoint::new(rect.origin.x, rect.origin.y),
786 LayerSize::new(radii.top_left.width, radii.top_left.height),
787 ),
788 outer_radius_x: radii.top_left.width,
789 outer_radius_y: radii.top_left.height,
790 inner_radius_x: 0.0,
791 inner_radius_y: 0.0,
792 },
793 top_right: ClipCorner {
794 rect: LayerRect::new(
795 LayerPoint::new(
796 rect.origin.x + rect.size.width - radii.top_right.width,
797 rect.origin.y,
798 ),
799 LayerSize::new(radii.top_right.width, radii.top_right.height),
800 ),
801 outer_radius_x: radii.top_right.width,
802 outer_radius_y: radii.top_right.height,
803 inner_radius_x: 0.0,
804 inner_radius_y: 0.0,
805 },
806 bottom_left: ClipCorner {
807 rect: LayerRect::new(
808 LayerPoint::new(
809 rect.origin.x,
810 rect.origin.y + rect.size.height - radii.bottom_left.height,
811 ),
812 LayerSize::new(radii.bottom_left.width, radii.bottom_left.height),
813 ),
814 outer_radius_x: radii.bottom_left.width,
815 outer_radius_y: radii.bottom_left.height,
816 inner_radius_x: 0.0,
817 inner_radius_y: 0.0,
818 },
819 bottom_right: ClipCorner {
820 rect: LayerRect::new(
821 LayerPoint::new(
822 rect.origin.x + rect.size.width - radii.bottom_right.width,
823 rect.origin.y + rect.size.height - radii.bottom_right.height,
824 ),
825 LayerSize::new(radii.bottom_right.width, radii.bottom_right.height),
826 ),
827 outer_radius_x: radii.bottom_right.width,
828 outer_radius_y: radii.bottom_right.height,
829 inner_radius_x: 0.0,
830 inner_radius_y: 0.0,
831 },
832 }
833 }
834
uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData835 pub fn uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData {
836 ClipData {
837 rect: ClipRect {
838 rect,
839 mode: mode as u32 as f32,
840 },
841 top_left: ClipCorner::uniform(
842 LayerRect::new(
843 LayerPoint::new(rect.origin.x, rect.origin.y),
844 LayerSize::new(radius, radius),
845 ),
846 radius,
847 0.0,
848 ),
849 top_right: ClipCorner::uniform(
850 LayerRect::new(
851 LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y),
852 LayerSize::new(radius, radius),
853 ),
854 radius,
855 0.0,
856 ),
857 bottom_left: ClipCorner::uniform(
858 LayerRect::new(
859 LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height - radius),
860 LayerSize::new(radius, radius),
861 ),
862 radius,
863 0.0,
864 ),
865 bottom_right: ClipCorner::uniform(
866 LayerRect::new(
867 LayerPoint::new(
868 rect.origin.x + rect.size.width - radius,
869 rect.origin.y + rect.size.height - radius,
870 ),
871 LayerSize::new(radius, radius),
872 ),
873 radius,
874 0.0,
875 ),
876 }
877 }
878
write(&self, request: &mut GpuDataRequest)879 pub fn write(&self, request: &mut GpuDataRequest) {
880 request.push(self.rect.rect);
881 request.push([self.rect.mode, 0.0, 0.0, 0.0]);
882 for corner in &[
883 &self.top_left,
884 &self.top_right,
885 &self.bottom_left,
886 &self.bottom_right,
887 ] {
888 corner.write(request);
889 }
890 }
891 }
892
893 #[derive(Debug)]
894 pub enum PrimitiveContainer {
895 TextRun(TextRunPrimitiveCpu),
896 Image(ImagePrimitiveCpu),
897 Border(BorderPrimitiveCpu),
898 Picture(PicturePrimitive),
899 Brush(BrushPrimitive),
900 }
901
902 pub struct PrimitiveStore {
903 /// CPU side information only.
904 pub cpu_brushes: Vec<BrushPrimitive>,
905 pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
906 pub cpu_pictures: Vec<PicturePrimitive>,
907 pub cpu_images: Vec<ImagePrimitiveCpu>,
908 pub cpu_metadata: Vec<PrimitiveMetadata>,
909 pub cpu_borders: Vec<BorderPrimitiveCpu>,
910 }
911
912 impl PrimitiveStore {
new() -> PrimitiveStore913 pub fn new() -> PrimitiveStore {
914 PrimitiveStore {
915 cpu_metadata: Vec::new(),
916 cpu_brushes: Vec::new(),
917 cpu_text_runs: Vec::new(),
918 cpu_pictures: Vec::new(),
919 cpu_images: Vec::new(),
920 cpu_borders: Vec::new(),
921 }
922 }
923
recycle(self) -> Self924 pub fn recycle(self) -> Self {
925 PrimitiveStore {
926 cpu_metadata: recycle_vec(self.cpu_metadata),
927 cpu_brushes: recycle_vec(self.cpu_brushes),
928 cpu_text_runs: recycle_vec(self.cpu_text_runs),
929 cpu_pictures: recycle_vec(self.cpu_pictures),
930 cpu_images: recycle_vec(self.cpu_images),
931 cpu_borders: recycle_vec(self.cpu_borders),
932 }
933 }
934
add_primitive( &mut self, local_rect: &LayerRect, local_clip_rect: &LayerRect, is_backface_visible: bool, clip_sources: Option<ClipSourcesHandle>, tag: Option<ItemTag>, container: PrimitiveContainer, ) -> PrimitiveIndex935 pub fn add_primitive(
936 &mut self,
937 local_rect: &LayerRect,
938 local_clip_rect: &LayerRect,
939 is_backface_visible: bool,
940 clip_sources: Option<ClipSourcesHandle>,
941 tag: Option<ItemTag>,
942 container: PrimitiveContainer,
943 ) -> PrimitiveIndex {
944 let prim_index = self.cpu_metadata.len();
945
946 let base_metadata = PrimitiveMetadata {
947 clip_sources,
948 gpu_location: GpuCacheHandle::new(),
949 clip_task_id: None,
950 local_rect: *local_rect,
951 local_clip_rect: *local_clip_rect,
952 clip_chain_rect_index: ClipChainRectIndex(0),
953 is_backface_visible: is_backface_visible,
954 screen_rect: None,
955 tag,
956 opacity: PrimitiveOpacity::translucent(),
957 prim_kind: PrimitiveKind::Brush,
958 cpu_prim_index: SpecificPrimitiveIndex(0),
959 };
960
961 let metadata = match container {
962 PrimitiveContainer::Brush(brush) => {
963 let opacity = match brush.kind {
964 BrushKind::Clear => PrimitiveOpacity::translucent(),
965 BrushKind::Solid { ref color } => PrimitiveOpacity::from_alpha(color.a),
966 BrushKind::Line { .. } => PrimitiveOpacity::translucent(),
967 BrushKind::Image { .. } => PrimitiveOpacity::translucent(),
968 BrushKind::YuvImage { .. } => PrimitiveOpacity::opaque(),
969 BrushKind::RadialGradient { .. } => PrimitiveOpacity::translucent(),
970 BrushKind::LinearGradient { .. } => PrimitiveOpacity::translucent(),
971 BrushKind::Picture => {
972 // TODO(gw): This is not currently used. In the future
973 // we should detect opaque pictures.
974 unreachable!();
975 }
976 };
977
978 let metadata = PrimitiveMetadata {
979 opacity,
980 prim_kind: PrimitiveKind::Brush,
981 cpu_prim_index: SpecificPrimitiveIndex(self.cpu_brushes.len()),
982 ..base_metadata
983 };
984
985 self.cpu_brushes.push(brush);
986
987 metadata
988 }
989 PrimitiveContainer::TextRun(text_cpu) => {
990 let metadata = PrimitiveMetadata {
991 opacity: PrimitiveOpacity::translucent(),
992 prim_kind: PrimitiveKind::TextRun,
993 cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
994 ..base_metadata
995 };
996
997 self.cpu_text_runs.push(text_cpu);
998 metadata
999 }
1000 PrimitiveContainer::Picture(picture) => {
1001 let metadata = PrimitiveMetadata {
1002 opacity: PrimitiveOpacity::translucent(),
1003 prim_kind: PrimitiveKind::Picture,
1004 cpu_prim_index: SpecificPrimitiveIndex(self.cpu_pictures.len()),
1005 ..base_metadata
1006 };
1007
1008 self.cpu_pictures.push(picture);
1009 metadata
1010 }
1011 PrimitiveContainer::Image(image_cpu) => {
1012 let metadata = PrimitiveMetadata {
1013 opacity: PrimitiveOpacity::translucent(),
1014 prim_kind: PrimitiveKind::Image,
1015 cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
1016 ..base_metadata
1017 };
1018
1019 self.cpu_images.push(image_cpu);
1020 metadata
1021 }
1022 PrimitiveContainer::Border(border_cpu) => {
1023 let metadata = PrimitiveMetadata {
1024 opacity: PrimitiveOpacity::translucent(),
1025 prim_kind: PrimitiveKind::Border,
1026 cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
1027 ..base_metadata
1028 };
1029
1030 self.cpu_borders.push(border_cpu);
1031 metadata
1032 }
1033 };
1034
1035 self.cpu_metadata.push(metadata);
1036
1037 PrimitiveIndex(prim_index)
1038 }
1039
get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata1040 pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
1041 &self.cpu_metadata[index.0]
1042 }
1043
prim_count(&self) -> usize1044 pub fn prim_count(&self) -> usize {
1045 self.cpu_metadata.len()
1046 }
1047
prepare_prim_for_render_inner( &mut self, prim_index: PrimitiveIndex, prim_run_context: &PrimitiveRunContext, pic_state_for_children: PictureState, pic_context: &PictureContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, )1048 fn prepare_prim_for_render_inner(
1049 &mut self,
1050 prim_index: PrimitiveIndex,
1051 prim_run_context: &PrimitiveRunContext,
1052 pic_state_for_children: PictureState,
1053 pic_context: &PictureContext,
1054 pic_state: &mut PictureState,
1055 frame_context: &FrameBuildingContext,
1056 frame_state: &mut FrameBuildingState,
1057 ) {
1058 let metadata = &mut self.cpu_metadata[prim_index.0];
1059 match metadata.prim_kind {
1060 PrimitiveKind::Border => {}
1061 PrimitiveKind::Picture => {
1062 self.cpu_pictures[metadata.cpu_prim_index.0]
1063 .prepare_for_render(
1064 prim_index,
1065 &metadata.screen_rect
1066 .expect("bug: trying to draw an off-screen picture!?")
1067 .clipped,
1068 &metadata.local_rect,
1069 pic_state_for_children,
1070 pic_state,
1071 frame_context,
1072 frame_state,
1073 );
1074 }
1075 PrimitiveKind::TextRun => {
1076 let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
1077 // The transform only makes sense for screen space rasterization
1078 let transform = if pic_context.draw_text_transformed {
1079 Some(prim_run_context.scroll_node.world_content_transform.into())
1080 } else {
1081 None
1082 };
1083 text.prepare_for_render(
1084 frame_state.resource_cache,
1085 frame_context.device_pixel_scale,
1086 transform,
1087 pic_context.display_list,
1088 frame_state.gpu_cache,
1089 );
1090 }
1091 PrimitiveKind::Image => {
1092 let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
1093 let image_properties = frame_state
1094 .resource_cache
1095 .get_image_properties(image_cpu.key.request.key);
1096
1097 // TODO(gw): Add image.rs and move this code out to a separate
1098 // source file as it gets more complicated, and we
1099 // start pre-rendering images for other reasons.
1100
1101 if let Some(image_properties) = image_properties {
1102 // See if this image has been updated since we last hit this code path.
1103 // If so, we need to (at least) update the opacity, and also rebuild
1104 // and render task cached portions of this image.
1105 if image_properties.epoch != image_cpu.current_epoch {
1106 image_cpu.current_epoch = image_properties.epoch;
1107
1108 // Update the opacity.
1109 metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
1110 image_cpu.tile_spacing.width == 0.0 &&
1111 image_cpu.tile_spacing.height == 0.0;
1112
1113 // Work out whether this image is a normal / simple type, or if
1114 // we need to pre-render it to the render task cache.
1115 image_cpu.source = match image_cpu.key.texel_rect {
1116 Some(texel_rect) => {
1117 ImageSource::Cache {
1118 // Size in device-pixels we need to allocate in render task cache.
1119 size: texel_rect.size,
1120 item: CacheItem::invalid(),
1121 }
1122 }
1123 None => {
1124 // Simple image - just use a normal texture cache entry.
1125 ImageSource::Default
1126 }
1127 };
1128 }
1129
1130 // Set if we need to request the source image from the cache this frame.
1131 let mut request_source_image = false;
1132
1133 // Every frame, for cached items, we need to request the render
1134 // task cache item. The closure will be invoked on the first
1135 // time through, and any time the render task output has been
1136 // evicted from the texture cache.
1137 match image_cpu.source {
1138 ImageSource::Cache { size, ref mut item } => {
1139 let key = image_cpu.key;
1140
1141 // Request a pre-rendered image task.
1142 *item = frame_state.resource_cache.request_render_task(
1143 RenderTaskCacheKey {
1144 size,
1145 kind: RenderTaskCacheKeyKind::Image(key),
1146 },
1147 frame_state.gpu_cache,
1148 frame_state.render_tasks,
1149 |render_tasks| {
1150 // We need to render the image cache this frame,
1151 // so will need access to the source texture.
1152 request_source_image = true;
1153
1154 // Create a task to blit from the texture cache to
1155 // a normal transient render task surface. This will
1156 // copy only the sub-rect, if specified.
1157 let cache_to_target_task = RenderTask::new_blit(
1158 size,
1159 BlitSource::Image {
1160 key,
1161 },
1162 );
1163 let cache_to_target_task_id = render_tasks.add(cache_to_target_task);
1164
1165 // Create a task to blit the rect from the child render
1166 // task above back into the right spot in the persistent
1167 // render target cache.
1168 let target_to_cache_task = RenderTask::new_blit(
1169 size,
1170 BlitSource::RenderTask {
1171 task_id: cache_to_target_task_id,
1172 },
1173 );
1174 let target_to_cache_task_id = render_tasks.add(target_to_cache_task);
1175
1176 // Hook this into the render task tree at the right spot.
1177 pic_state.tasks.push(target_to_cache_task_id);
1178
1179 // Pass the image opacity, so that the cached render task
1180 // item inherits the same opacity properties.
1181 (target_to_cache_task_id, image_properties.descriptor.is_opaque)
1182 }
1183 );
1184 }
1185 ImageSource::Default => {
1186 // Normal images just reference the source texture each frame.
1187 request_source_image = true;
1188 }
1189 }
1190
1191 // Request source image from the texture cache, if required.
1192 if request_source_image {
1193 frame_state.resource_cache.request_image(
1194 image_cpu.key.request,
1195 frame_state.gpu_cache,
1196 );
1197 }
1198 }
1199 }
1200 PrimitiveKind::Brush => {
1201 let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
1202
1203 match brush.kind {
1204 BrushKind::Image { request, ref mut current_epoch, .. } => {
1205 let image_properties = frame_state
1206 .resource_cache
1207 .get_image_properties(request.key);
1208
1209 if let Some(image_properties) = image_properties {
1210 // See if this image has been updated since we last hit this code path.
1211 // If so, we need to update the opacity.
1212 if image_properties.epoch != *current_epoch {
1213 *current_epoch = image_properties.epoch;
1214 metadata.opacity.is_opaque = image_properties.descriptor.is_opaque;
1215 }
1216 }
1217
1218 frame_state.resource_cache.request_image(
1219 request,
1220 frame_state.gpu_cache,
1221 );
1222 }
1223 BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => {
1224 let channel_num = format.get_plane_num();
1225 debug_assert!(channel_num <= 3);
1226 for channel in 0 .. channel_num {
1227 frame_state.resource_cache.request_image(
1228 ImageRequest {
1229 key: yuv_key[channel],
1230 rendering: image_rendering,
1231 tile: None,
1232 },
1233 frame_state.gpu_cache,
1234 );
1235 }
1236 }
1237 BrushKind::RadialGradient { gradient_index, stops_range, .. } => {
1238 let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
1239 if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
1240 let gradient_builder = GradientGpuBlockBuilder::new(
1241 stops_range,
1242 pic_context.display_list,
1243 );
1244 gradient_builder.build(
1245 false,
1246 &mut request,
1247 );
1248 }
1249 }
1250 BrushKind::LinearGradient { gradient_index, stops_range, reverse_stops, .. } => {
1251 let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
1252 if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
1253 let gradient_builder = GradientGpuBlockBuilder::new(
1254 stops_range,
1255 pic_context.display_list,
1256 );
1257 gradient_builder.build(
1258 reverse_stops,
1259 &mut request,
1260 );
1261 }
1262 }
1263 BrushKind::Solid { .. } |
1264 BrushKind::Clear |
1265 BrushKind::Line { .. } |
1266 BrushKind::Picture { .. } => {}
1267 }
1268 }
1269 }
1270
1271 // Mark this GPU resource as required for this frame.
1272 if let Some(mut request) = frame_state.gpu_cache.request(&mut metadata.gpu_location) {
1273 // has to match VECS_PER_BRUSH_PRIM
1274 request.push(metadata.local_rect);
1275 request.push(metadata.local_clip_rect);
1276
1277 match metadata.prim_kind {
1278 PrimitiveKind::Border => {
1279 let border = &self.cpu_borders[metadata.cpu_prim_index.0];
1280 border.write_gpu_blocks(request);
1281 }
1282 PrimitiveKind::Image => {
1283 let image = &self.cpu_images[metadata.cpu_prim_index.0];
1284 image.write_gpu_blocks(request);
1285 }
1286 PrimitiveKind::TextRun => {
1287 let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
1288 text.write_gpu_blocks(&mut request);
1289 }
1290 PrimitiveKind::Picture => {
1291 let pic = &self.cpu_pictures[metadata.cpu_prim_index.0];
1292 pic.write_gpu_blocks(&mut request);
1293
1294 let brush = &pic.brush;
1295 brush.write_gpu_blocks(&mut request);
1296 match brush.segment_desc {
1297 Some(ref segment_desc) => {
1298 for segment in &segment_desc.segments {
1299 // has to match VECS_PER_SEGMENT
1300 request.write_segment(segment.local_rect);
1301 }
1302 }
1303 None => {
1304 request.write_segment(metadata.local_rect);
1305 }
1306 }
1307 }
1308 PrimitiveKind::Brush => {
1309 let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
1310 brush.write_gpu_blocks(&mut request);
1311 match brush.segment_desc {
1312 Some(ref segment_desc) => {
1313 for segment in &segment_desc.segments {
1314 // has to match VECS_PER_SEGMENT
1315 request.write_segment(segment.local_rect);
1316 }
1317 }
1318 None => {
1319 request.write_segment(metadata.local_rect);
1320 }
1321 }
1322 }
1323 }
1324 }
1325 }
1326
write_brush_segment_description( brush: &mut BrushPrimitive, metadata: &PrimitiveMetadata, prim_run_context: &PrimitiveRunContext, clips: &Vec<ClipWorkItem>, has_clips_from_other_coordinate_systems: bool, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, )1327 fn write_brush_segment_description(
1328 brush: &mut BrushPrimitive,
1329 metadata: &PrimitiveMetadata,
1330 prim_run_context: &PrimitiveRunContext,
1331 clips: &Vec<ClipWorkItem>,
1332 has_clips_from_other_coordinate_systems: bool,
1333 frame_context: &FrameBuildingContext,
1334 frame_state: &mut FrameBuildingState,
1335 ) {
1336 match brush.segment_desc {
1337 Some(ref segment_desc) => {
1338 // If we already have a segment descriptor, only run through the
1339 // clips list if we haven't already determined the mask kind.
1340 if segment_desc.clip_mask_kind != BrushClipMaskKind::Unknown {
1341 return;
1342 }
1343 }
1344 None => {
1345 // If no segment descriptor built yet, see if it is a brush
1346 // type that wants to be segmented.
1347 if !brush.kind.supports_segments() {
1348 return;
1349 }
1350 if metadata.local_rect.size.area() <= MIN_BRUSH_SPLIT_AREA {
1351 return;
1352 }
1353 }
1354 }
1355
1356 let mut segment_builder = SegmentBuilder::new(
1357 metadata.local_rect,
1358 None,
1359 metadata.local_clip_rect
1360 );
1361
1362 // If this primitive is clipped by clips from a different coordinate system, then we
1363 // need to apply a clip mask for the entire primitive.
1364 let mut clip_mask_kind = match has_clips_from_other_coordinate_systems {
1365 true => BrushClipMaskKind::Global,
1366 false => BrushClipMaskKind::Individual,
1367 };
1368
1369 // Segment the primitive on all the local-space clip sources that we can.
1370 for clip_item in clips {
1371 if clip_item.coordinate_system_id != prim_run_context.scroll_node.coordinate_system_id {
1372 continue;
1373 }
1374
1375 let local_clips = frame_state.clip_store.get_opt(&clip_item.clip_sources).expect("bug");
1376 for &(ref clip, _) in &local_clips.clips {
1377 let (local_clip_rect, radius, mode) = match *clip {
1378 ClipSource::RoundedRectangle(rect, radii, clip_mode) => {
1379 (rect, Some(radii), clip_mode)
1380 }
1381 ClipSource::Rectangle(rect) => {
1382 (rect, None, ClipMode::Clip)
1383 }
1384 ClipSource::BoxShadow(ref info) => {
1385 // For inset box shadows, we can clip out any
1386 // pixels that are inside the shadow region
1387 // and are beyond the inner rect, as they can't
1388 // be affected by the blur radius.
1389 let inner_clip_mode = match info.clip_mode {
1390 BoxShadowClipMode::Outset => None,
1391 BoxShadowClipMode::Inset => Some(ClipMode::ClipOut),
1392 };
1393
1394 // Push a region into the segment builder where the
1395 // box-shadow can have an effect on the result. This
1396 // ensures clip-mask tasks get allocated for these
1397 // pixel regions, even if no other clips affect them.
1398 segment_builder.push_mask_region(
1399 info.prim_shadow_rect,
1400 info.prim_shadow_rect.inflate(
1401 -0.5 * info.shadow_rect_alloc_size.width,
1402 -0.5 * info.shadow_rect_alloc_size.height,
1403 ),
1404 inner_clip_mode,
1405 );
1406
1407 continue;
1408 }
1409 ClipSource::BorderCorner(..) |
1410 ClipSource::Image(..) => {
1411 // TODO(gw): We can easily extend the segment builder
1412 // to support these clip sources in the
1413 // future, but they are rarely used.
1414 clip_mask_kind = BrushClipMaskKind::Global;
1415 continue;
1416 }
1417 };
1418
1419 // If the scroll node transforms are different between the clip
1420 // node and the primitive, we need to get the clip rect in the
1421 // local space of the primitive, in order to generate correct
1422 // local segments.
1423 let local_clip_rect = if clip_item.scroll_node_data_index == prim_run_context.scroll_node.node_data_index {
1424 local_clip_rect
1425 } else {
1426 let clip_transform = frame_context
1427 .node_data[clip_item.scroll_node_data_index.0 as usize]
1428 .transform;
1429 let prim_transform = &prim_run_context.scroll_node.world_content_transform;
1430 let relative_transform = prim_transform
1431 .inverse()
1432 .unwrap_or(WorldToLayerFastTransform::identity())
1433 .pre_mul(&clip_transform.into());
1434
1435 relative_transform.transform_rect(&local_clip_rect)
1436 };
1437
1438 segment_builder.push_clip_rect(local_clip_rect, radius, mode);
1439 }
1440 }
1441
1442 match brush.segment_desc {
1443 Some(ref mut segment_desc) => {
1444 segment_desc.clip_mask_kind = clip_mask_kind;
1445 }
1446 None => {
1447 // TODO(gw): We can probably make the allocation
1448 // patterns of this and the segment
1449 // builder significantly better, by
1450 // retaining it across primitives.
1451 let mut segments = Vec::new();
1452
1453 segment_builder.build(|segment| {
1454 segments.push(
1455 BrushSegment::new(
1456 segment.rect.origin,
1457 segment.rect.size,
1458 segment.has_mask,
1459 segment.edge_flags,
1460 ),
1461 );
1462 });
1463
1464 brush.segment_desc = Some(BrushSegmentDescriptor {
1465 segments,
1466 clip_mask_kind,
1467 });
1468 }
1469 }
1470 }
1471
update_clip_task_for_brush( &mut self, prim_run_context: &PrimitiveRunContext, prim_index: PrimitiveIndex, clips: &Vec<ClipWorkItem>, combined_outer_rect: &DeviceIntRect, has_clips_from_other_coordinate_systems: bool, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> bool1472 fn update_clip_task_for_brush(
1473 &mut self,
1474 prim_run_context: &PrimitiveRunContext,
1475 prim_index: PrimitiveIndex,
1476 clips: &Vec<ClipWorkItem>,
1477 combined_outer_rect: &DeviceIntRect,
1478 has_clips_from_other_coordinate_systems: bool,
1479 pic_state: &mut PictureState,
1480 frame_context: &FrameBuildingContext,
1481 frame_state: &mut FrameBuildingState,
1482 ) -> bool {
1483 let metadata = &self.cpu_metadata[prim_index.0];
1484 let brush = match metadata.prim_kind {
1485 PrimitiveKind::Brush => {
1486 &mut self.cpu_brushes[metadata.cpu_prim_index.0]
1487 }
1488 PrimitiveKind::Picture => {
1489 &mut self.cpu_pictures[metadata.cpu_prim_index.0].brush
1490 }
1491 _ => {
1492 return false;
1493 }
1494 };
1495
1496 PrimitiveStore::write_brush_segment_description(
1497 brush,
1498 metadata,
1499 prim_run_context,
1500 clips,
1501 has_clips_from_other_coordinate_systems,
1502 frame_context,
1503 frame_state,
1504 );
1505
1506 let segment_desc = match brush.segment_desc {
1507 Some(ref mut description) => description,
1508 None => return false,
1509 };
1510 let clip_mask_kind = segment_desc.clip_mask_kind;
1511
1512 for segment in &mut segment_desc.segments {
1513 if !segment.may_need_clip_mask && clip_mask_kind != BrushClipMaskKind::Global {
1514 segment.clip_task_id = None;
1515 continue;
1516 }
1517
1518 let segment_screen_rect = calculate_screen_bounding_rect(
1519 &prim_run_context.scroll_node.world_content_transform,
1520 &segment.local_rect,
1521 frame_context.device_pixel_scale,
1522 );
1523
1524 let intersected_rect = combined_outer_rect.intersection(&segment_screen_rect);
1525 segment.clip_task_id = intersected_rect.map(|bounds| {
1526 let clip_task = RenderTask::new_mask(
1527 bounds,
1528 clips.clone(),
1529 prim_run_context.scroll_node.coordinate_system_id,
1530 frame_state.clip_store,
1531 frame_state.gpu_cache,
1532 frame_state.resource_cache,
1533 frame_state.render_tasks,
1534 );
1535
1536 let clip_task_id = frame_state.render_tasks.add(clip_task);
1537 pic_state.tasks.push(clip_task_id);
1538
1539 clip_task_id
1540 })
1541 }
1542
1543 true
1544 }
1545
update_clip_task( &mut self, prim_index: PrimitiveIndex, prim_run_context: &PrimitiveRunContext, prim_screen_rect: &DeviceIntRect, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> bool1546 fn update_clip_task(
1547 &mut self,
1548 prim_index: PrimitiveIndex,
1549 prim_run_context: &PrimitiveRunContext,
1550 prim_screen_rect: &DeviceIntRect,
1551 pic_state: &mut PictureState,
1552 frame_context: &FrameBuildingContext,
1553 frame_state: &mut FrameBuildingState,
1554 ) -> bool {
1555 self.cpu_metadata[prim_index.0].clip_task_id = None;
1556
1557 let prim_screen_rect = match prim_screen_rect.intersection(&frame_context.screen_rect) {
1558 Some(rect) => rect,
1559 None => {
1560 self.cpu_metadata[prim_index.0].screen_rect = None;
1561 return false;
1562 }
1563 };
1564
1565 let mut combined_outer_rect =
1566 prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect);
1567 let clip_chain = prim_run_context.clip_chain.nodes.clone();
1568
1569 let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
1570 let transform = &prim_run_context.scroll_node.world_content_transform;
1571 let extra_clip = {
1572 let metadata = &self.cpu_metadata[prim_index.0];
1573 metadata.clip_sources.as_ref().map(|ref clip_sources| {
1574 let prim_clips = frame_state.clip_store.get_mut(clip_sources);
1575 prim_clips.update(
1576 frame_state.gpu_cache,
1577 frame_state.resource_cache,
1578 frame_context.device_pixel_scale,
1579 );
1580 let (screen_inner_rect, screen_outer_rect) =
1581 prim_clips.get_screen_bounds(transform, frame_context.device_pixel_scale);
1582
1583 if let Some(outer) = screen_outer_rect {
1584 combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
1585 }
1586
1587 Arc::new(ClipChainNode {
1588 work_item: ClipWorkItem {
1589 scroll_node_data_index: prim_run_context.scroll_node.node_data_index,
1590 clip_sources: clip_sources.weak(),
1591 coordinate_system_id: prim_coordinate_system_id,
1592 },
1593 // The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes.
1594 // It's used to calculate a local clipping rectangle before we reach this
1595 // point, so we can set it to zero here. It should be unused from this point
1596 // on.
1597 local_clip_rect: LayerRect::zero(),
1598 screen_inner_rect,
1599 screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect),
1600 prev: None,
1601 })
1602 })
1603 };
1604
1605 // If everything is clipped out, then we don't need to render this primitive.
1606 let combined_outer_rect = match combined_outer_rect {
1607 Some(rect) if !rect.is_empty() => rect,
1608 _ => {
1609 self.cpu_metadata[prim_index.0].screen_rect = None;
1610 return false;
1611 }
1612 };
1613
1614 let mut has_clips_from_other_coordinate_systems = false;
1615 let mut combined_inner_rect = frame_context.screen_rect;
1616 let clips = convert_clip_chain_to_clip_vector(
1617 clip_chain,
1618 extra_clip,
1619 &combined_outer_rect,
1620 &mut combined_inner_rect,
1621 prim_run_context.scroll_node.coordinate_system_id,
1622 &mut has_clips_from_other_coordinate_systems
1623 );
1624
1625 // This can happen if we had no clips or if all the clips were optimized away. In
1626 // some cases we still need to create a clip mask in order to create a rectangular
1627 // clip in screen space coordinates.
1628 if clips.is_empty() {
1629 // If we don't have any clips from other coordinate systems, the local clip
1630 // calculated from the clip chain should be sufficient to ensure proper clipping.
1631 if !has_clips_from_other_coordinate_systems {
1632 return true;
1633 }
1634
1635 // If we have filtered all clips and the screen rect isn't any smaller, we can just
1636 // skip masking entirely.
1637 if combined_outer_rect == prim_screen_rect {
1638 return true;
1639 }
1640 // Otherwise we create an empty mask, but with an empty inner rect to avoid further
1641 // optimization of the empty mask.
1642 combined_inner_rect = DeviceIntRect::zero();
1643 }
1644
1645 if combined_inner_rect.contains_rect(&prim_screen_rect) {
1646 return true;
1647 }
1648
1649 // First try to render this primitive's mask using optimized brush rendering.
1650 if self.update_clip_task_for_brush(
1651 prim_run_context,
1652 prim_index,
1653 &clips,
1654 &combined_outer_rect,
1655 has_clips_from_other_coordinate_systems,
1656 pic_state,
1657 frame_context,
1658 frame_state,
1659 ) {
1660 return true;
1661 }
1662
1663 let clip_task = RenderTask::new_mask(
1664 combined_outer_rect,
1665 clips,
1666 prim_coordinate_system_id,
1667 frame_state.clip_store,
1668 frame_state.gpu_cache,
1669 frame_state.resource_cache,
1670 frame_state.render_tasks,
1671 );
1672
1673 let clip_task_id = frame_state.render_tasks.add(clip_task);
1674 self.cpu_metadata[prim_index.0].clip_task_id = Some(clip_task_id);
1675 pic_state.tasks.push(clip_task_id);
1676
1677 true
1678 }
1679
prepare_prim_for_render( &mut self, prim_index: PrimitiveIndex, prim_run_context: &PrimitiveRunContext, pic_context: &PictureContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> Option<LayerRect>1680 pub fn prepare_prim_for_render(
1681 &mut self,
1682 prim_index: PrimitiveIndex,
1683 prim_run_context: &PrimitiveRunContext,
1684 pic_context: &PictureContext,
1685 pic_state: &mut PictureState,
1686 frame_context: &FrameBuildingContext,
1687 frame_state: &mut FrameBuildingState,
1688 ) -> Option<LayerRect> {
1689 let mut may_need_clip_mask = true;
1690 let mut pic_state_for_children = PictureState::new();
1691
1692 // Do some basic checks first, that can early out
1693 // without even knowing the local rect.
1694 let (prim_kind, cpu_prim_index) = {
1695 let metadata = &self.cpu_metadata[prim_index.0];
1696
1697 if pic_context.perform_culling &&
1698 !metadata.is_backface_visible &&
1699 prim_run_context.scroll_node.world_content_transform.is_backface_visible() {
1700 return None;
1701 }
1702
1703 (metadata.prim_kind, metadata.cpu_prim_index)
1704 };
1705
1706 // If we have dependencies, we need to prepare them first, in order
1707 // to know the actual rect of this primitive.
1708 // For example, scrolling may affect the location of an item in
1709 // local space, which may force us to render this item on a larger
1710 // picture target, if being composited.
1711 if let PrimitiveKind::Picture = prim_kind {
1712 let pic_context_for_children = {
1713 let pic = &mut self.cpu_pictures[cpu_prim_index.0];
1714
1715 if !pic.resolve_scene_properties(frame_context.scene_properties) {
1716 return None;
1717 }
1718
1719 let (draw_text_transformed, original_reference_frame_index) = match pic.kind {
1720 PictureKind::Image { reference_frame_index, composite_mode, .. } => {
1721 may_need_clip_mask = composite_mode.is_some();
1722 (true, Some(reference_frame_index))
1723 }
1724 PictureKind::TextShadow { .. } => {
1725 (false, None)
1726 }
1727 };
1728
1729 let display_list = &frame_context
1730 .pipelines
1731 .get(&pic.pipeline_id)
1732 .expect("No display list?")
1733 .display_list;
1734
1735 let inv_world_transform = prim_run_context
1736 .scroll_node
1737 .world_content_transform
1738 .inverse();
1739
1740 PictureContext {
1741 pipeline_id: pic.pipeline_id,
1742 perform_culling: pic.cull_children,
1743 prim_runs: mem::replace(&mut pic.runs, Vec::new()),
1744 original_reference_frame_index,
1745 display_list,
1746 draw_text_transformed,
1747 inv_world_transform,
1748 }
1749 };
1750
1751 let result = self.prepare_prim_runs(
1752 &pic_context_for_children,
1753 &mut pic_state_for_children,
1754 frame_context,
1755 frame_state,
1756 );
1757
1758 // Restore the dependencies (borrow check dance)
1759 let pic = &mut self.cpu_pictures[cpu_prim_index.0];
1760 pic.runs = pic_context_for_children.prim_runs;
1761
1762 let metadata = &mut self.cpu_metadata[prim_index.0];
1763 metadata.local_rect = pic.update_local_rect(result);
1764 }
1765
1766 let (local_rect, unclipped_device_rect) = {
1767 let metadata = &mut self.cpu_metadata[prim_index.0];
1768 if metadata.local_rect.size.width <= 0.0 ||
1769 metadata.local_rect.size.height <= 0.0 {
1770 //warn!("invalid primitive rect {:?}", metadata.local_rect);
1771 return None;
1772 }
1773
1774 let local_rect = metadata.local_clip_rect.intersection(&metadata.local_rect);
1775 let local_rect = match local_rect {
1776 Some(local_rect) => local_rect,
1777 None if pic_context.perform_culling => return None,
1778 None => LayerRect::zero(),
1779 };
1780
1781 let screen_bounding_rect = calculate_screen_bounding_rect(
1782 &prim_run_context.scroll_node.world_content_transform,
1783 &local_rect,
1784 frame_context.device_pixel_scale,
1785 );
1786
1787 metadata.screen_rect = screen_bounding_rect
1788 .intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)
1789 .map(|clipped| {
1790 ScreenRect {
1791 clipped,
1792 unclipped: screen_bounding_rect,
1793 }
1794 });
1795
1796 if metadata.screen_rect.is_none() && pic_context.perform_culling {
1797 return None;
1798 }
1799
1800 metadata.clip_chain_rect_index = prim_run_context.clip_chain_rect_index;
1801
1802 (local_rect, screen_bounding_rect)
1803 };
1804
1805 if pic_context.perform_culling && may_need_clip_mask && !self.update_clip_task(
1806 prim_index,
1807 prim_run_context,
1808 &unclipped_device_rect,
1809 pic_state,
1810 frame_context,
1811 frame_state,
1812 ) {
1813 return None;
1814 }
1815
1816 self.prepare_prim_for_render_inner(
1817 prim_index,
1818 prim_run_context,
1819 pic_state_for_children,
1820 pic_context,
1821 pic_state,
1822 frame_context,
1823 frame_state,
1824 );
1825
1826 Some(local_rect)
1827 }
1828
1829 // TODO(gw): Make this simpler / more efficient by tidying
1830 // up the logic that early outs from prepare_prim_for_render.
reset_prim_visibility(&mut self)1831 pub fn reset_prim_visibility(&mut self) {
1832 for md in &mut self.cpu_metadata {
1833 md.screen_rect = None;
1834 }
1835 }
1836
prepare_prim_runs( &mut self, pic_context: &PictureContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> PrimitiveRunLocalRect1837 pub fn prepare_prim_runs(
1838 &mut self,
1839 pic_context: &PictureContext,
1840 pic_state: &mut PictureState,
1841 frame_context: &FrameBuildingContext,
1842 frame_state: &mut FrameBuildingState,
1843 ) -> PrimitiveRunLocalRect {
1844 let mut result = PrimitiveRunLocalRect {
1845 local_rect_in_actual_parent_space: LayerRect::zero(),
1846 local_rect_in_original_parent_space: LayerRect::zero(),
1847 };
1848
1849 for run in &pic_context.prim_runs {
1850 // TODO(gw): Perhaps we can restructure this to not need to create
1851 // a new primitive context for every run (if the hash
1852 // lookups ever show up in a profile).
1853 let scroll_node = &frame_context
1854 .clip_scroll_tree
1855 .nodes[run.clip_and_scroll.scroll_node_id.0];
1856 let clip_chain = frame_context
1857 .clip_scroll_tree
1858 .get_clip_chain(run.clip_and_scroll.clip_chain_index);
1859
1860 if pic_context.perform_culling {
1861 if !scroll_node.invertible {
1862 debug!("{:?} {:?}: position not invertible", run.base_prim_index, pic_context.pipeline_id);
1863 continue;
1864 }
1865
1866 if clip_chain.combined_outer_screen_rect.is_empty() {
1867 debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
1868 continue;
1869 }
1870 }
1871
1872 let parent_relative_transform = pic_context
1873 .inv_world_transform
1874 .map(|inv_parent| {
1875 inv_parent.pre_mul(&scroll_node.world_content_transform)
1876 });
1877
1878 let original_relative_transform = pic_context.original_reference_frame_index
1879 .and_then(|original_reference_frame_index| {
1880 let parent = frame_context
1881 .clip_scroll_tree
1882 .nodes[original_reference_frame_index.0]
1883 .world_content_transform;
1884 parent.inverse()
1885 .map(|inv_parent| {
1886 inv_parent.pre_mul(&scroll_node.world_content_transform)
1887 })
1888 });
1889
1890 let clip_chain_rect = match pic_context.perform_culling {
1891 true => get_local_clip_rect_for_nodes(scroll_node, clip_chain),
1892 false => None,
1893 };
1894
1895 let clip_chain_rect_index = match clip_chain_rect {
1896 Some(rect) if rect.is_empty() => continue,
1897 Some(rect) => {
1898 frame_state.local_clip_rects.push(rect);
1899 ClipChainRectIndex(frame_state.local_clip_rects.len() - 1)
1900 }
1901 None => ClipChainRectIndex(0), // This is no clipping.
1902 };
1903
1904 let child_prim_run_context = PrimitiveRunContext::new(
1905 clip_chain,
1906 scroll_node,
1907 clip_chain_rect_index,
1908 );
1909
1910 for i in 0 .. run.count {
1911 let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
1912
1913 if let Some(prim_local_rect) = self.prepare_prim_for_render(
1914 prim_index,
1915 &child_prim_run_context,
1916 pic_context,
1917 pic_state,
1918 frame_context,
1919 frame_state,
1920 ) {
1921 frame_state.profile_counters.visible_primitives.inc();
1922
1923 if let Some(ref matrix) = original_relative_transform {
1924 let bounds = matrix.transform_rect(&prim_local_rect);
1925 result.local_rect_in_original_parent_space =
1926 result.local_rect_in_original_parent_space.union(&bounds);
1927 }
1928
1929 if let Some(ref matrix) = parent_relative_transform {
1930 let bounds = matrix.transform_rect(&prim_local_rect);
1931 result.local_rect_in_actual_parent_space =
1932 result.local_rect_in_actual_parent_space.union(&bounds);
1933 }
1934 }
1935 }
1936 }
1937
1938 result
1939 }
1940 }
1941
1942 //Test for one clip region contains another
1943 trait InsideTest<T> {
might_contain(&self, clip: &T) -> bool1944 fn might_contain(&self, clip: &T) -> bool;
1945 }
1946
1947 impl InsideTest<ComplexClipRegion> for ComplexClipRegion {
1948 // Returns true if clip is inside self, can return false negative
might_contain(&self, clip: &ComplexClipRegion) -> bool1949 fn might_contain(&self, clip: &ComplexClipRegion) -> bool {
1950 let delta_left = clip.rect.origin.x - self.rect.origin.x;
1951 let delta_top = clip.rect.origin.y - self.rect.origin.y;
1952 let delta_right = self.rect.max_x() - clip.rect.max_x();
1953 let delta_bottom = self.rect.max_y() - clip.rect.max_y();
1954
1955 delta_left >= 0f32 && delta_top >= 0f32 && delta_right >= 0f32 && delta_bottom >= 0f32 &&
1956 clip.radii.top_left.width >= self.radii.top_left.width - delta_left &&
1957 clip.radii.top_left.height >= self.radii.top_left.height - delta_top &&
1958 clip.radii.top_right.width >= self.radii.top_right.width - delta_right &&
1959 clip.radii.top_right.height >= self.radii.top_right.height - delta_top &&
1960 clip.radii.bottom_left.width >= self.radii.bottom_left.width - delta_left &&
1961 clip.radii.bottom_left.height >= self.radii.bottom_left.height - delta_bottom &&
1962 clip.radii.bottom_right.width >= self.radii.bottom_right.width - delta_right &&
1963 clip.radii.bottom_right.height >= self.radii.bottom_right.height - delta_bottom
1964 }
1965 }
1966
convert_clip_chain_to_clip_vector( clip_chain_nodes: ClipChainNodeRef, extra_clip: ClipChainNodeRef, combined_outer_rect: &DeviceIntRect, combined_inner_rect: &mut DeviceIntRect, prim_coordinate_system: CoordinateSystemId, has_clips_from_other_coordinate_systems: &mut bool, ) -> Vec<ClipWorkItem>1967 fn convert_clip_chain_to_clip_vector(
1968 clip_chain_nodes: ClipChainNodeRef,
1969 extra_clip: ClipChainNodeRef,
1970 combined_outer_rect: &DeviceIntRect,
1971 combined_inner_rect: &mut DeviceIntRect,
1972 prim_coordinate_system: CoordinateSystemId,
1973 has_clips_from_other_coordinate_systems: &mut bool,
1974 ) -> Vec<ClipWorkItem> {
1975 // Filter out all the clip instances that don't contribute to the result.
1976 ClipChainNodeIter { current: extra_clip }
1977 .chain(ClipChainNodeIter { current: clip_chain_nodes })
1978 .filter_map(|node| {
1979 *has_clips_from_other_coordinate_systems |=
1980 prim_coordinate_system != node.work_item.coordinate_system_id;
1981
1982 *combined_inner_rect = if !node.screen_inner_rect.is_empty() {
1983 // If this clip's inner area contains the area of the primitive clipped
1984 // by previous clips, then it's not going to affect rendering in any way.
1985 if node.screen_inner_rect.contains_rect(&combined_outer_rect) {
1986 return None;
1987 }
1988 combined_inner_rect.intersection(&node.screen_inner_rect)
1989 .unwrap_or_else(DeviceIntRect::zero)
1990 } else {
1991 DeviceIntRect::zero()
1992 };
1993
1994 Some(node.work_item.clone())
1995 })
1996 .collect()
1997 }
1998
get_local_clip_rect_for_nodes( scroll_node: &ClipScrollNode, clip_chain: &ClipChain, ) -> Option<LayerRect>1999 fn get_local_clip_rect_for_nodes(
2000 scroll_node: &ClipScrollNode,
2001 clip_chain: &ClipChain,
2002 ) -> Option<LayerRect> {
2003 let local_rect = ClipChainNodeIter { current: clip_chain.nodes.clone() }.fold(
2004 None,
2005 |combined_local_clip_rect: Option<LayerRect>, node| {
2006 if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id {
2007 return combined_local_clip_rect;
2008 }
2009
2010 Some(match combined_local_clip_rect {
2011 Some(combined_rect) =>
2012 combined_rect.intersection(&node.local_clip_rect).unwrap_or_else(LayerRect::zero),
2013 None => node.local_clip_rect,
2014 })
2015 }
2016 );
2017
2018 match local_rect {
2019 Some(local_rect) => scroll_node.coordinate_system_relative_transform.unapply(&local_rect),
2020 None => None,
2021 }
2022 }
2023
2024 impl<'a> GpuDataRequest<'a> {
2025 // Write the GPU cache data for an individual segment.
2026 // TODO(gw): The second block is currently unused. In
2027 // the future, it will be used to store a
2028 // UV rect, allowing segments to reference
2029 // part of an image.
write_segment( &mut self, local_rect: LayerRect, )2030 fn write_segment(
2031 &mut self,
2032 local_rect: LayerRect,
2033 ) {
2034 self.push(local_rect);
2035 self.push([
2036 0.0,
2037 0.0,
2038 0.0,
2039 0.0
2040 ]);
2041 }
2042 }
2043