finished cleanup

pull/126/head
Ben Hansen 4 years ago
parent 7e9f305f7b
commit f1d35b6b38

@ -15,7 +15,7 @@ mod pipeline; // NEW!
mod texture;
use model::{DrawLight, DrawModel, Vertex};
use pipeline::{create_render_pipeline};
use pipeline::create_render_pipeline;
const NUM_INSTANCES_PER_ROW: u32 = 10;
@ -50,7 +50,8 @@ impl Instance {
fn to_raw(&self) -> InstanceRaw {
InstanceRaw {
model: (cgmath::Matrix4::from_translation(self.position)
* cgmath::Matrix4::from(self.rotation)).into(),
* cgmath::Matrix4::from(self.rotation))
.into(),
}
}
}
@ -139,7 +140,6 @@ struct State {
mouse_pressed: bool,
}
impl State {
async fn new(window: &Window) -> Self {
let size = window.inner_size();
@ -270,28 +270,24 @@ impl State {
let uniform_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
},
count: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
},
],
count: None,
}],
label: Some("uniform_bind_group_layout"),
});
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &uniform_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
},
],
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(uniform_buffer.slice(..)),
}],
label: Some("uniform_bind_group"),
});
@ -299,13 +295,14 @@ impl State {
let model_loader = model::ModelLoader::new(&device);
// UPDATED!
let obj_model = model_loader.load(
&device,
&queue,
&texture_bind_group_layout,
res_dir.join("cube.obj"),
)
.unwrap();
let obj_model = model_loader
.load(
&device,
&queue,
&texture_bind_group_layout,
res_dir.join("cube.obj"),
)
.unwrap();
let light = Light {
position: [2.0, 2.0, 2.0],
@ -503,25 +500,24 @@ impl State {
let old_position: cgmath::Vector3<_> = self.light.position.into();
self.light.position =
(cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0))
* old_position).into();
* old_position)
.into();
self.queue
.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light]));
}
fn render(&mut self) {
let frame = self
.swap_chain
.get_current_frame();
let frame = self.swap_chain.get_current_frame();
match frame {
Ok(frame) => {
let frame = frame.output;
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
let mut encoder =
self.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
@ -537,16 +533,18 @@ impl State {
store: true,
},
}],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
depth_stencil_attachment: Some(
wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
},
),
});
render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..));
render_pass.set_pipeline(&self.light_render_pipeline);
render_pass.draw_light_model(
@ -554,7 +552,7 @@ impl State {
&self.uniform_bind_group,
&self.light_bind_group,
);
render_pass.set_pipeline(&self.render_pipeline);
render_pass.draw_model_instanced(
&self.obj_model,
@ -570,7 +568,6 @@ impl State {
}
}
}
}
fn main() {

@ -4,8 +4,8 @@ use std::ops::Range;
use std::path::Path;
use wgpu::util::DeviceExt;
use crate::texture;
use crate::pipeline;
use crate::texture;
pub trait Vertex {
fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a>;
@ -21,7 +21,6 @@ pub struct ModelVertex {
bitangent: [f32; 3],
}
impl Vertex for ModelVertex {
fn desc<'a>() -> wgpu::VertexBufferDescriptor<'a> {
use std::mem;
@ -182,7 +181,7 @@ impl pipeline::Bindable for BitangentComputeBinding {
min_binding_size: None,
},
count: None,
}
},
]
}
@ -207,7 +206,7 @@ impl pipeline::Bindable for BitangentComputeBinding {
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Buffer(self.info_buffer.slice(..)),
}
},
]
}
}
@ -224,12 +223,16 @@ pub struct ModelLoader {
// UPDATED!
impl ModelLoader {
// NEW!
pub fn new(device: &wgpu::Device) -> Self {
let binder = pipeline::Binder::new(device, Some("ModelLoader Binder"));
let shader_src = wgpu::include_spirv!("model_load.comp.spv");
let pipeline = pipeline::create_compute_pipeline(device, &[&binder.layout], shader_src, Some("ModelLoader ComputePipeline"));
let pipeline = pipeline::create_compute_pipeline(
device,
&[&binder.layout],
shader_src,
Some("ModelLoader ComputePipeline"),
);
Self { binder, pipeline }
}
@ -303,18 +306,20 @@ impl ModelLoader {
})
.collect::<Vec<_>>();
let src_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices),
// UPDATED!
usage: wgpu::BufferUsage::STORAGE,
});
let dst_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices),
// UPDATED!
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::STORAGE,
});
let src_vertex_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices),
// UPDATED!
usage: wgpu::BufferUsage::STORAGE,
});
let dst_vertex_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices),
// UPDATED!
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::STORAGE,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", m.name)),
contents: bytemuck::cast_slice(&m.mesh.indices),
@ -342,11 +347,9 @@ impl ModelLoader {
};
// Calculate the tangents and bitangents
let calc_bind_group = self.binder.create_bind_group(
&binding,
device,
Some("Mesh BindGroup")
);
let calc_bind_group =
self.binder
.create_bind_group(&binding, device, Some("Mesh BindGroup"));
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Tangent and Bitangent Calc"),
});

