Example of rendering a dioxus-native app to a texture (#4365)
* Create example of rendering a dioxus-native app to a texture Signed-off-by: Nico Burns <nico@nicoburns.com> * Use dioxus-native-dom crate Signed-off-by: Nico Burns <nico@nicoburns.com> * bump lock * cleanup cargo tomls * rustc hash --------- Signed-off-by: Nico Burns <nico@nicoburns.com> Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
b9b847c3f8
commit
9db593e303
7 files changed
+243
-11
No files matched your search
Generated
+30
@@ -2822,6 +2822,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"usvg",
|
"usvg",
|
||||||
|
"woff",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -10632,6 +10633,25 @@ dependencies = [
|
|||||||
"jobserver",
|
"jobserver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "native-headless"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyrender_vello",
|
||||||
|
"blitz-dom",
|
||||||
|
"blitz-paint",
|
||||||
|
"blitz-traits",
|
||||||
|
"bytemuck",
|
||||||
|
"dioxus",
|
||||||
|
"dioxus-native-dom",
|
||||||
|
"futures-util",
|
||||||
|
"pollster",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"vello",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -19177,6 +19197,16 @@ dependencies = [
|
|||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "woff"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e229335e006d138b1c318e83b99eaa3e1ab03e08c4ced4967e46705e6e9d1cf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "writeable"
|
name = "writeable"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
|
|||||||
+10
-1
@@ -114,6 +114,7 @@ members = [
|
|||||||
"examples/fullstack-auth",
|
"examples/fullstack-auth",
|
||||||
"examples/fullstack-websockets",
|
"examples/fullstack-websockets",
|
||||||
"examples/wgpu-texture",
|
"examples/wgpu-texture",
|
||||||
|
"examples/native-headless",
|
||||||
"examples/bevy",
|
"examples/bevy",
|
||||||
|
|
||||||
# Playwright tests
|
# Playwright tests
|
||||||
@@ -201,6 +202,14 @@ wasm-used = { path = "packages/wasm-split/wasm-used", version = "0.7.0-alpha.2"
|
|||||||
depinfo = { path = "packages/depinfo", version = "0.7.0-alpha.2" }
|
depinfo = { path = "packages/depinfo", version = "0.7.0-alpha.2" }
|
||||||
warnings = { version = "0.2.1" }
|
warnings = { version = "0.2.1" }
|
||||||
|
|
||||||
|
# blitz
|
||||||
|
blitz-dom = { version = "=0.1.0-alpha.5", default-features = false }
|
||||||
|
blitz-net = { version = "=0.1.0-alpha.5" }
|
||||||
|
blitz-paint = { version = "=0.1.0-alpha.5" }
|
||||||
|
blitz-traits = { version = "=0.1.0-alpha.5" }
|
||||||
|
blitz-shell = { version = "=0.1.0-alpha.5", default-features = false }
|
||||||
|
wgpu = { version = "24.0" }
|
||||||
|
|
||||||
# a fork of pretty please for tests - let's get off of this if we can!
|
# a fork of pretty please for tests - let's get off of this if we can!
|
||||||
prettier-please = { version = "0.3.0", features = ["verbatim"] }
|
prettier-please = { version = "0.3.0", features = ["verbatim"] }
|
||||||
anyhow = "1.0.98"
|
anyhow = "1.0.98"
|
||||||
@@ -412,7 +421,7 @@ reqwest = { workspace = true, features = ["json"], optional = true }
|
|||||||
ciborium = { workspace = true, optional = true }
|
ciborium = { workspace = true, optional = true }
|
||||||
base64 = { workspace = true, optional = true }
|
base64 = { workspace = true, optional = true }
|
||||||
http-range = { version = "0.1.5", optional = true }
|
http-range = { version = "0.1.5", optional = true }
|
||||||
wgpu = { version = "24.0", optional = true }
|
wgpu = { workspace = true, optional = true }
|
||||||
ouroboros = { version = "*", optional = true }
|
ouroboros = { version = "*", optional = true }
|
||||||
wasm-splitter = { workspace = true, package = "wasm-split" }
|
wasm-splitter = { workspace = true, package = "wasm-split" }
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ tracing = ["dep:tracing-subscriber", "dioxus-native/tracing"]
|
|||||||
bevy = { version = "0.16" }
|
bevy = { version = "0.16" }
|
||||||
dioxus-native = { path = "../../packages/native" }
|
dioxus-native = { path = "../../packages/native" }
|
||||||
dioxus = { workspace = true }
|
dioxus = { workspace = true }
|
||||||
wgpu = "24"
|
wgpu = { workspace = true }
|
||||||
color = "0.3"
|
color = "0.3"
|
||||||
tracing-subscriber = { workspace = true, optional = true }
|
tracing-subscriber = { workspace = true, optional = true }
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "native-headless"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
tracing = ["dep:tracing-subscriber", "dioxus-native-dom/tracing"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dioxus = { workspace = true, default-features = false, features = ["html", "hooks", "signals"] }
|
||||||
|
dioxus-native-dom = { path = "../../packages/native-dom" }
|
||||||
|
anyrender_vello = "0.4.1"
|
||||||
|
vello = "0.5"
|
||||||
|
rustc-hash = "1"
|
||||||
|
futures-util = { workspace = true }
|
||||||
|
tracing-subscriber = { workspace = true, optional = true }
|
||||||
|
wgpu = { workspace = true }
|
||||||
|
pollster = "0.4"
|
||||||
|
bytemuck = "1"
|
||||||
|
blitz-paint = { workspace = true, default-features = true }
|
||||||
|
blitz-traits = { workspace = true, default-features = true }
|
||||||
|
blitz-dom = { workspace = true, default-features = true }
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
//! A "sketch" of how to integrate a Dioxus Native app to into a wider application
|
||||||
|
//! by rendering the UI to a texture and driving it with your own event loop
|
||||||
|
//!
|
||||||
|
//! (this example is not really intended to be run as-is, and requires you to fill
|
||||||
|
//! in the missing pieces)
|
||||||
|
use anyrender_vello::{wgpu_context::WGPUContext, CustomPaintSource, VelloScenePainter};
|
||||||
|
use blitz_dom::Document as _;
|
||||||
|
use blitz_paint::paint_scene;
|
||||||
|
use blitz_traits::{
|
||||||
|
events::{BlitzMouseButtonEvent, MouseEventButton, MouseEventButtons, UiEvent},
|
||||||
|
shell::{ColorScheme, Viewport},
|
||||||
|
};
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_native_dom::DioxusDocument;
|
||||||
|
use pollster::FutureExt as _;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::task::Context;
|
||||||
|
use vello::{
|
||||||
|
peniko::color::AlphaColor, RenderParams, Renderer as VelloRenderer, RendererOptions, Scene,
|
||||||
|
};
|
||||||
|
use wgpu::TextureFormat;
|
||||||
|
|
||||||
|
// Constant width, height, scale factor and color schemefor example purposes
|
||||||
|
const SCALE_FACTOR: f32 = 1.0;
|
||||||
|
const WIDTH: u32 = 800;
|
||||||
|
const HEIGHT: u32 = 600;
|
||||||
|
const COLOR_SCHEME: ColorScheme = ColorScheme::Light;
|
||||||
|
|
||||||
|
// Example Dioxus app.
|
||||||
|
fn app() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div { "Hello, world!" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// INITIAL SETUP
|
||||||
|
// =============
|
||||||
|
|
||||||
|
let waker = create_waker(Box::new(|| {
|
||||||
|
// This should wake up and "poll" your event loop
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Create the dioxus virtual dom and the dioxus-native document
|
||||||
|
// It is important to set the width, height, and scale factor on the document as these are used for layout.
|
||||||
|
let vdom = VirtualDom::new(app);
|
||||||
|
let mut dioxus_doc = DioxusDocument::new(vdom, None);
|
||||||
|
dioxus_doc.set_viewport(Viewport::new(WIDTH, HEIGHT, SCALE_FACTOR, COLOR_SCHEME));
|
||||||
|
|
||||||
|
// Setup a WGPU Device and Queue
|
||||||
|
//
|
||||||
|
// There is nothing special about WGPUContext. It is just used to
|
||||||
|
// reduce the amount of boilerplate associated with setting up WGPU
|
||||||
|
let mut wgpu_context = WGPUContext::new();
|
||||||
|
let device_id = wgpu_context
|
||||||
|
.find_or_create_device(None)
|
||||||
|
.block_on()
|
||||||
|
.expect("Failed to create WGPU device");
|
||||||
|
let device_handle = &wgpu_context.device_pool[device_id];
|
||||||
|
let device = device_handle.device.clone();
|
||||||
|
let queue = device_handle.queue.clone();
|
||||||
|
|
||||||
|
// Create Vello renderer
|
||||||
|
// Note: creating a VelloRenderer is expensive, so it should be done once per Device.
|
||||||
|
let mut vello_renderer = VelloRenderer::new(&device, RendererOptions::default()).unwrap();
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// CREATE TEXTURE (RECREATE ON RESIZE)
|
||||||
|
// =============
|
||||||
|
|
||||||
|
// Create texture and texture view
|
||||||
|
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: WIDTH,
|
||||||
|
height: HEIGHT,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
|
format: TextureFormat::Rgba8Unorm,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// EACH FRAME
|
||||||
|
// =============
|
||||||
|
|
||||||
|
// Poll the vdom
|
||||||
|
dioxus_doc.poll(Context::from_waker(&waker));
|
||||||
|
|
||||||
|
// Create a `VelloScenePainter` to paint into
|
||||||
|
let mut custom_paint_sources =
|
||||||
|
FxHashMap::<u64, Box<dyn CustomPaintSource + 'static>>::default();
|
||||||
|
let mut scene_painter = VelloScenePainter {
|
||||||
|
inner: Scene::new(),
|
||||||
|
renderer: &mut vello_renderer,
|
||||||
|
custom_paint_sources: &mut custom_paint_sources,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Paint the document using `blitz_paint::paint_scene`
|
||||||
|
//
|
||||||
|
// Note: the `paint_scene` will work with any implementation of `anyrender::PaintScene`
|
||||||
|
// so you could also write your own implementation if you want more control over rendering
|
||||||
|
// (i.e. to render a custom renderer instead of Vello)
|
||||||
|
paint_scene(
|
||||||
|
&mut scene_painter,
|
||||||
|
&dioxus_doc,
|
||||||
|
SCALE_FACTOR as f64,
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Extract the `vello::Scene` from the `VelloScenePainter`
|
||||||
|
let scene = scene_painter.finish();
|
||||||
|
|
||||||
|
// Render the `vello::Scene` to the Texture using the `VelloRenderer`
|
||||||
|
vello_renderer
|
||||||
|
.render_to_texture(
|
||||||
|
&device,
|
||||||
|
&queue,
|
||||||
|
&scene,
|
||||||
|
&texture_view,
|
||||||
|
&RenderParams {
|
||||||
|
base_color: AlphaColor::TRANSPARENT,
|
||||||
|
width: WIDTH,
|
||||||
|
height: HEIGHT,
|
||||||
|
antialiasing_method: vello::AaConfig::Msaa16,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("failed to render to texture");
|
||||||
|
|
||||||
|
// `texture` will now contain the rendered Scene
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// EVENT HANDLING
|
||||||
|
// =============
|
||||||
|
|
||||||
|
let event = UiEvent::MouseDown(BlitzMouseButtonEvent {
|
||||||
|
x: 30.0,
|
||||||
|
y: 40.0,
|
||||||
|
button: MouseEventButton::Main,
|
||||||
|
buttons: MouseEventButtons::Primary, // keep track of all pressed buttons
|
||||||
|
mods: Modifiers::empty(), // ctrl, alt, shift, etc
|
||||||
|
});
|
||||||
|
dioxus_doc.handle_event(event);
|
||||||
|
|
||||||
|
// Trigger a poll via your event loop (or wait for next frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a waker that will call an arbitrary callback
|
||||||
|
pub fn create_waker(callback: Box<dyn Fn() + 'static + Send + Sync>) -> std::task::Waker {
|
||||||
|
struct DomHandle {
|
||||||
|
callback: Box<dyn Fn() + 'static + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl futures_util::task::ArcWake for DomHandle {
|
||||||
|
fn wake_by_ref(arc_self: &Arc<Self>) {
|
||||||
|
(arc_self.callback)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
futures_util::task::waker(Arc::new(DomHandle { callback }))
|
||||||
|
}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
# Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "wgpu-texture"
|
name = "wgpu-texture"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@@ -17,7 +14,7 @@ tracing = ["dep:tracing-subscriber", "dioxus-native/tracing"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-native = { path = "../../packages/native" }
|
dioxus-native = { path = "../../packages/native" }
|
||||||
dioxus = { workspace = true }
|
dioxus = { workspace = true }
|
||||||
wgpu = "24"
|
wgpu = { workspace = true }
|
||||||
winit = "0.30"
|
winit = "0.30"
|
||||||
bytemuck = "1"
|
bytemuck = "1"
|
||||||
color = "0.3"
|
color = "0.3"
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ autofocus = []
|
|||||||
# Blitz dependencies
|
# Blitz dependencies
|
||||||
anyrender = { version = "0.4", default-features = false }
|
anyrender = { version = "0.4", default-features = false }
|
||||||
anyrender_vello = { version = "0.4", default-features = false }
|
anyrender_vello = { version = "0.4", default-features = false }
|
||||||
blitz-dom = { version = "=0.1.0-alpha.5", default-features = false }
|
blitz-dom = { workspace = true, default-features = false }
|
||||||
blitz-net = { version = "=0.1.0-alpha.5", optional = true }
|
blitz-net = { workspace = true, optional = true }
|
||||||
blitz-paint = { version = "=0.1.0-alpha.5", optional = true }
|
blitz-paint = { workspace = true, optional = true }
|
||||||
blitz-traits = { version = "=0.1.0-alpha.5" }
|
blitz-traits = { workspace = true }
|
||||||
blitz-shell = { version = "=0.1.0-alpha.5", default-features = false }
|
blitz-shell = { workspace = true, default-features = false }
|
||||||
|
|
||||||
# DioxusLabs dependencies
|
# DioxusLabs dependencies
|
||||||
dioxus-core = { workspace = true }
|
dioxus-core = { workspace = true }
|
||||||
|
|||||||
Reference in new issue
Block a user