Upgrade Blitz to 0.1.0-alpha.3 and add wgpu-texture example (#4286)

* Use Blitz 0.1.0-alpha.3

* Add wgpu-texture example

* Native: Support configuring wgpu Features and Limits
This commit is contained in:
Nico Burns authored and GitHub committed 2025-06-17 18:06:32 -07:00
1 parent 544d8cc4da
commit 6fcb496fd5
11 files changed
+796 -162

No files matched your search

+30 -37
View File
@@ -1,16 +1,16 @@
use anyrender_vello::VelloWindowRenderer;
use blitz_shell::BlitzApplication;
use dioxus_core::{ScopeId, VirtualDom};
use blitz_shell::{BlitzApplication, View};
use dioxus_core::ScopeId;
use dioxus_history::{History, MemoryHistory};
use std::{collections::HashSet, rc::Rc};
use std::rc::Rc;
use winit::application::ApplicationHandler;
use winit::event::{StartCause, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
use winit::window::WindowId;
use crate::DioxusNativeWindowRenderer;
use crate::{
assets::DioxusNativeNetProvider, contexts::DioxusNativeDocument,
mutation_writer::MutationWriter, BlitzShellEvent, DioxusDocument, WindowConfig,
contexts::DioxusNativeDocument, mutation_writer::MutationWriter, BlitzShellEvent,
DioxusDocument, WindowConfig,
};
/// Dioxus-native specific event type
@@ -36,21 +36,24 @@ pub enum DioxusNativeEvent {
}
pub struct DioxusNativeApplication {
pending_vdom: Option<VirtualDom>,
inner: BlitzApplication<VelloWindowRenderer>,
pending_window: Option<WindowConfig<DioxusNativeWindowRenderer>>,
inner: BlitzApplication<DioxusNativeWindowRenderer>,
proxy: EventLoopProxy<BlitzShellEvent>,
}
impl DioxusNativeApplication {
pub fn new(proxy: EventLoopProxy<BlitzShellEvent>, vdom: VirtualDom) -> Self {
pub fn new(
proxy: EventLoopProxy<BlitzShellEvent>,
config: WindowConfig<DioxusNativeWindowRenderer>,
) -> Self {
Self {
pending_vdom: Some(vdom),
pending_window: Some(config),
inner: BlitzApplication::new(proxy.clone()),
proxy,
}
}
pub fn add_window(&mut self, window_config: WindowConfig<VelloWindowRenderer>) {
pub fn add_window(&mut self, window_config: WindowConfig<DioxusNativeWindowRenderer>) {
self.inner.add_window(window_config);
}
@@ -112,42 +115,27 @@ impl DioxusNativeApplication {
impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
tracing::debug!("Injecting document provider into all windows");
let vdom = self.pending_vdom.take().unwrap();
#[cfg(feature = "net")]
let net_provider = {
let proxy = self.proxy.clone();
let net_provider = DioxusNativeNetProvider::shared(proxy);
Some(net_provider)
};
#[cfg(not(feature = "net"))]
let net_provider = None;
// Create document + window from the baked virtualdom
let doc = DioxusDocument::new(vdom, net_provider);
let window = WindowConfig::new(Box::new(doc) as _);
// little hack since View::init is not public - fix this once alpha-2 is out
let old_windows = self.inner.windows.keys().copied().collect::<HashSet<_>>();
self.add_window(window);
self.inner.resumed(event_loop);
let new_windows = self.inner.windows.keys().cloned().collect::<HashSet<_>>();
// todo(jon): we should actually mess with the pending windows instead of passing along the contexts
for window_id in new_windows.difference(&old_windows) {
let window = self.inner.windows.get_mut(window_id).unwrap();
if let Some(config) = self.pending_window.take() {
let mut window = View::init(config, event_loop, &self.proxy);
let renderer = window.renderer.clone();
let window_id = window.window_id();
let doc = window.downcast_doc_mut::<DioxusDocument>();
doc.vdom.in_runtime(|| {
let shared: Rc<dyn dioxus_document::Document> =
Rc::new(DioxusNativeDocument::new(self.proxy.clone(), *window_id));
Rc::new(DioxusNativeDocument::new(self.proxy.clone(), window_id));
ScopeId::ROOT.provide_context(shared);
});
// Add history
let history_provider: Rc<dyn History> = Rc::new(MemoryHistory::default());
doc.vdom
.in_runtime(|| ScopeId::ROOT.provide_context(history_provider));
.in_runtime(move || ScopeId::ROOT.provide_context(history_provider));
// Add renderer
doc.vdom
.in_runtime(move || ScopeId::ROOT.provide_context(renderer));
// Queue rebuild
let mut writer = MutationWriter::new(&mut doc.inner, &mut doc.vdom_state);
@@ -156,7 +144,12 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
// And then request redraw
window.request_redraw();
// todo(jon): we should actually mess with the pending windows instead of passing along the contexts
self.inner.windows.insert(window_id, window);
}
self.inner.resumed(event_loop);
}
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
+94
View File
@@ -0,0 +1,94 @@
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use anyrender::WindowRenderer;
pub use anyrender_vello::{
wgpu::{Features, Limits},
CustomPaintSource, VelloWindowRenderer as InnerRenderer,
};
pub fn use_wgpu<T: CustomPaintSource>(create_source: impl FnOnce() -> T) -> u64 {
use dioxus_core::prelude::{consume_context, use_hook_with_cleanup};
let (_renderer, id) = use_hook_with_cleanup(
|| {
let renderer = consume_context::<DioxusNativeWindowRenderer>();
let source = Box::new(create_source());
let id = renderer.register_custom_paint_source(source);
(renderer, id)
},
|(renderer, id)| {
renderer.unregister_custom_paint_source(id);
},
);
id
}
#[derive(Clone)]
pub struct DioxusNativeWindowRenderer {
inner: Rc<RefCell<InnerRenderer>>,
}
impl Default for DioxusNativeWindowRenderer {
fn default() -> Self {
Self::new()
}
}
impl DioxusNativeWindowRenderer {
pub fn new() -> Self {
let vello_renderer = InnerRenderer::new();
Self::with_inner_renderer(vello_renderer)
}
pub fn with_features_and_limits(features: Option<Features>, limits: Option<Limits>) -> Self {
let vello_renderer = InnerRenderer::with_features_and_limits(features, limits);
Self::with_inner_renderer(vello_renderer)
}
fn with_inner_renderer(vello_renderer: InnerRenderer) -> Self {
Self {
inner: Rc::new(RefCell::new(vello_renderer)),
}
}
}
impl DioxusNativeWindowRenderer {
pub fn register_custom_paint_source(&self, source: Box<dyn CustomPaintSource>) -> u64 {
self.inner.borrow_mut().register_custom_paint_source(source)
}
pub fn unregister_custom_paint_source(&self, id: u64) {
self.inner.borrow_mut().unregister_custom_paint_source(id)
}
}
impl WindowRenderer for DioxusNativeWindowRenderer {
type ScenePainter<'a>
= <InnerRenderer as WindowRenderer>::ScenePainter<'a>
where
Self: 'a;
fn resume(&mut self, window: Arc<dyn anyrender::WindowHandle>, width: u32, height: u32) {
self.inner.borrow_mut().resume(window, width, height)
}
fn suspend(&mut self) {
self.inner.borrow_mut().suspend()
}
fn is_active(&self) -> bool {
self.inner.borrow().is_active()
}
fn set_size(&mut self, width: u32, height: u32) {
self.inner.borrow_mut().set_size(width, height)
}
fn render<F: FnOnce(&mut Self::ScenePainter<'_>)>(&mut self, draw_fn: F) {
self.inner.borrow_mut().render(draw_fn)
}
}
+52 -9
View File
@@ -13,12 +13,16 @@ mod assets;
mod contexts;
mod dioxus_application;
mod dioxus_document;
mod dioxus_renderer;
mod events;
mod mutation_writer;
use blitz_dom::{ns, Atom, QualName};
pub use anyrender_vello::{CustomPaintCtx, CustomPaintSource, TextureHandle};
use assets::DioxusNativeNetProvider;
use blitz_dom::{ns, LocalName, Namespace, QualName};
pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
pub use dioxus_document::DioxusDocument;
pub use dioxus_renderer::{use_wgpu, DioxusNativeWindowRenderer, Features, Limits};
use blitz_shell::{create_default_event_loop, BlitzShellEvent, Config, WindowConfig};
use dioxus_core::{ComponentFunction, Element, VirtualDom};
@@ -44,12 +48,36 @@ pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
app: impl ComponentFunction<P, M>,
props: P,
contexts: Vec<Box<dyn Fn() -> Box<dyn Any> + Send + Sync>>,
_cfg: Vec<Box<dyn Any>>,
configs: Vec<Box<dyn Any>>,
) {
let _cfg = _cfg
.into_iter()
.find_map(|cfg| cfg.downcast::<Config>().ok())
.unwrap_or_default();
// Macro to attempt to downcast a type out of a Box<dyn Any>
macro_rules! try_read_config {
($input:ident, $store:ident, $kind:ty) => {
// Try to downcast the Box<dyn Any> to type $kind
match $input.downcast::<$kind>() {
// If the type matches then write downcast value to variable $store
Ok(value) => {
$store = Some(*value);
continue;
}
// Else extract the original Box<dyn Any> value out of the error type
// and return it so that we can try again with a different type.
Err(cfg) => cfg,
}
};
}
// Read config values
let mut features = None;
let mut limits = None;
let mut _config = None;
for mut cfg in configs {
cfg = try_read_config!(cfg, features, Features);
cfg = try_read_config!(cfg, limits, Limits);
cfg = try_read_config!(cfg, _config, Config);
let _ = cfg;
}
let event_loop = create_default_event_loop::<BlitzShellEvent>();
// Turn on the runtime and enter it
@@ -84,8 +112,23 @@ pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
vdom.insert_any_root_context(context());
}
#[cfg(feature = "net")]
let net_provider = {
let proxy = event_loop.create_proxy();
let net_provider = DioxusNativeNetProvider::shared(proxy);
Some(net_provider)
};
#[cfg(not(feature = "net"))]
let net_provider = None;
// Create document + window from the baked virtualdom
let doc = DioxusDocument::new(vdom, net_provider);
let renderer = DioxusNativeWindowRenderer::with_features_and_limits(features, limits);
let config = WindowConfig::new(Box::new(doc) as _, renderer.clone());
// Create application
let mut application = DioxusNativeApplication::new(event_loop.create_proxy(), vdom);
let mut application = DioxusNativeApplication::new(event_loop.create_proxy(), config);
// Run event loop
event_loop.run_app(&mut application).unwrap();
@@ -94,8 +137,8 @@ pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
pub(crate) fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName {
QualName {
prefix: None,
ns: namespace.map(Atom::from).unwrap_or(ns!(html)),
local: Atom::from(local_name),
ns: namespace.map(Namespace::from).unwrap_or(ns!(html)),
local: LocalName::from(local_name),
}
}