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::{CompositeOperator, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
6
use api::{LineStyle, LineOrientation, ClipMode, MixBlendMode, ColorF, ColorSpace};
7
use api::units::*;
8
use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange, ClipNodeFlags};
9
use crate::spatial_tree::SpatialNodeIndex;
10
use crate::filterdata::SFilterData;
11
use crate::frame_builder::FrameBuilderConfig;
12
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
13
use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind};
14
use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex};
15
use crate::picture::ResolvedSurfaceTexture;
16
use crate::prim_store::{PictureIndex, PrimitiveVisibilityMask};
17
use crate::prim_store::image::ImageCacheKey;
18
use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientStopKey};
19
#[cfg(feature = "debugger")]
20
use crate::print_tree::{PrintTreePrinter};
21
use crate::resource_cache::ResourceCache;
22
use std::{usize, f32, i32, u32};
23
use crate::render_target::{RenderTargetIndex, RenderTargetKind};
24
use crate::render_task_graph::{RenderTaskGraph, RenderTaskId};
25
use crate::render_task_cache::{RenderTaskCacheKey, RenderTaskCacheKeyKind};
26
use smallvec::SmallVec;
27
28
const RENDER_TASK_SIZE_SANITY_CHECK: i32 = 16000;
29
const FLOATS_PER_RENDER_TASK_INFO: usize = 8;
30
pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
31
pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8;
32
33
fn render_task_sanity_check(size: &DeviceIntSize) {
34
if size.width > RENDER_TASK_SIZE_SANITY_CHECK ||
35
size.height > RENDER_TASK_SIZE_SANITY_CHECK {
36
error!("Attempting to create a render task of size {}x{}", size.width, size.height);
37
panic!();
38
}
39
}
40
41
#[derive(Debug, Copy, Clone, PartialEq)]
42
#[repr(C)]
43
#[cfg_attr(feature = "capture", derive(Serialize))]
44
#[cfg_attr(feature = "replay", derive(Deserialize))]
45
pub struct RenderTaskAddress(pub u16);
46
47
/// Identifies the output buffer location for a given `RenderTask`.
48
#[derive(Clone, Debug)]
49
#[cfg_attr(feature = "capture", derive(Serialize))]
50
#[cfg_attr(feature = "replay", derive(Deserialize))]
51
pub enum RenderTaskLocation {
52
/// The `RenderTask` should be drawn to a fixed region in a specific render
53
/// target. This is used for the root `RenderTask`, where the main
54
/// framebuffer is used as the render target.
55
Fixed(DeviceIntRect),
56
/// The `RenderTask` should be drawn to a target provided by the atlas
57
/// allocator. This is the most common case.
58
///
59
/// The second member specifies the width and height of the task
60
/// output, and the first member is initially left as `None`. During the
61
/// build phase, we invoke `RenderTargetList::alloc()` and store the
62
/// resulting location in the first member. That location identifies the
63
/// render target and the offset of the allocated region within that target.
64
Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
65
/// The output of the `RenderTask` will be persisted beyond this frame, and
66
/// thus should be drawn into the `TextureCache`.
67
TextureCache {
68
/// Which texture in the texture cache should be drawn into.
69
texture: CacheTextureId,
70
/// The target layer in the above texture.
71
layer: LayerIndex,
72
/// The target region within the above layer.
73
rect: DeviceIntRect,
74
75
},
76
/// This render task will be drawn to a picture cache texture that is
77
/// persisted between both frames and scenes, if the content remains valid.
78
PictureCache {
79
/// Describes either a WR texture or a native OS compositor target
80
surface: ResolvedSurfaceTexture,
81
/// Size in device pixels of this picture cache tile.
82
size: DeviceIntSize,
83
},
84
}
85
86
impl RenderTaskLocation {
87
/// Returns true if this is a dynamic location.
88
pub fn is_dynamic(&self) -> bool {
89
match *self {
90
RenderTaskLocation::Dynamic(..) => true,
91
_ => false,
92
}
93
}
94
95
pub fn size(&self) -> DeviceIntSize {
96
match self {
97
RenderTaskLocation::Fixed(rect) => rect.size,
98
RenderTaskLocation::Dynamic(_, size) => *size,
99
RenderTaskLocation::TextureCache { rect, .. } => rect.size,
100
RenderTaskLocation::PictureCache { size, .. } => *size,
101
}
102
}
103
104
pub fn to_source_rect(&self) -> (DeviceIntRect, LayerIndex) {
105
match *self {
106
RenderTaskLocation::Fixed(rect) => (rect, 0),
107
RenderTaskLocation::Dynamic(None, _) => panic!("Expected position to be set for the task!"),
108
RenderTaskLocation::Dynamic(Some((origin, layer)), size) => (DeviceIntRect::new(origin, size), layer.0 as LayerIndex),
109
RenderTaskLocation::TextureCache { rect, layer, .. } => (rect, layer),
110
RenderTaskLocation::PictureCache { .. } => {
111
panic!("bug: picture cache tasks should never be a source!");
112
}
113
}
114
}
115
}
116
117
#[derive(Debug)]
118
#[cfg_attr(feature = "capture", derive(Serialize))]
119
#[cfg_attr(feature = "replay", derive(Deserialize))]
120
pub struct CacheMaskTask {
121
pub actual_rect: DeviceIntRect,
122
pub root_spatial_node_index: SpatialNodeIndex,
123
pub clip_node_range: ClipNodeRange,
124
pub device_pixel_scale: DevicePixelScale,
125
}
126
127
#[derive(Debug)]
128
#[cfg_attr(feature = "capture", derive(Serialize))]
129
#[cfg_attr(feature = "replay", derive(Deserialize))]
130
pub struct ClipRegionTask {
131
pub clip_data_address: GpuCacheAddress,
132
pub local_pos: LayoutPoint,
133
pub device_pixel_scale: DevicePixelScale,
134
}
135
136
#[cfg_attr(feature = "capture", derive(Serialize))]
137
#[cfg_attr(feature = "replay", derive(Deserialize))]
138
pub struct PictureTask {
139
pub pic_index: PictureIndex,
140
pub can_merge: bool,
141
pub content_origin: DeviceIntPoint,
142
pub uv_rect_handle: GpuCacheHandle,
143
pub surface_spatial_node_index: SpatialNodeIndex,
144
uv_rect_kind: UvRectKind,
145
pub device_pixel_scale: DevicePixelScale,
146
/// A bitfield that describes which dirty regions should be included
147
/// in batches built for this picture task.
148
pub vis_mask: PrimitiveVisibilityMask,
149
pub scissor_rect: Option<DeviceIntRect>,
150
pub valid_rect: Option<DeviceIntRect>,
151
}
152
153
#[derive(Debug)]
154
#[cfg_attr(feature = "capture", derive(Serialize))]
155
#[cfg_attr(feature = "replay", derive(Deserialize))]
156
pub struct BlurTask {
157
pub blur_std_deviation: f32,
158
pub target_kind: RenderTargetKind,
159
pub uv_rect_handle: GpuCacheHandle,
160
pub blur_region: DeviceIntSize,
161
uv_rect_kind: UvRectKind,
162
}
163
164
impl BlurTask {
165
#[cfg(feature = "debugger")]
166
fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
167
pt.add_item(format!("std deviation: {}", self.blur_std_deviation));
168
pt.add_item(format!("target: {:?}", self.target_kind));
169
}
170
}
171
172
#[derive(Debug)]
173
#[cfg_attr(feature = "capture", derive(Serialize))]
174
#[cfg_attr(feature = "replay", derive(Deserialize))]
175
pub struct ScalingTask {
176
pub target_kind: RenderTargetKind,
177
pub image: Option<ImageCacheKey>,
178
uv_rect_kind: UvRectKind,
179
pub padding: DeviceIntSideOffsets,
180
}
181
182
// Where the source data for a blit task can be found.
183
#[derive(Debug)]
184
#[cfg_attr(feature = "capture", derive(Serialize))]
185
#[cfg_attr(feature = "replay", derive(Deserialize))]
186
pub enum BlitSource {
187
Image {
188
key: ImageCacheKey,
189
},
190
RenderTask {
191
task_id: RenderTaskId,
192
},
193
}
194
195
#[derive(Debug)]
196
#[cfg_attr(feature = "capture", derive(Serialize))]
197
#[cfg_attr(feature = "replay", derive(Deserialize))]
198
pub struct BorderTask {
199
pub instances: Vec<BorderInstance>,
200
}
201
202
#[derive(Debug)]
203
#[cfg_attr(feature = "capture", derive(Serialize))]
204
#[cfg_attr(feature = "replay", derive(Deserialize))]
205
pub struct BlitTask {
206
pub source: BlitSource,
207
pub padding: DeviceIntSideOffsets,
208
}
209
210
#[derive(Debug)]
211
#[cfg_attr(feature = "capture", derive(Serialize))]
212
#[cfg_attr(feature = "replay", derive(Deserialize))]
213
pub struct GradientTask {
214
pub stops: [GradientStopKey; GRADIENT_FP_STOPS],
215
pub orientation: LineOrientation,
216
pub start_point: f32,
217
pub end_point: f32,
218
}
219
220
#[derive(Debug)]
221
#[cfg_attr(feature = "capture", derive(Serialize))]
222
#[cfg_attr(feature = "replay", derive(Deserialize))]
223
pub struct LineDecorationTask {
224
pub wavy_line_thickness: f32,
225
pub style: LineStyle,
226
pub orientation: LineOrientation,
227
pub local_size: LayoutSize,
228
}
229
230
#[derive(Debug)]
231
#[cfg_attr(feature = "capture", derive(Serialize))]
232
#[cfg_attr(feature = "replay", derive(Deserialize))]
233
pub enum SvgFilterInfo {
234
Blend(MixBlendMode),
235
Flood(ColorF),
236
LinearToSrgb,
237
SrgbToLinear,
238
Opacity(f32),
239
ColorMatrix(Box<[f32; 20]>),
240
DropShadow(ColorF),
241
Offset(DeviceVector2D),
242
ComponentTransfer(SFilterData),
243
Composite(CompositeOperator),
244
// TODO: This is used as a hack to ensure that a blur task's input is always in the blur's previous pass.
245
Identity,
246
}
247
248
#[derive(Debug)]
249
#[cfg_attr(feature = "capture", derive(Serialize))]
250
#[cfg_attr(feature = "replay", derive(Deserialize))]
251
pub struct SvgFilterTask {
252
pub info: SvgFilterInfo,
253
pub extra_gpu_cache_handle: Option<GpuCacheHandle>,
254
pub uv_rect_handle: GpuCacheHandle,
255
uv_rect_kind: UvRectKind,
256
}
257
258
#[derive(Debug)]
259
#[cfg_attr(feature = "capture", derive(Serialize))]
260
#[cfg_attr(feature = "replay", derive(Deserialize))]
261
pub struct RenderTaskData {
262
pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
263
}
264
265
#[cfg_attr(feature = "capture", derive(Serialize))]
266
#[cfg_attr(feature = "replay", derive(Deserialize))]
267
pub enum RenderTaskKind {
268
Picture(PictureTask),
269
CacheMask(CacheMaskTask),
270
ClipRegion(ClipRegionTask),
271
VerticalBlur(BlurTask),
272
HorizontalBlur(BlurTask),
273
Readback(DeviceIntRect),
274
Scaling(ScalingTask),
275
Blit(BlitTask),
276
Border(BorderTask),
277
LineDecoration(LineDecorationTask),
278
Gradient(GradientTask),
279
SvgFilter(SvgFilterTask),
280
#[cfg(test)]
281
Test(RenderTargetKind),
282
}
283
284
impl RenderTaskKind {
285
pub fn as_str(&self) -> &'static str {
286
match *self {
287
RenderTaskKind::Picture(..) => "Picture",
288
RenderTaskKind::CacheMask(..) => "CacheMask",
289
RenderTaskKind::ClipRegion(..) => "ClipRegion",
290
RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
291
RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
292
RenderTaskKind::Readback(..) => "Readback",
293
RenderTaskKind::Scaling(..) => "Scaling",
294
RenderTaskKind::Blit(..) => "Blit",
295
RenderTaskKind::Border(..) => "Border",
296
RenderTaskKind::LineDecoration(..) => "LineDecoration",
297
RenderTaskKind::Gradient(..) => "Gradient",
298
RenderTaskKind::SvgFilter(..) => "SvgFilter",
299
#[cfg(test)]
300
RenderTaskKind::Test(..) => "Test",
301
}
302
}
303
}
304
305
#[derive(Debug, Copy, Clone, PartialEq)]
306
#[cfg_attr(feature = "capture", derive(Serialize))]
307
#[cfg_attr(feature = "replay", derive(Deserialize))]
308
pub enum ClearMode {
309
// Applicable to color and alpha targets.
310
Zero,
311
One,
312
/// This task doesn't care what it is cleared to - it will completely overwrite it.
313
DontCare,
314
315
// Applicable to color targets only.
316
Transparent,
317
}
318
319
/// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs,
320
/// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive
321
/// in a single frame.
322
pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>;
323
324
/// Since we only use it within a single primitive, the key only needs to contain the down-scaling level
325
/// and the blur std deviation.
326
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
327
pub enum BlurTaskKey {
328
DownScale(u32),
329
Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 },
330
}
331
332
impl BlurTaskKey {
333
fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self {
334
// Quantise the std deviations and store it as integers to work around
335
// Eq and Hash's f32 allergy.
336
// The blur radius is rounded before RenderTask::new_blur so we don't need
337
// a lot of precision.
338
const QUANTIZATION_FACTOR: f32 = 1024.0;
339
let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32;
340
let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32;
341
BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y }
342
}
343
}
344
345
// The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that
346
// typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements
347
// avoids many tiny heap allocations in pages with a lot of text shadows and other
348
// types of render tasks.
349
pub type TaskDependencies = SmallVec<[RenderTaskId;2]>;
350
351
#[cfg_attr(feature = "capture", derive(Serialize))]
352
#[cfg_attr(feature = "replay", derive(Deserialize))]
353
pub struct RenderTask {
354
pub location: RenderTaskLocation,
355
pub children: TaskDependencies,
356
pub kind: RenderTaskKind,
357
pub clear_mode: ClearMode,
358
pub saved_index: Option<SavedTargetIndex>,
359
}
360
361
impl RenderTask {
362
#[inline]
363
pub fn with_dynamic_location(
364
size: DeviceIntSize,
365
children: TaskDependencies,
366
kind: RenderTaskKind,
367
clear_mode: ClearMode,
368
) -> Self {
369
render_task_sanity_check(&size);
370
371
RenderTask {
372
location: RenderTaskLocation::Dynamic(None, size),
373
children,
374
kind,
375
clear_mode,
376
saved_index: None,
377
}
378
}
379
380
#[cfg(test)]
381
pub fn new_test(
382
target: RenderTargetKind,
383
location: RenderTaskLocation,
384
children: TaskDependencies,
385
) -> Self {
386
RenderTask {
387
location,
388
children,
389
kind: RenderTaskKind::Test(target),
390
clear_mode: ClearMode::Transparent,
391
saved_index: None,
392
}
393
}
394
395
pub fn new_picture(
396
location: RenderTaskLocation,
397
unclipped_size: DeviceSize,
398
pic_index: PictureIndex,
399
content_origin: DeviceIntPoint,
400
uv_rect_kind: UvRectKind,
401
surface_spatial_node_index: SpatialNodeIndex,
402
device_pixel_scale: DevicePixelScale,
403
vis_mask: PrimitiveVisibilityMask,
404
scissor_rect: Option<DeviceIntRect>,
405
valid_rect: Option<DeviceIntRect>,
406
) -> Self {
407
let size = match location {
408
RenderTaskLocation::Dynamic(_, size) => size,
409
RenderTaskLocation::Fixed(rect) => rect.size,
410
RenderTaskLocation::TextureCache { rect, .. } => rect.size,
411
RenderTaskLocation::PictureCache { size, .. } => size,
412
};
413
414
render_task_sanity_check(&size);
415
416
let can_merge = size.width as f32 >= unclipped_size.width &&
417
size.height as f32 >= unclipped_size.height;
418
419
RenderTask {
420
location,
421
children: TaskDependencies::new(),
422
kind: RenderTaskKind::Picture(PictureTask {
423
pic_index,
424
content_origin,
425
can_merge,
426
uv_rect_handle: GpuCacheHandle::new(),
427
uv_rect_kind,
428
surface_spatial_node_index,
429
device_pixel_scale,
430
vis_mask,
431
scissor_rect,
432
valid_rect,
433
}),
434
clear_mode: ClearMode::Transparent,
435
saved_index: None,
436
}
437
}
438
439
pub fn new_gradient(
440
size: DeviceIntSize,
441
stops: [GradientStopKey; GRADIENT_FP_STOPS],
442
orientation: LineOrientation,
443
start_point: f32,
444
end_point: f32,
445
) -> Self {
446
RenderTask::with_dynamic_location(
447
size,
448
TaskDependencies::new(),
449
RenderTaskKind::Gradient(GradientTask {
450
stops,
451
orientation,
452
start_point,
453
end_point,
454
}),
455
ClearMode::DontCare,
456
)
457
}
458
459
pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
460
RenderTask::with_dynamic_location(
461
screen_rect.size,
462
TaskDependencies::new(),
463
RenderTaskKind::Readback(screen_rect),
464
ClearMode::Transparent,
465
)
466
}
467
468
pub fn new_blit(
469
size: DeviceIntSize,
470
source: BlitSource,
471
) -> Self {
472
RenderTask::new_blit_with_padding(size, DeviceIntSideOffsets::zero(), source)
473
}
474
475
pub fn new_blit_with_padding(
476
padded_size: DeviceIntSize,
477
padding: DeviceIntSideOffsets,
478
source: BlitSource,
479
) -> Self {
480
// If this blit uses a render task as a source,
481
// ensure it's added as a child task. This will
482
// ensure it gets allocated in the correct pass
483
// and made available as an input when this task
484
// executes.
485
let children = match source {
486
BlitSource::RenderTask { task_id } => smallvec![task_id],
487
BlitSource::Image { .. } => smallvec![],
488
};
489
490
RenderTask::with_dynamic_location(
491
padded_size,
492
children,
493
RenderTaskKind::Blit(BlitTask {
494
source,
495
padding,
496
}),
497
ClearMode::Transparent,
498
)
499
}
500
501
pub fn new_line_decoration(
502
size: DeviceIntSize,
503
style: LineStyle,
504
orientation: LineOrientation,
505
wavy_line_thickness: f32,
506
local_size: LayoutSize,
507
) -> Self {
508
RenderTask::with_dynamic_location(
509
size,
510
TaskDependencies::new(),
511
RenderTaskKind::LineDecoration(LineDecorationTask {
512
style,
513
orientation,
514
wavy_line_thickness,
515
local_size,
516
}),
517
ClearMode::Transparent,
518
)
519
}
520
521
pub fn new_mask(
522
outer_rect: DeviceIntRect,
523
clip_node_range: ClipNodeRange,
524
root_spatial_node_index: SpatialNodeIndex,
525
clip_store: &mut ClipStore,
526
gpu_cache: &mut GpuCache,
527
resource_cache: &mut ResourceCache,
528
render_tasks: &mut RenderTaskGraph,
529
clip_data_store: &mut ClipDataStore,
530
device_pixel_scale: DevicePixelScale,
531
fb_config: &FrameBuilderConfig,
532
) -> RenderTaskId {
533
// Step through the clip sources that make up this mask. If we find
534
// any box-shadow clip sources, request that image from the render
535
// task cache. This allows the blurred box-shadow rect to be cached
536
// in the texture cache across frames.
537
// TODO(gw): Consider moving this logic outside this function, especially
538
// as we add more clip sources that depend on render tasks.
539
// TODO(gw): If this ever shows up in a profile, we could pre-calculate
540
// whether a ClipSources contains any box-shadows and skip
541
// this iteration for the majority of cases.
542
let mut needs_clear = fb_config.gpu_supports_fast_clears;
543
544
for i in 0 .. clip_node_range.count {
545
let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
546
let clip_node = &mut clip_data_store[clip_instance.handle];
547
match clip_node.item.kind {
548
ClipItemKind::BoxShadow { ref mut source } => {
549
let (cache_size, cache_key) = source.cache_key
550
.as_ref()
551
.expect("bug: no cache key set")
552
.clone();
553
let blur_radius_dp = cache_key.blur_radius_dp as f32;
554
let clip_data_address = gpu_cache.get_address(&source.clip_data_handle);
555
556
// Request a cacheable render task with a blurred, minimal
557
// sized box-shadow rect.
558
source.cache_handle = Some(resource_cache.request_render_task(
559
RenderTaskCacheKey {
560
size: cache_size,
561
kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
562
},
563
gpu_cache,
564
render_tasks,
565
None,
566
false,
567
|render_tasks| {
568
// Draw the rounded rect.
569
let mask_task_id = render_tasks.add().init(RenderTask::new_rounded_rect_mask(
570
cache_size,
571
clip_data_address,
572
source.minimal_shadow_rect.origin,
573
device_pixel_scale,
574
fb_config,
575
));
576
577
// Blur it
578
RenderTask::new_blur(
579
DeviceSize::new(blur_radius_dp, blur_radius_dp),
580
mask_task_id,
581
render_tasks,
582
RenderTargetKind::Alpha,
583
ClearMode::Zero,
584
None,
585
cache_size,
586
)
587
}
588
));
589
}
590
ClipItemKind::Rectangle { mode: ClipMode::Clip, .. } => {
591
if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
592
// This is conservative - it's only the case that we actually need
593
// a clear here if we end up adding this mask via add_tiled_clip_mask,
594
// but for simplicity we will just clear if any of these are encountered,
595
// since they are rare.
596
needs_clear = true;
597
}
598
}
599
ClipItemKind::Rectangle { mode: ClipMode::ClipOut, .. } |
600
ClipItemKind::RoundedRectangle { .. } |
601
ClipItemKind::Image { .. } => {}
602
}
603
}
604
605
// If we have a potentially tiled clip mask, clear the mask area first. Otherwise,
606
// the first (primary) clip mask will overwrite all the clip mask pixels with
607
// blending disabled to set to the initial value.
608
let clear_mode = if needs_clear {
609
ClearMode::One
610
} else {
611
ClearMode::DontCare
612
};
613
614
render_tasks.add().init(
615
RenderTask::with_dynamic_location(
616
outer_rect.size,
617
smallvec![],
618
RenderTaskKind::CacheMask(CacheMaskTask {
619
actual_rect: outer_rect,
620
clip_node_range,
621
root_spatial_node_index,
622
device_pixel_scale,
623
}),
624
clear_mode,
625
)
626
)
627
}
628
629
pub fn new_rounded_rect_mask(
630
size: DeviceIntSize,
631
clip_data_address: GpuCacheAddress,
632
local_pos: LayoutPoint,
633
device_pixel_scale: DevicePixelScale,
634
fb_config: &FrameBuilderConfig,
635
) -> Self {
636
let clear_mode = if fb_config.gpu_supports_fast_clears {
637
ClearMode::One
638
} else {
639
ClearMode::DontCare
640
};
641
642
RenderTask::with_dynamic_location(
643
size,
644
TaskDependencies::new(),
645
RenderTaskKind::ClipRegion(ClipRegionTask {
646
clip_data_address,
647
local_pos,
648
device_pixel_scale,
649
}),
650
clear_mode,
651
)
652
}
653
654
// In order to do the blur down-scaling passes without introducing errors, we need the
655
// source of each down-scale pass to be a multuple of two. If need be, this inflates
656
// the source size so that each down-scale pass will sample correctly.
657
pub fn adjusted_blur_source_size(original_size: DeviceIntSize, mut std_dev: DeviceSize) -> DeviceIntSize {
658
let mut adjusted_size = original_size;
659
let mut scale_factor = 1.0;
660
while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION {
661
if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE ||
662
adjusted_size.height < MIN_DOWNSCALING_RT_SIZE {
663
break;
664
}
665
std_dev = std_dev * 0.5;
666
scale_factor *= 2.0;
667
adjusted_size = (original_size.to_f32() / scale_factor).ceil().to_i32();
668
}
669
670
adjusted_size * scale_factor as i32
671
}
672
673
// Construct a render task to apply a blur to a primitive.
674
// The render task chain that is constructed looks like:
675
//
676
// PrimitiveCacheTask: Draw the primitives.
677
// ^
678
// |
679
// DownscalingTask(s): Each downscaling task reduces the size of render target to
680
// ^ half. Also reduce the std deviation to half until the std
681
// | deviation less than 4.0.
682
// |
683
// |
684
// VerticalBlurTask: Apply the separable vertical blur to the primitive.
685
// ^
686
// |
687
// HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
688
// |
689
// +---- This is stored as the input task to the primitive shader.
690
//
691
pub fn new_blur(
692
blur_std_deviation: DeviceSize,
693
src_task_id: RenderTaskId,
694
render_tasks: &mut RenderTaskGraph,
695
target_kind: RenderTargetKind,
696
clear_mode: ClearMode,
697
mut blur_cache: Option<&mut BlurTaskCache>,
698
blur_region: DeviceIntSize,
699
) -> RenderTaskId {
700
// Adjust large std deviation value.
701
let mut adjusted_blur_std_deviation = blur_std_deviation;
702
let (blur_target_size, uv_rect_kind) = {
703
let src_task = &render_tasks[src_task_id];
704
(src_task.get_dynamic_size(), src_task.uv_rect_kind())
705
};
706
let mut adjusted_blur_target_size = blur_target_size;
707
let mut downscaling_src_task_id = src_task_id;
708
let mut scale_factor = 1.0;
709
let mut n_downscales = 1;
710
while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION &&
711
adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION {
712
if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
713
adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
714
break;
715
}
716
adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5;
717
scale_factor *= 2.0;
718
adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
719
720
let cached_task = match blur_cache {
721
Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(),
722
None => None,
723
};
724
725
downscaling_src_task_id = cached_task.unwrap_or_else(|| {
726
RenderTask::new_scaling(
727
downscaling_src_task_id,
728
render_tasks,
729
target_kind,
730
adjusted_blur_target_size,
731
)
732
});
733
734
if let Some(ref mut cache) = blur_cache {
735
cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id);
736
}
737
738
n_downscales += 1;
739
}
740
741
742
let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
743
744
let cached_task = match blur_cache {
745
Some(ref mut cache) => cache.get(&blur_key).cloned(),
746
None => None,
747
};
748
749
let blur_region = blur_region / (scale_factor as i32);
750
751
let blur_task_id = cached_task.unwrap_or_else(|| {
752
let blur_task_v = render_tasks.add().init(RenderTask::with_dynamic_location(
753
adjusted_blur_target_size,
754
smallvec![downscaling_src_task_id],
755
RenderTaskKind::VerticalBlur(BlurTask {
756
blur_std_deviation: adjusted_blur_std_deviation.height,
757
target_kind,
758
uv_rect_handle: GpuCacheHandle::new(),
759
blur_region,
760
uv_rect_kind,
761
}),
762
clear_mode,
763
));
764
765
render_tasks.add().init(RenderTask::with_dynamic_location(
766
adjusted_blur_target_size,
767
smallvec![blur_task_v],
768
RenderTaskKind::HorizontalBlur(BlurTask {
769
blur_std_deviation: adjusted_blur_std_deviation.width,
770
target_kind,
771
uv_rect_handle: GpuCacheHandle::new(),
772
blur_region,
773
uv_rect_kind,
774
}),
775
clear_mode,
776
))
777
});
778
779
if let Some(ref mut cache) = blur_cache {
780
cache.insert(blur_key, blur_task_id);
781
}
782
783
blur_task_id
784
}
785
786
pub fn new_border_segment(
787
size: DeviceIntSize,
788
instances: Vec<BorderInstance>,
789
) -> Self {
790
RenderTask::with_dynamic_location(
791
size,
792
TaskDependencies::new(),
793
RenderTaskKind::Border(BorderTask {
794
instances,
795
}),
796
ClearMode::Transparent,
797
)
798
}
799
800
pub fn new_scaling(
801
src_task_id: RenderTaskId,
802
render_tasks: &mut RenderTaskGraph,
803
target_kind: RenderTargetKind,
804
size: DeviceIntSize,
805
) -> RenderTaskId {
806
Self::new_scaling_with_padding(
807
BlitSource::RenderTask { task_id: src_task_id },
808
render_tasks,
809
target_kind,
810
size,
811
DeviceIntSideOffsets::zero(),
812
)
813
}
814
815
pub fn new_scaling_with_padding(
816
source: BlitSource,
817
render_tasks: &mut RenderTaskGraph,
818
target_kind: RenderTargetKind,
819
padded_size: DeviceIntSize,
820
padding: DeviceIntSideOffsets,
821
) -> RenderTaskId {
822
let (uv_rect_kind, children, image) = match source {
823
BlitSource::RenderTask { task_id } => (render_tasks[task_id].uv_rect_kind(), smallvec![task_id], None),
824
BlitSource::Image { key } => (UvRectKind::Rect, smallvec![], Some(key)),
825
};
826
827
render_tasks.add().init(
828
RenderTask::with_dynamic_location(
829
padded_size,
830
children,
831
RenderTaskKind::Scaling(ScalingTask {
832
target_kind,
833
image,
834
uv_rect_kind,
835
padding,
836
}),
837
ClearMode::DontCare,
838
)
839
)
840
}
841
842
pub fn new_svg_filter(
843
filter_primitives: &[FilterPrimitive],
844
filter_datas: &[SFilterData],
845
render_tasks: &mut RenderTaskGraph,
846
content_size: DeviceIntSize,
847
uv_rect_kind: UvRectKind,
848
original_task_id: RenderTaskId,
849
device_pixel_scale: DevicePixelScale,
850
) -> RenderTaskId {
851
852
if filter_primitives.is_empty() {
853
return original_task_id;
854
}
855
856
// Resolves the input to a filter primitive
857
let get_task_input = |
858
input: &FilterPrimitiveInput,
859
filter_primitives: &[FilterPrimitive],
860
render_tasks: &mut RenderTaskGraph,
861
cur_index: usize,
862
outputs: &[RenderTaskId],
863
original: RenderTaskId,
864
color_space: ColorSpace,
865
| {
866
// TODO(cbrewster): Not sure we can assume that the original input is sRGB.
867
let (mut task_id, input_color_space) = match input.to_index(cur_index) {
868
Some(index) => (outputs[index], filter_primitives[index].color_space),
869
None => (original, ColorSpace::Srgb),
870
};
871
872
match (input_color_space, color_space) {
873
(ColorSpace::Srgb, ColorSpace::LinearRgb) => {
874
task_id = render_tasks.add().init(RenderTask::new_svg_filter_primitive(
875
smallvec![task_id],
876
content_size,
877
uv_rect_kind,
878
SvgFilterInfo::SrgbToLinear,
879
));
880
},
881
(ColorSpace::LinearRgb, ColorSpace::Srgb) => {
882
task_id = render_tasks.add().init(RenderTask::new_svg_filter_primitive(
883
smallvec![task_id],
884
content_size,
885
uv_rect_kind,
886
SvgFilterInfo::LinearToSrgb,
887
));
888
},
889
_ => {},
890
}
891
892
task_id
893
};
894
895
let mut outputs = vec![];
896
let mut cur_filter_data = 0;
897
for (cur_index, primitive) in filter_primitives.iter().enumerate() {
898
let render_task_id = match primitive.kind {
899
FilterPrimitiveKind::Identity(ref identity) => {
900
// Identity does not create a task, it provides its input's render task
901
get_task_input(
902
&identity.input,
903
filter_primitives,
904
render_tasks,
905
cur_index,
906
&outputs,
907
original_task_id,
908
primitive.color_space
909
)
910
}
911
FilterPrimitiveKind::Blend(ref blend) => {
912
let input_1_task_id = get_task_input(
913
&blend.input1,
914
filter_primitives,
915
render_tasks,
916
cur_index,
917
&outputs,
918
original_task_id,
919
primitive.color_space
920
);
921
let input_2_task_id = get_task_input(
922
&blend.input2,
923
filter_primitives,
924
render_tasks,
925
cur_index,
926
&outputs,
927
original_task_id,
928
primitive.color_space
929
);
930
931
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
932
smallvec![input_1_task_id, input_2_task_id],
933
content_size,
934
uv_rect_kind,
935
SvgFilterInfo::Blend(blend.mode),
936
))
937
},
938
FilterPrimitiveKind::Flood(ref flood) => {
939
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
940
smallvec![],
941
content_size,
942
uv_rect_kind,
943
SvgFilterInfo::Flood(flood.color),
944
))
945
}
946
FilterPrimitiveKind::Blur(ref blur) => {
947
let blur_std_deviation = blur.radius * device_pixel_scale.0;
948
let input_task_id = get_task_input(
949
&blur.input,
950
filter_primitives,
951
render_tasks,
952
cur_index,
953
&outputs,
954
original_task_id,
955
primitive.color_space
956
);
957
958
RenderTask::new_blur(
959
DeviceSize::new(blur_std_deviation, blur_std_deviation),
960
// TODO: This is a hack to ensure that a blur task's input is always
961
// in the blur's previous pass.
962
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
963
smallvec![input_task_id],
964
content_size,
965
uv_rect_kind,
966
SvgFilterInfo::Identity,
967
)),
968
render_tasks,
969
RenderTargetKind::Color,
970
ClearMode::Transparent,
971
None,
972
content_size,
973
)
974
}
975
FilterPrimitiveKind::Opacity(ref opacity) => {
976
let input_task_id = get_task_input(
977
&opacity.input,
978
filter_primitives,
979
render_tasks,
980
cur_index,
981
&outputs,
982
original_task_id,
983
primitive.color_space
984
);
985
986
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
987
smallvec![input_task_id],
988
content_size,
989
uv_rect_kind,
990
SvgFilterInfo::Opacity(opacity.opacity),
991
))
992
}
993
FilterPrimitiveKind::ColorMatrix(ref color_matrix) => {
994
let input_task_id = get_task_input(
995
&color_matrix.input,
996
filter_primitives,
997
render_tasks,
998
cur_index,
999
&outputs,
1000
original_task_id,
1001
primitive.color_space
1002
);
1003
1004
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
1005
smallvec![input_task_id],
1006
content_size,
1007
uv_rect_kind,
1008
SvgFilterInfo::ColorMatrix(Box::new(color_matrix.matrix)),
1009
))
1010
}
1011
FilterPrimitiveKind::DropShadow(ref drop_shadow) => {
1012
let input_task_id = get_task_input(
1013
&drop_shadow.input,
1014
filter_primitives,
1015
render_tasks,
1016
cur_index,
1017
&outputs,
1018
original_task_id,
1019
primitive.color_space
1020
);
1021
1022
let blur_std_deviation = drop_shadow.shadow.blur_radius * device_pixel_scale.0;
1023
let offset = drop_shadow.shadow.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
1024
1025
let offset_task_id = render_tasks.add().init(
1026
RenderTask::new_svg_filter_primitive(
1027
smallvec![input_task_id],
1028
content_size,
1029
uv_rect_kind,
1030
SvgFilterInfo::Offset(offset),
1031
)
1032
);
1033
1034
let blur_task_id = RenderTask::new_blur(
1035
DeviceSize::new(blur_std_deviation, blur_std_deviation),
1036
offset_task_id,
1037
render_tasks,
1038
RenderTargetKind::Color,
1039
ClearMode::Transparent,
1040
None,
1041
content_size,
1042
);
1043
1044
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
1045
smallvec![input_task_id, blur_task_id],
1046
content_size,
1047
uv_rect_kind,
1048
SvgFilterInfo::DropShadow(drop_shadow.shadow.color),
1049
))
1050
}
1051
FilterPrimitiveKind::ComponentTransfer(ref component_transfer) => {
1052
let input_task_id = get_task_input(
1053
&component_transfer.input,
1054
filter_primitives,
1055
render_tasks,
1056
cur_index,
1057
&outputs,
1058
original_task_id,
1059
primitive.color_space
1060
);
1061
1062
let filter_data = &filter_datas[cur_filter_data];
1063
cur_filter_data += 1;
1064
if filter_data.is_identity() {
1065
input_task_id
1066
} else {
1067
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
1068
smallvec![input_task_id],
1069
content_size,
1070
uv_rect_kind,
1071
SvgFilterInfo::ComponentTransfer(filter_data.clone()),
1072
))
1073
}
1074
}
1075
FilterPrimitiveKind::Offset(ref info) => {
1076
let input_task_id = get_task_input(
1077
&info.input,
1078
filter_primitives,
1079
render_tasks,
1080
cur_index,
1081
&outputs,
1082
original_task_id,
1083
primitive.color_space
1084
);
1085
1086
let offset = info.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
1087
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
1088
smallvec![input_task_id],
1089
content_size,
1090
uv_rect_kind,
1091
SvgFilterInfo::Offset(offset),
1092
))
1093
}
1094
FilterPrimitiveKind::Composite(info) => {
1095
let input_1_task_id = get_task_input(
1096
&info.input1,
1097
filter_primitives,
1098
render_tasks,
1099
cur_index,
1100
&outputs,
1101
original_task_id,
1102
primitive.color_space
1103
);
1104
let input_2_task_id = get_task_input(
1105
&info.input2,
1106
filter_primitives,
1107
render_tasks,
1108
cur_index,
1109
&outputs,
1110
original_task_id,
1111
primitive.color_space
1112
);
1113
1114
render_tasks.add().init(RenderTask::new_svg_filter_primitive(
1115
smallvec![input_1_task_id, input_2_task_id],
1116
content_size,
1117
uv_rect_kind,
1118
SvgFilterInfo::Composite(info.operator),
1119
))
1120
}
1121
};
1122
outputs.push(render_task_id);
1123
}
1124
1125
// The output of a filter is the output of the last primitive in the chain.
1126
let mut render_task_id = *outputs.last().unwrap();
1127
1128
// Convert to sRGB if needed
1129
if filter_primitives.last().unwrap().color_space == ColorSpace::LinearRgb {
1130
render_task_id = render_tasks.add().init(RenderTask::new_svg_filter_primitive(
1131
smallvec![render_task_id],
1132
content_size,
1133
uv_rect_kind,
1134
SvgFilterInfo::LinearToSrgb,
1135
));
1136
}
1137
1138
render_task_id
1139
}
1140
1141
pub fn new_svg_filter_primitive(
1142
tasks: TaskDependencies,
1143
target_size: DeviceIntSize,
1144
uv_rect_kind: UvRectKind,
1145
info: SvgFilterInfo,
1146
) -> Self {
1147
RenderTask::with_dynamic_location(
1148
target_size,
1149
tasks,
1150
RenderTaskKind::SvgFilter(SvgFilterTask {
1151
extra_gpu_cache_handle: None,
1152
uv_rect_handle: GpuCacheHandle::new(),
1153
uv_rect_kind,
1154
info,
1155
}),
1156
ClearMode::Transparent,
1157
)
1158
}
1159
1160
pub fn uv_rect_kind(&self) -> UvRectKind {
1161
match self.kind {
1162
RenderTaskKind::CacheMask(..) |
1163
RenderTaskKind::Readback(..) => {
1164
unreachable!("bug: unexpected render task");
1165
}
1166
1167
RenderTaskKind::Picture(ref task) => {
1168
task.uv_rect_kind
1169
}
1170
1171
RenderTaskKind::VerticalBlur(ref task) |
1172
RenderTaskKind::HorizontalBlur(ref task) => {
1173
task.uv_rect_kind
1174
}
1175
1176
RenderTaskKind::Scaling(ref task) => {
1177
task.uv_rect_kind
1178
}
1179
1180
RenderTaskKind::SvgFilter(ref task) => {
1181
task.uv_rect_kind
1182
}
1183
1184
RenderTaskKind::ClipRegion(..) |
1185
RenderTaskKind::Border(..) |
1186
RenderTaskKind::Gradient(..) |
1187
RenderTaskKind::LineDecoration(..) |
1188
RenderTaskKind::Blit(..) => {
1189
UvRectKind::Rect
1190
}
1191
1192
#[cfg(test)]
1193
RenderTaskKind::Test(..) => {
1194
unreachable!("Unexpected render task");
1195
}
1196
}
1197
}
1198
1199
// Write (up to) 8 floats of data specific to the type
1200
// of render task that is provided to the GPU shaders
1201
// via a vertex texture.
1202
pub fn write_task_data(&self) -> RenderTaskData {
1203
// NOTE: The ordering and layout of these structures are
1204
// required to match both the GPU structures declared
1205
// in prim_shared.glsl, and also the uses in submit_batch()
1206
// in renderer.rs.
1207
// TODO(gw): Maybe there's a way to make this stuff a bit
1208
// more type-safe. Although, it will always need
1209
// to be kept in sync with the GLSL code anyway.
1210
1211
let data = match self.kind {
1212
RenderTaskKind::Picture(ref task) => {
1213
// Note: has to match `PICTURE_TYPE_*` in shaders
1214
[
1215
task.device_pixel_scale.0,
1216
task.content_origin.x as f32,
1217
task.content_origin.y as f32,
1218
]
1219
}
1220
RenderTaskKind::CacheMask(ref task) => {
1221
[
1222
task.device_pixel_scale.0,
1223
task.actual_rect.origin.x as f32,
1224
task.actual_rect.origin.y as f32,
1225
]
1226
}
1227
RenderTaskKind::ClipRegion(ref task) => {
1228
[
1229
task.device_pixel_scale.0,
1230
0.0,
1231
0.0,
1232
]
1233
}
1234
RenderTaskKind::VerticalBlur(ref task) |
1235
RenderTaskKind::HorizontalBlur(ref task) => {
1236
[
1237
task.blur_std_deviation,
1238
task.blur_region.width as f32,
1239
task.blur_region.height as f32,
1240
]
1241
}
1242
RenderTaskKind::Readback(..) |
1243
RenderTaskKind::Scaling(..) |
1244
RenderTaskKind::Border(..) |
1245
RenderTaskKind::LineDecoration(..) |
1246
RenderTaskKind::Gradient(..) |
1247
RenderTaskKind::Blit(..) => {
1248
[0.0; 3]
1249
}
1250
1251
1252
RenderTaskKind::SvgFilter(ref task) => {
1253
match task.info {
1254
SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0],
1255
SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0],
1256
_ => [0.0; 3]
1257
}
1258
}
1259
1260
#[cfg(test)]
1261
RenderTaskKind::Test(..) => {
1262
unreachable!();
1263
}
1264
};
1265
1266
let (mut target_rect, target_index) = self.get_target_rect();
1267
// The primitives inside a fixed-location render task
1268
// are already placed to their corresponding positions,
1269
// so the shader doesn't need to shift by the origin.
1270
if let RenderTaskLocation::Fixed(_) = self.location {
1271
target_rect.origin = DeviceIntPoint::origin();
1272
}
1273
1274
RenderTaskData {
1275
data: [
1276
target_rect.origin.x as f32,
1277
target_rect.origin.y as f32,
1278
target_rect.size.width as f32,
1279
target_rect.size.height as f32,
1280
target_index.0 as f32,
1281
data[0],
1282
data[1],
1283
data[2],
1284
]
1285
}
1286
}
1287
1288
pub fn get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress {
1289
match self.kind {
1290
RenderTaskKind::Picture(ref info) => {
1291
gpu_cache.get_address(&info.uv_rect_handle)
1292
}
1293
RenderTaskKind::VerticalBlur(ref info) |
1294
RenderTaskKind::HorizontalBlur(ref info) => {
1295
gpu_cache.get_address(&info.uv_rect_handle)
1296
}
1297
RenderTaskKind::SvgFilter(ref info) => {
1298
gpu_cache.get_address(&info.uv_rect_handle)
1299
}
1300
RenderTaskKind::ClipRegion(..) |
1301
RenderTaskKind::Readback(..) |
1302
RenderTaskKind::Scaling(..) |
1303
RenderTaskKind::Blit(..) |
1304
RenderTaskKind::Border(..) |
1305
RenderTaskKind::CacheMask(..) |
1306
RenderTaskKind::Gradient(..) |
1307
RenderTaskKind::LineDecoration(..) => {
1308
panic!("texture handle not supported for this task kind");
1309
}
1310
#[cfg(test)]
1311
RenderTaskKind::Test(..) => {
1312
panic!("RenderTask tests aren't expected to exercise this code");
1313
}
1314
}
1315
}
1316
1317
pub fn get_dynamic_size(&self) -> DeviceIntSize {
1318
match self.location {
1319
RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
1320
RenderTaskLocation::Dynamic(_, size) => size,
1321
RenderTaskLocation::TextureCache { rect, .. } => rect.size,
1322
RenderTaskLocation::PictureCache { size, .. } => size,
1323
}
1324
}
1325
1326
pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
1327
match self.location {
1328
RenderTaskLocation::Fixed(rect) => {
1329
(rect, RenderTargetIndex(0))
1330
}
1331
// Previously, we only added render tasks after the entire
1332
// primitive chain was determined visible. This meant that
1333
// we could assert any render task in the list was also
1334
// allocated (assigned to passes). Now, we add render
1335
// tasks earlier, and the picture they belong to may be
1336
// culled out later, so we can't assert that the task
1337
// has been allocated.
1338
// Render tasks that are created but not assigned to
1339
// passes consume a row in the render task texture, but
1340
// don't allocate any space in render targets nor
1341
// draw any pixels.
1342
// TODO(gw): Consider some kind of tag or other method
1343
// to mark a task as unused explicitly. This
1344
// would allow us to restore this debug check.
1345
RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
1346
(DeviceIntRect::new(origin, size), target_index)
1347
}
1348
RenderTaskLocation::Dynamic(None, _) => {
1349
(DeviceIntRect::zero(), RenderTargetIndex(0))
1350
}
1351
RenderTaskLocation::TextureCache {layer, rect, .. } => {
1352
(rect, RenderTargetIndex(layer as usize))
1353
}
1354
RenderTaskLocation::PictureCache { ref surface, size, .. } => {
1355
let layer = match surface {
1356
ResolvedSurfaceTexture::TextureCache { layer, .. } => *layer,
1357
ResolvedSurfaceTexture::Native { .. } => 0,
1358
};
1359
1360
(
1361
DeviceIntRect::new(
1362
DeviceIntPoint::zero(),
1363
size,
1364
),
1365
RenderTargetIndex(layer as usize),
1366
)
1367
}
1368
}
1369
}
1370
1371
pub fn target_kind(&self) -> RenderTargetKind {
1372
match self.kind {
1373
RenderTaskKind::LineDecoration(..) |
1374
RenderTaskKind::Readback(..) |
1375
RenderTaskKind::Border(..) |
1376
RenderTaskKind::Gradient(..) |
1377
RenderTaskKind::Picture(..) |
1378
RenderTaskKind::Blit(..) |
1379
RenderTaskKind::SvgFilter(..) => {
1380
RenderTargetKind::Color
1381
}
1382
1383
RenderTaskKind::ClipRegion(..) |
1384
RenderTaskKind::CacheMask(..) => {
1385
RenderTargetKind::Alpha
1386
}
1387
1388
RenderTaskKind::VerticalBlur(ref task_info) |
1389
RenderTaskKind::HorizontalBlur(ref task_info) => {
1390
task_info.target_kind
1391
}
1392
1393
RenderTaskKind::Scaling(ref task_info) => {
1394
task_info.target_kind
1395
}
1396
1397
#[cfg(test)]
1398
RenderTaskKind::Test(kind) => kind,
1399
}
1400
}
1401
1402
pub fn write_gpu_blocks(
1403
&mut self,
1404
gpu_cache: &mut GpuCache,
1405
) {
1406
let (target_rect, target_index) = self.get_target_rect();
1407
1408
let (cache_handle, uv_rect_kind) = match self.kind {
1409
RenderTaskKind::HorizontalBlur(ref mut info) |
1410
RenderTaskKind::VerticalBlur(ref mut info) => {
1411
(&mut info.uv_rect_handle, info.uv_rect_kind)
1412
}
1413
RenderTaskKind::Picture(ref mut info) => {
1414
(&mut info.uv_rect_handle, info.uv_rect_kind)
1415
}
1416
RenderTaskKind::SvgFilter(ref mut info) => {
1417
(&mut info.uv_rect_handle, info.uv_rect_kind)
1418
}
1419
RenderTaskKind::Readback(..) |
1420
RenderTaskKind::Scaling(..) |
1421
RenderTaskKind::Blit(..) |
1422
RenderTaskKind::ClipRegion(..) |
1423
RenderTaskKind::Border(..) |
1424
RenderTaskKind::CacheMask(..) |
1425
RenderTaskKind::Gradient(..) |
1426
RenderTaskKind::LineDecoration(..) => {
1427
return;
1428
}
1429
#[cfg(test)]
1430
RenderTaskKind::Test(..) => {
1431
panic!("RenderTask tests aren't expected to exercise this code");
1432
}
1433
};
1434
1435
if let Some(mut request) = gpu_cache.request(cache_handle) {
1436
let p0 = target_rect.min().to_f32();
1437
let p1 = target_rect.max().to_f32();
1438
let image_source = ImageSource {
1439
p0,
1440
p1,
1441
texture_layer: target_index.0 as f32,
1442
user_data: [0.0; 3],
1443
uv_rect_kind,
1444
};
1445
image_source.write_gpu_blocks(&mut request);
1446
}
1447
1448
if let RenderTaskKind::SvgFilter(ref mut filter_task) = self.kind {
1449
match filter_task.info {
1450
SvgFilterInfo::ColorMatrix(ref matrix) => {
1451
let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
1452
if let Some(mut request) = gpu_cache.request(handle) {
1453
for i in 0..5 {
1454
request.push([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]);
1455
}
1456
}
1457
}
1458
SvgFilterInfo::DropShadow(color) |
1459
SvgFilterInfo::Flood(color) => {
1460
let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
1461
if let Some(mut request) = gpu_cache.request(handle) {
1462
request.push(color.to_array());
1463
}
1464
}
1465
SvgFilterInfo::ComponentTransfer(ref data) => {
1466
let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
1467
if let Some(request) = gpu_cache.request(handle) {
1468
data.update(request);
1469
}
1470
}
1471
SvgFilterInfo::Composite(ref operator) => {
1472
if let CompositeOperator::Arithmetic(k_vals) = operator {
1473
let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
1474
if let Some(mut request) = gpu_cache.request(handle) {
1475
request.push(*k_vals);
1476
}
1477
}
1478
}
1479
_ => {},
1480
}
1481
}
1482
}
1483
1484
#[cfg(feature = "debugger")]
1485
pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskGraph) -> bool {
1486
match self.kind {
1487
RenderTaskKind::Picture(ref task) => {
1488
pt.new_level(format!("Picture of {:?}", task.pic_index));
1489
}
1490
RenderTaskKind::CacheMask(ref task) => {
1491
pt.new_level(format!("CacheMask with {} clips", task.clip_node_range.count));
1492
pt.add_item(format!("rect: {:?}", task.actual_rect));
1493
}
1494
RenderTaskKind::LineDecoration(..) => {
1495
pt.new_level("LineDecoration".to_owned());
1496
}
1497
RenderTaskKind::ClipRegion(..) => {
1498
pt.new_level("ClipRegion".to_owned());
1499
}
1500
RenderTaskKind::VerticalBlur(ref task) => {
1501
pt.new_level("VerticalBlur".to_owned());
1502
task.print_with(pt);
1503
}
1504
RenderTaskKind::HorizontalBlur(ref task) => {
1505
pt.new_level("HorizontalBlur".to_owned());
1506
task.print_with(pt);
1507
}
1508
RenderTaskKind::Readback(ref rect) => {
1509
pt.new_level("Readback".to_owned());
1510
pt.add_item(format!("rect: {:?}", rect));
1511
}
1512
RenderTaskKind::Scaling(ref kind) => {
1513
pt.new_level("Scaling".to_owned());
1514
pt.add_item(format!("kind: {:?}", kind));
1515
}
1516
RenderTaskKind::Border(..) => {
1517
pt.new_level("Border".to_owned());
1518
}
1519
RenderTaskKind::Blit(ref task) => {
1520
pt.new_level("Blit".to_owned());
1521
pt.add_item(format!("source: {:?}", task.source));
1522
}
1523
RenderTaskKind::Gradient(..) => {
1524
pt.new_level("Gradient".to_owned());
1525
}
1526
RenderTaskKind::SvgFilter(ref task) => {
1527
pt.new_level("SvgFilter".to_owned());
1528
pt.add_item(format!("primitive: {:?}", task.info));
1529
}
1530
#[cfg(test)]
1531
RenderTaskKind::Test(..) => {
1532
pt.new_level("Test".to_owned());
1533
}
1534
}
1535
1536
pt.add_item(format!("clear to: {:?}", self.clear_mode));
1537
pt.add_item(format!("dimensions: {:?}", self.location.size()));
1538
1539
for &child_id in &self.children {
1540
if tree[child_id].print_with(pt, tree) {
1541
pt.add_item(format!("self: {:?}", child_id))
1542
}
1543
}
1544
1545
pt.end_level();
1546
true
1547
}
1548
1549
/// Mark this render task for keeping the results alive up until the end of the frame.
1550
#[inline]
1551
pub fn mark_for_saving(&mut self) {
1552
match self.location {
1553
RenderTaskLocation::Fixed(..) |
1554
RenderTaskLocation::Dynamic(..) => {
1555
self.saved_index = Some(SavedTargetIndex::PENDING);
1556
}
1557
RenderTaskLocation::TextureCache { .. } |
1558
RenderTaskLocation::PictureCache { .. } => {
1559
panic!("Unable to mark a permanently cached task for saving!");
1560
}
1561
}
1562
}
1563
}