Implemented renderer extensions.
This commit is contained in:
parent
740f7700c3
commit
66fd1bad91
|
@ -54,7 +54,4 @@
|
|||
|
||||
* Implement window extension.
|
||||
- Access to raw handle and ability to spawn data that lives for the duration of the window.
|
||||
* Implement renderer extension.
|
||||
- Affect renderer creation.
|
||||
- Integrate with display item extensions.
|
||||
- Implement use case, webrender blob renderer.
|
||||
- Support window targeted commands in the API, not just as a parameter.
|
||||
|
|
|
@ -57,7 +57,7 @@ fn crash_respawn() -> impl UiNode {
|
|||
if let Ok(ext) = VIEW_PROCESS.extensions() {
|
||||
let crash_ext = zero_ui::core::app::view_process::ApiExtensionName::new("zero-ui.examples.respawn.crash").unwrap();
|
||||
if let Some(key) = ext.key(&crash_ext) {
|
||||
VIEW_PROCESS.extension::<_, ()>(key, &()).unwrap().unwrap();
|
||||
VIEW_PROCESS.app_extension::<_, ()>(key, &()).unwrap().unwrap();
|
||||
} else {
|
||||
tracing::error!(r#"extension "zero-ui-view.crash" unavailable"#)
|
||||
}
|
||||
|
|
|
@ -144,7 +144,8 @@ fn test_view_api_types() {
|
|||
use zero_ui::core::{
|
||||
app::view_process::zero_ui_view_api::EventFrameRendered,
|
||||
mouse::{MouseScrollDelta, TouchForce, TouchPhase},
|
||||
render::{webrender_api::DebugFlags, FrameId, RendererDebug},
|
||||
render::{webrender_api::DebugFlags, FrameId},
|
||||
window::RendererDebug,
|
||||
};
|
||||
|
||||
test_config!(FrameId::first().next().next_update().next());
|
||||
|
|
|
@ -8,9 +8,9 @@ use std::{
|
|||
|
||||
pub use zero_ui_view_api::{
|
||||
self, bytes_channel, AnimationsConfig, ApiExtensionName, ApiExtensionNameError, ApiExtensionRecvError, ApiExtensions, ColorScheme,
|
||||
CursorIcon, Event, EventCause, FocusIndicator, FrameRequest, FrameUpdateRequest, FrameWaitId, HeadlessOpenData, HeadlessRequest,
|
||||
ImageDataFormat, ImageDownscale, ImagePpi, ImageRequest, IpcBytes, IpcBytesReceiver, IpcBytesSender, LocaleConfig, MonitorInfo,
|
||||
RenderMode, VideoMode, ViewProcessGen, ViewProcessOffline, WindowRequest, WindowState, WindowStateAll,
|
||||
CursorIcon, Event, EventCause, ExtensionPayload, FocusIndicator, FrameRequest, FrameUpdateRequest, FrameWaitId, HeadlessOpenData,
|
||||
HeadlessRequest, ImageDataFormat, ImageDownscale, ImagePpi, ImageRequest, IpcBytes, IpcBytesReceiver, IpcBytesSender, LocaleConfig,
|
||||
MonitorInfo, RenderMode, VideoMode, ViewProcessGen, ViewProcessOffline, WindowRequest, WindowState, WindowStateAll,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -28,8 +28,7 @@ use zero_ui_view_api::{
|
|||
webrender_api::{
|
||||
FontInstanceKey, FontInstanceOptions, FontInstancePlatformOptions, FontKey, FontVariation, IdNamespace, ImageKey, PipelineId,
|
||||
},
|
||||
Controller, DeviceId as ApiDeviceId, ExtensionPayload, ImageId, ImageLoadedData, KeyRepeatConfig, MonitorId as ApiMonitorId,
|
||||
WindowId as ApiWindowId,
|
||||
Controller, DeviceId as ApiDeviceId, ImageId, ImageLoadedData, KeyRepeatConfig, MonitorId as ApiMonitorId, WindowId as ApiWindowId,
|
||||
};
|
||||
|
||||
use super::{App, AppId};
|
||||
|
@ -204,18 +203,18 @@ impl VIEW_PROCESS {
|
|||
}
|
||||
|
||||
/// Call an extension with custom encoded payload.
|
||||
pub fn extension_raw(&self, extension_key: usize, extension_request: ExtensionPayload) -> Result<ExtensionPayload> {
|
||||
self.write().process.extension(extension_key, extension_request)
|
||||
pub fn app_extension_raw(&self, extension_key: usize, extension_request: ExtensionPayload) -> Result<ExtensionPayload> {
|
||||
self.write().process.app_extension(extension_key, extension_request)
|
||||
}
|
||||
|
||||
/// Call an extension with payload `request`.
|
||||
pub fn extension<I, O>(&self, extension_key: usize, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
|
||||
pub fn app_extension<I, O>(&self, extension_key: usize, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
|
||||
where
|
||||
I: serde::Serialize,
|
||||
O: serde::de::DeserializeOwned,
|
||||
{
|
||||
let payload = ExtensionPayload::serialize(&request).unwrap();
|
||||
let response = self.write().process.extension(extension_key, payload)?;
|
||||
let response = self.write().process.app_extension(extension_key, payload)?;
|
||||
Ok(response.deserialize::<O>())
|
||||
}
|
||||
|
||||
|
@ -644,19 +643,6 @@ impl ViewWindow {
|
|||
self.0.call(|id, p| p.set_focus_indicator(id, indicator))
|
||||
}
|
||||
|
||||
/// Call an extension with payload `(view_window_id, request)`.
|
||||
pub fn extension<I, O>(&self, extension_key: usize, request: I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
|
||||
where
|
||||
I: serde::Serialize,
|
||||
O: serde::de::DeserializeOwned,
|
||||
{
|
||||
self.0.call(|id, p| {
|
||||
let payload = ExtensionPayload::serialize(&(id, request)).unwrap();
|
||||
let response = p.extension(extension_key, payload)?;
|
||||
Ok(response.deserialize::<O>())
|
||||
})
|
||||
}
|
||||
|
||||
/// Drop `self`.
|
||||
pub fn close(self) {
|
||||
drop(self)
|
||||
|
@ -877,23 +863,6 @@ impl ViewRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Call an extension with payload `(view_window_id, request)`.
|
||||
pub fn extension<I, O>(&self, extension_key: usize, request: I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
|
||||
where
|
||||
I: serde::Serialize,
|
||||
O: serde::de::DeserializeOwned,
|
||||
{
|
||||
if let Some(w) = self.0.upgrade() {
|
||||
w.call(|id, p| {
|
||||
let payload = ExtensionPayload::serialize(&(id, request)).unwrap();
|
||||
let response = p.extension(extension_key, payload)?;
|
||||
Ok(response.deserialize::<O>())
|
||||
})
|
||||
} else {
|
||||
Err(ViewProcessOffline)
|
||||
}
|
||||
}
|
||||
|
||||
/// Render a new frame.
|
||||
pub fn render(&self, frame: FrameRequest) -> Result<()> {
|
||||
let _s = tracing::debug_span!("ViewRenderer.render").entered();
|
||||
|
@ -919,6 +888,25 @@ impl ViewRenderer {
|
|||
Err(ViewProcessOffline)
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a render extension with custom encoded payload.
|
||||
pub fn render_extension_raw(&self, extension_key: usize, request: ExtensionPayload) -> Result<ExtensionPayload> {
|
||||
if let Some(w) = self.0.upgrade() {
|
||||
w.call(|id, p| p.render_extension(id, extension_key, request))
|
||||
} else {
|
||||
Err(ViewProcessOffline)
|
||||
}
|
||||
}
|
||||
|
||||
/// Call an extension with payload `(view_window_id, request)`.
|
||||
pub fn render_extension<I, O>(&self, extension_key: usize, request: &I) -> Result<std::result::Result<O, ApiExtensionRecvError>>
|
||||
where
|
||||
I: serde::Serialize,
|
||||
O: serde::de::DeserializeOwned,
|
||||
{
|
||||
let r = self.render_extension_raw(extension_key, ExtensionPayload::serialize(&request).unwrap())?;
|
||||
Ok(r.deserialize())
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to an image loading or loaded in the View Process.
|
||||
|
|
|
@ -19,9 +19,7 @@ use std::{marker::PhantomData, mem, sync::Arc};
|
|||
|
||||
use rayon::prelude::*;
|
||||
use webrender_api::{FontRenderMode, PipelineId};
|
||||
pub use zero_ui_view_api::{
|
||||
webrender_api, DisplayListBuilder, FilterOp, FrameId, FrameValue, FrameValueUpdate, RenderMode, RendererDebug, ReuseRange,
|
||||
};
|
||||
pub use zero_ui_view_api::{webrender_api, DisplayListBuilder, FilterOp, FrameId, FrameValue, FrameValueUpdate, RenderMode, ReuseRange};
|
||||
use zero_ui_view_api::{
|
||||
webrender_api::{DynamicProperties, GlyphInstance, GlyphOptions, MixBlendMode, SpatialTreeItemKey},
|
||||
DisplayList, ExtensionPayload, ReuseStart,
|
||||
|
@ -2498,37 +2496,3 @@ impl_from_and_into_var! {
|
|||
if enabled { FontSynthesis::ENABLED } else { FontSynthesis::DISABLED }
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_and_into_var! {
|
||||
fn from(profiler: crate::text::Txt) -> RendererDebug {
|
||||
RendererDebug::profiler(profiler)
|
||||
}
|
||||
}
|
||||
impl var::IntoVar<RendererDebug> for bool {
|
||||
type Var = var::LocalVar<RendererDebug>;
|
||||
|
||||
fn into_var(self) -> Self::Var {
|
||||
var::LocalVar(self.into())
|
||||
}
|
||||
}
|
||||
impl<'a> var::IntoVar<RendererDebug> for &'a str {
|
||||
type Var = var::LocalVar<RendererDebug>;
|
||||
|
||||
fn into_var(self) -> Self::Var {
|
||||
var::LocalVar(self.into())
|
||||
}
|
||||
}
|
||||
impl var::IntoVar<RendererDebug> for String {
|
||||
type Var = var::LocalVar<RendererDebug>;
|
||||
|
||||
fn into_var(self) -> Self::Var {
|
||||
var::LocalVar(self.into())
|
||||
}
|
||||
}
|
||||
impl var::IntoVar<RendererDebug> for webrender_api::DebugFlags {
|
||||
type Var = var::LocalVar<RendererDebug>;
|
||||
|
||||
fn into_var(self) -> Self::Var {
|
||||
var::LocalVar(self.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -416,17 +416,13 @@ impl HeadedCtrl {
|
|||
self.vars.0.actual_color_scheme.set_ne(scheme);
|
||||
}
|
||||
|
||||
if let Some(dbg) = self.vars.renderer_debug().get_new() {
|
||||
self.vars.renderer_debug().with_new(|dbg| {
|
||||
if let Some(view) = &self.window {
|
||||
if let Ok(ext) = VIEW_PROCESS.extensions() {
|
||||
if let Some(key) = ext.key(&ApiExtensionName::new("zero-ui-view.set_webrender_debug").unwrap()) {
|
||||
let _ = view.renderer().extension::<_, ()>(key, dbg);
|
||||
} else {
|
||||
tracing::error!(r#"extension "zero-ui-view.set_webrender_debug" unavailable"#)
|
||||
}
|
||||
if let Some(key) = dbg.extension_key() {
|
||||
let _ = view.renderer().render_extension::<_, ()>(key, dbg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.content.update(update_widgets);
|
||||
|
@ -795,10 +791,15 @@ impl HeadedCtrl {
|
|||
transparent: self.transparent,
|
||||
capture_mode: matches!(self.vars.frame_capture_mode().get(), FrameCaptureMode::All),
|
||||
render_mode: self.render_mode.unwrap_or_else(|| WINDOWS.default_render_mode().get()),
|
||||
renderer_debug: self.vars.renderer_debug().get(),
|
||||
|
||||
focus: self.start_focused,
|
||||
focus_indicator: self.vars.focus_indicator().get(),
|
||||
|
||||
extensions: {
|
||||
let mut exts = vec![];
|
||||
self.vars.renderer_debug().with(|d| d.push_extension(&mut exts));
|
||||
exts
|
||||
},
|
||||
};
|
||||
|
||||
match VIEW_PROCESS.open_window(request) {
|
||||
|
@ -898,10 +899,15 @@ impl HeadedCtrl {
|
|||
transparent: self.transparent,
|
||||
capture_mode: matches!(self.vars.frame_capture_mode().get(), FrameCaptureMode::All),
|
||||
render_mode: self.render_mode.unwrap_or_else(|| WINDOWS.default_render_mode().get()),
|
||||
renderer_debug: self.vars.renderer_debug().get(),
|
||||
|
||||
focus: WINDOWS.is_focused(WINDOW.id()).unwrap_or(false),
|
||||
focus_indicator: self.vars.focus_indicator().get(),
|
||||
|
||||
extensions: {
|
||||
let mut exts = vec![];
|
||||
self.vars.renderer_debug().with(|d| d.push_extension(&mut exts));
|
||||
exts
|
||||
},
|
||||
};
|
||||
|
||||
match VIEW_PROCESS.open_window(request) {
|
||||
|
@ -1084,17 +1090,14 @@ impl HeadlessWithRendererCtrl {
|
|||
if update_parent(&mut self.actual_parent, &self.vars) || self.var_bindings.is_dummy() {
|
||||
self.var_bindings = update_headless_vars(self.headless_monitor.scale_factor, &self.vars);
|
||||
}
|
||||
if let Some(dbg) = self.vars.renderer_debug().get_new() {
|
||||
|
||||
self.vars.renderer_debug().with_new(|dbg| {
|
||||
if let Some(view) = &self.surface {
|
||||
if let Ok(ext) = VIEW_PROCESS.extensions() {
|
||||
if let Some(key) = ext.key(&ApiExtensionName::new("zero-ui-view.set_webrender_debug").unwrap()) {
|
||||
let _ = view.renderer().extension::<_, ()>(key, dbg);
|
||||
} else {
|
||||
tracing::error!(r#"extension "zero-ui-view.set_webrender_debug" unavailable"#)
|
||||
}
|
||||
if let Some(key) = dbg.extension_key() {
|
||||
let _ = view.renderer().render_extension::<_, ()>(key, dbg);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self.content.update(update_widgets);
|
||||
}
|
||||
|
@ -1203,7 +1206,11 @@ impl HeadlessWithRendererCtrl {
|
|||
scale_factor: scale_factor.0,
|
||||
size,
|
||||
render_mode,
|
||||
renderer_debug: self.vars.renderer_debug().get(),
|
||||
extensions: {
|
||||
let mut exts = vec![];
|
||||
self.vars.renderer_debug().with(|d| d.push_extension(&mut exts));
|
||||
exts
|
||||
},
|
||||
});
|
||||
|
||||
match r {
|
||||
|
|
|
@ -5,6 +5,8 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use zero_ui_view_api::webrender_api::DebugFlags;
|
||||
|
||||
use crate::{
|
||||
crate_util::{IdSet, NameIdMap},
|
||||
event::{event, event_args},
|
||||
|
@ -1107,3 +1109,169 @@ impl crate::var::IntoVar<Option<WindowId>> for WindowId {
|
|||
}
|
||||
}
|
||||
impl crate::var::IntoValue<Option<WindowId>> for WindowId {}
|
||||
|
||||
/// Webrender renderer debug flags and profiler UI.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct RendererDebug {
|
||||
/// Debug flags.
|
||||
#[serde(with = "serde_debug_flags")]
|
||||
pub flags: DebugFlags,
|
||||
/// Profiler UI rendered when [`DebugFlags::PROFILER_DBG`] is set.
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// Comma-separated list of of tokens with trailing and leading spaces trimmed.
|
||||
/// Each tokens can be:
|
||||
/// - A counter name with an optional prefix. The name corresponds to the displayed name.
|
||||
/// - By default (no prefix) the counter is shown as average + max over half a second.
|
||||
/// - With a '#' prefix the counter is shown as a graph.
|
||||
/// - With a '*' prefix the counter is shown as a change indicator.
|
||||
/// - Some special counters such as GPU time queries have specific visualizations ignoring prefixes.
|
||||
/// - A preset name to append the preset to the UI.
|
||||
/// - An empty token to insert a bit of vertical space.
|
||||
/// - A '|' token to start a new column.
|
||||
/// - A '_' token to start a new row.
|
||||
///
|
||||
/// # Preset & Counter Names
|
||||
///
|
||||
/// * `"Default"`: `"FPS,|,Slow indicators,_,Time graphs,|,Frame times, ,Transaction times, ,Frame stats, ,Memory, ,Interners,_,GPU time queries,_,Paint phase graph"`
|
||||
/// * `"Compact"`: `"FPS, ,Frame times, ,Frame stats"`
|
||||
///
|
||||
/// See the `webrender/src/profiler.rs` file for more details and more counter names.
|
||||
pub profiler_ui: String,
|
||||
}
|
||||
impl Default for RendererDebug {
|
||||
/// Disabled
|
||||
fn default() -> Self {
|
||||
Self::disabled()
|
||||
}
|
||||
}
|
||||
impl RendererDebug {
|
||||
/// Default mode, no debugging enabled.
|
||||
pub fn disabled() -> Self {
|
||||
Self {
|
||||
flags: DebugFlags::empty(),
|
||||
profiler_ui: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable profiler UI rendering.
|
||||
pub fn profiler(ui: impl Into<String>) -> Self {
|
||||
Self {
|
||||
flags: DebugFlags::PROFILER_DBG,
|
||||
profiler_ui: ui.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom flags with no UI string.
|
||||
pub fn flags(flags: DebugFlags) -> Self {
|
||||
Self {
|
||||
flags,
|
||||
profiler_ui: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// If no flag nor profiler UI are set.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.flags.is_empty() && self.profiler_ui.is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn extension_key(&self) -> Option<usize> {
|
||||
if let Ok(ext) = crate::app::view_process::VIEW_PROCESS.extensions() {
|
||||
let name = crate::app::view_process::ApiExtensionName::new("zero-ui-view.webrender_debug").unwrap();
|
||||
ext.key(&name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn push_extension(&self, exts: &mut Vec<(usize, zero_ui_view_api::ExtensionPayload)>) {
|
||||
if !self.is_empty() {
|
||||
if let Some(key) = self.extension_key() {
|
||||
exts.push((key, crate::app::view_process::ExtensionPayload::serialize(self).unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl_from_and_into_var! {
|
||||
fn from(profiler_default: bool) -> RendererDebug {
|
||||
if profiler_default {
|
||||
Self::profiler("Default")
|
||||
} else {
|
||||
Self::disabled()
|
||||
}
|
||||
}
|
||||
|
||||
fn from(profiler: &str) -> RendererDebug {
|
||||
Self::profiler(profiler)
|
||||
}
|
||||
|
||||
fn from(profiler: Txt) -> RendererDebug {
|
||||
Self::profiler(profiler)
|
||||
}
|
||||
|
||||
fn from(flags: DebugFlags) -> RendererDebug {
|
||||
Self::flags(flags)
|
||||
}
|
||||
}
|
||||
|
||||
/// Named DebugFlags in JSON serialization.
|
||||
mod serde_debug_flags {
|
||||
use super::*;
|
||||
|
||||
use serde::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct DebugFlagsRef: u32 {
|
||||
const PROFILER_DBG = DebugFlags::PROFILER_DBG.bits();
|
||||
const RENDER_TARGET_DBG = DebugFlags::RENDER_TARGET_DBG.bits();
|
||||
const TEXTURE_CACHE_DBG = DebugFlags::TEXTURE_CACHE_DBG.bits();
|
||||
const GPU_TIME_QUERIES = DebugFlags::GPU_TIME_QUERIES.bits();
|
||||
const GPU_SAMPLE_QUERIES = DebugFlags::GPU_SAMPLE_QUERIES.bits();
|
||||
const DISABLE_BATCHING = DebugFlags::DISABLE_BATCHING.bits();
|
||||
const EPOCHS = DebugFlags::EPOCHS.bits();
|
||||
const ECHO_DRIVER_MESSAGES = DebugFlags::ECHO_DRIVER_MESSAGES.bits();
|
||||
const SHOW_OVERDRAW = DebugFlags::SHOW_OVERDRAW.bits();
|
||||
const GPU_CACHE_DBG = DebugFlags::GPU_CACHE_DBG.bits();
|
||||
const TEXTURE_CACHE_DBG_CLEAR_EVICTED = DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED.bits();
|
||||
const PICTURE_CACHING_DBG = DebugFlags::PICTURE_CACHING_DBG.bits();
|
||||
const PRIMITIVE_DBG = DebugFlags::PRIMITIVE_DBG.bits();
|
||||
const ZOOM_DBG = DebugFlags::ZOOM_DBG.bits();
|
||||
const SMALL_SCREEN = DebugFlags::SMALL_SCREEN.bits();
|
||||
const DISABLE_OPAQUE_PASS = DebugFlags::DISABLE_OPAQUE_PASS.bits();
|
||||
const DISABLE_ALPHA_PASS = DebugFlags::DISABLE_ALPHA_PASS.bits();
|
||||
const DISABLE_CLIP_MASKS = DebugFlags::DISABLE_CLIP_MASKS.bits();
|
||||
const DISABLE_TEXT_PRIMS = DebugFlags::DISABLE_TEXT_PRIMS.bits();
|
||||
const DISABLE_GRADIENT_PRIMS = DebugFlags::DISABLE_GRADIENT_PRIMS.bits();
|
||||
const OBSCURE_IMAGES = DebugFlags::OBSCURE_IMAGES.bits();
|
||||
const GLYPH_FLASHING = DebugFlags::GLYPH_FLASHING.bits();
|
||||
const SMART_PROFILER = DebugFlags::SMART_PROFILER.bits();
|
||||
const INVALIDATION_DBG = DebugFlags::INVALIDATION_DBG.bits();
|
||||
const PROFILER_CAPTURE = DebugFlags::PROFILER_CAPTURE.bits();
|
||||
const FORCE_PICTURE_INVALIDATION = DebugFlags::FORCE_PICTURE_INVALIDATION.bits();
|
||||
const WINDOW_VISIBILITY_DBG = DebugFlags::WINDOW_VISIBILITY_DBG.bits();
|
||||
const RESTRICT_BLOB_SIZE = DebugFlags::RESTRICT_BLOB_SIZE.bits();
|
||||
}
|
||||
}
|
||||
impl From<DebugFlagsRef> for DebugFlags {
|
||||
fn from(value: DebugFlagsRef) -> Self {
|
||||
DebugFlags::from_bits(value.bits()).unwrap()
|
||||
}
|
||||
}
|
||||
impl From<DebugFlags> for DebugFlagsRef {
|
||||
fn from(value: DebugFlags) -> Self {
|
||||
DebugFlagsRef::from_bits(value.bits()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S: serde::Serializer>(flags: &DebugFlags, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
DebugFlagsRef::from(*flags).serialize(serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<DebugFlags, D::Error> {
|
||||
DebugFlagsRef::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
context::WINDOW,
|
||||
crate_util::IdSet,
|
||||
image::Img,
|
||||
render::{RenderMode, RendererDebug},
|
||||
render::RenderMode,
|
||||
text::{ToText, Txt},
|
||||
units::*,
|
||||
var::*,
|
||||
|
|
|
@ -455,7 +455,7 @@ declare_api! {
|
|||
/// The extensions do not change for the duration of the view process.
|
||||
///
|
||||
/// Note that this represents both command extensions, and display item extensions. Command
|
||||
/// extensions are called using [`Api::extension`], display item extensions are inserted using
|
||||
/// extensions are called using [`Api::app_extension`] and [`Api::render_extension`], display item extensions are inserted using
|
||||
/// [`DisplayListBuilder::push_extension`].
|
||||
pub fn extensions(&mut self) -> ApiExtensions;
|
||||
|
||||
|
@ -467,7 +467,13 @@ declare_api! {
|
|||
/// Returns the extension response or [`ExtensionPayload::unknown_extension`] if the `extension_key` is
|
||||
/// not on the list, or [`ExtensionPayload::invalid_request`] if the `extension_request` is not in a
|
||||
/// format expected by the extension.
|
||||
pub fn extension(&mut self, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload;
|
||||
pub fn app_extension(&mut self, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload;
|
||||
|
||||
/// Call the API extension.
|
||||
///
|
||||
/// This is is similar to [`Api::app_extension`], but is targeting the instance of an extension associated
|
||||
/// with the `id` renderer.
|
||||
pub fn render_extension(&mut self, id: WindowId, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1331,92 +1331,6 @@ impl std::error::Error for ViewProcessOffline {}
|
|||
/// View Process IPC result.
|
||||
pub(crate) type VpResult<T> = std::result::Result<T, ViewProcessOffline>;
|
||||
|
||||
/// Webrender renderer debug flags and profiler UI.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct RendererDebug {
|
||||
/// Debug flags.
|
||||
#[serde(with = "serde_debug_flags")]
|
||||
pub flags: DebugFlags,
|
||||
/// Profiler UI rendered when [`DebugFlags::PROFILER_DBG`] is set.
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// Comma-separated list of of tokens with trailing and leading spaces trimmed.
|
||||
/// Each tokens can be:
|
||||
/// - A counter name with an optional prefix. The name corresponds to the displayed name.
|
||||
/// - By default (no prefix) the counter is shown as average + max over half a second.
|
||||
/// - With a '#' prefix the counter is shown as a graph.
|
||||
/// - With a '*' prefix the counter is shown as a change indicator.
|
||||
/// - Some special counters such as GPU time queries have specific visualizations ignoring prefixes.
|
||||
/// - A preset name to append the preset to the UI.
|
||||
/// - An empty token to insert a bit of vertical space.
|
||||
/// - A '|' token to start a new column.
|
||||
/// - A '_' token to start a new row.
|
||||
///
|
||||
/// # Preset & Counter Names
|
||||
///
|
||||
/// * `"Default"`: `"FPS,|,Slow indicators,_,Time graphs,|,Frame times, ,Transaction times, ,Frame stats, ,Memory, ,Interners,_,GPU time queries,_,Paint phase graph"`
|
||||
/// * `"Compact"`: `"FPS, ,Frame times, ,Frame stats"`
|
||||
///
|
||||
/// See the `webrender/src/profiler.rs` file for more details and more counter names.
|
||||
pub profiler_ui: String,
|
||||
}
|
||||
impl Default for RendererDebug {
|
||||
/// Disabled
|
||||
fn default() -> Self {
|
||||
Self::disabled()
|
||||
}
|
||||
}
|
||||
impl RendererDebug {
|
||||
/// Default mode, no debugging enabled.
|
||||
pub fn disabled() -> Self {
|
||||
Self {
|
||||
flags: DebugFlags::empty(),
|
||||
profiler_ui: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable profiler UI rendering.
|
||||
pub fn profiler(ui: impl Into<String>) -> Self {
|
||||
Self {
|
||||
flags: DebugFlags::PROFILER_DBG,
|
||||
profiler_ui: ui.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom flags with no UI string.
|
||||
pub fn flags(flags: DebugFlags) -> Self {
|
||||
Self {
|
||||
flags,
|
||||
profiler_ui: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<bool> for RendererDebug {
|
||||
fn from(profiler_default: bool) -> Self {
|
||||
if profiler_default {
|
||||
Self::profiler("Default")
|
||||
} else {
|
||||
Self::disabled()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> From<&'a str> for RendererDebug {
|
||||
fn from(profiler: &'a str) -> Self {
|
||||
Self::profiler(profiler)
|
||||
}
|
||||
}
|
||||
impl From<String> for RendererDebug {
|
||||
fn from(profiler: String) -> Self {
|
||||
Self::profiler(profiler)
|
||||
}
|
||||
}
|
||||
impl From<DebugFlags> for RendererDebug {
|
||||
fn from(flags: DebugFlags) -> Self {
|
||||
Self::flags(flags)
|
||||
}
|
||||
}
|
||||
|
||||
/// Data for rendering a new frame.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FrameRequest {
|
||||
|
@ -1591,15 +1505,15 @@ pub struct WindowRequest {
|
|||
/// Render mode preference for this window.
|
||||
pub render_mode: RenderMode,
|
||||
|
||||
/// Renderer debug flags and UI.
|
||||
pub renderer_debug: RendererDebug,
|
||||
|
||||
/// Focus request indicator on init.
|
||||
pub focus_indicator: Option<FocusIndicator>,
|
||||
|
||||
/// Ensures the window is focused after open, if not set the initial focus is decided by
|
||||
/// the windows manager, usually focusing the new window only if the process that causes the window has focus.
|
||||
pub focus: bool,
|
||||
|
||||
/// Config for renderer extensions.
|
||||
pub extensions: Vec<(usize, ExtensionPayload)>,
|
||||
}
|
||||
impl WindowRequest {
|
||||
/// Corrects invalid values if [`kiosk`] is `true`.
|
||||
|
@ -1784,8 +1698,8 @@ pub struct HeadlessRequest {
|
|||
/// Render mode preference for this headless surface.
|
||||
pub render_mode: RenderMode,
|
||||
|
||||
/// Renderer debug flags and UI.
|
||||
pub renderer_debug: RendererDebug,
|
||||
/// Config for renderer extensions.
|
||||
pub extensions: Vec<(usize, ExtensionPayload)>,
|
||||
}
|
||||
|
||||
/// Information about a monitor screen.
|
||||
|
@ -2127,65 +2041,6 @@ pub enum FocusIndicator {
|
|||
Info,
|
||||
}
|
||||
|
||||
/// Named DebugFlags in JSON serialization.
|
||||
mod serde_debug_flags {
|
||||
use super::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct DebugFlagsRef: u32 {
|
||||
const PROFILER_DBG = DebugFlags::PROFILER_DBG.bits();
|
||||
const RENDER_TARGET_DBG = DebugFlags::RENDER_TARGET_DBG.bits();
|
||||
const TEXTURE_CACHE_DBG = DebugFlags::TEXTURE_CACHE_DBG.bits();
|
||||
const GPU_TIME_QUERIES = DebugFlags::GPU_TIME_QUERIES.bits();
|
||||
const GPU_SAMPLE_QUERIES = DebugFlags::GPU_SAMPLE_QUERIES.bits();
|
||||
const DISABLE_BATCHING = DebugFlags::DISABLE_BATCHING.bits();
|
||||
const EPOCHS = DebugFlags::EPOCHS.bits();
|
||||
const ECHO_DRIVER_MESSAGES = DebugFlags::ECHO_DRIVER_MESSAGES.bits();
|
||||
const SHOW_OVERDRAW = DebugFlags::SHOW_OVERDRAW.bits();
|
||||
const GPU_CACHE_DBG = DebugFlags::GPU_CACHE_DBG.bits();
|
||||
const TEXTURE_CACHE_DBG_CLEAR_EVICTED = DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED.bits();
|
||||
const PICTURE_CACHING_DBG = DebugFlags::PICTURE_CACHING_DBG.bits();
|
||||
const PRIMITIVE_DBG = DebugFlags::PRIMITIVE_DBG.bits();
|
||||
const ZOOM_DBG = DebugFlags::ZOOM_DBG.bits();
|
||||
const SMALL_SCREEN = DebugFlags::SMALL_SCREEN.bits();
|
||||
const DISABLE_OPAQUE_PASS = DebugFlags::DISABLE_OPAQUE_PASS.bits();
|
||||
const DISABLE_ALPHA_PASS = DebugFlags::DISABLE_ALPHA_PASS.bits();
|
||||
const DISABLE_CLIP_MASKS = DebugFlags::DISABLE_CLIP_MASKS.bits();
|
||||
const DISABLE_TEXT_PRIMS = DebugFlags::DISABLE_TEXT_PRIMS.bits();
|
||||
const DISABLE_GRADIENT_PRIMS = DebugFlags::DISABLE_GRADIENT_PRIMS.bits();
|
||||
const OBSCURE_IMAGES = DebugFlags::OBSCURE_IMAGES.bits();
|
||||
const GLYPH_FLASHING = DebugFlags::GLYPH_FLASHING.bits();
|
||||
const SMART_PROFILER = DebugFlags::SMART_PROFILER.bits();
|
||||
const INVALIDATION_DBG = DebugFlags::INVALIDATION_DBG.bits();
|
||||
const PROFILER_CAPTURE = DebugFlags::PROFILER_CAPTURE.bits();
|
||||
const FORCE_PICTURE_INVALIDATION = DebugFlags::FORCE_PICTURE_INVALIDATION.bits();
|
||||
const WINDOW_VISIBILITY_DBG = DebugFlags::WINDOW_VISIBILITY_DBG.bits();
|
||||
const RESTRICT_BLOB_SIZE = DebugFlags::RESTRICT_BLOB_SIZE.bits();
|
||||
}
|
||||
}
|
||||
impl From<DebugFlagsRef> for DebugFlags {
|
||||
fn from(value: DebugFlagsRef) -> Self {
|
||||
DebugFlags::from_bits(value.bits()).unwrap()
|
||||
}
|
||||
}
|
||||
impl From<DebugFlags> for DebugFlagsRef {
|
||||
fn from(value: DebugFlags) -> Self {
|
||||
DebugFlagsRef::from_bits(value.bits()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<S: serde::Serializer>(flags: &DebugFlags, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
DebugFlagsRef::from(*flags).serialize(serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<DebugFlags, D::Error> {
|
||||
DebugFlagsRef::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom serialized data, in a format defined by the extension.
|
||||
///
|
||||
/// Note that the bytes here should represent a serialized small `struct` only, you
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
use std::any::Any;
|
||||
|
||||
use webrender::{DebugFlags, RenderApi};
|
||||
use zero_ui_view_api::{ApiExtensionName, ApiExtensions, ExtensionPayload};
|
||||
|
||||
/// The extension API.
|
||||
|
@ -14,17 +15,69 @@ pub trait ViewExtension: Send + Any {
|
|||
/// Unique name and version of this extension.
|
||||
fn name(&self) -> &ApiExtensionName;
|
||||
|
||||
/// Run the extension as a command.
|
||||
/// Run the extension as an app level command.
|
||||
fn command(&mut self, request: ExtensionPayload) -> Option<ExtensionPayload> {
|
||||
let _ = request;
|
||||
None
|
||||
}
|
||||
|
||||
/// Create a [`RendererExtension`] for a new renderer instance.
|
||||
fn renderer(&mut self) -> Option<Box<dyn RendererExtension>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a view extension associated with a renderer instance.
|
||||
pub trait RendererExtension: Any {
|
||||
/// Edit options for the new renderer.
|
||||
///
|
||||
/// The `cfg` is the raw config send with the renderer creation request addressing this extension. Note
|
||||
/// that this extension will participate in the renderer creation even if there is no config for it.
|
||||
fn configure(&mut self, cfg: Option<ExtensionPayload>, opts: &mut webrender::WebRenderOptions) {
|
||||
let _ = (cfg, opts);
|
||||
}
|
||||
|
||||
/// Called just after the renderer is created.
|
||||
fn renderer_created(&mut self, renderer: &mut webrender::Renderer, api_sender: &webrender::RenderApiSender) {
|
||||
let _ = (renderer, api_sender);
|
||||
}
|
||||
|
||||
/// If this extension can be dropped after render creation.
|
||||
fn is_config_only(&self) -> bool;
|
||||
|
||||
/// Called when a command request is made for the extension and renderer (window ID).
|
||||
///
|
||||
/// The `extension_key` is the current index of the extension, it can be used in error messages.
|
||||
fn command(
|
||||
&mut self,
|
||||
renderer: &mut webrender::Renderer,
|
||||
render_api: &RenderApi,
|
||||
request: ExtensionPayload,
|
||||
extension_key: usize,
|
||||
) -> ExtensionPayload {
|
||||
let _ = (renderer, render_api, request);
|
||||
ExtensionPayload::unknown_extension(extension_key)
|
||||
}
|
||||
|
||||
/// Called when a new frame is about to begin rendering.
|
||||
fn begin_render(&mut self) {}
|
||||
|
||||
/// Called when a new frame just finished rendering.
|
||||
fn finish_render(&mut self) {}
|
||||
|
||||
/// Called when a display item push for the extension is found.
|
||||
fn display_item_push(&mut self, payload: &mut ExtensionPayload, wr_list: &mut zero_ui_view_api::webrender_api::DisplayListBuilder) {
|
||||
let _ = (payload, wr_list);
|
||||
}
|
||||
|
||||
/// Called when a display item pop for the extension is found.
|
||||
fn display_item_pop(&mut self) {}
|
||||
}
|
||||
|
||||
/// View extensions register.
|
||||
#[derive(Default)]
|
||||
pub struct ViewExtensions {
|
||||
ext: Vec<Box<dyn ViewExtension>>,
|
||||
exts: Vec<Box<dyn ViewExtension>>,
|
||||
}
|
||||
impl ViewExtensions {
|
||||
/// New empty.
|
||||
|
@ -41,13 +94,13 @@ impl ViewExtensions {
|
|||
if self.is_registered(ext.name()) {
|
||||
panic!("extension `{:?}` is already registered", ext.name());
|
||||
}
|
||||
self.ext.push(ext);
|
||||
self.exts.push(ext);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns `true` is an extension of the same name is already registered.
|
||||
pub fn is_registered(&self, name: &ApiExtensionName) -> bool {
|
||||
self.ext.iter().any(|e| e.name() == name)
|
||||
self.exts.iter().any(|e| e.name() == name)
|
||||
}
|
||||
|
||||
/// Register a command extension with custom encoded messages.
|
||||
|
@ -85,25 +138,103 @@ impl ViewExtensions {
|
|||
})
|
||||
}
|
||||
|
||||
/// Register a renderer extension.
|
||||
pub fn renderer<E: RendererExtension>(
|
||||
&mut self,
|
||||
name: impl Into<ApiExtensionName>,
|
||||
new: impl FnMut() -> E + Send + 'static,
|
||||
) -> &mut Self {
|
||||
struct RendererExt<F>(ApiExtensionName, F);
|
||||
impl<E, F> ViewExtension for RendererExt<F>
|
||||
where
|
||||
E: RendererExtension,
|
||||
F: FnMut() -> E + Send + 'static,
|
||||
{
|
||||
fn name(&self) -> &ApiExtensionName {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn renderer(&mut self) -> Option<Box<dyn RendererExtension>> {
|
||||
Some(Box::new((self.1)()))
|
||||
}
|
||||
}
|
||||
self.register(Box::new(RendererExt(name.into(), new)));
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn api_extensions(&self) -> ApiExtensions {
|
||||
let mut r = ApiExtensions::new();
|
||||
for ext in &self.ext {
|
||||
for ext in &self.exts {
|
||||
r.insert(ext.name().clone()).unwrap();
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub(crate) fn call_command(&mut self, key: usize, request: ExtensionPayload) -> ExtensionPayload {
|
||||
if key >= self.ext.len() {
|
||||
if key >= self.exts.len() {
|
||||
ExtensionPayload::unknown_extension(key)
|
||||
} else if let Some(r) = self.ext[key].command(request) {
|
||||
} else if let Some(r) = self.exts[key].command(request) {
|
||||
r
|
||||
} else {
|
||||
ExtensionPayload::unknown_extension(key)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn contains(&self, extension_key: usize) -> bool {
|
||||
self.ext.len() > extension_key
|
||||
pub(crate) fn new_renderer(&mut self) -> Vec<(usize, Box<dyn RendererExtension>)> {
|
||||
self.exts
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.filter_map(|(i, e)| e.renderer().map(|e| (i, e)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets renderer debug flags.
|
||||
///
|
||||
/// This is a test case of the extensions API.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct RendererDebugExt {
|
||||
ui: Option<String>,
|
||||
}
|
||||
impl RendererExtension for RendererDebugExt {
|
||||
fn is_config_only(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn configure(&mut self, cfg: Option<ExtensionPayload>, opts: &mut webrender::WebRenderOptions) {
|
||||
if let Some(cfg) = cfg.and_then(|c| c.deserialize::<RendererDebug>().ok()) {
|
||||
opts.debug_flags = cfg.flags;
|
||||
self.ui = Some(cfg.profiler_ui);
|
||||
}
|
||||
}
|
||||
|
||||
fn renderer_created(&mut self, renderer: &mut webrender::Renderer, _: &webrender::RenderApiSender) {
|
||||
if let Some(ui) = self.ui.take() {
|
||||
renderer.set_profiler_ui(&ui);
|
||||
}
|
||||
}
|
||||
|
||||
fn command(
|
||||
&mut self,
|
||||
renderer: &mut webrender::Renderer,
|
||||
_: &RenderApi,
|
||||
request: ExtensionPayload,
|
||||
extension_key: usize,
|
||||
) -> ExtensionPayload {
|
||||
match request.deserialize::<RendererDebug>() {
|
||||
Ok(cfg) => {
|
||||
renderer.set_debug_flags(cfg.flags);
|
||||
renderer.set_profiler_ui(&cfg.profiler_ui);
|
||||
ExtensionPayload::empty()
|
||||
}
|
||||
Err(e) => ExtensionPayload::invalid_request(extension_key, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Webrender renderer debug flags and profiler UI.
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct RendererDebug {
|
||||
pub flags: DebugFlags,
|
||||
pub profiler_ui: String,
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
//!
|
||||
//! This implementation of the view API provides one extension:
|
||||
//!
|
||||
//! * `"zero-ui-view.set_webrender_debug"`: `(WindowId, RendererDebug) -> ()`, sets Webrender debug flags.
|
||||
//! * `"zero-ui-view.webrender_debug"`: `{ flags: DebugFlags, profiler_ui: String }`, sets Webrender debug flags.
|
||||
//!
|
||||
//! You can also inject your own extensions, see the [`extensions`] module for more details.
|
||||
//!
|
||||
|
@ -323,8 +323,7 @@ pub(crate) struct App {
|
|||
|
||||
headless: bool,
|
||||
|
||||
ext: ViewExtensions,
|
||||
webrender_debug_ext: Option<usize>,
|
||||
exts: ViewExtensions,
|
||||
|
||||
gl_manager: GlContextManager,
|
||||
window_target: *const EventLoopWindowTarget<AppEvent>,
|
||||
|
@ -584,13 +583,13 @@ impl App {
|
|||
response_sender: ResponseSender,
|
||||
event_sender: EventSender,
|
||||
request_recv: flume::Receiver<RequestEvent>,
|
||||
ext: ViewExtensions,
|
||||
mut ext: ViewExtensions,
|
||||
) -> Self {
|
||||
ext.renderer("zero-ui-view.webrender_debug", extensions::RendererDebugExt::default);
|
||||
App {
|
||||
headless: false,
|
||||
started: false,
|
||||
ext,
|
||||
webrender_debug_ext: None,
|
||||
exts: ext,
|
||||
gl_manager: GlContextManager::default(),
|
||||
image_cache: ImageCache::new(app_sender.clone()),
|
||||
app_sender,
|
||||
|
@ -1306,6 +1305,7 @@ impl App {
|
|||
config,
|
||||
unsafe { &*self.window_target },
|
||||
&mut self.gl_manager,
|
||||
self.exts.new_renderer(),
|
||||
self.app_sender.clone(),
|
||||
);
|
||||
let id_namespace = surf.id_namespace();
|
||||
|
@ -1372,7 +1372,7 @@ impl Api for App {
|
|||
scale_factor: 1.0,
|
||||
size: config.state.restore_rect.size,
|
||||
render_mode: config.render_mode,
|
||||
renderer_debug: config.renderer_debug,
|
||||
extensions: config.extensions,
|
||||
});
|
||||
let msg = WindowOpenData {
|
||||
id_namespace: data.id_namespace,
|
||||
|
@ -1405,6 +1405,7 @@ impl Api for App {
|
|||
config,
|
||||
unsafe { &*self.window_target },
|
||||
&mut self.gl_manager,
|
||||
self.exts.new_renderer(),
|
||||
self.app_sender.clone(),
|
||||
);
|
||||
|
||||
|
@ -1621,26 +1622,17 @@ impl Api for App {
|
|||
}
|
||||
|
||||
fn extensions(&mut self) -> ApiExtensions {
|
||||
let mut r = self.ext.api_extensions();
|
||||
if let Ok(k) = r.insert(ApiExtensionName::new("zero-ui-view.set_webrender_debug").unwrap()) {
|
||||
self.webrender_debug_ext = Some(k);
|
||||
}
|
||||
r
|
||||
self.exts.api_extensions()
|
||||
}
|
||||
|
||||
fn extension(&mut self, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload {
|
||||
if self.ext.contains(extension_key) {
|
||||
self.ext.call_command(extension_key, extension_request)
|
||||
} else if self.webrender_debug_ext == Some(extension_key) {
|
||||
let (id, dbg) = match extension_request.deserialize::<(WindowId, RendererDebug)>() {
|
||||
Ok(p) => p,
|
||||
Err(e) => return ExtensionPayload::invalid_request(extension_key, &e),
|
||||
};
|
||||
with_window_or_surface!(self, id, |w| w.set_renderer_debug(dbg), || ());
|
||||
ExtensionPayload::empty()
|
||||
} else {
|
||||
ExtensionPayload::unknown_extension(extension_key)
|
||||
}
|
||||
fn app_extension(&mut self, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload {
|
||||
self.exts.call_command(extension_key, extension_request)
|
||||
}
|
||||
|
||||
fn render_extension(&mut self, id: WindowId, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload {
|
||||
with_window_or_surface!(self, id, |w| w.render_extension(extension_key, extension_request), || {
|
||||
ExtensionPayload::invalid_request(extension_key, "renderer not found")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1746,13 +1738,3 @@ impl RenderNotifier for WrNotifier {
|
|||
let _ = self.sender.frame_ready(self.id, msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RenderExtensionArgs {}
|
||||
|
||||
pub trait RenderExtension: DisplayListExtension {
|
||||
fn begin_render(&mut self, args: RenderExtensionArgs);
|
||||
fn finish_render(&mut self, args: RenderExtensionArgs);
|
||||
|
||||
fn begin_update(&mut self, args: RenderExtensionArgs);
|
||||
fn finish_update(&mut self, args: RenderExtensionArgs);
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@ use webrender::{
|
|||
RenderApi, Renderer, Transaction,
|
||||
};
|
||||
use zero_ui_view_api::{
|
||||
units::*, DisplayListCache, FrameId, FrameRequest, FrameUpdateRequest, HeadlessRequest, ImageId, ImageLoadedData, RenderMode,
|
||||
RendererDebug, ViewProcessGen, WindowId,
|
||||
units::*, DisplayListCache, ExtensionPayload, FrameId, FrameRequest, FrameUpdateRequest, HeadlessRequest, ImageId, ImageLoadedData,
|
||||
RenderMode, ViewProcessGen, WindowId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
extensions::RendererExtension,
|
||||
gl::{GlContext, GlContextManager},
|
||||
image_cache::{Image, ImageCache, ImageUseMap, WrImageCache},
|
||||
util::PxToWinit,
|
||||
|
@ -33,6 +34,7 @@ pub(crate) struct Surface {
|
|||
|
||||
context: GlContext,
|
||||
renderer: Option<Renderer>,
|
||||
renderer_exts: Vec<(usize, Box<dyn RendererExtension>)>,
|
||||
image_use: ImageUseMap,
|
||||
|
||||
display_list_cache: DisplayListCache,
|
||||
|
@ -55,9 +57,10 @@ impl fmt::Debug for Surface {
|
|||
impl Surface {
|
||||
pub fn open(
|
||||
gen: ViewProcessGen,
|
||||
cfg: HeadlessRequest,
|
||||
mut cfg: HeadlessRequest,
|
||||
window_target: &EventLoopWindowTarget<AppEvent>,
|
||||
gl_manager: &mut GlContextManager,
|
||||
mut renderer_exts: Vec<(usize, Box<dyn RendererExtension>)>,
|
||||
event_sender: AppEventSender,
|
||||
) -> Self {
|
||||
let id = cfg.id;
|
||||
|
@ -67,7 +70,7 @@ impl Surface {
|
|||
context.resize(size.to_winit());
|
||||
let context = context;
|
||||
|
||||
let opts = webrender::WebRenderOptions {
|
||||
let mut opts = webrender::WebRenderOptions {
|
||||
// text-aa config from Firefox.
|
||||
enable_aa: true,
|
||||
enable_subpixel_aa: cfg!(not(target_os = "android")),
|
||||
|
@ -81,19 +84,29 @@ impl Surface {
|
|||
clear_caches_with_quads: !context.is_software(),
|
||||
enable_gpu_markers: !context.is_software(),
|
||||
|
||||
debug_flags: cfg.renderer_debug.flags,
|
||||
|
||||
//panic_on_gl_error: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for (key, ext) in &mut renderer_exts {
|
||||
let cfg = cfg
|
||||
.extensions
|
||||
.iter()
|
||||
.position(|(k, _)| k == key)
|
||||
.map(|i| cfg.extensions.swap_remove(i).1);
|
||||
ext.configure(cfg, &mut opts);
|
||||
}
|
||||
|
||||
let device_size = cfg.size.to_px(cfg.scale_factor).to_wr_device();
|
||||
|
||||
let (mut renderer, sender) =
|
||||
webrender::create_webrender_instance(context.gl().clone(), WrNotifier::create(id, event_sender), opts, None).unwrap();
|
||||
renderer.set_external_image_handler(WrImageCache::new_boxed());
|
||||
|
||||
renderer.set_profiler_ui(&cfg.renderer_debug.profiler_ui);
|
||||
renderer_exts.retain_mut(|(_, ext)| {
|
||||
ext.renderer_created(&mut renderer, &sender);
|
||||
!ext.is_config_only()
|
||||
});
|
||||
|
||||
let api = sender.create_api();
|
||||
let document_id = api.add_document(device_size);
|
||||
|
@ -110,6 +123,7 @@ impl Surface {
|
|||
|
||||
context,
|
||||
renderer: Some(renderer),
|
||||
renderer_exts,
|
||||
image_use: ImageUseMap::default(),
|
||||
|
||||
display_list_cache: DisplayListCache::new(pipeline_id),
|
||||
|
@ -207,11 +221,6 @@ impl Surface {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_renderer_debug(&mut self, dbg: RendererDebug) {
|
||||
self.api.set_debug_flags(dbg.flags);
|
||||
self.renderer.as_mut().unwrap().set_profiler_ui(&dbg.profiler_ui);
|
||||
}
|
||||
|
||||
pub fn render(&mut self, frame: FrameRequest) {
|
||||
let _span = tracing::trace_span!("render").entered();
|
||||
|
||||
|
@ -345,6 +354,16 @@ impl Surface {
|
|||
self.scale_factor,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calls the render extension command.
|
||||
pub fn render_extension(&mut self, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload {
|
||||
for (key, ext) in &mut self.renderer_exts {
|
||||
if *key == extension_key {
|
||||
return ext.command(self.renderer.as_mut().unwrap(), &self.api, extension_request, extension_key);
|
||||
}
|
||||
}
|
||||
ExtensionPayload::unknown_extension(extension_key)
|
||||
}
|
||||
}
|
||||
impl Drop for Surface {
|
||||
fn drop(&mut self) {
|
||||
|
|
|
@ -14,14 +14,16 @@ use winit::{
|
|||
window::{Fullscreen, Icon, Window as GWindow, WindowBuilder},
|
||||
};
|
||||
use zero_ui_view_api::{
|
||||
units::*, ColorScheme, CursorIcon, DeviceId, DisplayListCache, FocusIndicator, FrameId, FrameRequest, FrameUpdateRequest, ImageId,
|
||||
ImageLoadedData, RenderMode, RendererDebug, VideoMode, ViewProcessGen, WindowId, WindowRequest, WindowState, WindowStateAll,
|
||||
units::*, ColorScheme, CursorIcon, DeviceId, DisplayListCache, ExtensionPayload, FocusIndicator, FrameId, FrameRequest,
|
||||
FrameUpdateRequest, ImageId, ImageLoadedData, RenderMode, VideoMode, ViewProcessGen, WindowId, WindowRequest, WindowState,
|
||||
WindowStateAll,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use zero_ui_view_api::{Event, Key, KeyState, ScanCode};
|
||||
|
||||
use crate::{
|
||||
extensions::RendererExtension,
|
||||
gl::{GlContext, GlContextManager},
|
||||
image_cache::{Image, ImageCache, ImageUseMap, WrImageCache},
|
||||
util::{CursorToWinit, DipToWinit, WinitToDip, WinitToPx},
|
||||
|
@ -43,6 +45,7 @@ pub(crate) struct Window {
|
|||
context: GlContext, // context must be dropped before window.
|
||||
window: GWindow,
|
||||
renderer: Option<Renderer>,
|
||||
renderer_exts: Vec<(usize, Box<dyn RendererExtension>)>,
|
||||
capture_mode: bool,
|
||||
|
||||
pending_frames: VecDeque<(FrameId, bool, Option<EnteredSpan>)>,
|
||||
|
@ -91,23 +94,24 @@ impl Window {
|
|||
pub fn open(
|
||||
gen: ViewProcessGen,
|
||||
icon: Option<Icon>,
|
||||
req: WindowRequest,
|
||||
mut cfg: WindowRequest,
|
||||
window_target: &EventLoopWindowTarget<AppEvent>,
|
||||
gl_manager: &mut GlContextManager,
|
||||
mut renderer_exts: Vec<(usize, Box<dyn RendererExtension>)>,
|
||||
event_sender: AppEventSender,
|
||||
) -> Self {
|
||||
let id = req.id;
|
||||
let id = cfg.id;
|
||||
|
||||
let window_scope = tracing::trace_span!("glutin").entered();
|
||||
|
||||
// create window and OpenGL context
|
||||
let mut winit = WindowBuilder::new()
|
||||
.with_title(req.title)
|
||||
.with_resizable(req.resizable)
|
||||
.with_transparent(req.transparent)
|
||||
.with_title(cfg.title)
|
||||
.with_resizable(cfg.resizable)
|
||||
.with_transparent(cfg.transparent)
|
||||
.with_window_icon(icon);
|
||||
|
||||
let mut s = req.state;
|
||||
let mut s = cfg.state;
|
||||
s.clamp_size();
|
||||
|
||||
if let WindowState::Normal = s.state {
|
||||
|
@ -117,11 +121,11 @@ impl Window {
|
|||
.with_inner_size(s.restore_rect.size.to_winit());
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
if req.default_position {
|
||||
if cfg.default_position {
|
||||
// default X11 position is outer zero.
|
||||
winit = winit.with_position(DipPoint::new(Dip::new(120), Dip::new(80)).to_winit());
|
||||
}
|
||||
} else if req.default_position {
|
||||
} else if cfg.default_position {
|
||||
if let Some(screen) = window_target.primary_monitor() {
|
||||
// fallback to center.
|
||||
let screen_size = screen.size().to_px().to_dip(screen.scale_factor() as f32);
|
||||
|
@ -136,7 +140,7 @@ impl Window {
|
|||
// so that there is no white frame when it's opening.
|
||||
//
|
||||
// unless its "kiosk" mode.
|
||||
.with_visible(req.kiosk);
|
||||
.with_visible(cfg.kiosk);
|
||||
|
||||
winit = match s.state {
|
||||
WindowState::Normal | WindowState::Minimized => winit,
|
||||
|
@ -144,7 +148,7 @@ impl Window {
|
|||
WindowState::Fullscreen | WindowState::Exclusive => winit.with_fullscreen(Some(Fullscreen::Borderless(None))),
|
||||
};
|
||||
|
||||
let mut render_mode = req.render_mode;
|
||||
let mut render_mode = cfg.render_mode;
|
||||
if !cfg!(software) && render_mode == RenderMode::Software {
|
||||
tracing::warn!("ignoring `RenderMode::Software` because did not build with \"software\" feature");
|
||||
render_mode = RenderMode::Integrated;
|
||||
|
@ -204,7 +208,7 @@ impl Window {
|
|||
|
||||
let device_size = winit_window.inner_size().to_px().to_wr_device();
|
||||
|
||||
let opts = webrender::WebRenderOptions {
|
||||
let mut opts = webrender::WebRenderOptions {
|
||||
// text-aa config from Firefox.
|
||||
enable_aa: true,
|
||||
enable_subpixel_aa: cfg!(not(target_os = "android")),
|
||||
|
@ -221,17 +225,27 @@ impl Window {
|
|||
// best for GL
|
||||
upload_method: UploadMethod::PixelBuffer(VertexUsageHint::Dynamic),
|
||||
|
||||
debug_flags: req.renderer_debug.flags,
|
||||
|
||||
//panic_on_gl_error: true,
|
||||
..Default::default()
|
||||
};
|
||||
for (key, ext) in &mut renderer_exts {
|
||||
let cfg = cfg
|
||||
.extensions
|
||||
.iter()
|
||||
.position(|(k, _)| k == key)
|
||||
.map(|i| cfg.extensions.swap_remove(i).1);
|
||||
|
||||
ext.configure(cfg, &mut opts);
|
||||
}
|
||||
|
||||
let (mut renderer, sender) =
|
||||
webrender::create_webrender_instance(context.gl().clone(), WrNotifier::create(id, event_sender), opts, None).unwrap();
|
||||
renderer.set_external_image_handler(WrImageCache::new_boxed());
|
||||
|
||||
renderer.set_profiler_ui(&req.renderer_debug.profiler_ui);
|
||||
renderer_exts.retain_mut(|(_, ext)| {
|
||||
ext.renderer_created(&mut renderer, &sender);
|
||||
!ext.is_config_only()
|
||||
});
|
||||
|
||||
let api = sender.create_api();
|
||||
let document_id = api.add_document(device_size);
|
||||
|
@ -247,24 +261,25 @@ impl Window {
|
|||
prev_size: winit_window.inner_size().to_px(),
|
||||
prev_monitor: winit_window.current_monitor(),
|
||||
state: s,
|
||||
kiosk: req.kiosk,
|
||||
kiosk: cfg.kiosk,
|
||||
window: winit_window,
|
||||
context,
|
||||
capture_mode: req.capture_mode,
|
||||
capture_mode: cfg.capture_mode,
|
||||
renderer: Some(renderer),
|
||||
video_mode: req.video_mode,
|
||||
renderer_exts,
|
||||
video_mode: cfg.video_mode,
|
||||
api,
|
||||
document_id,
|
||||
pipeline_id,
|
||||
resized: true,
|
||||
display_list_cache: DisplayListCache::new(pipeline_id),
|
||||
waiting_first_frame: true,
|
||||
steal_init_focus: req.focus,
|
||||
init_focus_request: req.focus_indicator,
|
||||
visible: req.visible,
|
||||
steal_init_focus: cfg.focus,
|
||||
init_focus_request: cfg.focus_indicator,
|
||||
visible: cfg.visible,
|
||||
is_always_on_top: false,
|
||||
taskbar_visible: true,
|
||||
movable: req.movable,
|
||||
movable: cfg.movable,
|
||||
pending_frames: VecDeque::new(),
|
||||
rendered_frame_id: FrameId::INVALID,
|
||||
cursor_pos: DipPoint::zero(),
|
||||
|
@ -275,15 +290,15 @@ impl Window {
|
|||
render_mode,
|
||||
};
|
||||
|
||||
if !req.default_position && win.state.state == WindowState::Normal {
|
||||
if !cfg.default_position && win.state.state == WindowState::Normal {
|
||||
win.set_inner_position(win.state.restore_rect.origin);
|
||||
}
|
||||
|
||||
if req.always_on_top {
|
||||
if cfg.always_on_top {
|
||||
win.set_always_on_top(true);
|
||||
}
|
||||
|
||||
if win.state.state == WindowState::Normal && req.default_position {
|
||||
if win.state.state == WindowState::Normal && cfg.default_position {
|
||||
// system position.
|
||||
win.state.restore_rect.origin = win.window.inner_position().unwrap_or_default().to_px().to_dip(win.scale_factor());
|
||||
}
|
||||
|
@ -293,8 +308,8 @@ impl Window {
|
|||
win.windows_set_restore();
|
||||
}
|
||||
|
||||
win.set_cursor(req.cursor);
|
||||
win.set_taskbar_visible(req.taskbar_visible);
|
||||
win.set_cursor(cfg.cursor);
|
||||
win.set_taskbar_visible(cfg.taskbar_visible);
|
||||
win
|
||||
}
|
||||
|
||||
|
@ -1006,11 +1021,6 @@ impl Window {
|
|||
self.capture_mode = enabled;
|
||||
}
|
||||
|
||||
pub fn set_renderer_debug(&mut self, dbg: RendererDebug) {
|
||||
self.api.set_debug_flags(dbg.flags);
|
||||
self.renderer.as_mut().unwrap().set_profiler_ui(&dbg.profiler_ui);
|
||||
}
|
||||
|
||||
/// Start rendering a new frame.
|
||||
///
|
||||
/// The [callback](#callback) will be called when the frame is ready to be [presented](Self::present).
|
||||
|
@ -1227,6 +1237,16 @@ impl Window {
|
|||
pub fn render_mode(&self) -> RenderMode {
|
||||
self.render_mode
|
||||
}
|
||||
|
||||
/// Calls the render extension command.
|
||||
pub fn render_extension(&mut self, extension_key: usize, extension_request: ExtensionPayload) -> ExtensionPayload {
|
||||
for (key, ext) in &mut self.renderer_exts {
|
||||
if *key == extension_key {
|
||||
return ext.command(self.renderer.as_mut().unwrap(), &self.api, extension_request, extension_key);
|
||||
}
|
||||
}
|
||||
ExtensionPayload::unknown_extension(extension_key)
|
||||
}
|
||||
}
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
|
|
|
@ -4,8 +4,8 @@ use crate::core::color::ColorScheme;
|
|||
use crate::core::config::{ConfigKey, CONFIG};
|
||||
use crate::core::text::formatx;
|
||||
use crate::core::window::{
|
||||
AutoSize, FrameCaptureMode, MonitorQuery, WindowChrome, WindowIcon, WindowId, WindowLoadingHandle, WindowState, WindowVars, MONITORS,
|
||||
WINDOW_CTRL, WINDOW_LOAD_EVENT,
|
||||
AutoSize, FrameCaptureMode, MonitorQuery, RendererDebug, WindowChrome, WindowIcon, WindowId, WindowLoadingHandle, WindowState,
|
||||
WindowVars, MONITORS, WINDOW_CTRL, WINDOW_LOAD_EVENT,
|
||||
};
|
||||
use crate::prelude::new_property::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
Loading…
Reference in New Issue