pull/95/head
Ben Hansen 4 years ago
parent 786ce2410c
commit 73aed6c6bd

7
Cargo.lock generated

@ -1209,16 +1209,15 @@ dependencies = [
"anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
"bytemuck 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytemuck 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "cgmath 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"framework 0.1.0",
"fs_extra 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fs_extra 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.7 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.23.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"shaderc 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "shaderc 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tobj 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "tobj 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wgpu 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "wgpu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winit 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", "winit 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

@ -1,39 +1,30 @@
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{WindowBuilder}, window::WindowBuilder,
}; };
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| match event {
match event {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => match event { } if window_id == window.id() => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
_ => {} _ => {}
} },
_ => {} _ => {}
}
}); });
} }

@ -2,7 +2,7 @@ use std::iter;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
@ -24,21 +24,25 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
.unwrap();
let (device, queue) = adapter.request_device( let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -64,7 +68,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -74,10 +77,7 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::CursorMoved { WindowEvent::CursorMoved { position, .. } => {
position,
..
} => {
self.clear_color = wgpu::Color { self.clear_color = wgpu::Color {
r: position.x as f64 / self.size.width as f64, r: position.x as f64 / self.size.width as f64,
g: position.y as f64 / self.size.height as f64, g: position.y as f64 / self.size.height as f64,
@ -90,31 +90,31 @@ impl State {
} }
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(self.clear_color), load: wgpu::LoadOp::Clear(self.clear_color),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
} }
@ -126,9 +126,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -140,22 +138,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -166,6 +160,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -2,7 +2,7 @@ use std::iter;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
@ -23,21 +23,25 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
.unwrap();
let (device, queue) = adapter.request_device( let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -58,7 +62,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -71,23 +74,24 @@ impl State {
false false
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
@ -98,9 +102,8 @@ impl State {
a: 1.0, a: 1.0,
}), }),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
} }
@ -113,9 +116,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -127,22 +128,19 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { // UPDATED! } if window_id == window.id() => {
if !state.input(event) {
// UPDATED!
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -153,6 +151,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,7 +1,7 @@
use glob::glob;
use anyhow::*; use anyhow::*;
use glob::glob;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -12,7 +12,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -26,7 +27,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -42,17 +48,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -65,7 +69,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -2,7 +2,7 @@ use std::iter;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
@ -26,21 +26,25 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
.unwrap();
let (device, queue) = adapter.request_device( let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -54,16 +58,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[], bind_group_layouts: &[],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -74,25 +76,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -101,14 +99,13 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vs_module = device.create_shader_module(wgpu::include_spirv!("challenge.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("challenge.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("challenge.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("challenge.frag.spv"));
let challenge_render_pipeline = device.create_render_pipeline( let challenge_render_pipeline =
&wgpu::RenderPipelineDescriptor { device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -119,25 +116,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -146,8 +139,7 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let use_color = true; let use_color = true;
@ -164,7 +156,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -175,11 +166,10 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some( virtual_keycode: Some(VirtualKeyCode::Space),
VirtualKeyCode::Space
),
.. ..
}, },
.. ..
@ -191,38 +181,36 @@ impl State {
} }
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -241,9 +229,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -255,22 +241,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -281,6 +263,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -2,7 +2,7 @@ use std::iter;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
@ -25,21 +25,25 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
.unwrap();
let (device, queue) = adapter.request_device( let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -53,16 +57,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[], bind_group_layouts: &[],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -73,25 +75,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -100,8 +98,7 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
Self { Self {
surface, surface,
@ -114,7 +111,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -127,38 +123,36 @@ impl State {
false false
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -173,9 +167,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -187,22 +179,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -213,6 +201,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,7 +1,7 @@
use glob::glob;
use anyhow::*; use anyhow::*;
use glob::glob;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -12,7 +12,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -26,7 +27,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -42,17 +48,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -65,7 +69,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,11 +1,11 @@
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -33,24 +33,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float3, format: wgpu::VertexFormat::Float3,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], color: [0.5, 0.0, 0.5] }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], color: [0.5, 0.0, 0.5] }, // C color: [0.5, 0.0, 0.5],
Vertex { position: [0.35966998, -0.3473291, 0.0], color: [0.5, 0.0, 0.5] }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0],color: [0.5, 0.0, 0.5] }, // E Vertex {
position: [-0.49513406, 0.06958647, 0.0],
color: [0.5, 0.0, 0.5],
}, // B
Vertex {
position: [-0.21918549, -0.44939706, 0.0],
color: [0.5, 0.0, 0.5],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
color: [0.5, 0.0, 0.5],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
color: [0.5, 0.0, 0.5],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
struct State { struct State {
surface: wgpu::Surface, surface: wgpu::Surface,
@ -81,21 +92,25 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
.unwrap();
let (device, queue) = adapter.request_device( let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -109,16 +124,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[], bind_group_layouts: &[],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -129,25 +142,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -156,63 +165,50 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
let num_vertices = 16; let num_vertices = 16;
let angle = std::f32::consts::PI * 2.0 / num_vertices as f32; let angle = std::f32::consts::PI * 2.0 / num_vertices as f32;
let challenge_verts = (0..num_vertices).map(|i| { let challenge_verts = (0..num_vertices)
.map(|i| {
let theta = angle * i as f32; let theta = angle * i as f32;
Vertex { Vertex {
position: [ position: [0.5 * theta.cos(), -0.5 * theta.sin(), 0.0],
0.5 * theta.cos(), color: [(1.0 + theta.cos()) / 2.0, (1.0 + theta.sin()) / 2.0, 1.0],
-0.5 * theta.sin(),
0.0,
],
color: [
(1.0 + theta.cos()) / 2.0,
(1.0 + theta.sin()) / 2.0,
1.0,
]
} }
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
let num_triangles = num_vertices - 2; let num_triangles = num_vertices - 2;
let challenge_indices = (1u16..num_triangles+1).into_iter().flat_map(|i| { let challenge_indices = (1u16..num_triangles + 1)
vec![i + 1, i, 0] .into_iter()
}).collect::<Vec<_>>(); .flat_map(|i| vec![i + 1, i, 0])
.collect::<Vec<_>>();
let num_challenge_indices = challenge_indices.len() as u32; let num_challenge_indices = challenge_indices.len() as u32;
let challenge_vertex_buffer = device.create_buffer_init( let challenge_vertex_buffer =
&wgpu::util::BufferInitDescriptor { device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Challenge Vertex Buffer"), label: Some("Challenge Vertex Buffer"),
contents: bytemuck::cast_slice(&challenge_verts), contents: bytemuck::cast_slice(&challenge_verts),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let challenge_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let challenge_index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Challenge Index Buffer"), label: Some("Challenge Index Buffer"),
contents: bytemuck::cast_slice(&challenge_indices), contents: bytemuck::cast_slice(&challenge_indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let use_complex = false; let use_complex = false;
@ -234,7 +230,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -245,7 +240,8 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(VirtualKeyCode::Space), virtual_keycode: Some(VirtualKeyCode::Space),
.. ..
@ -259,38 +255,36 @@ impl State {
} }
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -300,14 +294,10 @@ impl State {
( (
&self.challenge_vertex_buffer, &self.challenge_vertex_buffer,
&self.challenge_index_buffer, &self.challenge_index_buffer,
self.num_challenge_indices self.num_challenge_indices,
) )
} else { } else {
( (&self.vertex_buffer, &self.index_buffer, self.num_indices)
&self.vertex_buffer,
&self.index_buffer,
self.num_indices,
)
}; };
render_pass.set_vertex_buffer(0, data.0.slice(..)); render_pass.set_vertex_buffer(0, data.0.slice(..));
render_pass.set_index_buffer(data.1.slice(..)); render_pass.set_index_buffer(data.1.slice(..));
@ -322,9 +312,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -336,22 +324,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -362,6 +346,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,11 +1,11 @@
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -33,24 +33,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float3, format: wgpu::VertexFormat::Float3,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], color: [0.5, 0.0, 0.5] }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], color: [0.5, 0.0, 0.5] }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], color: [0.5, 0.0, 0.5] }, // C color: [0.5, 0.0, 0.5],
Vertex { position: [0.35966998, -0.3473291, 0.0], color: [0.5, 0.0, 0.5] }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0],color: [0.5, 0.0, 0.5] }, // E Vertex {
position: [-0.49513406, 0.06958647, 0.0],
color: [0.5, 0.0, 0.5],
}, // B
Vertex {
position: [-0.21918549, -0.44939706, 0.0],
color: [0.5, 0.0, 0.5],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
color: [0.5, 0.0, 0.5],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
color: [0.5, 0.0, 0.5],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
struct State { struct State {
surface: wgpu::Surface, surface: wgpu::Surface,
@ -74,21 +85,25 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
.unwrap();
let (device, queue) = adapter.request_device( let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -102,16 +117,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[], bind_group_layouts: &[],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -122,25 +135,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -149,23 +158,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -182,7 +186,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -195,38 +198,36 @@ impl State {
false false
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -243,9 +244,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -257,22 +256,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -283,6 +278,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,7 +1,7 @@
use glob::glob;
use anyhow::*; use anyhow::*;
use glob::glob;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -12,7 +12,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -26,7 +27,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -42,17 +48,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -65,7 +69,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,11 +1,11 @@
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -36,25 +36,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C tex_coords: [0.4131759, 0.00759614],
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E Vertex {
]; position: [-0.49513406, 0.06958647, 0.0],
tex_coords: [0.0048659444, 0.43041354],
const INDICES: &[u16] = &[ }, // B
0, 1, 4, Vertex {
1, 2, 4, position: [-0.21918549, -0.44939706, 0.0],
2, 3, 4, tex_coords: [0.28081453, 0.949397057],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
tex_coords: [0.85967, 0.84732911],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
tex_coords: [0.9414737, 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
struct State { struct State {
surface: wgpu::Surface, surface: wgpu::Surface,
@ -84,20 +94,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -108,8 +122,8 @@ impl State {
}; };
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout = device.create_bind_group_layout( let texture_bind_group_layout =
&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -124,26 +138,18 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
"happy-tree.png",
).unwrap();
let diffuse_bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -153,22 +159,17 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let cartoon_bytes = include_bytes!("happy-tree-cartoon.png"); let cartoon_bytes = include_bytes!("happy-tree-cartoon.png");
let cartoon_texture = texture::Texture::from_bytes( let cartoon_texture =
&device, texture::Texture::from_bytes(&device, &queue, cartoon_bytes, "happy-tree-cartoon.png")
&queue, .unwrap();
cartoon_bytes,
"happy-tree-cartoon.png", let cartoon_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
).unwrap();
let cartoon_bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -178,25 +179,22 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&cartoon_texture.sampler), resource: wgpu::BindingResource::Sampler(&cartoon_texture.sampler),
} },
], ],
label: Some("cartoon_bind_group"), label: Some("cartoon_bind_group"),
} });
);
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&texture_bind_group_layout], bind_group_layouts: &[&texture_bind_group_layout],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -207,25 +205,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -234,23 +228,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -272,7 +261,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -283,7 +271,8 @@ impl State {
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(VirtualKeyCode::Space), virtual_keycode: Some(VirtualKeyCode::Space),
.. ..
@ -297,38 +286,36 @@ impl State {
} }
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -352,9 +339,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -366,22 +351,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -392,6 +373,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,11 +1,11 @@
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -36,24 +36,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C tex_coords: [0.4131759, 0.00759614],
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E Vertex {
position: [-0.49513406, 0.06958647, 0.0],
tex_coords: [0.0048659444, 0.43041354],
}, // B
Vertex {
position: [-0.21918549, -0.44939706, 0.0],
tex_coords: [0.28081453, 0.949397057],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
tex_coords: [0.85967, 0.84732911],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
tex_coords: [0.9414737, 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
struct State { struct State {
surface: wgpu::Surface, surface: wgpu::Surface,
@ -80,21 +91,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -106,15 +120,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes,
"happy-tree.png"
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout( let texture_bind_group_layout =
&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -129,18 +139,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -150,25 +156,22 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&texture_bind_group_layout], bind_group_layouts: &[&texture_bind_group_layout],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -179,25 +182,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -206,23 +205,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -241,7 +235,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -254,38 +247,36 @@ impl State {
false false
} }
fn update(&mut self) { fn update(&mut self) {}
}
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -303,9 +294,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -317,22 +306,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -343,6 +328,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,5 +1,5 @@
use image::GenericImageView;
use anyhow::*; use anyhow::*;
use image::GenericImageView;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
@ -12,7 +12,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bytes: &[u8], bytes: &[u8],
label: &str label: &str,
) -> Result<Self> { ) -> Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label)) Self::from_image(device, queue, &img, Some(label))
@ -22,7 +22,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
img: &image::DynamicImage, img: &image::DynamicImage,
label: Option<&str> label: Option<&str>,
) -> Result<Self> { ) -> Result<Self> {
let rgba = img.as_rgba8().unwrap(); let rgba = img.as_rgba8().unwrap();
let dimensions = img.dimensions(); let dimensions = img.dimensions();
@ -32,8 +32,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -41,8 +40,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -60,8 +58,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -69,9 +66,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -1,7 +1,7 @@
use glob::glob;
use anyhow::*; use anyhow::*;
use glob::glob;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -12,7 +12,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -26,7 +27,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -42,17 +48,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -65,7 +69,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,11 +1,11 @@
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -36,24 +36,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C tex_coords: [0.4131759, 0.00759614],
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E Vertex {
position: [-0.49513406, 0.06958647, 0.0],
tex_coords: [0.0048659444, 0.43041354],
}, // B
Vertex {
position: [-0.21918549, -0.44939706, 0.0],
tex_coords: [0.28081453, 0.949397057],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
tex_coords: [0.85967, 0.84732911],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
tex_coords: [0.9414737, 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -88,17 +99,18 @@ struct UniformStaging {
impl UniformStaging { impl UniformStaging {
fn new(camera: Camera) -> Self { fn new(camera: Camera) -> Self {
Self { camera, model_rotation: cgmath::Deg(0.0) } Self {
camera,
model_rotation: cgmath::Deg(0.0),
}
} }
fn update_uniforms(&self, uniforms: &mut Uniforms) { fn update_uniforms(&self, uniforms: &mut Uniforms) {
uniforms.model_view_proj = uniforms.model_view_proj = OPENGL_TO_WGPU_MATRIX
OPENGL_TO_WGPU_MATRIX
* self.camera.build_view_projection_matrix() * self.camera.build_view_projection_matrix()
* cgmath::Matrix4::from_angle_z(self.model_rotation); * cgmath::Matrix4::from_angle_z(self.model_rotation);
} }
} }
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
struct Uniforms { struct Uniforms {
@ -143,7 +155,8 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool { fn process_events(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(keycode), virtual_keycode: Some(keycode),
.. ..
@ -245,20 +258,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -270,15 +287,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let texture_bind_group_layout =
"happy-tree.png" device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -293,18 +306,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -314,11 +323,10 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 1.0, 2.0).into(), eye: (0.0, 1.0, 2.0).into(),
@ -335,17 +343,15 @@ impl State {
let uniform_staging = UniformStaging::new(camera); let uniform_staging = UniformStaging::new(camera);
uniform_staging.update_uniforms(&mut uniforms); uniform_staging.update_uniforms(&mut uniforms);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
entries: &[ device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
wgpu::BindGroupLayoutEntry { entries: &[wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStage::VERTEX, visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::UniformBuffer { ty: wgpu::BindingType::UniformBuffer {
@ -353,38 +359,30 @@ impl State {
min_binding_size: None, min_binding_size: None,
}, },
count: None, count: None,
} }],
],
label: Some("uniform_bind_group_layout"), label: Some("uniform_bind_group_layout"),
}); });
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &uniform_bind_group_layout, layout: &uniform_bind_group_layout,
entries: &[ entries: &[wgpu::BindGroupEntry {
wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
} }],
],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
}); });
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -395,25 +393,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -422,23 +416,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -462,7 +451,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -477,40 +465,45 @@ impl State {
} }
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.uniform_staging.camera); self.camera_controller
.update_camera(&mut self.uniform_staging.camera);
self.uniform_staging.model_rotation += cgmath::Deg(2.0); self.uniform_staging.model_rotation += cgmath::Deg(2.0);
self.uniform_staging.update_uniforms(&mut self.uniforms); self.uniform_staging.update_uniforms(&mut self.uniforms);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -529,9 +522,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -543,22 +534,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -569,6 +556,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,11 +1,11 @@
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -36,24 +36,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C tex_coords: [0.4131759, 0.00759614],
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E Vertex {
position: [-0.49513406, 0.06958647, 0.0],
tex_coords: [0.0048659444, 0.43041354],
}, // B
Vertex {
position: [-0.21918549, -0.44939706, 0.0],
tex_coords: [0.28081453, 0.949397057],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
tex_coords: [0.85967, 0.84732911],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
tex_coords: [0.9414737, 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -129,7 +140,8 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool { fn process_events(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(keycode), virtual_keycode: Some(keycode),
.. ..
@ -232,20 +244,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -257,15 +273,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let texture_bind_group_layout =
"happy-tree.png" device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -280,18 +292,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -301,11 +309,10 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 1.0, 2.0).into(), eye: (0.0, 1.0, 2.0).into(),
@ -321,17 +328,15 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
entries: &[ device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
wgpu::BindGroupLayoutEntry { entries: &[wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStage::VERTEX, visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::UniformBuffer { ty: wgpu::BindingType::UniformBuffer {
@ -339,38 +344,30 @@ impl State {
min_binding_size: None, min_binding_size: None,
}, },
count: None, count: None,
} }],
],
label: Some("uniform_bind_group_layout"), label: Some("uniform_bind_group_layout"),
}); });
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &uniform_bind_group_layout, layout: &uniform_bind_group_layout,
entries: &[ entries: &[wgpu::BindGroupEntry {
wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
} }],
],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
}); });
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -381,25 +378,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -408,23 +401,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -448,7 +436,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -465,37 +452,41 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -514,9 +505,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -528,22 +517,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -554,6 +539,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,5 +1,5 @@
use image::GenericImageView;
use anyhow::*; use anyhow::*;
use image::GenericImageView;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
@ -12,7 +12,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bytes: &[u8], bytes: &[u8],
label: &str label: &str,
) -> Result<Self> { ) -> Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label)) Self::from_image(device, queue, &img, Some(label))
@ -22,7 +22,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
img: &image::DynamicImage, img: &image::DynamicImage,
label: Option<&str> label: Option<&str>,
) -> Result<Self> { ) -> Result<Self> {
let rgba = img.as_rgba8().unwrap(); let rgba = img.as_rgba8().unwrap();
let dimensions = img.dimensions(); let dimensions = img.dimensions();
@ -32,8 +32,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -41,8 +40,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -60,8 +58,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -69,9 +66,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -1,7 +1,7 @@
use glob::glob;
use anyhow::*; use anyhow::*;
use glob::glob;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -12,7 +12,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -26,7 +27,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -42,17 +48,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -65,7 +69,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,12 +1,12 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -37,24 +37,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B position: [-0.0868241, -0.49240386, 0.0],
Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614],
Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E Vertex {
position: [-0.49513406, -0.06958647, 0.0],
tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354],
}, // B
Vertex {
position: [-0.21918549, 0.44939706, 0.0],
tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057],
}, // C
Vertex {
position: [0.35966998, 0.3473291, 0.0],
tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911],
}, // D
Vertex {
position: [0.44147372, -0.2347359, 0.0],
tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -65,8 +76,11 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
); );
const NUM_INSTANCES_PER_ROW: u32 = 10; const NUM_INSTANCES_PER_ROW: u32 = 10;
const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(
NUM_INSTANCES_PER_ROW as f32 * 0.5,
0.0,
NUM_INSTANCES_PER_ROW as f32 * 0.5,
);
struct Camera { struct Camera {
eye: cgmath::Point3<f32>, eye: cgmath::Point3<f32>,
@ -133,7 +147,8 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool { fn process_events(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(keycode), virtual_keycode: Some(keycode),
.. ..
@ -214,10 +229,9 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
let transform = cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation); let transform =
InstanceRaw { cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation);
transform, InstanceRaw { transform }
}
} }
} }
@ -254,7 +268,6 @@ struct State {
} }
fn quat_mul(q: cgmath::Quaternion<f32>, r: cgmath::Quaternion<f32>) -> cgmath::Quaternion<f32> { fn quat_mul(q: cgmath::Quaternion<f32>, r: cgmath::Quaternion<f32>) -> cgmath::Quaternion<f32> {
// This block uses quaternions of the form of // This block uses quaternions of the form of
// q=q0+iq1+jq2+kq3 // q=q0+iq1+jq2+kq3
@ -274,7 +287,6 @@ fn quat_mul(q: cgmath::Quaternion<f32>, r: cgmath::Quaternion<f32>) -> cgmath::Q
// t2=(r0 q2 + r1 q3 + r2 q0 r3 q1) // t2=(r0 q2 + r1 q3 + r2 q0 r3 q1)
// t3=(r0 q3 r1 q2 + r2 q1 + r3 q0 // t3=(r0 q3 r1 q2 + r2 q1 + r3 q0
let w = r.s * q.s - r.v.x * q.v.x - r.v.y * q.v.y - r.v.z * q.v.z; let w = r.s * q.s - r.v.x * q.v.x - r.v.y * q.v.y - r.v.z * q.v.z;
let xi = r.s * q.v.x + r.v.x * q.s - r.v.y * q.v.z + r.v.z * q.v.y; let xi = r.s * q.v.x + r.v.x * q.s - r.v.y * q.v.z + r.v.z * q.v.y;
let yj = r.s * q.v.y + r.v.x * q.v.z + r.v.y * q.s - r.v.z * q.v.x; let yj = r.s * q.v.y + r.v.x * q.v.z + r.v.y * q.s - r.v.z * q.v.x;
@ -291,20 +303,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -316,15 +332,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let texture_bind_group_layout =
"happy-tree.png" device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -339,18 +351,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -360,11 +368,10 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, -10.0).into(), eye: (0.0, 5.0, -10.0).into(),
@ -380,42 +387,49 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { let instances = (0..NUM_INSTANCES_PER_ROW)
.flat_map(|z| {
(0..NUM_INSTANCES_PER_ROW).map(move |x| { (0..NUM_INSTANCES_PER_ROW).map(move |x| {
let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; let position = cgmath::Vector3 {
x: x as f32,
y: 0.0,
z: z as f32,
} - INSTANCE_DISPLACEMENT;
let rotation = if position.is_zero() { let rotation = if position.is_zero() {
// this is needed so an object at (0, 0, 0) won't get scaled to zero // this is needed so an object at (0, 0, 0) won't get scaled to zero
// as Quaternions can effect scale if they're not create correctly // as Quaternions can effect scale if they're not create correctly
cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_y(), cgmath::Deg(0.0)) cgmath::Quaternion::from_axis_angle(
cgmath::Vector3::unit_y(),
cgmath::Deg(0.0),
)
} else { } else {
cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) cgmath::Quaternion::from_axis_angle(
position.clone().normalize(),
cgmath::Deg(45.0),
)
}; };
Instance { Instance { position, rotation }
position, rotation, })
}
}) })
}).collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::STORAGE | wgpu::BufferUsage::COPY_DST,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -448,12 +462,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -462,19 +476,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -485,25 +494,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -512,23 +517,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -554,7 +554,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -571,45 +570,57 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
for instance in &mut self.instances { for instance in &mut self.instances {
let amount = cgmath::Quaternion::from_angle_y(cgmath::Rad(ROTATION_SPEED)); let amount = cgmath::Quaternion::from_angle_y(cgmath::Rad(ROTATION_SPEED));
let current = instance.rotation; let current = instance.rotation;
instance.rotation = quat_mul(amount, current); instance.rotation = quat_mul(amount, current);
} }
let instance_data = self.instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = self
self.queue.write_buffer(&self.instance_buffer, 0, bytemuck::cast_slice(&instance_data)); .instances
.iter()
.map(Instance::to_raw)
.collect::<Vec<_>>();
self.queue.write_buffer(
&self.instance_buffer,
0,
bytemuck::cast_slice(&instance_data),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -628,9 +639,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -642,22 +651,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -668,6 +673,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,17 +1,21 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
const NUM_INSTANCES_PER_ROW: u32 = 10; const NUM_INSTANCES_PER_ROW: u32 = 10;
const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(
NUM_INSTANCES_PER_ROW as f32 * 0.5,
0.0,
NUM_INSTANCES_PER_ROW as f32 * 0.5,
);
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -40,24 +44,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.43041354], }, // B position: [-0.0868241, 0.49240386, 0.0],
Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.949397057], }, // C tex_coords: [0.4131759, 0.00759614],
Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.2652641], }, // E Vertex {
position: [-0.49513406, 0.06958647, 0.0],
tex_coords: [0.0048659444, 0.43041354],
}, // B
Vertex {
position: [-0.21918549, -0.44939706, 0.0],
tex_coords: [0.28081453, 0.949397057],
}, // C
Vertex {
position: [0.35966998, -0.3473291, 0.0],
tex_coords: [0.85967, 0.84732911],
}, // D
Vertex {
position: [0.44147372, 0.2347359, 0.0],
tex_coords: [0.9414737, 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -132,7 +147,8 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool { fn process_events(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(keycode), virtual_keycode: Some(keycode),
.. ..
@ -203,7 +219,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -251,20 +268,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -276,15 +297,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let texture_bind_group_layout =
"happy-tree.png" device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -299,18 +316,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -320,11 +333,10 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, 10.0).into(), eye: (0.0, 5.0, 10.0).into(),
@ -340,42 +352,49 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { let instances = (0..NUM_INSTANCES_PER_ROW)
.flat_map(|z| {
(0..NUM_INSTANCES_PER_ROW).map(move |x| { (0..NUM_INSTANCES_PER_ROW).map(move |x| {
let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; let position = cgmath::Vector3 {
x: x as f32,
y: 0.0,
z: z as f32,
} - INSTANCE_DISPLACEMENT;
let rotation = if position.is_zero() { let rotation = if position.is_zero() {
// this is needed so an object at (0, 0, 0) won't get scaled to zero // this is needed so an object at (0, 0, 0) won't get scaled to zero
// as Quaternions can effect scale if they're not created correctly // as Quaternions can effect scale if they're not created correctly
cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) cgmath::Quaternion::from_axis_angle(
cgmath::Vector3::unit_z(),
cgmath::Deg(0.0),
)
} else { } else {
cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) cgmath::Quaternion::from_axis_angle(
position.clone().normalize(),
cgmath::Deg(45.0),
)
}; };
Instance { Instance { position, rotation }
position, rotation,
}
}) })
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -408,12 +427,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -422,19 +441,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -445,25 +459,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -472,23 +482,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -515,7 +520,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -532,37 +536,41 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -582,9 +590,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -596,22 +602,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -622,6 +624,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,5 +1,5 @@
use image::GenericImageView;
use anyhow::*; use anyhow::*;
use image::GenericImageView;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
@ -12,7 +12,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bytes: &[u8], bytes: &[u8],
label: &str label: &str,
) -> Result<Self> { ) -> Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label)) Self::from_image(device, queue, &img, Some(label))
@ -22,7 +22,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
img: &image::DynamicImage, img: &image::DynamicImage,
label: Option<&str> label: Option<&str>,
) -> Result<Self> { ) -> Result<Self> {
let rgba = img.as_rgba8().unwrap(); let rgba = img.as_rgba8().unwrap();
let dimensions = img.dimensions(); let dimensions = img.dimensions();
@ -32,8 +32,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -41,8 +40,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -60,8 +58,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -69,9 +66,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -1,7 +1,7 @@
use glob::glob;
use anyhow::*; use anyhow::*;
use glob::glob;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -12,7 +12,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -26,7 +27,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -42,17 +48,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -65,7 +69,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,12 +1,12 @@
use std::iter; use std::iter;
use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use cgmath::prelude::*;
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -37,36 +37,56 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B position: [-0.0868241, -0.49240386, 0.0],
Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614],
Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E Vertex {
position: [-0.49513406, -0.06958647, 0.0],
tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354],
}, // B
Vertex {
position: [-0.21918549, 0.44939706, 0.0],
tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057],
}, // C
Vertex {
position: [0.35966998, 0.3473291, 0.0],
tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911],
}, // D
Vertex {
position: [0.44147372, -0.2347359, 0.0],
tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
const DEPTH_VERTICES: &[Vertex] = &[ const DEPTH_VERTICES: &[Vertex] = &[
Vertex { position: [0.0, 0.0, 0.0], tex_coords: [0.0, 1.0]}, Vertex {
Vertex { position: [1.0, 0.0, 0.0], tex_coords: [1.0, 1.0]}, position: [0.0, 0.0, 0.0],
Vertex { position: [1.0, 1.0, 0.0], tex_coords: [1.0, 0.0]}, tex_coords: [0.0, 1.0],
Vertex { position: [0.0, 1.0, 0.0], tex_coords: [0.0, 0.0]}, },
Vertex {
position: [1.0, 0.0, 0.0],
tex_coords: [1.0, 1.0],
},
Vertex {
position: [1.0, 1.0, 0.0],
tex_coords: [1.0, 0.0],
},
Vertex {
position: [0.0, 1.0, 0.0],
tex_coords: [0.0, 0.0],
},
]; ];
const DEPTH_INDICES: &[u16] = &[ const DEPTH_INDICES: &[u16] = &[0, 1, 2, 0, 2, 3];
0, 1, 2,
0, 2, 3,
];
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -79,8 +99,11 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
const NUM_INSTANCES_PER_ROW: u32 = 10; const NUM_INSTANCES_PER_ROW: u32 = 10;
#[allow(dead_code)] #[allow(dead_code)]
const NUM_INSTANCES: u32 = NUM_INSTANCES_PER_ROW * NUM_INSTANCES_PER_ROW; const NUM_INSTANCES: u32 = NUM_INSTANCES_PER_ROW * NUM_INSTANCES_PER_ROW;
const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(
NUM_INSTANCES_PER_ROW as f32 * 0.5,
0.0,
NUM_INSTANCES_PER_ROW as f32 * 0.5,
);
struct Camera { struct Camera {
eye: cgmath::Point3<f32>, eye: cgmath::Point3<f32>,
@ -147,7 +170,8 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool { fn process_events(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(keycode), virtual_keycode: Some(keycode),
.. ..
@ -227,7 +251,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -255,8 +280,7 @@ impl DepthPass {
fn new(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { fn new(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self {
let texture = texture::Texture::create_depth_texture(device, sc_desc, "depth_texture"); let texture = texture::Texture::create_depth_texture(device, sc_desc, "depth_texture");
let layout = device.create_bind_group_layout( let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
&wgpu::BindGroupLayoutDescriptor {
label: Some("Depth Pass Layout"), label: Some("Depth Pass Layout"),
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
@ -272,14 +296,11 @@ impl DepthPass {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
count: None, count: None,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: true },
comparison: true,
},
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
} },
] ],
} });
);
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout, layout: &layout,
@ -291,33 +312,27 @@ impl DepthPass {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&texture.sampler), resource: wgpu::BindingResource::Sampler(&texture.sampler),
} },
], ],
label: Some("depth_pass.bind_group"), label: Some("depth_pass.bind_group"),
}); });
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Depth Pass VB"), label: Some("Depth Pass VB"),
contents: bytemuck::cast_slice(DEPTH_VERTICES), contents: bytemuck::cast_slice(DEPTH_VERTICES),
usage: wgpu::BufferUsage::VERTEX usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Depth Pass IB"), label: Some("Depth Pass IB"),
contents: bytemuck::cast_slice(DEPTH_INDICES), contents: bytemuck::cast_slice(DEPTH_INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let pipeline_layout = device.create_pipeline_layout( let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
&wgpu::PipelineLayoutDescriptor {
label: Some("Depth Pass Pipeline Layout"), label: Some("Depth Pass Pipeline Layout"),
bind_group_layouts: &[&layout], bind_group_layouts: &[&layout],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let vs_module = device.create_shader_module(wgpu::include_spirv!("challenge.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("challenge.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("challenge.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("challenge.frag.spv"));
@ -342,20 +357,16 @@ impl DepthPass {
clamp_depth: false, clamp_depth: false,
}), }),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[ vertex_buffers: &[Vertex::desc()],
Vertex::desc(),
],
}, },
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
@ -385,7 +396,7 @@ impl DepthPass {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&self.texture.sampler), resource: wgpu::BindingResource::Sampler(&self.texture.sampler),
} },
], ],
label: Some("depth_pass.bind_group"), label: Some("depth_pass.bind_group"),
}); });
@ -393,16 +404,14 @@ impl DepthPass {
fn render(&self, frame: &wgpu::SwapChainTexture, encoder: &mut wgpu::CommandEncoder) { fn render(&self, frame: &wgpu::SwapChainTexture, encoder: &mut wgpu::CommandEncoder) {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Load, load: wgpu::LoadOp::Load,
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
render_pass.set_pipeline(&self.render_pipeline); render_pass.set_pipeline(&self.render_pipeline);
@ -446,20 +455,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -471,15 +484,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let texture_bind_group_layout =
"happy-tree.png" device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -494,18 +503,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -518,8 +523,7 @@ impl State {
}, },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, -10.0).into(), eye: (0.0, 5.0, -10.0).into(),
@ -535,42 +539,49 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { let instances = (0..NUM_INSTANCES_PER_ROW)
.flat_map(|z| {
(0..NUM_INSTANCES_PER_ROW).map(move |x| { (0..NUM_INSTANCES_PER_ROW).map(move |x| {
let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; let position = cgmath::Vector3 {
x: x as f32,
y: 0.0,
z: z as f32,
} - INSTANCE_DISPLACEMENT;
let rotation = if position.is_zero() { let rotation = if position.is_zero() {
// this is needed so an object at (0, 0, 0) won't get scaled to zero // this is needed so an object at (0, 0, 0) won't get scaled to zero
// as Quaternions can effect scale if they're not create correctly // as Quaternions can effect scale if they're not create correctly
cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) cgmath::Quaternion::from_axis_angle(
cgmath::Vector3::unit_z(),
cgmath::Deg(0.0),
)
} else { } else {
cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) cgmath::Quaternion::from_axis_angle(
position.clone().normalize(),
cgmath::Deg(45.0),
)
}; };
Instance { Instance { position, rotation }
position, rotation,
}
}) })
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -603,12 +614,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -617,19 +628,14 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -640,25 +646,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 2, // corresponds to bilinear filtering depth_bias: 2, // corresponds to bilinear filtering
depth_bias_slope_scale: 2.0, depth_bias_slope_scale: 2.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING), clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING),
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
format: texture::Texture::DEPTH_FORMAT, format: texture::Texture::DEPTH_FORMAT,
depth_write_enabled: true, depth_write_enabled: true,
@ -672,23 +674,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
let depth_pass = DepthPass::new(&device, &sc_desc); let depth_pass = DepthPass::new(&device, &sc_desc);
@ -717,7 +714,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -736,37 +732,41 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_pass.texture.view, attachment: &self.depth_pass.texture.view,
depth_ops: Some(wgpu::Operations { depth_ops: Some(wgpu::Operations {
@ -794,9 +794,7 @@ impl State {
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -808,22 +806,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -834,6 +828,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,12 +1,12 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder},
}; };
use wgpu::util::DeviceExt;
mod texture; mod texture;
@ -37,24 +37,35 @@ impl Vertex {
shader_location: 1, shader_location: 1,
format: wgpu::VertexFormat::Float2, format: wgpu::VertexFormat::Float2,
}, },
] ],
} }
} }
} }
const VERTICES: &[Vertex] = &[ const VERTICES: &[Vertex] = &[
Vertex { position: [-0.0868241, -0.49240386, 0.0], tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614], }, // A Vertex {
Vertex { position: [-0.49513406, -0.06958647, 0.0], tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354], }, // B position: [-0.0868241, -0.49240386, 0.0],
Vertex { position: [-0.21918549, 0.44939706, 0.0], tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057], }, // C tex_coords: [1.0 - 0.4131759, 1.0 - 0.00759614],
Vertex { position: [0.35966998, 0.3473291, 0.0], tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911], }, // D }, // A
Vertex { position: [0.44147372, -0.2347359, 0.0], tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641], }, // E Vertex {
position: [-0.49513406, -0.06958647, 0.0],
tex_coords: [1.0 - 0.0048659444, 1.0 - 0.43041354],
}, // B
Vertex {
position: [-0.21918549, 0.44939706, 0.0],
tex_coords: [1.0 - 0.28081453, 1.0 - 0.949397057],
}, // C
Vertex {
position: [0.35966998, 0.3473291, 0.0],
tex_coords: [1.0 - 0.85967, 1.0 - 0.84732911],
}, // D
Vertex {
position: [0.44147372, -0.2347359, 0.0],
tex_coords: [1.0 - 0.9414737, 1.0 - 0.2652641],
}, // E
]; ];
const INDICES: &[u16] = &[ const INDICES: &[u16] = &[0, 1, 4, 1, 2, 4, 2, 3, 4];
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -65,8 +76,11 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
); );
const NUM_INSTANCES_PER_ROW: u32 = 10; const NUM_INSTANCES_PER_ROW: u32 = 10;
const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5); const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(
NUM_INSTANCES_PER_ROW as f32 * 0.5,
0.0,
NUM_INSTANCES_PER_ROW as f32 * 0.5,
);
struct Camera { struct Camera {
eye: cgmath::Point3<f32>, eye: cgmath::Point3<f32>,
@ -133,7 +147,8 @@ impl CameraController {
fn process_events(&mut self, event: &WindowEvent) -> bool { fn process_events(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
state, state,
virtual_keycode: Some(keycode), virtual_keycode: Some(keycode),
.. ..
@ -213,7 +228,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -261,20 +277,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -286,15 +306,11 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let diffuse_bytes = include_bytes!("happy-tree.png"); let diffuse_bytes = include_bytes!("happy-tree.png");
let diffuse_texture = texture::Texture::from_bytes( let diffuse_texture =
&device, texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "happy-tree.png").unwrap();
&queue,
diffuse_bytes, let texture_bind_group_layout =
"happy-tree.png" device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
).unwrap();
let texture_bind_group_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -309,18 +325,14 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let diffuse_bind_group = device.create_bind_group( let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout, layout: &texture_bind_group_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -330,11 +342,10 @@ impl State {
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
} },
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, -10.0).into(), eye: (0.0, 5.0, -10.0).into(),
@ -350,42 +361,49 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| { let instances = (0..NUM_INSTANCES_PER_ROW)
.flat_map(|z| {
(0..NUM_INSTANCES_PER_ROW).map(move |x| { (0..NUM_INSTANCES_PER_ROW).map(move |x| {
let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT; let position = cgmath::Vector3 {
x: x as f32,
y: 0.0,
z: z as f32,
} - INSTANCE_DISPLACEMENT;
let rotation = if position.is_zero() { let rotation = if position.is_zero() {
// this is needed so an object at (0, 0, 0) won't get scaled to zero // this is needed so an object at (0, 0, 0) won't get scaled to zero
// as Quaternions can effect scale if they're not created correctly // as Quaternions can effect scale if they're not created correctly
cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0)) cgmath::Quaternion::from_axis_angle(
cgmath::Vector3::unit_z(),
cgmath::Deg(0.0),
)
} else { } else {
cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0)) cgmath::Quaternion::from_axis_angle(
position.clone().normalize(),
cgmath::Deg(45.0),
)
}; };
Instance { Instance { position, rotation }
position, rotation, })
}
}) })
}).collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -418,12 +436,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -432,21 +450,17 @@ impl State {
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -457,25 +471,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
format: texture::Texture::DEPTH_FORMAT, format: texture::Texture::DEPTH_FORMAT,
depth_write_enabled: true, depth_write_enabled: true,
@ -489,23 +499,18 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(VERTICES), contents: bytemuck::cast_slice(VERTICES),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"), label: Some("Index Buffer"),
contents: bytemuck::cast_slice(INDICES), contents: bytemuck::cast_slice(INDICES),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
let num_indices = INDICES.len() as u32; let num_indices = INDICES.len() as u32;
Self { Self {
@ -532,7 +537,6 @@ impl State {
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size; self.size = new_size;
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
@ -540,7 +544,8 @@ impl State {
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32; self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32;
// NEW! // NEW!
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
@ -550,37 +555,41 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view, attachment: &self.depth_texture.view,
depth_ops: Some(wgpu::Operations { depth_ops: Some(wgpu::Operations {
@ -605,9 +614,7 @@ impl State {
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
let window = WindowBuilder::new() let window = WindowBuilder::new().build(&event_loop).unwrap();
.build(&event_loop)
.unwrap();
use futures::executor::block_on; use futures::executor::block_on;
@ -619,22 +626,18 @@ fn main() {
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} if window_id == window.id() => if !state.input(event) { } if window_id == window.id() => {
if !state.input(event) {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput { input, .. } => match input {
input,
..
} => {
match input {
KeyboardInput { KeyboardInput {
state: ElementState::Pressed, state: ElementState::Pressed,
virtual_keycode: Some(VirtualKeyCode::Escape), virtual_keycode: Some(VirtualKeyCode::Escape),
.. ..
} => *control_flow = ControlFlow::Exit, } => *control_flow = ControlFlow::Exit,
_ => {} _ => {}
} },
}
WindowEvent::Resized(physical_size) => { WindowEvent::Resized(physical_size) => {
state.resize(*physical_size); state.resize(*physical_size);
} }
@ -645,6 +648,7 @@ fn main() {
_ => {} _ => {}
} }
} }
}
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
state.update(); state.update();
state.render(); state.render();

@ -1,7 +1,6 @@
use anyhow::*; use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -11,7 +10,11 @@ pub struct Texture {
impl Texture { impl Texture {
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -24,13 +27,11 @@ impl Texture {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
| wgpu::TextureUsage::SAMPLED,
}; };
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -41,17 +42,20 @@ impl Texture {
lod_min_clamp: -100.0, lod_min_clamp: -100.0,
lod_max_clamp: 100.0, lod_max_clamp: 100.0,
..Default::default() ..Default::default()
} });
);
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
pub fn from_bytes( pub fn from_bytes(
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bytes: &[u8], bytes: &[u8],
label: &str label: &str,
) -> Result<Self> { ) -> Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label)) Self::from_image(device, queue, &img, Some(label))
@ -61,7 +65,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
img: &image::DynamicImage, img: &image::DynamicImage,
label: Option<&str> label: Option<&str>,
) -> Result<Self> { ) -> Result<Self> {
let rgba = img.as_rgba8().unwrap(); let rgba = img.as_rgba8().unwrap();
let dimensions = img.dimensions(); let dimensions = img.dimensions();
@ -71,8 +75,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -80,8 +83,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -99,8 +101,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -108,9 +109,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -4,7 +4,7 @@ use fs_extra::dir::CopyOptions;
use glob::glob; use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -15,7 +15,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -29,7 +30,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -37,7 +43,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=src/*"); println!("cargo:rerun-if-changed=src/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
let mut shader_paths = [ let mut shader_paths = [
glob("./src/**/*.vert")?, glob("./src/**/*.vert")?,
@ -46,17 +51,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -69,7 +72,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,13 +1,12 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window}, window::Window,
}; };
use wgpu::util::DeviceExt;
mod model; mod model;
mod texture; mod texture;
@ -168,7 +167,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -210,20 +210,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -235,8 +239,8 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout = device.create_bind_group_layout( let texture_bind_group_layout =
&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -251,15 +255,12 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, -10.0).into(), eye: (0.0, 5.0, -10.0).into(),
@ -275,13 +276,11 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
const SPACE_BETWEEN: f32 = 3.0; const SPACE_BETWEEN: f32 = 3.0;
let instances = (0..NUM_INSTANCES_PER_ROW) let instances = (0..NUM_INSTANCES_PER_ROW)
@ -310,15 +309,14 @@ impl State {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -351,12 +349,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -368,26 +366,23 @@ impl State {
&queue, &queue,
&texture_bind_group_layout, &texture_bind_group_layout,
res_dir.join("cube.obj"), res_dir.join("cube.obj"),
).unwrap(); )
.unwrap();
let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv")); let vs_module = device.create_shader_module(wgpu::include_spirv!("shader.vert.spv"));
let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv")); let fs_module = device.create_shader_module(wgpu::include_spirv!("shader.frag.spv"));
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = device.create_pipeline_layout( let render_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"), label: Some("Render Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
&texture_bind_group_layout,
&uniform_bind_group_layout,
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
let render_pipeline = device.create_render_pipeline( let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout), layout: Some(&render_pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
@ -398,25 +393,21 @@ impl State {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
}), }),
rasterization_state: Some( rasterization_state: Some(wgpu::RasterizationStateDescriptor {
wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,
depth_bias: 0, depth_bias: 0,
depth_bias_slope_scale: 0.0, depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
clamp_depth: false, clamp_depth: false,
} }),
),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: sc_desc.format, format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
format: texture::Texture::DEPTH_FORMAT, format: texture::Texture::DEPTH_FORMAT,
depth_write_enabled: true, depth_write_enabled: true,
@ -430,8 +421,7 @@ impl State {
sample_count: 1, sample_count: 1,
sample_mask: !0, sample_mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
} });
);
Self { Self {
surface, surface,
@ -459,7 +449,8 @@ impl State {
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
self.sc_desc.height = new_size.height; self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
self.camera_controller.process_events(event) self.camera_controller.process_events(event)
@ -468,37 +459,41 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
} }],
],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view, attachment: &self.depth_texture.view,
depth_ops: Some(wgpu::Operations { depth_ops: Some(wgpu::Operations {

@ -75,13 +75,13 @@ impl Model {
let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?; let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?;
// We're assuming that the texture files are stored with the obj file // We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent() let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
.context("Directory has no parent")?;
let mut materials = Vec::new(); let mut materials = Vec::new();
for mat in obj_materials { for mat in obj_materials {
let diffuse_path = mat.diffuse_texture; let diffuse_path = mat.diffuse_texture;
let diffuse_texture = texture::Texture::load(device, queue, containing_folder.join(diffuse_path))?; let diffuse_texture =
texture::Texture::load(device, queue, containing_folder.join(diffuse_path))?;
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout, layout,
@ -124,20 +124,16 @@ impl Model {
}); });
} }
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())), label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", path.as_ref())), label: Some(&format!("{:?} Index Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&m.mesh.indices), contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
meshes.push(Mesh { meshes.push(Mesh {
name: m.name, name: m.name,

@ -2,7 +2,6 @@ use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
use std::path::Path; use std::path::Path;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -25,7 +24,11 @@ impl Texture {
Self::from_image(device, queue, &img, label) Self::from_image(device, queue, &img, label)
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -38,13 +41,11 @@ impl Texture {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
| wgpu::TextureUsage::SAMPLED,
}; };
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -55,10 +56,13 @@ impl Texture {
lod_min_clamp: -100.0, lod_min_clamp: -100.0,
lod_max_clamp: 100.0, lod_max_clamp: 100.0,
..Default::default() ..Default::default()
} });
);
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -66,7 +70,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bytes: &[u8], bytes: &[u8],
label: &str label: &str,
) -> Result<Self> { ) -> Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label)) Self::from_image(device, queue, &img, Some(label))
@ -76,7 +80,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
img: &image::DynamicImage, img: &image::DynamicImage,
label: Option<&str> label: Option<&str>,
) -> Result<Self> { ) -> Result<Self> {
let dimensions = img.dimensions(); let dimensions = img.dimensions();
let rgba = img.to_rgba(); let rgba = img.to_rgba();
@ -86,8 +90,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -95,8 +98,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -114,8 +116,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -123,9 +124,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -4,7 +4,7 @@ use fs_extra::dir::CopyOptions;
use glob::glob; use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -15,7 +15,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -29,7 +30,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -37,7 +43,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=src/*"); println!("cargo:rerun-if-changed=src/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
let mut shader_paths = [ let mut shader_paths = [
glob("./src/**/*.vert")?, glob("./src/**/*.vert")?,
@ -46,17 +51,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -69,7 +72,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,12 +1,12 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window}, window::Window,
}; };
use wgpu::util::DeviceExt;
mod model; mod model;
mod texture; mod texture;
@ -175,7 +175,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -287,20 +288,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -312,8 +317,8 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout = device.create_bind_group_layout( let texture_bind_group_layout =
&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -328,15 +333,12 @@ impl State {
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false,
},
count: None, count: None,
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, -10.0).into(), eye: (0.0, 5.0, -10.0).into(),
@ -353,13 +355,11 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
const SPACE_BETWEEN: f32 = 3.0; const SPACE_BETWEEN: f32 = 3.0;
let instances = (0..NUM_INSTANCES_PER_ROW) let instances = (0..NUM_INSTANCES_PER_ROW)
@ -388,15 +388,14 @@ impl State {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -429,12 +428,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -446,8 +445,8 @@ impl State {
&queue, &queue,
&texture_bind_group_layout, &texture_bind_group_layout,
res_dir.join("cube.obj"), res_dir.join("cube.obj"),
).unwrap(); )
.unwrap();
let light = Light { let light = Light {
position: (2.0, 2.0, 2.0).into(), position: (2.0, 2.0, 2.0).into(),
@ -455,13 +454,11 @@ impl State {
color: (1.0, 1.0, 1.0).into(), color: (1.0, 1.0, 1.0).into(),
}; };
let light_buffer = device.create_buffer_init( let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Light VB"), label: Some("Light VB"),
contents: bytemuck::cast_slice(&[light]), contents: bytemuck::cast_slice(&[light]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let light_bind_group_layout = let light_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -486,7 +483,8 @@ impl State {
label: None, label: None,
}); });
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -510,16 +508,11 @@ impl State {
); );
let light_render_pipeline = { let light_render_pipeline = {
let layout = device.create_pipeline_layout( let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
&wgpu::PipelineLayoutDescriptor {
label: Some("Light Pipeline Layout"), label: Some("Light Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&uniform_bind_group_layout, &light_bind_group_layout],
&uniform_bind_group_layout,
&light_bind_group_layout
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
create_render_pipeline( create_render_pipeline(
&device, &device,
@ -554,7 +547,6 @@ impl State {
light_bind_group, light_bind_group,
light_render_pipeline, light_render_pipeline,
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -563,7 +555,8 @@ impl State {
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
self.sc_desc.height = new_size.height; self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
@ -573,22 +566,31 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
// Update the light // Update the light
let old_position = self.light.position; let old_position = self.light.position;
self.light.position = self.light.position =
cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position; * old_position;
self.queue.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light])); self.queue
.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light]));
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
@ -598,16 +600,14 @@ impl State {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
}], }],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view, attachment: &self.depth_texture.view,
@ -637,7 +637,6 @@ impl State {
} }
} }
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();

