Update wgpu from 0.4 to 0.5 (currently it's not rendering)

This commit is contained in:
Keavon Chambers
2020-05-02 14:44:28 -07:00
parent b30ee294a6
commit 323a951362
12 changed files with 562 additions and 388 deletions
+129 -72
View File
@@ -1,17 +1,17 @@
// use super::render_state::RenderState;
// use super::program_state::ProgramState;
use super::color_palette::ColorPalette;
use super::gui_rect::GUIRect;
use super::pipeline::Pipeline;
use super::pipeline::PipelineDetails;
use super::shader_cache::ShaderCache;
use super::texture::Texture;
use super::shader_cache::ShaderCache;
use super::pipeline_cache::PipelineCache;
use super::draw_command::DrawCommand;
use std::collections::VecDeque;
use winit::event::*;
use winit::event_loop::ControlFlow;
use winit::event_loop::EventLoop;
use winit::window::Window;
use futures::executor::block_on;
pub struct Application {
pub surface: wgpu::Surface,
@@ -21,9 +21,10 @@ pub struct Application {
pub swap_chain_descriptor: wgpu::SwapChainDescriptor,
pub swap_chain: wgpu::SwapChain,
pub shader_cache: ShaderCache,
pub pipeline_cache: PipelineCache,
// pub texture_cache: TextureCache,
pub gui_rect_queue: VecDeque<GUIRect>,
pub pipeline_queue: VecDeque<Pipeline>,
pub draw_command_queue: VecDeque<DrawCommand>,
pub temp_color_toggle: bool,
}
@@ -33,13 +34,19 @@ impl Application {
let surface = wgpu::Surface::create(window);
// Represents a GPU, exposes the real GPU device and queue
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { ..Default::default() }).unwrap();
let adapter = block_on(wgpu::Adapter::request(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
compatible_surface: Some(&surface),
},
wgpu::BackendBit::PRIMARY,
)).unwrap();
// Requests the device and queue from the adapter
let requested_device = adapter.request_device(&wgpu::DeviceDescriptor {
let requested_device = block_on(adapter.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions { anisotropic_filtering: false },
limits: Default::default(),
});
}));
// Connection to the physical GPU
let device = requested_device.0;
@@ -53,18 +60,19 @@ impl Application {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
width: window.inner_size().width,
height: window.inner_size().height,
present_mode: wgpu::PresentMode::Vsync,
present_mode: wgpu::PresentMode::Fifo,
};
// Series of frame buffers with images presented to the surface
let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor);
// Cache of all loaded shaders
// Cache of all loaded shaders and the Pipeline programs they form
let shader_cache = ShaderCache::new();
let pipeline_cache = PipelineCache::new();
let gui_rect_queue = VecDeque::new();
let pipeline_queue = VecDeque::new();
let draw_command_queue = VecDeque::new();
Self {
surface,
@@ -74,28 +82,59 @@ impl Application {
swap_chain_descriptor,
swap_chain,
shader_cache,
pipeline_cache,
gui_rect_queue,
pipeline_queue,
draw_command_queue,
temp_color_toggle: true,
}
}
pub fn example(&mut self) {
// Example vertex data
const VERTICES: &[[f32; 2]] = &[
[-0.0868241, -0.49240386],
[-0.49513406, -0.06958647],
[-0.21918549, 0.44939706],
[0.35966998, 0.3473291],
[0.44147372, -0.2347359],
];
const INDICES: &[u16] = &[
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
// Load the vertex and fragment shaders
self.shader_cache.load(&self.device, "shaders/shader.vert", glsl_to_spirv::ShaderType::Vertex).unwrap();
self.shader_cache.load(&self.device, "shaders/shader.frag", glsl_to_spirv::ShaderType::Fragment).unwrap();
let vertex_shader = self.shader_cache.get_by_path("shaders/shader.vert").unwrap();
let fragment_shader = self.shader_cache.get_by_path("shaders/shader.frag").unwrap();
let texture_view = Texture::from_filepath(&self.device, &mut self.queue, "textures/grid.png").unwrap().view;
let example_pipeline = Pipeline::new(&self.device, PipelineDetails {
vertex_shader,
fragment_shader,
texture_view: Some(&texture_view),
// Construct a pipeline from the shader pair and a new BindGroup that holds a new TextureView, then store the pipeline in the cache
let example_pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader);
let example_texture_view = Texture::from_filepath(&self.device, &mut self.queue, "textures/grid.png").unwrap().texture_view;
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &example_pipeline.bind_group_layout,
bindings: &[
wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::TextureView(&example_texture_view),
},
// wgpu::Binding {
// binding: 1,
// resource: wgpu::BindingResource::Sampler(&texture.sampler),
// }
],
label: None,
});
let pipeline_id = self.pipeline_cache.set("example", example_pipeline);
self.pipeline_queue.push_back(example_pipeline);
assert_eq!(pipeline_id, super::pipeline_cache::PipelineID::new(0));
// Create a draw command with the vertex data and bind group
let example_draw_command = DrawCommand::new(&self.device, pipeline_id, VERTICES, INDICES, bind_group);
self.draw_command_queue.push_back(example_draw_command);
}
pub fn begin_lifecycle(mut self, event_loop: EventLoop<()>, window: Window) {
@@ -103,31 +142,30 @@ impl Application {
}
pub fn main_event_loop<T>(&mut self, event: Event<'_, T>, control_flow: &mut ControlFlow, window: &Window) {
// Wait for the next event to cause a subsequent event loop run, instead of looping instantly as a game would need
*control_flow = ControlFlow::Wait;
match event {
// Handle all window events in sequence
// Handle all window events (like input and resize) in sequence
Event::WindowEvent { ref event, window_id } if window_id == window.id() => {
self.window_event(event, control_flow);
},
// After handling every event and updating the GUI, request a new sequence of draw commands
// Once every event is handled and the GUI structure is updated, this requests a new sequence of draw commands
Event::MainEventsCleared => {
// Turn the GUI changes into draw commands added to the render pipeline queue
self.redraw();
// If any draw commands were actually added, ask the window to issue a redraw event
if !self.pipeline_queue.is_empty() {
// If any draw commands were actually added, ask the window to dispatch a redraw event
if !self.draw_command_queue.is_empty() {
window.request_redraw();
}
*control_flow = ControlFlow::Wait;
},
// Resizing or calling `window.request_redraw()` now redraws the GUI with the pipeline queue
// Resizing or calling `window.request_redraw()` renders the GUI with the queued draw commands
Event::RedrawRequested(_) => {
self.render();
*control_flow = ControlFlow::Wait;
},
// Catch extraneous events
_ => {
*control_flow = ControlFlow::Wait;
},
}
}
@@ -188,53 +226,72 @@ impl Application {
// Render the queue of pipeline draw commands over the current window
pub fn render(&mut self) {
// Get a frame buffer to render on
let frame = self.swap_chain.get_next_texture().unwrap();
// Generates a render pass that commands are applied to, then generates a command buffer when finished
let mut command_encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
// Temporary way to swap clear color every render
let color = match self.temp_color_toggle {
true => ColorPalette::get_color_linear(ColorPalette::MildBlack),
false => ColorPalette::get_color_linear(ColorPalette::NearBlack),
};
self.temp_color_toggle = !self.temp_color_toggle;
// Recording of commands while in "rendering mode" that go into a command buffer
let mut render_pass = command_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: color,
}
],
depth_stencil_attachment: None,
});
// let mut currently_set_pipeline_id = None;
println!("Draw queue is length {}", self.draw_command_queue.len());
// Turn the queue of pipelines each into a command buffer and submit it to the render queue
while !self.pipeline_queue.is_empty() {
// Get a frame buffer to render on
let frame = self.swap_chain.get_next_texture();
self.draw_command_queue.iter().for_each(|command| {
// // Bind the pipeline required by the current draw command
// let new_pipeline_id = command.pipeline_id;
// if currently_set_pipeline_id == None || new_pipeline_id != currently_set_pipeline_id.unwrap() {
// currently_set_pipeline_id = Some(new_pipeline_id);
// let pipeline = self.pipeline_cache.get_by_id(new_pipeline_id).unwrap();
// render_pass.set_pipeline(&pipeline.render_pipeline);
// println!("Set pipeline");
// }
let pipeline = self.pipeline_cache.get_by_id(command.pipeline_id).unwrap();
render_pass.set_pipeline(&pipeline.render_pipeline);
// Get the pipeline to render in this iteration
let pipeline_struct = self.pipeline_queue.pop_back().unwrap();
// Generates a render pass that commands are applied to, then generates a command buffer when finished
let mut command_encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
// Temporary way to swap clear color every render
let color = match self.temp_color_toggle {
true => ColorPalette::get_color_linear(ColorPalette::MildBlack),
false => ColorPalette::get_color_linear(ColorPalette::NearBlack),
};
self.temp_color_toggle = !self.temp_color_toggle;
// Recording of commands while in "rendering mode" that go into a command buffer
let mut render_pass = command_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: color,
}
],
depth_stencil_attachment: None,
});
// Commands sent to the GPU for drawing during this render pass
render_pass.set_pipeline(&pipeline_struct.render_pipeline);
render_pass.set_vertex_buffers(0, &[(&pipeline_struct.vertex_buffer, 0)]);
render_pass.set_index_buffer(&pipeline_struct.index_buffer, 0);
render_pass.set_bind_group(0, &pipeline_struct.texture_bind_group, &[]);
render_pass.draw_indexed(0..pipeline_struct.index_count, 0, 0..1);
render_pass.set_vertex_buffer(0, &command.vertex_buffer, 0, 0);
render_pass.set_index_buffer(&command.index_buffer, 0, 0);
render_pass.set_bind_group(0, &command.bind_group, &[]);
// Done sending render pass commands so we can give up mutation rights to command_encoder
drop(render_pass);
// Draw call
render_pass.draw_indexed(0..command.index_count, 0, 0..1);
println!("Draw call!");
});
// Turn the recording of commands into a complete command buffer
let command_buffer = command_encoder.finish();
// Submit the command buffer to the GPU command queue
self.queue.submit(&[command_buffer]);
}
// Done sending render pass commands so we can give up mutation rights to command_encoder
drop(render_pass);
// Turn the recording of commands into a complete command buffer
let command_buffer = command_encoder.finish();
// After the draw command queue has been iterated through and used, empty it for use next frame
self.draw_command_queue.clear();
// Submit the command buffer to the GPU command queue
self.queue.submit(&[command_buffer]);
}
}
+25
View File
@@ -0,0 +1,25 @@
use super::pipeline_cache::PipelineID;
pub struct DrawCommand {
pub pipeline_id: PipelineID,
pub bind_group: wgpu::BindGroup,
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer,
pub index_count: u32,
}
impl DrawCommand {
pub fn new(device: &wgpu::Device, pipeline_id: PipelineID, vertices: &[[f32; 2]], indices: &[u16], bind_group: wgpu::BindGroup) -> Self {
let vertex_buffer = device.create_buffer_with_data(bytemuck::cast_slice(vertices), wgpu::BufferUsage::VERTEX);
let index_buffer = device.create_buffer_with_data(bytemuck::cast_slice(indices), wgpu::BufferUsage::INDEX);
let index_count = indices.len() as u32;
Self {
pipeline_id,
bind_group,
vertex_buffer,
index_buffer,
index_count,
}
}
}
+2 -2
View File
@@ -1,10 +1,11 @@
mod application;
mod gui_rect;
mod pipeline;
mod program_state;
mod texture;
mod color_palette;
mod shader_cache;
mod pipeline_cache;
mod draw_command;
use application::Application;
use winit::event_loop::EventLoop;
@@ -23,7 +24,6 @@ fn main() {
// State managers for render pipeline and program logic
// let app_render_state = RenderState::new(&mut app);
// let app_program_state = ProgramState::new(&mut app);
// Begin the application lifecycle
app.begin_lifecycle(event_loop, window);
+26 -70
View File
@@ -1,75 +1,41 @@
pub struct PipelineDetails<'a> {
pub vertex_shader: &'a wgpu::ShaderModule,
pub fragment_shader: &'a wgpu::ShaderModule,
pub texture_view: Option<&'a wgpu::TextureView>,
}
pub struct Pipeline {
pub bind_group_layout: wgpu::BindGroupLayout,
pub render_pipeline: wgpu::RenderPipeline,
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: wgpu::Buffer,
pub index_count: u32,
pub texture_bind_group: wgpu::BindGroup,
}
impl Pipeline {
pub fn new(device: &wgpu::Device, pipeline_details: PipelineDetails) -> Self {
let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
pub fn new(device: &wgpu::Device, vertex_shader: &wgpu::ShaderModule, fragment_shader: &wgpu::ShaderModule) -> Self {
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[
wgpu::BindGroupLayoutBinding {
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::SampledTexture {
multisampled: false,
dimension: wgpu::TextureViewDimension::D2,
component_type: wgpu::TextureComponentType::Float,
multisampled: false,
},
},
// wgpu::BindGroupLayoutBinding {
// wgpu::BindGroupLayoutEntry {
// binding: 1,
// visibility: wgpu::ShaderStage::FRAGMENT,
// ty: wgpu::BindingType::Sampler,
// },
],
});
let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout,
bindings: &[
wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::TextureView(pipeline_details.texture_view.unwrap()),
},
// wgpu::Binding {
// binding: 1,
// resource: wgpu::BindingResource::Sampler(&texture.sampler),
// }
],
label: None,
});
let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&texture_bind_group_layout],
bind_group_layouts: &[&bind_group_layout],
});
let vertex_buffer_descriptors = wgpu::VertexBufferDescriptor {
stride: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &[
wgpu::VertexAttributeDescriptor {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float2,
},
],
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &render_pipeline_layout,
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: pipeline_details.vertex_shader,
module: vertex_shader,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: pipeline_details.fragment_shader,
module: fragment_shader,
entry_point: "main",
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
@@ -79,6 +45,7 @@ impl Pipeline {
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[
wgpu::ColorStateDescriptor {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
@@ -87,39 +54,28 @@ impl Pipeline {
write_mask: wgpu::ColorWrite::ALL,
},
],
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
depth_stencil_state: None,
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[vertex_buffer_descriptors],
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: std::mem::size_of::<[f32; 2]>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &[wgpu::VertexAttributeDescriptor {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float2,
},
],
}],
},
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
let vertex_buffer = device.create_buffer_mapped(VERTICES.len(), wgpu::BufferUsage::VERTEX).fill_from_slice(VERTICES);
let index_buffer = device.create_buffer_mapped(INDICES.len(), wgpu::BufferUsage::INDEX).fill_from_slice(INDICES);
let index_count = INDICES.len() as u32;
Self {
bind_group_layout,
render_pipeline,
vertex_buffer,
index_buffer,
index_count,
texture_bind_group,
}
}
}
const VERTICES: &[[f32; 2]] = &[
[-0.0868241, -0.49240386],
[-0.49513406, -0.06958647],
[-0.21918549, 0.44939706],
[0.35966998, 0.3473291],
[0.44147372, -0.2347359],
];
const INDICES: &[u16] = &[
0, 1, 4,
1, 2, 4,
2, 3, 4,
];
+57
View File
@@ -0,0 +1,57 @@
use std::collections::HashMap;
use super::pipeline::Pipeline;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct PipelineID {
index: usize,
}
impl PipelineID {
pub fn new(index: usize) -> Self {
Self { index }
}
}
pub struct PipelineCache {
pub pipelines: Vec<Pipeline>,
pub name_to_id: HashMap<String, PipelineID>,
}
impl PipelineCache {
pub fn new() -> Self {
let pipelines = Vec::new();
let name_to_id = HashMap::new();
Self {
pipelines,
name_to_id,
}
}
pub fn get_by_name(&self, name: &str) -> Option<&Pipeline> {
match self.name_to_id.get(name) {
Some(id) => self.pipelines.get(id.index),
None => None,
}
}
pub fn get_by_id(&self, id: PipelineID) -> Option<&Pipeline> {
self.pipelines.get(id.index)
}
pub fn set(&mut self, name: &str, pipeline: Pipeline) -> PipelineID {
match self.name_to_id.get(name) {
Some(id) => {
self.pipelines[id.index] = pipeline;
id.clone()
},
None => {
let last_index = self.name_to_id.len();
let id = PipelineID::new(last_index);
self.name_to_id.insert(String::from(name), id);
self.pipelines.push(pipeline);
id
}
}
}
}
-13
View File
@@ -1,13 +0,0 @@
use super::application::Application;
pub struct ProgramState {
}
impl ProgramState {
pub fn new(application: &mut Application) -> ProgramState {
Self {
}
}
}
+10 -4
View File
@@ -1,8 +1,14 @@
use std::collections::HashMap;
#[derive(Copy, Clone)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct ShaderID {
pub index: usize,
index: usize,
}
impl ShaderID {
pub fn new(index: usize) -> Self {
Self { index }
}
}
pub struct ShaderCache {
@@ -39,8 +45,8 @@ impl ShaderCache {
let compiled = wgpu::read_spirv(spirv).unwrap();
let shader = device.create_shader_module(&compiled);
let length = self.path_to_id.len();
self.path_to_id.insert(String::from(path), ShaderID { index: length });
let last_index = self.path_to_id.len();
self.path_to_id.insert(String::from(path), ShaderID { index: last_index });
self.shaders.push(shader);
}
+29 -16
View File
@@ -2,31 +2,43 @@ use image::GenericImageView;
pub struct Texture {
pub texture: wgpu::Texture,
pub view: wgpu::TextureView,
pub texture_view: wgpu::TextureView,
pub sampler: wgpu::Sampler,
}
impl Texture {
pub fn from_filepath(device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str) -> Result<Self, failure::Error> {
// Read the raw bytes from the specified file
let bytes = std::fs::read(path)?;
// Construct and return a Texture from the bytes
Texture::from_bytes(device, queue, &bytes[..])
}
pub fn from_bytes(device: &wgpu::Device, queue: &mut wgpu::Queue, bytes: &[u8]) -> Result<Self, failure::Error> {
let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, &img)
// Create an image with the Image library
let image = image::load_from_memory(bytes)?;
// Construct and return a Texture from the Image
Self::from_image(device, queue, &image)
}
pub fn from_image(device: &wgpu::Device, queue: &mut wgpu::Queue, img: &image::DynamicImage) -> Result<Self, failure::Error> {
let rgba = img.as_rgba8().unwrap();
let dimensions = img.dimensions();
pub fn from_image(device: &wgpu::Device, queue: &mut wgpu::Queue, image: &image::DynamicImage) -> Result<Self, failure::Error> {
// Get data from image
let rgba = image.as_rgba8().unwrap();
let dimensions = image.dimensions();
let size = wgpu::Extent3d {
width: dimensions.0,
height: dimensions.1,
depth: 1,
};
// Create a buffer on the GPU and load it with the image pixel data
let buffer = device.create_buffer_with_data(&rgba, wgpu::BufferUsage::COPY_SRC);
// Create an empty texture on the GPU of the correct size for the buffer
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size,
array_layer_count: 1,
mip_level_count: 1,
@@ -36,16 +48,14 @@ impl Texture {
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
});
let buffer = device.create_buffer_mapped(rgba.len(), wgpu::BufferUsage::COPY_SRC).fill_from_slice(&rgba);
let mut encoder = device.create_command_encoder(&Default::default());
// Use a command encoder to transfer the pixel data buffer into the texture
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
encoder.copy_buffer_to_texture(
wgpu::BufferCopyView {
buffer: &buffer,
offset: 0,
row_pitch: 4 * dimensions.0,
image_height: dimensions.1,
bytes_per_row: 4 * dimensions.0,
rows_per_image: dimensions.1,
},
wgpu::TextureCopyView {
texture: &texture,
@@ -56,9 +66,14 @@ impl Texture {
size,
);
// Finishing the encoding yields the resulting command buffer that is submitted to the GPU's command queue
let command_buffer = encoder.finish();
queue.submit(&[command_buffer]);
// Create the TextureView for this texture
let view = texture.create_default_view();
// Create the Sampler for this texture
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
@@ -68,11 +83,9 @@ impl Texture {
mipmap_filter: wgpu::FilterMode::Nearest,
lod_min_clamp: -100.0,
lod_max_clamp: 100.0,
compare_function: wgpu::CompareFunction::Always,
compare: wgpu::CompareFunction::Always,
});
queue.submit(&[command_buffer]);
Ok(Self { texture, view, sampler })
Ok(Self { texture, texture_view: view, sampler })
}
}