Compare commits

...

12 Commits

Author SHA1 Message Date
Benjamin Hansen ce957226d3 cargo fmt 3 weeks ago
Benjamin Hansen 09c1024a8f fixed non-srgb on hdr tutorial 3 weeks ago
Benjamin Hansen 3d27fca202 demos working 3 weeks ago
Benjamin Hansen 5eacaf4e9c fixed model code 4 weeks ago
Benjamin Hansen 8bb1b080b4 fixed up to model loading for wasm 4 weeks ago
Benjamin Hansen 202012b76d switch to `Backends::PRIMARY` 2 months ago
Benjamin Hansen 3e618336bd not crashing 2 months ago
Benjamin Hansen 6325590051 got pong working natively 2 months ago
Benjamin Hansen 4daff65c34 more cleanup 3 months ago
Benjamin Hansen 7c290e7436 cleaning up and fixing snow 3 months ago
Benjamin Hansen 12f115dfa1 code is working again 3 months ago
Benjamin Hansen 3f0caa6904 migrated tutorials 1 and 2 4 months ago

1027
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -13,19 +13,19 @@ path = "src/main.rs"
[dependencies]
cfg-if = "1"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
log = "0.4"
wgpu = "0.18"
wgpu = "0.19"
pollster = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wasm-bindgen = "=0.2.87"
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4.30"
web-sys = { version = "0.3.53", features = [
web-sys = { version = "0.3.69", features = [
"Document",
"Window",
"Element",

@ -1,6 +1,7 @@
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::WindowBuilder,
};
@ -12,13 +13,13 @@ pub fn run() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Couldn't initialize logger");
console_log::init_with_level(log::Level::Debug).expect("Couldn't initialize logger");
} else {
env_logger::init();
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -26,37 +27,42 @@ pub fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == window.id() => match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
event_loop
.run(move |event, control_flow| match event {
Event::Resumed => {
log::debug!("Resumed");
}
Event::WindowEvent {
ref event,
window_id,
} if window_id == window.id() => match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
_ => {}
},
_ => {}
},
_ => {}
});
})
.unwrap();
}

