浏览代码

wip: native canvas API

Jonathan Kelley 2 月之前
父节点
当前提交
18c2adb776

+ 3 - 13
Cargo.lock

@@ -1532,8 +1532,6 @@ dependencies = [
 [[package]]
 [[package]]
 name = "blitz-dom"
 name = "blitz-dom"
 version = "0.1.0-alpha.1"
 version = "0.1.0-alpha.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eae8889dde80e2aaceec27455c63446ca841cb7217a4f5489691295da922792b"
 dependencies = [
 dependencies = [
  "accesskit",
  "accesskit",
  "app_units",
  "app_units",
@@ -1566,21 +1564,16 @@ dependencies = [
 [[package]]
 [[package]]
 name = "blitz-net"
 name = "blitz-net"
 version = "0.1.0-alpha.1"
 version = "0.1.0-alpha.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efc80023b8f6758d7f61bda68440917b72cbe2fe6187cbe1b7837e73b2522dfe"
 dependencies = [
 dependencies = [
  "blitz-traits",
  "blitz-traits",
  "data-url 0.3.1",
  "data-url 0.3.1",
  "reqwest 0.12.15",
  "reqwest 0.12.15",
- "thiserror 1.0.69",
  "tokio",
  "tokio",
 ]
 ]
 
 
 [[package]]
 [[package]]
 name = "blitz-renderer-vello"
 name = "blitz-renderer-vello"
 version = "0.1.0-alpha.1"
 version = "0.1.0-alpha.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44fdafaf87eb502da99667fad2ae9757d01c8bd83772f6b7b6a78c48c3ab6ff5"
 dependencies = [
 dependencies = [
  "blitz-dom",
  "blitz-dom",
  "blitz-traits",
  "blitz-traits",
@@ -1601,8 +1594,6 @@ dependencies = [
 [[package]]
 [[package]]
 name = "blitz-shell"
 name = "blitz-shell"
 version = "0.1.0-alpha.1"
 version = "0.1.0-alpha.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef0a6122f3619a746dddd21883edaae5f791d6e9f401dbdf7f7756caa4f69453"
 dependencies = [
 dependencies = [
  "accesskit",
  "accesskit",
  "accesskit_winit",
  "accesskit_winit",
@@ -1619,8 +1610,6 @@ dependencies = [
 [[package]]
 [[package]]
 name = "blitz-traits"
 name = "blitz-traits"
 version = "0.1.0-alpha.1"
 version = "0.1.0-alpha.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9972442314e8bf9dd65af7aee0634a2d575972587d404520c7c6986abbd523d3"
 dependencies = [
 dependencies = [
  "bitflags 2.9.0",
  "bitflags 2.9.0",
  "bytes",
  "bytes",
@@ -4147,6 +4136,7 @@ dependencies = [
  "blitz-renderer-vello",
  "blitz-renderer-vello",
  "blitz-shell",
  "blitz-shell",
  "blitz-traits",
  "blitz-traits",
+ "dioxus",
  "dioxus-asset-resolver",
  "dioxus-asset-resolver",
  "dioxus-cli-config",
  "dioxus-cli-config",
  "dioxus-core",
  "dioxus-core",
@@ -4156,9 +4146,11 @@ dependencies = [
  "dioxus-html",
  "dioxus-html",
  "futures-util",
  "futures-util",
  "keyboard-types",
  "keyboard-types",
+ "peniko",
  "rustc-hash 1.1.0",
  "rustc-hash 1.1.0",
  "tokio",
  "tokio",
  "tracing",
  "tracing",
+ "wgpu 23.0.1",
  "winit",
  "winit",
 ]
 ]
 
 
@@ -13004,8 +12996,6 @@ checksum = "decb57071c4b4d5690a9719fb04a07cf2fab0fa3df99a830ef735192a1a98e5d"
 [[package]]
 [[package]]
 name = "stylo_taffy"
 name = "stylo_taffy"
 version = "0.1.0-alpha.1"
 version = "0.1.0-alpha.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6da84b2bae9d25a2156b7cf89acab90683ac4d25dcb3fe1817dc9e77a20ce2f"
 dependencies = [
 dependencies = [
  "stylo",
  "stylo",
  "taffy",
  "taffy",

+ 14 - 0
Cargo.toml

@@ -554,3 +554,17 @@ doc-scrape-examples = true
 [[example]]
 [[example]]
 name = "logging"
 name = "logging"
 doc-scrape-examples = true
 doc-scrape-examples = true
+
+
+[patch.crates-io]
+blitz-renderer-vello = { path = "../Projects/blitz/packages/blitz-renderer-vello" }
+blitz-dom = { path = "../Projects/blitz/packages/blitz-dom" }
+blitz-net = { path = "../Projects/blitz/packages/blitz-net" }
+blitz-traits = { path = "../Projects/blitz/packages/blitz-traits" }
+blitz-shell = { path = "../Projects/blitz/packages/blitz-shell" }
+# blitz-renderer-vello = { path = "../Projects/blitz/packages/blitz", default-features = false }
+# blitz-dom = { path = "../Projects/blitz/packages/blitz", default-features = false }
+# blitz-net = { path = "../Projects/blitz/packages/blitz", optional = true }
+# blitz-traits = { path = "../Projects/blitz/packages/blitz" }
+# blitz-shell = { path = "../Projects/blitz/packages/blitz", default-features = false }
+

+ 11 - 0
examples/native-canvas.rs

@@ -0,0 +1,11 @@
+//! Native-canvas: render wgpu textures to the `<canvas>` element.
+
+use dioxus::prelude::*;
+
+fn main() {
+    dioxus::launch(app);
+}
+
+fn app() -> Element {
+    rsx! {}
+}

+ 8 - 0
packages/native/Cargo.toml

@@ -27,6 +27,7 @@ blitz-dom = { version = "0.1.0-alpha.1", default-features = false }
 blitz-net = { version = "0.1.0-alpha.1", optional = true }
 blitz-net = { version = "0.1.0-alpha.1", optional = true }
 blitz-traits = { version = "0.1.0-alpha.1" }
 blitz-traits = { version = "0.1.0-alpha.1" }
 blitz-shell = { version = "0.1.0-alpha.1", default-features = false }
 blitz-shell = { version = "0.1.0-alpha.1", default-features = false }
+peniko = { version = "0.3.0" }
 # blitz-renderer-vello = { git = "https://github.com/dioxuslabs/blitz", rev="c1a7ecf06d1760a268e0046dc0e43f6c796ddc3c", default-features = false }
 # blitz-renderer-vello = { git = "https://github.com/dioxuslabs/blitz", rev="c1a7ecf06d1760a268e0046dc0e43f6c796ddc3c", default-features = false }
 # blitz-dom = { git = "https://github.com/dioxuslabs/blitz", rev="c1a7ecf06d1760a268e0046dc0e43f6c796ddc3c", default-features = false }
 # blitz-dom = { git = "https://github.com/dioxuslabs/blitz", rev="c1a7ecf06d1760a268e0046dc0e43f6c796ddc3c", default-features = false }
 # blitz-net = { git = "https://github.com/dioxuslabs/blitz", rev="c1a7ecf06d1760a268e0046dc0e43f6c796ddc3c", optional = true }
 # blitz-net = { git = "https://github.com/dioxuslabs/blitz", rev="c1a7ecf06d1760a268e0046dc0e43f6c796ddc3c", optional = true }
@@ -54,6 +55,13 @@ tracing = { workspace = true, optional = true }
 rustc-hash = { workspace = true }
 rustc-hash = { workspace = true }
 futures-util = { workspace = true }
 futures-util = { workspace = true }
 
 
+# wgpu
+wgpu = "23"
+
+[dev-dependencies]
+dioxus = { workspace = true }
+
+
 [package.metadata.docs.rs]
 [package.metadata.docs.rs]
 all-features = true
 all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 rustdoc-args = ["--cfg", "docsrs"]

+ 94 - 0
packages/native/examples/canvas.rs

@@ -0,0 +1,94 @@
+use std::sync::Arc;
+
+use dioxus::prelude::*;
+use dioxus_native::SharedNativeTexture;
+use wgpu::{
+    core::instance, Extent3d, ImageCopyTexture, ImageCopyTextureBase, InstanceDescriptor, Origin3d,
+    TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
+};
+
+fn main() {
+    dioxus::launch(app);
+}
+
+fn app() -> Element {
+    let width = 500;
+    let height = 500;
+
+    use_future(move || attach_egui(width, height));
+
+    rsx! {
+        div {
+            h1 { "Hello native canvas" }
+            canvas {
+                id: "egui-demo",
+                width: "{width}",
+                height: "{height}",
+                style: "border: 1px solid black;",
+            }
+        }
+    }
+}
+
+async fn attach_egui(width: u32, height: u32) {
+    let document = dioxus_native::document();
+
+    let instance = wgpu::Instance::default();
+    let surface = instance.create_surface(document.window_handle()).unwrap();
+
+    let adapter = instance
+        .request_adapter(&wgpu::RequestAdapterOptions {
+            power_preference: wgpu::PowerPreference::default(),
+            force_fallback_adapter: false,
+            compatible_surface: Some(&surface),
+        })
+        .await
+        .unwrap();
+
+    let (device, _queue) = adapter
+        .request_device(
+            &wgpu::DeviceDescriptor {
+                label: None,
+                required_features: wgpu::Features::empty(),
+                // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
+                required_limits: wgpu::Limits::downlevel_webgl2_defaults()
+                    .using_resolution(adapter.limits()),
+                memory_hints: wgpu::MemoryHints::Performance,
+            },
+            None,
+        )
+        .await
+        .expect("Failed to create device");
+
+    let texture = device.create_texture(&TextureDescriptor {
+        label: Some("egui-demo"),
+        format: TextureFormat::Rgba32Float,
+        usage: TextureUsages::RENDER_ATTACHMENT,
+        size: Extent3d {
+            width,
+            height,
+            depth_or_array_layers: 1,
+        },
+        mip_level_count: 1,
+        sample_count: 1,
+        dimension: TextureDimension::D2,
+        view_formats: &[],
+    });
+
+    let copy_texture = ImageCopyTextureBase {
+        texture: Arc::new(texture),
+        mip_level: 1,
+        origin: Origin3d { x: 0, y: 0, z: 0 },
+        aspect: TextureAspect::All,
+    };
+
+    document.set_custom_texture(
+        "egui-demo",
+        SharedNativeTexture {
+            inner: copy_texture,
+        },
+    );
+
+    // todo - handle resize events!
+    futures_util::future::pending::<()>().await;
+}

+ 20 - 0
packages/native/src/api.rs

@@ -0,0 +1,20 @@
+use std::{any::Any, rc::Rc, sync::Arc};
+
+use wgpu::{ImageCopyTextureBase, Texture};
+use winit::window::Window;
+
+use crate::contexts::NativeDocument;
+
+pub fn document() -> Rc<NativeDocument> {
+    dioxus_core::prelude::consume_context::<Rc<NativeDocument>>()
+}
+
+#[derive(Clone)]
+pub struct SharedNativeTexture {
+    pub inner: ImageCopyTextureBase<Arc<Texture>>,
+}
+
+#[derive(Clone)]
+pub struct ReservedNativeTexture {
+    pub texture: SharedNativeTexture,
+}

+ 64 - 9
packages/native/src/contexts.rs

@@ -1,21 +1,76 @@
+use std::{cell::RefCell, ops::Deref, rc::Rc, sync::Arc};
+
+use blitz_renderer_vello::BlitzVelloRenderer;
 use blitz_shell::BlitzShellEvent;
 use blitz_shell::BlitzShellEvent;
+use blitz_traits::BlitzWindowHandle;
 use dioxus_document::{Document, NoOpDocument};
 use dioxus_document::{Document, NoOpDocument};
-use winit::{event_loop::EventLoopProxy, window::WindowId};
+use peniko::Blob;
+use winit::{
+    event_loop::EventLoopProxy,
+    window::{Window, WindowId},
+};
 
 
-use crate::DioxusNativeEvent;
+use crate::{DioxusNativeEvent, ReservedNativeTexture, SharedNativeTexture};
 
 
-pub struct DioxusNativeDocument {
+pub struct NativeDocument {
     pub(crate) proxy: EventLoopProxy<BlitzShellEvent>,
     pub(crate) proxy: EventLoopProxy<BlitzShellEvent>,
-    pub(crate) window: WindowId,
+    pub(crate) renderer: Rc<RefCell<BlitzVelloRenderer>>,
+    pub(crate) window_id: WindowId,
 }
 }
 
 
-impl DioxusNativeDocument {
-    pub(crate) fn new(proxy: EventLoopProxy<BlitzShellEvent>, window: WindowId) -> Self {
-        Self { proxy, window }
+impl NativeDocument {
+    pub(crate) fn new(
+        proxy: EventLoopProxy<BlitzShellEvent>,
+        window: WindowId,
+        renderer: Rc<RefCell<BlitzVelloRenderer>>,
+    ) -> Self {
+        Self {
+            proxy,
+            renderer,
+            window_id: window,
+        }
+    }
+
+    pub fn window_handle(&self) -> Arc<dyn BlitzWindowHandle> {
+        self.renderer.borrow().window_handle.clone()
+    }
+
+    pub fn set_custom_texture(&self, node_id: &str, texture: SharedNativeTexture) {
+        let mut renderer = self.renderer.borrow_mut();
+
+        if let Some(image) = renderer.custom_textures.get(node_id).cloned() {
+            let blitz_renderer_vello::RenderState::Active(state) = &mut renderer.render_state
+            else {
+                return;
+            };
+
+            state
+                .renderer
+                .override_image(&image, Some(texture.inner.clone()));
+        }
+
+        // if let Some(node_id) = window.doc.inner.nodes_to_id.get(node_id).cloned() {
+        //     match &window.doc.inner.nodes[node_id].data {
+        //         blitz_dom::NodeData::Element(data) => match &data.node_specific_data {
+        //             blitz_dom::node::NodeSpecificData::Image(image_data) => {
+        //                 match image_data.as_ref() {
+        //                     blitz_dom::node::ImageData::CustomTexture(image) => {
+        //                         state
+        //                             .renderer
+        //                             .override_image(&image, Some(texture.inner.clone()));
+        //                     }
+        //                     _ => {}
+        //                 }
+        //             }
+        //             _ => {}
+        //         },
+        //         _ => {}
+        //     }
+        // }
     }
     }
 }
 }
 
 
-impl Document for DioxusNativeDocument {
+impl Document for NativeDocument {
     fn eval(&self, _js: String) -> dioxus_document::Eval {
     fn eval(&self, _js: String) -> dioxus_document::Eval {
         NoOpDocument.eval(_js)
         NoOpDocument.eval(_js)
     }
     }
@@ -26,7 +81,7 @@ impl Document for DioxusNativeDocument {
         attributes: &[(&str, String)],
         attributes: &[(&str, String)],
         contents: Option<String>,
         contents: Option<String>,
     ) {
     ) {
-        let window = self.window;
+        let window = self.window_id;
         _ = self.proxy.send_event(BlitzShellEvent::embedder_event(
         _ = self.proxy.send_event(BlitzShellEvent::embedder_event(
             DioxusNativeEvent::CreateHeadElement {
             DioxusNativeEvent::CreateHeadElement {
                 name: name.to_string(),
                 name: name.to_string(),

+ 61 - 52
packages/native/src/dioxus_application.rs

@@ -1,38 +1,42 @@
 use blitz_renderer_vello::BlitzVelloRenderer;
 use blitz_renderer_vello::BlitzVelloRenderer;
-use blitz_shell::BlitzApplication;
+use blitz_shell::{BlitzApplication, View};
 use dioxus_core::{ScopeId, VirtualDom};
 use dioxus_core::{ScopeId, VirtualDom};
 use dioxus_history::{History, MemoryHistory};
 use dioxus_history::{History, MemoryHistory};
-use std::{collections::HashSet, rc::Rc};
+use std::{
+    cell::RefCell,
+    collections::{HashMap, HashSet},
+    rc::Rc,
+};
 use winit::application::ApplicationHandler;
 use winit::application::ApplicationHandler;
 use winit::event::{StartCause, WindowEvent};
 use winit::event::{StartCause, WindowEvent};
 use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
 use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
 use winit::window::WindowId;
 use winit::window::WindowId;
 
 
 use crate::{
 use crate::{
-    assets::DioxusNativeNetProvider, contexts::DioxusNativeDocument,
-    mutation_writer::MutationWriter, BlitzShellEvent, DioxusDocument, DioxusNativeEvent,
-    WindowConfig,
+    assets::DioxusNativeNetProvider, contexts::NativeDocument, mutation_writer::MutationWriter,
+    BlitzShellEvent, DioxusDocument, DioxusNativeEvent, SharedNativeTexture, WindowConfig,
 };
 };
 
 
 pub struct DioxusNativeApplication {
 pub struct DioxusNativeApplication {
     pending_vdom: Option<VirtualDom>,
     pending_vdom: Option<VirtualDom>,
-    inner: BlitzApplication<DioxusDocument, BlitzVelloRenderer>,
+    // inner: BlitzApplication<DioxusDocument, BlitzVelloRenderer>,
+    pub windows: HashMap<WindowId, View<DioxusDocument, BlitzVelloRenderer>>,
+
     proxy: EventLoopProxy<BlitzShellEvent>,
     proxy: EventLoopProxy<BlitzShellEvent>,
 }
 }
 
 
+pub struct DioxusNativeWindow {}
+
 impl DioxusNativeApplication {
 impl DioxusNativeApplication {
     pub fn new(proxy: EventLoopProxy<BlitzShellEvent>, vdom: VirtualDom) -> Self {
     pub fn new(proxy: EventLoopProxy<BlitzShellEvent>, vdom: VirtualDom) -> Self {
         Self {
         Self {
             pending_vdom: Some(vdom),
             pending_vdom: Some(vdom),
-            inner: BlitzApplication::new(proxy.clone()),
+            windows: HashMap::new(),
             proxy,
             proxy,
+            // inner: BlitzApplication::new(proxy.clone()),
         }
         }
     }
     }
 
 
-    pub fn add_window(&mut self, window_config: WindowConfig<DioxusDocument, BlitzVelloRenderer>) {
-        self.inner.add_window(window_config);
-    }
-
     fn handle_blitz_shell_event(
     fn handle_blitz_shell_event(
         &mut self,
         &mut self,
         event_loop: &ActiveEventLoop,
         event_loop: &ActiveEventLoop,
@@ -47,7 +51,7 @@ impl DioxusNativeApplication {
             ))]
             ))]
             DioxusNativeEvent::DevserverEvent(event) => match event {
             DioxusNativeEvent::DevserverEvent(event) => match event {
                 dioxus_devtools::DevserverMsg::HotReload(hotreload_message) => {
                 dioxus_devtools::DevserverMsg::HotReload(hotreload_message) => {
-                    for window in self.inner.windows.values_mut() {
+                    for window in self.windows.values_mut() {
                         dioxus_devtools::apply_changes(&window.doc.vdom, hotreload_message);
                         dioxus_devtools::apply_changes(&window.doc.vdom, hotreload_message);
                         window.poll();
                         window.poll();
                     }
                     }
@@ -64,7 +68,7 @@ impl DioxusNativeApplication {
                 contents,
                 contents,
                 window,
                 window,
             } => {
             } => {
-                if let Some(window) = self.inner.windows.get_mut(window) {
+                if let Some(window) = self.windows.get_mut(window) {
                     window.doc.create_head_element(name, attributes, contents);
                     window.doc.create_head_element(name, attributes, contents);
                     window.poll();
                     window.poll();
                 }
                 }
@@ -102,46 +106,51 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
 
 
         // Create document + window from the baked virtualdom
         // Create document + window from the baked virtualdom
         let doc = DioxusDocument::new(vdom, net_provider);
         let doc = DioxusDocument::new(vdom, net_provider);
-        let window = WindowConfig::new(doc);
+        // let window = WindowConfig::new(doc);
 
 
         // little hack since View::init is not public - fix this once alpha-2 is out
         // 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<_>>();
+        // let old_windows = self.inner.windows.keys().copied().collect::<HashSet<_>>();
+        // self.inner.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
         // 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();
-            window.doc.vdom.in_runtime(|| {
-                let shared: Rc<dyn dioxus_document::Document> =
-                    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());
-            window
-                .doc
-                .vdom
-                .in_runtime(|| ScopeId::ROOT.provide_context(history_provider));
-
-            // Queue rebuild
-            let mut writer = MutationWriter::new(&mut window.doc.inner, &mut window.doc.vdom_state);
-            window.doc.vdom.rebuild(&mut writer);
-            drop(writer);
-
-            // And then request redraw
-            window.request_redraw();
-        }
+        // for window_id in new_windows.difference(&old_windows) {
+        //     let window = self.inner.windows.get_mut(window_id).unwrap();
+        //     window.doc.vdom.in_runtime(|| {
+        //         let document = Rc::new(NativeDocument::new(
+        //             self.proxy.clone(),
+        //             *window_id,
+        //             window.renderer.clone(),
+        //         ));
+        //         let shared: Rc<dyn dioxus_document::Document> = document.clone();
+        //         ScopeId::ROOT.provide_context(document);
+        //         ScopeId::ROOT.provide_context(shared);
+        //     });
+
+        //     // Add history
+        //     let history_provider: Rc<dyn History> = Rc::new(MemoryHistory::default());
+        //     window
+        //         .doc
+        //         .vdom
+        //         .in_runtime(|| ScopeId::ROOT.provide_context(history_provider));
+
+        //     // Queue rebuild
+        //     let mut writer = MutationWriter::new(&mut window.doc.inner, &mut window.doc.vdom_state);
+        //     window.doc.vdom.rebuild(&mut writer);
+        //     drop(writer);
+
+        //     // And then request redraw
+        //     window.request_redraw();
+        // }
     }
     }
 
 
     fn suspended(&mut self, event_loop: &ActiveEventLoop) {
     fn suspended(&mut self, event_loop: &ActiveEventLoop) {
-        self.inner.suspended(event_loop);
+        // self.inner.suspended(event_loop);
     }
     }
 
 
     fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
     fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
-        self.inner.new_events(event_loop, cause);
+        // self.inner.new_events(event_loop, cause);
     }
     }
 
 
     fn window_event(
     fn window_event(
@@ -150,17 +159,17 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
         window_id: WindowId,
         window_id: WindowId,
         event: WindowEvent,
         event: WindowEvent,
     ) {
     ) {
-        self.inner.window_event(event_loop, window_id, event);
+        // self.inner.window_event(event_loop, window_id, event);
     }
     }
 
 
     fn user_event(&mut self, event_loop: &ActiveEventLoop, event: BlitzShellEvent) {
     fn user_event(&mut self, event_loop: &ActiveEventLoop, event: BlitzShellEvent) {
-        match event {
-            BlitzShellEvent::Embedder(event) => {
-                if let Some(event) = event.downcast_ref::<DioxusNativeEvent>() {
-                    self.handle_blitz_shell_event(event_loop, event);
-                }
-            }
-            event => self.inner.user_event(event_loop, event),
-        }
+        // match event {
+        //     BlitzShellEvent::Embedder(event) => {
+        //         if let Some(event) = event.downcast_ref::<DioxusNativeEvent>() {
+        //             self.handle_blitz_shell_event(event_loop, event);
+        //         }
+        //     }
+        //     event => self.inner.user_event(event_loop, event),
+        // }
     }
     }
 }
 }

+ 4 - 2
packages/native/src/dioxus_document.rs

@@ -15,8 +15,11 @@ use dioxus_html::{set_event_converter, FormValue, PlatformEventData};
 use futures_util::{pin_mut, FutureExt};
 use futures_util::{pin_mut, FutureExt};
 
 
 use super::event_handler::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
 use super::event_handler::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
-use crate::mutation_writer::{DioxusState, MutationWriter};
 use crate::NodeId;
 use crate::NodeId;
+use crate::{
+    mutation_writer::{DioxusState, MutationWriter},
+    ReservedNativeTexture, SharedNativeTexture,
+};
 
 
 pub(crate) fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName {
 pub(crate) fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName {
     QualName {
     QualName {
@@ -42,7 +45,6 @@ pub struct DioxusDocument {
 }
 }
 
 
 // Implement DocumentLike and required traits for DioxusDocument
 // Implement DocumentLike and required traits for DioxusDocument
-
 impl AsRef<BaseDocument> for DioxusDocument {
 impl AsRef<BaseDocument> for DioxusDocument {
     fn as_ref(&self) -> &BaseDocument {
     fn as_ref(&self) -> &BaseDocument {
         &self.inner
         &self.inner

+ 1 - 1
packages/native/src/event_handler.rs

@@ -180,7 +180,7 @@ impl InteractionLocation for NativeClickData {
 
 
 impl InteractionElementOffset for NativeClickData {
 impl InteractionElementOffset for NativeClickData {
     fn element_coordinates(&self) -> ElementPoint {
     fn element_coordinates(&self) -> ElementPoint {
-        todo!()
+        unimplemented!()
     }
     }
 }
 }
 
 

+ 2 - 0
packages/native/src/lib.rs

@@ -9,6 +9,7 @@
 //!  - `menu`: Enables the [`muda`] menubar.
 //!  - `menu`: Enables the [`muda`] menubar.
 //!  - `tracing`: Enables tracing support.
 //!  - `tracing`: Enables tracing support.
 
 
+mod api;
 mod assets;
 mod assets;
 mod contexts;
 mod contexts;
 mod dioxus_application;
 mod dioxus_application;
@@ -17,6 +18,7 @@ mod event;
 mod event_handler;
 mod event_handler;
 mod mutation_writer;
 mod mutation_writer;
 
 
+pub use api::*;
 pub use dioxus_application::DioxusNativeApplication;
 pub use dioxus_application::DioxusNativeApplication;
 pub use dioxus_document::DioxusDocument;
 pub use dioxus_document::DioxusDocument;
 pub use event::DioxusNativeEvent;
 pub use event::DioxusNativeEvent;

+ 30 - 3
packages/native/src/mutation_writer.rs

@@ -1,14 +1,15 @@
 use crate::{dioxus_document::qual_name, NodeId};
 use crate::{dioxus_document::qual_name, NodeId};
 use blitz_dom::{
 use blitz_dom::{
     local_name, namespace_url,
     local_name, namespace_url,
-    node::{Attribute, NodeSpecificData},
+    node::{Attribute, ImageData, NodeSpecificData},
     ns, BaseDocument, ElementNodeData, NodeData, QualName, RestyleHint,
     ns, BaseDocument, ElementNodeData, NodeData, QualName, RestyleHint,
 };
 };
+use blitz_renderer_vello::BlitzVelloRenderer;
 use dioxus_core::{
 use dioxus_core::{
     AttributeValue, ElementId, Template, TemplateAttribute, TemplateNode, WriteMutations,
     AttributeValue, ElementId, Template, TemplateAttribute, TemplateNode, WriteMutations,
 };
 };
 use rustc_hash::FxHashMap;
 use rustc_hash::FxHashMap;
-use std::collections::HashSet;
+use std::{cell::RefCell, collections::HashSet, rc::Rc};
 
 
 /// The state of the Dioxus integration with the RealDom
 /// The state of the Dioxus integration with the RealDom
 #[derive(Debug)]
 #[derive(Debug)]
@@ -168,11 +169,37 @@ impl MutationWriter<'_> {
                     .collect();
                     .collect();
 
 
                 let id = self.doc.create_node(NodeData::Element(data));
                 let id = self.doc.create_node(NodeData::Element(data));
-                let node = self.doc.get_node(id).unwrap();
+                let node = self.doc.get_node_mut(id).unwrap();
 
 
                 // Initialise style data
                 // Initialise style data
                 *node.stylo_element_data.borrow_mut() = Some(Default::default());
                 *node.stylo_element_data.borrow_mut() = Some(Default::default());
 
 
+                if node.data.downcast_element().unwrap().name.local == local_name!("canvas") {
+                    let width_attr = node.attr(local_name!("width"));
+                    let height_attr = node.attr(local_name!("height"));
+
+                    if let (Some(width), Some(height)) = (width_attr, height_attr) {
+                        let width = width.parse::<u32>().unwrap_or(100);
+                        let height = height.parse::<u32>().unwrap_or(100);
+
+                        match &mut node.data {
+                            NodeData::Element(element_node_data) => {
+                                let texture =
+                                    blitz_dom::node::ImageData::custom_texture(width, height);
+
+                                element_node_data.node_specific_data =
+                                    NodeSpecificData::Image(Box::new(texture.clone()));
+
+                                if let ImageData::CustomTexture(texture) = &texture {
+                                    self.doc.custom_textures.insert(id, texture.clone());
+                                }
+                            }
+                            _ => {}
+                        };
+                    }
+                }
+
+                let node = self.doc.get_node(id).unwrap();
                 if let Some(src_attr) = node.attr(local_name!("src")) {
                 if let Some(src_attr) = node.attr(local_name!("src")) {
                     crate::assets::fetch_image(self.doc, id, src_attr.to_string());
                     crate::assets::fetch_image(self.doc, id, src_attr.to_string());
                 }
                 }