@ -20,7 +20,12 @@ impl<T: Bindable> Binder<T> {
}
}
pub fn create_bind_group(&self, data: &T, device: &wgpu::Device, label: Option<&str>) -> wgpu::BindGroup {
pub fn create_bind_group(
&self,
data: &T,
device: &wgpu::Device,
label: Option<&str>,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label,
layout: &self.layout,
@ -84,10 +89,10 @@ pub fn create_render_pipeline(
}
pub fn create_compute_pipeline(
device: &wgpu::Device,
bind_group_layouts: &[&wgpu::BindGroupLayout],
shader_src: wgpu::ShaderModuleSource,
label: Option<&str>
device: &wgpu::Device,
bind_group_layouts: &[&wgpu::BindGroupLayout],
shader_src: wgpu::ShaderModuleSource,
label: Option<&str>,
) -> wgpu::ComputePipeline {
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label,
@ -99,8 +104,8 @@ pub fn create_compute_pipeline(
layout: Some(&layout),
compute_stage: wgpu::ProgrammableStageDescriptor {
module: &device.create_shader_module(shader_src),
entry_point: "main"
entry_point: "main",
},
});
pipeline
}
}

@ -4,16 +4,16 @@ mod light;
mod model;
mod pipeline;
pub mod prelude;
mod texture;
mod shader_canvas;
mod texture;
pub use buffer::*;
pub use camera::*;
pub use light::*;
pub use model::*;
pub use pipeline::*;
pub use texture::*;
pub use shader_canvas::*;
pub use texture::*;
use anyhow::*;
use cgmath::*;

@ -7,7 +7,7 @@
use std::time::Instant;
use thiserror::Error;
use wgpu::util::{DeviceExt, BufferInitDescriptor};
use wgpu::util::{BufferInitDescriptor, DeviceExt};
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
@ -29,7 +29,6 @@ pub enum ShaderBuildError {
InvalidDisplayFormat,
}
pub struct ShaderCanvas {
pipeline: wgpu::RenderPipeline,
start_time: Option<Instant>,
@ -51,8 +50,8 @@ impl ShaderCanvas {
}
pub fn render(
&mut self,
queue: &wgpu::Queue,
&mut self,
queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
frame: &wgpu::TextureView,
width: f32,
@ -65,7 +64,7 @@ impl ShaderCanvas {
let t = current_time;
self.start_time = Some(t);
t
},
}
};
let last_time = self.last_time.unwrap_or(current_time);
self.last_time = Some(current_time);
@ -73,19 +72,21 @@ impl ShaderCanvas {
self.simulation_data.delta_time = (current_time - last_time).as_secs_f32();
self.simulation_data.canvas_size[0] = width;
self.simulation_data.canvas_size[1] = height;
queue.write_buffer(&self.simulation_data_buffer, 0, bytemuck::cast_slice(&[self.simulation_data]));
queue.write_buffer(
&self.simulation_data_buffer,
0,
bytemuck::cast_slice(&[self.simulation_data]),
);
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: frame,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
}
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: frame,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
},
],
}],
depth_stencil_attachment: None,
});
pass.set_bind_group(0, &self.simulation_bind_group, &[]);
@ -141,9 +142,17 @@ impl<'a> ShaderCanvasBuilder<'a> {
}
pub fn build(&mut self, device: &wgpu::Device) -> Result<ShaderCanvas, ShaderBuildError> {
let display_format = self.display_format.ok_or(ShaderBuildError::InvalidDisplayFormat)?;
let frag_code = self.frag_code.take().ok_or(ShaderBuildError::InvalidFragmentShader)?;
let vert_code = self.vert_code.take().ok_or(ShaderBuildError::InvalidVertexShader)?;
let display_format = self
.display_format
.ok_or(ShaderBuildError::InvalidDisplayFormat)?;
let frag_code = self
.frag_code
.take()
.ok_or(ShaderBuildError::InvalidFragmentShader)?;
let vert_code = self
.vert_code
.take()
.ok_or(ShaderBuildError::InvalidVertexShader)?;
let simulation_data = SimulationData {
time: 0.0,
@ -158,30 +167,29 @@ impl<'a> ShaderCanvasBuilder<'a> {
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
});
let simulation_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: self.label,
entries: &[
// SimulationData
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
count: None,
ty: wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
}
}
],
});
let simulation_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: self.label,
entries: &[
// SimulationData
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
count: None,
ty: wgpu::BindingType::UniformBuffer {
dynamic: false,
min_binding_size: None,
},
},
],
});
let simulation_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &simulation_bind_group_layout,
label: self.label,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(simulation_data_buffer.slice(..))
}
]
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(simulation_data_buffer.slice(..)),
}],
});
let vert_module = device.create_shader_module(vert_code);