@ -9,16 +9,16 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
log = "0.4"
wgpu = "0.18"
wgpu = "0.19.3"
pollster = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -2,32 +2,36 @@ use std::iter;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
struct State {
struct State<'a> {
#[allow(dead_code)]
instance: wgpu::Instance,
#[allow(dead_code)]
adapter: wgpu::Adapter,
surface: wgpu::Surface,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
size: winit::dpi::PhysicalSize<u32>,
clear_color: wgpu::Color,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
@ -35,7 +39,7 @@ impl State {
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -50,10 +54,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -81,9 +85,9 @@ impl State {
height: size.height,
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
desired_maximum_frame_latency: 2,
view_formats: vec![],
};
surface.configure(&device, &config);
let clear_color = wgpu::Color::BLACK;
@ -172,59 +176,67 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
surface_configured = true;
}
WindowEvent::RedrawRequested => {
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -2,15 +2,16 @@ use std::iter;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -18,25 +19,24 @@ struct State {
// The window must be declared after the surface so
// it gets dropped after it as the surface contains
// unsafe references to the window's resources.
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -51,10 +51,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -83,9 +83,9 @@ impl State {
height: size.height,
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
desired_maximum_frame_latency: 2,
view_formats: vec![],
};
surface.configure(&device, &config);
Self {
surface,
@ -163,13 +163,13 @@ pub async fn run() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Couldn't initialize logger");
console_log::init_with_level(log::Level::Info).expect("Couldn't initialize logger");
} else {
env_logger::init();
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -177,73 +177,83 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
// UPDATED!
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &&mut so w have to dereference it twice
state.resize(**new_inner_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
// UPDATED!
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
log::info!("physical_size: {physical_size:?}");
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
_ => {}
}
}
_ => {}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
Event::RedrawEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -9,16 +9,16 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
log = "0.4"
wgpu = "0.18"
wgpu = "0.19"
pollster = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -2,12 +2,13 @@ use std::iter;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -15,25 +16,24 @@ struct State {
render_pipeline: wgpu::RenderPipeline,
challenge_render_pipeline: wgpu::RenderPipeline,
use_color: bool,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -48,10 +48,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -80,8 +80,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
@ -213,10 +213,10 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(VirtualKeyCode::Space),
physical_key: PhysicalKey::Code(KeyCode::Space),
..
},
..
@ -284,61 +284,69 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
_ => {}
}
}
_ => {}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -2,40 +2,40 @@ use std::iter;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
size: winit::dpi::PhysicalSize<u32>,
// NEW!
render_pipeline: wgpu::RenderPipeline,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -50,10 +50,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -82,8 +82,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
@ -227,7 +227,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -235,14 +235,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -250,57 +250,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
_ => {}
}
}
_ => {}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -10,8 +10,8 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
winit = "0.28"
wgpu = "0.18"
winit = { version = "0.29", features = ["rwh_05"] }
wgpu = "0.19"
env_logger = "0.10"
log = "0.4"
pollster = "0.3"
@ -21,7 +21,7 @@ bytemuck = { version = "1.12", features = [ "derive" ] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -3,7 +3,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -60,8 +61,8 @@ const VERTICES: &[Vertex] = &[
const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -78,17 +79,20 @@ struct State {
use_complex: bool,
size: winit::dpi::PhysicalSize<u32>,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
@ -96,7 +100,7 @@ impl State {
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -111,10 +115,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -143,8 +147,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
@ -281,10 +285,10 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(VirtualKeyCode::Space),
physical_key: PhysicalKey::Code(KeyCode::Space),
..
},
..
@ -361,61 +365,69 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -3,7 +3,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -63,8 +64,8 @@ const VERTICES: &[Vertex] = &[
const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4, /* padding */ 0];
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -74,17 +75,20 @@ struct State {
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
num_indices: u32,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
@ -92,7 +96,7 @@ impl State {
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -107,10 +111,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -139,8 +143,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
@ -301,7 +305,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -309,14 +313,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -324,57 +328,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -14,8 +14,8 @@ bytemuck = { version = "1.12", features = [ "derive" ] }
env_logger = "0.10"
log = "0.4"
pollster = "0.3"
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -25,7 +25,7 @@ features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -3,7 +3,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -63,8 +64,8 @@ const VERTICES: &[Vertex] = &[
const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -80,17 +81,20 @@ struct State {
cartoon_texture: texture::Texture,
cartoon_bind_group: wgpu::BindGroup,
is_space_pressed: bool,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
@ -98,7 +102,7 @@ impl State {
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -112,10 +116,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -144,8 +148,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -312,10 +316,10 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(VirtualKeyCode::Space),
physical_key: PhysicalKey::Code(KeyCode::Space),
..
},
..
@ -388,61 +392,68 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so we have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -3,7 +3,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -66,8 +67,8 @@ const VERTICES: &[Vertex] = &[
const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4, /* padding */ 0];
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -80,25 +81,24 @@ struct State {
#[allow(dead_code)]
diffuse_texture: texture::Texture,
diffuse_bind_group: wgpu::BindGroup,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -112,10 +112,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -144,8 +144,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -351,7 +351,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -359,14 +359,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -374,57 +374,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -15,8 +15,8 @@ cgmath = "0.18"
env_logger = "0.10"
log = "0.4"
pollster = "0.3"
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -26,7 +26,7 @@ features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -3,7 +3,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -146,29 +147,29 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -212,8 +213,8 @@ impl CameraController {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -230,25 +231,24 @@ struct State {
camera_buffer: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup,
size: winit::dpi::PhysicalSize<u32>,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -262,10 +262,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -294,8 +294,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -559,61 +559,69 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -3,7 +3,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -137,37 +138,37 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Space => {
KeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -211,8 +212,8 @@ impl CameraController {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -230,25 +231,24 @@ struct State {
camera_uniform: CameraUniform,
camera_buffer: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -262,10 +262,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -294,8 +294,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -560,7 +560,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -568,14 +568,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -583,57 +583,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -15,8 +15,8 @@ cgmath = "0.18"
env_logger = "0.10"
log = "0.4"
pollster = "0.3"
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -26,7 +26,7 @@ features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -4,7 +4,8 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -137,29 +138,29 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -264,8 +265,8 @@ impl InstanceRaw {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -284,25 +285,24 @@ struct State {
size: winit::dpi::PhysicalSize<u32>,
instances: Vec<Instance>,
instance_buffer: wgpu::Buffer,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -316,10 +316,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -348,8 +348,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -660,61 +660,69 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -4,7 +4,8 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -144,37 +145,37 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Space => {
KeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -269,8 +270,8 @@ impl InstanceRaw {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -291,25 +292,24 @@ struct State {
instances: Vec<Instance>,
#[allow(dead_code)]
instance_buffer: wgpu::Buffer,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -323,10 +323,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -355,8 +355,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -658,7 +658,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -666,14 +666,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -681,57 +681,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -15,8 +15,8 @@ cgmath = "0.18"
env_logger = "0.10"
pollster = "0.3"
log = "0.4"
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -26,7 +26,7 @@ features = ["png", "jpeg"]
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -1,11 +1,12 @@
use std::iter;
use cgmath::prelude::*;
use wgpu::include_spirv_raw;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -159,29 +160,29 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -461,8 +462,8 @@ impl DepthPass {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -483,25 +484,24 @@ struct State {
#[allow(dead_code)]
instance_buffer: wgpu::Buffer,
depth_pass: DepthPass,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -515,10 +515,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -547,8 +547,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -867,61 +867,69 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
_ => {}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -4,7 +4,8 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::{Window, WindowBuilder},
};
@ -144,37 +145,37 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Space => {
KeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -278,8 +279,8 @@ impl InstanceRaw {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -301,25 +302,24 @@ struct State {
instance_buffer: wgpu::Buffer,
// NEW!
depth_texture: texture::Texture,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -333,10 +333,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -365,8 +365,8 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture =
@ -685,7 +685,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
@ -693,14 +693,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -708,57 +708,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
// new_inner_size is &mut so w have to dereference it twice
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
Event::MainEventsCleared => {
// RedrawRequested will only trigger once, unless we manually
// request it.
state.window().request_redraw();
}
_ => {}
}
});
})
.unwrap();
}

@ -10,14 +10,15 @@ pub struct Texture {
impl Texture {
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
#[allow(unused)]
pub fn create_depth_texture(
device: &wgpu::Device,
config: &wgpu::SurfaceConfiguration,
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {
@ -59,8 +60,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -28,7 +28,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -4,7 +4,8 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -27,6 +28,7 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
const NUM_INSTANCES_PER_ROW: u32 = 10;
#[derive(Debug)]
struct Camera {
eye: cgmath::Point3<f32>,
target: cgmath::Point3<f32>,
@ -46,7 +48,7 @@ impl Camera {
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct CameraUniform {
view_proj: [[f32; 4]; 4],
}
@ -89,37 +91,37 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Space => {
KeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -223,8 +225,8 @@ impl InstanceRaw {
}
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -240,26 +242,25 @@ struct State {
#[allow(dead_code)]
instance_buffer: wgpu::Buffer,
depth_texture: texture::Texture,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
log::warn!("WGPU setup");
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -274,10 +275,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -308,10 +309,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -506,10 +506,10 @@ impl State {
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.camera.aspect = self.config.width as f32 / self.config.height as f32;
self.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
self.size = new_size;
self.camera.aspect = self.config.width as f32 / self.config.height as f32;
self.surface.configure(&self.device, &self.config);
self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture");
@ -521,7 +521,9 @@ impl State {
fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera);
log::info!("{:?}", self.camera);
self.camera_uniform.update_view_proj(&self.camera);
log::info!("{:?}", self.camera_uniform);
self.queue.write_buffer(
&self.camera_buffer,
0,
@ -590,13 +592,13 @@ pub async fn run() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Warn).expect("Could't initialize logger");
console_log::init_with_level(log::Level::Info).expect("Could't initialize logger");
} else {
env_logger::init();
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -608,14 +610,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -623,53 +625,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::MainEventsCleared => state.window().request_redraw(),
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
_ => {}
}
}
_ => {}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
});
})
.unwrap();
}

@ -122,7 +122,6 @@ where
camera_bind_group: &'b wgpu::BindGroup,
) {
for mesh in &model.meshes {
log::warn!("materials: {}", model.materials.len());
let material = &model.materials[mesh.material];
self.draw_mesh_instanced(mesh, material, instances.clone(), camera_bind_group);
}

@ -144,6 +144,7 @@ pub async fn load_model(
usage: wgpu::BufferUsages::INDEX,
});
log::info!("Mesh: {}", m.name);
model::Mesh {
name: file_name.to_string(),
vertex_buffer,

@ -16,8 +16,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -28,7 +28,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -4,7 +4,8 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -93,37 +94,37 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Space => {
KeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -255,9 +256,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -339,22 +340,21 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -368,10 +368,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -400,10 +400,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -623,10 +622,10 @@ impl State {
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.camera.aspect = self.config.width as f32 / self.config.height as f32;
self.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
self.size = new_size;
self.camera.aspect = self.config.width as f32 / self.config.height as f32;
self.surface.configure(&self.device, &self.config);
self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture");
@ -733,7 +732,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -745,13 +744,13 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
if let Some(dst) = doc.get_element_by_id("wasm-example") {
dst.append_child(&canvas).ok()?;
} else {
@ -763,53 +762,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::MainEventsCleared => state.window().request_redraw(),
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
_ => {}
}
});
})
.unwrap();
}

@ -1,6 +1,5 @@
use anyhow::*;
use image::GenericImageView;
use std::num::NonZeroU32;
pub struct Texture {
pub texture: wgpu::Texture,
@ -17,8 +16,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
[dependencies.image]
version = "0.24"
@ -28,7 +28,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -4,7 +4,8 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -92,37 +93,37 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state,
virtual_keycode: Some(keycode),
physical_key: PhysicalKey::Code(keycode),
..
},
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::Space => {
KeyCode::Space => {
self.is_up_pressed = is_pressed;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.is_down_pressed = is_pressed;
true
}
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -253,9 +254,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -339,22 +340,21 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -368,10 +368,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -400,10 +400,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -674,10 +673,10 @@ impl State {
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.camera.aspect = self.config.width as f32 / self.config.height as f32;
self.size = new_size;
self.config.width = new_size.width;
self.config.height = new_size.height;
self.size = new_size;
self.camera.aspect = self.config.width as f32 / self.config.height as f32;
self.surface.configure(&self.device, &self.config);
self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture");
@ -784,7 +783,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -796,14 +795,14 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
@ -811,53 +810,65 @@ pub async fn run() {
}
// State::new uses async code, so we're going to wait for it to finish
let mut state = State::new(window).await;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::MainEventsCleared => state.window().request_redraw(),
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
let mut state = State::new(&window).await;
let mut surface_configured = false;
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
window_id,
} if window_id == state.window().id() => {
if !state.input(event) {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
surface_configured = true;
state.resize(*physical_size);
}
WindowEvent::RedrawRequested => {
// This tells winit that we want another frame after this one
state.window().request_redraw();
if !surface_configured {
return;
}
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => {
log::error!("OutOfMemory");
control_flow.exit();
}
// This happens when the a frame takes too long to present
Err(wgpu::SurfaceError::Timeout) => {
log::warn!("Surface timeout")
}
}
}
_ => {}
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
state.update();
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
state.resize(state.size)
}
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
_ => {}
}
_ => {}
}
});
})
.unwrap();
}

@ -1,6 +1,5 @@
use anyhow::*;
use image::GenericImageView;
use std::num::NonZeroU32;
pub struct Texture {
pub texture: wgpu::Texture,
@ -17,8 +16,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
instant = "0.1"
[dependencies.image]
@ -29,7 +29,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [
@ -38,6 +38,7 @@ web-sys = { version = "0.3", features = [
"Element",
"Location",
]}
instant = { version = "0.1", features = [ "wasm-bindgen" ] }
[build-dependencies]
anyhow = "1.0"

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -104,34 +105,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -1,10 +1,11 @@
use std::iter;
use std::{f32::consts::PI, iter};
use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -130,9 +131,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -219,22 +220,21 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -248,10 +248,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -280,10 +280,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -564,9 +563,9 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
@ -601,10 +600,11 @@ impl State {
// Update the light
let old_position: cgmath::Vector3<_> = self.light_uniform.position.into();
self.light_uniform.position =
(cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position)
.into();
self.light_uniform.position = (cgmath::Quaternion::from_axis_angle(
(0.0, 1.0, 0.0).into(),
cgmath::Deg(PI * dt.as_secs_f32()),
) * old_position)
.into();
self.queue.write_buffer(
&self.light_buffer,
0,
@ -686,7 +686,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -698,26 +698,24 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
let mut state = State::new(window).await; // NEW!
let mut state = State::new(&window).await; // NEW!
let mut last_render_time = instant::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
event_loop.run(move |event, control_flow| {
match event {
Event::MainEventsCleared => state.window().request_redraw(),
// NEW!
Event::DeviceEvent {
event: DeviceEvent::MouseMotion{ delta, },
@ -734,40 +732,38 @@ pub async fn run() {
#[cfg(not(target_arch="wasm32"))]
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
// UPDATED!
WindowEvent::RedrawRequested => {
state.window().request_redraw();
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
}
// UPDATED!
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
});
}).unwrap();
}

@ -16,8 +16,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
instant = "0.1"
[dependencies.image]
@ -37,6 +37,7 @@ web-sys = { version = "0.3", features = [
"Element",
"Location",
]}
instant = { version = "0.1", features = [ "wasm-bindgen" ] }
[build-dependencies]
anyhow = "1.0"

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001;
@ -97,34 +98,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -79,7 +79,7 @@ impl HdrPipeline {
let pipeline = create_render_pipeline(
device,
&pipeline_layout,
config.format,
config.format.add_srgb_suffix(),
None,
&[],
wgpu::PrimitiveTopology::TriangleList,

@ -1,10 +1,11 @@
use std::iter;
use std::{f32::consts::PI, iter};
use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -146,9 +147,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -238,26 +239,22 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> anyhow::Result<Self> {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> anyhow::Result<State<'a>> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
// UPDATED
#[cfg(target_arch="wasm32")]
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::BROWSER_WEBGPU,
#[cfg(not(target_arch="wasm32"))]
backends: wgpu::Backends::all(),
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -272,9 +269,9 @@ impl State {
&wgpu::DeviceDescriptor {
label: None,
// UPDATED!
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// UPDATED!
limits: wgpu::Limits::downlevel_defaults(),
required_limits: wgpu::Limits::downlevel_defaults(),
},
None, // Trace path
)
@ -298,11 +295,11 @@ impl State {
height: size.height,
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
// NEW!
view_formats: vec![surface_format.add_srgb_suffix()],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -665,9 +662,9 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
@ -701,10 +698,11 @@ impl State {
// Update the light
let old_position: cgmath::Vector3<_> = self.light_uniform.position.into();
self.light_uniform.position =
(cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position)
.into();
self.light_uniform.position = (cgmath::Quaternion::from_axis_angle(
(0.0, 1.0, 0.0).into(),
cgmath::Deg(PI * dt.as_secs_f32()),
) * old_position)
.into();
self.queue.write_buffer(
&self.light_buffer,
0,
@ -714,9 +712,10 @@ impl State {
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let output = self.surface.get_current_texture()?;
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let view = output.texture.create_view(&wgpu::TextureViewDescriptor {
format: Some(self.config.format.add_srgb_suffix()),
..Default::default()
});
let mut encoder = self
.device
@ -816,7 +815,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -828,26 +827,24 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
let mut state = State::new(window).await.unwrap(); // NEW!
let mut state = State::new(&window).await.unwrap();
let mut last_render_time = instant::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
event_loop.run(move |event, control_flow| {
match event {
Event::MainEventsCleared => state.window().request_redraw(),
Event::DeviceEvent {
event: DeviceEvent::MouseMotion{ delta, },
.. // We're not using device_id currently
@ -863,40 +860,38 @@ pub async fn run() {
#[cfg(not(target_arch="wasm32"))]
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
// UPDATED!
WindowEvent::RedrawRequested => {
state.window().request_redraw();
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
}
// UPDATED!
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
});
}).unwrap();
}

@ -190,7 +190,7 @@ pub async fn load_model(
// Average the tangents/bitangents
for (i, n) in triangles_included.into_iter().enumerate() {
let denom = 1.0 / n as f32;
let mut v = &mut vertices[i];
let v = &mut vertices[i];
v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into();
v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into();
}
@ -286,14 +286,28 @@ impl HdrLoader {
) -> anyhow::Result<texture::CubeTexture> {
let hdr_decoder = HdrDecoder::new(Cursor::new(data))?;
let meta = hdr_decoder.metadata();
let mut pixels = vec![[0.0, 0.0, 0.0, 0.0]; meta.width as usize * meta.height as usize];
hdr_decoder.read_image_transform(
|pix| {
#[cfg(not(target_arch = "wasm32"))]
let pixels = {
let mut pixels = vec![[0.0, 0.0, 0.0, 0.0]; meta.width as usize * meta.height as usize];
hdr_decoder.read_image_transform(
|pix| {
let rgb = pix.to_hdr();
[rgb.0[0], rgb.0[1], rgb.0[2], 1.0f32]
},
&mut pixels[..],
)?;
pixels
};
#[cfg(target_arch = "wasm32")]
let pixels = hdr_decoder
.read_image_native()?
.into_iter()
.map(|pix| {
let rgb = pix.to_hdr();
[rgb.0[0], rgb.0[1], rgb.0[2], 1.0f32]
},
&mut pixels[..],
)?;
})
.collect::<Vec<_>>();
let src = texture::Texture::create_2d_texture(
device,
@ -327,8 +341,7 @@ impl HdrLoader {
dst_size,
self.texture_format,
1,
wgpu::TextureUsages::STORAGE_BINDING
| wgpu::TextureUsages::TEXTURE_BINDING,
wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
wgpu::FilterMode::Nearest,
label,
);
@ -356,7 +369,10 @@ impl HdrLoader {
});
let mut encoder = device.create_command_encoder(&Default::default());
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label, timestamp_writes: None });
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label,
timestamp_writes: None,
});
let num_workgroups = (dst_size + 15) / 16;
pass.set_pipeline(&self.equirect_to_cubemap);

@ -17,8 +17,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {
@ -239,10 +239,15 @@ impl CubeTexture {
}
}
pub fn texture(&self) -> &wgpu::Texture { &self.texture }
pub fn view(&self) -> &wgpu::TextureView { &self.view }
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
pub fn sampler(&self) -> &wgpu::Sampler { &self.sampler }
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
pub fn sampler(&self) -> &wgpu::Sampler {
&self.sampler
}
}

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
instant = "0.1"
[dependencies.image]
@ -29,7 +29,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -2,7 +2,7 @@ use std::marker::PhantomData;
pub struct Binder<B> {
layout: wgpu::BindGroupLayout,
_marker: PhantomData<B>
_marker: PhantomData<B>,
}
// pub trait Uniform
// pub trait Uniform

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -104,34 +105,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -4,19 +4,20 @@ use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
mod bindgroups;
mod camera;
mod model;
mod resources;
mod terrain;
mod texture;
mod bindgroups; // NEW!
mod texture; // NEW!
use model::{DrawLight, DrawModel, Vertex};
@ -132,9 +133,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -220,22 +221,21 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -249,10 +249,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -281,10 +281,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -606,9 +605,9 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
@ -736,7 +735,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -749,27 +748,25 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
let mut state = State::new(window).await; // NEW!
let mut state = State::new(&window).await; // NEW!
state.window().set_visible(true);
let mut last_render_time = instant::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
event_loop.run(move |event, control_flow| {
match event {
Event::MainEventsCleared => state.window().request_redraw(),
// NEW!
Event::DeviceEvent {
event: DeviceEvent::MouseMotion{ delta, },
@ -786,40 +783,37 @@ pub async fn run() {
#[cfg(not(target_arch="wasm32"))]
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
// UPDATED!
WindowEvent::RedrawRequested => {
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
}
// UPDATED!
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
});
}).unwrap();
}

@ -241,8 +241,7 @@ impl GenerateChunk for TerrainPipeline {
let gen_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("TerrainPipeline: ChunkData"),
size: size_of_val(&data) as _,
usage: wgpu::BufferUsages::UNIFORM
| wgpu::BufferUsages::COPY_DST,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
queue.write_buffer(&gen_buffer, 0, bytemuck::bytes_of(&data));

@ -17,8 +17,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {
@ -63,7 +63,14 @@ impl Texture {
address_mode: AddressMode, // NEW!
) -> Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label), is_normal_map, address_mode) // UPDATED!
Self::from_image(
device,
queue,
&img,
Some(label),
is_normal_map,
address_mode,
) // UPDATED!
}
pub fn from_image(
@ -117,7 +124,7 @@ impl Texture {
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
// UPDATED!
address_mode_u: address_mode,
address_mode_u: address_mode,
address_mode_v: address_mode,
address_mode_w: address_mode,
mag_filter: wgpu::FilterMode::Linear,

@ -14,8 +14,8 @@ image = "0.24"
log = "0.4"
rayon = "1.4"
tobj = "2.0"
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
[build-dependencies]
anyhow = "1.0"

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -104,34 +105,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -5,7 +5,8 @@ use wgpu::util::DeviceExt;
use winit::{
dpi::PhysicalPosition,
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -129,8 +130,8 @@ struct LightUniform {
_padding2: u32,
}
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -155,25 +156,24 @@ struct State {
debug_material: model::Material,
last_mouse_pos: PhysicalPosition<f64>,
mouse_pressed: bool,
window: Window,
window: &'a Window,
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -187,10 +187,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -219,10 +219,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -506,9 +505,9 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
@ -636,18 +635,16 @@ fn main() {
async fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
.build(&event_loop)
.unwrap();
let mut state = State::new(window).await; // NEW!
let mut state = State::new(&window).await; // NEW!
let mut last_render_time = std::time::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::MainEventsCleared => state.window().request_redraw(),
event_loop
.run(move |event, control_flow| match event {
Event::WindowEvent {
ref event,
window_id,
@ -656,32 +653,30 @@ async fn run() {
match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
WindowEvent::RedrawRequested => {
state.window.request_redraw();
let now = std::time::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
state.render();
}
_ => {}
}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
let now = std::time::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
state.render();
}
_ => {}
}
});
})
.unwrap();
}

@ -298,7 +298,10 @@ impl ModelLoader {
m.mesh.positions[i * 3 + 2],
],
// tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1], 0.0]
tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]],
tex_coords: [
m.mesh.texcoords[i * 2],
1.0 - m.mesh.texcoords[i * 2 + 1],
],
normal: [
m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1],

@ -31,8 +31,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -14,9 +14,9 @@ pollster = "0.3"
image = "0.24.2"
log = "0.4"
tobj = "2.0"
wgpu = "0.18"
wgpu = "0.19"
wgpu-subscriber = "0.1"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
[build-dependencies]
anyhow = "1.0"

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -104,34 +105,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -21,24 +21,28 @@ use std::time::{Duration, Instant};
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use winit::event::*;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::window::{Window, WindowBuilder};
pub struct Display {
surface: wgpu::Surface,
pub window: Window,
pub struct Display<'a> {
surface: wgpu::Surface<'a>,
pub window: &'a Window,
pub config: wgpu::SurfaceConfiguration,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
}
impl Display {
pub async fn new(window: Window) -> Result<Self, Error> {
impl<'a> Display<'a> {
pub async fn new(window: &'a Window) -> Result<Display<'a>, Error> {
let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
@ -51,10 +55,10 @@ impl Display {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -82,8 +86,8 @@ impl Display {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
Ok(Self {
surface,
@ -214,7 +218,7 @@ impl UniformBinding {
pub trait Demo: 'static + Sized {
fn init(display: &Display) -> Result<Self, Error>;
fn process_mouse(&mut self, dx: f64, dy: f64);
fn process_keyboard(&mut self, key: VirtualKeyCode, pressed: bool);
fn process_keyboard(&mut self, key: KeyCode, pressed: bool);
fn resize(&mut self, display: &Display);
fn update(&mut self, display: &Display, dt: Duration);
fn render(&mut self, display: &mut Display);
@ -223,74 +227,71 @@ pub trait Demo: 'static + Sized {
pub async fn run<D: Demo>() -> Result<(), Error> {
wgpu_subscriber::initialize_default_subscriber(None);
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new()
.with_title(env!("CARGO_PKG_NAME"))
.build(&event_loop)?;
let mut display = Display::new(window).await?;
let mut display = Display::new(&window).await?;
let mut demo = D::init(&display)?;
let mut last_update = Instant::now();
let mut is_resumed = true;
let mut is_focused = true;
let mut is_redraw_requested = true;
let _is_redraw_requested = true;
event_loop.run(move |event, _, control_flow| {
*control_flow = if is_resumed && is_focused {
ControlFlow::Poll
event_loop.run(move |event, control_flow| {
if is_resumed && is_focused {
control_flow.set_control_flow(ControlFlow::Poll)
} else {
ControlFlow::Wait
control_flow.set_control_flow(ControlFlow::Wait)
};
match event {
Event::Resumed => is_resumed = true,
Event::Suspended => is_resumed = false,
Event::RedrawRequested(wid) => {
if wid == display.window().id() {
let now = Instant::now();
let dt = now - last_update;
last_update = now;
demo.update(&display, dt);
demo.render(&mut display);
is_redraw_requested = false;
}
}
Event::MainEventsCleared => {
if is_focused && is_resumed && !is_redraw_requested {
display.window().request_redraw();
is_redraw_requested = true;
} else {
// Freeze time while the demo is not in the foreground
last_update = Instant::now();
}
}
Event::WindowEvent {
event, window_id, ..
} => {
if window_id == display.window().id() {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::CloseRequested => control_flow.exit(),
WindowEvent::Focused(f) => is_focused = f,
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
display.resize(new_inner_size.width, new_inner_size.height);
demo.resize(&display);
}
WindowEvent::Resized(new_inner_size) => {
display.resize(new_inner_size.width, new_inner_size.height);
demo.resize(&display);
}
WindowEvent::KeyboardInput { input: KeyboardInput {
virtual_keycode: Some(key),
state,
WindowEvent::KeyboardInput {
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
..
}, .. } => {
} => {
demo.process_keyboard(key, state == ElementState::Pressed);
}
WindowEvent::RedrawRequested => {
let now = Instant::now();
let dt = now - last_update;
last_update = now;
demo.update(&display, dt);
demo.render(&mut display);
if is_focused && is_resumed {
display.window().request_redraw();
} else {
// Freeze time while the demo is not in the foreground
last_update = Instant::now();
}
}
_ => {}
}
}
}
_ => {}
}
});
})?;
Ok(())
}

@ -1,7 +1,7 @@
use anyhow::*;
use image::GenericImageView;
use std::path::Path;
use std::mem;
use std::path::Path;
use crate::buffer;

@ -14,8 +14,8 @@ pollster = "0.3"
image = "0.24.2"
log = "0.4"
tobj = "3.1"
wgpu = "0.18"
winit = "0.28"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }
gif = "0.11.4"
futures-intrusive = "0.4"

@ -16,10 +16,10 @@ async fn run() {
.request_device(
&wgpu::DeviceDescriptor {
label: Some("Device"),
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()

@ -8,9 +8,9 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
wgpu = "0.18"
wgpu = "0.19"
pollster = "0.3"
imgui = "0.7"
imgui-wgpu = "0.18"
imgui-wgpu = "0.19"
imgui-winit-support = "0.7"
framework = { path = "../framework" }

@ -1,12 +0,0 @@
[package]
name = "lost-window"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1"
wgpu = "0.18"
winit = "0.28"
pollster = "0.3"

@ -1,127 +0,0 @@
use std::time::{Duration, Instant};
use winit::{
event::{Event, WindowEvent},
event_loop::EventLoop,
window::WindowBuilder,
};
async fn run() -> anyhow::Result<()> {
let event_loop = EventLoop::new();
let mut window = Some(
WindowBuilder::new()
.with_visible(false)
.build(&event_loop)?,
);
let window2 = WindowBuilder::new()
.with_visible(false)
.build(&event_loop)?;
let backends = wgpu::Backends::all();
let instance = wgpu::Instance::default();
let surface = unsafe { instance.create_surface(window.as_ref().unwrap()) }.unwrap();
let adapter = instance
.enumerate_adapters(backends)
.filter(|a| a.is_surface_supported(&surface))
.next()
.unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: Default::default(),
limits: Default::default(),
},
None,
)
.await?;
let inner_size = window.as_ref().unwrap().inner_size();
let mut config = wgpu::SurfaceConfiguration {
width: inner_size.width,
height: inner_size.height,
format: surface.get_capabilities(&adapter).formats[0],
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
present_mode: Default::default(),
alpha_mode: wgpu::CompositeAlphaMode::Auto,
view_formats: vec![],
};
surface.configure(&device, &config);
let start_time = Instant::now();
window.as_ref().unwrap().set_visible(true);
window2.set_visible(true);
event_loop.run(move |ev, _, cf| {
match ev {
Event::WindowEvent { event, window_id } => {
match event {
WindowEvent::Resized(size) => {
if window.is_some() && window_id == window.as_ref().unwrap().id() {
config.width = size.width;
config.height = size.height;
surface.configure(&device, &config);
}
}
WindowEvent::CloseRequested => {
// You'll only really want to close the window while testing
cf.set_exit()
}
_ => (),
}
}
Event::RedrawRequested(_) => {
let frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(e) => {
println!("An error occurred: {:?}", e);
return;
}
};
let view = frame.texture.create_view(&Default::default());
let mut encoder = device.create_command_encoder(&Default::default());
drop(encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: Default::default(),
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
}));
queue.submit([encoder.finish()]);
frame.present();
}
Event::RedrawEventsCleared => {
let current_time = Instant::now();
let dt = current_time - start_time;
// last_time = current_time;
if let Some(w) = window.as_ref() {
w.request_redraw();
}
window2.request_redraw();
println!("dt: {:?}", dt);
if dt > Duration::from_secs(5) {
// Exit the loop
cf.set_exit();
} else if dt > Duration::from_secs(2) {
// Dispose of the first window
if let Some(window) = window.take() {
drop(window);
}
}
}
_ => (),
}
});
}
fn main() -> anyhow::Result<()> {
pollster::block_on(run())
}

@ -16,8 +16,8 @@ env_logger = "0.10"
pollster = "0.3"
log = "0.4"
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
instant = "0.1"
[dependencies.image]
@ -29,7 +29,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -104,34 +105,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -5,7 +5,8 @@ use math::Ray;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -131,9 +132,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -224,22 +225,21 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -253,10 +253,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -285,10 +285,9 @@ impl State {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -577,9 +576,9 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
@ -733,7 +732,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -745,26 +744,24 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
let mut state = State::new(window).await; // NEW!
let mut state = State::new(&window).await; // NEW!
let mut last_render_time = instant::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
event_loop.run(move |event, control_flow| {
match event {
Event::MainEventsCleared => state.window().request_redraw(),
// NEW!
Event::DeviceEvent {
event: DeviceEvent::MouseMotion{ delta, },
@ -781,40 +778,38 @@ pub async fn run() {
#[cfg(not(target_arch="wasm32"))]
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
// UPDATED!
WindowEvent::RedrawRequested => {
state.window.request_redraw();
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
}
// UPDATED!
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
});
}).unwrap();
}

@ -118,7 +118,7 @@ pub async fn load_model(
.into_iter()
.map(|m| {
// NEW!
let mut bounding_box = BoundingBox {
let bounding_box = BoundingBox {
min: cgmath::Vector3::new(
std::f32::INFINITY,
std::f32::INFINITY,
@ -218,7 +218,7 @@ pub async fn load_model(
// Average the tangents/bitangents
for (i, n) in triangles_included.into_iter().enumerate() {
let denom = 1.0 / n as f32;
let mut v = &mut vertices[i];
let v = &mut vertices[i];
v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into();
v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into();
}

@ -1,6 +1,5 @@
use anyhow::*;
use image::GenericImageView;
use std::num::NonZeroU32;
pub struct Texture {
pub texture: wgpu::Texture,
@ -17,8 +16,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -10,13 +10,13 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
cfg-if = "1"
env_logger = "0.10"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
anyhow = "1.0"
bytemuck = { version = "1.12", features = [ "derive" ] }
cgmath = "0.18"
pollster = "0.3"
wgpu = { version = "0.18", features = ["spirv"]}
wgpu_glyph = { version = "0.21", git = "https://github.com/hecrj/wgpu_glyph.git" }
wgpu = { version = "0.19", features = ["spirv"]}
wgpu_glyph = { version = "0.22", git = "https://github.com/hecrj/wgpu_glyph.git" }
rand = "0.8"
rodio = { version = "0.16", default-features = false, features = ["wav"] }
log = "0.4"
@ -28,13 +28,13 @@ console_log = "1.0"
getrandom = { version = "0.2", features = ["js"] }
rodio = { version = "0.16", default-features = false, features = ["wasm-bindgen", "wav"] }
wasm-bindgen-futures = "0.4.20"
wasm-bindgen = "=0.2.87"
web-sys = { version = "0.3.53", features = [
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = [
"Document",
"Window",
"Element",
]}
wgpu = { version = "0.18", features = ["spirv", "webgl"]}
wgpu = { version = "0.19", features = ["spirv", "webgl"]}
[build-dependencies]
anyhow = "1.0"

@ -1,4 +1,4 @@
use winit::event::{ElementState, VirtualKeyCode};
use winit::{event::ElementState, keyboard::KeyCode};
#[derive(Debug, Default)]
pub struct Input {
@ -14,26 +14,26 @@ impl Input {
Default::default()
}
pub fn update(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn update(&mut self, key: KeyCode, state: ElementState) -> bool {
let pressed = state == ElementState::Pressed;
match key {
VirtualKeyCode::Up => {
KeyCode::ArrowUp => {
self.p2_up_pressed = pressed;
true
}
VirtualKeyCode::Down => {
KeyCode::ArrowDown => {
self.p2_down_pressed = pressed;
true
}
VirtualKeyCode::W => {
KeyCode::KeyW => {
self.p1_up_pressed = pressed;
true
}
VirtualKeyCode::S => {
KeyCode::KeyS => {
self.p1_down_pressed = pressed;
true
}
VirtualKeyCode::Return => {
KeyCode::Enter => {
self.enter_pressed = pressed;
true
}

@ -13,7 +13,8 @@ use wasm_bindgen::prelude::*;
use winit::dpi::PhysicalSize;
use winit::event::*;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::event_loop::{EventLoop, EventLoopWindowTarget};
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::window::{Fullscreen, WindowBuilder};
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
@ -27,7 +28,7 @@ pub fn start() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let monitor = event_loop.primary_monitor().unwrap();
let video_mode = monitor.video_modes().next();
let size = video_mode
@ -40,10 +41,6 @@ pub fn start() {
.build(&event_loop)
.unwrap();
if window.fullscreen().is_none() {
window.set_inner_size(PhysicalSize::new(512, 512));
}
window.set_cursor_visible(false);
#[cfg(target_arch = "wasm32")]
@ -53,7 +50,7 @@ pub fn start() {
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
// Request fullscreen, if denied, continue as normal
@ -159,125 +156,126 @@ pub fn start() {
log::info!("Event Loop...");
event_loop.run(move |event, _, control_flow| {
*control_flow = if state.game_state == state::GameState::Quiting {
ControlFlow::Exit
} else {
ControlFlow::Poll
};
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
state.game_state = state::GameState::Quiting;
}
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: element_state,
virtual_keycode: Some(key),
..
},
..
},
..
} => {
let input_handled = match state.game_state {
state::GameState::Quiting => true,
_ => input.update(key, element_state),
};
if !input_handled {
process_input(element_state, key, control_flow);
let window = &window;
let mut last_time = instant::Instant::now();
event_loop
.run(move |event, control_flow| {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
state.game_state = state::GameState::Quiting;
}
}
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
render.resize(size);
events.push(state::Event::Resize(size.width as f32, size.height as f32));
}
Event::RedrawEventsCleared => {
window.request_redraw();
}
Event::RedrawRequested(window_id) if window_id == window.id() => {
for event in &events {
match event {
state::Event::FocusChanged | state::Event::ButtonPressed => {
sound_system.queue(sound_pack.bounce());
}
state::Event::BallBounce(_pos) => {
sound_system.queue(sound_pack.bounce());
}
state::Event::Score(_) => {
sound_system.queue(sound_pack.bounce());
}
state::Event::Resize(width, height) => {
// TODO: there should be a system that handles this
state.player1_score.position = (width * 0.25, 20.0).into();
state.player2_score.position = (width * 0.75, 20.0).into();
state.win_text.position = (width * 0.5, height * 0.5).into();
}
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
event:
KeyEvent {
state: element_state,
physical_key: PhysicalKey::Code(key),
..
},
..
},
..
} => {
let input_handled = match state.game_state {
state::GameState::Quiting => true,
_ => input.update(key, element_state),
};
if !input_handled {
process_input(element_state, key, control_flow);
}
}
events.clear();
visiblity_system.update_state(&input, &mut state, &mut events);
match state.game_state {
state::GameState::MainMenu => {
menu_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
render.resize(size);
events.push(state::Event::Resize(size.width as f32, size.height as f32));
}
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
let dt = last_time.elapsed();
last_time = instant::Instant::now();
window.request_redraw();
for event in &events {
match event {
state::Event::FocusChanged | state::Event::ButtonPressed => {
sound_system.queue(sound_pack.bounce());
}
state::Event::BallBounce(_pos) => {
sound_system.queue(sound_pack.bounce());
}
state::Event::Score(_) => {
sound_system.queue(sound_pack.bounce());
}
state::Event::Resize(width, height) => {
// TODO: there should be a system that handles this
state.player1_score.position = (width * 0.25, 20.0).into();
state.player2_score.position = (width * 0.75, 20.0).into();
state.win_text.position = (width * 0.5, height * 0.5).into();
}
}
}
state::GameState::Serving => {
serving_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Playing {
play_system.start(&mut state);
events.clear();
visiblity_system.update_state(&input, dt, &mut state, &mut events);
match state.game_state {
state::GameState::MainMenu => {
menu_system.update_state(&input, dt, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
}
}
}
state::GameState::Playing => {
ball_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
} else if state.game_state == state::GameState::GameOver {
game_over_system.start(&mut state);
state::GameState::Serving => {
serving_system.update_state(&input, dt, &mut state, &mut events);
play_system.update_state(&input, dt, &mut state, &mut events);
if state.game_state == state::GameState::Playing {
play_system.start(&mut state);
}
}
}
state::GameState::GameOver => {
game_over_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state);
state::GameState::Playing => {
ball_system.update_state(&input, dt, &mut state, &mut events);
play_system.update_state(&input, dt, &mut state, &mut events);
if state.game_state == state::GameState::Serving {
serving_system.start(&mut state);
} else if state.game_state == state::GameState::GameOver {
game_over_system.start(&mut state);
}
}
state::GameState::GameOver => {
game_over_system.update_state(&input, dt, &mut state, &mut events);
if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state);
}
}
state::GameState::Quiting => {
control_flow.exit();
}
}
state::GameState::Quiting => {}
}
render.render_state(&state);
if state.game_state != state::GameState::Quiting {
window.request_redraw();
render.render_state(&state);
if state.game_state != state::GameState::Quiting {
window.request_redraw();
}
}
_ => {}
}
_ => {}
}
});
})
.unwrap();
}
fn process_input(
element_state: ElementState,
keycode: VirtualKeyCode,
control_flow: &mut ControlFlow,
keycode: KeyCode,
control_flow: &EventLoopWindowTarget<()>,
) {
match (keycode, element_state) {
(VirtualKeyCode::Escape, ElementState::Pressed) => {
*control_flow = ControlFlow::Exit;
}
(KeyCode::Escape, ElementState::Pressed) => control_flow.exit(),
_ => {}
}
}

@ -12,8 +12,8 @@ use crate::state;
const FONT_BYTES: &[u8] = include_bytes!("../../res/fonts/PressStart2P-Regular.ttf");
pub struct Render {
surface: wgpu::Surface,
pub struct Render<'a> {
surface: wgpu::Surface<'a>,
config: wgpu::SurfaceConfiguration,
#[allow(dead_code)]
adapter: wgpu::Adapter,
@ -26,7 +26,7 @@ pub struct Render {
staging_belt: wgpu::util::StagingBelt,
}
impl Render {
impl<'a> Render<'a> {
pub fn width(&self) -> f32 {
self.config.width as f32
}
@ -36,20 +36,19 @@ impl Render {
self.config.height as f32
}
pub async fn new(window: &Window, size: PhysicalSize<u32>) -> Self {
pub async fn new(window: &'a Window, size: PhysicalSize<u32>) -> Render<'a> {
log::warn!("size: {:?}", size);
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -63,8 +62,8 @@ impl Render {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::downlevel_webgl2_defaults(),
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::downlevel_webgl2_defaults(),
},
None, // Trace path
)
@ -89,8 +88,8 @@ impl Render {
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[],

@ -9,6 +9,7 @@ pub trait System {
fn update_state(
&self,
input: &input::Input,
dt: instant::Duration,
state: &mut state::State,
events: &mut Vec<state::Event>,
);
@ -19,6 +20,7 @@ impl System for VisibilitySystem {
fn update_state(
&self,
_input: &input::Input,
_dt: instant::Duration,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
@ -60,6 +62,7 @@ impl System for MenuSystem {
fn update_state(
&self,
input: &input::Input,
_dt: instant::Duration,
state: &mut state::State,
events: &mut Vec<state::Event>,
) {
@ -92,21 +95,23 @@ impl System for PlaySystem {
fn update_state(
&self,
input: &input::Input,
dt: instant::Duration,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let dt = dt.as_secs_f32();
// move the players
if input.p1_up_pressed {
state.player1.position.y += util::PLAYER_SPEED;
state.player1.position.y += util::PLAYER_SPEED * dt;
}
if input.p1_down_pressed {
state.player1.position.y -= util::PLAYER_SPEED;
state.player1.position.y -= util::PLAYER_SPEED * dt;
}
if input.p2_up_pressed {
state.player2.position.y += util::PLAYER_SPEED;
state.player2.position.y += util::PLAYER_SPEED * dt;
}
if input.p2_down_pressed {
state.player2.position.y -= util::PLAYER_SPEED;
state.player2.position.y -= util::PLAYER_SPEED * dt;
}
// normalize players
@ -134,22 +139,25 @@ impl System for BallSystem {
fn update_state(
&self,
_input: &input::Input,
dt: instant::Duration,
state: &mut state::State,
events: &mut Vec<state::Event>,
) {
let dt = dt.as_secs_f32();
// bounce the ball off the players
if state.player1.contains(&state.ball) {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.x -= state.ball.velocity.x - state.player1.size.x;
state.ball.position.x -= state.ball.velocity.x * dt - state.player1.size.x;
state.ball.velocity = util::calc_ball_velocity(&state.ball, &state.player1);
} else if state.player2.contains(&state.ball) {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.x -= state.ball.velocity.x + state.player2.size.x;
state.ball.position.x -= state.ball.velocity.x * dt + state.player2.size.x;
state.ball.velocity.x *= -state.player2.size.y;
state.ball.velocity = util::calc_ball_velocity(&state.ball, &state.player2);
}
state.ball.position += state.ball.velocity;
state.ball.position += state.ball.velocity * dt;
if state.ball.position.y > 1.0 {
events.push(state::Event::BallBounce(state.ball.position));
state.ball.position.y = 1.0;
@ -199,12 +207,11 @@ impl System for ServingSystem {
fn update_state(
&self,
_input: &input::Input,
_dt: instant::Duration,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let current_time = instant::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 2.0 {
if self.last_time.elapsed().as_secs_f32() > 2.0 {
log::info!("Serving...");
state.game_state = state::GameState::Playing;
}
@ -242,12 +249,11 @@ impl System for GameOverSystem {
fn update_state(
&self,
_input: &input::Input,
_dt: instant::Duration,
state: &mut state::State,
_events: &mut Vec<state::Event>,
) {
let current_time = instant::Instant::now();
let delta_time = current_time - self.last_time;
if delta_time.as_secs_f32() > 1.0 {
if self.last_time.elapsed().as_secs_f32() > 1.0 {
state.game_state = state::GameState::MainMenu;
}
}

@ -2,8 +2,8 @@
use crate::state;
pub const PLAYER_SPEED: f32 = 0.05;
pub const BALL_SPEED: f32 = 0.025;
pub const PLAYER_SPEED: f32 = 1.5;
pub const BALL_SPEED: f32 = 1.0;
const BOUNCE_ANGLE: f32 = std::f32::consts::FRAC_PI_2;

@ -11,5 +11,5 @@ bytemuck = { version = "1.13.1", features = ["derive"] }
framework = { version = "0.1", path = "../framework"}
glam = { version = "0.24.1", features = ["bytemuck"] }
pollster = "0.3.0"
wgpu = "0.18"
winit = "0.28.6"
wgpu = "0.19"
winit = { version = "0.29", features = ["rwh_05"] }

@ -1,6 +1,6 @@
use std::{time::Duration, f32::consts::FRAC_PI_2};
use std::{f32::consts::FRAC_PI_2, time::Duration};
use winit::{event::{VirtualKeyCode, MouseScrollDelta}, dpi::PhysicalPosition};
use winit::{dpi::PhysicalPosition, event::MouseScrollDelta, keyboard::KeyCode};
const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001;
@ -12,11 +12,7 @@ pub struct Camera {
}
impl Camera {
pub fn new<V: Into<glam::Vec3>>(
position: V,
yaw: f32,
pitch: f32,
) -> Self {
pub fn new<V: Into<glam::Vec3>>(position: V, yaw: f32, pitch: f32) -> Self {
Self {
position: position.into(),
yaw,
@ -94,34 +90,30 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, pressed: bool) -> bool {
let amount = if pressed {
1.0
} else {
0.0
};
pub fn process_keyboard(&mut self, key: KeyCode, pressed: bool) -> bool {
let amount = if pressed { 1.0 } else { 0.0 };
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}
@ -183,4 +175,4 @@ impl CameraController {
camera.pitch = SAFE_FRAC_PI_2;
}
}
}
}

@ -2,8 +2,9 @@ mod camera;
use std::f32::consts::PI;
use camera::{Camera, Projection, CameraController};
use camera::{Camera, CameraController, Projection};
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use winit::keyboard::KeyCode;
const MAX_PARTICLES: u32 = 1000;
const PARTICLE_SIZE: u64 = 4 * 4 * 2;
@ -181,21 +182,25 @@ impl framework::Demo for Snow {
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None },
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
});
let uniforms_bind_group = display.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("uniforms_bind_group"),
layout: &uniforms_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
let uniforms_bind_group = display
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("uniforms_bind_group"),
layout: &uniforms_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}
],
});
}],
});
let draw_particles_layout =
display
@ -260,14 +265,15 @@ impl framework::Demo for Snow {
self.camera_controller.process_mouse(dx, dy);
self.uniforms_dirty = true;
}
fn process_keyboard(&mut self, key: winit::event::VirtualKeyCode, pressed: bool) {
fn process_keyboard(&mut self, key: KeyCode, pressed: bool) {
self.camera_controller.process_keyboard(key, pressed);
self.uniforms_dirty = true;
}
fn resize(&mut self, display: &framework::Display) {
self.projection.resize(display.config.width, display.config.height);
self.projection
.resize(display.config.width, display.config.height);
self.uniforms_dirty = true;
self.uniforms_dirty = true;
}
@ -278,7 +284,9 @@ impl framework::Demo for Snow {
self.uniforms_dirty = false;
self.camera_controller.update_camera(&mut self.camera, dt);
self.uniforms.view_proj = self.projection.calc_matrix() * self.camera.calc_matrix();
display.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&self.uniforms));
display
.queue
.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&self.uniforms));
}
let dt = dt.as_secs_f32();

@ -17,8 +17,8 @@ pollster = "0.3"
log = "0.4"
rayon = "1.4" # NEW!
tobj = { version = "3.2", features = ["async"]}
wgpu = { version = "0.18"}
winit = "0.28"
wgpu = { version = "0.19"}
winit = { version = "0.29", features = ["rwh_05"] }
instant = "0.1"
async-std = "1"
@ -31,7 +31,7 @@ features = ["png", "jpeg"]
reqwest = { version = "0.11" }
console_error_panic_hook = "0.1"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -3,6 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::PhysicalPosition;
use winit::event::*;
use winit::keyboard::KeyCode;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -104,34 +105,34 @@ impl CameraController {
}
}
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key {
VirtualKeyCode::W | VirtualKeyCode::Up => {
KeyCode::KeyW | KeyCode::ArrowUp => {
self.amount_forward = amount;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.amount_backward = amount;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.amount_left = amount;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.amount_right = amount;
true
}
VirtualKeyCode::Space => {
KeyCode::Space => {
self.amount_up = amount;
true
}
VirtualKeyCode::LShift => {
KeyCode::ShiftLeft => {
self.amount_down = amount;
true
}

@ -4,7 +4,8 @@ use std::iter;
use wgpu::util::DeviceExt;
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::Window,
};
@ -130,9 +131,9 @@ struct LightUniform {
_padding2: u32,
}
struct State {
window: Window,
surface: wgpu::Surface,
struct State<'a> {
window: &'a Window,
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -218,26 +219,25 @@ fn create_render_pipeline(
})
}
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
// # Safety
//
// The surface needs to live as long as the window that created it.
// State owns the window so this should be safe.
let surface = unsafe { instance.create_surface(&window) }.unwrap();
let surface = instance.create_surface(window).unwrap();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
})
@ -247,10 +247,10 @@ impl State {
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -278,11 +278,10 @@ impl State {
height: size.height,
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
desired_maximum_frame_latency: 2, // The max number of frames that can be in the queue
view_formats: vec![],
};
surface.configure(&device, &config);
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
@ -573,9 +572,9 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool {
match event {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(key),
event:
KeyEvent {
physical_key: PhysicalKey::Code(key),
state,
..
},
@ -694,7 +693,7 @@ pub async fn run() {
}
}
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let title = env!("CARGO_PKG_NAME");
let window = winit::window::WindowBuilder::new()
.with_title(title)
@ -706,34 +705,33 @@ pub async fn run() {
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})
.expect("Couldn't append canvas to document body.");
}
let mut state = State::new(window).await; // NEW!
let mut state = State::new(&window).await; // NEW!
let mut last_render_time = instant::Instant::now();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
// let window = &window;
event_loop.run(move |event, control_flow| {
// let _ = window;
match event {
Event::MainEventsCleared => state.window().request_redraw(),
// NEW!
Event::DeviceEvent {
event: DeviceEvent::MouseMotion{ delta, },
.. // We're not using device_id currently
} => if state.mouse_pressed {
state.camera_controller.process_mouse(delta.0, delta.1)
}
// UPDATED!
Event::WindowEvent {
ref event,
window_id,
@ -742,39 +740,37 @@ pub async fn run() {
#[cfg(not(target_arch="wasm32"))]
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
WindowEvent::RedrawRequested => {
state.window().request_redraw();
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
}
Event::RedrawRequested(window_id) if window_id == state.window().id() => {
let now = instant::Instant::now();
let dt = now - last_render_time;
last_render_time = now;
state.update(dt);
match state.render() {
Ok(_) => {}
// Reconfigure the surface if it's lost or outdated
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
// The system is out of memory, we should probably quit
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
// We're ignoring timeouts
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
}
}
_ => {}
}
});
}).unwrap();
}

@ -189,7 +189,7 @@ pub async fn load_model(
// Average the tangents/bitangents
for (i, n) in triangles_included.into_iter().enumerate() {
let denom = 1.0 / n as f32;
let mut v = &mut vertices[i];
let v = &mut vertices[i];
v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into();
v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into();
}

@ -1,6 +1,5 @@
use anyhow::*;
use image::GenericImageView;
use std::num::NonZeroU32;
pub struct Texture {
pub texture: wgpu::Texture,
@ -17,8 +16,8 @@ impl Texture {
label: &str,
) -> Self {
let size = wgpu::Extent3d {
width: config.width,
height: config.height,
width: config.width.max(1),
height: config.height.max(1),
depth_or_array_layers: 1,
};
let desc = wgpu::TextureDescriptor {

@ -9,7 +9,7 @@ edition = "2018"
[dependencies]
image = "0.24"
shaderc = "0.8"
wgpu = { version = "0.18", features = ["spirv"] }
wgpu = { version = "0.19", features = ["spirv"] }
pollster = "0.3"
futures-intrusive = "0.4"

@ -1,5 +1,3 @@
use std::num::NonZeroU32;
async fn run() {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),

@ -12,4 +12,4 @@ env_logger = "0.10"
rayon = "1"
serde = { version = "1", features = ["derive"]}
serde_json = "1"
wasm-bindgen-cli-support = "0.2.86"
wasm-bindgen-cli-support = "0.2.90"

@ -1,5 +1,9 @@
use std::{path::PathBuf, process::Command};
use std::{
path::PathBuf,
process::{Command, ExitStatus},
};
use anyhow::bail;
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use wasm_bindgen_cli_support::Bindgen;
@ -37,7 +41,11 @@ fn main() -> anyhow::Result<()> {
for target in &targets {
command.arg("-p").arg(&target.package);
}
command.spawn()?.wait()?;
let status = command.spawn()?.wait()?;
if !status.success() {
bail!("Failed to compile WASM with code ({status})");
}
let errors = targets
.par_iter()

@ -1,45 +1,52 @@
# Dependencies and the window
## Boring, I know
Some of you reading this are very experienced with opening up windows in Rust and probably have your favorite windowing library, but this guide is designed for everybody, so it's something that we need to cover. Luckily, you don't need to read this if you know what you're doing. One thing that you do need to know is that whatever windowing solution you use needs to support the [raw-window-handle](https://github.com/rust-windowing/raw-window-handle) crate.
## What crates are we using?
For the beginner stuff, we're going to keep things very simple. We'll add things as we go, but I've listed the relevant `Cargo.toml` bits below.
```toml
[dependencies]
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
log = "0.4"
wgpu = "0.18"
wgpu = "0.19.3"
```
## Using Rust's new resolver
As of version 0.10, wgpu requires Cargo's [newest feature resolver](https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2), which is the default in the 2021 edition (any new project started with Rust version 1.56.0 or newer). However, if you are still using the 2018 edition, you must include `resolver = "2"` in either the `[package]` section of `Cargo.toml` if you are working on a single crate or the `[workspace]` section of the root `Cargo.toml` in a workspace.
## env_logger
It is very important to enable logging via `env_logger::init();`.
When wgpu hits any error, it panics with a generic message, while logging the real error via the log crate.
This means if you don't include `env_logger::init()`, wgpu will fail silently, leaving you very confused!
(This has been done in the code below)
## Create a new project
run ```cargo new project_name``` where project_name is the name of the project.
(In the example below, I have used 'tutorial1_window')
## The code
There's not much going on here yet, so I'm just going to post the code in full. Just paste this into your `lib.rs` or equivalent.
```rust
use winit::{
event::*,
event_loop::{ControlFlow, EventLoop},
event_loop::EventLoop,
keyboard::{KeyCode, PhysicalKey},
window::WindowBuilder,
};
pub fn run() {
env_logger::init();
let event_loop = EventLoop::new();
let event_loop = EventLoop::new().unwrap();
let window = WindowBuilder::new().build(&event_loop).unwrap();
event_loop.run(move |event, _, control_flow| match event {
@ -49,10 +56,10 @@ pub fn run() {
} if window_id == window.id() => match event {
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
@ -110,7 +117,7 @@ cfg-if = "1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4.30"
web-sys = { version = "0.3", features = [
@ -174,14 +181,14 @@ Next, after we create our event loop and window, we need to add a canvas to the
// Winit prevents sizing with CSS, so we have to set
// the size manually when on web.
use winit::dpi::PhysicalSize;
window.set_inner_size(PhysicalSize::new(450, 400));
let _ = window.request_inner_size(PhysicalSize::new(450, 400));
use winit::platform::web::WindowExtWebSys;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let dst = doc.get_element_by_id("wasm-example")?;
let canvas = web_sys::Element::from(window.canvas());
let canvas = web_sys::Element::from(window.canvas()?);
dst.append_child(&canvas).ok()?;
Some(())
})

@ -7,8 +7,8 @@ For convenience, we're going to pack all the fields into a struct and create som
// lib.rs
use winit::window::Window;
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -16,12 +16,12 @@ struct State {
// The window must be declared after the surface so
// it gets dropped after it as the surface contains
// unsafe references to the window's resources.
window: Window,
window: &'a Window,
}
impl State {
impl<'a> State<'a> {
// Creating some of the wgpu types requires async code
async fn new(window: Window) -> Self {
async fn new(window: &'a Window) -> State<'a> {
todo!()
}
@ -53,15 +53,18 @@ I'm glossing over `State`s fields, but they'll make more sense as I explain the
The code for this is pretty straightforward, but let's break it down a bit.
```rust
impl State {
impl<'a> State<'a> {
// ...
async fn new(window: Window) -> Self {
async fn new(window: &'a Window) -> State<'a> {
let size = window.inner_size();
// The instance is a handle to our GPU
// Backends::all => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
#[cfg(not(target_arch="wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch="wasm32")]
backends: wgpu::Backends::GL,
..Default::default()
});
@ -127,10 +130,10 @@ Let's use the `adapter` to create the device and queue.
```rust
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
features: wgpu::Features::empty(),
required_features: wgpu::Features::empty(),
// WebGL doesn't support all of wgpu's features, so if
// we're building for the web, we'll have to disable some.
limits: if cfg!(target_arch = "wasm32") {
required_limits: if cfg!(target_arch = "wasm32") {
wgpu::Limits::downlevel_webgl2_defaults()
} else {
wgpu::Limits::default()
@ -145,7 +148,7 @@ The `features` field on `DeviceDescriptor` allows us to specify what extra featu
<div class="note">
The graphics card you have limits the features you can use. If you want to use certain features, you may need to limit what devices you support or provide workarounds.
The graphics card you haverequired_limits the features you can use. If you want to use certain features, you may need to limit what devices you support or provide workarounds.
You can get a list of features supported by your device using `adapter.features()` or `device.features()`.
@ -153,7 +156,7 @@ You can view a full list of features [here](https://docs.rs/wgpu/latest/wgpu/str
</div>
The `limits` field describes the limit of certain types of resources that we can create. We'll use the defaults for this tutorial so we can support most devices. You can view a list of limits [here](https://docs.rs/wgpu/latest/wgpu/struct.Limits.html).
The `limits` field describes the limit of certain types of resources that we can create. We'll use the defaults for this tutorial so we can support most devices. You can view a list ofrequired_limits [here](https://docs.rs/wgpu/latest/wgpu/struct.Limits.html).
```rust
let surface_caps = surface.get_capabilities(&adapter);
@ -173,8 +176,8 @@ The `limits` field describes the limit of certain types of resources that we can
present_mode: surface_caps.present_modes[0],
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
```
Here we are defining a config for our surface. This will define how the surface creates its underlying `SurfaceTexture`s. We will talk about `SurfaceTexture` when we get to the `render` function. For now, let's talk about the config's fields.
@ -210,7 +213,7 @@ Regardless, `PresentMode::Fifo` will always be supported, and `PresentMode::Auto
Now that we've configured our surface properly, we can add these new fields at the end of the method.
```rust
async fn new(window: Window) -> Self {
async fn new(window: &'a Window) -> State<'a> {
// ...
Self {
@ -233,9 +236,9 @@ Our `window` has beened moved to the State instance, we will need to update our
pub async fn run() {
// Window setup...
let mut state = State::new(window).await;
let mut state = State::new(&window).await;
event_loop.run(move |event, _, control_flow| {
event_loop.run(move |event, control_flow| {
match event {
Event::WindowEvent {
ref event,
@ -286,16 +289,16 @@ If we try to build WASM now, it will fail because `wasm-bindgen` doesn't support
```toml
[dependencies]
cfg-if = "1"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
log = "0.4"
wgpu = "0.18"
wgpu = "0.19"
pollster = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6"
console_log = "1.0"
wgpu = { version = "0.18", features = ["webgl"]}
wgpu = { version = "0.19", features = ["webgl"]}
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [

@ -156,8 +156,8 @@ This is the part where we finally make the thing in the title: the pipeline. Fir
```rust
// lib.rs
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,

@ -247,7 +247,7 @@ struct State {
num_vertices: u32,
}
impl State {
impl<'a> State<'a> {
// ...
fn new(...) -> Self {
// ...
@ -380,8 +380,8 @@ let num_indices = INDICES.len() as u32;
We don't need to implement `Pod` and `Zeroable` for our indices because `bytemuck` has already implemented them for basic types such as `u16`. That means we can just add `index_buffer` and `num_indices` to the `State` struct.
```rust
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,

@ -242,8 +242,8 @@ Looking at this, you might get a bit of déjà vu! That's because a `BindGroup`
Now that we have our `diffuse_bind_group`, let's add it to our `State` struct:
```rust
struct State {
surface: wgpu::Surface,
struct State<'a> {
surface: wgpu::Surface<'a>,
device: wgpu::Device,
queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration,
@ -259,7 +259,7 @@ struct State {
Make sure we return these fields in the `new` method:
```rust
impl State {
impl<'a> State<'a> {
async fn new() -> Self {
// ...
Self {
@ -446,11 +446,11 @@ For convenience, let's pull our texture code into its own module. We'll first ne
[dependencies]
image = "0.23"
cgmath = "0.18"
winit = "0.28"
winit = { version = "0.29", features = ["rwh_05"] }
env_logger = "0.10"
log = "0.4"
pollster = "0.3"
wgpu = "0.18"
wgpu = "0.19"
bytemuck = { version = "1.12", features = [ "derive" ] }
anyhow = "1.0" # NEW!
```
@ -593,7 +593,7 @@ struct State {
```
```rust
impl State {
impl<'a> State<'a> {
async fn new() -> Self {
// ...
Self {

@ -310,20 +310,19 @@ impl CameraController {
..
} => {
let is_pressed = *state == ElementState::Pressed;
match keycode {
VirtualKeyCode::W | VirtualKeyCode::Up => {
match keycode {KeyCode::KeyW | KeyCode::ArrowUp => {
self.is_forward_pressed = is_pressed;
true
}
VirtualKeyCode::A | VirtualKeyCode::Left => {
KeyCode::KeyA | KeyCode::ArrowLeft => {
self.is_left_pressed = is_pressed;
true
}
VirtualKeyCode::S | VirtualKeyCode::Down => {
KeyCode::KeyS | KeyCode::ArrowDown => {
self.is_backward_pressed = is_pressed;
true
}
VirtualKeyCode::D | VirtualKeyCode::Right => {
KeyCode::KeyD | KeyCode::ArrowRight => {
self.is_right_pressed = is_pressed;
true
}
@ -381,8 +380,8 @@ struct State {
// ...
}
// ...
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
// ...
let camera_controller = CameraController::new(0.2);
// ...

@ -97,8 +97,8 @@ const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(NUM_INS
Now, we can create the actual instances.
```rust
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
// ...
let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| {
(0..NUM_INSTANCES_PER_ROW).map(move |x| {

@ -453,7 +453,7 @@ where
Finally, we want to add Light rendering to our render passes.
```rust
impl State {
impl<'a> State<'a> {
// ...
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
// ...

@ -568,7 +568,7 @@ I found a cobblestone texture with a matching normal map and created a `debug_ma
```rust
// lib.rs
impl State {
impl<'a> State<'a> {
async fn new(window: &Window) -> Result<Self> {
// ...
let debug_material = {

@ -27,7 +27,12 @@ const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001;
`std::time::Instant` panics on WASM, so we'll use the [instant crate](https://docs.rs/instant). You'll want to include it in your `Cargo.toml`:
```toml
[dependencies]
# ...
instant = "0.1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
instant = { version = "0.1", features = [ "wasm-bindgen" ] }
```
</div>
@ -293,8 +298,8 @@ You'll need to import `winit::dpi::PhysicalPosition` if you haven't already.
We need to update `new()` as well.
```rust
impl State {
async fn new(window: Window) -> Self {
impl<'a> State<'a> {
async fn new(window: &'a Window) -> State<'a> {
// ...
// UPDATED!
@ -369,8 +374,7 @@ Here are the changes to `run()`:
```rust
fn main() {
// ...
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
event_loop.run(move |event, control_flow| {
match event {
// ...
// NEW!
@ -389,14 +393,14 @@ fn main() {
#[cfg(not(target_arch="wasm32"))]
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
input:
KeyboardInput {
event:
KeyEvent {
state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape),
physical_key: PhysicalKey::Code(KeyCode::Escape),
..
},
..
} => *control_flow = ControlFlow::Exit,
} => control_flow.exit(),
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
}
@ -437,10 +441,9 @@ We still need to calculate `dt`. Let's do that in the `main` function.
```rust
fn main() {
// ...
let mut state = State::new(window).await;
let mut state = State::new(&window).await;
let mut last_render_time = instant::Instant::now(); // NEW!
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
event_loop.run(move |event, control_flow| {
match event {
// ...
// UPDATED!

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save