mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-22 17:36:33 +11:00
Merge pull request #314 from linebender/failure
A bit of polishing on the demo
This commit is contained in:
commit
03545e5d9a
8 changed files with 324 additions and 35 deletions
|
@ -104,8 +104,11 @@ async fn render(mut scenes: SceneSet, index: usize, args: &Args) -> Result<()> {
|
||||||
resolution: None,
|
resolution: None,
|
||||||
base_color: None,
|
base_color: None,
|
||||||
interactive: false,
|
interactive: false,
|
||||||
|
complexity: 0,
|
||||||
};
|
};
|
||||||
(example_scene.function)(&mut builder, &mut scene_params);
|
example_scene
|
||||||
|
.function
|
||||||
|
.render(&mut builder, &mut scene_params);
|
||||||
let mut transform = Affine::IDENTITY;
|
let mut transform = Affine::IDENTITY;
|
||||||
let (width, height) = if let Some(resolution) = scene_params.resolution {
|
let (width, height) = if let Some(resolution) = scene_params.resolution {
|
||||||
let ratio = resolution.x / resolution.y;
|
let ratio = resolution.x / resolution.y;
|
||||||
|
|
|
@ -16,6 +16,7 @@ vello_svg = { path = "../../integrations/vello_svg" }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
image = "0.24.5"
|
image = "0.24.5"
|
||||||
|
rand = "0.8.5"
|
||||||
instant = { workspace = true }
|
instant = { workspace = true }
|
||||||
|
|
||||||
# Used for the `download` command
|
# Used for the `download` command
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod download;
|
pub mod download;
|
||||||
mod images;
|
mod images;
|
||||||
|
mod mmark;
|
||||||
mod simple_text;
|
mod simple_text;
|
||||||
mod svg;
|
mod svg;
|
||||||
mod test_scenes;
|
mod test_scenes;
|
||||||
|
@ -25,6 +26,7 @@ pub struct SceneParams<'a> {
|
||||||
pub images: &'a mut ImageCache,
|
pub images: &'a mut ImageCache,
|
||||||
pub resolution: Option<Vec2>,
|
pub resolution: Option<Vec2>,
|
||||||
pub base_color: Option<vello::peniko::Color>,
|
pub base_color: Option<vello::peniko::Color>,
|
||||||
|
pub complexity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SceneConfig {
|
pub struct SceneConfig {
|
||||||
|
@ -34,11 +36,20 @@ pub struct SceneConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExampleScene {
|
pub struct ExampleScene {
|
||||||
#[allow(clippy::type_complexity)]
|
pub function: Box<dyn TestScene>,
|
||||||
pub function: Box<dyn FnMut(&mut SceneBuilder, &mut SceneParams)>,
|
|
||||||
pub config: SceneConfig,
|
pub config: SceneConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TestScene {
|
||||||
|
fn render(&mut self, sb: &mut SceneBuilder, params: &mut SceneParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnMut(&mut SceneBuilder, &mut SceneParams)> TestScene for F {
|
||||||
|
fn render(&mut self, sb: &mut SceneBuilder, params: &mut SceneParams) {
|
||||||
|
self(sb, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SceneSet {
|
pub struct SceneSet {
|
||||||
pub scenes: Vec<ExampleScene>,
|
pub scenes: Vec<ExampleScene>,
|
||||||
}
|
}
|
||||||
|
|
201
examples/scenes/src/mmark.rs
Normal file
201
examples/scenes/src/mmark.rs
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
//! A benchmark based on MotionMark 1.2's path benchmark.
|
||||||
|
//! This is roughly comparable to:
|
||||||
|
//!
|
||||||
|
//! https://browserbench.org/MotionMark1.2/developer.html?warmup-length=2000&warmup-frame-count=30&first-frame-minimum-length=0&test-interval=15&display=minimal&tiles=big&controller=adaptive&frame-rate=50&time-measurement=performance&suite-name=MotionMark&test-name=Paths&complexity=1
|
||||||
|
//!
|
||||||
|
//! However, at this point it cannot be directly compared, as we don't accurately
|
||||||
|
//! implement the stroke style parameters, and it has not been carefully validated.
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
use rand::{seq::SliceRandom, Rng};
|
||||||
|
use vello::peniko::Color;
|
||||||
|
use vello::{
|
||||||
|
kurbo::{Affine, BezPath, CubicBez, Line, ParamCurve, PathSeg, Point, QuadBez},
|
||||||
|
peniko::Stroke,
|
||||||
|
SceneBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{SceneParams, TestScene};
|
||||||
|
|
||||||
|
const WIDTH: usize = 1600;
|
||||||
|
const HEIGHT: usize = 900;
|
||||||
|
|
||||||
|
const GRID_WIDTH: i64 = 80;
|
||||||
|
const GRID_HEIGHT: i64 = 40;
|
||||||
|
|
||||||
|
pub struct MMark {
|
||||||
|
elements: Vec<Element>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Element {
|
||||||
|
seg: PathSeg,
|
||||||
|
color: Color,
|
||||||
|
width: f64,
|
||||||
|
is_split: bool,
|
||||||
|
grid_point: GridPoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct GridPoint(i64, i64);
|
||||||
|
|
||||||
|
impl MMark {
|
||||||
|
pub fn new(n: usize) -> MMark {
|
||||||
|
let mut result = MMark { elements: vec![] };
|
||||||
|
result.resize(n);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&mut self, n: usize) {
|
||||||
|
let old_n = self.elements.len();
|
||||||
|
match n.cmp(&old_n) {
|
||||||
|
Ordering::Less => self.elements.truncate(n),
|
||||||
|
Ordering::Greater => {
|
||||||
|
let mut last = self
|
||||||
|
.elements
|
||||||
|
.last()
|
||||||
|
.map(|e| e.grid_point)
|
||||||
|
.unwrap_or(GridPoint(GRID_WIDTH / 2, GRID_HEIGHT / 2));
|
||||||
|
self.elements.extend((old_n..n).map(|_| {
|
||||||
|
let element = Element::new_rand(last);
|
||||||
|
last = element.grid_point;
|
||||||
|
element
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestScene for MMark {
|
||||||
|
fn render(&mut self, sb: &mut SceneBuilder, params: &mut SceneParams) {
|
||||||
|
let c = params.complexity;
|
||||||
|
let n = if c < 10 {
|
||||||
|
(c + 1) * 1000
|
||||||
|
} else {
|
||||||
|
((c - 8) * 10000).min(120_000)
|
||||||
|
};
|
||||||
|
self.resize(n);
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut path = BezPath::new();
|
||||||
|
let len = self.elements.len();
|
||||||
|
for (i, element) in self.elements.iter_mut().enumerate() {
|
||||||
|
if path.is_empty() {
|
||||||
|
path.move_to(element.seg.start());
|
||||||
|
}
|
||||||
|
match element.seg {
|
||||||
|
PathSeg::Line(l) => path.line_to(l.p1),
|
||||||
|
PathSeg::Quad(q) => path.quad_to(q.p1, q.p2),
|
||||||
|
PathSeg::Cubic(c) => path.curve_to(c.p1, c.p2, c.p3),
|
||||||
|
}
|
||||||
|
if element.is_split || i == len {
|
||||||
|
// This gets color and width from the last element, original
|
||||||
|
// gets it from the first, but this should not matter.
|
||||||
|
sb.stroke(
|
||||||
|
&Stroke::new(element.width as f32),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
element.color,
|
||||||
|
None,
|
||||||
|
&path,
|
||||||
|
);
|
||||||
|
path.truncate(0); // Should have clear method, to avoid allocations.
|
||||||
|
}
|
||||||
|
if rng.gen::<f32>() > 0.995 {
|
||||||
|
element.is_split ^= true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let label = format!("mmark test: {} path elements (up/down to adjust)", n);
|
||||||
|
params.text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
40.0,
|
||||||
|
None,
|
||||||
|
Affine::translate((100.0, 1100.0)),
|
||||||
|
&label,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const COLORS: &[Color] = &[
|
||||||
|
Color::rgb8(0x10, 0x10, 0x10),
|
||||||
|
Color::rgb8(0x80, 0x80, 0x80),
|
||||||
|
Color::rgb8(0xc0, 0xc0, 0xc0),
|
||||||
|
Color::rgb8(0x10, 0x10, 0x10),
|
||||||
|
Color::rgb8(0x80, 0x80, 0x80),
|
||||||
|
Color::rgb8(0xc0, 0xc0, 0xc0),
|
||||||
|
Color::rgb8(0xe0, 0x10, 0x40),
|
||||||
|
];
|
||||||
|
|
||||||
|
impl Element {
|
||||||
|
fn new_rand(last: GridPoint) -> Element {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let seg_type = rng.gen_range(0..4);
|
||||||
|
let next = GridPoint::random_point(last);
|
||||||
|
let (grid_point, seg) = if seg_type < 2 {
|
||||||
|
(
|
||||||
|
next,
|
||||||
|
PathSeg::Line(Line::new(last.coordinate(), next.coordinate())),
|
||||||
|
)
|
||||||
|
} else if seg_type < 3 {
|
||||||
|
let p2 = GridPoint::random_point(next);
|
||||||
|
(
|
||||||
|
p2,
|
||||||
|
PathSeg::Quad(QuadBez::new(
|
||||||
|
last.coordinate(),
|
||||||
|
next.coordinate(),
|
||||||
|
p2.coordinate(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let p2 = GridPoint::random_point(next);
|
||||||
|
let p3 = GridPoint::random_point(next);
|
||||||
|
(
|
||||||
|
p3,
|
||||||
|
PathSeg::Cubic(CubicBez::new(
|
||||||
|
last.coordinate(),
|
||||||
|
next.coordinate(),
|
||||||
|
p2.coordinate(),
|
||||||
|
p3.coordinate(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let color = *COLORS.choose(&mut rng).unwrap();
|
||||||
|
let width = rng.gen::<f64>().powi(5) * 20.0 + 1.0;
|
||||||
|
let is_split = rng.gen();
|
||||||
|
Element {
|
||||||
|
seg,
|
||||||
|
color,
|
||||||
|
width,
|
||||||
|
is_split,
|
||||||
|
grid_point,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const OFFSETS: &[(i64, i64)] = &[(-4, 0), (2, 0), (1, -2), (1, 2)];
|
||||||
|
|
||||||
|
impl GridPoint {
|
||||||
|
fn random_point(last: GridPoint) -> GridPoint {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
let offset = OFFSETS.choose(&mut rng).unwrap();
|
||||||
|
let mut x = last.0 + offset.0;
|
||||||
|
if !(0..=GRID_WIDTH).contains(&x) {
|
||||||
|
x -= offset.0 * 2;
|
||||||
|
}
|
||||||
|
let mut y = last.1 + offset.1;
|
||||||
|
if !(0..=GRID_HEIGHT).contains(&y) {
|
||||||
|
y -= offset.1 * 2;
|
||||||
|
}
|
||||||
|
GridPoint(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coordinate(&self) -> Point {
|
||||||
|
let scale_x = WIDTH as f64 / ((GRID_WIDTH + 1) as f64);
|
||||||
|
let scale_y = HEIGHT as f64 / ((GRID_HEIGHT + 1) as f64);
|
||||||
|
Point::new(
|
||||||
|
(self.0 as f64 + 0.5) * scale_x,
|
||||||
|
100.0 + (self.1 as f64 + 0.5) * scale_y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,9 +24,23 @@ macro_rules! scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_scenes() -> SceneSet {
|
pub fn test_scenes() -> SceneSet {
|
||||||
// For WASM below, must be mutable
|
let splash_scene = ExampleScene {
|
||||||
#[allow(unused_mut)]
|
config: SceneConfig {
|
||||||
let mut scenes = vec![
|
animated: false,
|
||||||
|
name: "splash_with_tiger".to_owned(),
|
||||||
|
},
|
||||||
|
function: Box::new(splash_with_tiger()),
|
||||||
|
};
|
||||||
|
let mmark_scene = ExampleScene {
|
||||||
|
config: SceneConfig {
|
||||||
|
animated: false,
|
||||||
|
name: "mmark".to_owned(),
|
||||||
|
},
|
||||||
|
function: Box::new(crate::mmark::MMark::new(80_000)),
|
||||||
|
};
|
||||||
|
let scenes = vec![
|
||||||
|
splash_scene,
|
||||||
|
mmark_scene,
|
||||||
scene!(funky_paths),
|
scene!(funky_paths),
|
||||||
scene!(cardioid_and_friends),
|
scene!(cardioid_and_friends),
|
||||||
scene!(animated_text: animated),
|
scene!(animated_text: animated),
|
||||||
|
@ -38,14 +52,6 @@ pub fn test_scenes() -> SceneSet {
|
||||||
scene!(labyrinth),
|
scene!(labyrinth),
|
||||||
scene!(base_color_test: animated),
|
scene!(base_color_test: animated),
|
||||||
];
|
];
|
||||||
#[cfg(any(target_arch = "wasm32", target_os = "android"))]
|
|
||||||
scenes.push(ExampleScene {
|
|
||||||
config: SceneConfig {
|
|
||||||
animated: false,
|
|
||||||
name: "included_tiger".to_owned(),
|
|
||||||
},
|
|
||||||
function: Box::new(included_tiger()),
|
|
||||||
});
|
|
||||||
|
|
||||||
SceneSet { scenes }
|
SceneSet { scenes }
|
||||||
}
|
}
|
||||||
|
@ -491,15 +497,6 @@ fn blend_grid(sb: &mut SceneBuilder, _: &mut SceneParams) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_arch = "wasm32", target_os = "android"))]
|
|
||||||
fn included_tiger() -> impl FnMut(&mut SceneBuilder, &mut SceneParams) {
|
|
||||||
let contents = include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/../assets/Ghostscript_Tiger.svg"
|
|
||||||
));
|
|
||||||
crate::svg::svg_function_of("Ghostscript Tiger".to_string(), move || contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support functions
|
// Support functions
|
||||||
|
|
||||||
fn render_cardioid(sb: &mut SceneBuilder) {
|
fn render_cardioid(sb: &mut SceneBuilder) {
|
||||||
|
@ -841,3 +838,39 @@ fn make_diamond(cx: f64, cy: f64) -> [PathEl; 5] {
|
||||||
PathEl::ClosePath,
|
PathEl::ClosePath,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn splash_screen(sb: &mut SceneBuilder, params: &mut SceneParams) {
|
||||||
|
let strings = [
|
||||||
|
"Vello test",
|
||||||
|
" Arrow keys: switch scenes",
|
||||||
|
" Space: reset transform",
|
||||||
|
" S: toggle stats",
|
||||||
|
" V: toggle vsync",
|
||||||
|
" Q, E: rotate",
|
||||||
|
];
|
||||||
|
// Tweak to make it fit with tiger
|
||||||
|
let a = Affine::scale(0.12) * Affine::translate((-90.0, -50.0));
|
||||||
|
for (i, s) in strings.iter().enumerate() {
|
||||||
|
let text_size = if i == 0 { 60.0 } else { 40.0 };
|
||||||
|
params.text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
text_size,
|
||||||
|
None,
|
||||||
|
a * Affine::translate((100.0, 100.0 + 60.0 * i as f64)),
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn splash_with_tiger() -> impl FnMut(&mut SceneBuilder, &mut SceneParams) {
|
||||||
|
let contents = include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/../assets/Ghostscript_Tiger.svg"
|
||||||
|
));
|
||||||
|
let mut tiger = crate::svg::svg_function_of("Ghostscript Tiger".to_string(), move || contents);
|
||||||
|
move |sb, params| {
|
||||||
|
tiger(sb, params);
|
||||||
|
splash_screen(sb, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,4 +45,4 @@ android_logger = "0.13.0"
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
console_log = "0.2"
|
console_log = "0.2"
|
||||||
wasm-bindgen-futures = "0.4.33"
|
wasm-bindgen-futures = "0.4.33"
|
||||||
web-sys = "0.3.60"
|
web-sys = { version = "0.3.60", features = [ "HtmlCollection", "Text" ] }
|
||||||
|
|
|
@ -117,6 +117,7 @@ fn run(
|
||||||
let mut prior_position: Option<Vec2> = None;
|
let mut prior_position: Option<Vec2> = None;
|
||||||
// We allow looping left and right through the scenes, so use a signed index
|
// We allow looping left and right through the scenes, so use a signed index
|
||||||
let mut scene_ix: i32 = 0;
|
let mut scene_ix: i32 = 0;
|
||||||
|
let mut complexity: usize = 0;
|
||||||
if let Some(set_scene) = args.scene {
|
if let Some(set_scene) = args.scene {
|
||||||
scene_ix = set_scene;
|
scene_ix = set_scene;
|
||||||
}
|
}
|
||||||
|
@ -138,6 +139,8 @@ fn run(
|
||||||
match input.virtual_keycode {
|
match input.virtual_keycode {
|
||||||
Some(VirtualKeyCode::Left) => scene_ix = scene_ix.saturating_sub(1),
|
Some(VirtualKeyCode::Left) => scene_ix = scene_ix.saturating_sub(1),
|
||||||
Some(VirtualKeyCode::Right) => scene_ix = scene_ix.saturating_add(1),
|
Some(VirtualKeyCode::Right) => scene_ix = scene_ix.saturating_add(1),
|
||||||
|
Some(VirtualKeyCode::Up) => complexity += 1,
|
||||||
|
Some(VirtualKeyCode::Down) => complexity = complexity.saturating_sub(1),
|
||||||
Some(key @ VirtualKeyCode::Q) | Some(key @ VirtualKeyCode::E) => {
|
Some(key @ VirtualKeyCode::Q) | Some(key @ VirtualKeyCode::E) => {
|
||||||
if let Some(prior_position) = prior_position {
|
if let Some(prior_position) = prior_position {
|
||||||
let is_clockwise = key == VirtualKeyCode::E;
|
let is_clockwise = key == VirtualKeyCode::E;
|
||||||
|
@ -302,8 +305,11 @@ fn run(
|
||||||
resolution: None,
|
resolution: None,
|
||||||
base_color: None,
|
base_color: None,
|
||||||
interactive: true,
|
interactive: true,
|
||||||
|
complexity,
|
||||||
};
|
};
|
||||||
(example_scene.function)(&mut builder, &mut scene_params);
|
example_scene
|
||||||
|
.function
|
||||||
|
.render(&mut builder, &mut scene_params);
|
||||||
|
|
||||||
// If the user specifies a base color in the CLI we use that. Otherwise we use any
|
// If the user specifies a base color in the CLI we use that. Otherwise we use any
|
||||||
// color specified by the scene. The default is black.
|
// color specified by the scene. The default is black.
|
||||||
|
@ -421,7 +427,7 @@ fn run(
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
let surface_future = render_cx.create_surface(&window, size.width, size.height);
|
let surface_future = render_cx.create_surface(&window, size.width, size.height);
|
||||||
// We need to block here, in case a Suspended event appeared
|
// We need to block here, in case a Suspended event appeared
|
||||||
let surface = pollster::block_on(surface_future);
|
let surface = pollster::block_on(surface_future).expect("Error creating surface");
|
||||||
render_state = {
|
render_state = {
|
||||||
let render_state = RenderState { window, surface };
|
let render_state = RenderState { window, surface };
|
||||||
renderers.resize_with(render_cx.devices.len(), || None);
|
renderers.resize_with(render_cx.devices.len(), || None);
|
||||||
|
@ -461,6 +467,27 @@ enum UserEvent {
|
||||||
HotReload,
|
HotReload,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn display_error_message() -> Option<()> {
|
||||||
|
let window = web_sys::window()?;
|
||||||
|
let document = window.document()?;
|
||||||
|
let elements = document.get_elements_by_tag_name("body");
|
||||||
|
let body = elements.item(0)?;
|
||||||
|
body.set_inner_html(
|
||||||
|
r#"<style>
|
||||||
|
p {
|
||||||
|
margin: 2em 10em;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<p><a href="https://caniuse.com/webgpu">WebGPU</a>
|
||||||
|
is not enabled. Make sure your browser is updated to
|
||||||
|
<a href="https://chromiumdash.appspot.com/schedule">Chrome M113</a> or
|
||||||
|
another browser compatible with WebGPU.</p>"#,
|
||||||
|
);
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() -> Result<()> {
|
pub fn main() -> Result<()> {
|
||||||
// TODO: initializing both env_logger and console_logger fails on wasm.
|
// TODO: initializing both env_logger and console_logger fails on wasm.
|
||||||
// Figure out a more principled approach.
|
// Figure out a more principled approach.
|
||||||
|
@ -497,16 +524,21 @@ pub fn main() -> Result<()> {
|
||||||
web_sys::window()
|
web_sys::window()
|
||||||
.and_then(|win| win.document())
|
.and_then(|win| win.document())
|
||||||
.and_then(|doc| doc.body())
|
.and_then(|doc| doc.body())
|
||||||
.and_then(|body| body.append_child(&web_sys::Element::from(canvas)).ok())
|
.and_then(|body| body.append_child(canvas.as_ref()).ok())
|
||||||
.expect("couldn't append canvas to document body");
|
.expect("couldn't append canvas to document body");
|
||||||
|
_ = web_sys::HtmlElement::from(canvas).focus();
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
let surface = render_cx
|
let surface = render_cx
|
||||||
.create_surface(&window, size.width, size.height)
|
.create_surface(&window, size.width, size.height)
|
||||||
.await;
|
.await;
|
||||||
|
if let Ok(surface) = surface {
|
||||||
let render_state = RenderState { window, surface };
|
let render_state = RenderState { window, surface };
|
||||||
// No error handling here; if the event loop has finished, we don't need to send them the surface
|
// No error handling here; if the event loop has finished, we don't need to send them the surface
|
||||||
run(event_loop, args, scenes, render_cx, render_state);
|
run(event_loop, args, scenes, render_cx, render_state);
|
||||||
|
} else {
|
||||||
|
_ = display_error_message();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
src/util.rs
18
src/util.rs
|
@ -50,12 +50,20 @@ impl RenderContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new surface for the specified window and dimensions.
|
/// Creates a new surface for the specified window and dimensions.
|
||||||
pub async fn create_surface<W>(&mut self, window: &W, width: u32, height: u32) -> RenderSurface
|
pub async fn create_surface<W>(
|
||||||
|
&mut self,
|
||||||
|
window: &W,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
) -> Result<RenderSurface>
|
||||||
where
|
where
|
||||||
W: HasRawWindowHandle + HasRawDisplayHandle,
|
W: HasRawWindowHandle + HasRawDisplayHandle,
|
||||||
{
|
{
|
||||||
let surface = unsafe { self.instance.create_surface(window) }.unwrap();
|
let surface = unsafe { self.instance.create_surface(window) }?;
|
||||||
let dev_id = self.device(Some(&surface)).await.unwrap();
|
let dev_id = self
|
||||||
|
.device(Some(&surface))
|
||||||
|
.await
|
||||||
|
.ok_or("Error creating device")?;
|
||||||
|
|
||||||
let device_handle = &self.devices[dev_id];
|
let device_handle = &self.devices[dev_id];
|
||||||
let capabilities = surface.get_capabilities(&device_handle.adapter);
|
let capabilities = surface.get_capabilities(&device_handle.adapter);
|
||||||
|
@ -75,12 +83,12 @@ impl RenderContext {
|
||||||
view_formats: vec![],
|
view_formats: vec![],
|
||||||
};
|
};
|
||||||
surface.configure(&self.devices[dev_id].device, &config);
|
surface.configure(&self.devices[dev_id].device, &config);
|
||||||
RenderSurface {
|
Ok(RenderSurface {
|
||||||
surface,
|
surface,
|
||||||
config,
|
config,
|
||||||
dev_id,
|
dev_id,
|
||||||
format,
|
format,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resizes the surface to the new dimensions.
|
/// Resizes the surface to the new dimensions.
|
||||||
|
|
Loading…
Add table
Reference in a new issue