mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-18 18:38:05 +08:00
Refactor to generalize pipeline drawing sequence
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
use super::color_palette::ColorPalette;
|
||||
use super::window_events;
|
||||
use super::pipeline::Pipeline;
|
||||
use super::texture::Texture;
|
||||
use super::shader_stage::compile_from_glsl;
|
||||
use super::resource_cache::ResourceCache;
|
||||
use super::draw_command::DrawCommand;
|
||||
use super::gui_tree::GuiTree;
|
||||
use std::collections::VecDeque;
|
||||
use crate::color_palette::ColorPalette;
|
||||
use crate::window_events;
|
||||
use crate::pipeline::Pipeline;
|
||||
use crate::texture::Texture;
|
||||
use crate::resource_cache::ResourceCache;
|
||||
use crate::draw_command::DrawCommand;
|
||||
use crate::gui_node::GuiNode;
|
||||
use winit::event::*;
|
||||
use winit::event_loop::*;
|
||||
use winit::window::Window;
|
||||
@@ -20,12 +18,9 @@ pub struct Application {
|
||||
pub swap_chain_descriptor: wgpu::SwapChainDescriptor,
|
||||
pub swap_chain: wgpu::SwapChain,
|
||||
pub shader_cache: ResourceCache<wgpu::ShaderModule>,
|
||||
pub bind_group_cache: ResourceCache<wgpu::BindGroup>,
|
||||
pub pipeline_cache: ResourceCache<Pipeline>,
|
||||
pub texture_cache: ResourceCache<Texture>,
|
||||
pub draw_command_queue: VecDeque<DrawCommand>,
|
||||
pub gui_tree: GuiTree,
|
||||
pub temp_color_toggle: bool,
|
||||
pub gui_root: rctree::Node<GuiNode>,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
@@ -67,17 +62,24 @@ impl Application {
|
||||
let swap_chain = device.create_swap_chain(&surface, &swap_chain_descriptor);
|
||||
|
||||
// Resource caches that own the application's shaders, pipelines, and textures
|
||||
let shader_cache = ResourceCache::<wgpu::ShaderModule>::new();
|
||||
let bind_group_cache = ResourceCache::<wgpu::BindGroup>::new();
|
||||
let pipeline_cache = ResourceCache::<Pipeline>::new();
|
||||
let mut shader_cache = ResourceCache::<wgpu::ShaderModule>::new();
|
||||
let mut pipeline_cache = ResourceCache::<Pipeline>::new();
|
||||
let texture_cache = ResourceCache::<Texture>::new();
|
||||
|
||||
// Ordered list of draw commands to send to the GPU on the next frame render
|
||||
let draw_command_queue = VecDeque::new();
|
||||
// Temporary setup below, TODO: move to appropriate place in architecture
|
||||
|
||||
// Data structure maintaining the user interface
|
||||
let gui_tree = GuiTree::new();
|
||||
// Window uniform bind group layout
|
||||
let window_binding_types = vec![wgpu::BindingType::UniformBuffer { dynamic: false }];
|
||||
let window_bind_group_layout = Pipeline::build_bind_group_layout(&device, &window_binding_types);
|
||||
|
||||
// Data structure maintaining the user interface
|
||||
// let extra_layouts = vec![&window_bind_group_layout];
|
||||
let gui_rect_pipeline = Pipeline::new(&device, swap_chain_descriptor.format, vec![], &mut shader_cache, ("shaders/shader.vert", "shaders/shader.frag"));
|
||||
pipeline_cache.set("gui_rect", gui_rect_pipeline);
|
||||
|
||||
let gui_root_data = GuiNode::new(swap_chain_descriptor.width, swap_chain_descriptor.height, ColorPalette::get_color_srgb(ColorPalette::Accent));
|
||||
let gui_root = rctree::Node::new(gui_root_data);
|
||||
|
||||
Self {
|
||||
surface,
|
||||
adapter,
|
||||
@@ -86,85 +88,12 @@ impl Application {
|
||||
swap_chain_descriptor,
|
||||
swap_chain,
|
||||
shader_cache,
|
||||
bind_group_cache,
|
||||
pipeline_cache,
|
||||
texture_cache,
|
||||
draw_command_queue,
|
||||
gui_tree,
|
||||
temp_color_toggle: true,
|
||||
gui_root,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn example(&mut self) {
|
||||
// Example vertex data
|
||||
const VERTICES: &[[f32; 2]] = &[
|
||||
[-0.5, 0.5],
|
||||
[0.5, 0.5],
|
||||
[0.5, 1.0],
|
||||
[-0.5, 1.0],
|
||||
];
|
||||
const INDICES: &[u16] = &[
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
];
|
||||
|
||||
// If uncached, construct a vertex shader loaded from its source code file
|
||||
let vertex_shader_path = "shaders/shader.vert";
|
||||
if self.shader_cache.get(vertex_shader_path).is_none() {
|
||||
let vertex_shader_module = compile_from_glsl(&self.device, vertex_shader_path, glsl_to_spirv::ShaderType::Vertex).unwrap();
|
||||
self.shader_cache.set(vertex_shader_path, vertex_shader_module);
|
||||
}
|
||||
|
||||
// If uncached, construct a fragment shader loaded from its source code file
|
||||
let fragment_shader_path = "shaders/shader.frag";
|
||||
if self.shader_cache.get(fragment_shader_path).is_none() {
|
||||
let fragment_shader_module = compile_from_glsl(&self.device, fragment_shader_path, glsl_to_spirv::ShaderType::Fragment).unwrap();
|
||||
self.shader_cache.set(fragment_shader_path, fragment_shader_module);
|
||||
}
|
||||
|
||||
// Get the shader pair
|
||||
let vertex_shader = self.shader_cache.get(vertex_shader_path).unwrap();
|
||||
let fragment_shader = self.shader_cache.get(fragment_shader_path).unwrap();
|
||||
|
||||
// If uncached, construct a pipeline from the shader pair
|
||||
let pipeline_name = "example-pipeline";
|
||||
if self.pipeline_cache.get(pipeline_name).is_none() {
|
||||
let bind_group_layout_binding_types = vec![
|
||||
wgpu::BindingType::SampledTexture {
|
||||
dimension: wgpu::TextureViewDimension::D2,
|
||||
component_type: wgpu::TextureComponentType::Float,
|
||||
multisampled: false,
|
||||
},
|
||||
// ty: wgpu::BindingType::Sampler,
|
||||
];
|
||||
let pipeline = Pipeline::new(&self.device, vertex_shader, fragment_shader, bind_group_layout_binding_types);
|
||||
self.pipeline_cache.set(pipeline_name, pipeline);
|
||||
}
|
||||
let example_pipeline = self.pipeline_cache.get(pipeline_name).unwrap();
|
||||
|
||||
// If uncached, construct a texture loaded from the image file
|
||||
let texture_path = "textures/grid.png";
|
||||
if self.texture_cache.get(texture_path).is_none() {
|
||||
let texture = Texture::from_filepath(&self.device, &mut self.queue, texture_path).unwrap();
|
||||
self.texture_cache.set(texture_path, texture);
|
||||
}
|
||||
let grid_texture = self.texture_cache.get(texture_path).unwrap();
|
||||
|
||||
// If uncached, construct a bind group with resources matching the pipeline's bind group layout
|
||||
let bind_group_name = "example-bindgroup";
|
||||
if self.bind_group_cache.get(bind_group_name).is_none() {
|
||||
let binding_resources = vec![
|
||||
wgpu::BindingResource::TextureView(&grid_texture.texture_view),
|
||||
];
|
||||
let bind_group = example_pipeline.build_bind_group(&self.device, binding_resources);
|
||||
self.bind_group_cache.set(bind_group_name, bind_group);
|
||||
}
|
||||
|
||||
// Create a draw command with the vertex data and bind group and push it to the GPU command queue
|
||||
let draw_command = DrawCommand::new(&self.device, pipeline_name, bind_group_name, VERTICES, INDICES);
|
||||
self.draw_command_queue.push_back(draw_command);
|
||||
}
|
||||
|
||||
// Initializes the event loop for rendering and event handling
|
||||
pub fn begin_lifecycle(mut self, event_loop: EventLoop<()>, window: Window) {
|
||||
event_loop.run(move |event, _, control_flow| self.main_event_loop(event, control_flow, &window));
|
||||
@@ -182,8 +111,8 @@ impl Application {
|
||||
Event::DeviceEvent { .. } => (),
|
||||
// Handle custom-dispatched events
|
||||
Event::UserEvent(_) => (),
|
||||
// Once every event is handled and the GUI structure is updated, this requests a new sequence of draw commands
|
||||
Event::MainEventsCleared => self.redraw_gui(window),
|
||||
// Called once every event is handled and the GUI structure is updated
|
||||
Event::MainEventsCleared => self.update_gui(window),
|
||||
// Resizing or calling `window.request_redraw()` renders the GUI with the queued draw commands
|
||||
Event::RedrawRequested(_) => self.render(),
|
||||
// Once all windows have been redrawn
|
||||
@@ -196,14 +125,8 @@ impl Application {
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse dirty GUI elements and turn GUI changes into draw commands added to the render pipeline queue
|
||||
pub fn redraw_gui(&mut self, window: &Window) {
|
||||
self.example();
|
||||
|
||||
// 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();
|
||||
}
|
||||
pub fn update_gui(&mut self, window: &Window) {
|
||||
|
||||
}
|
||||
|
||||
// Render the queue of pipeline draw commands over the current window
|
||||
@@ -214,12 +137,21 @@ impl Application {
|
||||
// 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: Some("Render Encoder") });
|
||||
|
||||
// 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;
|
||||
// Build an array of draw commands
|
||||
let gui_node = self.gui_root.borrow_mut();
|
||||
let mut nodes = vec![gui_node]; // TODO: Generate the DrawCommands as a list by recursively traversing the gui node tree
|
||||
|
||||
let device = &mut self.device;
|
||||
|
||||
// let commands: Vec<DrawCommand> = nodes.map(|mut node| node.build_draw_command(dev)).collect();
|
||||
let mut commands = Vec::<DrawCommand>::with_capacity(nodes.len());
|
||||
let mut bind_groups = Vec::<Vec<wgpu::BindGroup>>::with_capacity(nodes.len());
|
||||
for i in 0..nodes.len() {
|
||||
let new_pipeline = self.pipeline_cache.get("gui_rect").unwrap();
|
||||
|
||||
commands.push(nodes[i].build_draw_command(device));
|
||||
bind_groups.push(nodes[i].build_bind_groups(device, &mut self.queue, new_pipeline, &mut self.texture_cache));
|
||||
}
|
||||
|
||||
// Recording of commands while in "rendering mode" that go into a command buffer
|
||||
let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
@@ -229,34 +161,40 @@ impl Application {
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: color,
|
||||
clear_color: wgpu::Color::BLACK,
|
||||
}
|
||||
],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
let mut current_pipeline = String::new();
|
||||
|
||||
|
||||
// Prepare a variable to cache the pipeline name
|
||||
let mut bound_pipeline = self.pipeline_cache.get("gui_rect").unwrap(); //nodes[0].get_pipeline(&self.pipeline_cache);
|
||||
render_pass.set_pipeline(&bound_pipeline.render_pipeline);
|
||||
|
||||
// Turn the queue of pipelines each into a command buffer and submit it to the render queue
|
||||
self.draw_command_queue.iter().for_each(|command| {
|
||||
// Tell the GPU which pipeline to draw in this render pass
|
||||
if current_pipeline != command.pipeline_name {
|
||||
let pipeline = self.pipeline_cache.get(&command.pipeline_name).unwrap();
|
||||
render_pass.set_pipeline(&pipeline.render_pipeline);
|
||||
current_pipeline = command.pipeline_name.clone();
|
||||
for i in 0..nodes.len() {
|
||||
// let command = commands[i];
|
||||
// If the previously set pipeline can't be reused, send the GPU the new pipeline to draw with
|
||||
let new_pipeline = self.pipeline_cache.get("gui_rect").unwrap(); //node.get_pipeline(&self.pipeline_cache);
|
||||
if bound_pipeline.render_pipeline != new_pipeline.render_pipeline {
|
||||
render_pass.set_pipeline(&new_pipeline.render_pipeline);
|
||||
bound_pipeline = new_pipeline;
|
||||
}
|
||||
|
||||
// Send the GPU the vertices and triangle indices
|
||||
render_pass.set_vertex_buffer(0, &command.vertex_buffer, 0, 0);
|
||||
render_pass.set_index_buffer(&command.index_buffer, 0, 0);
|
||||
render_pass.set_vertex_buffer(0, &commands[i].vertex_buffer, 0, 0);
|
||||
render_pass.set_index_buffer(&commands[i].index_buffer, 0, 0);
|
||||
|
||||
// let bind_groups = nodes[i].build_bind_groups(&self.device, &mut self.queue, new_pipeline, &mut self.texture_cache);
|
||||
|
||||
// Send the GPU the bind group resources
|
||||
let bind_group = self.bind_group_cache.get(&command.bind_group_name).unwrap();
|
||||
render_pass.set_bind_group(0, bind_group, &[]);
|
||||
for (index, bind_group) in bind_groups[i].iter().enumerate() {
|
||||
render_pass.set_bind_group(index as u32, bind_group, &[]);
|
||||
}
|
||||
|
||||
// Draw call
|
||||
render_pass.draw_indexed(0..command.index_count, 0, 0..1);
|
||||
});
|
||||
render_pass.draw_indexed(0..commands[i].index_count, 0, 0..1);
|
||||
};
|
||||
|
||||
// Done sending render pass commands so we can give up mutation rights to command_encoder
|
||||
drop(render_pass);
|
||||
@@ -264,9 +202,6 @@ impl Application {
|
||||
// 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]);
|
||||
}
|
||||
|
||||
13
src/bind_group_resource.rs
Normal file
13
src/bind_group_resource.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
pub enum BindGroupResource<'a> {
|
||||
Owned(wgpu::BindGroup),
|
||||
Borrowed(&'a wgpu::BindGroup),
|
||||
}
|
||||
|
||||
impl<'a> BindGroupResource<'a> {
|
||||
pub fn borrow(&self) -> BindGroupResource {
|
||||
match self {
|
||||
BindGroupResource::Owned(ref bind_group) => BindGroupResource::Borrowed(bind_group),
|
||||
BindGroupResource::Borrowed(ref bind_group) => BindGroupResource::Borrowed(bind_group),
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/color.rs
Normal file
77
src/color.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Color {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
|
||||
pub const TRANSPARENT: Self = Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.0,
|
||||
};
|
||||
|
||||
pub const BLACK: Self = Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const WHITE: Self = Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const RED: Self = Color {
|
||||
r: 1.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const YELLOW: Self = Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const GREEN: Self = Color {
|
||||
r: 0.0,
|
||||
g: 1.0,
|
||||
b: 0.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const CYAN: Self = Color {
|
||||
r: 0.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const BLUE: Self = Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
pub const MAGENTA: Self = Color {
|
||||
r: 1.0,
|
||||
g: 0.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::color::Color;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum ColorPalette {
|
||||
Black,
|
||||
@@ -20,7 +22,7 @@ pub enum ColorPalette {
|
||||
}
|
||||
|
||||
impl ColorPalette {
|
||||
pub fn get_color(self) -> wgpu::Color {
|
||||
pub fn get_color_srgb(self) -> Color {
|
||||
let grayscale = match self {
|
||||
ColorPalette::Black => 0 * 17, // #000000
|
||||
ColorPalette::NearBlack => 1 * 17, // #111111
|
||||
@@ -42,8 +44,8 @@ impl ColorPalette {
|
||||
};
|
||||
|
||||
if grayscale > -1 {
|
||||
let value = grayscale as f64 / 255.0;
|
||||
return wgpu::Color { r: value, g: value, b: value, a: 1.0 };
|
||||
let value = grayscale as f32 / 255.0;
|
||||
return Color::new(value, value, value, 1.0);
|
||||
}
|
||||
|
||||
let rgba = match self {
|
||||
@@ -51,19 +53,14 @@ impl ColorPalette {
|
||||
_ => (0, 0, 0, 255), // Unimplemented returns black
|
||||
};
|
||||
|
||||
wgpu::Color {
|
||||
r: rgba.0 as f64 / 255.0,
|
||||
g: rgba.1 as f64 / 255.0,
|
||||
b: rgba.2 as f64 / 255.0,
|
||||
a: rgba.3 as f64 / 255.0
|
||||
}
|
||||
Color::new(rgba.0 as f32 / 255.0, rgba.1 as f32 / 255.0, rgba.2 as f32 / 255.0, rgba.3 as f32 / 255.0)
|
||||
}
|
||||
|
||||
pub fn get_color_linear(self) -> wgpu::Color {
|
||||
let standard_rgb = ColorPalette::get_color(self);
|
||||
pub fn get_color_linear(self) -> Color {
|
||||
let standard_rgb = ColorPalette::get_color_srgb(self);
|
||||
|
||||
let linear = palette::Srgb::new(standard_rgb.r, standard_rgb.g, standard_rgb.b).into_linear();
|
||||
|
||||
wgpu::Color { r: linear.red, g: linear.green, b: linear.blue, a: standard_rgb.a }
|
||||
Color::new(linear.red, linear.green, linear.blue, standard_rgb.a)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
// use crate::bind_group_resource::BindGroupResource;
|
||||
|
||||
pub struct DrawCommand {
|
||||
pub pipeline_name: String,
|
||||
pub bind_group_name: String,
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
pub index_buffer: wgpu::Buffer,
|
||||
pub index_count: u32,
|
||||
}
|
||||
|
||||
impl DrawCommand {
|
||||
pub fn new(device: &wgpu::Device, pipeline_name: &str, bind_group_name: &str, vertices: &[[f32; 2]], indices: &[u16]) -> Self {
|
||||
pub fn new(device: &wgpu::Device, vertices: &[[f32; 2]], indices: &[u16]) -> 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_name: String::from(pipeline_name),
|
||||
bind_group_name: String::from(bind_group_name),
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
index_count,
|
||||
|
||||
42
src/gui_attributes.rs
Normal file
42
src/gui_attributes.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Corners<T> {
|
||||
pub top_left: T,
|
||||
pub top_right: T,
|
||||
pub bottom_right: T,
|
||||
pub bottom_left: T,
|
||||
}
|
||||
|
||||
impl<T> Corners<T> {
|
||||
pub fn new(top_left: T, top_right: T, bottom_right: T, bottom_left: T) -> Self {
|
||||
Self { top_left, top_right, bottom_right, bottom_left }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Sides<T> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
|
||||
impl<T> Sides<T> {
|
||||
pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
|
||||
Self { top, right, bottom, left }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Dimensions<T> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
|
||||
impl<T> Dimensions<T> {
|
||||
pub fn new(width: T, height: T) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
}
|
||||
83
src/gui_node.rs
Normal file
83
src/gui_node.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use crate::resource_cache::ResourceCache;
|
||||
use crate::draw_command::DrawCommand;
|
||||
use crate::color::Color;
|
||||
use crate::texture::Texture;
|
||||
use crate::pipeline::Pipeline;
|
||||
use crate::gui_attributes::*;
|
||||
|
||||
pub struct GuiNode {
|
||||
pub form_factor: GuiNodeUniform,
|
||||
}
|
||||
|
||||
impl GuiNode {
|
||||
pub fn new(width: u32, height: u32, color: Color) -> Self {
|
||||
Self {
|
||||
form_factor: GuiNodeUniform::new(width, height, color),
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn get_pipeline(&self, pipeline_cache: &ResourceCache<Pipeline>) -> &Pipeline {
|
||||
// pipeline_cache.get("gui_rect").unwrap()
|
||||
// }
|
||||
|
||||
pub fn build_draw_command(&mut self, device: &wgpu::Device) -> DrawCommand {
|
||||
const VERTICES: &[[f32; 2]] = &[
|
||||
[-0.5, 0.5],
|
||||
[0.5, 0.5],
|
||||
[0.5, 1.0],
|
||||
[-0.5, 1.0],
|
||||
];
|
||||
const INDICES: &[u16] = &[
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
];
|
||||
|
||||
// Create a draw command with the vertex data then push it to the GPU command queue
|
||||
DrawCommand::new(device, VERTICES, INDICES)
|
||||
}
|
||||
|
||||
pub fn build_bind_groups(&mut self, device: &wgpu::Device, queue: &mut wgpu::Queue, pipeline: &Pipeline, texture_cache: &mut ResourceCache<Texture>) -> Vec<wgpu::BindGroup> {
|
||||
// Load the cached texture
|
||||
let texture = Texture::cached_load(device, queue, "textures/grid.png", texture_cache);
|
||||
|
||||
// Build a staging buffer from the uniform resource data
|
||||
let binding_staging_buffer = Pipeline::build_binding_staging_buffer(device, self.form_factor);
|
||||
|
||||
// Construct the bind group for this GUI node
|
||||
let bind_group = Pipeline::build_bind_group(device, &pipeline.bind_group_layout, vec![
|
||||
Pipeline::build_binding_resource(&binding_staging_buffer),
|
||||
wgpu::BindingResource::TextureView(&texture.texture_view),
|
||||
]);
|
||||
|
||||
vec![
|
||||
bind_group,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct GuiNodeUniform {
|
||||
pub dimensions: Dimensions<u32>,
|
||||
pub corners_radius: Corners<f32>,
|
||||
pub sides_inset: Sides<f32>,
|
||||
pub border_thickness: f32,
|
||||
pub border_color: Color,
|
||||
pub fill_color: Color,
|
||||
}
|
||||
|
||||
impl GuiNodeUniform {
|
||||
pub fn new(width: u32, height: u32, color: Color) -> Self {
|
||||
GuiNodeUniform {
|
||||
dimensions: Dimensions::<u32>::new(width, height),
|
||||
corners_radius: Corners::<f32>::new(0.0, 0.0, 0.0, 0.0),
|
||||
sides_inset: Sides::<f32>::new(0.0, 0.0, 0.0, 0.0),
|
||||
border_thickness: 0.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
fill_color: color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Zeroable for GuiNodeUniform {}
|
||||
unsafe impl bytemuck::Pod for GuiNodeUniform {}
|
||||
@@ -1,23 +0,0 @@
|
||||
pub struct GuiRect {
|
||||
pub corners: Corners<(f32, f32)>,
|
||||
pub corners_radius: Corners<f32>,
|
||||
pub sides_inset: Sides<f32>,
|
||||
pub border: f32,
|
||||
pub border_color: wgpu::Color,
|
||||
pub fill_color: wgpu::Color,
|
||||
pub fill_texture: Option<wgpu::Texture>,
|
||||
}
|
||||
|
||||
pub struct Corners<T> {
|
||||
pub top_left: T,
|
||||
pub top_right: T,
|
||||
pub bottom_right: T,
|
||||
pub bottom_left: T,
|
||||
}
|
||||
|
||||
pub struct Sides<T> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
pub struct GuiTree {
|
||||
|
||||
}
|
||||
|
||||
impl GuiTree {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
mod application;
|
||||
mod gui_rect;
|
||||
mod pipeline;
|
||||
mod texture;
|
||||
mod color;
|
||||
mod color_palette;
|
||||
mod resource_cache;
|
||||
mod shader_stage;
|
||||
mod draw_command;
|
||||
mod gui_tree;
|
||||
mod gui_node;
|
||||
mod gui_attributes;
|
||||
mod window_events;
|
||||
mod window_uniform;
|
||||
mod bind_group_resource;
|
||||
|
||||
use application::Application;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
129
src/pipeline.rs
129
src/pipeline.rs
@@ -1,4 +1,6 @@
|
||||
use std::mem;
|
||||
use crate::resource_cache::ResourceCache;
|
||||
use crate::shader_stage;
|
||||
|
||||
pub struct Pipeline {
|
||||
pub bind_group_layout: wgpu::BindGroupLayout,
|
||||
@@ -6,23 +8,107 @@ pub struct Pipeline {
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(device: &wgpu::Device, vertex_shader: &wgpu::ShaderModule, fragment_shader: &wgpu::ShaderModule, bind_group_layout_binding_types: Vec<wgpu::BindingType>) -> Self {
|
||||
let bind_group_layout_entries = bind_group_layout_binding_types.into_iter().enumerate().map(|(index, binding_type)|
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
pub fn new(device: &wgpu::Device, swap_chain_color_format: wgpu::TextureFormat, extra_layouts: Vec<&wgpu::BindGroupLayout>, shader_cache: &mut ResourceCache<wgpu::ShaderModule>, shader_pair_path: (&str, &str)) -> Self {
|
||||
// Load the vertex and fragment shaders
|
||||
let shader_pair = Pipeline::get_shader_pair(device, shader_cache, shader_pair_path);
|
||||
|
||||
// Prepare a bind group layout for the GUI element's texture and form factor data
|
||||
let bind_group_layout = Pipeline::build_bind_group_layout(device, &vec![
|
||||
wgpu::BindingType::UniformBuffer { dynamic: false },
|
||||
wgpu::BindingType::SampledTexture {
|
||||
dimension: wgpu::TextureViewDimension::D2,
|
||||
component_type: wgpu::TextureComponentType::Float,
|
||||
multisampled: false,
|
||||
},
|
||||
]);
|
||||
|
||||
// Combine all bind group layouts
|
||||
let mut bind_group_layouts = vec![&bind_group_layout];
|
||||
bind_group_layouts.append(&mut extra_layouts.clone());
|
||||
|
||||
// Construct the pipeline
|
||||
let render_pipeline = Pipeline::build_pipeline(device, swap_chain_color_format, bind_group_layouts, shader_pair);
|
||||
Self {
|
||||
bind_group_layout,
|
||||
render_pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_shader_pair<'a>(device: &wgpu::Device, shader_cache: &'a mut ResourceCache<wgpu::ShaderModule>, shader_pair_path: (&str, &str)) -> (&'a wgpu::ShaderModule, &'a wgpu::ShaderModule) {
|
||||
// If uncached, construct a vertex shader loaded from its source code file
|
||||
if shader_cache.get(shader_pair_path.0).is_none() {
|
||||
let vertex_shader_module = shader_stage::compile_from_glsl(device, shader_pair_path.0, glsl_to_spirv::ShaderType::Vertex).unwrap();
|
||||
shader_cache.set(shader_pair_path.0, vertex_shader_module);
|
||||
}
|
||||
|
||||
// If uncached, construct a fragment shader loaded from its source code file
|
||||
if shader_cache.get(shader_pair_path.1).is_none() {
|
||||
let fragment_shader_module = shader_stage::compile_from_glsl(&device, shader_pair_path.1, glsl_to_spirv::ShaderType::Fragment).unwrap();
|
||||
shader_cache.set(shader_pair_path.1, fragment_shader_module);
|
||||
}
|
||||
|
||||
// Get the shader pair
|
||||
let vertex_shader = shader_cache.get(shader_pair_path.0).unwrap();
|
||||
let fragment_shader = shader_cache.get(shader_pair_path.1).unwrap();
|
||||
|
||||
(vertex_shader, fragment_shader)
|
||||
}
|
||||
|
||||
pub fn build_bind_group_layouts(device: &wgpu::Device, bind_group_layouts: &Vec<Vec<wgpu::BindingType>>) -> Vec<wgpu::BindGroupLayout> {
|
||||
bind_group_layouts.into_iter().map(|layout_entry| Self::build_bind_group_layout(device, layout_entry)).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
pub fn build_bind_group_layout(device: &wgpu::Device, bind_group_layout: &Vec<wgpu::BindingType>) -> wgpu::BindGroupLayout {
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
bindings: bind_group_layout.into_iter().enumerate().map(|(index, binding_type)|
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: index as u32,
|
||||
visibility: wgpu::ShaderStage::all(),
|
||||
ty: binding_type.clone(),
|
||||
}
|
||||
).collect::<Vec<_>>().as_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build_binding_staging_buffer<T: bytemuck::Pod>(device: &wgpu::Device, resource: T) -> wgpu::Buffer { // TODO: Turn this into a borrow
|
||||
// Construct a staging buffer with the binary uniform struct data
|
||||
device.create_buffer_with_data(
|
||||
bytemuck::cast_slice(&[resource]),
|
||||
wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_binding_resource(resource_buffer: &wgpu::Buffer) -> wgpu::BindingResource {
|
||||
// Return the buffer as a binding resource
|
||||
wgpu::BindingResource::Buffer {
|
||||
buffer: resource_buffer,
|
||||
range: 0..std::mem::size_of_val(resource_buffer) as wgpu::BufferAddress,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_bind_group(device: &wgpu::Device, bind_group_layout: &wgpu::BindGroupLayout, binding_resources: Vec<wgpu::BindingResource>) -> wgpu::BindGroup {
|
||||
let bindings = binding_resources.into_iter().enumerate().map(|(index, binding_resource)|
|
||||
wgpu::Binding {
|
||||
binding: index as u32,
|
||||
visibility: wgpu::ShaderStage::all(),
|
||||
ty: binding_type,
|
||||
resource: binding_resource,
|
||||
}
|
||||
).collect::<Vec<_>>();
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
bindings: bind_group_layout_entries.as_slice(),
|
||||
label: None,
|
||||
});
|
||||
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: bind_group_layout,
|
||||
bindings: bindings.as_slice(),
|
||||
label: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn build_pipeline(device: &wgpu::Device, swap_chain_color_format: wgpu::TextureFormat, bind_group_layouts: Vec<&wgpu::BindGroupLayout>, shader_pair: (&wgpu::ShaderModule, &wgpu::ShaderModule)) -> wgpu::RenderPipeline {
|
||||
let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
bind_group_layouts: bind_group_layouts.as_slice(),
|
||||
});
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
|
||||
let (vertex_shader, fragment_shader) = shader_pair;
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
layout: &render_pipeline_layout,
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
module: vertex_shader,
|
||||
@@ -41,7 +127,7 @@ impl Pipeline {
|
||||
}),
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: &[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb, // TODO: Make this match Application.swap_chain_descriptor
|
||||
format: swap_chain_color_format,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
@@ -62,25 +148,6 @@ impl Pipeline {
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
});
|
||||
|
||||
Self {
|
||||
bind_group_layout,
|
||||
render_pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_bind_group(&self, device: &wgpu::Device, binding_resources: Vec<wgpu::BindingResource>) -> wgpu::BindGroup {
|
||||
let bindings = binding_resources.into_iter().enumerate().map(|(index, binding_resource)|
|
||||
wgpu::Binding {
|
||||
binding: index as u32,
|
||||
resource: binding_resource,
|
||||
}
|
||||
).collect::<Vec<_>>();
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
bindings: bindings.as_slice(),
|
||||
label: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::fs;
|
||||
use image::GenericImageView;
|
||||
use crate::resource_cache::ResourceCache;
|
||||
|
||||
pub struct Texture {
|
||||
pub texture: wgpu::Texture,
|
||||
@@ -8,6 +9,15 @@ pub struct Texture {
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
pub fn cached_load<'a>(device: &wgpu::Device, queue: &mut wgpu::Queue, path: &str, texture_cache: &'a mut ResourceCache<Texture>) -> &'a Texture {
|
||||
// If uncached, construct a texture loaded from the image file
|
||||
if texture_cache.get(path).is_none() {
|
||||
let texture = Texture::from_filepath(device, queue, path).unwrap();
|
||||
texture_cache.set(path, texture);
|
||||
}
|
||||
texture_cache.get(path).unwrap()
|
||||
}
|
||||
|
||||
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 = fs::read(path)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::application::Application;
|
||||
use crate::application::Application;
|
||||
use winit::event::*;
|
||||
use winit::event_loop::ControlFlow;
|
||||
|
||||
@@ -30,7 +30,20 @@ pub fn window_event(application: &mut Application, control_flow: &mut ControlFlo
|
||||
fn keyboard_event(application: &mut Application, control_flow: &mut ControlFlow, input: &KeyboardInput) {
|
||||
match input {
|
||||
KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Escape), .. } => quit(control_flow),
|
||||
KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Space), .. } => application.example(),
|
||||
KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(VirtualKeyCode::Space), .. } => {
|
||||
// const VERTICES: &[[f32; 2]] = &[
|
||||
// [-0.2, 0.0],
|
||||
// [0.2, 0.0],
|
||||
// [0.2, -0.5],
|
||||
// [-0.2, -0.5],
|
||||
// ];
|
||||
// const INDICES: &[u16] = &[
|
||||
// 0, 1, 2,
|
||||
// 0, 2, 3,
|
||||
// ];
|
||||
|
||||
// application.example(VERTICES, INDICES);
|
||||
},
|
||||
_ => *control_flow = ControlFlow::Wait,
|
||||
}
|
||||
}
|
||||
|
||||
18
src/window_uniform.rs
Normal file
18
src/window_uniform.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use crate::gui_attributes::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WindowUniform {
|
||||
pub dimensions: Dimensions<u32>,
|
||||
}
|
||||
|
||||
impl WindowUniform {
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
Self {
|
||||
dimensions: Dimensions::new(width, height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Zeroable for WindowUniform {}
|
||||
unsafe impl bytemuck::Pod for WindowUniform {}
|
||||
Reference in New Issue
Block a user