@ -75,13 +75,13 @@ impl Model {
let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?; let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?;
// We're assuming that the texture files are stored with the obj file // We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent() let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
.context("Directory has no parent")?;
let mut materials = Vec::new(); let mut materials = Vec::new();
for mat in obj_materials { for mat in obj_materials {
let diffuse_path = mat.diffuse_texture; let diffuse_path = mat.diffuse_texture;
let diffuse_texture = texture::Texture::load(device, queue, containing_folder.join(diffuse_path))?; let diffuse_texture =
texture::Texture::load(device, queue, containing_folder.join(diffuse_path))?;
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout, layout,
@ -124,20 +124,16 @@ impl Model {
}); });
} }
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())), label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", path.as_ref())), label: Some(&format!("{:?} Index Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&m.mesh.indices), contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
meshes.push(Mesh { meshes.push(Mesh {
name: m.name, name: m.name,

@ -2,7 +2,6 @@ use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
use std::path::Path; use std::path::Path;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -25,7 +24,11 @@ impl Texture {
Self::from_image(device, queue, &img, label) Self::from_image(device, queue, &img, label)
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -38,13 +41,11 @@ impl Texture {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
| wgpu::TextureUsage::SAMPLED,
}; };
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -55,10 +56,13 @@ impl Texture {
lod_min_clamp: -100.0, lod_min_clamp: -100.0,
lod_max_clamp: 100.0, lod_max_clamp: 100.0,
..Default::default() ..Default::default()
} });
);
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -66,7 +70,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
bytes: &[u8], bytes: &[u8],
label: &str label: &str,
) -> Result<Self> { ) -> Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img, Some(label)) Self::from_image(device, queue, &img, Some(label))
@ -76,7 +80,7 @@ impl Texture {
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
img: &image::DynamicImage, img: &image::DynamicImage,
label: Option<&str> label: Option<&str>,
) -> Result<Self> { ) -> Result<Self> {
let dimensions = img.dimensions(); let dimensions = img.dimensions();
let rgba = img.to_rgba(); let rgba = img.to_rgba();
@ -86,8 +90,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -95,8 +98,7 @@ impl Texture {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -114,8 +116,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -123,9 +124,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -4,7 +4,7 @@ use fs_extra::dir::CopyOptions;
use glob::glob; use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -15,7 +15,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -29,7 +30,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -37,7 +43,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=src/*"); println!("cargo:rerun-if-changed=src/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
let mut shader_paths = [ let mut shader_paths = [
glob("./src/**/*.vert")?, glob("./src/**/*.vert")?,
@ -46,17 +51,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -69,7 +72,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,12 +1,12 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
event::*, event::*,
event_loop::{EventLoop, ControlFlow}, event_loop::{ControlFlow, EventLoop},
window::{Window}, window::Window,
}; };
use wgpu::util::DeviceExt;
mod model; mod model;
mod texture; mod texture;
@ -41,7 +41,6 @@ impl Camera {
} }
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Uniforms { struct Uniforms {
@ -173,7 +172,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -287,20 +287,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -312,8 +316,8 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout = device.create_bind_group_layout( let texture_bind_group_layout =
&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -350,8 +354,7 @@ impl State {
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
let camera = Camera { let camera = Camera {
eye: (0.0, 5.0, -10.0).into(), eye: (0.0, 5.0, -10.0).into(),
@ -368,13 +371,11 @@ impl State {
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera); uniforms.update_view_proj(&camera);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
const SPACE_BETWEEN: f32 = 3.0; const SPACE_BETWEEN: f32 = 3.0;
let instances = (0..NUM_INSTANCES_PER_ROW) let instances = (0..NUM_INSTANCES_PER_ROW)
@ -403,15 +404,14 @@ impl State {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -444,12 +444,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -461,7 +461,8 @@ impl State {
&queue, &queue,
&texture_bind_group_layout, &texture_bind_group_layout,
res_dir.join("cube.obj"), res_dir.join("cube.obj"),
).unwrap(); )
.unwrap();
let light = Light { let light = Light {
position: (2.0, 2.0, 2.0).into(), position: (2.0, 2.0, 2.0).into(),
@ -469,13 +470,11 @@ impl State {
color: (1.0, 1.0, 1.0).into(), color: (1.0, 1.0, 1.0).into(),
}; };
let light_buffer = device.create_buffer_init( let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Light VB"), label: Some("Light VB"),
contents: bytemuck::cast_slice(&[light]), contents: bytemuck::cast_slice(&[light]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let light_bind_group_layout = let light_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -500,7 +499,8 @@ impl State {
label: None, label: None,
}); });
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -524,16 +524,11 @@ impl State {
); );
let light_render_pipeline = { let light_render_pipeline = {
let layout = device.create_pipeline_layout( let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
&wgpu::PipelineLayoutDescriptor {
label: Some("Light Pipeline Layout"), label: Some("Light Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&uniform_bind_group_layout, &light_bind_group_layout],
&uniform_bind_group_layout,
&light_bind_group_layout
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
create_render_pipeline( create_render_pipeline(
&device, &device,
@ -550,10 +545,30 @@ impl State {
let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png"); let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png");
let normal_bytes = include_bytes!("../res/cobble-normal.png"); let normal_bytes = include_bytes!("../res/cobble-normal.png");
let diffuse_texture = texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "res/alt-diffuse.png", false).unwrap(); let diffuse_texture = texture::Texture::from_bytes(
let normal_texture = texture::Texture::from_bytes(&device, &queue, normal_bytes, "res/alt-normal.png", true).unwrap(); &device,
&queue,
diffuse_bytes,
"res/alt-diffuse.png",
false,
)
.unwrap();
let normal_texture = texture::Texture::from_bytes(
&device,
&queue,
normal_bytes,
"res/alt-normal.png",
true,
)
.unwrap();
model::Material::new(&device, "alt-material", diffuse_texture, normal_texture, &texture_bind_group_layout) model::Material::new(
&device,
"alt-material",
diffuse_texture,
normal_texture,
&texture_bind_group_layout,
)
}; };
Self { Self {
@ -580,7 +595,6 @@ impl State {
#[allow(dead_code)] #[allow(dead_code)]
debug_material, debug_material,
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -589,7 +603,8 @@ impl State {
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
self.sc_desc.height = new_size.height; self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
@ -599,22 +614,31 @@ impl State {
fn update(&mut self) { fn update(&mut self) {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
// Update the light // Update the light
let old_position = self.light.position; let old_position = self.light.position;
self.light.position = self.light.position =
cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position; * old_position;
self.queue.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light])); self.queue
.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light]));
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
@ -624,16 +648,14 @@ impl State {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
}], }],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view, attachment: &self.depth_texture.view,
@ -664,7 +686,6 @@ impl State {
} }
} }
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();

@ -130,16 +130,17 @@ impl Model {
let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?; let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?;
// We're assuming that the texture files are stored with the obj file // We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent() let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
.context("Directory has no parent")?;
let mut materials = Vec::new(); let mut materials = Vec::new();
for mat in obj_materials { for mat in obj_materials {
let diffuse_path = mat.diffuse_texture; let diffuse_path = mat.diffuse_texture;
let diffuse_texture = texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?; let diffuse_texture =
texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?;
let normal_path = mat.normal_texture; let normal_path = mat.normal_texture;
let normal_texture = texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?; let normal_texture =
texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?;
materials.push(Material::new( materials.push(Material::new(
device, device,
@ -159,16 +160,15 @@ impl Model {
m.mesh.positions[i * 3], m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1], m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2], m.mesh.positions[i * 3 + 2],
].into(), ]
tex_coords: [ .into(),
m.mesh.texcoords[i * 2], tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]].into(),
m.mesh.texcoords[i * 2 + 1]
].into(),
normal: [ normal: [
m.mesh.normals[i * 3], m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2], m.mesh.normals[i * 3 + 2],
].into(), ]
.into(),
// We'll calculate these later // We'll calculate these later
tangent: [0.0; 3].into(), tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(), bitangent: [0.0; 3].into(),
@ -208,7 +208,7 @@ impl Model {
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided // Luckily, the place I found this equation provided
// the solution! // the solution!
let r = 1.0 / (delta_uv1 .x * delta_uv2.y - delta_uv1.y * delta_uv2.x); let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r; let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
@ -222,20 +222,16 @@ impl Model {
vertices[c[2] as usize].bitangent = bitangent; vertices[c[2] as usize].bitangent = bitangent;
} }
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())), label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", path.as_ref())), label: Some(&format!("{:?} Index Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&m.mesh.indices), contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
meshes.push(Mesh { meshes.push(Mesh {
name: m.name, name: m.name,

@ -2,7 +2,6 @@ use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
use std::path::Path; use std::path::Path;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -26,7 +25,11 @@ impl Texture {
Self::from_image(device, queue, &img, label, is_normal_map) Self::from_image(device, queue, &img, label, is_normal_map)
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -39,13 +42,11 @@ impl Texture {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
| wgpu::TextureUsage::SAMPLED,
}; };
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -56,10 +57,13 @@ impl Texture {
lod_min_clamp: -100.0, lod_min_clamp: -100.0,
lod_max_clamp: 100.0, lod_max_clamp: 100.0,
..Default::default() ..Default::default()
} });
);
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -89,8 +93,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -102,8 +105,7 @@ impl Texture {
wgpu::TextureFormat::Rgba8UnormSrgb wgpu::TextureFormat::Rgba8UnormSrgb
}, },
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -121,8 +123,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -130,9 +131,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -4,7 +4,7 @@ use fs_extra::dir::CopyOptions;
use glob::glob; use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -15,7 +15,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -29,7 +30,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -37,7 +43,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=src/*"); println!("cargo:rerun-if-changed=src/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
let mut shader_paths = [ let mut shader_paths = [
glob("./src/**/*.vert")?, glob("./src/**/*.vert")?,
@ -46,17 +51,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -69,7 +72,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,8 +1,8 @@
use cgmath::*; use cgmath::*;
use winit::event::*;
use winit::dpi::LogicalPosition;
use std::time::Duration;
use std::f32::consts::FRAC_PI_2; use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::LogicalPosition;
use winit::event::*;
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -20,11 +20,7 @@ pub struct Camera {
} }
impl Camera { impl Camera {
pub fn new< pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
V: Into<Point3<f32>>,
Y: Into<Rad<f32>>,
P: Into<Rad<f32>>,
>(
position: V, position: V,
yaw: Y, yaw: Y,
pitch: P, pitch: P,
@ -39,11 +35,7 @@ impl Camera {
pub fn calc_matrix(&self) -> Matrix4<f32> { pub fn calc_matrix(&self) -> Matrix4<f32> {
Matrix4::look_at_dir( Matrix4::look_at_dir(
self.position, self.position,
Vector3::new( Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
self.yaw.0.cos(),
self.pitch.0.sin(),
self.yaw.0.sin(),
).normalize(),
Vector3::unit_y(), Vector3::unit_y(),
) )
} }
@ -57,13 +49,7 @@ pub struct Projection {
} }
impl Projection { impl Projection {
pub fn new<F: Into<Rad<f32>>>( pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
width: u32,
height: u32,
fovy: F,
znear: f32,
zfar: f32,
) -> Self {
Self { Self {
aspect: width as f32 / height as f32, aspect: width as f32 / height as f32,
fovy: fovy.into(), fovy: fovy.into(),
@ -113,8 +99,12 @@ impl CameraController {
} }
} }
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool{ pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 }; let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key { match key {
VirtualKeyCode::W | VirtualKeyCode::Up => { VirtualKeyCode::W | VirtualKeyCode::Up => {
self.amount_forward = amount; self.amount_forward = amount;
@ -153,10 +143,7 @@ impl CameraController {
self.scroll = match delta { self.scroll = match delta {
// I'm assuming a line is about 100 pixels // I'm assuming a line is about 100 pixels
MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0, MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
MouseScrollDelta::PixelDelta(LogicalPosition { MouseScrollDelta::PixelDelta(LogicalPosition { y: scroll, .. }) => *scroll as f32,
y: scroll,
..
}) => *scroll as f32,
}; };
} }
@ -175,7 +162,8 @@ impl CameraController {
// changes when zooming. I've added this to make it easier // changes when zooming. I've added this to make it easier
// to get closer to an object you want to focus on. // to get closer to an object you want to focus on.
let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos(); let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
let scrollward = Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize(); let scrollward =
Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt; camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
self.scroll = 0.0; self.scroll = 0.0;

@ -1,23 +1,22 @@
use std::iter; use std::iter;
use cgmath::prelude::*; use cgmath::prelude::*;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
event::*, event::*,
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
window::{Window}, window::Window,
}; };
use wgpu::util::DeviceExt;
mod camera;
mod model; mod model;
mod texture; mod texture; // NEW!
mod camera; // NEW!
use model::{DrawLight, DrawModel, Vertex}; use model::{DrawLight, DrawModel, Vertex};
const NUM_INSTANCES_PER_ROW: u32 = 10; const NUM_INSTANCES_PER_ROW: u32 = 10;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Uniforms { struct Uniforms {
@ -51,7 +50,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -169,21 +169,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -195,9 +198,8 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout =
let texture_bind_group_layout = device.create_bind_group_layout( device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -234,24 +236,22 @@ impl State {
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
// UPDATED! // UPDATED!
let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0)); let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0));
let projection = camera::Projection::new(sc_desc.width, sc_desc.height, cgmath::Deg(45.0), 0.1, 100.0); let projection =
camera::Projection::new(sc_desc.width, sc_desc.height, cgmath::Deg(45.0), 0.1, 100.0);
let camera_controller = camera::CameraController::new(4.0, 0.4); let camera_controller = camera::CameraController::new(4.0, 0.4);
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera, &projection); uniforms.update_view_proj(&camera, &projection);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
const SPACE_BETWEEN: f32 = 3.0; const SPACE_BETWEEN: f32 = 3.0;
let instances = (0..NUM_INSTANCES_PER_ROW) let instances = (0..NUM_INSTANCES_PER_ROW)
@ -280,15 +280,14 @@ impl State {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -321,12 +320,12 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
// NEW! // NEW!
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -339,7 +338,8 @@ impl State {
&queue, &queue,
&texture_bind_group_layout, &texture_bind_group_layout,
res_dir.join("cube.obj"), res_dir.join("cube.obj"),
).unwrap(); )
.unwrap();
println!("Elapsed (Original): {:?}", std::time::Instant::now() - now); println!("Elapsed (Original): {:?}", std::time::Instant::now() - now);
let light = Light { let light = Light {
@ -348,13 +348,11 @@ impl State {
color: (1.0, 1.0, 1.0).into(), color: (1.0, 1.0, 1.0).into(),
}; };
let light_buffer = device.create_buffer_init( let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Light VB"), label: Some("Light VB"),
contents: bytemuck::cast_slice(&[light]), contents: bytemuck::cast_slice(&[light]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let light_bind_group_layout = let light_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -379,7 +377,8 @@ impl State {
label: None, label: None,
}); });
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -403,16 +402,11 @@ impl State {
); );
let light_render_pipeline = { let light_render_pipeline = {
let layout = device.create_pipeline_layout( let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
&wgpu::PipelineLayoutDescriptor {
label: Some("Light Pipeline Layout"), label: Some("Light Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&uniform_bind_group_layout, &light_bind_group_layout],
&uniform_bind_group_layout,
&light_bind_group_layout
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
create_render_pipeline( create_render_pipeline(
&device, &device,
@ -429,10 +423,30 @@ impl State {
let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png"); let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png");
let normal_bytes = include_bytes!("../res/cobble-normal.png"); let normal_bytes = include_bytes!("../res/cobble-normal.png");
let diffuse_texture = texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "res/alt-diffuse.png", false).unwrap(); let diffuse_texture = texture::Texture::from_bytes(
let normal_texture = texture::Texture::from_bytes(&device, &queue, normal_bytes, "res/alt-normal.png", true).unwrap(); &device,
&queue,
diffuse_bytes,
"res/alt-diffuse.png",
false,
)
.unwrap();
let normal_texture = texture::Texture::from_bytes(
&device,
&queue,
normal_bytes,
"res/alt-normal.png",
true,
)
.unwrap();
model::Material::new(&device, "alt-material", diffuse_texture, normal_texture, &texture_bind_group_layout) model::Material::new(
&device,
"alt-material",
diffuse_texture,
normal_texture,
&texture_bind_group_layout,
)
}; };
Self { Self {
@ -463,7 +477,6 @@ impl State {
last_mouse_pos: (0.0, 0.0).into(), last_mouse_pos: (0.0, 0.0).into(),
mouse_pressed: false, mouse_pressed: false,
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -473,24 +486,23 @@ impl State {
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
self.sc_desc.height = new_size.height; self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
// UPDATED! // UPDATED!
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
virtual_keycode: Some(key), virtual_keycode: Some(key),
state, state,
.. ..
}, },
.. ..
} => self.camera_controller.process_keyboard(*key, *state), } => self.camera_controller.process_keyboard(*key, *state),
WindowEvent::MouseWheel { WindowEvent::MouseWheel { delta, .. } => {
delta,
..
} => {
self.camera_controller.process_scroll(delta); self.camera_controller.process_scroll(delta);
true true
} }
@ -502,10 +514,7 @@ impl State {
self.mouse_pressed = *state == ElementState::Pressed; self.mouse_pressed = *state == ElementState::Pressed;
true true
} }
WindowEvent::CursorMoved { WindowEvent::CursorMoved { position, .. } => {
position,
..
} => {
let mouse_dx = position.x - self.last_mouse_pos.x; let mouse_dx = position.x - self.last_mouse_pos.x;
let mouse_dy = position.y - self.last_mouse_pos.y; let mouse_dy = position.y - self.last_mouse_pos.y;
self.last_mouse_pos = *position; self.last_mouse_pos = *position;
@ -521,23 +530,33 @@ impl State {
fn update(&mut self, dt: std::time::Duration) { fn update(&mut self, dt: std::time::Duration) {
// UPDATED! // UPDATED!
self.camera_controller.update_camera(&mut self.camera, dt); self.camera_controller.update_camera(&mut self.camera, dt);
self.uniforms.update_view_proj(&self.camera, &self.projection); self.uniforms
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); .update_view_proj(&self.camera, &self.projection);
self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
// Update the light // Update the light
let old_position = self.light.position; let old_position = self.light.position;
self.light.position = self.light.position =
cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position; * old_position;
self.queue.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light])); self.queue
.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light]));
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
@ -547,16 +566,14 @@ impl State {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
}], }],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view, attachment: &self.depth_texture.view,
@ -587,7 +604,6 @@ impl State {
} }
} }
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();

@ -130,16 +130,17 @@ impl Model {
let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?; let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?;
// We're assuming that the texture files are stored with the obj file // We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent() let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
.context("Directory has no parent")?;
let mut materials = Vec::new(); let mut materials = Vec::new();
for mat in obj_materials { for mat in obj_materials {
let diffuse_path = mat.diffuse_texture; let diffuse_path = mat.diffuse_texture;
let diffuse_texture = texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?; let diffuse_texture =
texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?;
let normal_path = mat.normal_texture; let normal_path = mat.normal_texture;
let normal_texture = texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?; let normal_texture =
texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?;
materials.push(Material::new( materials.push(Material::new(
device, device,
@ -159,16 +160,15 @@ impl Model {
m.mesh.positions[i * 3], m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1], m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2], m.mesh.positions[i * 3 + 2],
].into(), ]
tex_coords: [ .into(),
m.mesh.texcoords[i * 2], tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]].into(),
m.mesh.texcoords[i * 2 + 1]
].into(),
normal: [ normal: [
m.mesh.normals[i * 3], m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2], m.mesh.normals[i * 3 + 2],
].into(), ]
.into(),
// We'll calculate these later // We'll calculate these later
tangent: [0.0; 3].into(), tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(), bitangent: [0.0; 3].into(),
@ -208,7 +208,7 @@ impl Model {
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided // Luckily, the place I found this equation provided
// the solution! // the solution!
let r = 1.0 / (delta_uv1 .x * delta_uv2.y - delta_uv1.y * delta_uv2.x); let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r; let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
@ -222,20 +222,16 @@ impl Model {
vertices[c[2] as usize].bitangent = bitangent; vertices[c[2] as usize].bitangent = bitangent;
} }
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())), label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", path.as_ref())), label: Some(&format!("{:?} Index Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&m.mesh.indices), contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
meshes.push(Mesh { meshes.push(Mesh {
name: m.name, name: m.name,

@ -2,7 +2,6 @@ use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
use std::path::Path; use std::path::Path;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -26,7 +25,11 @@ impl Texture {
Self::from_image(device, queue, &img, label, is_normal_map) Self::from_image(device, queue, &img, label, is_normal_map)
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -39,13 +42,11 @@ impl Texture {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
| wgpu::TextureUsage::SAMPLED,
}; };
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -56,10 +57,13 @@ impl Texture {
lod_min_clamp: -100.0, lod_min_clamp: -100.0,
lod_max_clamp: 100.0, lod_max_clamp: 100.0,
..Default::default() ..Default::default()
} });
);
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -89,8 +93,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -102,8 +105,7 @@ impl Texture {
wgpu::TextureFormat::Rgba8UnormSrgb wgpu::TextureFormat::Rgba8UnormSrgb
}, },
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -121,8 +123,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -130,9 +131,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -5,7 +5,7 @@ use glob::glob;
use rayon::prelude::*; use rayon::prelude::*;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -16,7 +16,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -30,7 +31,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -38,7 +44,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=src/*"); println!("cargo:rerun-if-changed=src/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
// UDPATED! // UDPATED!
let mut shader_paths = Vec::new(); let mut shader_paths = Vec::new();
@ -48,16 +53,14 @@ fn main() -> Result<()> {
// UPDATED! // UPDATED!
// This is parallelized // This is parallelized
let shaders = shader_paths.into_par_iter() let shaders = shader_paths
.map(|glob_result| { .into_par_iter()
ShaderData::load(glob_result?) .map(|glob_result| ShaderData::load(glob_result?))
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -70,7 +73,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,8 +1,8 @@
use cgmath::*; use cgmath::*;
use winit::event::*;
use winit::dpi::LogicalPosition;
use std::time::Duration;
use std::f32::consts::FRAC_PI_2; use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::LogicalPosition;
use winit::event::*;
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -20,11 +20,7 @@ pub struct Camera {
} }
impl Camera { impl Camera {
pub fn new< pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
V: Into<Point3<f32>>,
Y: Into<Rad<f32>>,
P: Into<Rad<f32>>,
>(
position: V, position: V,
yaw: Y, yaw: Y,
pitch: P, pitch: P,
@ -39,11 +35,7 @@ impl Camera {
pub fn calc_matrix(&self) -> Matrix4<f32> { pub fn calc_matrix(&self) -> Matrix4<f32> {
Matrix4::look_at_dir( Matrix4::look_at_dir(
self.position, self.position,
Vector3::new( Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
self.yaw.0.cos(),
self.pitch.0.sin(),
self.yaw.0.sin(),
).normalize(),
Vector3::unit_y(), Vector3::unit_y(),
) )
} }
@ -57,13 +49,7 @@ pub struct Projection {
} }
impl Projection { impl Projection {
pub fn new<F: Into<Rad<f32>>>( pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
width: u32,
height: u32,
fovy: F,
znear: f32,
zfar: f32,
) -> Self {
Self { Self {
aspect: width as f32 / height as f32, aspect: width as f32 / height as f32,
fovy: fovy.into(), fovy: fovy.into(),
@ -113,8 +99,12 @@ impl CameraController {
} }
} }
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool{ pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 }; let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key { match key {
VirtualKeyCode::W | VirtualKeyCode::Up => { VirtualKeyCode::W | VirtualKeyCode::Up => {
self.amount_forward = amount; self.amount_forward = amount;
@ -153,10 +143,7 @@ impl CameraController {
self.scroll = -match delta { self.scroll = -match delta {
// I'm assuming a line is about 100 pixels // I'm assuming a line is about 100 pixels
MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0, MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
MouseScrollDelta::PixelDelta(LogicalPosition { MouseScrollDelta::PixelDelta(LogicalPosition { y: scroll, .. }) => *scroll as f32,
y: scroll,
..
}) => *scroll as f32,
}; };
} }
@ -175,7 +162,8 @@ impl CameraController {
// changes when zooming. I've added this to make it easier // changes when zooming. I've added this to make it easier
// to get closer to an object you want to focus on. // to get closer to an object you want to focus on.
let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos(); let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
let scrollward = Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize(); let scrollward =
Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt; camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
self.scroll = 0.0; self.scroll = 0.0;

@ -1,23 +1,22 @@
use cgmath::prelude::*; use cgmath::prelude::*;
use rayon::prelude::*; use rayon::prelude::*;
use std::iter; use std::iter;
use wgpu::util::DeviceExt;
use winit::{ use winit::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
event::*, event::*,
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
window::{Window}, window::Window,
}; };
use wgpu::util::DeviceExt;
mod camera;
mod model; mod model;
mod texture; mod texture; // NEW!
mod camera; // NEW!
use model::{DrawLight, DrawModel, Vertex}; use model::{DrawLight, DrawModel, Vertex};
const NUM_INSTANCES_PER_ROW: u32 = 10; const NUM_INSTANCES_PER_ROW: u32 = 10;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Uniforms { struct Uniforms {
@ -51,7 +50,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -168,21 +168,24 @@ impl State {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -194,9 +197,8 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout =
let texture_bind_group_layout = device.create_bind_group_layout( device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -233,24 +235,22 @@ impl State {
}, },
], ],
label: Some("texture_bind_group_layout"), label: Some("texture_bind_group_layout"),
} });
);
// UPDATED! // UPDATED!
let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0)); let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0));
let projection = camera::Projection::new(sc_desc.width, sc_desc.height, cgmath::Deg(45.0), 0.1, 100.0); let projection =
camera::Projection::new(sc_desc.width, sc_desc.height, cgmath::Deg(45.0), 0.1, 100.0);
let camera_controller = camera::CameraController::new(4.0, 0.4); let camera_controller = camera::CameraController::new(4.0, 0.4);
let mut uniforms = Uniforms::new(); let mut uniforms = Uniforms::new();
uniforms.update_view_proj(&camera, &projection); uniforms.update_view_proj(&camera, &projection);
let uniform_buffer = device.create_buffer_init( let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[uniforms]), contents: bytemuck::cast_slice(&[uniforms]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
const SPACE_BETWEEN: f32 = 3.0; const SPACE_BETWEEN: f32 = 3.0;
let instances = (0..NUM_INSTANCES_PER_ROW) let instances = (0..NUM_INSTANCES_PER_ROW)
@ -281,15 +281,14 @@ impl State {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>(); let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
let instance_buffer = device.create_buffer_init( let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
contents: bytemuck::cast_slice(&instance_data), contents: bytemuck::cast_slice(&instance_data),
usage: wgpu::BufferUsage::STORAGE, usage: wgpu::BufferUsage::STORAGE,
} });
);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -319,11 +318,11 @@ impl State {
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}, },
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)) resource: wgpu::BindingResource::Buffer(instance_buffer.slice(..)),
}, },
], ],
label: Some("uniform_bind_group"), label: Some("uniform_bind_group"),
@ -335,7 +334,8 @@ impl State {
&queue, &queue,
&texture_bind_group_layout, &texture_bind_group_layout,
res_dir.join("cube.obj"), res_dir.join("cube.obj"),
).unwrap(); )
.unwrap();
let light = Light { let light = Light {
position: (2.0, 2.0, 2.0).into(), position: (2.0, 2.0, 2.0).into(),
@ -343,13 +343,11 @@ impl State {
color: (1.0, 1.0, 1.0).into(), color: (1.0, 1.0, 1.0).into(),
}; };
let light_buffer = device.create_buffer_init( let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some("Light VB"), label: Some("Light VB"),
contents: bytemuck::cast_slice(&[light]), contents: bytemuck::cast_slice(&[light]),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
} });
);
let light_bind_group_layout = let light_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -374,7 +372,8 @@ impl State {
label: None, label: None,
}); });
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -398,16 +397,11 @@ impl State {
); );
let light_render_pipeline = { let light_render_pipeline = {
let layout = device.create_pipeline_layout( let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
&wgpu::PipelineLayoutDescriptor {
label: Some("Light Pipeline Layout"), label: Some("Light Pipeline Layout"),
bind_group_layouts: &[ bind_group_layouts: &[&uniform_bind_group_layout, &light_bind_group_layout],
&uniform_bind_group_layout,
&light_bind_group_layout
],
push_constant_ranges: &[], push_constant_ranges: &[],
} });
);
create_render_pipeline( create_render_pipeline(
&device, &device,
@ -424,10 +418,30 @@ impl State {
let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png"); let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png");
let normal_bytes = include_bytes!("../res/cobble-normal.png"); let normal_bytes = include_bytes!("../res/cobble-normal.png");
let diffuse_texture = texture::Texture::from_bytes(&device, &queue, diffuse_bytes, "res/alt-diffuse.png", false).unwrap(); let diffuse_texture = texture::Texture::from_bytes(
let normal_texture = texture::Texture::from_bytes(&device, &queue, normal_bytes, "res/alt-normal.png", true).unwrap(); &device,
&queue,
diffuse_bytes,
"res/alt-diffuse.png",
false,
)
.unwrap();
let normal_texture = texture::Texture::from_bytes(
&device,
&queue,
normal_bytes,
"res/alt-normal.png",
true,
)
.unwrap();
model::Material::new(&device, "alt-material", diffuse_texture, normal_texture, &texture_bind_group_layout) model::Material::new(
&device,
"alt-material",
diffuse_texture,
normal_texture,
&texture_bind_group_layout,
)
}; };
Self { Self {
@ -457,7 +471,6 @@ impl State {
last_mouse_pos: (0.0, 0.0).into(), last_mouse_pos: (0.0, 0.0).into(),
mouse_pressed: false, mouse_pressed: false,
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -466,23 +479,22 @@ impl State {
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
self.sc_desc.height = new_size.height; self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
virtual_keycode: Some(key), virtual_keycode: Some(key),
state, state,
.. ..
}, },
.. ..
} => self.camera_controller.process_keyboard(*key, *state), } => self.camera_controller.process_keyboard(*key, *state),
WindowEvent::MouseWheel { WindowEvent::MouseWheel { delta, .. } => {
delta,
..
} => {
self.camera_controller.process_scroll(delta); self.camera_controller.process_scroll(delta);
true true
} }
@ -494,10 +506,7 @@ impl State {
self.mouse_pressed = *state == ElementState::Pressed; self.mouse_pressed = *state == ElementState::Pressed;
true true
} }
WindowEvent::CursorMoved { WindowEvent::CursorMoved { position, .. } => {
position,
..
} => {
let mouse_dx = position.x - self.last_mouse_pos.x; let mouse_dx = position.x - self.last_mouse_pos.x;
let mouse_dy = position.y - self.last_mouse_pos.y; let mouse_dy = position.y - self.last_mouse_pos.y;
self.last_mouse_pos = *position; self.last_mouse_pos = *position;
@ -512,23 +521,33 @@ impl State {
fn update(&mut self, dt: std::time::Duration) { fn update(&mut self, dt: std::time::Duration) {
self.camera_controller.update_camera(&mut self.camera, dt); self.camera_controller.update_camera(&mut self.camera, dt);
self.uniforms.update_view_proj(&self.camera, &self.projection); self.uniforms
self.queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[self.uniforms])); .update_view_proj(&self.camera, &self.projection);
self.queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[self.uniforms]),
);
// Update the light // Update the light
let old_position = self.light.position; let old_position = self.light.position;
self.light.position = self.light.position =
cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position; * old_position;
self.queue.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light])); self.queue
.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light]));
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_current_frame() let frame = self
.swap_chain
.get_current_frame()
.expect("Timeout getting texture") .expect("Timeout getting texture")
.output; .output;
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), label: Some("Render Encoder"),
}); });
@ -538,16 +557,14 @@ impl State {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: 0.1, r: 0.1,
g: 0.2, g: 0.2,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
} },
}], }],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view, attachment: &self.depth_texture.view,
@ -578,7 +595,6 @@ impl State {
} }
} }
fn main() { fn main() {
env_logger::init(); env_logger::init();
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();

@ -131,19 +131,23 @@ impl Model {
let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?; let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?;
// We're assuming that the texture files are stored with the obj file // We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent() let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
.context("Directory has no parent")?;
// UPDATED! // UPDATED!
let materials = obj_materials.par_iter().map(|mat| { let materials = obj_materials
.par_iter()
.map(|mat| {
// We can also parallelize loading the textures! // We can also parallelize loading the textures!
let mut textures = [ let mut textures = [
(containing_folder.join(&mat.diffuse_texture), false), (containing_folder.join(&mat.diffuse_texture), false),
(containing_folder.join(&mat.normal_texture), true), (containing_folder.join(&mat.normal_texture), true),
].par_iter().map(|(texture_path, is_normal_map)| { ]
.par_iter()
.map(|(texture_path, is_normal_map)| {
// //
texture::Texture::load(device, queue, texture_path, *is_normal_map) texture::Texture::load(device, queue, texture_path, *is_normal_map)
}).collect::<Result<Vec<_>>>()?; })
.collect::<Result<Vec<_>>>()?;
// Pop removes from the end of the list. // Pop removes from the end of the list.
let normal_texture = textures.pop().unwrap(); let normal_texture = textures.pop().unwrap();
@ -156,31 +160,37 @@ impl Model {
normal_texture, normal_texture,
layout, layout,
)) ))
}).collect::<Result<Vec<Material>>>()?; })
.collect::<Result<Vec<Material>>>()?;
// UPDATED! // UPDATED!
let meshes = obj_models.par_iter().map(|m| { let meshes = obj_models
let mut vertices = (0..m.mesh.positions.len() / 3).into_par_iter().map(|i| { .par_iter()
.map(|m| {
let mut vertices = (0..m.mesh.positions.len() / 3)
.into_par_iter()
.map(|i| {
ModelVertex { ModelVertex {
position: [ position: [
m.mesh.positions[i * 3], m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1], m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2], m.mesh.positions[i * 3 + 2],
].into(), ]
tex_coords: [ .into(),
m.mesh.texcoords[i * 2], tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]]
m.mesh.texcoords[i * 2 + 1] .into(),
].into(),
normal: [ normal: [
m.mesh.normals[i * 3], m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2], m.mesh.normals[i * 3 + 2],
].into(), ]
.into(),
// We'll calculate these later // We'll calculate these later
tangent: [0.0; 3].into(), tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(), bitangent: [0.0; 3].into(),
} }
}).collect::<Vec<_>>(); })
.collect::<Vec<_>>();
let indices = &m.mesh.indices; let indices = &m.mesh.indices;
@ -215,7 +225,7 @@ impl Model {
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided // Luckily, the place I found this equation provided
// the solution! // the solution!
let r = 1.0 / (delta_uv1 .x * delta_uv2.y - delta_uv1.y * delta_uv2.x); let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r; let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
@ -227,22 +237,18 @@ impl Model {
vertices[c[0] as usize].bitangent = bitangent; vertices[c[0] as usize].bitangent = bitangent;
vertices[c[1] as usize].bitangent = bitangent; vertices[c[1] as usize].bitangent = bitangent;
vertices[c[2] as usize].bitangent = bitangent; vertices[c[2] as usize].bitangent = bitangent;
}; }
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)), label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", m.name)), label: Some(&format!("{:?} Index Buffer", m.name)),
contents: bytemuck::cast_slice(&m.mesh.indices), contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
Ok(Mesh { Ok(Mesh {
name: m.name.clone(), name: m.name.clone(),
@ -251,7 +257,8 @@ impl Model {
num_elements: m.mesh.indices.len() as u32, num_elements: m.mesh.indices.len() as u32,
material: m.mesh.material_id.unwrap_or(0), material: m.mesh.material_id.unwrap_or(0),
}) })
}).collect::<Result<Vec<_>>>()?; })
.collect::<Result<Vec<_>>>()?;
Ok(Self { meshes, materials }) Ok(Self { meshes, materials })
} }

@ -2,7 +2,6 @@ use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
use std::path::Path; use std::path::Path;
pub struct Texture { pub struct Texture {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -26,7 +25,11 @@ impl Texture {
Self::from_image(device, queue, &img, label, is_normal_map) Self::from_image(device, queue, &img, label, is_normal_map)
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -39,13 +42,11 @@ impl Texture {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
| wgpu::TextureUsage::SAMPLED,
}; };
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -56,10 +57,13 @@ impl Texture {
lod_min_clamp: -100.0, lod_min_clamp: -100.0,
lod_max_clamp: 100.0, lod_max_clamp: 100.0,
..Default::default() ..Default::default()
} });
);
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -89,8 +93,7 @@ impl Texture {
height: dimensions.1, height: dimensions.1,
depth: 1, depth: 1,
}; };
let texture = device.create_texture( let texture = device.create_texture(&wgpu::TextureDescriptor {
&wgpu::TextureDescriptor {
label, label,
size, size,
mip_level_count: 1, mip_level_count: 1,
@ -102,8 +105,7 @@ impl Texture {
wgpu::TextureFormat::Rgba8UnormSrgb wgpu::TextureFormat::Rgba8UnormSrgb
}, },
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
} });
);
queue.write_texture( queue.write_texture(
wgpu::TextureCopyView { wgpu::TextureCopyView {
@ -121,8 +123,7 @@ impl Texture {
); );
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -130,9 +131,12 @@ impl Texture {
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
} });
);
Ok(Self { texture, view, sampler }) Ok(Self {
texture,
view,
sampler,
})
} }
} }

@ -1,10 +1,10 @@
use glob::glob;
use failure::bail; use failure::bail;
use fs_extra::copy_items; use fs_extra::copy_items;
use fs_extra::dir::CopyOptions; use fs_extra::dir::CopyOptions;
use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
fn main() { fn main() {
copy_res(); copy_res();
@ -35,11 +35,10 @@ fn compile_shaders() {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result.unwrap()).unwrap())
ShaderData::load(glob_result.unwrap()).unwrap()
})
.collect::<Vec<ShaderData>>(); .collect::<Vec<ShaderData>>();
let mut compiler = shaderc::Compiler::new().unwrap(); let mut compiler = shaderc::Compiler::new().unwrap();
@ -50,13 +49,15 @@ fn compile_shaders() {
// be better just to only compile shaders that have been changed // be better just to only compile shaders that have been changed
// recently. // recently.
for shader in shaders { for shader in shaders {
let compiled = compiler.compile_into_spirv( let compiled = compiler
.compile_into_spirv(
&shader.src, &shader.src,
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
).unwrap(); )
.unwrap();
write(shader.spv_path, compiled.as_binary_u8()).unwrap(); write(shader.spv_path, compiled.as_binary_u8()).unwrap();
} }
@ -83,6 +84,11 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }

@ -1,8 +1,8 @@
use cgmath::prelude::*; use cgmath::prelude::*;
use cgmath::*; use cgmath::*;
use winit::event::*;
use std::time::Duration;
use std::f32::consts::FRAC_PI_2; use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::event::*;
#[rustfmt::skip] #[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -20,11 +20,7 @@ pub struct Camera {
} }
impl Camera { impl Camera {
pub fn new< pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
V: Into<Point3<f32>>,
Y: Into<Rad<f32>>,
P: Into<Rad<f32>>,
>(
position: V, position: V,
yaw: Y, yaw: Y,
pitch: P, pitch: P,
@ -39,11 +35,7 @@ impl Camera {
pub fn calc_matrix(&self) -> Matrix4<f32> { pub fn calc_matrix(&self) -> Matrix4<f32> {
Matrix4::look_at_dir( Matrix4::look_at_dir(
self.position, self.position,
Vector3::new( Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
self.yaw.0.cos(),
self.pitch.0.sin(),
self.yaw.0.sin(),
).normalize(),
Vector3::unit_y(), Vector3::unit_y(),
) )
} }
@ -57,13 +49,7 @@ pub struct Projection {
} }
impl Projection { impl Projection {
pub fn new<F: Into<Rad<f32>>>( pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
width: u32,
height: u32,
fovy: F,
znear: f32,
zfar: f32,
) -> Self {
Self { Self {
aspect: width as f32 / height as f32, aspect: width as f32 / height as f32,
fovy: fovy.into(), fovy: fovy.into(),
@ -107,8 +93,12 @@ impl CameraController {
} }
} }
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool{ pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 }; let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key { match key {
VirtualKeyCode::W | VirtualKeyCode::Up => { VirtualKeyCode::W | VirtualKeyCode::Up => {
self.amount_forward = amount; self.amount_forward = amount;

@ -1,7 +1,6 @@
use cgmath::*; use cgmath::*;
use std::mem::size_of; use std::mem::size_of;
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Vertex { pub struct Vertex {
@ -43,16 +42,10 @@ impl Mesh {
vertex(0.0, 1000.0, 0.0), vertex(0.0, 1000.0, 0.0),
vertex(0.0, 0.0, 1000.0), vertex(0.0, 0.0, 1000.0),
]; ];
let axes_indices: &[u16] = &[ let axes_indices: &[u16] = &[0, 1, 0, 2, 0, 3];
0, 1,
0, 2,
0, 3,
];
let index_count = axes_indices.len() as u32; let index_count = axes_indices.len() as u32;
let index_buffer = device.create_buffer_with_data( let index_buffer = device
bytemuck::cast_slice(axes_indices), .create_buffer_with_data(bytemuck::cast_slice(axes_indices), wgpu::BufferUsage::INDEX);
wgpu::BufferUsage::INDEX,
);
let vertex_buffer = device.create_buffer_with_data( let vertex_buffer = device.create_buffer_with_data(
bytemuck::cast_slice(axes_vertices), bytemuck::cast_slice(axes_vertices),
wgpu::BufferUsage::VERTEX, wgpu::BufferUsage::VERTEX,

@ -1,8 +1,8 @@
use winit::window::Window;
use winit::event::*;
use winit::dpi::*;
use cgmath::*; use cgmath::*;
use std::time::Duration; use std::time::Duration;
use winit::dpi::*;
use winit::event::*;
use winit::window::Window;
use crate::camera::*; use crate::camera::*;
use crate::data::*; use crate::data::*;
@ -40,10 +40,11 @@ impl Demo {
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, },
wgpu::BackendBit::PRIMARY, wgpu::BackendBit::PRIMARY,
).await.unwrap(); )
let (device, queue): (wgpu::Device, wgpu::Queue) = adapter.request_device( .await
&Default::default(), .unwrap();
).await; let (device, queue): (wgpu::Device, wgpu::Queue) =
adapter.request_device(&Default::default()).await;
let inner_size = window.inner_size(); let inner_size = window.inner_size();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -55,14 +56,14 @@ impl Demo {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let camera = Camera::new((0.0, 0.5, 3.0), Deg(-90.0), Deg(0.0)); let camera = Camera::new((0.0, 0.5, 3.0), Deg(-90.0), Deg(0.0));
let controller = CameraController::new(0.5); let controller = CameraController::new(0.5);
let projection = Projection::new(inner_size.width, inner_size.height, Deg(45.0), 0.1, 1000.0); let projection =
Projection::new(inner_size.width, inner_size.height, Deg(45.0), 0.1, 1000.0);
let uniforms = Uniforms::new(&device, &camera, &projection); let uniforms = Uniforms::new(&device, &camera, &projection);
let (uniform_layout, uniforms_bind_group) = create_uniform_binding(&device, &uniforms); let (uniform_layout, uniforms_bind_group) = create_uniform_binding(&device, &uniforms);
let debug_pipeline_layout = device.create_pipeline_layout( let debug_pipeline_layout =
&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&uniform_layout] bind_group_layouts: &[&uniform_layout],
} });
);
let debug_pipeline = RenderPipelineBuilder::new() let debug_pipeline = RenderPipelineBuilder::new()
.layout(&debug_pipeline_layout) .layout(&debug_pipeline_layout)
.color_solid(sc_desc.format) .color_solid(sc_desc.format)
@ -72,7 +73,8 @@ impl Demo {
.index_format(wgpu::IndexFormat::Uint16) .index_format(wgpu::IndexFormat::Uint16)
.vertex_buffer(Vertex::desc()) .vertex_buffer(Vertex::desc())
.cull_mode(wgpu::CullMode::None) .cull_mode(wgpu::CullMode::None)
.build(&device).unwrap(); .build(&device)
.unwrap();
let axes = Mesh::axes(&device); let axes = Mesh::axes(&device);
Self { Self {
@ -84,7 +86,12 @@ impl Demo {
swap_chain, swap_chain,
debug_pipeline, debug_pipeline,
axes, axes,
clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0 }, clear_color: wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
},
is_running: true, is_running: true,
camera, camera,
controller, controller,
@ -107,7 +114,8 @@ impl Demo {
pub fn input(&mut self, event: &WindowEvent) -> bool { pub fn input(&mut self, event: &WindowEvent) -> bool {
match event { match event {
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
input: KeyboardInput { input:
KeyboardInput {
virtual_keycode: Some(key), virtual_keycode: Some(key),
state, state,
.. ..
@ -131,10 +139,7 @@ impl Demo {
self.mouse_pressed = *state == ElementState::Pressed; self.mouse_pressed = *state == ElementState::Pressed;
true true
} }
WindowEvent::CursorMoved { WindowEvent::CursorMoved { position, .. } => {
position,
..
} => {
let mouse_dx = position.x - self.last_mouse_pos.x; let mouse_dx = position.x - self.last_mouse_pos.x;
let mouse_dy = position.y - self.last_mouse_pos.y; let mouse_dy = position.y - self.last_mouse_pos.y;
self.last_mouse_pos = *position; self.last_mouse_pos = *position;
@ -160,27 +165,25 @@ impl Demo {
} }
pub fn render(&mut self) { pub fn render(&mut self) {
let mut encoder = self.device.create_command_encoder( let mut encoder = self
&wgpu::CommandEncoderDescriptor{ label: None }, .device
); .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
let frame = self.swap_chain.get_next_texture() let frame = self
.swap_chain
.get_next_texture()
.expect("Unable to retrieve swap chain texture"); .expect("Unable to retrieve swap chain texture");
{ {
let mut pass = encoder.begin_render_pass( let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view, attachment: &frame.view,
resolve_target: None, resolve_target: None,
load_op: wgpu::LoadOp::Clear, load_op: wgpu::LoadOp::Clear,
store_op: wgpu::StoreOp::Store, store_op: wgpu::StoreOp::Store,
clear_color: self.clear_color, clear_color: self.clear_color,
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
} });
);
pass.set_pipeline(&self.debug_pipeline); pass.set_pipeline(&self.debug_pipeline);
pass.set_bind_group(0, &self.uniforms_bind_group, &[]); pass.set_bind_group(0, &self.uniforms_bind_group, &[]);
pass.set_index_buffer(&self.axes.index_buffer, 0, 0); pass.set_index_buffer(&self.axes.index_buffer, 0, 0);

@ -6,10 +6,10 @@ mod resource;
use demo::*; use demo::*;
use winit::window::*;
use winit::event::*;
use winit::dpi::*; use winit::dpi::*;
use winit::event::*;
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::*;
use futures::executor::block_on; use futures::executor::block_on;
@ -21,7 +21,8 @@ fn main() {
let window = WindowBuilder::new() let window = WindowBuilder::new()
.with_inner_size(PhysicalSize::new(800, 600)) .with_inner_size(PhysicalSize::new(800, 600))
.with_title(env!("CARGO_PKG_NAME")) .with_title(env!("CARGO_PKG_NAME"))
.build(&event_loop).unwrap(); .build(&event_loop)
.unwrap();
let mut demo = block_on(Demo::new(&window)); let mut demo = block_on(Demo::new(&window));
let mut last_update = Instant::now(); let mut last_update = Instant::now();
let mut is_focused = false; let mut is_focused = false;
@ -34,31 +35,36 @@ fn main() {
ControlFlow::Exit ControlFlow::Exit
}; };
match event { match event {
Event::MainEventsCleared => if is_focused { Event::MainEventsCleared => {
if is_focused {
window.request_redraw(); window.request_redraw();
} }
}
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,
} => if window_id == window.id() && !demo.input(event) { } => {
if window_id == window.id() && !demo.input(event) {
match event { match event {
WindowEvent::Focused(f) => is_focused = *f, WindowEvent::Focused(f) => is_focused = *f,
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::ScaleFactorChanged { WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
new_inner_size, demo.resize(**new_inner_size)
.. }
} => demo.resize(**new_inner_size),
WindowEvent::Resized(new_size) => demo.resize(*new_size), WindowEvent::Resized(new_size) => demo.resize(*new_size),
_ => {} _ => {}
} }
} }
Event::RedrawRequested(window_id) => if window_id == window.id() { }
Event::RedrawRequested(window_id) => {
if window_id == window.id() {
let now = Instant::now(); let now = Instant::now();
let dt = now - last_update; let dt = now - last_update;
last_update = now; last_update = now;
demo.update(dt); demo.update(dt);
demo.render(); demo.render();
} }
}
_ => {} _ => {}
} }
}); });

@ -68,7 +68,6 @@ impl<'a> RenderPipelineBuilder<'a> {
self self
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn depth_bias(&mut self, db: i32) -> &mut Self { pub fn depth_bias(&mut self, db: i32) -> &mut Self {
self.depth_bias = db; self.depth_bias = db;
@ -100,14 +99,12 @@ impl<'a> RenderPipelineBuilder<'a> {
/// Helper method for [RenderPipelineBuilder::color_state] /// Helper method for [RenderPipelineBuilder::color_state]
pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self { pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self {
self.color_state( self.color_state(wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format, format,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
} })
)
} }
pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self { pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self {
@ -122,8 +119,7 @@ impl<'a> RenderPipelineBuilder<'a> {
depth_write_enabled: bool, depth_write_enabled: bool,
depth_compare: wgpu::CompareFunction, depth_compare: wgpu::CompareFunction,
) -> &mut Self { ) -> &mut Self {
self.depth_stencil_state( self.depth_stencil_state(wgpu::DepthStencilStateDescriptor {
wgpu::DepthStencilStateDescriptor {
format, format,
depth_write_enabled, depth_write_enabled,
depth_compare, depth_compare,
@ -131,8 +127,7 @@ impl<'a> RenderPipelineBuilder<'a> {
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_read_mask: 0, stencil_read_mask: 0,
stencil_write_mask: 0, stencil_write_mask: 0,
} })
)
} }
/// Helper method for [RenderPipelineBuilder::depth_no_stencil] /// Helper method for [RenderPipelineBuilder::depth_no_stencil]
@ -194,22 +189,21 @@ impl<'a> RenderPipelineBuilder<'a> {
// Having the shader be optional is giving me issues with // Having the shader be optional is giving me issues with
// the borrow checker so I'm going to use a default shader // the borrow checker so I'm going to use a default shader
// if the user doesn't supply one. // if the user doesn't supply one.
let fs_spv = self.fragment_shader.unwrap_or(include_bytes!("default.frag.spv")); let fs_spv = self
.fragment_shader
.unwrap_or(include_bytes!("default.frag.spv"));
let fs = create_shader_module(device, fs_spv); let fs = create_shader_module(device, fs_spv);
let pipeline = device.create_render_pipeline( let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
layout: &layout, layout: &layout,
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs, module: &vs,
entry_point: "main", entry_point: "main",
}, },
fragment_stage: Some( fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
wgpu::ProgrammableStageDescriptor {
module: &fs, module: &fs,
entry_point: "main", entry_point: "main",
} }),
),
rasterization_state: Some(wgpu::RasterizationStateDescriptor { rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: self.front_face, front_face: self.front_face,
cull_mode: self.cull_mode, cull_mode: self.cull_mode,
@ -227,19 +221,11 @@ impl<'a> RenderPipelineBuilder<'a> {
sample_count: self.sample_count, sample_count: self.sample_count,
sample_mask: self.sample_mask, sample_mask: self.sample_mask,
alpha_to_coverage_enabled: self.alpha_to_coverage_enabled, alpha_to_coverage_enabled: self.alpha_to_coverage_enabled,
} });
);
Ok(pipeline) Ok(pipeline)
} }
} }
fn create_shader_module(device: &wgpu::Device, spirv: &[u8]) -> wgpu::ShaderModule { fn create_shader_module(device: &wgpu::Device, spirv: &[u8]) -> wgpu::ShaderModule {
device.create_shader_module( device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(spirv)).unwrap())
&wgpu::read_spirv(
std::io::Cursor::new(
spirv
)
).unwrap()
)
} }

@ -22,11 +22,7 @@ unsafe impl bytemuck::Pod for UniformsRaw {}
unsafe impl bytemuck::Zeroable for UniformsRaw {} unsafe impl bytemuck::Zeroable for UniformsRaw {}
impl Uniforms { impl Uniforms {
pub fn new( pub fn new(device: &wgpu::Device, camera: &Camera, projection: &Projection) -> Self {
device: &wgpu::Device,
camera: &Camera,
projection: &Projection,
) -> Self {
let projection_matrix = projection.calc_matrix(); let projection_matrix = projection.calc_matrix();
let view_matrix = camera.calc_matrix(); let view_matrix = camera.calc_matrix();
let raw = UniformsRaw { let raw = UniformsRaw {
@ -56,10 +52,7 @@ impl Uniforms {
self.dirty = true; self.dirty = true;
} }
pub fn update( pub fn update(&mut self, device: &wgpu::Device) -> Option<wgpu::CommandBuffer> {
&mut self,
device: &wgpu::Device,
) -> Option<wgpu::CommandBuffer> {
if self.dirty { if self.dirty {
self.dirty = false; self.dirty = false;
self.raw.view_proj_matrix = self.raw.projection_matrix * self.raw.view_matrix; self.raw.view_proj_matrix = self.raw.projection_matrix * self.raw.view_matrix;
@ -69,11 +62,9 @@ impl Uniforms {
wgpu::BufferUsage::COPY_SRC, wgpu::BufferUsage::COPY_SRC,
); );
let mut encoder = device.create_command_encoder( let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
&wgpu::CommandEncoderDescriptor {
label: Some("Uniforms::update()"), label: Some("Uniforms::update()"),
} });
);
encoder.copy_buffer_to_buffer( encoder.copy_buffer_to_buffer(
&copy_buffer, &copy_buffer,
0, 0,
@ -88,33 +79,28 @@ impl Uniforms {
} }
} }
pub fn create_uniform_binding(device: &wgpu::Device, uniforms: &Uniforms) -> (wgpu::BindGroupLayout, wgpu::BindGroup) { pub fn create_uniform_binding(
let layout = device.create_bind_group_layout( device: &wgpu::Device,
&wgpu::BindGroupLayoutDescriptor { uniforms: &Uniforms,
) -> (wgpu::BindGroupLayout, wgpu::BindGroup) {
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Uniforms::BindGroupLayout"), label: Some("Uniforms::BindGroupLayout"),
bindings: &[ bindings: &[wgpu::BindGroupLayoutEntry {
wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { dynamic: false }, ty: wgpu::BindingType::UniformBuffer { dynamic: false },
} }],
], });
} let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
);
let bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
label: Some("Uniforms::BindGroup"), label: Some("Uniforms::BindGroup"),
layout: &layout, layout: &layout,
bindings: &[ bindings: &[wgpu::Binding {
wgpu::Binding {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer { resource: wgpu::BindingResource::Buffer {
buffer: &uniforms.buffer, buffer: &uniforms.buffer,
range: 0..size_of::<UniformsRaw>() as wgpu::BufferAddress, range: 0..size_of::<UniformsRaw>() as wgpu::BufferAddress,
} },
} }],
] });
}
);
(layout, bind_group) (layout, bind_group)
} }

@ -1,10 +1,10 @@
use glob::glob;
use failure::bail; use failure::bail;
use fs_extra::copy_items; use fs_extra::copy_items;
use fs_extra::dir::CopyOptions; use fs_extra::dir::CopyOptions;
use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
fn main() { fn main() {
copy_res(); copy_res();
@ -35,11 +35,10 @@ fn compile_shaders() {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result.unwrap()).unwrap())
ShaderData::load(glob_result.unwrap()).unwrap()
})
.collect::<Vec<ShaderData>>(); .collect::<Vec<ShaderData>>();
let mut compiler = shaderc::Compiler::new().unwrap(); let mut compiler = shaderc::Compiler::new().unwrap();
@ -50,13 +49,15 @@ fn compile_shaders() {
// be better just to only compile shaders that have been changed // be better just to only compile shaders that have been changed
// recently. // recently.
for shader in shaders { for shader in shaders {
let compiled = compiler.compile_into_spirv( let compiled = compiler
.compile_into_spirv(
&shader.src, &shader.src,
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
).unwrap(); )
.unwrap();
write(shader.spv_path, compiled.as_binary_u8()).unwrap(); write(shader.spv_path, compiled.as_binary_u8()).unwrap();
} }
@ -83,6 +84,11 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }

@ -1,15 +1,15 @@
use cgmath::prelude::*; use cgmath::prelude::*;
use futures::executor::block_on;
use std::path::Path;
use std::time::{Duration, Instant};
use winit::{ use winit::{
event::*, event::*,
event_loop::{ControlFlow, EventLoop}, event_loop::{ControlFlow, EventLoop},
window::Window, window::Window,
}; };
use futures::executor::block_on;
use std::path::Path;
use std::time::{Duration, Instant};
mod pipeline;
mod model; mod model;
mod pipeline;
mod texture; mod texture;
use model::{DrawLight, DrawModel, Vertex}; use model::{DrawLight, DrawModel, Vertex};
@ -42,7 +42,6 @@ impl Camera {
} }
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct Uniforms { struct Uniforms {
@ -174,7 +173,8 @@ struct Instance {
impl Instance { impl Instance {
fn to_raw(&self) -> InstanceRaw { fn to_raw(&self) -> InstanceRaw {
InstanceRaw { InstanceRaw {
model: cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation), model: cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation),
} }
} }
} }
@ -226,7 +226,6 @@ struct State {
debug_material: model::Material, debug_material: model::Material,
} }
impl State { impl State {
async fn new(window: &Window) -> Self { async fn new(window: &Window) -> Self {
let size = window.inner_size(); let size = window.inner_size();
@ -239,15 +238,18 @@ impl State {
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, },
wgpu::BackendBit::PRIMARY, // Vulkan + Metal + DX12 + Browser WebGPU wgpu::BackendBit::PRIMARY, // Vulkan + Metal + DX12 + Browser WebGPU
).await.unwrap(); )
.await
.unwrap();
let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions { extensions: wgpu::Extensions {
anisotropic_filtering: false, anisotropic_filtering: false,
}, },
limits: Default::default(), limits: Default::default(),
}).await; })
.await;
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -259,7 +261,8 @@ impl State {
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[ bindings: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -340,10 +343,7 @@ impl State {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let instance_data = instances let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
.iter()
.map(Instance::to_raw)
.collect::<Vec<_>>();
let instance_buffer_size = let instance_buffer_size =
instance_data.len() * std::mem::size_of::<cgmath::Matrix4<f32>>(); instance_data.len() * std::mem::size_of::<cgmath::Matrix4<f32>>();
@ -398,7 +398,8 @@ impl State {
&device, &device,
&texture_bind_group_layout, &texture_bind_group_layout,
res_dir.join("cube.obj"), res_dir.join("cube.obj"),
).unwrap(); )
.unwrap();
queue.submit(&cmds); queue.submit(&cmds);
@ -435,7 +436,8 @@ impl State {
label: None, label: None,
}); });
let depth_texture = texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture"); let depth_texture =
texture::Texture::create_depth_texture(&device, &sc_desc, "depth_texture");
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -453,7 +455,8 @@ impl State {
.vertex_buffer(model::ModelVertex::desc()) .vertex_buffer(model::ModelVertex::desc())
.vertex_shader(include_bytes!("shader.vert.spv")) .vertex_shader(include_bytes!("shader.vert.spv"))
.fragment_shader(include_bytes!("shader.frag.spv")) .fragment_shader(include_bytes!("shader.frag.spv"))
.build(&device).unwrap(); .build(&device)
.unwrap();
let light_render_pipeline = { let light_render_pipeline = {
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -467,7 +470,8 @@ impl State {
.vertex_buffer(model::ModelVertex::desc()) .vertex_buffer(model::ModelVertex::desc())
.vertex_shader(include_bytes!("light.vert.spv")) .vertex_shader(include_bytes!("light.vert.spv"))
.fragment_shader(include_bytes!("light.frag.spv")) .fragment_shader(include_bytes!("light.frag.spv"))
.build(&device).unwrap() .build(&device)
.unwrap()
}; };
let debug_material = { let debug_material = {
@ -475,13 +479,23 @@ impl State {
let normal_bytes = include_bytes!("../res/cobble-normal.png"); let normal_bytes = include_bytes!("../res/cobble-normal.png");
let mut command_buffers = vec![]; let mut command_buffers = vec![];
let (diffuse_texture, cmds) = texture::Texture::from_bytes(&device, diffuse_bytes, "res/alt-diffuse.png", false).unwrap(); let (diffuse_texture, cmds) =
texture::Texture::from_bytes(&device, diffuse_bytes, "res/alt-diffuse.png", false)
.unwrap();
command_buffers.push(cmds); command_buffers.push(cmds);
let (normal_texture, cmds) = texture::Texture::from_bytes(&device, normal_bytes, "res/alt-normal.png", true).unwrap(); let (normal_texture, cmds) =
texture::Texture::from_bytes(&device, normal_bytes, "res/alt-normal.png", true)
.unwrap();
command_buffers.push(cmds); command_buffers.push(cmds);
queue.submit(&command_buffers); queue.submit(&command_buffers);
model::Material::new(&device, "alt-material", diffuse_texture, normal_texture, &texture_bind_group_layout) model::Material::new(
&device,
"alt-material",
diffuse_texture,
normal_texture,
&texture_bind_group_layout,
)
}; };
Self { Self {
@ -508,7 +522,6 @@ impl State {
#[allow(dead_code)] #[allow(dead_code)]
debug_material, debug_material,
} }
} }
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) { fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
@ -517,7 +530,8 @@ impl State {
self.sc_desc.width = new_size.width; self.sc_desc.width = new_size.width;
self.sc_desc.height = new_size.height; self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture"); self.depth_texture =
texture::Texture::create_depth_texture(&self.device, &self.sc_desc, "depth_texture");
} }
fn input(&mut self, event: &WindowEvent) -> bool { fn input(&mut self, event: &WindowEvent) -> bool {
@ -528,8 +542,9 @@ impl State {
self.camera_controller.update_camera(&mut self.camera); self.camera_controller.update_camera(&mut self.camera);
self.uniforms.update_view_proj(&self.camera); self.uniforms.update_view_proj(&self.camera);
let mut encoder = let mut encoder = self
self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
let staging_buffer = self.device.create_buffer_with_data( let staging_buffer = self.device.create_buffer_with_data(
bytemuck::cast_slice(&[self.uniforms]), bytemuck::cast_slice(&[self.uniforms]),
@ -546,10 +561,9 @@ impl State {
// Update the light // Update the light
let old_position = self.light.position; let old_position = self.light.position;
self.light.position = self.light.position = cgmath::Quaternion::from_axis_angle(
cgmath::Quaternion::from_axis_angle(
(0.0, 1.0, 0.0).into(), (0.0, 1.0, 0.0).into(),
cgmath::Deg(45.0) * dt.as_secs_f32() cgmath::Deg(45.0) * dt.as_secs_f32(),
) * old_position; ) * old_position;
let staging_buffer = self.device.create_buffer_with_data( let staging_buffer = self.device.create_buffer_with_data(
@ -568,11 +582,13 @@ impl State {
} }
fn render(&mut self) { fn render(&mut self) {
let frame = self.swap_chain.get_next_texture() let frame = self
.swap_chain
.get_next_texture()
.expect("Timeout getting texture"); .expect("Timeout getting texture");
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
label: None .device
}); .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{ {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -637,9 +653,11 @@ fn main() {
ControlFlow::Wait ControlFlow::Wait
}; };
match event { match event {
Event::MainEventsCleared => if is_focused { Event::MainEventsCleared => {
if is_focused {
window.request_redraw(); window.request_redraw();
} }
}
Event::WindowEvent { Event::WindowEvent {
ref event, ref event,
window_id, window_id,

@ -135,14 +135,16 @@ impl Model {
let mut materials = Vec::new(); let mut materials = Vec::new();
for mat in obj_materials { for mat in obj_materials {
let diffuse_path = mat.diffuse_texture; let diffuse_path = mat.diffuse_texture;
let (diffuse_texture, cmds) = texture::Texture::load(device, containing_folder.join(diffuse_path), false)?; let (diffuse_texture, cmds) =
texture::Texture::load(device, containing_folder.join(diffuse_path), false)?;
command_buffers.push(cmds); command_buffers.push(cmds);
let normal_path = match mat.unknown_param.get("map_Bump") { let normal_path = match mat.unknown_param.get("map_Bump") {
Some(v) => Ok(v), Some(v) => Ok(v),
None => Err(failure::err_msg("Unable to find normal map")) None => Err(failure::err_msg("Unable to find normal map")),
}; };
let (normal_texture, cmds) = texture::Texture::load(device, containing_folder.join(normal_path?), true)?; let (normal_texture, cmds) =
texture::Texture::load(device, containing_folder.join(normal_path?), true)?;
command_buffers.push(cmds); command_buffers.push(cmds);
materials.push(Material::new( materials.push(Material::new(
@ -163,16 +165,15 @@ impl Model {
m.mesh.positions[i * 3], m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1], m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2], m.mesh.positions[i * 3 + 2],
].into(), ]
tex_coords: [ .into(),
m.mesh.texcoords[i * 2], tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]].into(),
m.mesh.texcoords[i * 2 + 1]
].into(),
normal: [ normal: [
m.mesh.normals[i * 3], m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2], m.mesh.normals[i * 3 + 2],
].into(), ]
.into(),
// We'll calculate these later // We'll calculate these later
tangent: [0.0; 3].into(), tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(), bitangent: [0.0; 3].into(),
@ -212,7 +213,7 @@ impl Model {
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided // Luckily, the place I found this equation provided
// the solution! // the solution!
let r = 1.0 / (delta_uv1 .x * delta_uv2.y - delta_uv1.y * delta_uv2.x); let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r; let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
@ -231,10 +232,8 @@ impl Model {
wgpu::BufferUsage::VERTEX, wgpu::BufferUsage::VERTEX,
); );
let index_buffer = device.create_buffer_with_data( let index_buffer = device
bytemuck::cast_slice(indices), .create_buffer_with_data(bytemuck::cast_slice(indices), wgpu::BufferUsage::INDEX);
wgpu::BufferUsage::INDEX,
);
meshes.push(Mesh { meshes.push(Mesh {
name: m.name, name: m.name,

@ -68,7 +68,6 @@ impl<'a> RenderPipelineBuilder<'a> {
self self
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn depth_bias(&mut self, db: i32) -> &mut Self { pub fn depth_bias(&mut self, db: i32) -> &mut Self {
self.depth_bias = db; self.depth_bias = db;
@ -100,14 +99,12 @@ impl<'a> RenderPipelineBuilder<'a> {
/// Helper method for [RenderPipelineBuilder::color_state] /// Helper method for [RenderPipelineBuilder::color_state]
pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self { pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self {
self.color_state( self.color_state(wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format, format,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
} })
)
} }
pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self { pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self {
@ -122,8 +119,7 @@ impl<'a> RenderPipelineBuilder<'a> {
depth_write_enabled: bool, depth_write_enabled: bool,
depth_compare: wgpu::CompareFunction, depth_compare: wgpu::CompareFunction,
) -> &mut Self { ) -> &mut Self {
self.depth_stencil_state( self.depth_stencil_state(wgpu::DepthStencilStateDescriptor {
wgpu::DepthStencilStateDescriptor {
format, format,
depth_write_enabled, depth_write_enabled,
depth_compare, depth_compare,
@ -131,8 +127,7 @@ impl<'a> RenderPipelineBuilder<'a> {
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_read_mask: 0, stencil_read_mask: 0,
stencil_write_mask: 0, stencil_write_mask: 0,
} })
)
} }
/// Helper method for [RenderPipelineBuilder::depth_no_stencil] /// Helper method for [RenderPipelineBuilder::depth_no_stencil]
@ -193,22 +188,21 @@ impl<'a> RenderPipelineBuilder<'a> {
// Having the shader be optional is giving me issues with // Having the shader be optional is giving me issues with
// the borrow checker so I'm going to use a default shader // the borrow checker so I'm going to use a default shader
// if the user doesn't supply one. // if the user doesn't supply one.
let fs_spv = self.fragment_shader.unwrap_or(include_bytes!("default.frag.spv")); let fs_spv = self
.fragment_shader
.unwrap_or(include_bytes!("default.frag.spv"));
let fs = create_shader_module(device, fs_spv); let fs = create_shader_module(device, fs_spv);
let pipeline = device.create_render_pipeline( let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
layout: &layout, layout: &layout,
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs, module: &vs,
entry_point: "main", entry_point: "main",
}, },
fragment_stage: Some( fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
wgpu::ProgrammableStageDescriptor {
module: &fs, module: &fs,
entry_point: "main", entry_point: "main",
} }),
),
rasterization_state: Some(wgpu::RasterizationStateDescriptor { rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: self.front_face, front_face: self.front_face,
cull_mode: self.cull_mode, cull_mode: self.cull_mode,
@ -226,19 +220,11 @@ impl<'a> RenderPipelineBuilder<'a> {
sample_count: self.sample_count, sample_count: self.sample_count,
sample_mask: self.sample_mask, sample_mask: self.sample_mask,
alpha_to_coverage_enabled: self.alpha_to_coverage_enabled, alpha_to_coverage_enabled: self.alpha_to_coverage_enabled,
} });
);
Ok(pipeline) Ok(pipeline)
} }
} }
fn create_shader_module(device: &wgpu::Device, spirv: &[u8]) -> wgpu::ShaderModule { fn create_shader_module(device: &wgpu::Device, spirv: &[u8]) -> wgpu::ShaderModule {
device.create_shader_module( device.create_shader_module(&wgpu::read_spirv(std::io::Cursor::new(spirv)).unwrap())
&wgpu::read_spirv(
std::io::Cursor::new(
spirv
)
).unwrap()
)
} }

@ -28,7 +28,11 @@ impl Texture {
Self::from_image(device, &img, label, is_normal_map) Self::from_image(device, &img, label, is_normal_map)
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, label: &str) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
label: &str,
) -> Self {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@ -61,7 +65,11 @@ impl Texture {
compare: wgpu::CompareFunction::LessEqual, compare: wgpu::CompareFunction::LessEqual,
}); });
Self { texture, view, sampler } Self {
texture,
view,
sampler,
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -119,16 +127,11 @@ impl Texture {
}; };
let texture = device.create_texture(&texture_desc); let texture = device.create_texture(&texture_desc);
let buffer = device.create_buffer_with_data( let buffer = device.create_buffer_with_data(&rgba, wgpu::BufferUsage::COPY_SRC);
&rgba,
wgpu::BufferUsage::COPY_SRC,
);
let mut encoder = device.create_command_encoder( let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
&wgpu::CommandEncoderDescriptor {
label: Some("texture_buffer_copy_encoder"), label: Some("texture_buffer_copy_encoder"),
} });
);
encoder.copy_buffer_to_texture( encoder.copy_buffer_to_texture(
wgpu::BufferCopyView { wgpu::BufferCopyView {
@ -153,7 +156,7 @@ impl Texture {
&device, &device,
&texture, &texture,
&texture_desc, &texture_desc,
mip_level_count mip_level_count,
); );
let cmd_buffer = encoder.finish(); let cmd_buffer = encoder.finish();
@ -171,7 +174,14 @@ impl Texture {
compare: wgpu::CompareFunction::LessEqual, compare: wgpu::CompareFunction::LessEqual,
}); });
Ok((Self { texture, view, sampler }, cmd_buffer)) Ok((
Self {
texture,
view,
sampler,
},
cmd_buffer,
))
} }
pub fn generate_mipmaps( pub fn generate_mipmaps(
@ -181,8 +191,7 @@ impl Texture {
texture_desc: &wgpu::TextureDescriptor, texture_desc: &wgpu::TextureDescriptor,
mip_count: u32, mip_count: u32,
) { ) {
let bind_group_layout = device.create_bind_group_layout( let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
&wgpu::BindGroupLayoutDescriptor {
bindings: &[ bindings: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
@ -191,24 +200,19 @@ impl Texture {
multisampled: false, multisampled: false,
component_type: wgpu::TextureComponentType::Float, component_type: wgpu::TextureComponentType::Float,
dimension: wgpu::TextureViewDimension::D2, dimension: wgpu::TextureViewDimension::D2,
} },
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler { comparison: false },
comparison: false, },
}
}
], ],
label: None, label: None,
} });
); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
let pipeline_layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&bind_group_layout], bind_group_layouts: &[&bind_group_layout],
}, });
);
// This pipeline will render out a texture to another texture. // This pipeline will render out a texture to another texture.
// We create the mipmaps by rendering to increasingly smaller // We create the mipmaps by rendering to increasingly smaller
@ -220,12 +224,12 @@ impl Texture {
.fragment_shader(include_bytes!("blit.frag.spv")) .fragment_shader(include_bytes!("blit.frag.spv"))
// Using wgpu::TriangleStrip makes our lives easier in the shader. // Using wgpu::TriangleStrip makes our lives easier in the shader.
.primitive_topology(wgpu::PrimitiveTopology::TriangleStrip) .primitive_topology(wgpu::PrimitiveTopology::TriangleStrip)
.build(device).unwrap(); .build(device)
.unwrap();
// This sampler ensures that the smaller textures get the right // This sampler ensures that the smaller textures get the right
// color data. // color data.
let sampler = device.create_sampler( let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
@ -237,13 +241,12 @@ impl Texture {
lod_min_clamp: 0.0, lod_min_clamp: 0.0,
lod_max_clamp: 0.0, lod_max_clamp: 0.0,
compare: wgpu::CompareFunction::Always, compare: wgpu::CompareFunction::Always,
} });
);
// Create a view for every mip level. // Create a view for every mip level.
let views = (0..mip_count).map(|mip| { let views = (0..mip_count)
texture.create_view( .map(|mip| {
&wgpu::TextureViewDescriptor { texture.create_view(&wgpu::TextureViewDescriptor {
format: texture_desc.format, format: texture_desc.format,
dimension: wgpu::TextureViewDimension::D2, dimension: wgpu::TextureViewDimension::D2,
aspect: wgpu::TextureAspect::All, aspect: wgpu::TextureAspect::All,
@ -251,14 +254,13 @@ impl Texture {
level_count: 1, level_count: 1,
base_array_layer: 0, base_array_layer: 0,
array_layer_count: 1, array_layer_count: 1,
} })
) })
}).collect::<Vec<_>>(); .collect::<Vec<_>>();
// Skip the first view, as that is the base one // Skip the first view, as that is the base one
for target_mip in 1..mip_count as usize { for target_mip in 1..mip_count as usize {
let bind_group = device.create_bind_group( let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &bind_group_layout, layout: &bind_group_layout,
bindings: &[ bindings: &[
wgpu::Binding { wgpu::Binding {
@ -269,26 +271,21 @@ impl Texture {
wgpu::Binding { wgpu::Binding {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler), resource: wgpu::BindingResource::Sampler(&sampler),
} },
], ],
label: None, label: None,
}, });
);
let mut pass = encoder.begin_render_pass( let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &views[target_mip], attachment: &views[target_mip],
resolve_target: None, resolve_target: None,
clear_color: wgpu::Color::WHITE, clear_color: wgpu::Color::WHITE,
load_op: wgpu::LoadOp::Clear, load_op: wgpu::LoadOp::Clear,
store_op: wgpu::StoreOp::Store, store_op: wgpu::StoreOp::Store,
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}, });
);
pass.set_pipeline(&blit_pipeline); pass.set_pipeline(&blit_pipeline);
pass.set_bind_group(0, &bind_group, &[]); pass.set_bind_group(0, &bind_group, &[]);

@ -1,5 +1,5 @@
use std::mem; use std::mem;
use wgpu::util::{DeviceExt, BufferInitDescriptor}; use wgpu::util::{BufferInitDescriptor, DeviceExt};
pub trait ToRaw { pub trait ToRaw {
type Output; type Output;
@ -7,28 +7,29 @@ pub trait ToRaw {
} }
pub struct RawBuffer<R> pub struct RawBuffer<R>
where R: Copy + bytemuck::Pod + bytemuck::Zeroable where
R: Copy + bytemuck::Pod + bytemuck::Zeroable,
{ {
pub buffer: wgpu::Buffer, pub buffer: wgpu::Buffer,
pub data: Vec<R>, pub data: Vec<R>,
} }
impl<R: Copy + bytemuck::Pod + bytemuck::Zeroable> RawBuffer<R> { impl<R: Copy + bytemuck::Pod + bytemuck::Zeroable> RawBuffer<R> {
pub fn from_slice<T: ToRaw<Output = R>>(
pub fn from_slice<T: ToRaw<Output=R>>(device: &wgpu::Device, data: &[T], usage: wgpu::BufferUsage) -> Self { device: &wgpu::Device,
data: &[T],
usage: wgpu::BufferUsage,
) -> Self {
let raw_data = data.iter().map(ToRaw::to_raw).collect::<Vec<R>>(); let raw_data = data.iter().map(ToRaw::to_raw).collect::<Vec<R>>();
Self::from_vec(device, raw_data, usage) Self::from_vec(device, raw_data, usage)
} }
pub fn from_vec(device: &wgpu::Device, data: Vec<R>, usage: wgpu::BufferUsage) -> Self { pub fn from_vec(device: &wgpu::Device, data: Vec<R>, usage: wgpu::BufferUsage) -> Self {
let buffer = device.create_buffer_init( let buffer = device.create_buffer_init(&BufferInitDescriptor {
&BufferInitDescriptor {
contents: bytemuck::cast_slice(&data), contents: bytemuck::cast_slice(&data),
usage, usage,
label: None, label: None,
} });
);
Self::from_parts(buffer, data, usage) Self::from_parts(buffer, data, usage)
} }
@ -41,13 +42,13 @@ impl<R: Copy + bytemuck::Pod + bytemuck::Zeroable> RawBuffer<R> {
} }
} }
pub struct Buffer<U: ToRaw<Output=R>, R: Copy + bytemuck::Pod + bytemuck::Zeroable> { pub struct Buffer<U: ToRaw<Output = R>, R: Copy + bytemuck::Pod + bytemuck::Zeroable> {
pub data: Vec<U>, pub data: Vec<U>,
pub raw_buffer: RawBuffer<R>, pub raw_buffer: RawBuffer<R>,
pub usage: wgpu::BufferUsage, pub usage: wgpu::BufferUsage,
} }
impl<U: ToRaw<Output=R>, R: Copy + bytemuck::Pod + bytemuck::Zeroable> Buffer<U, R> { impl<U: ToRaw<Output = R>, R: Copy + bytemuck::Pod + bytemuck::Zeroable> Buffer<U, R> {
pub fn uniform(device: &wgpu::Device, datum: U) -> Self { pub fn uniform(device: &wgpu::Device, datum: U) -> Self {
let data = vec![datum]; let data = vec![datum];
let usage = wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST; let usage = wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST;
@ -61,7 +62,9 @@ impl<U: ToRaw<Output=R>, R: Copy + bytemuck::Pod + bytemuck::Zeroable> Buffer<U,
pub fn staging(device: &wgpu::Device, other: &Self) -> Self { pub fn staging(device: &wgpu::Device, other: &Self) -> Self {
let buffer_size = other.raw_buffer.buffer_size(); let buffer_size = other.raw_buffer.buffer_size();
let usage = wgpu::BufferUsage::COPY_SRC | wgpu::BufferUsage::MAP_READ | wgpu::BufferUsage::MAP_WRITE; let usage = wgpu::BufferUsage::COPY_SRC
| wgpu::BufferUsage::MAP_READ
| wgpu::BufferUsage::MAP_WRITE;
let buffer = device.create_buffer(&wgpu::BufferDescriptor { let buffer = device.create_buffer(&wgpu::BufferDescriptor {
size: buffer_size, size: buffer_size,
usage, usage,
@ -78,6 +81,10 @@ impl<U: ToRaw<Output=R>, R: Copy + bytemuck::Pod + bytemuck::Zeroable> Buffer<U,
} }
pub fn from_parts(data: Vec<U>, raw_buffer: RawBuffer<R>, usage: wgpu::BufferUsage) -> Self { pub fn from_parts(data: Vec<U>, raw_buffer: RawBuffer<R>, usage: wgpu::BufferUsage) -> Self {
Self { data, raw_buffer, usage } Self {
data,
raw_buffer,
usage,
}
} }
} }

@ -1,8 +1,8 @@
use cgmath::*; use cgmath::*;
use winit::event::*;
use winit::dpi::LogicalPosition;
use std::time::Duration;
use std::f32::consts::FRAC_PI_2; use std::f32::consts::FRAC_PI_2;
use std::time::Duration;
use winit::dpi::LogicalPosition;
use winit::event::*;
#[cfg_attr(rustfmt, rustfmt_skip)] #[cfg_attr(rustfmt, rustfmt_skip)]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new( pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
@ -12,11 +12,7 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
0.0, 0.0, 0.5, 1.0, 0.0, 0.0, 0.5, 1.0,
); );
pub fn camera_setup< pub fn camera_setup<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
V: Into<Point3<f32>>,
Y: Into<Rad<f32>>,
P: Into<Rad<f32>>,
>(
position: V, position: V,
yaw: Y, yaw: Y,
pitch: P, pitch: P,
@ -38,11 +34,7 @@ pub struct Camera {
} }
impl Camera { impl Camera {
pub fn new< pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
V: Into<Point3<f32>>,
Y: Into<Rad<f32>>,
P: Into<Rad<f32>>,
>(
position: V, position: V,
yaw: Y, yaw: Y,
pitch: P, pitch: P,
@ -57,11 +49,7 @@ impl Camera {
pub fn calc_matrix(&self) -> Matrix4<f32> { pub fn calc_matrix(&self) -> Matrix4<f32> {
Matrix4::look_at_dir( Matrix4::look_at_dir(
self.position, self.position,
Vector3::new( Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
self.yaw.0.cos(),
self.pitch.0.sin(),
self.yaw.0.sin(),
).normalize(),
Vector3::unit_y(), Vector3::unit_y(),
) )
} }
@ -75,13 +63,7 @@ pub struct Projection {
} }
impl Projection { impl Projection {
pub fn new<F: Into<Rad<f32>>>( pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
width: u32,
height: u32,
fovy: F,
znear: f32,
zfar: f32,
) -> Self {
Self { Self {
aspect: width as f32 / height as f32, aspect: width as f32 / height as f32,
fovy: fovy.into(), fovy: fovy.into(),
@ -131,8 +113,12 @@ impl CameraController {
} }
} }
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool{ pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 }; let amount = if state == ElementState::Pressed {
1.0
} else {
0.0
};
match key { match key {
VirtualKeyCode::W | VirtualKeyCode::Up => { VirtualKeyCode::W | VirtualKeyCode::Up => {
self.amount_forward = amount; self.amount_forward = amount;
@ -171,10 +157,7 @@ impl CameraController {
self.scroll = -match delta { self.scroll = -match delta {
// I'm assuming a line is about 100 pixels // I'm assuming a line is about 100 pixels
MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0, MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
MouseScrollDelta::PixelDelta(LogicalPosition { MouseScrollDelta::PixelDelta(LogicalPosition { y: scroll, .. }) => *scroll as f32,
y: scroll,
..
}) => *scroll as f32,
}; };
} }
@ -193,7 +176,8 @@ impl CameraController {
// changes when zooming. I've added this to make it easier // changes when zooming. I've added this to make it easier
// to get closer to an object you want to focus on. // to get closer to an object you want to focus on.
let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos(); let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
let scrollward = Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize(); let scrollward =
Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt; camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
// Move up/down. Since we don't use roll, we can just // Move up/down. Since we don't use roll, we can just

@ -3,8 +3,8 @@ mod camera;
mod light; mod light;
mod model; mod model;
mod pipeline; mod pipeline;
mod texture;
pub mod prelude; pub mod prelude;
mod texture;
pub use buffer::*; pub use buffer::*;
pub use camera::*; pub use camera::*;
@ -34,20 +34,24 @@ impl Display {
let size = window.inner_size(); let size = window.inner_size();
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, None,
).await.unwrap(); )
.await
.unwrap();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb, format: wgpu::TextureFormat::Bgra8UnormSrgb,
@ -73,7 +77,6 @@ impl Display {
} }
} }
/** /**
* Holds the camera data to be passed to wgpu. * Holds the camera data to be passed to wgpu.
*/ */
@ -98,13 +101,11 @@ impl Uniforms {
view_position: Zero::zero(), view_position: Zero::zero(),
view_proj: cgmath::Matrix4::identity(), view_proj: cgmath::Matrix4::identity(),
}; };
let buffer = device.create_buffer_init( let buffer = device.create_buffer_init(&BufferInitDescriptor {
&BufferInitDescriptor {
label: Some("Uniform Buffer"), label: Some("Uniform Buffer"),
contents: bytemuck::cast_slice(&[data]), contents: bytemuck::cast_slice(&[data]),
usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
} });
);
Self { data, buffer } Self { data, buffer }
} }
@ -115,13 +116,11 @@ impl Uniforms {
} }
pub fn update_buffer(&self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) { pub fn update_buffer(&self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
let staging_buffer = device.create_buffer_init( let staging_buffer = device.create_buffer_init(&BufferInitDescriptor {
&BufferInitDescriptor {
label: Some("Uniform Update Buffer"), label: Some("Uniform Update Buffer"),
contents: bytemuck::cast_slice(&[self.data]), contents: bytemuck::cast_slice(&[self.data]),
usage: wgpu::BufferUsage::COPY_SRC, usage: wgpu::BufferUsage::COPY_SRC,
} });
);
encoder.copy_buffer_to_buffer( encoder.copy_buffer_to_buffer(
&staging_buffer, &staging_buffer,
0, 0,
@ -143,10 +142,8 @@ pub struct UniformBinding {
impl UniformBinding { impl UniformBinding {
pub fn new(device: &wgpu::Device, uniforms: &Uniforms) -> Self { pub fn new(device: &wgpu::Device, uniforms: &Uniforms) -> Self {
let layout = device.create_bind_group_layout( let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
&wgpu::BindGroupLayoutDescriptor { entries: &[wgpu::BindGroupLayoutEntry {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer { ty: wgpu::BindingType::UniformBuffer {
@ -154,40 +151,30 @@ impl UniformBinding {
min_binding_size: None, min_binding_size: None,
}, },
count: None, count: None,
}, }],
],
label: Some("UniformBinding::layout"), label: Some("UniformBinding::layout"),
} });
); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
let bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout: &layout, layout: &layout,
entries: &[ entries: &[wgpu::BindGroupEntry {
wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniforms.buffer.slice(..)), resource: wgpu::BindingResource::Buffer(uniforms.buffer.slice(..)),
}, }],
], label: Some("UniformBinding::bind_group"),
label: Some("UniformBinding::bind_group") });
}
);
Self { layout, bind_group } Self { layout, bind_group }
} }
pub fn rebind(&mut self, device: &wgpu::Device, uniforms: &Uniforms) { pub fn rebind(&mut self, device: &wgpu::Device, uniforms: &Uniforms) {
self.bind_group = device.create_bind_group( self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
&wgpu::BindGroupDescriptor {
layout: &self.layout, layout: &self.layout,
entries: &[ entries: &[wgpu::BindGroupEntry {
wgpu::BindGroupEntry {
binding: 0, binding: 0,
resource: wgpu::BindingResource::Buffer(uniforms.buffer.slice(..)) resource: wgpu::BindingResource::Buffer(uniforms.buffer.slice(..)),
}, }],
], label: Some("UniformBinding::bind_group"),
label: Some("UniformBinding::bind_group") });
}
);
} }
} }
@ -220,7 +207,8 @@ pub async fn run<D: Demo>() -> Result<(), Error> {
match event { match event {
Event::Resumed => is_resumed = true, Event::Resumed => is_resumed = true,
Event::Suspended => is_resumed = false, Event::Suspended => is_resumed = false,
Event::RedrawRequested(wid) => if wid == window.id() { Event::RedrawRequested(wid) => {
if wid == window.id() {
let now = Instant::now(); let now = Instant::now();
let dt = now - last_update; let dt = now - last_update;
last_update = now; last_update = now;
@ -228,6 +216,7 @@ pub async fn run<D: Demo>() -> Result<(), Error> {
demo.update(&mut display, dt); demo.update(&mut display, dt);
demo.render(&mut display); demo.render(&mut display);
} }
}
Event::MainEventsCleared => { Event::MainEventsCleared => {
if is_focused && is_resumed { if is_focused && is_resumed {
window.request_redraw(); window.request_redraw();
@ -237,17 +226,13 @@ pub async fn run<D: Demo>() -> Result<(), Error> {
} }
} }
Event::WindowEvent { Event::WindowEvent {
event, event, window_id, ..
window_id, } => {
.. if window_id == window.id() {
} => if window_id == window.id() {
match event { match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Focused(f) => is_focused = f, WindowEvent::Focused(f) => is_focused = f,
WindowEvent::ScaleFactorChanged { WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
new_inner_size,
..
} => {
display.resize(new_inner_size.width, new_inner_size.height); display.resize(new_inner_size.width, new_inner_size.height);
demo.resize(&mut display); demo.resize(&mut display);
} }
@ -258,6 +243,7 @@ pub async fn run<D: Demo>() -> Result<(), Error> {
_ => {} _ => {}
} }
} }
}
_ => {} _ => {}
} }
}); });

