Implemented renderer extensions.

This commit is contained in:
Samuel Guerra 2023-06-06 00:16:24 -03:00
parent 740f7700c3
commit 66fd1bad91
15 changed files with 483 additions and 345 deletions

View File

@ -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.

View File

@ -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"#)
}

View File

@ -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());

View File

@ -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.

View File

@ -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())
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -7,7 +7,7 @@ use crate::{
context::WINDOW,
crate_util::IdSet,
image::Img,
render::{RenderMode, RendererDebug},
render::RenderMode,
text::{ToText, Txt},
units::*,
var::*,

View File

@ -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;
}

View File

@ -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

View File

@ -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,
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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};