@ -1,7 +1,7 @@
use anyhow::*;
use futures::executor::block_on;
use imgui::*;
use imgui_wgpu::{Renderer, RendererConfig};
use futures::executor::block_on;
use std::time::Duration;
struct ImguiDemo {
@ -21,7 +21,7 @@ impl framework::Demo for ImguiDemo {
let mut imgui = imgui::Context::create();
let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);
platform.attach_window(
imgui.io_mut(),
imgui.io_mut(),
&display.window,
imgui_winit_support::HiDpiMode::Default,
);
@ -37,15 +37,21 @@ impl framework::Demo for ImguiDemo {
size_pixels: font_size,
..Default::default()
}),
}]);
}]);
let renderer_config = RendererConfig {
texture_format: display.sc_desc.format,
..Default::default()
};
let renderer = Renderer::new(&mut imgui, &display.device, &display.queue, renderer_config);
Ok(Self { canvas, imgui, platform, renderer, last_cursor: None })
Ok(Self {
canvas,
imgui,
platform,
renderer,
last_cursor: None,
})
}
fn process_mouse(&mut self, dx: f64, dy: f64) {
@ -89,15 +95,16 @@ impl framework::Demo for ImguiDemo {
eprintln!("Error getting frame: {:?}", e);
return;
}
}.output;
}
.output;
// Render the scene
self.canvas.render(
&display.queue,
&mut encoder,
&output.view,
display.sc_desc.width as f32,
display.sc_desc.height as f32
&display.queue,
&mut encoder,
&output.view,
display.sc_desc.width as f32,
display.sc_desc.height as f32,
);
// Render the UI

@ -52,6 +52,7 @@ module.exports = {
'/showcase/pong/',
'/showcase/compute/',
'/showcase/alignment/',
'/showcase/imgui-demo/',
]
},
'/news/'

