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
6
use api::units::*;
7
use api::{ColorF, PremultipliedColorF, ImageFormat, LineOrientation, BorderStyle, PipelineId};
8
use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, resolve_image};
9
use crate::batch::{ClipBatcher, BatchBuilder};
10
use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX};
11
use crate::clip::ClipStore;
12
use crate::composite::CompositeState;
13
use crate::device::Texture;
14
use crate::frame_builder::{FrameGlobalResources};
15
use crate::gpu_cache::{GpuCache, GpuCacheAddress};
16
use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
17
use crate::gpu_types::{TransformPalette, ZBufferIdGenerator};
18
use crate::internal_types::{FastHashMap, TextureSource, LayerIndex, Swizzle, SavedTargetIndex};
19
use crate::picture::{SurfaceInfo, ResolvedSurfaceTexture};
20
use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer, PrimitiveVisibilityMask};
21
use crate::prim_store::gradient::GRADIENT_FP_STOPS;
22
use crate::render_backend::DataStores;
23
use crate::render_task::{RenderTaskKind, RenderTaskAddress, ClearMode, BlitSource};
24
use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo};
25
use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
26
use crate::resource_cache::ResourceCache;
27
use crate::texture_allocator::{ArrayAllocationTracker, FreeRectSlice};
28
use std::{cmp, mem};
29
30
31
const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16);
32
const STYLE_MASK: i32 = 0x00FF_FF00;
33
34
/// According to apitrace, textures larger than 2048 break fast clear
35
/// optimizations on some intel drivers. We sometimes need to go larger, but
36
/// we try to avoid it. This can go away when proper tiling support lands,
37
/// since we can then split large primitives across multiple textures.
38
const IDEAL_MAX_TEXTURE_DIMENSION: i32 = 2048;
39
/// If we ever need a larger texture than the ideal, we better round it up to a
40
/// reasonable number in order to have a bit of leeway in placing things inside.
41
const TEXTURE_DIMENSION_MASK: i32 = 0xFF;
42
43
/// A tag used to identify the output format of a `RenderTarget`.
44
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
45
#[cfg_attr(feature = "capture", derive(Serialize))]
46
#[cfg_attr(feature = "replay", derive(Deserialize))]
47
pub enum RenderTargetKind {
48
Color, // RGBA8
49
Alpha, // R8
50
}
51
52
/// Identifies a given `RenderTarget` in a `RenderTargetList`.
53
#[derive(Debug, Copy, Clone)]
54
#[cfg_attr(feature = "capture", derive(Serialize))]
55
#[cfg_attr(feature = "replay", derive(Deserialize))]
56
pub struct RenderTargetIndex(pub usize);
57
58
pub struct RenderTargetContext<'a, 'rc> {
59
pub global_device_pixel_scale: DevicePixelScale,
60
pub prim_store: &'a PrimitiveStore,
61
pub resource_cache: &'rc mut ResourceCache,
62
pub use_dual_source_blending: bool,
63
pub use_advanced_blending: bool,
64
pub break_advanced_blend_batches: bool,
65
pub batch_lookback_count: usize,
66
pub spatial_tree: &'a SpatialTree,
67
pub data_stores: &'a DataStores,
68
pub surfaces: &'a [SurfaceInfo],
69
pub scratch: &'a PrimitiveScratchBuffer,
70
pub screen_world_rect: WorldRect,
71
pub globals: &'a FrameGlobalResources,
72
}
73
74
/// Represents a number of rendering operations on a surface.
75
///
76
/// In graphics parlance, a "render target" usually means "a surface (texture or
77
/// framebuffer) bound to the output of a shader". This trait has a slightly
78
/// different meaning, in that it represents the operations on that surface
79
/// _before_ it's actually bound and rendered. So a `RenderTarget` is built by
80
/// the `RenderBackend` by inserting tasks, and then shipped over to the
81
/// `Renderer` where a device surface is resolved and the tasks are transformed
82
/// into draw commands on that surface.
83
///
84
/// We express this as a trait to generalize over color and alpha surfaces.
85
/// a given `RenderTask` will draw to one or the other, depending on its type
86
/// and sometimes on its parameters. See `RenderTask::target_kind`.
87
pub trait RenderTarget {
88
/// Creates a new RenderTarget of the given type.
89
fn new(
90
screen_size: DeviceIntSize,
91
gpu_supports_fast_clears: bool,
92
) -> Self;
93
94
/// Optional hook to provide additional processing for the target at the
95
/// end of the build phase.
96
fn build(
97
&mut self,
98
_ctx: &mut RenderTargetContext,
99
_gpu_cache: &mut GpuCache,
100
_render_tasks: &mut RenderTaskGraph,
101
_deferred_resolves: &mut Vec<DeferredResolve>,
102
_prim_headers: &mut PrimitiveHeaders,
103
_transforms: &mut TransformPalette,
104
_z_generator: &mut ZBufferIdGenerator,
105
_composite_state: &mut CompositeState,
106
) {
107
}
108
109
/// Associates a `RenderTask` with this target. That task must be assigned
110
/// to a region returned by invoking `allocate()` on this target.
111
///
112
/// TODO(gw): It's a bit odd that we need the deferred resolves and mutable
113
/// GPU cache here. They are typically used by the build step above. They
114
/// are used for the blit jobs to allow resolve_image to be called. It's a
115
/// bit of extra overhead to store the image key here and the resolve them
116
/// in the build step separately. BUT: if/when we add more texture cache
117
/// target jobs, we might want to tidy this up.
118
fn add_task(
119
&mut self,
120
task_id: RenderTaskId,
121
ctx: &RenderTargetContext,
122
gpu_cache: &mut GpuCache,
123
render_tasks: &RenderTaskGraph,
124
clip_store: &ClipStore,
125
transforms: &mut TransformPalette,
126
deferred_resolves: &mut Vec<DeferredResolve>,
127
);
128
129
fn needs_depth(&self) -> bool;
130
131
fn used_rect(&self) -> DeviceIntRect;
132
fn add_used(&mut self, rect: DeviceIntRect);
133
}
134
135
/// A series of `RenderTarget` instances, serving as the high-level container
136
/// into which `RenderTasks` are assigned.
137
///
138
/// During the build phase, we iterate over the tasks in each `RenderPass`. For
139
/// each task, we invoke `allocate()` on the `RenderTargetList`, which in turn
140
/// attempts to allocate an output region in the last `RenderTarget` in the
141
/// list. If allocation fails (or if the list is empty), a new `RenderTarget` is
142
/// created and appended to the list. The build phase then assign the task into
143
/// the target associated with the final allocation.
144
///
145
/// The result is that each `RenderPass` is associated with one or two
146
/// `RenderTargetLists`, depending on whether we have all our tasks have the
147
/// same `RenderTargetKind`. The lists are then shipped to the `Renderer`, which
148
/// allocates a device texture array, with one slice per render target in the
149
/// list.
150
///
151
/// The upshot of this scheme is that it maximizes batching. In a given pass,
152
/// we need to do a separate batch for each individual render target. But with
153
/// the texture array, we can expose the entirety of the previous pass to each
154
/// task in the current pass in a single batch, which generally allows each
155
/// task to be drawn in a single batch regardless of how many results from the
156
/// previous pass it depends on.
157
///
158
/// Note that in some cases (like drop-shadows), we can depend on the output of
159
/// a pass earlier than the immediately-preceding pass. See `SavedTargetIndex`.
160
#[cfg_attr(feature = "capture", derive(Serialize))]
161
#[cfg_attr(feature = "replay", derive(Deserialize))]
162
pub struct RenderTargetList<T> {
163
screen_size: DeviceIntSize,
164
pub format: ImageFormat,
165
/// The maximum width and height of any single primitive we've encountered
166
/// that will be drawn to a dynamic location.
167
///
168
/// We initially create our per-slice allocators with a width and height of
169
/// IDEAL_MAX_TEXTURE_DIMENSION. If we encounter a larger primitive, the
170
/// allocation will fail, but we'll bump max_dynamic_size, which will cause the
171
/// allocator for the next slice to be just large enough to accomodate it.
172
pub max_dynamic_size: DeviceIntSize,
173
pub targets: Vec<T>,
174
pub saved_index: Option<SavedTargetIndex>,
175
pub alloc_tracker: ArrayAllocationTracker,
176
gpu_supports_fast_clears: bool,
177
}
178
179
impl<T: RenderTarget> RenderTargetList<T> {
180
pub fn new(
181
screen_size: DeviceIntSize,
182
format: ImageFormat,
183
gpu_supports_fast_clears: bool,
184
) -> Self {
185
RenderTargetList {
186
screen_size,
187
format,
188
max_dynamic_size: DeviceIntSize::new(0, 0),
189
targets: Vec::new(),
190
saved_index: None,
191
alloc_tracker: ArrayAllocationTracker::new(),
192
gpu_supports_fast_clears,
193
}
194
}
195
196
pub fn build(
197
&mut self,
198
ctx: &mut RenderTargetContext,
199
gpu_cache: &mut GpuCache,
200
render_tasks: &mut RenderTaskGraph,
201
deferred_resolves: &mut Vec<DeferredResolve>,
202
saved_index: Option<SavedTargetIndex>,
203
prim_headers: &mut PrimitiveHeaders,
204
transforms: &mut TransformPalette,
205
z_generator: &mut ZBufferIdGenerator,
206
composite_state: &mut CompositeState,
207
) {
208
debug_assert_eq!(None, self.saved_index);
209
self.saved_index = saved_index;
210
211
for target in &mut self.targets {
212
target.build(
213
ctx,
214
gpu_cache,
215
render_tasks,
216
deferred_resolves,
217
prim_headers,
218
transforms,
219
z_generator,
220
composite_state,
221
);
222
}
223
}
224
225
pub fn allocate(
226
&mut self,
227
alloc_size: DeviceIntSize,
228
) -> (RenderTargetIndex, DeviceIntPoint) {
229
let (free_rect_slice, origin) = match self.alloc_tracker.allocate(&alloc_size) {
230
Some(allocation) => allocation,
231
None => {
232
// Have the allocator restrict slice sizes to our max ideal
233
// dimensions, unless we've already gone bigger on a previous
234
// slice.
235
let rounded_dimensions = DeviceIntSize::new(
236
(self.max_dynamic_size.width + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
237
(self.max_dynamic_size.height + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK,
238
);
239
let allocator_dimensions = DeviceIntSize::new(
240
cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, rounded_dimensions.width),
241
cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, rounded_dimensions.height),
242
);
243
244
assert!(alloc_size.width <= allocator_dimensions.width &&
245
alloc_size.height <= allocator_dimensions.height);
246
let slice = FreeRectSlice(self.targets.len() as u32);
247
self.targets.push(T::new(self.screen_size, self.gpu_supports_fast_clears));
248
249
self.alloc_tracker.extend(
250
slice,
251
allocator_dimensions,
252
alloc_size,
253
);
254
255
(slice, DeviceIntPoint::zero())
256
}
257
};
258
259
if alloc_size.is_empty_or_negative() && self.targets.is_empty() {
260
// push an unused target here, only if we don't have any
261
self.targets.push(T::new(self.screen_size, self.gpu_supports_fast_clears));
262
}
263
264
self.targets[free_rect_slice.0 as usize]
265
.add_used(DeviceIntRect::new(origin, alloc_size));
266
267
(RenderTargetIndex(free_rect_slice.0 as usize), origin)
268
}
269
270
pub fn needs_depth(&self) -> bool {
271
self.targets.iter().any(|target| target.needs_depth())
272
}
273
274
pub fn check_ready(&self, t: &Texture) {
275
let dimensions = t.get_dimensions();
276
assert!(dimensions.width >= self.max_dynamic_size.width);
277
assert!(dimensions.height >= self.max_dynamic_size.height);
278
assert_eq!(t.get_format(), self.format);
279
assert_eq!(t.get_layer_count() as usize, self.targets.len());
280
assert!(t.supports_depth() >= self.needs_depth());
281
}
282
}
283
284
285
/// Contains the work (in the form of instance arrays) needed to fill a color
286
/// color output surface (RGBA8).
287
///
288
/// See `RenderTarget`.
289
#[cfg_attr(feature = "capture", derive(Serialize))]
290
#[cfg_attr(feature = "replay", derive(Deserialize))]
291
pub struct ColorRenderTarget {
292
pub alpha_batch_containers: Vec<AlphaBatchContainer>,
293
// List of blur operations to apply for this render target.
294
pub vertical_blurs: Vec<BlurInstance>,
295
pub horizontal_blurs: Vec<BlurInstance>,
296
pub readbacks: Vec<DeviceIntRect>,
297
pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>,
298
pub svg_filters: Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
299
pub blits: Vec<BlitJob>,
300
// List of frame buffer outputs for this render target.
301
pub outputs: Vec<FrameOutput>,
302
alpha_tasks: Vec<RenderTaskId>,
303
screen_size: DeviceIntSize,
304
// Track the used rect of the render target, so that
305
// we can set a scissor rect and only clear to the
306
// used portion of the target as an optimization.
307
pub used_rect: DeviceIntRect,
308
}
309
310
impl RenderTarget for ColorRenderTarget {
311
fn new(
312
screen_size: DeviceIntSize,
313
_: bool,
314
) -> Self {
315
ColorRenderTarget {
316
alpha_batch_containers: Vec::new(),
317
vertical_blurs: Vec::new(),
318
horizontal_blurs: Vec::new(),
319
readbacks: Vec::new(),
320
scalings: FastHashMap::default(),
321
svg_filters: Vec::new(),
322
blits: Vec::new(),
323
outputs: Vec::new(),
324
alpha_tasks: Vec::new(),
325
screen_size,
326
used_rect: DeviceIntRect::zero(),
327
}
328
}
329
330
fn build(
331
&mut self,
332
ctx: &mut RenderTargetContext,
333
gpu_cache: &mut GpuCache,
334
render_tasks: &mut RenderTaskGraph,
335
deferred_resolves: &mut Vec<DeferredResolve>,
336
prim_headers: &mut PrimitiveHeaders,
337
transforms: &mut TransformPalette,
338
z_generator: &mut ZBufferIdGenerator,
339
composite_state: &mut CompositeState,
340
) {
341
let mut merged_batches = AlphaBatchContainer::new(None);
342
343
for task_id in &self.alpha_tasks {
344
let task = &render_tasks[*task_id];
345
346
match task.clear_mode {
347
ClearMode::One |
348
ClearMode::Zero => {
349
panic!("bug: invalid clear mode for color task");
350
}
351
ClearMode::DontCare |
352
ClearMode::Transparent => {}
353
}
354
355
match task.kind {
356
RenderTaskKind::Picture(ref pic_task) => {
357
let pic = &ctx.prim_store.pictures[pic_task.pic_index.0];
358
359
let raster_spatial_node_index = match pic.raster_config {
360
Some(ref raster_config) => {
361
let surface = &ctx.surfaces[raster_config.surface_index.0];
362
surface.raster_spatial_node_index
363
}
364
None => {
365
// This must be the main framebuffer
366
ROOT_SPATIAL_NODE_INDEX
367
}
368
};
369
370
let (target_rect, _) = task.get_target_rect();
371
372
let scissor_rect = if pic_task.can_merge {
373
None
374
} else {
375
Some(target_rect)
376
};
377
378
// TODO(gw): The type names of AlphaBatchBuilder and BatchBuilder
379
// are still confusing. Once more of the picture caching
380
// improvement code lands, the AlphaBatchBuilder and
381
// AlphaBatchList types will be collapsed into one, which
382
// should simplify coming up with better type names.
383
let alpha_batch_builder = AlphaBatchBuilder::new(
384
self.screen_size,
385
ctx.break_advanced_blend_batches,
386
ctx.batch_lookback_count,
387
*task_id,
388
render_tasks.get_task_address(*task_id),
389
PrimitiveVisibilityMask::all(),
390
);
391
392
let mut batch_builder = BatchBuilder::new(
393
vec![alpha_batch_builder],
394
);
395
396
batch_builder.add_pic_to_batch(
397
pic,
398
ctx,
399
gpu_cache,
400
render_tasks,
401
deferred_resolves,
402
prim_headers,
403
transforms,
404
raster_spatial_node_index,
405
pic_task.surface_spatial_node_index,
406
z_generator,
407
composite_state,
408
);
409
410
let alpha_batch_builders = batch_builder.finalize();
411
412
for batcher in alpha_batch_builders {
413
batcher.build(
414
&mut self.alpha_batch_containers,
415
&mut merged_batches,
416
target_rect,
417
scissor_rect,
418
);
419
}
420
}
421
_ => {
422
unreachable!();
423
}
424
}
425
}
426
427
if !merged_batches.is_empty() {
428
self.alpha_batch_containers.push(merged_batches);
429
}
430
}
431
432
fn add_task(
433
&mut self,
434
task_id: RenderTaskId,
435
ctx: &RenderTargetContext,
436
gpu_cache: &mut GpuCache,
437
render_tasks: &RenderTaskGraph,
438
_: &ClipStore,
439
_: &mut TransformPalette,
440
deferred_resolves: &mut Vec<DeferredResolve>,
441
) {
442
let task = &render_tasks[task_id];
443
444
match task.kind {
445
RenderTaskKind::VerticalBlur(..) => {
446
add_blur_instances(
447
&mut self.vertical_blurs,
448
BlurDirection::Vertical,
449
render_tasks.get_task_address(task_id),
450
render_tasks.get_task_address(task.children[0]),
451
);
452
}
453
RenderTaskKind::HorizontalBlur(..) => {
454
add_blur_instances(
455
&mut self.horizontal_blurs,
456
BlurDirection::Horizontal,
457
render_tasks.get_task_address(task_id),
458
render_tasks.get_task_address(task.children[0]),
459
);
460
}
461
RenderTaskKind::Picture(ref task_info) => {
462
let pic = &ctx.prim_store.pictures[task_info.pic_index.0];
463
self.alpha_tasks.push(task_id);
464
465
// If this pipeline is registered as a frame output
466
// store the information necessary to do the copy.
467
if let Some(pipeline_id) = pic.frame_output_pipeline_id {
468
self.outputs.push(FrameOutput {
469
pipeline_id,
470
task_id,
471
});
472
}
473
}
474
RenderTaskKind::SvgFilter(ref task_info) => {
475
add_svg_filter_instances(
476
&mut self.svg_filters,
477
render_tasks,
478
&task_info.info,
479
task_id,
480
task.children.get(0).cloned(),
481
task.children.get(1).cloned(),
482
task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)),
483
)
484
}
485
RenderTaskKind::ClipRegion(..) |
486
RenderTaskKind::Border(..) |
487
RenderTaskKind::CacheMask(..) |
488
RenderTaskKind::Gradient(..) |
489
RenderTaskKind::LineDecoration(..) => {
490
panic!("Should not be added to color target!");
491
}
492
RenderTaskKind::Readback(device_rect) => {
493
self.readbacks.push(device_rect);
494
}
495
RenderTaskKind::Scaling(ref info) => {
496
add_scaling_instances(
497
info,
498
&mut self.scalings,
499
task,
500
task.children.first().map(|&child| &render_tasks[child]),
501
ctx.resource_cache,
502
gpu_cache,
503
deferred_resolves,
504
);
505
}
506
RenderTaskKind::Blit(ref task_info) => {
507
let source = match task_info.source {
508
BlitSource::Image { key } => {
509
// Get the cache item for the source texture.
510
let cache_item = resolve_image(
511
key.request,
512
ctx.resource_cache,
513
gpu_cache,
514
deferred_resolves,
515
);
516
517
// Work out a source rect to copy from the texture, depending on whether
518
// a sub-rect is present or not.
519
let source_rect = key.texel_rect.map_or(cache_item.uv_rect.to_i32(), |sub_rect| {
520
DeviceIntRect::new(
521
DeviceIntPoint::new(
522
cache_item.uv_rect.origin.x as i32 + sub_rect.origin.x,
523
cache_item.uv_rect.origin.y as i32 + sub_rect.origin.y,
524
),
525
sub_rect.size,
526
)
527
});
528
529
// Store the blit job for the renderer to execute, including
530
// the allocated destination rect within this target.
531
BlitJobSource::Texture(
532
cache_item.texture_id,
533
cache_item.texture_layer,
534
source_rect,
535
)
536
}
537
BlitSource::RenderTask { task_id } => {
538
BlitJobSource::RenderTask(task_id)
539
}
540
};
541
542
let target_rect = task
543
.get_target_rect()
544
.0
545
.inner_rect(task_info.padding);
546
self.blits.push(BlitJob {
547
source,
548
target_rect,
549
});
550
}
551
#[cfg(test)]
552
RenderTaskKind::Test(..) => {}
553
}
554
}
555
556
fn needs_depth(&self) -> bool {
557
self.alpha_batch_containers.iter().any(|ab| {
558
!ab.opaque_batches.is_empty()
559
})
560
}
561
562
fn used_rect(&self) -> DeviceIntRect {
563
self.used_rect
564
}
565
566
fn add_used(&mut self, rect: DeviceIntRect) {
567
self.used_rect = self.used_rect.union(&rect);
568
}
569
}
570
571
/// Contains the work (in the form of instance arrays) needed to fill an alpha
572
/// output surface (R8).
573
///
574
/// See `RenderTarget`.
575
#[cfg_attr(feature = "capture", derive(Serialize))]
576
#[cfg_attr(feature = "replay", derive(Deserialize))]
577
pub struct AlphaRenderTarget {
578
pub clip_batcher: ClipBatcher,
579
// List of blur operations to apply for this render target.
580
pub vertical_blurs: Vec<BlurInstance>,
581
pub horizontal_blurs: Vec<BlurInstance>,
582
pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>,
583
pub zero_clears: Vec<RenderTaskId>,
584
pub one_clears: Vec<RenderTaskId>,
585
// Track the used rect of the render target, so that
586
// we can set a scissor rect and only clear to the
587
// used portion of the target as an optimization.
588
pub used_rect: DeviceIntRect,
589
}
590
591
impl RenderTarget for AlphaRenderTarget {
592
fn new(
593
_: DeviceIntSize,
594
gpu_supports_fast_clears: bool,
595
) -> Self {
596
AlphaRenderTarget {
597
clip_batcher: ClipBatcher::new(gpu_supports_fast_clears),
598
vertical_blurs: Vec::new(),
599
horizontal_blurs: Vec::new(),
600
scalings: FastHashMap::default(),
601
zero_clears: Vec::new(),
602
one_clears: Vec::new(),
603
used_rect: DeviceIntRect::zero(),
604
}
605
}
606
607
fn add_task(
608
&mut self,
609
task_id: RenderTaskId,
610
ctx: &RenderTargetContext,
611
gpu_cache: &mut GpuCache,
612
render_tasks: &RenderTaskGraph,
613
clip_store: &ClipStore,
614
transforms: &mut TransformPalette,
615
deferred_resolves: &mut Vec<DeferredResolve>,
616
) {
617
let task = &render_tasks[task_id];
618
let (target_rect, _) = task.get_target_rect();
619
620
match task.clear_mode {
621
ClearMode::Zero => {
622
self.zero_clears.push(task_id);
623
}
624
ClearMode::One => {
625
self.one_clears.push(task_id);
626
}
627
ClearMode::DontCare => {}
628
ClearMode::Transparent => {
629
panic!("bug: invalid clear mode for alpha task");
630
}
631
}
632
633
match task.kind {
634
RenderTaskKind::Readback(..) |
635
RenderTaskKind::Picture(..) |
636
RenderTaskKind::Blit(..) |
637
RenderTaskKind::Border(..) |
638
RenderTaskKind::LineDecoration(..) |
639
RenderTaskKind::Gradient(..) |
640
RenderTaskKind::SvgFilter(..) => {
641
panic!("BUG: should not be added to alpha target!");
642
}
643
RenderTaskKind::VerticalBlur(..) => {
644
add_blur_instances(
645
&mut self.vertical_blurs,
646
BlurDirection::Vertical,
647
render_tasks.get_task_address(task_id),
648
render_tasks.get_task_address(task.children[0]),
649
);
650
}
651
RenderTaskKind::HorizontalBlur(..) => {
652
add_blur_instances(
653
&mut self.horizontal_blurs,
654
BlurDirection::Horizontal,
655
render_tasks.get_task_address(task_id),
656
render_tasks.get_task_address(task.children[0]),
657
);
658
}
659
RenderTaskKind::CacheMask(ref task_info) => {
660
self.clip_batcher.add(
661
task_info.clip_node_range,
662
task_info.root_spatial_node_index,
663
ctx.resource_cache,
664
gpu_cache,
665
clip_store,
666
ctx.spatial_tree,
667
transforms,
668
&ctx.data_stores.clip,
669
task_info.actual_rect,
670
&ctx.screen_world_rect,
671
task_info.device_pixel_scale,
672
target_rect.origin.to_f32(),
673
task_info.actual_rect.origin.to_f32(),
674
);
675
}
676
RenderTaskKind::ClipRegion(ref region_task) => {
677
let device_rect = DeviceRect::new(
678
DevicePoint::zero(),
679
target_rect.size.to_f32(),
680
);
681
self.clip_batcher.add_clip_region(
682
region_task.clip_data_address,
683
region_task.local_pos,
684
device_rect,
685
target_rect.origin.to_f32(),
686
DevicePoint::zero(),
687
region_task.device_pixel_scale.0,
688
);
689
}
690
RenderTaskKind::Scaling(ref info) => {
691
add_scaling_instances(
692
info,
693
&mut self.scalings,
694
task,
695
task.children.first().map(|&child| &render_tasks[child]),
696
ctx.resource_cache,
697
gpu_cache,
698
deferred_resolves,
699
);
700
}
701
#[cfg(test)]
702
RenderTaskKind::Test(..) => {}
703
}
704
}
705
706
fn needs_depth(&self) -> bool {
707
false
708
}
709
710
fn used_rect(&self) -> DeviceIntRect {
711
self.used_rect
712
}
713
714
fn add_used(&mut self, rect: DeviceIntRect) {
715
self.used_rect = self.used_rect.union(&rect);
716
}
717
}
718
719
#[cfg_attr(feature = "capture", derive(Serialize))]
720
#[cfg_attr(feature = "replay", derive(Deserialize))]
721
pub struct PictureCacheTarget {
722
pub surface: ResolvedSurfaceTexture,
723
pub alpha_batch_container: AlphaBatchContainer,
724
pub clear_color: Option<ColorF>,
725
pub dirty_rect: DeviceIntRect,
726
pub valid_rect: DeviceIntRect,
727
}
728
729
#[cfg_attr(feature = "capture", derive(Serialize))]
730
#[cfg_attr(feature = "replay", derive(Deserialize))]
731
pub struct TextureCacheRenderTarget {
732
pub target_kind: RenderTargetKind,
733
pub horizontal_blurs: Vec<BlurInstance>,
734
pub blits: Vec<BlitJob>,
735
pub border_segments_complex: Vec<BorderInstance>,
736
pub border_segments_solid: Vec<BorderInstance>,
737
pub clears: Vec<DeviceIntRect>,
738
pub line_decorations: Vec<LineDecorationJob>,
739
pub gradients: Vec<GradientJob>,
740
}
741
742
impl TextureCacheRenderTarget {
743
pub fn new(target_kind: RenderTargetKind) -> Self {
744
TextureCacheRenderTarget {
745
target_kind,
746
horizontal_blurs: vec![],
747
blits: vec![],
748
border_segments_complex: vec![],
749
border_segments_solid: vec![],
750
clears: vec![],
751
line_decorations: vec![],
752
gradients: vec![],
753
}
754
}
755
756
pub fn add_task(
757
&mut self,
758
task_id: RenderTaskId,
759
render_tasks: &mut RenderTaskGraph,
760
) {
761
let task_address = render_tasks.get_task_address(task_id);
762
let src_task_address = render_tasks[task_id].children.get(0).map(|src_task_id| {
763
render_tasks.get_task_address(*src_task_id)
764
});
765
766
let task = &mut render_tasks[task_id];
767
let target_rect = task.get_target_rect();
768
769
match task.kind {
770
RenderTaskKind::LineDecoration(ref info) => {
771
self.clears.push(target_rect.0);
772
773
self.line_decorations.push(LineDecorationJob {
774
task_rect: target_rect.0.to_f32(),
775
local_size: info.local_size,
776
style: info.style as i32,
777
axis_select: match info.orientation {
778
LineOrientation::Horizontal => 0.0,
779
LineOrientation::Vertical => 1.0,
780
},
781
wavy_line_thickness: info.wavy_line_thickness,
782
});
783
}
784
RenderTaskKind::HorizontalBlur(..) => {
785
add_blur_instances(
786
&mut self.horizontal_blurs,
787
BlurDirection::Horizontal,
788
task_address,
789
src_task_address.unwrap(),
790
);
791
}
792
RenderTaskKind::Blit(ref task_info) => {
793
match task_info.source {
794
BlitSource::Image { .. } => {
795
// reading/writing from the texture cache at the same time
796
// is undefined behavior.
797
panic!("bug: a single blit cannot be to/from texture cache");
798
}
799
BlitSource::RenderTask { task_id } => {
800
// Add a blit job to copy from an existing render
801
// task to this target.
802
self.blits.push(BlitJob {
803
source: BlitJobSource::RenderTask(task_id),
804
target_rect: target_rect.0.inner_rect(task_info.padding),
805
});
806
}
807
}
808
}
809
RenderTaskKind::Border(ref mut task_info) => {
810
self.clears.push(target_rect.0);
811
812
let task_origin = target_rect.0.origin.to_f32();
813
let instances = mem::replace(&mut task_info.instances, Vec::new());
814
for mut instance in instances {
815
// TODO(gw): It may be better to store the task origin in
816
// the render task data instead of per instance.
817
instance.task_origin = task_origin;
818
if instance.flags & STYLE_MASK == STYLE_SOLID {
819
self.border_segments_solid.push(instance);
820
} else {
821
self.border_segments_complex.push(instance);
822
}
823
}
824
}
825
RenderTaskKind::Gradient(ref task_info) => {
826
let mut stops = [0.0; 4];
827
let mut colors = [PremultipliedColorF::BLACK; 4];
828
829
let axis_select = match task_info.orientation {
830
LineOrientation::Horizontal => 0.0,
831
LineOrientation::Vertical => 1.0,
832
};
833
834
for (stop, (offset, color)) in task_info.stops.iter().zip(stops.iter_mut().zip(colors.iter_mut())) {
835
*offset = stop.offset;
836
*color = ColorF::from(stop.color).premultiplied();
837
}
838
839
self.gradients.push(GradientJob {
840
task_rect: target_rect.0.to_f32(),
841
axis_select,
842
stops,
843
colors,
844
start_stop: [task_info.start_point, task_info.end_point],
845
});
846
}
847
RenderTaskKind::VerticalBlur(..) |
848
RenderTaskKind::Picture(..) |
849
RenderTaskKind::ClipRegion(..) |
850
RenderTaskKind::CacheMask(..) |
851
RenderTaskKind::Readback(..) |
852
RenderTaskKind::Scaling(..) |
853
RenderTaskKind::SvgFilter(..) => {
854
panic!("BUG: unexpected task kind for texture cache target");
855
}
856
#[cfg(test)]
857
RenderTaskKind::Test(..) => {}
858
}
859
}
860
}
861
862
fn add_blur_instances(
863
instances: &mut Vec<BlurInstance>,
864
blur_direction: BlurDirection,
865
task_address: RenderTaskAddress,
866
src_task_address: RenderTaskAddress,
867
) {
868
let instance = BlurInstance {
869
task_address,
870
src_task_address,
871
blur_direction,
872
};
873
874
instances.push(instance);
875
}
876
877
fn add_scaling_instances(
878
task: &ScalingTask,
879
instances: &mut FastHashMap<TextureSource, Vec<ScalingInstance>>,
880
target_task: &RenderTask,
881
source_task: Option<&RenderTask>,
882
resource_cache: &ResourceCache,
883
gpu_cache: &mut GpuCache,
884
deferred_resolves: &mut Vec<DeferredResolve>,
885
) {
886
let target_rect = target_task
887
.get_target_rect()
888
.0
889
.inner_rect(task.padding)
890
.to_f32();
891
892
let (source, (source_rect, source_layer)) = match task.image {
893
Some(key) => {
894
assert!(source_task.is_none());
895
896
// Get the cache item for the source texture.
897
let cache_item = resolve_image(
898
key.request,
899
resource_cache,
900
gpu_cache,
901
deferred_resolves,
902
);
903
904
// Work out a source rect to copy from the texture, depending on whether
905
// a sub-rect is present or not.
906
let source_rect = key.texel_rect.map_or(cache_item.uv_rect, |sub_rect| {
907
DeviceIntRect::new(
908
DeviceIntPoint::new(
909
cache_item.uv_rect.origin.x + sub_rect.origin.x,
910
cache_item.uv_rect.origin.y + sub_rect.origin.y,
911
),
912
sub_rect.size,
913
)
914
});
915
916
(
917
cache_item.texture_id,
918
(source_rect, cache_item.texture_layer as LayerIndex),
919
)
920
}
921
None => {
922
(
923
match task.target_kind {
924
RenderTargetKind::Color => TextureSource::PrevPassColor,
925
RenderTargetKind::Alpha => TextureSource::PrevPassAlpha,
926
},
927
source_task.unwrap().location.to_source_rect(),
928
)
929
}
930
};
931
932
instances
933
.entry(source)
934
.or_insert(Vec::new())
935
.push(ScalingInstance {
936
target_rect,
937
source_rect,
938
source_layer: source_layer as i32,
939
});
940
}
941
942
fn add_svg_filter_instances(
943
instances: &mut Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
944
render_tasks: &RenderTaskGraph,
945
filter: &SvgFilterInfo,
946
task_id: RenderTaskId,
947
input_1_task: Option<RenderTaskId>,
948
input_2_task: Option<RenderTaskId>,
949
extra_data_address: Option<GpuCacheAddress>,
950
) {
951
let mut textures = BatchTextures::no_texture();
952
953
if let Some(saved_index) = input_1_task.map(|id| &render_tasks[id].saved_index) {
954
textures.colors[0] = match saved_index {
955
Some(saved_index) => TextureSource::RenderTaskCache(*saved_index, Swizzle::default()),
956
None => TextureSource::PrevPassColor,
957
};
958
}
959
960
if let Some(saved_index) = input_2_task.map(|id| &render_tasks[id].saved_index) {
961
textures.colors[1] = match saved_index {
962
Some(saved_index) => TextureSource::RenderTaskCache(*saved_index, Swizzle::default()),
963
None => TextureSource::PrevPassColor,
964
};
965
}
966
967
let kind = match filter {
968
SvgFilterInfo::Blend(..) => 0,
969
SvgFilterInfo::Flood(..) => 1,
970
SvgFilterInfo::LinearToSrgb => 2,
971
SvgFilterInfo::SrgbToLinear => 3,
972
SvgFilterInfo::Opacity(..) => 4,
973
SvgFilterInfo::ColorMatrix(..) => 5,
974
SvgFilterInfo::DropShadow(..) => 6,
975
SvgFilterInfo::Offset(..) => 7,
976
SvgFilterInfo::ComponentTransfer(..) => 8,
977
SvgFilterInfo::Identity => 9,
978
SvgFilterInfo::Composite(..) => 10,
979
};
980
981
let input_count = match filter {
982
SvgFilterInfo::Flood(..) => 0,
983
984
SvgFilterInfo::LinearToSrgb |
985
SvgFilterInfo::SrgbToLinear |
986
SvgFilterInfo::Opacity(..) |
987
SvgFilterInfo::ColorMatrix(..) |
988
SvgFilterInfo::Offset(..) |
989
SvgFilterInfo::ComponentTransfer(..) |
990
SvgFilterInfo::Identity => 1,
991
992
// Not techincally a 2 input filter, but we have 2 inputs here: original content & blurred content.
993
SvgFilterInfo::DropShadow(..) |
994
SvgFilterInfo::Blend(..) |
995
SvgFilterInfo::Composite(..) => 2,
996
};
997
998
let generic_int = match filter {
999
SvgFilterInfo::Blend(mode) => *mode as u16,
1000
SvgFilterInfo::ComponentTransfer(data) =>
1001
((data.r_func.to_int() << 12 |
1002
data.g_func.to_int() << 8 |
1003
data.b_func.to_int() << 4 |
1004
data.a_func.to_int()) as u16),
1005
SvgFilterInfo::Composite(operator) =>
1006
operator.as_int() as u16,
1007
SvgFilterInfo::LinearToSrgb |
1008
SvgFilterInfo::SrgbToLinear |
1009
SvgFilterInfo::Flood(..) |
1010
SvgFilterInfo::Opacity(..) |
1011
SvgFilterInfo::ColorMatrix(..) |
1012
SvgFilterInfo::DropShadow(..) |
1013
SvgFilterInfo::Offset(..) |
1014
SvgFilterInfo::Identity => 0,
1015
};
1016
1017
let instance = SvgFilterInstance {
1018
task_address: render_tasks.get_task_address(task_id),
1019
input_1_task_address: input_1_task.map(|id| render_tasks.get_task_address(id)).unwrap_or(RenderTaskAddress(0)),
1020
input_2_task_address: input_2_task.map(|id| render_tasks.get_task_address(id)).unwrap_or(RenderTaskAddress(0)),
1021
kind,
1022
input_count,
1023
generic_int,
1024
extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID),
1025
};
1026
1027
for (ref mut batch_textures, ref mut batch) in instances.iter_mut() {
1028
if let Some(combined_textures) = batch_textures.combine_textures(textures) {
1029
batch.push(instance);
1030
// Update the batch textures to the newly combined batch textures
1031
*batch_textures = combined_textures;
1032
return;
1033
}
1034
}
1035
1036
instances.push((textures, vec![instance]));
1037
}
1038
1039
// Defines where the source data for a blit job can be found.
1040
#[cfg_attr(feature = "capture", derive(Serialize))]
1041
#[cfg_attr(feature = "replay", derive(Deserialize))]
1042
pub enum BlitJobSource {
1043
Texture(TextureSource, i32, DeviceIntRect),
1044
RenderTask(RenderTaskId),
1045
}
1046
1047
// Information required to do a blit from a source to a target.
1048
#[cfg_attr(feature = "capture", derive(Serialize))]
1049
#[cfg_attr(feature = "replay", derive(Deserialize))]
1050
pub struct BlitJob {
1051
pub source: BlitJobSource,
1052
pub target_rect: DeviceIntRect,
1053
}
1054
1055
#[cfg_attr(feature = "capture", derive(Serialize))]
1056
#[cfg_attr(feature = "replay", derive(Deserialize))]
1057
#[derive(Debug)]
1058
pub struct LineDecorationJob {
1059
pub task_rect: DeviceRect,
1060
pub local_size: LayoutSize,
1061
pub wavy_line_thickness: f32,
1062
pub style: i32,
1063
pub axis_select: f32,
1064
}
1065
1066
#[cfg_attr(feature = "capture", derive(Serialize))]
1067
#[cfg_attr(feature = "replay", derive(Deserialize))]
1068
#[repr(C)]
1069
pub struct GradientJob {
1070
pub task_rect: DeviceRect,
1071
pub stops: [f32; GRADIENT_FP_STOPS],
1072
pub colors: [PremultipliedColorF; GRADIENT_FP_STOPS],
1073
pub axis_select: f32,
1074
pub start_stop: [f32; 2],
1075
}
1076
1077
/// Frame output information for a given pipeline ID.
1078
/// Storing the task ID allows the renderer to find
1079
/// the target rect within the render target that this
1080
/// pipeline exists at.
1081
#[cfg_attr(feature = "capture", derive(Serialize))]
1082
#[cfg_attr(feature = "replay", derive(Deserialize))]
1083
pub struct FrameOutput {
1084
pub task_id: RenderTaskId,
1085
pub pipeline_id: PipelineId,
1086
}