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