azul_webrender/
internal_types.rs

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