Source code

Revision control

Other Tools

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::{ColorF, DebugFlags, DocumentLayer, FontRenderMode, PremultipliedColorF};
6
use api::units::*;
7
use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
8
use crate::clip::{ClipStore, ClipChainStack, ClipDataHandle};
9
use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId};
10
use crate::composite::{CompositorKind, CompositeState};
11
use crate::debug_render::DebugItem;
12
use crate::gpu_cache::{GpuCache, GpuCacheHandle};
13
use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
14
use crate::gpu_types::TransformData;
15
use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
16
use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
17
use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode};
18
use crate::picture::{BackdropKind, TileCacheLogger};
19
use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
20
use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask};
21
use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters};
22
use crate::render_backend::{DataStores, FrameStamp, FrameId};
23
use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget};
24
use crate::render_target::{RenderTargetContext, RenderTargetKind};
25
use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, RenderTaskGraphCounters};
26
use crate::render_task_graph::{RenderPassKind, RenderPass};
27
use crate::render_task::{RenderTask, RenderTaskLocation, RenderTaskKind};
28
use crate::resource_cache::{ResourceCache};
29
use crate::scene::{BuiltScene, SceneProperties};
30
use crate::segment::SegmentBuilder;
31
use std::{f32, mem};
32
use crate::util::MaxRect;
33
34
35
#[derive(Clone, Copy, Debug, PartialEq)]
36
#[cfg_attr(feature = "capture", derive(Serialize))]
37
#[cfg_attr(feature = "replay", derive(Deserialize))]
38
pub enum ChasePrimitive {
39
Nothing,
40
Id(PrimitiveDebugId),
41
LocalRect(LayoutRect),
42
}
43
44
impl Default for ChasePrimitive {
45
fn default() -> Self {
46
ChasePrimitive::Nothing
47
}
48
}
49
50
#[derive(Clone, Copy, Debug)]
51
#[cfg_attr(feature = "capture", derive(Serialize))]
52
#[cfg_attr(feature = "replay", derive(Deserialize))]
53
pub struct FrameBuilderConfig {
54
pub default_font_render_mode: FontRenderMode,
55
pub dual_source_blending_is_supported: bool,
56
pub dual_source_blending_is_enabled: bool,
57
pub chase_primitive: ChasePrimitive,
58
/// The immutable global picture caching enable from `RendererOptions`
59
pub global_enable_picture_caching: bool,
60
/// True if we're running tests (i.e. via wrench).
61
pub testing: bool,
62
pub gpu_supports_fast_clears: bool,
63
pub gpu_supports_advanced_blend: bool,
64
pub advanced_blend_is_coherent: bool,
65
pub batch_lookback_count: usize,
66
pub background_color: Option<ColorF>,
67
pub compositor_kind: CompositorKind,
68
pub tile_size_override: Option<DeviceIntSize>,
69
pub max_depth_ids: i32,
70
}
71
72
/// A set of common / global resources that are retained between
73
/// new display lists, such that any GPU cache handles can be
74
/// persisted even when a new display list arrives.
75
#[cfg_attr(feature = "capture", derive(Serialize))]
76
pub struct FrameGlobalResources {
77
/// The image shader block for the most common / default
78
/// set of image parameters (color white, stretch == rect.size).
79
pub default_image_handle: GpuCacheHandle,
80
81
/// A GPU cache config for drawing transparent rectangle primitives.
82
/// This is used to 'cut out' overlay tiles where a compositor
83
/// surface exists.
84
pub default_transparent_rect_handle: GpuCacheHandle,
85
}
86
87
impl FrameGlobalResources {
88
pub fn empty() -> Self {
89
FrameGlobalResources {
90
default_image_handle: GpuCacheHandle::new(),
91
default_transparent_rect_handle: GpuCacheHandle::new(),
92
}
93
}
94
95
pub fn update(
96
&mut self,
97
gpu_cache: &mut GpuCache,
98
) {
99
if let Some(mut request) = gpu_cache.request(&mut self.default_image_handle) {
100
request.push(PremultipliedColorF::WHITE);
101
request.push(PremultipliedColorF::WHITE);
102
request.push([
103
-1.0, // -ve means use prim rect for stretch size
104
0.0,
105
0.0,
106
0.0,
107
]);
108
}
109
110
if let Some(mut request) = gpu_cache.request(&mut self.default_transparent_rect_handle) {
111
request.push(PremultipliedColorF::TRANSPARENT);
112
}
113
}
114
}
115
116
/// Produces the frames that are sent to the renderer.
117
#[cfg_attr(feature = "capture", derive(Serialize))]
118
pub struct FrameBuilder {
119
/// Cache of surface tiles from the previous frame builder
120
/// that can optionally be consumed by this frame builder.
121
pending_retained_tiles: RetainedTiles,
122
pub globals: FrameGlobalResources,
123
}
124
125
pub struct FrameVisibilityContext<'a> {
126
pub spatial_tree: &'a SpatialTree,
127
pub global_screen_world_rect: WorldRect,
128
pub global_device_pixel_scale: DevicePixelScale,
129
pub surfaces: &'a [SurfaceInfo],
130
pub debug_flags: DebugFlags,
131
pub scene_properties: &'a SceneProperties,
132
pub config: FrameBuilderConfig,
133
}
134
135
pub struct FrameVisibilityState<'a> {
136
pub clip_store: &'a mut ClipStore,
137
pub resource_cache: &'a mut ResourceCache,
138
pub gpu_cache: &'a mut GpuCache,
139
pub scratch: &'a mut PrimitiveScratchBuffer,
140
pub tile_cache: Option<Box<TileCacheInstance>>,
141
pub retained_tiles: &'a mut RetainedTiles,
142
pub data_stores: &'a mut DataStores,
143
pub clip_chain_stack: ClipChainStack,
144
pub render_tasks: &'a mut RenderTaskGraph,
145
pub composite_state: &'a mut CompositeState,
146
/// A stack of currently active off-screen surfaces during the
147
/// visibility frame traversal.
148
pub surface_stack: Vec<SurfaceIndex>,
149
}
150
151
impl<'a> FrameVisibilityState<'a> {
152
pub fn push_surface(
153
&mut self,
154
surface_index: SurfaceIndex,
155
shared_clips: &[ClipDataHandle]
156
) {
157
self.surface_stack.push(surface_index);
158
self.clip_chain_stack.push_surface(shared_clips);
159
}
160
161
pub fn pop_surface(&mut self) {
162
self.surface_stack.pop().unwrap();
163
self.clip_chain_stack.pop_surface();
164
}
165
}
166
167
pub struct FrameBuildingContext<'a> {
168
pub global_device_pixel_scale: DevicePixelScale,
169
pub scene_properties: &'a SceneProperties,
170
pub global_screen_world_rect: WorldRect,
171
pub spatial_tree: &'a SpatialTree,
172
pub max_local_clip: LayoutRect,
173
pub debug_flags: DebugFlags,
174
pub fb_config: &'a FrameBuilderConfig,
175
}
176
177
pub struct FrameBuildingState<'a> {
178
pub render_tasks: &'a mut RenderTaskGraph,
179
pub profile_counters: &'a mut FrameProfileCounters,
180
pub clip_store: &'a mut ClipStore,
181
pub resource_cache: &'a mut ResourceCache,
182
pub gpu_cache: &'a mut GpuCache,
183
pub transforms: &'a mut TransformPalette,
184
pub segment_builder: SegmentBuilder,
185
pub surfaces: &'a mut Vec<SurfaceInfo>,
186
pub dirty_region_stack: Vec<DirtyRegion>,
187
pub composite_state: &'a mut CompositeState,
188
}
189
190
impl<'a> FrameBuildingState<'a> {
191
/// Retrieve the current dirty region during primitive traversal.
192
pub fn current_dirty_region(&self) -> &DirtyRegion {
193
self.dirty_region_stack.last().unwrap()
194
}
195
196
/// Push a new dirty region for child primitives to cull / clip against.
197
pub fn push_dirty_region(&mut self, region: DirtyRegion) {
198
self.dirty_region_stack.push(region);
199
}
200
201
/// Pop the top dirty region from the stack.
202
pub fn pop_dirty_region(&mut self) {
203
self.dirty_region_stack.pop().unwrap();
204
}
205
}
206
207
/// Immutable context of a picture when processing children.
208
#[derive(Debug)]
209
pub struct PictureContext {
210
pub pic_index: PictureIndex,
211
pub apply_local_clip_rect: bool,
212
pub is_passthrough: bool,
213
pub surface_spatial_node_index: SpatialNodeIndex,
214
pub raster_spatial_node_index: SpatialNodeIndex,
215
/// The surface that this picture will render on.
216
pub surface_index: SurfaceIndex,
217
pub dirty_region_count: usize,
218
pub subpixel_mode: SubpixelMode,
219
}
220
221
/// Mutable state of a picture that gets modified when
222
/// the children are processed.
223
pub struct PictureState {
224
pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
225
pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
226
pub map_pic_to_raster: SpaceMapper<PicturePixel, RasterPixel>,
227
pub map_raster_to_world: SpaceMapper<RasterPixel, WorldPixel>,
228
/// If the plane splitter, the primitives get added to it instead of
229
/// batching into their parent pictures.
230
pub plane_splitter: Option<PlaneSplitter>,
231
}
232
233
impl FrameBuilder {
234
pub fn new() -> Self {
235
FrameBuilder {
236
pending_retained_tiles: RetainedTiles::new(),
237
globals: FrameGlobalResources::empty(),
238
}
239
}
240
241
/// Provide any cached surface tiles from the previous frame builder
242
/// to a new frame builder. These will be consumed or dropped the
243
/// first time a new frame builder creates a frame.
244
pub fn set_retained_resources(&mut self, retained_tiles: RetainedTiles) {
245
// In general, the pending retained tiles are consumed by the frame
246
// builder the first time a frame is built after a new scene has
247
// arrived. However, if two scenes arrive in quick succession, the
248
// frame builder may not have had a chance to build a frame and
249
// consume the pending tiles. In this case, the pending tiles will
250
// be lost, causing a full invalidation of the entire screen. To
251
// avoid this, if there are still pending tiles, include them in
252
// the retained tiles passed to the next frame builder.
253
self.pending_retained_tiles.merge(retained_tiles);
254
}
255
256
/// Compute the contribution (bounding rectangles, and resources) of layers and their
257
/// primitives in screen space.
258
fn build_layer_screen_rects_and_cull_layers(
259
&mut self,
260
scene: &mut BuiltScene,
261
global_screen_world_rect: WorldRect,
262
resource_cache: &mut ResourceCache,
263
gpu_cache: &mut GpuCache,
264
render_tasks: &mut RenderTaskGraph,
265
profile_counters: &mut FrameProfileCounters,
266
global_device_pixel_scale: DevicePixelScale,
267
scene_properties: &SceneProperties,
268
transform_palette: &mut TransformPalette,
269
data_stores: &mut DataStores,
270
surfaces: &mut Vec<SurfaceInfo>,
271
scratch: &mut PrimitiveScratchBuffer,
272
debug_flags: DebugFlags,
273
texture_cache_profile: &mut TextureCacheProfileCounters,
274
composite_state: &mut CompositeState,
275
tile_cache_logger: &mut TileCacheLogger,
276
) -> Option<RenderTaskId> {
277
profile_scope!("cull");
278
279
if scene.prim_store.pictures.is_empty() {
280
return None
281
}
282
283
scratch.begin_frame();
284
285
let root_spatial_node_index = scene.spatial_tree.root_reference_frame_index();
286
287
const MAX_CLIP_COORD: f32 = 1.0e9;
288
289
let frame_context = FrameBuildingContext {
290
global_device_pixel_scale,
291
scene_properties,
292
global_screen_world_rect,
293
spatial_tree: &scene.spatial_tree,
294
max_local_clip: LayoutRect::new(
295
LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
296
LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD),
297
),
298
debug_flags,
299
fb_config: &scene.config,
300
};
301
302
let root_render_task_id = render_tasks.add().init(
303
RenderTask::new_picture(
304
RenderTaskLocation::Fixed(scene.output_rect),
305
scene.output_rect.size.to_f32(),
306
scene.root_pic_index,
307
DeviceIntPoint::zero(),
308
UvRectKind::Rect,
309
ROOT_SPATIAL_NODE_INDEX,
310
global_device_pixel_scale,
311
PrimitiveVisibilityMask::all(),
312
None,
313
None,
314
)
315
);
316
317
// Construct a dummy root surface, that represents the
318
// main framebuffer surface.
319
let root_surface = SurfaceInfo::new(
320
ROOT_SPATIAL_NODE_INDEX,
321
ROOT_SPATIAL_NODE_INDEX,
322
0.0,
323
global_screen_world_rect,
324
&scene.spatial_tree,
325
global_device_pixel_scale,
326
(1.0, 1.0),
327
);
328
surfaces.push(root_surface);
329
330
let mut retained_tiles = mem::replace(
331
&mut self.pending_retained_tiles,
332
RetainedTiles::new(),
333
);
334
335
// The first major pass of building a frame is to walk the picture
336
// tree. This pass must be quick (it should never touch individual
337
// primitives). For now, all we do here is determine which pictures
338
// will create surfaces. In the future, this will be expanded to
339
// set up render tasks, determine scaling of surfaces, and detect
340
// which surfaces have valid cached surfaces that don't need to
341
// be rendered this frame.
342
PictureUpdateState::update_all(
343
surfaces,
344
scene.root_pic_index,
345
&mut scene.prim_store.pictures,
346
&frame_context,
347
gpu_cache,
348
&scene.clip_store,
349
data_stores,
350
composite_state,
351
);
352
353
{
354
profile_marker!("UpdateVisibility");
355
356
let visibility_context = FrameVisibilityContext {
357
global_device_pixel_scale,
358
spatial_tree: &scene.spatial_tree,
359
global_screen_world_rect,
360
surfaces,
361
debug_flags,
362
scene_properties,
363
config: scene.config,
364
};
365
366
let mut visibility_state = FrameVisibilityState {
367
resource_cache,
368
gpu_cache,
369
clip_store: &mut scene.clip_store,
370
scratch,
371
tile_cache: None,
372
retained_tiles: &mut retained_tiles,
373
data_stores,
374
clip_chain_stack: ClipChainStack::new(),
375
render_tasks,
376
composite_state,
377
/// Try to avoid allocating during frame traversal - it's unlikely to have a
378
/// surface stack depth of > 16 in most cases.
379
surface_stack: Vec::with_capacity(16),
380
};
381
382
scene.prim_store.update_visibility(
383
scene.root_pic_index,
384
ROOT_SURFACE_INDEX,
385
&global_screen_world_rect,
386
&visibility_context,
387
&mut visibility_state,
388
);
389
390
// When there are tiles that are left remaining in the `retained_tiles`,
391
// dirty rects are not valid.
392
if !visibility_state.retained_tiles.caches.is_empty() {
393
visibility_state.composite_state.dirty_rects_are_valid = false;
394
}
395
396
// When a new display list is processed by WR, the existing tiles from
397
// any picture cache are stored in the `retained_tiles` field above. This
398
// allows the first frame of a new display list to reuse any existing tiles
399
// and surfaces that match. Once the `update_visibility` call above is
400
// complete, any tiles that are left remaining in the `retained_tiles`
401
// map are not needed and will be dropped. For simple compositing mode,
402
// this is fine, since texture cache handles are garbage collected at
403
// the end of each frame. However, if we're in native compositor mode,
404
// we need to manually clean up any native compositor surfaces that were
405
// allocated by these tiles.
406
for (_, mut cache_state) in visibility_state.retained_tiles.caches.drain() {
407
if let Some(native_surface) = cache_state.native_surface.take() {
408
visibility_state.resource_cache.destroy_compositor_surface(native_surface.opaque);
409
visibility_state.resource_cache.destroy_compositor_surface(native_surface.alpha);
410
}
411
412
for (_, external_surface) in cache_state.external_native_surface_cache.drain() {
413
visibility_state.resource_cache.destroy_compositor_surface(external_surface.native_surface_id)
414
}
415
}
416
}
417
418
let mut frame_state = FrameBuildingState {
419
render_tasks,
420
profile_counters,
421
clip_store: &mut scene.clip_store,
422
resource_cache,
423
gpu_cache,
424
transforms: transform_palette,
425
segment_builder: SegmentBuilder::new(),
426
surfaces,
427
dirty_region_stack: Vec::new(),
428
composite_state,
429
};
430
431
frame_state
432
.surfaces
433
.first_mut()
434
.unwrap()
435
.render_tasks = Some(SurfaceRenderTasks {
436
root: root_render_task_id,
437
port: root_render_task_id,
438
});
439
440
// Push a default dirty region which culls primitives
441
// against the screen world rect, in absence of any
442
// other dirty regions.
443
let mut default_dirty_region = DirtyRegion::new();
444
default_dirty_region.push(
445
frame_context.global_screen_world_rect,
446
PrimitiveVisibilityMask::all(),
447
);
448
frame_state.push_dirty_region(default_dirty_region);
449
450
let (pic_context, mut pic_state, mut prim_list) = scene
451
.prim_store
452
.pictures[scene.root_pic_index.0]
453
.take_context(
454
scene.root_pic_index,
455
WorldRect::max_rect(),
456
root_spatial_node_index,
457
root_spatial_node_index,
458
ROOT_SURFACE_INDEX,
459
&SubpixelMode::Allow,
460
&mut frame_state,
461
&frame_context,
462
scratch,
463
tile_cache_logger
464
)
465
.unwrap();
466
467
tile_cache_logger.advance();
468
469
{
470
profile_marker!("PreparePrims");
471
472
scene.prim_store.prepare_primitives(
473
&mut prim_list,
474
&pic_context,
475
&mut pic_state,
476
&frame_context,
477
&mut frame_state,
478
data_stores,
479
scratch,
480
tile_cache_logger,
481
);
482
}
483
484
let pic = &mut scene.prim_store.pictures[scene.root_pic_index.0];
485
pic.restore_context(
486
ROOT_SURFACE_INDEX,
487
prim_list,
488
pic_context,
489
pic_state,
490
&mut frame_state,
491
);
492
493
frame_state.pop_dirty_region();
494
495
{
496
profile_marker!("BlockOnResources");
497
498
resource_cache.block_until_all_resources_added(gpu_cache,
499
render_tasks,
500
texture_cache_profile);
501
}
502
503
Some(root_render_task_id)
504
}
505
506
pub fn build(
507
&mut self,
508
scene: &mut BuiltScene,
509
resource_cache: &mut ResourceCache,
510
gpu_cache: &mut GpuCache,
511
stamp: FrameStamp,
512
global_device_pixel_scale: DevicePixelScale,
513
layer: DocumentLayer,
514
device_origin: DeviceIntPoint,
515
pan: WorldPoint,
516
resource_profile: &mut ResourceProfileCounters,
517
scene_properties: &SceneProperties,
518
data_stores: &mut DataStores,
519
scratch: &mut PrimitiveScratchBuffer,
520
render_task_counters: &mut RenderTaskGraphCounters,
521
debug_flags: DebugFlags,
522
tile_cache_logger: &mut TileCacheLogger,
523
) -> Frame {
524
profile_scope!("build");
525
profile_marker!("BuildFrame");
526
527
let mut profile_counters = FrameProfileCounters::new();
528
profile_counters
529
.total_primitives
530
.set(scene.prim_store.prim_count());
531
resource_profile.content_slices.set(scene.content_slice_count);
532
resource_cache.begin_frame(stamp);
533
gpu_cache.begin_frame(stamp);
534
535
self.globals.update(gpu_cache);
536
537
scene.spatial_tree.update_tree(
538
pan,
539
global_device_pixel_scale,
540
scene_properties,
541
);
542
let mut transform_palette = scene.spatial_tree.build_transform_palette();
543
scene.clip_store.clear_old_instances();
544
545
let mut render_tasks = RenderTaskGraph::new(
546
stamp.frame_id(),
547
render_task_counters,
548
);
549
let mut surfaces = Vec::new();
550
551
let output_size = scene.output_rect.size.to_i32();
552
let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
553
554
// Determine if we will draw this frame with picture caching enabled. This depends on:
555
// (1) If globally enabled when WR was initialized
556
// (2) If current debug flags allow picture caching
557
// (3) Whether we are currently pinch zooming
558
// (4) If any picture cache spatial nodes are not in the root coordinate system
559
let picture_caching_is_enabled =
560
scene.config.global_enable_picture_caching &&
561
!debug_flags.contains(DebugFlags::DISABLE_PICTURE_CACHING) &&
562
!scene.picture_cache_spatial_nodes.iter().any(|spatial_node_index| {
563
let spatial_node = &scene
564
.spatial_tree
565
.spatial_nodes[spatial_node_index.0 as usize];
566
spatial_node.coordinate_system_id != CoordinateSystemId::root() ||
567
spatial_node.is_ancestor_or_self_zooming
568
});
569
570
let mut composite_state = CompositeState::new(
571
scene.config.compositor_kind,
572
picture_caching_is_enabled,
573
global_device_pixel_scale,
574
scene.config.max_depth_ids,
575
);
576
577
let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
578
scene,
579
screen_world_rect,
580
resource_cache,
581
gpu_cache,
582
&mut render_tasks,
583
&mut profile_counters,
584
global_device_pixel_scale,
585
scene_properties,
586
&mut transform_palette,
587
data_stores,
588
&mut surfaces,
589
scratch,
590
debug_flags,
591
&mut resource_profile.texture_cache,
592
&mut composite_state,
593
tile_cache_logger,
594
);
595
596
let mut passes;
597
let mut deferred_resolves = vec![];
598
let mut has_texture_cache_tasks = false;
599
let mut prim_headers = PrimitiveHeaders::new();
600
601
{
602
profile_marker!("Batching");
603
604
passes = render_tasks.generate_passes(
605
main_render_task_id,
606
output_size,
607
scene.config.gpu_supports_fast_clears,
608
);
609
610
// Used to generated a unique z-buffer value per primitive.
611
let mut z_generator = ZBufferIdGenerator::new(layer, scene.config.max_depth_ids);
612
let use_dual_source_blending = scene.config.dual_source_blending_is_enabled &&
613
scene.config.dual_source_blending_is_supported;
614
615
for pass in &mut passes {
616
let mut ctx = RenderTargetContext {
617
global_device_pixel_scale,
618
prim_store: &scene.prim_store,
619
resource_cache,
620
use_dual_source_blending,
621
use_advanced_blending: scene.config.gpu_supports_advanced_blend,
622
break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
623
batch_lookback_count: scene.config.batch_lookback_count,
624
spatial_tree: &scene.spatial_tree,
625
data_stores,
626
surfaces: &surfaces,
627
scratch,
628
screen_world_rect,
629
globals: &self.globals,
630
};
631
632
build_render_pass(
633
pass,
634
&mut ctx,
635
gpu_cache,
636
&mut render_tasks,
637
&mut deferred_resolves,
638
&scene.clip_store,
639
&mut transform_palette,
640
&mut prim_headers,
641
&mut z_generator,
642
&mut composite_state,
643
);
644
645
match pass.kind {
646
RenderPassKind::MainFramebuffer { .. } => {}
647
RenderPassKind::OffScreen {
648
ref texture_cache,
649
ref picture_cache,
650
..
651
} => {
652
has_texture_cache_tasks |= !texture_cache.is_empty();
653
has_texture_cache_tasks |= !picture_cache.is_empty();
654
}
655
}
656
}
657
}
658
659
let gpu_cache_frame_id = gpu_cache.end_frame(&mut resource_profile.gpu_cache).frame_id();
660
661
render_tasks.write_task_data();
662
*render_task_counters = render_tasks.counters();
663
resource_cache.end_frame(&mut resource_profile.texture_cache);
664
665
Frame {
666
content_origin: scene.output_rect.origin,
667
device_rect: DeviceIntRect::new(
668
device_origin,
669
scene.output_rect.size,
670
),
671
layer,
672
profile_counters,
673
passes,
674
transform_palette: transform_palette.finish(),
675
render_tasks,
676
deferred_resolves,
677
gpu_cache_frame_id,
678
has_been_rendered: false,
679
has_texture_cache_tasks,
680
prim_headers,
681
recorded_dirty_regions: mem::replace(&mut scratch.recorded_dirty_regions, Vec::new()),
682
debug_items: mem::replace(&mut scratch.debug_items, Vec::new()),
683
composite_state,
684
}
685
}
686
}
687
688
/// Processes this pass to prepare it for rendering.
689
///
690
/// Among other things, this allocates output regions for each of our tasks
691
/// (added via `add_render_task`) in a RenderTarget and assigns it into that
692
/// target.
693
pub fn build_render_pass(
694
pass: &mut RenderPass,
695
ctx: &mut RenderTargetContext,
696
gpu_cache: &mut GpuCache,
697
render_tasks: &mut RenderTaskGraph,
698
deferred_resolves: &mut Vec<DeferredResolve>,
699
clip_store: &ClipStore,
700
transforms: &mut TransformPalette,
701
prim_headers: &mut PrimitiveHeaders,
702
z_generator: &mut ZBufferIdGenerator,
703
composite_state: &mut CompositeState,
704
) {
705
profile_scope!("RenderPass::build");
706
707
match pass.kind {
708
RenderPassKind::MainFramebuffer { ref mut main_target, .. } => {
709
for &task_id in &pass.tasks {
710
assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color);
711
main_target.add_task(
712
task_id,
713
ctx,
714
gpu_cache,
715
render_tasks,
716
clip_store,
717
transforms,
718
deferred_resolves,
719
);
720
}
721
main_target.build(
722
ctx,
723
gpu_cache,
724
render_tasks,
725
deferred_resolves,
726
prim_headers,
727
transforms,
728
z_generator,
729
composite_state,
730
);
731
}
732
RenderPassKind::OffScreen {
733
ref mut color,
734
ref mut alpha,
735
ref mut texture_cache,
736
ref mut picture_cache,
737
} => {
738
let saved_color = if pass.tasks.iter().any(|&task_id| {
739
let t = &render_tasks[task_id];
740
t.target_kind() == RenderTargetKind::Color && t.saved_index.is_some()
741
}) {
742
Some(render_tasks.save_target())
743
} else {
744
None
745
};
746
let saved_alpha = if pass.tasks.iter().any(|&task_id| {
747
let t = &render_tasks[task_id];
748
t.target_kind() == RenderTargetKind::Alpha && t.saved_index.is_some()
749
}) {
750
Some(render_tasks.save_target())
751
} else {
752
None
753
};
754
755
// Collect a list of picture cache tasks, keyed by picture index.
756
// This allows us to only walk that picture root once, adding the
757
// primitives to all relevant batches at the same time.
758
let mut picture_cache_tasks = FastHashMap::default();
759
760
// Step through each task, adding to batches as appropriate.
761
for &task_id in &pass.tasks {
762
let (target_kind, texture_target, layer) = {
763
let task = &mut render_tasks[task_id];
764
let target_kind = task.target_kind();
765
766
// Find a target to assign this task to, or create a new
767
// one if required.
768
let (texture_target, layer) = match task.location {
769
RenderTaskLocation::TextureCache { texture, layer, .. } => {
770
(Some(texture), layer)
771
}
772
RenderTaskLocation::Fixed(..) => {
773
(None, 0)
774
}
775
RenderTaskLocation::Dynamic(ref mut origin, size) => {
776
let (target_index, alloc_origin) = match target_kind {
777
RenderTargetKind::Color => color.allocate(size),
778
RenderTargetKind::Alpha => alpha.allocate(size),
779
};
780
*origin = Some((alloc_origin, target_index));
781
(None, target_index.0)
782
}
783
RenderTaskLocation::PictureCache { .. } => {
784
// For picture cache tiles, just store them in the map
785
// of picture cache tasks, to be handled below.
786
let pic_index = match task.kind {
787
RenderTaskKind::Picture(ref info) => {
788
info.pic_index
789
}
790
_ => {
791
unreachable!();
792
}
793
};
794
795
picture_cache_tasks
796
.entry(pic_index)
797
.or_insert_with(Vec::new)
798
.push(task_id);
799
800
continue;
801
}
802
};
803
804
// Replace the pending saved index with a real one
805
if let Some(index) = task.saved_index {
806
assert_eq!(index, SavedTargetIndex::PENDING);
807
task.saved_index = match target_kind {
808
RenderTargetKind::Color => saved_color,
809
RenderTargetKind::Alpha => saved_alpha,
810
};
811
}
812
813
// Give the render task an opportunity to add any
814
// information to the GPU cache, if appropriate.
815
task.write_gpu_blocks(gpu_cache);
816
817
(target_kind, texture_target, layer)
818
};
819
820
match texture_target {
821
Some(texture_target) => {
822
let texture = texture_cache
823
.entry((texture_target, layer))
824
.or_insert_with(||
825
TextureCacheRenderTarget::new(target_kind)
826
);
827
texture.add_task(task_id, render_tasks);
828
}
829
None => {
830
match target_kind {
831
RenderTargetKind::Color => {
832
color.targets[layer].add_task(
833
task_id,
834
ctx,
835
gpu_cache,
836
render_tasks,
837
clip_store,
838
transforms,
839
deferred_resolves,
840
)
841
}
842
RenderTargetKind::Alpha => {
843
alpha.targets[layer].add_task(
844
task_id,
845
ctx,
846
gpu_cache,
847
render_tasks,
848
clip_store,
849
transforms,
850
deferred_resolves,
851
)
852
}
853
}
854
}
855
}
856
}
857
858
// For each picture in this pass that has picture cache tiles, create
859
// a batcher per task, and then build batches for each of the tasks
860
// at the same time.
861
for (pic_index, task_ids) in picture_cache_tasks {
862
let pic = &ctx.prim_store.pictures[pic_index.0];
863
let tile_cache = pic.tile_cache.as_ref().expect("bug");
864
865
// Extract raster/surface spatial nodes for this surface.
866
let (root_spatial_node_index, surface_spatial_node_index) = match pic.raster_config {
867
Some(ref rc) => {
868
let surface = &ctx.surfaces[rc.surface_index.0];
869
(surface.raster_spatial_node_index, surface.surface_spatial_node_index)
870
}
871
None => {
872
unreachable!();
873
}
874
};
875
876
// Determine the clear color for this picture cache.
877
// If the entire tile cache is opaque, we can skip clear completely.
878
// If it's the first layer, clear it to white to allow subpixel AA on that
879
// first layer even if it's technically transparent.
880
// Otherwise, clear to transparent and composite with alpha.
881
// TODO(gw): We can detect per-tile opacity for the clear color here
882
// which might be a significant win on some pages?
883
let forced_opaque = match tile_cache.background_color {
884
Some(color) => color.a >= 1.0,
885
None => false,
886
};
887
let mut clear_color = if forced_opaque {
888
Some(ColorF::WHITE)
889
} else {
890
Some(ColorF::TRANSPARENT)
891
};
892
893
// If this picture cache has a valid color backdrop, we will use
894
// that as the clear color, skipping the draw of the backdrop
895
// primitive (and anything prior to it) during batching.
896
if let Some(BackdropKind::Color { color }) = tile_cache.backdrop.kind {
897
clear_color = Some(color);
898
}
899
900
// Create an alpha batcher for each of the tasks of this picture.
901
let mut batchers = Vec::new();
902
for task_id in &task_ids {
903
let task_id = *task_id;
904
let vis_mask = match render_tasks[task_id].kind {
905
RenderTaskKind::Picture(ref info) => info.vis_mask,
906
_ => unreachable!(),
907
};
908
batchers.push(AlphaBatchBuilder::new(
909
pass.screen_size,
910
ctx.break_advanced_blend_batches,
911
ctx.batch_lookback_count,
912
task_id,
913
render_tasks.get_task_address(task_id),
914
vis_mask,
915
));
916
}
917
918
// Run the batch creation code for this picture, adding items to
919
// all relevant per-task batchers.
920
let mut batch_builder = BatchBuilder::new(batchers);
921
batch_builder.add_pic_to_batch(
922
pic,
923
ctx,
924
gpu_cache,
925
render_tasks,
926
deferred_resolves,
927
prim_headers,
928
transforms,
929
root_spatial_node_index,
930
surface_spatial_node_index,
931
z_generator,
932
composite_state,
933
);
934
935
// Create picture cache targets, one per render task, and assign
936
// the correct batcher to them.
937
let batchers = batch_builder.finalize();
938
for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) {
939
let task = &render_tasks[task_id];
940
let (target_rect, _) = task.get_target_rect();
941
942
match task.location {
943
RenderTaskLocation::PictureCache { ref surface, .. } => {
944
// TODO(gw): The interface here is a bit untidy since it's
945
// designed to support batch merging, which isn't
946
// relevant for picture cache targets. We
947
// can restructure / tidy this up a bit.
948
let (scissor_rect, valid_rect) = match render_tasks[task_id].kind {
949
RenderTaskKind::Picture(ref info) => {
950
(
951
info.scissor_rect.expect("bug: must be set for cache tasks"),
952
info.valid_rect.expect("bug: must be set for cache tasks"),
953
)
954
}
955
_ => unreachable!(),
956
};
957
let mut batch_containers = Vec::new();
958
let mut alpha_batch_container = AlphaBatchContainer::new(Some(scissor_rect));
959
batcher.build(
960
&mut batch_containers,
961
&mut alpha_batch_container,
962
target_rect,
963
None,
964
);
965
debug_assert!(batch_containers.is_empty());
966
967
let target = PictureCacheTarget {
968
surface: surface.clone(),
969
clear_color,
970
alpha_batch_container,
971
dirty_rect: scissor_rect,
972
valid_rect,
973
};
974
975
picture_cache.push(target);
976
}
977
_ => {
978
unreachable!()
979
}
980
}
981
}
982
}
983
984
color.build(
985
ctx,
986
gpu_cache,
987
render_tasks,
988
deferred_resolves,
989
saved_color,
990
prim_headers,
991
transforms,
992
z_generator,
993
composite_state,
994
);
995
alpha.build(
996
ctx,
997
gpu_cache,
998
render_tasks,
999
deferred_resolves,
1000
saved_alpha,
1001
prim_headers,
1002
transforms,
1003
z_generator,
1004
composite_state,
1005
);
1006
}
1007
}
1008
}
1009
1010
/// A rendering-oriented representation of the frame built by the render backend
1011
/// and presented to the renderer.
1012
#[cfg_attr(feature = "capture", derive(Serialize))]
1013
#[cfg_attr(feature = "replay", derive(Deserialize))]
1014
pub struct Frame {
1015
/// The origin on content produced by the render tasks.
1016
pub content_origin: DeviceIntPoint,
1017
/// The rectangle to show the frame in, on screen.
1018
pub device_rect: DeviceIntRect,
1019
pub layer: DocumentLayer,
1020
pub passes: Vec<RenderPass>,
1021
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(default = "FrameProfileCounters::new", skip))]
1022
pub profile_counters: FrameProfileCounters,
1023
1024
pub transform_palette: Vec<TransformData>,
1025
pub render_tasks: RenderTaskGraph,
1026
pub prim_headers: PrimitiveHeaders,
1027
1028
/// The GPU cache frame that the contents of Self depend on
1029
pub gpu_cache_frame_id: FrameId,
1030
1031
/// List of textures that we don't know about yet
1032
/// from the backend thread. The render thread
1033
/// will use a callback to resolve these and
1034
/// patch the data structures.
1035
pub deferred_resolves: Vec<DeferredResolve>,
1036
1037
/// True if this frame contains any render tasks
1038
/// that write to the texture cache.
1039
pub has_texture_cache_tasks: bool,
1040
1041
/// True if this frame has been drawn by the
1042
/// renderer.
1043
pub has_been_rendered: bool,
1044
1045
/// Dirty regions recorded when generating this frame. Empty when not in
1046
/// testing.
1047
#[cfg_attr(feature = "serde", serde(skip))]
1048
pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
1049
1050
/// Debugging information to overlay for this frame.
1051
pub debug_items: Vec<DebugItem>,
1052
1053
/// Contains picture cache tiles, and associated information.
1054
/// Used by the renderer to composite tiles into the framebuffer,
1055
/// or hand them off to an OS compositor.
1056
pub composite_state: CompositeState,
1057
}
1058
1059
impl Frame {
1060
// This frame must be flushed if it writes to the
1061
// texture cache, and hasn't been drawn yet.
1062
pub fn must_be_drawn(&self) -> bool {
1063
self.has_texture_cache_tasks && !self.has_been_rendered
1064
}
1065
1066
// Returns true if this frame doesn't alter what is on screen currently.
1067
pub fn is_nop(&self) -> bool {
1068
// If picture caching is disabled, we don't have enough information
1069
// to know if this frame is a nop, so it gets drawn unconditionally.
1070
if !self.composite_state.picture_caching_is_enabled {
1071
return false;
1072
}
1073
1074
// When picture caching is enabled, the first (main framebuffer) pass
1075
// consists of compositing tiles only (whether via the simple compositor
1076
// or the native OS compositor). If there are no other passes, that
1077
// implies that none of the picture cache tiles were updated, and thus
1078
// the frame content must be exactly the same as last frame. If this is
1079
// true, drawing this frame is a no-op and can be skipped.
1080
1081
if self.passes.len() > 1 {
1082
return false;
1083
}
1084
1085
true
1086
}
1087
}