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, DebugCommand, DocumentId, ExternalImageData, ExternalImageId, PrimitiveFlags};
6
use api::{ImageFormat, ItemTag, NotificationRequest, Shadow, FilterOp};
7
use api::units::*;
8
use api;
9
use crate::composite::NativeSurfaceOperation;
10
use crate::device::TextureFilter;
11
use crate::renderer::PipelineInfo;
12
use crate::gpu_cache::GpuCacheUpdateList;
13
use crate::frame_builder::Frame;
14
use fxhash::FxHasher;
15
use plane_split::BspSplitter;
16
use crate::profiler::BackendProfileCounters;
17
use smallvec::SmallVec;
18
use std::{usize, i32};
19
use std::collections::{HashMap, HashSet};
20
use std::f32;
21
use std::hash::BuildHasherDefault;
22
use std::path::PathBuf;
23
use std::sync::Arc;
24
25
#[cfg(feature = "capture")]
26
use crate::capture::{CaptureConfig, ExternalCaptureImage};
27
#[cfg(feature = "replay")]
28
use crate::capture::PlainExternalImage;
29
30
pub type FastHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
31
pub type FastHashSet<K> = HashSet<K, BuildHasherDefault<FxHasher>>;
32
33
/// Custom field embedded inside the Polygon struct of the plane-split crate.
34
#[derive(Copy, Clone, Debug)]
35
#[cfg_attr(feature = "capture", derive(Serialize))]
36
pub struct PlaneSplitAnchor {
37
pub cluster_index: usize,
38
pub instance_index: usize,
39
}
40
41
impl PlaneSplitAnchor {
42
pub fn new(cluster_index: usize, instance_index: usize) -> Self {
43
PlaneSplitAnchor {
44
cluster_index,
45
instance_index,
46
}
47
}
48
}
49
50
impl Default for PlaneSplitAnchor {
51
fn default() -> Self {
52
PlaneSplitAnchor {
53
cluster_index: 0,
54
instance_index: 0,
55
}
56
}
57
}
58
59
/// A concrete plane splitter type used in WebRender.
60
pub type PlaneSplitter = BspSplitter<f64, WorldPixel, PlaneSplitAnchor>;
61
62
/// An arbitrary number which we assume opacity is invisible below.
63
const OPACITY_EPSILON: f32 = 0.001;
64
65
/// Equivalent to api::FilterOp with added internal information
66
#[derive(Clone, Debug, PartialEq)]
67
#[cfg_attr(feature = "capture", derive(Serialize))]
68
#[cfg_attr(feature = "replay", derive(Deserialize))]
69
pub enum Filter {
70
Identity,
71
Blur(f32),
72
Brightness(f32),
73
Contrast(f32),
74
Grayscale(f32),
75
HueRotate(f32),
76
Invert(f32),
77
Opacity(api::PropertyBinding<f32>, f32),
78
Saturate(f32),
79
Sepia(f32),
80
DropShadows(SmallVec<[Shadow; 1]>),
81
ColorMatrix(Box<[f32; 20]>),
82
SrgbToLinear,
83
LinearToSrgb,
84
ComponentTransfer,
85
Flood(ColorF),
86
}
87
88
impl Filter {
89
pub fn is_visible(&self) -> bool {
90
match *self {
91
Filter::Identity |
92
Filter::Blur(..) |
93
Filter::Brightness(..) |
94
Filter::Contrast(..) |
95
Filter::Grayscale(..) |
96
Filter::HueRotate(..) |
97
Filter::Invert(..) |
98
Filter::Saturate(..) |
99
Filter::Sepia(..) |
100
Filter::DropShadows(..) |
101
Filter::ColorMatrix(..) |
102
Filter::SrgbToLinear |
103
Filter::LinearToSrgb |
104
Filter::ComponentTransfer => true,
105
Filter::Opacity(_, amount) => {
106
amount > OPACITY_EPSILON
107
},
108
Filter::Flood(color) => {
109
color.a > OPACITY_EPSILON
110
}
111
}
112
}
113
114
pub fn is_noop(&self) -> bool {
115
match *self {
116
Filter::Identity => false, // this is intentional
117
Filter::Blur(length) => length == 0.0,
118
Filter::Brightness(amount) => amount == 1.0,
119
Filter::Contrast(amount) => amount == 1.0,
120
Filter::Grayscale(amount) => amount == 0.0,
121
Filter::HueRotate(amount) => amount == 0.0,
122
Filter::Invert(amount) => amount == 0.0,
123
Filter::Opacity(_, amount) => amount >= 1.0,
124
Filter::Saturate(amount) => amount == 1.0,
125
Filter::Sepia(amount) => amount == 0.0,
126
Filter::DropShadows(ref shadows) => {
127
for shadow in shadows {
128
if shadow.offset.x != 0.0 || shadow.offset.y != 0.0 || shadow.blur_radius != 0.0 {
129
return false;
130
}
131
}
132
133
true
134
}
135
Filter::ColorMatrix(ref matrix) => {
136
**matrix == [
137
1.0, 0.0, 0.0, 0.0,
138
0.0, 1.0, 0.0, 0.0,
139
0.0, 0.0, 1.0, 0.0,
140
0.0, 0.0, 0.0, 1.0,
141
0.0, 0.0, 0.0, 0.0
142
]
143
}
144
Filter::SrgbToLinear |
145
Filter::LinearToSrgb |
146
Filter::ComponentTransfer |
147
Filter::Flood(..) => false,
148
}
149
}
150
151
152
pub fn as_int(&self) -> i32 {
153
// Must be kept in sync with brush_blend.glsl
154
match *self {
155
Filter::Identity => 0, // matches `Contrast(1)`
156
Filter::Contrast(..) => 0,
157
Filter::Grayscale(..) => 1,
158
Filter::HueRotate(..) => 2,
159
Filter::Invert(..) => 3,
160
Filter::Saturate(..) => 4,
161
Filter::Sepia(..) => 5,
162
Filter::Brightness(..) => 6,
163
Filter::ColorMatrix(..) => 7,
164
Filter::SrgbToLinear => 8,
165
Filter::LinearToSrgb => 9,
166
Filter::Flood(..) => 10,
167
Filter::ComponentTransfer => 11,
168
Filter::Blur(..) => 12,
169
Filter::DropShadows(..) => 13,
170
Filter::Opacity(..) => 14,
171
}
172
}
173
}
174
175
impl From<FilterOp> for Filter {
176
fn from(op: FilterOp) -> Self {
177
match op {
178
FilterOp::Identity => Filter::Identity,
179
FilterOp::Blur(r) => Filter::Blur(r),
180
FilterOp::Brightness(b) => Filter::Brightness(b),
181
FilterOp::Contrast(c) => Filter::Contrast(c),
182
FilterOp::Grayscale(g) => Filter::Grayscale(g),
183
FilterOp::HueRotate(h) => Filter::HueRotate(h),
184
FilterOp::Invert(i) => Filter::Invert(i),
185
FilterOp::Opacity(binding, opacity) => Filter::Opacity(binding, opacity),
186
FilterOp::Saturate(s) => Filter::Saturate(s),
187
FilterOp::Sepia(s) => Filter::Sepia(s),
188
FilterOp::ColorMatrix(mat) => Filter::ColorMatrix(Box::new(mat)),
189
FilterOp::SrgbToLinear => Filter::SrgbToLinear,
190
FilterOp::LinearToSrgb => Filter::LinearToSrgb,
191
FilterOp::ComponentTransfer => Filter::ComponentTransfer,
192
FilterOp::DropShadow(shadow) => Filter::DropShadows(smallvec![shadow]),
193
FilterOp::Flood(color) => Filter::Flood(color),
194
}
195
}
196
}
197
198
#[cfg_attr(feature = "capture", derive(Serialize))]
199
#[cfg_attr(feature = "replay", derive(Deserialize))]
200
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
201
pub enum Swizzle {
202
Rgba,
203
Bgra,
204
}
205
206
impl Default for Swizzle {
207
fn default() -> Self {
208
Swizzle::Rgba
209
}
210
}
211
212
/// Swizzle settings of the texture cache.
213
#[cfg_attr(feature = "capture", derive(Serialize))]
214
#[cfg_attr(feature = "replay", derive(Deserialize))]
215
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
216
pub struct SwizzleSettings {
217
/// Swizzle required on sampling a texture with BGRA8 format.
218
pub bgra8_sampling_swizzle: Swizzle,
219
}
220
221
/// An ID for a texture that is owned by the `texture_cache` module.
222
///
223
/// This can include atlases or standalone textures allocated via the texture
224
/// cache (e.g. if an image is too large to be added to an atlas). The texture
225
/// cache manages the allocation and freeing of these IDs, and the rendering
226
/// thread maintains a map from cache texture ID to native texture.
227
///
228
/// We never reuse IDs, so we use a u64 here to be safe.
229
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
230
#[cfg_attr(feature = "capture", derive(Serialize))]
231
#[cfg_attr(feature = "replay", derive(Deserialize))]
232
pub struct CacheTextureId(pub u64);
233
234
/// Canonical type for texture layer indices.
235
///
236
/// WebRender is currently not very consistent about layer index types. Some
237
/// places use i32 (since that's the type used in various OpenGL APIs), some
238
/// places use u32 (since having it be signed is non-sensical, but the
239
/// underlying graphics APIs generally operate on 32-bit integers) and some
240
/// places use usize (since that's most natural in Rust).
241
///
242
/// Going forward, we aim to us usize throughout the codebase, since that allows
243
/// operations like indexing without a cast, and convert to the required type in
244
/// the device module when making calls into the platform layer.
245
pub type LayerIndex = usize;
246
247
/// Identifies a render pass target that is persisted until the end of the frame.
248
///
249
/// By default, only the targets of the immediately-preceding pass are bound as
250
/// inputs to the next pass. However, tasks can opt into having their target
251
/// preserved in a list until the end of the frame, and this type specifies the
252
/// index in that list.
253
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
254
#[cfg_attr(feature = "capture", derive(Serialize))]
255
#[cfg_attr(feature = "replay", derive(Deserialize))]
256
pub struct SavedTargetIndex(pub usize);
257
258
impl SavedTargetIndex {
259
pub const PENDING: Self = SavedTargetIndex(!0);
260
}
261
262
/// Identifies the source of an input texture to a shader.
263
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
264
#[cfg_attr(feature = "capture", derive(Serialize))]
265
#[cfg_attr(feature = "replay", derive(Deserialize))]
266
pub enum TextureSource {
267
/// Equivalent to `None`, allowing us to avoid using `Option`s everywhere.
268
Invalid,
269
/// An entry in the texture cache.
270
TextureCache(CacheTextureId, Swizzle),
271
/// An external image texture, mananged by the embedding.
272
External(ExternalImageData),
273
/// The alpha target of the immediately-preceding pass.
274
PrevPassAlpha,
275
/// The color target of the immediately-preceding pass.
276
PrevPassColor,
277
/// A render target from an earlier pass. Unlike the immediately-preceding
278
/// passes, these are not made available automatically, but are instead
279
/// opt-in by the `RenderTask` (see `mark_for_saving()`).
280
RenderTaskCache(SavedTargetIndex, Swizzle),
281
/// Select a dummy 1x1 white texture. This can be used by image
282
/// shaders that want to draw a solid color.
283
Dummy,
284
}
285
286
#[derive(Copy, Clone, Debug, PartialEq)]
287
#[cfg_attr(feature = "capture", derive(Serialize))]
288
#[cfg_attr(feature = "replay", derive(Deserialize))]
289
pub struct RenderTargetInfo {
290
pub has_depth: bool,
291
}
292
293
#[derive(Debug)]
294
pub enum TextureUpdateSource {
295
External {
296
id: ExternalImageId,
297
channel_index: u8,
298
},
299
Bytes { data: Arc<Vec<u8>> },
300
/// Clears the target area, rather than uploading any pixels. Used when the
301
/// texture cache debug display is active.
302
DebugClear,
303
}
304
305
/// Command to allocate, reallocate, or free a texture for the texture cache.
306
#[derive(Debug)]
307
pub struct TextureCacheAllocation {
308
/// The virtual ID (i.e. distinct from device ID) of the texture.
309
pub id: CacheTextureId,
310
/// Details corresponding to the operation in question.
311
pub kind: TextureCacheAllocationKind,
312
}
313
314
/// Information used when allocating / reallocating.
315
#[derive(Debug)]
316
pub struct TextureCacheAllocInfo {
317
pub width: i32,
318
pub height: i32,
319
pub layer_count: i32,
320
pub format: ImageFormat,
321
pub filter: TextureFilter,
322
/// Indicates whether this corresponds to one of the shared texture caches.
323
pub is_shared_cache: bool,
324
/// If true, this texture requires a depth target.
325
pub has_depth: bool,
326
}
327
328
/// Sub-operation-specific information for allocation operations.
329
#[derive(Debug)]
330
pub enum TextureCacheAllocationKind {
331
/// Performs an initial texture allocation.
332
Alloc(TextureCacheAllocInfo),
333
/// Reallocates the texture. The existing live texture with the same id
334
/// will be deallocated and its contents blitted over. The new size must
335
/// be greater than the old size.
336
Realloc(TextureCacheAllocInfo),
337
/// Reallocates the texture without preserving its contents.
338
Reset(TextureCacheAllocInfo),
339
/// Frees the texture and the corresponding cache ID.
340
Free,
341
}
342
343
/// Command to update the contents of the texture cache.
344
#[derive(Debug)]
345
pub struct TextureCacheUpdate {
346
pub rect: DeviceIntRect,
347
pub stride: Option<i32>,
348
pub offset: i32,
349
pub layer_index: i32,
350
pub format_override: Option<ImageFormat>,
351
pub source: TextureUpdateSource,
352
}
353
354
/// Atomic set of commands to manipulate the texture cache, generated on the
355
/// RenderBackend thread and executed on the Renderer thread.
356
///
357
/// The list of allocation operations is processed before the updates. This is
358
/// important to allow coalescing of certain allocation operations.
359
#[derive(Default)]
360
pub struct TextureUpdateList {
361
/// Indicates that there was some kind of cleanup clear operation. Used for
362
/// sanity checks.
363
pub clears_shared_cache: bool,
364
/// Commands to alloc/realloc/free the textures. Processed first.
365
pub allocations: Vec<TextureCacheAllocation>,
366
/// Commands to update the contents of the textures. Processed second.
367
pub updates: FastHashMap<CacheTextureId, Vec<TextureCacheUpdate>>,
368
}
369
370
impl TextureUpdateList {
371
/// Mints a new `TextureUpdateList`.
372
pub fn new() -> Self {
373
TextureUpdateList {
374
clears_shared_cache: false,
375
allocations: Vec::new(),
376
updates: FastHashMap::default(),
377
}
378
}
379
380
/// Returns true if this is a no-op (no updates to be applied).
381
pub fn is_nop(&self) -> bool {
382
self.allocations.is_empty() && self.updates.is_empty()
383
}
384
385
/// Sets the clears_shared_cache flag for renderer-side sanity checks.
386
#[inline]
387
pub fn note_clear(&mut self) {
388
self.clears_shared_cache = true;
389
}
390
391
/// Pushes an update operation onto the list.
392
#[inline]
393
pub fn push_update(&mut self, id: CacheTextureId, update: TextureCacheUpdate) {
394
self.updates
395
.entry(id)
396
.or_default()
397
.push(update);
398
}
399
400
/// Sends a command to the Renderer to clear the portion of the shared region
401
/// we just freed. Used when the texture cache debugger is enabled.
402
#[cold]
403
pub fn push_debug_clear(
404
&mut self,
405
id: CacheTextureId,
406
origin: DeviceIntPoint,
407
width: i32,
408
height: i32,
409
layer_index: usize
410
) {
411
let size = DeviceIntSize::new(width, height);
412
let rect = DeviceIntRect::new(origin, size);
413
self.push_update(id, TextureCacheUpdate {
414
rect,
415
stride: None,
416
offset: 0,
417
layer_index: layer_index as i32,
418
format_override: None,
419
source: TextureUpdateSource::DebugClear,
420
});
421
}
422
423
424
/// Pushes an allocation operation onto the list.
425
pub fn push_alloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
426
debug_assert!(!self.allocations.iter().any(|x| x.id == id));
427
self.allocations.push(TextureCacheAllocation {
428
id,
429
kind: TextureCacheAllocationKind::Alloc(info),
430
});
431
}
432
433
/// Pushes a reallocation operation onto the list, potentially coalescing
434
/// with previous operations.
435
pub fn push_realloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
436
self.debug_assert_coalesced(id);
437
438
// Coallesce this realloc into a previous alloc or realloc, if available.
439
if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) {
440
match cur.kind {
441
TextureCacheAllocationKind::Alloc(ref mut i) => *i = info,
442
TextureCacheAllocationKind::Realloc(ref mut i) => *i = info,
443
TextureCacheAllocationKind::Reset(ref mut i) => *i = info,
444
TextureCacheAllocationKind::Free => panic!("Reallocating freed texture"),
445
}
446
return
447
}
448
449
self.allocations.push(TextureCacheAllocation {
450
id,
451
kind: TextureCacheAllocationKind::Realloc(info),
452
});
453
}
454
455
/// Pushes a reallocation operation onto the list, potentially coalescing
456
/// with previous operations.
457
pub fn push_reset(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
458
self.debug_assert_coalesced(id);
459
460
// Coallesce this realloc into a previous alloc or realloc, if available.
461
if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) {
462
match cur.kind {
463
TextureCacheAllocationKind::Alloc(ref mut i) => *i = info,
464
TextureCacheAllocationKind::Reset(ref mut i) => *i = info,
465
TextureCacheAllocationKind::Free => panic!("Resetting freed texture"),
466
TextureCacheAllocationKind::Realloc(_) => {
467
// Reset takes precedence over realloc
468
cur.kind = TextureCacheAllocationKind::Reset(info);
469
}
470
}
471
return
472
}
473
474
self.allocations.push(TextureCacheAllocation {
475
id,
476
kind: TextureCacheAllocationKind::Reset(info),
477
});
478
}
479
480
/// Pushes a free operation onto the list, potentially coalescing with
481
/// previous operations.
482
pub fn push_free(&mut self, id: CacheTextureId) {
483
self.debug_assert_coalesced(id);
484
485
// Drop any unapplied updates to the to-be-freed texture.
486
self.updates.remove(&id);
487
488
// Drop any allocations for it as well. If we happen to be allocating and
489
// freeing in the same batch, we can collapse them to a no-op.
490
let idx = self.allocations.iter().position(|x| x.id == id);
491
let removed_kind = idx.map(|i| self.allocations.remove(i).kind);
492
match removed_kind {
493
Some(TextureCacheAllocationKind::Alloc(..)) => { /* no-op! */ },
494
Some(TextureCacheAllocationKind::Free) => panic!("Double free"),
495
Some(TextureCacheAllocationKind::Realloc(..)) |
496
Some(TextureCacheAllocationKind::Reset(..)) |
497
None => {
498
self.allocations.push(TextureCacheAllocation {
499
id,
500
kind: TextureCacheAllocationKind::Free,
501
});
502
}
503
};
504
}
505
506
fn debug_assert_coalesced(&self, id: CacheTextureId) {
507
debug_assert!(
508
self.allocations.iter().filter(|x| x.id == id).count() <= 1,
509
"Allocations should have been coalesced",
510
);
511
}
512
}
513
514
/// A list of updates built by the render backend that should be applied
515
/// by the renderer thread.
516
pub struct ResourceUpdateList {
517
/// List of OS native surface create / destroy operations to apply.
518
pub native_surface_updates: Vec<NativeSurfaceOperation>,
519
520
/// Atomic set of texture cache updates to apply.
521
pub texture_updates: TextureUpdateList,
522
}
523
524
impl ResourceUpdateList {
525
/// Returns true if this update list has no effect.
526
pub fn is_nop(&self) -> bool {
527
self.texture_updates.is_nop() && self.native_surface_updates.is_empty()
528
}
529
}
530
531
/// Wraps a frame_builder::Frame, but conceptually could hold more information
532
pub struct RenderedDocument {
533
pub frame: Frame,
534
pub is_new_scene: bool,
535
}
536
537
pub enum DebugOutput {
538
FetchDocuments(String),
539
FetchClipScrollTree(String),
540
#[cfg(feature = "capture")]
541
SaveCapture(CaptureConfig, Vec<ExternalCaptureImage>),
542
#[cfg(feature = "replay")]
543
LoadCapture(PathBuf, Vec<PlainExternalImage>),
544
}
545
546
#[allow(dead_code)]
547
pub enum ResultMsg {
548
DebugCommand(DebugCommand),
549
DebugOutput(DebugOutput),
550
RefreshShader(PathBuf),
551
UpdateGpuCache(GpuCacheUpdateList),
552
UpdateResources {
553
resource_updates: ResourceUpdateList,
554
memory_pressure: bool,
555
},
556
PublishPipelineInfo(PipelineInfo),
557
PublishDocument(
558
DocumentId,
559
RenderedDocument,
560
ResourceUpdateList,
561
BackendProfileCounters,
562
),
563
AppendNotificationRequests(Vec<NotificationRequest>),
564
ForceRedraw,
565
}
566
567
#[derive(Clone, Debug)]
568
pub struct ResourceCacheError {
569
description: String,
570
}
571
572
impl ResourceCacheError {
573
pub fn new(description: String) -> ResourceCacheError {
574
ResourceCacheError {
575
description,
576
}
577
}
578
}
579
580
/// Primitive metadata we pass around in a bunch of places
581
#[derive(Copy, Clone, Debug)]
582
pub struct LayoutPrimitiveInfo {
583
/// NOTE: this is *ideally* redundant with the clip_rect
584
/// but that's an ongoing project, so for now it exists and is used :(
585
pub rect: LayoutRect,
586
pub clip_rect: LayoutRect,
587
pub flags: PrimitiveFlags,
588
pub hit_info: Option<ItemTag>,
589
}
590
591
impl LayoutPrimitiveInfo {
592
pub fn with_clip_rect(rect: LayoutRect, clip_rect: LayoutRect) -> Self {
593
Self {
594
rect,
595
clip_rect,
596
flags: PrimitiveFlags::default(),
597
hit_info: None,
598
}
599
}
600
}