mirror of https://github.com/sotrh/learn-wgpu
started instancing examples
parent
252ca8b909
commit
0cd250d5fe
@ -1,44 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout(location=0) in vec3 a_position;
|
||||
layout(location=1) in vec2 a_tex_coords;
|
||||
layout(location=2) in vec3 a_normal;
|
||||
// NEW!
|
||||
layout(location=3) in vec3 a_tangent;
|
||||
layout(location=4) in vec3 a_bitangent;
|
||||
|
||||
layout(location=0) out vec2 v_tex_coords;
|
||||
layout(location=1) out vec3 v_position; // UPDATED!
|
||||
layout(location=2) out mat3 v_tangent_matrix; // NEW!
|
||||
|
||||
layout(set=1, binding=0)
|
||||
uniform Uniforms {
|
||||
vec3 u_view_position;
|
||||
mat4 u_view_proj;
|
||||
};
|
||||
|
||||
layout(set=1, binding=1)
|
||||
buffer Instances {
|
||||
mat4 s_models[];
|
||||
};
|
||||
|
||||
void main() {
|
||||
v_tex_coords = a_tex_coords;
|
||||
|
||||
mat4 model_matrix = s_models[gl_InstanceIndex];
|
||||
|
||||
mat3 normal_matrix = mat3(transpose(inverse(model_matrix)));
|
||||
vec3 normal = normalize(normal_matrix * a_normal);
|
||||
vec3 tangent = normalize(normal_matrix * a_tangent);
|
||||
vec3 bitangent = normalize(normal_matrix * a_bitangent);
|
||||
v_tangent_matrix = mat3(
|
||||
tangent,
|
||||
bitangent,
|
||||
normal
|
||||
);
|
||||
|
||||
vec4 model_space = model_matrix * vec4(a_position, 1.0);
|
||||
v_position = model_space.xyz;
|
||||
|
||||
gl_Position = u_view_proj * model_space;
|
||||
}
|
@ -1,25 +1,220 @@
|
||||
use cgmath::*;
|
||||
use winit::event::*;
|
||||
use winit::dpi::LogicalPosition;
|
||||
use std::time::Duration;
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, -1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.0, 0.0, 0.5, 1.0,
|
||||
);
|
||||
|
||||
pub fn camera_setup<
|
||||
V: Into<Point3<f32>>,
|
||||
Y: Into<Rad<f32>>,
|
||||
P: Into<Rad<f32>>,
|
||||
>(
|
||||
position: V,
|
||||
yaw: Y,
|
||||
pitch: P,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> (Camera, Projection, CameraController) {
|
||||
(
|
||||
Camera::new(position, yaw, pitch),
|
||||
Projection::new(width, height, Deg(45.0), 0.1, 100.0),
|
||||
CameraController::new(4.0, 0.4),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Camera {
|
||||
eye: cgmath::Point3<f32>,
|
||||
target: cgmath::Point3<f32>,
|
||||
up: cgmath::Vector3<f32>,
|
||||
pub position: Point3<f32>,
|
||||
yaw: Rad<f32>,
|
||||
pitch: Rad<f32>,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new<
|
||||
V: Into<Point3<f32>>,
|
||||
Y: Into<Rad<f32>>,
|
||||
P: Into<Rad<f32>>,
|
||||
>(
|
||||
position: V,
|
||||
yaw: Y,
|
||||
pitch: P,
|
||||
) -> Self {
|
||||
Self {
|
||||
position: position.into(),
|
||||
yaw: yaw.into(),
|
||||
pitch: pitch.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
||||
Matrix4::look_at_dir(
|
||||
self.position,
|
||||
Vector3::new(
|
||||
self.yaw.0.cos(),
|
||||
self.pitch.0.sin(),
|
||||
self.yaw.0.sin(),
|
||||
).normalize(),
|
||||
Vector3::unit_y(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Projection {
|
||||
aspect: f32,
|
||||
fovy: f32,
|
||||
fovy: Rad<f32>,
|
||||
znear: f32,
|
||||
zfar: f32,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn build_view_projection_matrix(&self) -> cgmath::Matrix4<f32> {
|
||||
let view = cgmath::Matrix4::look_at(self.eye, self.target, self.up);
|
||||
let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar);
|
||||
return proj * view;
|
||||
impl Projection {
|
||||
pub fn new<F: Into<Rad<f32>>>(
|
||||
width: u32,
|
||||
height: u32,
|
||||
fovy: F,
|
||||
znear: f32,
|
||||
zfar: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
aspect: width as f32 / height as f32,
|
||||
fovy: fovy.into(),
|
||||
znear,
|
||||
zfar,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.aspect = width as f32 / height as f32;
|
||||
}
|
||||
|
||||
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
||||
OPENGL_TO_WGPU_MATRIX * perspective(self.fovy, self.aspect, self.znear, self.zfar)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CameraController {
|
||||
amount_left: f32,
|
||||
amount_right: f32,
|
||||
amount_forward: f32,
|
||||
amount_backward: f32,
|
||||
amount_up: f32,
|
||||
amount_down: f32,
|
||||
rotate_horizontal: f32,
|
||||
rotate_vertical: f32,
|
||||
scroll: f32,
|
||||
speed: f32,
|
||||
sensitivity: f32,
|
||||
}
|
||||
|
||||
impl CameraController {
|
||||
pub fn new(speed: f32, sensitivity: f32) -> Self {
|
||||
Self {
|
||||
amount_left: 0.0,
|
||||
amount_right: 0.0,
|
||||
amount_forward: 0.0,
|
||||
amount_backward: 0.0,
|
||||
amount_up: 0.0,
|
||||
amount_down: 0.0,
|
||||
rotate_horizontal: 0.0,
|
||||
rotate_vertical: 0.0,
|
||||
scroll: 0.0,
|
||||
speed,
|
||||
sensitivity,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool{
|
||||
let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 };
|
||||
match key {
|
||||
VirtualKeyCode::W | VirtualKeyCode::Up => {
|
||||
self.amount_forward = amount;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::S | VirtualKeyCode::Down => {
|
||||
self.amount_backward = amount;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::A | VirtualKeyCode::Left => {
|
||||
self.amount_left = amount;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::D | VirtualKeyCode::Right => {
|
||||
self.amount_right = amount;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::Space => {
|
||||
self.amount_up = amount;
|
||||
true
|
||||
}
|
||||
VirtualKeyCode::LShift => {
|
||||
self.amount_down = amount;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
|
||||
self.rotate_horizontal = mouse_dx as f32;
|
||||
self.rotate_vertical = mouse_dy as f32;
|
||||
}
|
||||
|
||||
pub fn process_scroll(&mut self, delta: &MouseScrollDelta) {
|
||||
self.scroll = -match delta {
|
||||
// I'm assuming a line is about 100 pixels
|
||||
MouseScrollDelta::LineDelta(_, scroll) => scroll * 100.0,
|
||||
MouseScrollDelta::PixelDelta(LogicalPosition {
|
||||
y: scroll,
|
||||
..
|
||||
}) => *scroll as f32,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) {
|
||||
let dt = dt.as_secs_f32();
|
||||
|
||||
// Move forward/backward and left/right
|
||||
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
|
||||
let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
|
||||
let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
|
||||
camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
|
||||
camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
|
||||
|
||||
// Move in/out (aka. "zoom")
|
||||
// Note: this isn't an actual zoom. The camera's position
|
||||
// changes when zooming. I've added this to make it easier
|
||||
// to get closer to an object you want to focus on.
|
||||
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();
|
||||
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
|
||||
|
||||
// Move up/down. Since we don't use roll, we can just
|
||||
// modify the y coordinate directly.
|
||||
camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;
|
||||
|
||||
// Rotate
|
||||
camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt;
|
||||
camera.pitch += Rad(-self.rotate_vertical) * self.sensitivity * dt;
|
||||
|
||||
// If process_mouse isn't called every frame, these values
|
||||
// will not get set to zero, and the camera will rotate
|
||||
// when moving in a non cardinal direction.
|
||||
self.rotate_horizontal = 0.0;
|
||||
self.rotate_vertical = 0.0;
|
||||
|
||||
// Keep the camera's angle from going too high/low.
|
||||
if camera.pitch < -Rad(FRAC_PI_2) {
|
||||
camera.pitch = -Rad(FRAC_PI_2);
|
||||
} else if camera.pitch > Rad(FRAC_PI_2) {
|
||||
camera.pitch = Rad(FRAC_PI_2);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,254 @@
|
||||
mod buffer;
|
||||
mod camera;
|
||||
mod light;
|
||||
mod model;
|
||||
mod pipeline;
|
||||
mod texture;
|
||||
pub mod prelude;
|
||||
|
||||
pub use buffer::*;
|
||||
pub use camera::*;
|
||||
pub use light::*;
|
||||
pub use model::*;
|
||||
pub use texture::*;
|
||||
pub use pipeline::*;
|
||||
pub use texture::*;
|
||||
|
||||
use anyhow::*;
|
||||
use cgmath::*;
|
||||
use std::time::{Duration, Instant};
|
||||
use winit::event::*;
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
pub struct Display {
|
||||
_adapter: wgpu::Adapter,
|
||||
surface: wgpu::Surface,
|
||||
pub sc_desc: wgpu::SwapChainDescriptor,
|
||||
pub swap_chain: wgpu::SwapChain,
|
||||
pub device: wgpu::Device,
|
||||
pub queue: wgpu::Queue,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub async fn new(window: &Window) -> Result<Self, Error> {
|
||||
let size = window.inner_size();
|
||||
let surface = wgpu::Surface::create(window);
|
||||
let _adapter: wgpu::Adapter = wgpu::Adapter::request(
|
||||
&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::Default,
|
||||
compatible_surface: Some(&surface),
|
||||
},
|
||||
wgpu::BackendBit::PRIMARY,
|
||||
).await.context("Unable to find valid device!")?;
|
||||
let (device, queue) = _adapter.request_device(&Default::default()).await;
|
||||
let sc_desc = wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
};
|
||||
let swap_chain = device.create_swap_chain(&surface, &sc_desc);
|
||||
|
||||
Ok(Self {
|
||||
_adapter,
|
||||
surface,
|
||||
sc_desc,
|
||||
swap_chain,
|
||||
device,
|
||||
queue,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.sc_desc.width = width;
|
||||
self.sc_desc.height = height;
|
||||
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holds the camera data to be passed to wgpu.
|
||||
*/
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct UniformData {
|
||||
view_position: cgmath::Vector4<f32>,
|
||||
view_proj: cgmath::Matrix4<f32>,
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Zeroable for UniformData {}
|
||||
unsafe impl bytemuck::Pod for UniformData {}
|
||||
|
||||
pub struct Uniforms {
|
||||
data: UniformData,
|
||||
buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl Uniforms {
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
let data = UniformData {
|
||||
view_position: Zero::zero(),
|
||||
view_proj: cgmath::Matrix4::identity(),
|
||||
};
|
||||
let buffer = device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&[data]),
|
||||
wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
|
||||
);
|
||||
|
||||
Self { data, buffer }
|
||||
}
|
||||
|
||||
pub fn update_view_proj(&mut self, camera: &camera::Camera, projection: &camera::Projection) {
|
||||
self.data.view_position = camera.position.to_homogeneous();
|
||||
self.data.view_proj = projection.calc_matrix() * camera.calc_matrix()
|
||||
}
|
||||
|
||||
pub fn update_buffer(&self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder) {
|
||||
let staging_buffer = device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&[self.data]),
|
||||
wgpu::BufferUsage::COPY_SRC,
|
||||
);
|
||||
encoder.copy_buffer_to_buffer(
|
||||
&staging_buffer,
|
||||
0,
|
||||
&self.buffer,
|
||||
0,
|
||||
std::mem::size_of::<UniformData>() as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the wgpu::BindGroupLayout and one wgpu::BindGroup for the
|
||||
* just the Uniforms struct.
|
||||
*/
|
||||
pub struct UniformBinding {
|
||||
pub layout: wgpu::BindGroupLayout,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
impl UniformBinding {
|
||||
pub fn new(device: &wgpu::Device, uniforms: &Uniforms) -> Self {
|
||||
let layout = device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
bindings: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
|
||||
},
|
||||
],
|
||||
label: Some("UniformBinding::layout"),
|
||||
}
|
||||
);
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &layout,
|
||||
bindings: &[
|
||||
wgpu::Binding {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer {
|
||||
buffer: &uniforms.buffer,
|
||||
range: 0..std::mem::size_of_val(&uniforms.data) as _,
|
||||
},
|
||||
},
|
||||
],
|
||||
label: Some("UniformBinding::bind_group")
|
||||
}
|
||||
);
|
||||
|
||||
Self { layout, bind_group }
|
||||
}
|
||||
|
||||
pub fn rebind(&mut self, device: &wgpu::Device, uniforms: &Uniforms) {
|
||||
self.bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout: &self.layout,
|
||||
bindings: &[
|
||||
wgpu::Binding {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer {
|
||||
buffer: &uniforms.buffer,
|
||||
range: 0..std::mem::size_of_val(&uniforms) as wgpu::BufferAddress,
|
||||
},
|
||||
},
|
||||
],
|
||||
label: Some("UniformBinding::bind_group")
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Demo: 'static + Sized {
|
||||
fn init(display: &Display) -> Result<Self, Error>;
|
||||
fn process_mouse(&mut self, dx: f64, dy: f64);
|
||||
fn resize(&mut self, display: &Display);
|
||||
fn update(&mut self, display: &Display, dt: Duration);
|
||||
fn render(&mut self, display: &mut Display);
|
||||
}
|
||||
|
||||
pub async fn run<D: Demo>() -> Result<(), Error> {
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title(env!("CARGO_PKG_NAME"))
|
||||
.build(&event_loop)?;
|
||||
let mut display = Display::new(&window).await?;
|
||||
let mut demo = D::init(&mut display)?;
|
||||
let mut last_update = Instant::now();
|
||||
let mut is_resumed = true;
|
||||
let mut is_focused = true;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = if is_resumed && is_focused {
|
||||
ControlFlow::Poll
|
||||
} else {
|
||||
ControlFlow::Wait
|
||||
};
|
||||
|
||||
match event {
|
||||
Event::Resumed => is_resumed = true,
|
||||
Event::Suspended => is_resumed = false,
|
||||
Event::RedrawRequested(wid) => if wid == window.id() {
|
||||
let now = Instant::now();
|
||||
let dt = now - last_update;
|
||||
last_update = now;
|
||||
|
||||
demo.update(&mut display, dt);
|
||||
demo.render(&mut display);
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
if is_focused && is_resumed {
|
||||
window.request_redraw();
|
||||
} else {
|
||||
// Freeze time while the demo is not in the foreground
|
||||
last_update = Instant::now();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event,
|
||||
window_id,
|
||||
..
|
||||
} => if window_id == window.id() {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::Focused(f) => is_focused = f,
|
||||
WindowEvent::ScaleFactorChanged {
|
||||
new_inner_size,
|
||||
..
|
||||
} => {
|
||||
display.resize(new_inner_size.width, new_inner_size.height);
|
||||
demo.resize(&mut display);
|
||||
}
|
||||
WindowEvent::Resized(new_inner_size) => {
|
||||
display.resize(new_inner_size.width, new_inner_size.height);
|
||||
demo.resize(&mut display);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
use cgmath::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct LightData {
|
||||
pub position: Vector4<f32>,
|
||||
pub color: Vector4<f32>,
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for LightData {}
|
||||
unsafe impl bytemuck::Zeroable for LightData {}
|
||||
|
||||
pub struct Light {
|
||||
data: LightData,
|
||||
buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl Light {
|
||||
pub fn new(device: &wgpu::Device, position: Vector3<f32>, color: Vector3<f32>) -> Self {
|
||||
let data = LightData {
|
||||
position: Vector4::new(position.x, position.y, position.z, 1.0),
|
||||
color: Vector4::new(color.x, color.y, color.z, 1.0),
|
||||
};
|
||||
let buffer = device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&[data]),
|
||||
wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM,
|
||||
);
|
||||
|
||||
Self { data, buffer }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LightBinding {
|
||||
pub layout: wgpu::BindGroupLayout,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
use anyhow::*;
|
||||
use crate::model::Vertex;
|
||||
|
||||
pub struct RenderPipelineBuilder<'a> {
|
||||
layout: Option<&'a wgpu::PipelineLayout>,
|
||||
vertex_shader: Option<&'a [u8]>,
|
||||
fragment_shader: Option<&'a [u8]>,
|
||||
front_face: wgpu::FrontFace,
|
||||
cull_mode: wgpu::CullMode,
|
||||
depth_bias: i32,
|
||||
depth_bias_slope_scale: f32,
|
||||
depth_bias_clamp: f32,
|
||||
primitive_topology: wgpu::PrimitiveTopology,
|
||||
color_states: Vec<wgpu::ColorStateDescriptor>,
|
||||
depth_stencil_state: Option<wgpu::DepthStencilStateDescriptor>,
|
||||
index_format: wgpu::IndexFormat,
|
||||
vertex_buffers: Vec<wgpu::VertexBufferDescriptor<'a>>,
|
||||
sample_count: u32,
|
||||
sample_mask: u32,
|
||||
alpha_to_coverage_enabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> RenderPipelineBuilder<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
layout: None,
|
||||
vertex_shader: None,
|
||||
fragment_shader: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: wgpu::CullMode::None,
|
||||
depth_bias: 0,
|
||||
depth_bias_slope_scale: 0.0,
|
||||
depth_bias_clamp: 0.0,
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: Vec::new(),
|
||||
depth_stencil_state: None,
|
||||
index_format: wgpu::IndexFormat::Uint32,
|
||||
vertex_buffers: Vec::new(),
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layout(&mut self, layout: &'a wgpu::PipelineLayout) -> &mut Self {
|
||||
self.layout = Some(layout);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vertex_shader(&mut self, spv: &'a [u8]) -> &mut Self {
|
||||
self.vertex_shader = Some(spv);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fragment_shader(&mut self, spv: &'a [u8]) -> &mut Self {
|
||||
self.fragment_shader = Some(spv);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn front_face(&mut self, ff: wgpu::FrontFace) -> &mut Self {
|
||||
self.front_face = ff;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cull_mode(&mut self, cm: wgpu::CullMode) -> &mut Self {
|
||||
self.cull_mode = cm;
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_bias(&mut self, db: i32) -> &mut Self {
|
||||
self.depth_bias = db;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_bias_slope_scale(&mut self, dbss: f32) -> &mut Self {
|
||||
self.depth_bias_slope_scale = dbss;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn depth_bias_clamp(&mut self, dbc: f32) -> &mut Self {
|
||||
self.depth_bias_clamp = dbc;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn primitive_topology(&mut self, pt: wgpu::PrimitiveTopology) -> &mut Self {
|
||||
self.primitive_topology = pt;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color_state(&mut self, cs: wgpu::ColorStateDescriptor) -> &mut Self {
|
||||
self.color_states.push(cs);
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper method for [RenderPipelineBuilder::color_state]
|
||||
pub fn color_solid(&mut self, format: wgpu::TextureFormat) -> &mut Self {
|
||||
self.color_state(
|
||||
wgpu::ColorStateDescriptor {
|
||||
format,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn depth_stencil_state(&mut self, dss: wgpu::DepthStencilStateDescriptor) -> &mut Self {
|
||||
self.depth_stencil_state = Some(dss);
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper method for [RenderPipelineBuilder::depth_stencil_state]
|
||||
pub fn depth_no_stencil(
|
||||
&mut self,
|
||||
format: wgpu::TextureFormat,
|
||||
depth_write_enabled: bool,
|
||||
depth_compare: wgpu::CompareFunction,
|
||||
) -> &mut Self {
|
||||
self.depth_stencil_state(
|
||||
wgpu::DepthStencilStateDescriptor {
|
||||
format,
|
||||
depth_write_enabled,
|
||||
depth_compare,
|
||||
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
|
||||
stencil_read_mask: 0,
|
||||
stencil_write_mask: 0,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Helper method for [RenderPipelineBuilder::depth_no_stencil]
|
||||
pub fn depth_format(&mut self, format: wgpu::TextureFormat) -> &mut Self {
|
||||
self.depth_no_stencil(format, true, wgpu::CompareFunction::Less)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn index_format(&mut self, ifmt: wgpu::IndexFormat) -> &mut Self {
|
||||
self.index_format = ifmt;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vertex_buffer<V: Vertex>(&mut self) -> &mut Self {
|
||||
self.vertex_buffers.push(V::desc());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn vertex_buffer_desc(&mut self, vb: wgpu::VertexBufferDescriptor<'a>) -> &mut Self {
|
||||
self.vertex_buffers.push(vb);
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn sample_count(&mut self, sc: u32) -> &mut Self {
|
||||
self.sample_count = sc;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn sample_mask(&mut self, sm: u32) -> &mut Self {
|
||||
self.sample_mask = sm;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn alpha_to_coverage_enabled(&mut self, atce: bool) -> &mut Self {
|
||||
self.alpha_to_coverage_enabled = atce;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self, device: &wgpu::Device) -> Result<wgpu::RenderPipeline> {
|
||||
// We need a layout
|
||||
if self.layout.is_none() {
|
||||
bail!("No pipeline layout supplied!");
|
||||
}
|
||||
let layout = self.layout.unwrap();
|
||||
|
||||
// Render pipelines always have a vertex shader, but due
|
||||
// to the way the builder pattern works, we can't
|
||||
// guarantee that the user will specify one, so we'll
|
||||
// just return an error if they forgot.
|
||||
//
|
||||
// We could supply a default one, but a "default" vertex
|
||||
// could take on many forms. An error is much more
|
||||
// explicit.
|
||||
if self.vertex_shader.is_none() {
|
||||
bail!("No vertex shader supplied!")
|
||||
}
|
||||
let vs = create_shader_module(device, self.vertex_shader.context("Please include a vertex shader")?);
|
||||
|
||||
// The fragment shader is optional (IDK why, but it is).
|
||||
// Having the shader be optional is giving me issues with
|
||||
// the borrow checker so I'm going to use a default shader
|
||||
// if the user doesn't supply one.
|
||||
let fs_spv = self.fragment_shader.context("Please include a fragment shader")?;
|
||||
let fs = create_shader_module(device, fs_spv);
|
||||
|
||||
let pipeline = device.create_render_pipeline(
|
||||
&wgpu::RenderPipelineDescriptor {
|
||||
layout: &layout,
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
module: &vs,
|
||||
entry_point: "main",
|
||||
},
|
||||
fragment_stage: Some(
|
||||
wgpu::ProgrammableStageDescriptor {
|
||||
module: &fs,
|
||||
entry_point: "main",
|
||||
}
|
||||
),
|
||||
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
|
||||
front_face: self.front_face,
|
||||
cull_mode: self.cull_mode,
|
||||
depth_bias: self.depth_bias,
|
||||
depth_bias_slope_scale: self.depth_bias_slope_scale,
|
||||
depth_bias_clamp: self.depth_bias_clamp,
|
||||
}),
|
||||
primitive_topology: self.primitive_topology,
|
||||
color_states: &self.color_states,
|
||||
depth_stencil_state: self.depth_stencil_state.clone(),
|
||||
vertex_state: wgpu::VertexStateDescriptor {
|
||||
index_format: self.index_format,
|
||||
vertex_buffers: &self.vertex_buffers,
|
||||
},
|
||||
sample_count: self.sample_count,
|
||||
sample_mask: self.sample_mask,
|
||||
alpha_to_coverage_enabled: self.alpha_to_coverage_enabled,
|
||||
}
|
||||
);
|
||||
Ok(pipeline)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn create_shader_module(device: &wgpu::Device, spirv: &[u8]) -> wgpu::ShaderModule {
|
||||
device.create_shader_module(
|
||||
&wgpu::read_spirv(
|
||||
std::io::Cursor::new(
|
||||
spirv
|
||||
)
|
||||
).unwrap()
|
||||
)
|
||||
}
|
@ -0,0 +1 @@
|
||||
pub use crate::model::{DrawLight, DrawModel};
|
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "instancing"
|
||||
version = "0.1.0"
|
||||
authors = ["Ben Hansen <bhbenjaminhansen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bytemuck = "1.3"
|
||||
image = "0.23"
|
||||
framework = { path = "../framework"}
|
||||
futures = "0.3"
|
||||
rand = "0.7"
|
||||
tobj = "2"
|
||||
winit = "0.22"
|
||||
wgpu = "0.5"
|
||||
|
||||
[dependencies.cgmath]
|
||||
version = "0.17"
|
||||
features = ["swizzle"]
|
||||
|
||||
[build-dependencies]
|
||||
failure = "0.1"
|
||||
fs_extra = "1.1"
|
||||
glob = "0.3"
|
||||
shaderc = "0.6"
|
||||
|
||||
[[bin]]
|
||||
name = "instancing-storage-buffers"
|
||||
path = "src/storage_buffers.rs"
|
@ -0,0 +1,88 @@
|
||||
use glob::glob;
|
||||
use failure::bail;
|
||||
use fs_extra::copy_items;
|
||||
use fs_extra::dir::CopyOptions;
|
||||
use std::env;
|
||||
use std::fs::{read_to_string, write};
|
||||
use std::path::{PathBuf};
|
||||
|
||||
fn main() {
|
||||
copy_res();
|
||||
compile_shaders();
|
||||
}
|
||||
|
||||
fn copy_res() {
|
||||
// This tells cargo to rerun this script if something in /res/ changes.
|
||||
println!("cargo:rerun-if-changed=res/*");
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let mut copy_options = CopyOptions::new();
|
||||
copy_options.overwrite = true;
|
||||
let mut paths_to_copy = Vec::new();
|
||||
paths_to_copy.push("res/");
|
||||
copy_items(&paths_to_copy, out_dir, ©_options).unwrap();
|
||||
}
|
||||
|
||||
fn compile_shaders() {
|
||||
// This tells cargo to rerun this script if something in /src/ changes.
|
||||
println!("cargo:rerun-if-changed=src/*");
|
||||
|
||||
// Collect all shaders recursively within /src/
|
||||
let mut shader_paths = [
|
||||
glob("./src/**/*.vert").unwrap(),
|
||||
glob("./src/**/*.frag").unwrap(),
|
||||
glob("./src/**/*.comp").unwrap(),
|
||||
];
|
||||
|
||||
// This could be parallelized
|
||||
let shaders = shader_paths.iter_mut()
|
||||
.flatten()
|
||||
.map(|glob_result| {
|
||||
ShaderData::load(glob_result.unwrap()).unwrap()
|
||||
})
|
||||
.collect::<Vec<ShaderData>>();
|
||||
|
||||
let mut compiler = shaderc::Compiler::new().unwrap();
|
||||
|
||||
// This can't be parallelized. The [shaderc::Compiler] is not
|
||||
// thread safe. Also, it creates a lot of resources. You could
|
||||
// spawn multiple processes to handle this, but it would probably
|
||||
// be better just to only compile shaders that have been changed
|
||||
// recently.
|
||||
for shader in shaders {
|
||||
let compiled = compiler.compile_into_spirv(
|
||||
&shader.src,
|
||||
shader.kind,
|
||||
&shader.src_path.to_str().unwrap(),
|
||||
"main",
|
||||
None
|
||||
).unwrap();
|
||||
write(shader.spv_path, compiled.as_binary_u8()).unwrap();
|
||||
}
|
||||
|
||||
// panic!("Debugging...");
|
||||
}
|
||||
|
||||
struct ShaderData {
|
||||
src: String,
|
||||
src_path: PathBuf,
|
||||
spv_path: PathBuf,
|
||||
kind: shaderc::ShaderKind,
|
||||
}
|
||||
|
||||
impl ShaderData {
|
||||
pub fn load(src_path: PathBuf) -> Result<Self, failure::Error> {
|
||||
let extension = src_path.extension().unwrap().to_str().unwrap();
|
||||
let kind = match extension {
|
||||
"vert" => shaderc::ShaderKind::Vertex,
|
||||
"frag" => shaderc::ShaderKind::Fragment,
|
||||
"comp" => shaderc::ShaderKind::Compute,
|
||||
_ => bail!("Unsupported shader: {}", src_path.display()),
|
||||
};
|
||||
|
||||
let src = read_to_string(src_path.clone())?;
|
||||
let spv_path = src_path.with_extension(format!("{}.spv", extension));
|
||||
|
||||
Ok(Self { src, src_path, spv_path, kind })
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
@ -0,0 +1,14 @@
|
||||
# Blender MTL File: 'cube.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material.001
|
||||
Ns 323.999994
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.800000 0.800000 0.800000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Bump cube-normal.png
|
||||
map_Kd cube-diffuse.jpg
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
layout(location=0) in vec3 v_color;
|
||||
layout(location=0) out vec4 f_color;
|
||||
|
||||
void main() {
|
||||
f_color = vec4(v_color, 1.0);
|
||||
}
|
Binary file not shown.
@ -0,0 +1,27 @@
|
||||
#version 450
|
||||
|
||||
layout(location=0) in vec3 a_position;
|
||||
|
||||
layout(location=0) out vec3 v_color;
|
||||
|
||||
layout(set=0, binding=0)
|
||||
uniform Uniforms {
|
||||
vec3 u_view_position;
|
||||
mat4 u_view_proj;
|
||||
};
|
||||
|
||||
layout(set=1, binding=0)
|
||||
uniform Light {
|
||||
vec3 u_position;
|
||||
vec3 u_color;
|
||||
};
|
||||
|
||||
// Let's keep our light smaller than our other objects
|
||||
float scale = 0.25;
|
||||
|
||||
void main() {
|
||||
vec3 v_position = a_position * scale + u_position;
|
||||
gl_Position = u_view_proj * vec4(v_position, 1);
|
||||
|
||||
v_color = u_color;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,262 @@
|
||||
use anyhow::Result;
|
||||
use cgmath::*;
|
||||
use rand::Rng;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
use framework::prelude::*;
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
struct InstanceData {
|
||||
model_matrix: Matrix4<f32>,
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for InstanceData {}
|
||||
unsafe impl bytemuck::Zeroable for InstanceData {}
|
||||
|
||||
struct StorageBuffersDemo<'a> {
|
||||
depth_texture: framework::Texture<'a>,
|
||||
cube_model: framework::Model<'a>,
|
||||
model_pipeline: wgpu::RenderPipeline,
|
||||
instances: Vec<InstanceData>,
|
||||
instance_buffer: wgpu::Buffer,
|
||||
instance_bind_group: wgpu::BindGroup,
|
||||
uniforms: framework::Uniforms,
|
||||
uniform_binding: framework::UniformBinding,
|
||||
camera: framework::Camera,
|
||||
controller: framework::CameraController,
|
||||
projection: framework::Projection,
|
||||
}
|
||||
|
||||
impl framework::Demo for StorageBuffersDemo<'static> {
|
||||
fn init(display: &framework::Display) -> Result<Self> {
|
||||
let texture_layout = display.device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
bindings: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::SampledTexture {
|
||||
multisampled: false,
|
||||
component_type: wgpu::TextureComponentType::Float,
|
||||
dimension: wgpu::TextureViewDimension::D2,
|
||||
}
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler { comparison: false },
|
||||
},
|
||||
// normal map
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::SampledTexture {
|
||||
multisampled: false,
|
||||
component_type: wgpu::TextureComponentType::Float,
|
||||
dimension: wgpu::TextureViewDimension::D2,
|
||||
},
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 3,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler { comparison: false },
|
||||
},
|
||||
],
|
||||
label: Some("texture_layout")
|
||||
}
|
||||
);
|
||||
|
||||
let depth_texture = framework::Texture::create_depth_texture(&display.device, &display.sc_desc);
|
||||
|
||||
let mut res_cmds = Vec::new();
|
||||
let res_dir = Path::new(env!("OUT_DIR")).join("res");
|
||||
let (cube_model, cmds) = framework::Model::load(
|
||||
&display.device,
|
||||
&texture_layout,
|
||||
res_dir.join("cube.obj")
|
||||
)?;
|
||||
res_cmds.extend(cmds);
|
||||
|
||||
let mut encoder = display.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("init::encoder")
|
||||
}
|
||||
);
|
||||
|
||||
let (camera, projection, controller) = framework::camera_setup(
|
||||
(0.0, 5.0, 10.0),
|
||||
cgmath::Deg(-90.0),
|
||||
cgmath::Deg(-20.0),
|
||||
display.sc_desc.width,
|
||||
display.sc_desc.height,
|
||||
);
|
||||
|
||||
let mut uniforms = framework::Uniforms::new(&display.device);
|
||||
uniforms.update_view_proj(&camera, &projection);
|
||||
uniforms.update_buffer(&display.device, &mut encoder);
|
||||
let uniform_binding = framework::UniformBinding::new(&display.device, &uniforms);
|
||||
|
||||
const NUM_INSTANCES: u32 = 100;
|
||||
const RADIUS: f32 = 50.0;
|
||||
let instances = (0..NUM_INSTANCES).map(|_| {
|
||||
let mut rng = rand::thread_rng();
|
||||
let position = Vector3::new(
|
||||
rng.gen_range(-RADIUS, RADIUS),
|
||||
rng.gen_range(-RADIUS, RADIUS),
|
||||
rng.gen_range(-RADIUS, RADIUS),
|
||||
);
|
||||
let model_matrix = Matrix4::from_translation(position);
|
||||
|
||||
InstanceData { model_matrix }
|
||||
}).collect::<Vec<_>>();
|
||||
let instance_buffer = display.device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&instances),
|
||||
wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::STORAGE_READ,
|
||||
);
|
||||
let instance_layout = display.device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("instance_layout"),
|
||||
bindings: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::VERTEX,
|
||||
ty: wgpu::BindingType::StorageBuffer {
|
||||
dynamic: false,
|
||||
readonly: true,
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
);
|
||||
let instance_buffer_size = instances.len() * std::mem::size_of::<InstanceData>();
|
||||
let instance_bind_group = display.device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
label: Some("instance_bind_group"),
|
||||
layout: &instance_layout,
|
||||
bindings: &[
|
||||
wgpu::Binding {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Buffer {
|
||||
buffer: &instance_buffer,
|
||||
range: 0..instance_buffer_size as _,
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
let model_layout = display.device.create_pipeline_layout(
|
||||
&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[
|
||||
&texture_layout,
|
||||
&uniform_binding.layout,
|
||||
&instance_layout,
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
let model_pipeline = framework::RenderPipelineBuilder::new()
|
||||
.layout(&model_layout)
|
||||
.depth_format(framework::Texture::DEPTH_FORMAT)
|
||||
.color_solid(display.sc_desc.format)
|
||||
.vertex_buffer::<framework::ModelVertex>()
|
||||
.vertex_shader(include_bytes!("shader.vert.spv"))
|
||||
.fragment_shader(include_bytes!("shader.frag.spv"))
|
||||
.build(&display.device)?;
|
||||
|
||||
res_cmds.push(encoder.finish());
|
||||
display.queue.submit(&res_cmds);
|
||||
|
||||
|
||||
Ok(Self {
|
||||
depth_texture,
|
||||
cube_model,
|
||||
model_pipeline,
|
||||
instances,
|
||||
instance_buffer,
|
||||
instance_bind_group,
|
||||
uniforms,
|
||||
uniform_binding,
|
||||
camera,
|
||||
controller,
|
||||
projection,
|
||||
})
|
||||
}
|
||||
|
||||
fn process_mouse(&mut self, dx: f64, dy: f64) {
|
||||
self.controller.process_mouse(dx, dy);
|
||||
}
|
||||
|
||||
fn resize(&mut self, display: &framework::Display) {
|
||||
|
||||
}
|
||||
|
||||
fn update(&mut self, display: &framework::Display, dt: Duration) {
|
||||
self.controller.update_camera(&mut self.camera, dt);
|
||||
self.uniforms.update_view_proj(&self.camera, &self.projection);
|
||||
|
||||
let mut encoder = display.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("update::encoder")
|
||||
}
|
||||
);
|
||||
self.uniforms.update_buffer(&display.device, &mut encoder);
|
||||
|
||||
display.queue.submit(&[encoder.finish()]);
|
||||
}
|
||||
|
||||
fn render(&mut self, display: &mut framework::Display) {
|
||||
let mut encoder = display.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor { label: Some("render::encoder")}
|
||||
);
|
||||
let mut frame = display.swap_chain.get_next_texture().expect("Timeout");
|
||||
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(
|
||||
&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[
|
||||
wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: &frame.view,
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.2,
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
},
|
||||
}
|
||||
],
|
||||
depth_stencil_attachment: Some(
|
||||
wgpu::RenderPassDepthStencilAttachmentDescriptor {
|
||||
attachment: &self.depth_texture.view,
|
||||
depth_load_op: wgpu::LoadOp::Clear,
|
||||
depth_store_op: wgpu::StoreOp::Store,
|
||||
clear_depth: 1.0,
|
||||
stencil_load_op: wgpu::LoadOp::Clear,
|
||||
stencil_store_op: wgpu::StoreOp::Store,
|
||||
clear_stencil: 0,
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
pass.set_pipeline(&self.model_pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_binding.bind_group, &[]);
|
||||
// pass.set_bind_group(1, &self.light_binding.bind_group, &[]);
|
||||
for mesh in &self.cube_model.meshes {
|
||||
let mat = &self.cube_model.materials[mesh.material];
|
||||
pass.set_bind_group(2, &mat.bind_group, &[]);
|
||||
pass.draw_indexed(0..mesh.num_elements, 0, 0..self.instances.len() as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() -> Result<()> {
|
||||
futures::executor::block_on(framework::run::<StorageBuffersDemo>())
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit 037a594e0406e14c88976ac1c486234b81d6a0b5
|
||||
Subproject commit 6580a9a0350cfa2852a9abf4af433e41f7fe3448
|
Loading…
Reference in New Issue