zng_wgt_webrender_debug/
lib.rs

1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Webrender debug flags property for use with `zng-view` view-process.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9
10pub use webrender_api::DebugFlags;
11
12use zng_app::view_process::{VIEW_PROCESS, VIEW_PROCESS_INITED_EVENT};
13use zng_ext_window::WINDOWS;
14use zng_wgt::prelude::*;
15
16/// Sets the Webrender renderer debug flags and profiler UI for the current window.
17///
18/// Fails silently if the view-process does not implement the `"zng-view.webrender_debug"` extension.
19#[property(CONTEXT, default(RendererDebug::disabled()))]
20pub fn renderer_debug(child: impl IntoUiNode, debug: impl IntoVar<RendererDebug>) -> UiNode {
21    let debug = debug.into_var();
22    let mut send = false;
23    match_node(child, move |_, op| match op {
24        UiNodeOp::Init => {
25            WIDGET.sub_var(&debug).sub_event(&VIEW_PROCESS_INITED_EVENT);
26            send = debug.with(|d| !d.is_empty());
27        }
28        UiNodeOp::Event { update } => {
29            if VIEW_PROCESS_INITED_EVENT.has(update) {
30                send = true;
31                WIDGET.layout();
32            }
33        }
34        UiNodeOp::Update { .. } => {
35            if debug.is_new() {
36                send = true;
37                WIDGET.layout();
38            }
39        }
40        UiNodeOp::Layout { .. } => {
41            if std::mem::take(&mut send)
42                && let Some(ext_id) = VIEW_PROCESS.extension_id("zng-view.webrender_debug").ok().flatten()
43            {
44                debug.with(|d| match WINDOWS.view_render_extension(WINDOW.id(), ext_id, d) {
45                    Ok(()) => {}
46                    Err(e) => tracing::error!("{e}"),
47                });
48            }
49        }
50        _ => {}
51    })
52}
53
54/// Webrender renderer debug flags and profiler UI.
55#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
56#[non_exhaustive]
57pub struct RendererDebug {
58    /// Debug flags.
59    #[serde(with = "serde_debug_flags")]
60    pub flags: DebugFlags,
61    /// Profiler UI rendered when [`DebugFlags::PROFILER_DBG`] is set.
62    ///
63    /// # Syntax
64    ///
65    /// Comma-separated list of tokens with trailing and leading spaces trimmed.
66    /// Each tokens can be:
67    /// - A counter name with an optional prefix. The name corresponds to the displayed name.
68    ///   - By default (no prefix) the counter is shown as average + max over half a second.
69    ///   - With a '#' prefix the counter is shown as a graph.
70    ///   - With a '*' prefix the counter is shown as a change indicator.
71    ///   - Some special counters such as GPU time queries have specific visualizations ignoring prefixes.
72    /// - A preset name to append the preset to the UI.
73    /// - An empty token to insert a bit of vertical space.
74    /// - A '|' token to start a new column.
75    /// - A '_' token to start a new row.
76    ///
77    /// # Preset & Counter Names
78    ///
79    /// * `"Default"`: `"FPS,|,Slow indicators,_,Time graphs,|,Frame times, ,Transaction times, ,Frame stats, ,Memory, ,Interners,_,GPU time queries,_,Paint phase graph"`
80    /// * `"Compact"`: `"FPS, ,Frame times, ,Frame stats"`
81    ///
82    /// See the `webrender/src/profiler.rs` file for more details and more counter names.
83    pub profiler_ui: String,
84}
85impl Default for RendererDebug {
86    /// Disabled
87    fn default() -> Self {
88        Self::disabled()
89    }
90}
91impl RendererDebug {
92    /// Default mode, no debugging enabled.
93    pub fn disabled() -> Self {
94        Self {
95            flags: DebugFlags::empty(),
96            profiler_ui: String::new(),
97        }
98    }
99
100    /// Enable profiler UI rendering.
101    pub fn profiler(ui: impl Into<String>) -> Self {
102        Self {
103            flags: DebugFlags::PROFILER_DBG,
104            profiler_ui: ui.into(),
105        }
106    }
107
108    /// Custom flags with no UI string.
109    pub fn flags(flags: DebugFlags) -> Self {
110        Self {
111            flags,
112            profiler_ui: String::new(),
113        }
114    }
115
116    /// If no flag nor profiler UI are set.
117    pub fn is_empty(&self) -> bool {
118        self.flags.is_empty() && self.profiler_ui.is_empty()
119    }
120}
121impl_from_and_into_var! {
122    fn from(profiler_default: bool) -> RendererDebug {
123        if profiler_default {
124            Self::profiler("Default")
125        } else {
126            Self::disabled()
127        }
128    }
129
130    fn from(profiler: &str) -> RendererDebug {
131        Self::profiler(profiler)
132    }
133
134    fn from(profiler: Txt) -> RendererDebug {
135        Self::profiler(profiler)
136    }
137
138    fn from(flags: DebugFlags) -> RendererDebug {
139        Self::flags(flags)
140    }
141}
142
143/// Named DebugFlags in JSON serialization.
144mod serde_debug_flags {
145    use super::*;
146
147    use serde::*;
148
149    bitflags::bitflags! {
150        #[repr(C)]
151        #[derive(Default, Deserialize, Serialize)]
152        #[serde(transparent)]
153        struct DebugFlagsRef: u64 {
154            const PROFILER_DBG = DebugFlags::PROFILER_DBG.bits();
155            const RENDER_TARGET_DBG = DebugFlags::RENDER_TARGET_DBG.bits();
156            const TEXTURE_CACHE_DBG = DebugFlags::TEXTURE_CACHE_DBG.bits();
157            const GPU_TIME_QUERIES = DebugFlags::GPU_TIME_QUERIES.bits();
158            const GPU_SAMPLE_QUERIES = DebugFlags::GPU_SAMPLE_QUERIES.bits();
159            const DISABLE_BATCHING = DebugFlags::DISABLE_BATCHING.bits();
160            const EPOCHS = DebugFlags::EPOCHS.bits();
161            const ECHO_DRIVER_MESSAGES = DebugFlags::ECHO_DRIVER_MESSAGES.bits();
162            const SHOW_OVERDRAW = DebugFlags::SHOW_OVERDRAW.bits();
163            const GPU_CACHE_DBG = DebugFlags::GPU_CACHE_DBG.bits();
164            const TEXTURE_CACHE_DBG_CLEAR_EVICTED = DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED.bits();
165            const PICTURE_CACHING_DBG = DebugFlags::PICTURE_CACHING_DBG.bits();
166            const ZOOM_DBG = DebugFlags::ZOOM_DBG.bits();
167            const SMALL_SCREEN = DebugFlags::SMALL_SCREEN.bits();
168            const DISABLE_OPAQUE_PASS = DebugFlags::DISABLE_OPAQUE_PASS.bits();
169            const DISABLE_ALPHA_PASS = DebugFlags::DISABLE_ALPHA_PASS.bits();
170            const DISABLE_CLIP_MASKS = DebugFlags::DISABLE_CLIP_MASKS.bits();
171            const DISABLE_TEXT_PRIMS = DebugFlags::DISABLE_TEXT_PRIMS.bits();
172            const DISABLE_GRADIENT_PRIMS = DebugFlags::DISABLE_GRADIENT_PRIMS.bits();
173            const OBSCURE_IMAGES = DebugFlags::OBSCURE_IMAGES.bits();
174            const GLYPH_FLASHING = DebugFlags::GLYPH_FLASHING.bits();
175            const SMART_PROFILER = DebugFlags::SMART_PROFILER.bits();
176            const INVALIDATION_DBG = DebugFlags::INVALIDATION_DBG.bits();
177            const PROFILER_CAPTURE = DebugFlags::PROFILER_CAPTURE.bits();
178            const FORCE_PICTURE_INVALIDATION = DebugFlags::FORCE_PICTURE_INVALIDATION.bits();
179            const WINDOW_VISIBILITY_DBG = DebugFlags::WINDOW_VISIBILITY_DBG.bits();
180            const RESTRICT_BLOB_SIZE = DebugFlags::RESTRICT_BLOB_SIZE.bits();
181            const SURFACE_PROMOTION_LOGGING = DebugFlags::SURFACE_PROMOTION_LOGGING.bits();
182            const PICTURE_BORDERS = DebugFlags::PICTURE_BORDERS.bits();
183            const MISSING_SNAPSHOT_PANIC = DebugFlags::MISSING_SNAPSHOT_PANIC.bits();
184            const MISSING_SNAPSHOT_PINK = DebugFlags::MISSING_SNAPSHOT_PINK.bits();
185            const HIGHLIGHT_BACKDROP_FILTERS = DebugFlags::HIGHLIGHT_BACKDROP_FILTERS.bits();
186        }
187    }
188    impl From<DebugFlagsRef> for DebugFlags {
189        fn from(value: DebugFlagsRef) -> Self {
190            DebugFlags::from_bits(value.bits()).unwrap()
191        }
192    }
193    impl From<DebugFlags> for DebugFlagsRef {
194        fn from(value: DebugFlags) -> Self {
195            DebugFlagsRef::from_bits(value.bits()).unwrap()
196        }
197    }
198
199    pub fn serialize<S: serde::Serializer>(flags: &DebugFlags, serializer: S) -> Result<S::Ok, S::Error> {
200        DebugFlagsRef::from(*flags).serialize(serializer)
201    }
202
203    pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<DebugFlags, D::Error> {
204        DebugFlagsRef::deserialize(deserializer).map(Into::into)
205    }
206}