blob: d7147c233b783197778d2be539f8e1152cee6961 [file] [log] [blame] [edit]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use instant::Instant;
use vello::kurbo::Circle;
use anyhow::Result;
use vello::peniko::{Brush, Color, Style};
use vello::util::RenderSurface;
use vello::RendererOptions;
use vello::{kurbo::Affine, util::RenderContext, AaConfig, Renderer, Scene, SceneBuilder};
use winit::{event_loop::EventLoop, window::Window};
use crate::simple_text::SimpleText;
pub mod simple_text;
struct RenderState {
// SAFETY: We MUST drop the surface before the `window`, so the fields
// must be in this order
surface: RenderSurface,
window: Window,
}
fn run(event_loop: EventLoop<()>) {
use winit::{event::*, event_loop::ControlFlow};
let mut renderers: Vec<Option<Renderer>> = vec![];
let mut render_cx = RenderContext::new().unwrap();
let mut render_state = None::<RenderState>;
let use_cpu = false;
// Whilst suspended, we drop `render_state`, but need to keep the same window.
// If render_state exists, we must store the window in it, to maintain drop order
let mut cached_window = None;
let mut scene = Scene::new();
let mut simple_text = SimpleText::new();
let mut vsync_on = true;
#[allow(unused)]
let start = Instant::now();
// _event_loop is used on non-wasm platforms to create new windows
event_loop.run(move |event, _event_loop, control_flow| match event {
Event::WindowEvent {
ref event,
window_id,
} => {
let Some(render_state) = &mut render_state else {
return;
};
if render_state.window.id() != window_id {
return;
}
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
// WindowEvent::ModifiersChanged(m) => modifiers = *m,
WindowEvent::KeyboardInput { input, .. } => {
if input.state == ElementState::Pressed {
match input.virtual_keycode {
Some(VirtualKeyCode::V) => {
// Toggle vsync
vsync_on = !vsync_on;
render_cx.set_present_mode(
&mut render_state.surface,
if vsync_on {
wgpu::PresentMode::AutoVsync
} else {
wgpu::PresentMode::AutoNoVsync
},
);
}
Some(VirtualKeyCode::Escape) => {
*control_flow = ControlFlow::Exit;
}
_ => {}
}
}
}
WindowEvent::Resized(size) => {
render_cx.resize_surface(&mut render_state.surface, size.width, size.height);
render_state.window.request_redraw();
}
_ => {}
}
}
Event::MainEventsCleared => {
if let Some(render_state) = &mut render_state {
render_state.window.request_redraw();
}
}
Event::RedrawRequested(_) => {
let Some(render_state) = &mut render_state else {
return;
};
let width = render_state.surface.config.width;
let height = render_state.surface.config.height;
let device_handle = &render_cx.devices[render_state.surface.dev_id];
let mut builder = SceneBuilder::for_scene(&mut scene);
{
// Draw your scene here
builder.fill(
vello::peniko::Fill::NonZero,
Affine::skew(1.0, 1.5),
&Brush::Solid(Color::rgb(255., 100., 0.)),
None,
&Circle::new((300., 300.), 200.),
);
simple_text.add_run(
&mut builder,
None,
24.,
&Brush::Solid(Color::WHITE),
Affine::translate((100., 200.)),
None,
&Style::Fill(vello::peniko::Fill::EvenOdd),
"Hello Rustlab 2023 Hacknight!",
)
}
// The base colour is the background colour
let base_color = Color::BLACK;
let render_params = vello::RenderParams {
base_color,
width,
height,
antialiasing_method: AaConfig::Area,
};
let surface_texture = render_state
.surface
.surface
.get_current_texture()
.expect("failed to get surface texture");
vello::block_on_wgpu(
&device_handle.device,
renderers[render_state.surface.dev_id]
.as_mut()
.unwrap()
.render_to_surface_async(
&device_handle.device,
&device_handle.queue,
&scene,
&surface_texture,
&render_params,
),
)
.expect("failed to render to surface");
surface_texture.present();
device_handle.device.poll(wgpu::Maintain::Poll);
}
Event::Suspended => {
// When we suspend, we need to remove the `wgpu` Surface
if let Some(render_state) = render_state.take() {
cached_window = Some(render_state.window);
}
*control_flow = ControlFlow::Wait;
}
Event::Resumed => {
let Option::None = render_state else { return };
let window = cached_window
.take()
.unwrap_or_else(|| create_window(_event_loop));
let size = window.inner_size();
let surface_future = render_cx.create_surface(&window, size.width, size.height);
// We need to block here, in case a Suspended event appeared
let surface = pollster::block_on(surface_future).expect("Error creating surface");
render_state = {
let render_state = RenderState { window, surface };
renderers.resize_with(render_cx.devices.len(), || None);
let id = render_state.surface.dev_id;
renderers[id].get_or_insert_with(|| {
eprintln!("Creating renderer {id}");
Renderer::new(
&render_cx.devices[id].device,
RendererOptions {
surface_format: Some(render_state.surface.format),
timestamp_period: render_cx.devices[id].queue.get_timestamp_period(),
use_cpu,
antialiasing_support: vello::AaSupport::all(),
},
)
.expect("Could create renderer")
});
Some(render_state)
};
*control_flow = ControlFlow::Poll;
}
_ => {}
});
}
fn create_window(event_loop: &winit::event_loop::EventLoopWindowTarget<()>) -> Window {
use winit::{dpi::LogicalSize, window::WindowBuilder};
WindowBuilder::new()
.with_inner_size(LogicalSize::new(1044, 800))
.with_resizable(true)
.with_title("Vello demo")
.build(event_loop)
.unwrap()
}
pub fn main() -> Result<()> {
// TODO: initializing both env_logger and console_logger fails on wasm.
// Figure out a more principled approach.
#[cfg(not(target_arch = "wasm32"))]
env_logger::init();
let event_loop = EventLoop::new();
run(event_loop);
Ok(())
}