@ -24,13 +24,11 @@ impl Light {
position: Vector4::new(position.x, position.y, position.z, 1.0), position: Vector4::new(position.x, position.y, position.z, 1.0),
color: Vector4::new(color.x, color.y, color.z, 1.0), color: Vector4::new(color.x, color.y, color.z, 1.0),
}; };
let buffer = device.create_buffer_init( let buffer = device.create_buffer_init(&BufferInitDescriptor {
&BufferInitDescriptor {
contents: bytemuck::cast_slice(&[data]), contents: bytemuck::cast_slice(&[data]),
usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
label: Some("Light Buffer"), label: Some("Light Buffer"),
} });
);
Self { data, buffer } Self { data, buffer }
} }

@ -130,16 +130,17 @@ impl<'a> Model<'a> {
let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?; let (obj_models, obj_materials) = tobj::load_obj(path.as_ref(), true)?;
// We're assuming that the texture files are stored with the obj file // We're assuming that the texture files are stored with the obj file
let containing_folder = path.as_ref().parent() let containing_folder = path.as_ref().parent().context("Directory has no parent")?;
.context("Directory has no parent")?;
let mut materials = Vec::new(); let mut materials = Vec::new();
for mat in obj_materials { for mat in obj_materials {
let diffuse_path = mat.diffuse_texture; let diffuse_path = mat.diffuse_texture;
let diffuse_texture = texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?; let diffuse_texture =
texture::Texture::load(device, queue, containing_folder.join(diffuse_path), false)?;
let normal_path = mat.normal_texture; let normal_path = mat.normal_texture;
let normal_texture = texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?; let normal_texture =
texture::Texture::load(device, queue, containing_folder.join(normal_path), true)?;
materials.push(Material::new( materials.push(Material::new(
device, device,
@ -159,16 +160,15 @@ impl<'a> Model<'a> {
m.mesh.positions[i * 3], m.mesh.positions[i * 3],
m.mesh.positions[i * 3 + 1], m.mesh.positions[i * 3 + 1],
m.mesh.positions[i * 3 + 2], m.mesh.positions[i * 3 + 2],
].into(), ]
tex_coords: [ .into(),
m.mesh.texcoords[i * 2], tex_coords: [m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]].into(),
m.mesh.texcoords[i * 2 + 1]
].into(),
normal: [ normal: [
m.mesh.normals[i * 3], m.mesh.normals[i * 3],
m.mesh.normals[i * 3 + 1], m.mesh.normals[i * 3 + 1],
m.mesh.normals[i * 3 + 2], m.mesh.normals[i * 3 + 2],
].into(), ]
.into(),
// We'll calculate these later // We'll calculate these later
tangent: [0.0; 3].into(), tangent: [0.0; 3].into(),
bitangent: [0.0; 3].into(), bitangent: [0.0; 3].into(),
@ -208,7 +208,7 @@ impl<'a> Model<'a> {
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided // Luckily, the place I found this equation provided
// the solution! // the solution!
let r = 1.0 / (delta_uv1 .x * delta_uv2.y - delta_uv1.y * delta_uv2.x); let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r; let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
@ -222,20 +222,16 @@ impl<'a> Model<'a> {
vertices[c[2] as usize].bitangent = bitangent; vertices[c[2] as usize].bitangent = bitangent;
} }
let vertex_buffer = device.create_buffer_init( let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", path.as_ref())), label: Some(&format!("{:?} Vertex Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&vertices), contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsage::VERTEX, usage: wgpu::BufferUsage::VERTEX,
} });
); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", path.as_ref())), label: Some(&format!("{:?} Index Buffer", path.as_ref())),
contents: bytemuck::cast_slice(&m.mesh.indices), contents: bytemuck::cast_slice(&m.mesh.indices),
usage: wgpu::BufferUsage::INDEX, usage: wgpu::BufferUsage::INDEX,
} });
);
meshes.push(Mesh { meshes.push(Mesh {
name: m.name, name: m.name,

@ -1,5 +1,5 @@
use anyhow::*;
use crate::model::Vertex; use crate::model::Vertex;
use anyhow::*;
pub struct RenderPipelineBuilder<'a> { pub struct RenderPipelineBuilder<'a> {
layout: Option<&'a wgpu::PipelineLayout>, layout: Option<&'a wgpu::PipelineLayout>,
@ -69,7 +69,6 @@ impl<'a> RenderPipelineBuilder<'a> {
self self
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn depth_bias(&mut self, db: i32) -> &mut Self { pub fn depth_bias(&mut self, db: i32) -> &mut Self {
self.depth_bias = db; self.depth_bias = db;
@ -101,14 +100,12 @@ impl<'a> RenderPipelineBuilder<'a> {
/// Helper method for [RenderPipelineBuilder::color_state] /// Helper method for [RenderPipelineBuilder::color_state]
pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self { pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self {
self.color_state( self.color_state(wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format, format,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
} })
)
} }
pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self { pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self {
@ -123,14 +120,12 @@ impl<'a> RenderPipelineBuilder<'a> {
depth_write_enabled: bool, depth_write_enabled: bool,
depth_compare: wgpu::CompareFunction, depth_compare: wgpu::CompareFunction,
) -> &mut Self { ) -> &mut Self {
self.depth_stencil_state( self.depth_stencil_state(wgpu::DepthStencilStateDescriptor {
wgpu::DepthStencilStateDescriptor {
format, format,
depth_write_enabled, depth_write_enabled,
depth_compare, depth_compare,
stencil: Default::default(), stencil: Default::default(),
} })
)
} }
/// Helper method for [RenderPipelineBuilder::depth_no_stencil] /// Helper method for [RenderPipelineBuilder::depth_no_stencil]
@ -190,29 +185,34 @@ impl<'a> RenderPipelineBuilder<'a> {
if self.vertex_shader.is_none() { if self.vertex_shader.is_none() {
bail!("No vertex shader supplied!") bail!("No vertex shader supplied!")
} }
let vs = create_shader_module(device, self.vertex_shader.take().context("Please include a vertex shader")?); let vs = create_shader_module(
device,
self.vertex_shader
.take()
.context("Please include a vertex shader")?,
);
// The fragment shader is optional (IDK why, but it is). // The fragment shader is optional (IDK why, but it is).
// Having the shader be optional is giving me issues with // Having the shader be optional is giving me issues with
// the borrow checker so I'm going to use a default shader // the borrow checker so I'm going to use a default shader
// if the user doesn't supply one. // if the user doesn't supply one.
let fs_spv = self.fragment_shader.take().context("Please include a fragment shader")?; let fs_spv = self
.fragment_shader
.take()
.context("Please include a fragment shader")?;
let fs = create_shader_module(device, fs_spv); let fs = create_shader_module(device, fs_spv);
let pipeline = device.create_render_pipeline( let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"), label: Some("Render Pipeline"),
layout: Some(&layout), layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs, module: &vs,
entry_point: "main", entry_point: "main",
}, },
fragment_stage: Some( fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
wgpu::ProgrammableStageDescriptor {
module: &fs, module: &fs,
entry_point: "main", entry_point: "main",
} }),
),
rasterization_state: Some(wgpu::RasterizationStateDescriptor { rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: self.front_face, front_face: self.front_face,
cull_mode: self.cull_mode, cull_mode: self.cull_mode,
@ -231,13 +231,14 @@ impl<'a> RenderPipelineBuilder<'a> {
sample_count: self.sample_count, sample_count: self.sample_count,
sample_mask: self.sample_mask, sample_mask: self.sample_mask,
alpha_to_coverage_enabled: self.alpha_to_coverage_enabled, alpha_to_coverage_enabled: self.alpha_to_coverage_enabled,
} });
);
Ok(pipeline) Ok(pipeline)
} }
} }
fn create_shader_module(
fn create_shader_module(device: &wgpu::Device, spirv: wgpu::ShaderModuleSource) -> wgpu::ShaderModule { device: &wgpu::Device,
spirv: wgpu::ShaderModuleSource,
) -> wgpu::ShaderModule {
device.create_shader_module(spirv) device.create_shader_module(spirv)
} }

@ -1,11 +1,10 @@
use anyhow::*;
use image::GenericImageView; use image::GenericImageView;
use std::path::Path;
use std::mem; use std::mem;
use anyhow::*; use std::path::Path;
use crate::buffer; use crate::buffer;
pub struct Texture<'a> { pub struct Texture<'a> {
pub texture: wgpu::Texture, pub texture: wgpu::Texture,
pub view: wgpu::TextureView, pub view: wgpu::TextureView,
@ -28,10 +27,7 @@ impl<'a> Texture<'a> {
Self::from_image(device, queue, &img, Some(label), is_normal_map) Self::from_image(device, queue, &img, Some(label), is_normal_map)
} }
pub fn from_descriptor( pub fn from_descriptor(device: &wgpu::Device, desc: wgpu::TextureDescriptor<'a>) -> Self {
device: &wgpu::Device,
desc: wgpu::TextureDescriptor<'a>
) -> Self {
let texture = device.create_texture(&desc); let texture = device.create_texture(&desc);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
@ -48,7 +44,12 @@ impl<'a> Texture<'a> {
..Default::default() ..Default::default()
}); });
Self { texture, view, sampler, desc } Self {
texture,
view,
sampler,
desc,
}
} }
pub fn from_bytes( pub fn from_bytes(
@ -121,10 +122,18 @@ impl<'a> Texture<'a> {
..Default::default() ..Default::default()
}); });
Ok(Self { texture, view, sampler, desc }) Ok(Self {
texture,
view,
sampler,
desc,
})
} }
pub fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> Self { pub fn create_depth_texture(
device: &wgpu::Device,
sc_desc: &wgpu::SwapChainDescriptor,
) -> Self {
let desc = wgpu::TextureDescriptor { let desc = wgpu::TextureDescriptor {
label: None, label: None,
size: wgpu::Extent3d { size: wgpu::Extent3d {
@ -141,10 +150,10 @@ impl<'a> Texture<'a> {
Self::from_descriptor(device, desc) Self::from_descriptor(device, desc)
} }
pub fn prepare_buffer_rgba(&self, device: &wgpu::Device) -> buffer::RawBuffer<[f32;4]> { pub fn prepare_buffer_rgba(&self, device: &wgpu::Device) -> buffer::RawBuffer<[f32; 4]> {
let num_pixels = self.desc.size.width * self.desc.size.height * self.desc.size.depth; let num_pixels = self.desc.size.width * self.desc.size.height * self.desc.size.depth;
let buffer_size = num_pixels * mem::size_of::<[f32;4]>() as u32; let buffer_size = num_pixels * mem::size_of::<[f32; 4]>() as u32;
let buffer_usage = wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ; let buffer_usage = wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ;
let buffer_desc = wgpu::BufferDescriptor { let buffer_desc = wgpu::BufferDescriptor {
size: buffer_size as wgpu::BufferAddress, size: buffer_size as wgpu::BufferAddress,

@ -4,7 +4,7 @@ use fs_extra::dir::CopyOptions;
use glob::glob; use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -15,7 +15,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -29,7 +30,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -37,7 +43,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=src/*"); println!("cargo:rerun-if-changed=src/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
let mut shader_paths = [ let mut shader_paths = [
glob("./src/**/*.vert")?, glob("./src/**/*.vert")?,
@ -46,17 +51,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -69,7 +72,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -5,17 +5,21 @@ use std::{iter, mem};
async fn run() { async fn run() {
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions::default() .request_adapter(&wgpu::RequestAdapterOptions::default())
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let colors = [ let colors = [
[0.0, 0.0, 0.0], [0.0, 0.0, 0.0],
@ -40,8 +44,7 @@ async fn run() {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::COPY_SRC usage: wgpu::TextureUsage::COPY_SRC | wgpu::TextureUsage::OUTPUT_ATTACHMENT,
| wgpu::TextureUsage::OUTPUT_ATTACHMENT,
label: None, label: None,
}; };
let render_target = framework::Texture::from_descriptor(&device, rt_desc); let render_target = framework::Texture::from_descriptor(&device, rt_desc);
@ -50,7 +53,7 @@ async fn run() {
// wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. Because of this we'll // wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. Because of this we'll
// need to save both the padded_bytes_per_row as well as the // need to save both the padded_bytes_per_row as well as the
// unpadded_bytes_per_row // unpadded_bytes_per_row
let pixel_size = mem::size_of::<[u8;4]>() as u32; let pixel_size = mem::size_of::<[u8; 4]>() as u32;
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
let unpadded_bytes_per_row = pixel_size * texture_size; let unpadded_bytes_per_row = pixel_size * texture_size;
let padding = (align - unpadded_bytes_per_row % align) % align; let padding = (align - unpadded_bytes_per_row % align) % align;
@ -72,28 +75,23 @@ async fn run() {
let mut frames = Vec::new(); let mut frames = Vec::new();
for c in &colors { for c in &colors {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder =
label: None, device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
});
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &render_target.view, attachment: &render_target.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear( load: wgpu::LoadOp::Clear(wgpu::Color {
wgpu::Color {
r: c[0], r: c[0],
g: c[1], g: c[1],
b: c[2], b: c[2],
a: 1.0, a: 1.0,
} }),
),
store: true, store: true,
}, },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -114,9 +112,9 @@ async fn run() {
offset: 0, offset: 0,
bytes_per_row: padded_bytes_per_row, bytes_per_row: padded_bytes_per_row,
rows_per_image: texture_size, rows_per_image: texture_size,
}
}, },
render_target.desc.size },
render_target.desc.size,
); );
queue.submit(iter::once(encoder.finish())); queue.submit(iter::once(encoder.finish()));
@ -133,24 +131,23 @@ async fn run() {
let padded_data = buffer_slice.get_mapped_range(); let padded_data = buffer_slice.get_mapped_range();
let data = padded_data let data = padded_data
.chunks(padded_bytes_per_row as _) .chunks(padded_bytes_per_row as _)
.map(|chunk| { &chunk[..unpadded_bytes_per_row as _]}) .map(|chunk| &chunk[..unpadded_bytes_per_row as _])
.flatten() .flatten()
.map(|x| { *x }) .map(|x| *x)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
drop(padded_data); drop(padded_data);
output_buffer.unmap(); output_buffer.unmap();
frames.push(data); frames.push(data);
} }
_ => { eprintln!("Something went wrong") } _ => eprintln!("Something went wrong"),
} }
} }
save_gif("output.gif", &mut frames, 10, texture_size as u16).unwrap(); save_gif("output.gif", &mut frames, 10, texture_size as u16).unwrap();
} }
fn save_gif(path: &str, frames: &mut Vec<Vec<u8>>, speed: i32, size: u16) -> Result<()> { fn save_gif(path: &str, frames: &mut Vec<Vec<u8>>, speed: i32, size: u16) -> Result<()> {
use gif::{Frame, Encoder, Repeat, SetParameter}; use gif::{Encoder, Frame, Repeat, SetParameter};
let mut image = std::fs::File::create(path)?; let mut image = std::fs::File::create(path)?;
let mut encoder = Encoder::new(&mut image, size, size, &[])?; let mut encoder = Encoder::new(&mut image, size, size, &[])?;
@ -163,8 +160,10 @@ fn save_gif(path: &str, frames: &mut Vec<Vec<u8>>, speed: i32, size: u16) -> Res
Ok(()) Ok(())
} }
fn create_render_pipeline(
fn create_render_pipeline(device: &wgpu::Device, target: &framework::Texture) -> wgpu::RenderPipeline { device: &wgpu::Device,
target: &framework::Texture,
) -> wgpu::RenderPipeline {
let vs_src = wgpu::include_spirv!("shader.vert.spv"); let vs_src = wgpu::include_spirv!("shader.vert.spv");
let fs_src = wgpu::include_spirv!("shader.frag.spv"); let fs_src = wgpu::include_spirv!("shader.frag.spv");
let vs_module = device.create_shader_module(vs_src); let vs_module = device.create_shader_module(vs_src);
@ -189,14 +188,12 @@ fn create_render_pipeline(device: &wgpu::Device, target: &framework::Texture) ->
}), }),
rasterization_state: None, rasterization_state: None,
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: target.desc.format, format: target.desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,