@ -10,4 +10,10 @@ Wgpu actually has C bindings to allow you to write C/C++ code with it, as well a
You should be fairly familiar with Rust before using this tutorial as I won't go into much detail on Rust syntax. If you're not super comfortable with Rust you can review the [Rust tutorial](https://www.rust-lang.org/learn). You should also be familiar about [Cargo](https://doc.rust-lang.org/cargo/).
I'm using this project as a way to learn wgpu myself, so I might miss some important details, or explain things wrong. I'm always open to constructive feedback. That being said, let's get started!
I'm using this project as a way to learn wgpu myself, so I might miss some important details, or explain things wrong. I'm always open to constructive feedback.
## Contribution and Support
* I accept pull requests for fixing issues with this tutorial such as typos, incorrect information, and other inconsistencies.
* Due to wgpu's rapidly changing api, I'm not accepting any new pull requests for showcase demos.
* If you want to support me directly, check out my [patreon](https://www.patreon.com/sotrh)!

@ -1,5 +1,23 @@
# News
## November 2020 Cleanup, Content Freeze, and Patreon
School is starting to ramp up, so I haven't had as much time to work on the site as I would like to. Because of that there were some issues piling up. I decided to tackle a bunch of them in one go. Here's a snapshot of what I did:
* The tutorial now handles `SwapChainError` properly
* I'm now using bytemuck's derive feature on all buffer data structs.
* The [instancing tutorial](/beginner/tutorial7-instancing) now uses vertex buffers instead of storage buffers.
* `build.rs` now updates when individual shaders are changed, not whenever `/src` is changed.
* Had some help from Github user @kanerogers to clean up the [texturing tutorial](/beginner/tutorial5-textures).
* I made a [compute pipeline showcase](/showcase/compute) that computes the tangent and bitangent for each vertex in a model.
* I made a [imgui showcase](/showcase/imgui-demo). It's very basic, but it should be a good starting point.
Now in the headline I mentioned a "Content Freeze". Wgpu is still a moving target. The migration from `0.4` to `0.5` was lot of work. The same goes for `0.5` to `0.6`. I'm expected the next migration to be just as much work. As such, I won't be added much content until the API becomes a bit more stable. That being said, I still plan on resolving any issues with the content.
One more thing. This is actually quite awkward for me (especially since I'll be slowing down development), but I've started a [patreon](https://www.patreon.com/sotrh). My job doesn't give me a ton of hours, so things are a bit tight. You are by no means obligated to donate, but I would appreciate it.
You can find out more about contributing to this project on the [introduction page](/#contribution-and-support)
## 0.6
This took me way too long. The changes weren't difficult, but I had to do a lot of copy pasting. The main changes are using `queue.write_buffer()` and `queue.write_texture()` everywhere. I won't get into the nitty gritty, but you can checkout the [pull request](https://github.com/sotrh/learn-wgpu/pull/90) if you're interested.

@ -0,0 +1,135 @@
# Basic Imgui Demo
This is not an in depth guid on how to use Imgui. But here are some of the basics you'll need to get started. We'll need to import [imgui-rs](https://docs.rs/imgui), [imgui-wgpu](https://docs.rs/imgui-wgpu), and [imgui-winit-support](https://docs.rs/imgui-winit-support).
```toml
imgui = "0.6"
imgui-wgpu = "0.12"
imgui-winit-support = "0.6"
```
<div class="note">
I've excluded some dependencies for brevity. I'm also using the [framework crate](https://github.com/sotrh/learn-wgpu/tree/master/code/showcase/framework) I've created for showcases to simplify setup. If you see a `display` variable in code, it's from the `framework`. `Display` is where the the `device`, `queue`, `swap_chain`, and other basic wgpu objects are stored.
</div>
We need to setup imgui and a `WinitPlatform` to get started. Do this after creating you're `winit::Window`.
```rust
let mut imgui = imgui::Context::create();
let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);
platform.attach_window(
imgui.io_mut(),
&display.window,
imgui_winit_support::HiDpiMode::Default,
);
imgui.set_ini_filename(None);
```
Now we need to configure the default font. We'll using the window's scale factor to keep things from being too big or small.
```rust
let hidpi_factor = display.window.scale_factor();
let font_size = (13.0 * hidpi_factor) as f32;
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
imgui.fonts().add_font(&[FontSource::DefaultFontData {
config: Some(imgui::FontConfig {
oversample_h: 1,
pixel_snap_h: true,
size_pixels: font_size,
..Default::default()
}),
}]);
```
Then you need to create the renderer. We need to use the swap chains `TextureFormat` in order for things to work properly.
```rust
let renderer_config = RendererConfig {
texture_format: display.sc_desc.format,
..Default::default()
};
let renderer = Renderer::new(&mut imgui, &display.device, &display.queue, renderer_config);
```
When we update the scene, we'll need to update imgui.
```rust
self.imgui.io_mut().update_delta_time(dt); // dt: std::time::Duration
```
I'm not an expert with imgui, so I'll let the code speak for itself.
```rust
// Build the UI
self.platform
.prepare_frame(self.imgui.io_mut(), &display.window)
.expect("Failed to prepare frame!");
let ui = self.imgui.frame();
{
let window = imgui::Window::new(im_str!("Hello Imgui from WGPU!"));
window
.size([300.0, 100.0], Condition::FirstUseEver)
.build(&ui, || {
ui.text(im_str!("Hello world!"));
ui.text(im_str!("This is a demo of imgui-rs using imgui-wgpu!"));
ui.separator();
let mouse_pos = ui.io().mouse_pos;
ui.text(im_str!(
"Mouse Position: ({:.1}, {:.1})",
mouse_pos[0],
mouse_pos[1],
));
});
}
// Prepare to render
let mut encoder = display.device.create_command_encoder(&Default::default());
let output = match display.swap_chain.get_current_frame() {
Ok(frame) => frame,
Err(e) => {
eprintln!("Error getting frame: {:?}", e);
return;
}
}.output;
// Render the scene
self.canvas.render(
&display.queue,
&mut encoder,
&output.view,
display.sc_desc.width as f32,
display.sc_desc.height as f32
);
// Render the UI
if self.last_cursor != ui.mouse_cursor() {
self.last_cursor = ui.mouse_cursor();
self.platform.prepare_render(&ui, &display.window);
}
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &output.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
},
}],
depth_stencil_attachment: None,
});
self.renderer
.render(ui.render(), &display.queue, &display.device, &mut pass)
.expect("Failed to render UI!");
drop(pass);
display.queue.submit(Some(encoder.finish()));
```
That's all there is to it. Here's a picture of the results!
![./screenshot.png](./screenshot.png)
<AutoGithubLink/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Loading…
Cancel
Save