@ -4,29 +4,20 @@ version = "0.1.0"
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"] authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
bytemuck = "1.3" bytemuck = "1.4"
image = "0.23" cgmath = "0.17"
framework = { path = "../framework"} env_logger = "0.7"
futures = "0.3" futures = "0.3"
rand = "0.7" image = "0.23"
tobj = "2" log = "0.4"
tobj = "2.0"
wgpu = "0.6"
winit = "0.22" winit = "0.22"
wgpu = "0.5"
[dependencies.cgmath]
version = "0.17"
features = ["swizzle"]
[build-dependencies] [build-dependencies]
failure = "0.1" anyhow = "1.0"
fs_extra = "1.1" fs_extra = "1.2"
glob = "0.3" glob = "0.3"
shaderc = "0.6" shaderc = "0.6"
[[bin]]
name = "instancing-storage-buffers"
path = "src/storage_buffers.rs"

@ -1,10 +1,10 @@
use glob::glob;
use failure::bail; use failure::bail;
use fs_extra::copy_items; use fs_extra::copy_items;
use fs_extra::dir::CopyOptions; use fs_extra::dir::CopyOptions;
use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
fn main() { fn main() {
copy_res(); copy_res();
@ -35,11 +35,10 @@ fn compile_shaders() {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result.unwrap()).unwrap())
ShaderData::load(glob_result.unwrap()).unwrap()
})
.collect::<Vec<ShaderData>>(); .collect::<Vec<ShaderData>>();
let mut compiler = shaderc::Compiler::new().unwrap(); let mut compiler = shaderc::Compiler::new().unwrap();
@ -50,13 +49,15 @@ fn compile_shaders() {
// be better just to only compile shaders that have been changed // be better just to only compile shaders that have been changed
// recently. // recently.
for shader in shaders { for shader in shaders {
let compiled = compiler.compile_into_spirv( let compiled = compiler
.compile_into_spirv(
&shader.src, &shader.src,
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
).unwrap(); )
.unwrap();
write(shader.spv_path, compiled.as_binary_u8()).unwrap(); write(shader.spv_path, compiled.as_binary_u8()).unwrap();
} }
@ -83,6 +84,11 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }

@ -4,7 +4,7 @@ use fs_extra::dir::CopyOptions;
use glob::glob; use glob::glob;
use std::env; use std::env;
use std::fs::{read_to_string, write}; use std::fs::{read_to_string, write};
use std::path::{PathBuf}; use std::path::PathBuf;
struct ShaderData { struct ShaderData {
src: String, src: String,
@ -15,7 +15,8 @@ struct ShaderData {
impl ShaderData { impl ShaderData {
pub fn load(src_path: PathBuf) -> Result<Self> { pub fn load(src_path: PathBuf) -> Result<Self> {
let extension = src_path.extension() let extension = src_path
.extension()
.context("File has no extension")? .context("File has no extension")?
.to_str() .to_str()
.context("Extension cannot be converted to &str")?; .context("Extension cannot be converted to &str")?;
@ -29,7 +30,12 @@ impl ShaderData {
let src = read_to_string(src_path.clone())?; let src = read_to_string(src_path.clone())?;
let spv_path = src_path.with_extension(format!("{}.spv", extension)); let spv_path = src_path.with_extension(format!("{}.spv", extension));
Ok(Self { src, src_path, spv_path, kind }) Ok(Self {
src,
src_path,
spv_path,
kind,
})
} }
} }
@ -37,7 +43,6 @@ fn main() -> Result<()> {
// This tells cargo to rerun this script if something in /src/ changes. // This tells cargo to rerun this script if something in /src/ changes.
println!("cargo:rerun-if-changed=res/*"); println!("cargo:rerun-if-changed=res/*");
// Collect all shaders recursively within /src/ // Collect all shaders recursively within /src/
let mut shader_paths = [ let mut shader_paths = [
glob("./res/**/*.vert")?, glob("./res/**/*.vert")?,
@ -46,17 +51,15 @@ fn main() -> Result<()> {
]; ];
// This could be parallelized // This could be parallelized
let shaders = shader_paths.iter_mut() let shaders = shader_paths
.iter_mut()
.flatten() .flatten()
.map(|glob_result| { .map(|glob_result| ShaderData::load(glob_result?))
ShaderData::load(glob_result?)
})
.collect::<Vec<Result<_>>>() .collect::<Vec<Result<_>>>()
.into_iter() .into_iter()
.collect::<Result<Vec<_>>>(); .collect::<Result<Vec<_>>>();
let mut compiler = shaderc::Compiler::new() let mut compiler = shaderc::Compiler::new().context("Unable to create shader compiler")?;
.context("Unable to create shader compiler")?;
// This can't be parallelized. The [shaderc::Compiler] is not // This can't be parallelized. The [shaderc::Compiler] is not
// thread safe. Also, it creates a lot of resources. You could // thread safe. Also, it creates a lot of resources. You could
@ -69,7 +72,7 @@ fn main() -> Result<()> {
shader.kind, shader.kind,
&shader.src_path.to_str().unwrap(), &shader.src_path.to_str().unwrap(),
"main", "main",
None None,
)?; )?;
write(shader.spv_path, compiled.as_binary_u8())?; write(shader.spv_path, compiled.as_binary_u8())?;
} }

@ -1,4 +1,4 @@
use winit::event::{VirtualKeyCode, ElementState}; use winit::event::{ElementState, VirtualKeyCode};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Input { pub struct Input {
@ -37,7 +37,7 @@ impl Input {
self.enter_pressed = pressed; self.enter_pressed = pressed;
true true
} }
_ => false _ => false,
} }
} }

@ -1,18 +1,17 @@
mod input;
mod render; mod render;
mod sound;
mod state; mod state;
mod util;
mod system; mod system;
mod sound; mod util;
mod input;
use system::System;
use input::Input; use input::Input;
use system::System;
use winit::event::*;
use winit::window::{WindowBuilder, Fullscreen};
use winit::event_loop::{EventLoop, ControlFlow};
use futures::executor::block_on; use futures::executor::block_on;
use winit::event::*;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Fullscreen, WindowBuilder};
fn main() { fn main() {
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -22,7 +21,8 @@ fn main() {
.with_visible(false) .with_visible(false)
.with_title("Pong") .with_title("Pong")
.with_fullscreen(Some(Fullscreen::Exclusive(video_mode.clone()))) .with_fullscreen(Some(Fullscreen::Exclusive(video_mode.clone())))
.build(&event_loop).unwrap(); .build(&event_loop)
.unwrap();
window.set_cursor_visible(false); window.set_cursor_visible(false);
let mut render = block_on(render::Render::new(&window, &video_mode)); let mut render = block_on(render::Render::new(&window, &video_mode));
@ -124,8 +124,10 @@ fn main() {
state.game_state = state::GameState::Quiting; state.game_state = state::GameState::Quiting;
} }
Event::WindowEvent { Event::WindowEvent {
event: WindowEvent::KeyboardInput { event:
input: KeyboardInput { WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: element_state, state: element_state,
virtual_keycode: Some(key), virtual_keycode: Some(key),
.. ..
@ -145,8 +147,7 @@ fn main() {
Event::RedrawRequested(_) => { Event::RedrawRequested(_) => {
for event in &events { for event in &events {
match event { match event {
state::Event::FocusChanged state::Event::FocusChanged | state::Event::ButtonPressed => {
| state::Event::ButtonPressed => {
sound_system.queue(sound_pack.bounce()); sound_system.queue(sound_pack.bounce());
} }
state::Event::BallBounce(_pos) => { state::Event::BallBounce(_pos) => {
@ -154,8 +155,7 @@ fn main() {
} }
state::Event::Score(_) => { state::Event::Score(_) => {
sound_system.queue(sound_pack.bounce()); sound_system.queue(sound_pack.bounce());
} } // _ => {}
// _ => {}
} }
} }
events.clear(); events.clear();
@ -167,14 +167,14 @@ fn main() {
if state.game_state == state::GameState::Serving { if state.game_state == state::GameState::Serving {
serving_system.start(&mut state); serving_system.start(&mut state);
} }
}, }
state::GameState::Serving => { state::GameState::Serving => {
serving_system.update_state(&input, &mut state, &mut events); serving_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events); play_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::Playing { if state.game_state == state::GameState::Playing {
play_system.start(&mut state); play_system.start(&mut state);
} }
}, }
state::GameState::Playing => { state::GameState::Playing => {
ball_system.update_state(&input, &mut state, &mut events); ball_system.update_state(&input, &mut state, &mut events);
play_system.update_state(&input, &mut state, &mut events); play_system.update_state(&input, &mut state, &mut events);
@ -183,14 +183,14 @@ fn main() {
} else if state.game_state == state::GameState::GameOver { } else if state.game_state == state::GameState::GameOver {
game_over_system.start(&mut state); game_over_system.start(&mut state);
} }
}, }
state::GameState::GameOver => { state::GameState::GameOver => {
game_over_system.update_state(&input, &mut state, &mut events); game_over_system.update_state(&input, &mut state, &mut events);
if state.game_state == state::GameState::MainMenu { if state.game_state == state::GameState::MainMenu {
menu_system.start(&mut state); menu_system.start(&mut state);
} }
}, }
state::GameState::Quiting => {}, state::GameState::Quiting => {}
} }
render.render_state(&state); render.render_state(&state);
@ -203,7 +203,6 @@ fn main() {
}); });
} }
fn process_input( fn process_input(
element_state: ElementState, element_state: ElementState,
keycode: VirtualKeyCode, keycode: VirtualKeyCode,

@ -1,5 +1,5 @@
use crate::util::size_of_slice;
use crate::state; use crate::state;
use crate::util::size_of_slice;
use wgpu::util::{BufferInitDescriptor, DeviceExt}; use wgpu::util::{BufferInitDescriptor, DeviceExt};
pub const U32_SIZE: wgpu::BufferAddress = std::mem::size_of::<u32>() as wgpu::BufferAddress; pub const U32_SIZE: wgpu::BufferAddress = std::mem::size_of::<u32>() as wgpu::BufferAddress;
@ -109,14 +109,12 @@ pub struct StagingBuffer {
impl StagingBuffer { impl StagingBuffer {
pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer { pub fn new<T: bytemuck::Pod + Sized>(device: &wgpu::Device, data: &[T]) -> StagingBuffer {
StagingBuffer { StagingBuffer {
buffer: device.create_buffer_init( buffer: device.create_buffer_init(&BufferInitDescriptor {
&BufferInitDescriptor {
contents: bytemuck::cast_slice(data), contents: bytemuck::cast_slice(data),
usage: wgpu::BufferUsage::COPY_SRC, usage: wgpu::BufferUsage::COPY_SRC,
label: Some("Staging Buffer"), label: Some("Staging Buffer"),
} }),
), size: size_of_slice(data) as wgpu::BufferAddress,
size: size_of_slice(data) as wgpu::BufferAddress
} }
} }

@ -2,9 +2,9 @@ mod buffer;
use std::iter; use std::iter;
use winit::window::{Window};
use winit::monitor::{VideoMode};
use wgpu_glyph::{ab_glyph, Section, Text}; use wgpu_glyph::{ab_glyph, Section, Text};
use winit::monitor::VideoMode;
use winit::window::Window;
use buffer::*; use buffer::*;
@ -43,20 +43,24 @@ impl Render {
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
let surface = unsafe { instance.create_surface(window) }; let surface = unsafe { instance.create_surface(window) };
let adapter = instance.request_adapter( let adapter = instance
&wgpu::RequestAdapterOptions { .request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default, power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
}, })
).await.unwrap(); .await
let (device, queue) = adapter.request_device( .unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
features: wgpu::Features::empty(), features: wgpu::Features::empty(),
limits: wgpu::Limits::default(), limits: wgpu::Limits::default(),
shader_validation: true, shader_validation: true,
}, },
None, // Trace path None, // Trace path
).await.unwrap(); )
.await
.unwrap();
let size = video_mode.size(); let size = video_mode.size();
let sc_desc = wgpu::SwapChainDescriptor { let sc_desc = wgpu::SwapChainDescriptor {
@ -68,13 +72,11 @@ impl Render {
}; };
let swap_chain = device.create_swap_chain(&surface, &sc_desc); let swap_chain = device.create_swap_chain(&surface, &sc_desc);
let pipeline_layout = device.create_pipeline_layout( let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[], bind_group_layouts: &[],
push_constant_ranges: &[], push_constant_ranges: &[],
label: Some("Pipeline Layout"), label: Some("Pipeline Layout"),
} });
);
let pipeline = create_render_pipeline( let pipeline = create_render_pipeline(
&device, &device,
&pipeline_layout, &pipeline_layout,
@ -99,8 +101,8 @@ impl Render {
}); });
let font = ab_glyph::FontArc::try_from_slice(FONT_BYTES).unwrap(); let font = ab_glyph::FontArc::try_from_slice(FONT_BYTES).unwrap();
let glyph_brush = wgpu_glyph::GlyphBrushBuilder::using_font(font) let glyph_brush =
.build(&device, sc_desc.format); wgpu_glyph::GlyphBrushBuilder::using_font(font).build(&device, sc_desc.format);
let staging_belt = wgpu::util::StagingBelt::new(1024); let staging_belt = wgpu::util::StagingBelt::new(1024);
Self { Self {
@ -119,14 +121,11 @@ impl Render {
} }
pub fn render_state(&mut self, state: &state::State) { pub fn render_state(&mut self, state: &state::State) {
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = self
label: None, .device
}); .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
let num_indices = if state.ball.visible let num_indices = if state.ball.visible || state.player1.visible || state.player2.visible {
|| state.player1.visible
|| state.player2.visible
{
let (stg_vertex, stg_index, num_indices) = QuadBufferBuilder::new() let (stg_vertex, stg_index, num_indices) = QuadBufferBuilder::new()
.push_ball(&state.ball) .push_ball(&state.ball)
.push_player(&state.player1) .push_player(&state.player1)
@ -143,13 +142,11 @@ impl Render {
match self.swap_chain.get_current_frame() { match self.swap_chain.get_current_frame() {
Ok(frame) => { Ok(frame) => {
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.output.view, attachment: &frame.output.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations::default(), ops: wgpu::Operations::default(),
}, }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -181,14 +178,16 @@ impl Render {
draw_text(&state.win_text, &mut self.glyph_brush); draw_text(&state.win_text, &mut self.glyph_brush);
} }
self.glyph_brush.draw_queued( self.glyph_brush
.draw_queued(
&self.device, &self.device,
&mut self.staging_belt, &mut self.staging_belt,
&mut encoder, &mut encoder,
&frame.output.view, &frame.output.view,
self.sc_desc.width, self.sc_desc.width,
self.sc_desc.height, self.sc_desc.height,
).unwrap(); )
.unwrap();
self.staging_belt.finish(); self.staging_belt.finish();
self.queue.submit(iter::once(encoder.finish())); self.queue.submit(iter::once(encoder.finish()));
@ -203,33 +202,27 @@ impl Render {
} }
} }
fn draw_text( fn draw_text(text: &state::Text, glyph_brush: &mut wgpu_glyph::GlyphBrush<()>) {
text: &state::Text, let layout = wgpu_glyph::Layout::default().h_align(if text.centered {
glyph_brush: &mut wgpu_glyph::GlyphBrush<()>,
) {
let layout = wgpu_glyph::Layout::default()
.h_align(
if text.centered {
wgpu_glyph::HorizontalAlign::Center wgpu_glyph::HorizontalAlign::Center
} else { } else {
wgpu_glyph::HorizontalAlign::Left wgpu_glyph::HorizontalAlign::Left
} });
);
let section = Section { let section =
Section {
screen_position: text.position.into(), screen_position: text.position.into(),
bounds: text.bounds.into(), bounds: text.bounds.into(),
layout, layout,
..Section::default() ..Section::default()
}.add_text( }
Text::new(&text.text) .add_text(Text::new(&text.text).with_color(text.color).with_scale(
.with_color(text.color) if text.focused {
.with_scale(if text.focused {
text.size + 8.0 text.size + 8.0
} else { } else {
text.size text.size
}) },
); ));
glyph_brush.queue(section); glyph_brush.queue(section);
} }

@ -15,12 +15,8 @@ impl SoundSystem {
let sink = rodio::Sink::new(&device); let sink = rodio::Sink::new(&device);
sink.set_volume(0.5); sink.set_volume(0.5);
let spatial_sink = rodio::SpatialSink::new( let spatial_sink =
&device, rodio::SpatialSink::new(&device, [0.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [1.0, 0.0, 0.0]);
[0.0, 0.0, 0.0],
[-1.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
);
Self { Self {
device, device,

@ -1,4 +1,3 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum GameState { pub enum GameState {
MainMenu, MainMenu,
@ -50,10 +49,7 @@ impl Player {
let b_min = ball.position - b_radii; let b_min = ball.position - b_radii;
let b_max = ball.position + b_radii; let b_max = ball.position + b_radii;
min.x < b_max.x min.x < b_max.x && max.x > b_min.x && min.y < b_max.y && max.y > b_min.y
&& max.x > b_min.x
&& min.y < b_max.y
&& max.y > b_min.y
} }
} }

@ -1,5 +1,5 @@
use crate::state::{self, GameState};
use crate::input; use crate::input;
use crate::state::{self, GameState};
use crate::util; use crate::util;
pub trait System { pub trait System {
@ -23,7 +23,12 @@ impl System for VisibilitySystem {
) { ) {
let gs = state.game_state; let gs = state.game_state;
let is_in_game = any!(gs, GameState::Serving, GameState::Playing, GameState::GameOver); let is_in_game = any!(
gs,
GameState::Serving,
GameState::Playing,
GameState::GameOver
);
state.ball.visible = is_in_game && gs != GameState::GameOver; state.ball.visible = is_in_game && gs != GameState::GameOver;
state.player1.visible = is_in_game; state.player1.visible = is_in_game;
state.player1_score.visible = is_in_game; state.player1_score.visible = is_in_game;
@ -55,9 +60,8 @@ impl System for MenuSystem {
&self, &self,
input: &input::Input, input: &input::Input,
state: &mut state::State, state: &mut state::State,
events: &mut Vec<state::Event> events: &mut Vec<state::Event>,
) { ) {
if state.play_button.focused && input.ui_down_pressed() { if state.play_button.focused && input.ui_down_pressed() {
events.push(state::Event::FocusChanged); events.push(state::Event::FocusChanged);
state.play_button.focused = false; state.play_button.focused = false;
@ -80,7 +84,6 @@ impl System for MenuSystem {
pub struct PlaySystem; pub struct PlaySystem;
impl System for PlaySystem { impl System for PlaySystem {
fn update_state( fn update_state(
&self, &self,
input: &input::Input, input: &input::Input,
@ -119,7 +122,6 @@ impl System for PlaySystem {
} }
} }
pub struct BallSystem; pub struct BallSystem;
impl System for BallSystem { impl System for BallSystem {
@ -164,7 +166,6 @@ impl System for BallSystem {
} }
} }
pub struct ServingSystem { pub struct ServingSystem {
last_time: std::time::Instant, last_time: std::time::Instant,
} }
@ -227,7 +228,8 @@ impl System for GameOverSystem {
}; };
} }
fn update_state(&self, fn update_state(
&self,
_input: &input::Input, _input: &input::Input,
state: &mut state::State, state: &mut state::State,
_events: &mut Vec<state::Event>, _events: &mut Vec<state::Event>,

@ -29,7 +29,6 @@ macro_rules! any {
}; };
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[allow(unused_imports)] #[allow(unused_imports)]

@ -5,7 +5,9 @@ async fn run() {
compatible_surface: None, compatible_surface: None,
}, },
wgpu::BackendBit::PRIMARY, wgpu::BackendBit::PRIMARY,
).await.unwrap(); )
.await
.unwrap();
let (device, queue) = adapter.request_device(&Default::default()).await; let (device, queue) = adapter.request_device(&Default::default()).await;
let texture_size = 256u32; let texture_size = 256u32;
@ -20,9 +22,7 @@ async fn run() {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsage::COPY_SRC usage: wgpu::TextureUsage::COPY_SRC | wgpu::TextureUsage::OUTPUT_ATTACHMENT,
| wgpu::TextureUsage::OUTPUT_ATTACHMENT
,
label: None, label: None,
}; };
let texture = device.create_texture(&texture_desc); let texture = device.create_texture(&texture_desc);
@ -44,8 +44,24 @@ async fn run() {
let vs_src = include_str!("shader.vert"); let vs_src = include_str!("shader.vert");
let fs_src = include_str!("shader.frag"); let fs_src = include_str!("shader.frag");
let mut compiler = shaderc::Compiler::new().unwrap(); let mut compiler = shaderc::Compiler::new().unwrap();
let vs_spirv = compiler.compile_into_spirv(vs_src, shaderc::ShaderKind::Vertex, "shader.vert", "main", None).unwrap(); let vs_spirv = compiler
let fs_spirv = compiler.compile_into_spirv(fs_src, shaderc::ShaderKind::Fragment, "shader.frag", "main", None).unwrap(); .compile_into_spirv(
vs_src,
shaderc::ShaderKind::Vertex,
"shader.vert",
"main",
None,
)
.unwrap();
let fs_spirv = compiler
.compile_into_spirv(
fs_src,
shaderc::ShaderKind::Fragment,
"shader.frag",
"main",
None,
)
.unwrap();
let vs_data = wgpu::read_spirv(std::io::Cursor::new(vs_spirv.as_binary_u8())).unwrap(); let vs_data = wgpu::read_spirv(std::io::Cursor::new(vs_spirv.as_binary_u8())).unwrap();
let fs_data = wgpu::read_spirv(std::io::Cursor::new(fs_spirv.as_binary_u8())).unwrap(); let fs_data = wgpu::read_spirv(std::io::Cursor::new(fs_spirv.as_binary_u8())).unwrap();
let vs_module = device.create_shader_module(&vs_data); let vs_module = device.create_shader_module(&vs_data);
@ -73,14 +89,12 @@ async fn run() {
depth_bias_clamp: 0.0, depth_bias_clamp: 0.0,
}), }),
primitive_topology: wgpu::PrimitiveTopology::TriangleList, primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[ color_states: &[wgpu::ColorStateDescriptor {
wgpu::ColorStateDescriptor {
format: texture_desc.format, format: texture_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE, color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL, write_mask: wgpu::ColorWrite::ALL,
}, }],
],
depth_stencil_state: None, depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor { vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16, index_format: wgpu::IndexFormat::Uint16,
@ -91,14 +105,12 @@ async fn run() {
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}); });
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder =
label: None, device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
});
{ {
let render_pass_desc = wgpu::RenderPassDescriptor { let render_pass_desc = wgpu::RenderPassDescriptor {
color_attachments: &[ color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &texture_view, attachment: &texture_view,
resolve_target: None, resolve_target: None,
load_op: wgpu::LoadOp::Clear, load_op: wgpu::LoadOp::Clear,
@ -109,8 +121,7 @@ async fn run() {
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
}, },
} }],
],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}; };
let mut render_pass = encoder.begin_render_pass(&render_pass_desc); let mut render_pass = encoder.begin_render_pass(&render_pass_desc);
@ -146,11 +157,7 @@ async fn run() {
let data = result.as_slice(); let data = result.as_slice();
use image::{ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba};
let buffer = ImageBuffer::<Rgba<u8>, _>::from_raw( let buffer = ImageBuffer::<Rgba<u8>, _>::from_raw(texture_size, texture_size, data).unwrap();
texture_size,
texture_size,
data,
).unwrap();
buffer.save("image.png").unwrap(); buffer.save("image.png").unwrap();
} }

@ -1 +1 @@
Subproject commit e5b27f1c61c59e17b315e1044ae582e67943ee29 Subproject commit d764e13f7b3afe4455cb376ae3eb562c001c6752
Loading…
Cancel
Save