From b23bc27f55bbb974252fbf11b26bdf6cc7fbf2c4 Mon Sep 17 00:00:00 2001 From: Werner Date: Tue, 9 Nov 2021 21:07:16 -0300 Subject: [PATCH] Initial wgpu abstraction --- Cargo.toml | 1 + Examples/Triangle.rs | 184 +++++++++++++-------------- Source/Color/HslaColor.rs | 17 +++ Source/Color/RgbaColor.rs | 17 +++ Source/Color/mod.rs | 7 + Source/Display.rs | 65 ---------- Source/Render/IndexBuffer.rs | 14 ++ Source/Render/IndexFormat.rs | 17 +++ Source/Render/PolygonMode.rs | 20 +++ Source/Render/Renderer.rs | 154 ++++++++++++++++++++++ Source/Render/StepMode.rs | 23 ++++ Source/Render/UniformBuffer.rs | 8 ++ Source/Render/Vertex.rs | 4 +- Source/Render/VertexAttribute.rs | 21 +++ Source/Render/VertexBuffer.rs | 8 ++ Source/Render/VertexBufferLayout.rs | 11 ++ Source/Render/VertexFormat.rs | 190 ++++++++++++++++++++++++++++ Source/Render/mod.rs | 76 ++++++++--- Source/Runtime.rs | 33 ++--- Source/Shader/Shader.rs | 46 +++++++ Source/Shader/ShaderSource.rs | 22 ++++ Source/Shader/ShaderStage.rs | 17 +++ Source/Shader/mod.rs | 11 ++ Source/State.rs | 12 +- Source/lib.rs | 6 +- 25 files changed, 775 insertions(+), 209 deletions(-) create mode 100644 Source/Color/HslaColor.rs create mode 100644 Source/Color/RgbaColor.rs create mode 100644 Source/Color/mod.rs delete mode 100644 Source/Display.rs create mode 100644 Source/Render/IndexBuffer.rs create mode 100644 Source/Render/IndexFormat.rs create mode 100644 Source/Render/PolygonMode.rs create mode 100644 Source/Render/Renderer.rs create mode 100644 Source/Render/StepMode.rs create mode 100644 Source/Render/UniformBuffer.rs create mode 100644 Source/Render/VertexAttribute.rs create mode 100644 Source/Render/VertexBuffer.rs create mode 100644 Source/Render/VertexBufferLayout.rs create mode 100644 Source/Render/VertexFormat.rs create mode 100644 Source/Shader/Shader.rs create mode 100644 Source/Shader/ShaderSource.rs create mode 100644 Source/Shader/ShaderStage.rs create mode 100644 Source/Shader/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 644cf82..0e09cff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ env_logger = "0.9" image = "0.23" log = "0.4" pollster = "0.2" +serde = { version = "1.0", features = ["derive"] } tobj = "3.0" wgpu = "0.11" winit = "0.25" diff --git a/Examples/Triangle.rs b/Examples/Triangle.rs index d41ef71..dded6ca 100644 --- a/Examples/Triangle.rs +++ b/Examples/Triangle.rs @@ -4,10 +4,10 @@ use anyhow::Result; use bytemuck::{Pod, Zeroable}; use std::time::Duration; -use wgpu::util::DeviceExt; use winit::event::*; use Graphics::Render::*; -use Graphics::{Display, Runtime, State}; +use Graphics::Shader::*; +use Graphics::{Runtime, State}; #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] @@ -17,20 +17,23 @@ struct TriangleVertex { } impl Vertex for TriangleVertex { - fn GetDescriptor<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - wgpu::VertexAttribute { + fn GetLayout() -> VertexBufferLayout { + VertexBufferLayout { + label: "Triangle".into(), + stride: std::mem::size_of::(), + step_mode: StepMode::Vertex, + attributes: vec![ + VertexAttribute { + label: "Position".into(), + format: VertexFormat::Float32x3, offset: 0, shader_location: 0, - format: wgpu::VertexFormat::Float32x3, }, - wgpu::VertexAttribute { - offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + VertexAttribute { + label: "Color".into(), + format: VertexFormat::Float32x3, + offset: std::mem::size_of::<[f32; 3]>(), shader_location: 1, - format: wgpu::VertexFormat::Float32x3, }, ], } @@ -57,84 +60,100 @@ struct Triangle { } impl State for Triangle { - fn Init(display: &Display) -> Result { + fn Init(renderer: &Renderer) -> Result { // Shader - let shader = display - .device - .create_shader_module(&wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("../Shaders/Triangle.wgsl").into()), - }); + let shader = Shader::FromWgsl(include_str!("../Shaders/Triangle.wgsl")); + let shader_module = renderer.SubmitShader(&shader); // Pipeline let render_pipeline_layout = - display + renderer .device .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), + label: Some("RENDER_PIPELINE_LAYOUT"), bind_group_layouts: &[], push_constant_ranges: &[], }); - let render_pipeline = - display + let render_pipeline = { + let mut layout = TriangleVertex::GetLayout(); + + let attributes = layout + .attributes + .drain(..) + .map(|x| x.into()) + .collect::>(); + + let wgpu_layout = wgpu::VertexBufferLayout { + array_stride: layout.stride as wgpu::BufferAddress, + step_mode: layout.step_mode.into(), + attributes: attributes.as_ref(), + }; + + renderer .device .create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), + label: Some("RENDER_PIPELINE"), layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { - module: &shader, + module: &shader_module, entry_point: "main", - buffers: &[TriangleVertex::GetDescriptor()], + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() + as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress, + shader_location: 1, + format: wgpu::VertexFormat::Float32x3, + }, + ], + }], }, fragment: Some(wgpu::FragmentState { - // 3. - module: &shader, + module: &shader_module, entry_point: "main", targets: &[wgpu::ColorTargetState { - // 4. - format: display.config.format, + format: renderer.config.format, blend: Some(wgpu::BlendState::REPLACE), write_mask: wgpu::ColorWrites::ALL, }], }), primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, // 1. + topology: wgpu::PrimitiveTopology::TriangleList, strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, // 2. + front_face: wgpu::FrontFace::Ccw, cull_mode: Some(wgpu::Face::Back), - // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE polygon_mode: wgpu::PolygonMode::Fill, - // Requires Features::DEPTH_CLAMPING clamp_depth: false, - // Requires Features::CONSERVATIVE_RASTERIZATION conservative: false, }, - depth_stencil: None, // 1. + depth_stencil: None, multisample: wgpu::MultisampleState { - count: 1, // 2. - mask: !0, // 3. - alpha_to_coverage_enabled: false, // 4. + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, }, - }); + }) + }; - let vertex_buffer = display - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(VERTICES), - usage: wgpu::BufferUsages::VERTEX, - }); + let vertex_buffer = renderer.SubmitVertexBuffer(&VertexBuffer { + label: "Vertex Buffer".into(), + content: bytemuck::cast_slice(VERTICES).to_vec(), + }); - let index_buffer = display - .device - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(INDICES), - usage: wgpu::BufferUsages::INDEX, - }); + let index_buffer = renderer.SubmitIndexBuffer(&IndexBuffer { + label: "Index Buffer".into(), + content: bytemuck::cast_slice(INDICES).to_vec(), + }); let num_indices = INDICES.len() as u32; @@ -146,56 +165,23 @@ impl State for Triangle { }) } - fn Input(&mut self, display: &Display, event: &WindowEvent) -> bool { + fn Input(&mut self, renderer: &Renderer, event: &WindowEvent) -> bool { false } - fn Update(&mut self, display: &Display, delta: Duration) {} + fn Update(&mut self, renderer: &Renderer, delta: Duration) {} - fn Resize(&mut self, display: &Display) {} + fn Resize(&mut self, renderer: &Renderer) {} - fn Draw(&mut self, display: &mut Display) -> Result<(), wgpu::SurfaceError> { - let output = display.surface.get_current_texture().unwrap(); - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = display - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[ - // This is what [[location(0)]] in the fragment shader targets - wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 1.0, - }), - store: true, - }, - }, - ], - depth_stencil_attachment: None, - }); - - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); - render_pass.draw_indexed(0..self.num_indices, 0, 0..1); - } - - display.queue.submit(std::iter::once(encoder.finish())); - output.present(); + fn Draw(&mut self, renderer: &mut Renderer) -> Result<(), wgpu::SurfaceError> { + renderer + .Draw( + &self.render_pipeline, + &self.vertex_buffer, + &self.index_buffer, + self.num_indices, + ) + .unwrap(); Ok(()) } diff --git a/Source/Color/HslaColor.rs b/Source/Color/HslaColor.rs new file mode 100644 index 0000000..0ee211b --- /dev/null +++ b/Source/Color/HslaColor.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct HslaColor { + /// Hue component. [0.0, 360.0] + pub hue: f32, + + /// Saturation component. [0.0, 1.0] + pub saturation: f32, + + /// Lightness component. [0.0, 1.0] + pub lightness: f32, + + /// Alpha component. [0.0, 1.0] + pub alpha: f32, +} diff --git a/Source/Color/RgbaColor.rs b/Source/Color/RgbaColor.rs new file mode 100644 index 0000000..97d9c51 --- /dev/null +++ b/Source/Color/RgbaColor.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RgbaColor { + /// Red component. [0.0, 1.0] + pub red: f32, + + /// Green component. [0.0, 1.0] + pub green: f32, + + /// Blue component. [0.0, 1.0] + pub blue: f32, + + /// Alpha component. [0.0, 1.0] + pub alpha: f32, +} diff --git a/Source/Color/mod.rs b/Source/Color/mod.rs new file mode 100644 index 0000000..ef8108a --- /dev/null +++ b/Source/Color/mod.rs @@ -0,0 +1,7 @@ +#[path = "HslaColor.rs"] +mod _HslaColor; +pub use self::_HslaColor::*; + +#[path = "RgbaColor.rs"] +mod _RgbaColor; +pub use self::_RgbaColor::*; diff --git a/Source/Display.rs b/Source/Display.rs deleted file mode 100644 index cc64e75..0000000 --- a/Source/Display.rs +++ /dev/null @@ -1,65 +0,0 @@ -use anyhow::Result; -use winit::window::Window; - -/// A handler to system window and render adapter. -pub struct Display { - pub surface: wgpu::Surface, - pub window: Window, - pub config: wgpu::SurfaceConfiguration, - pub device: wgpu::Device, - pub queue: wgpu::Queue, -} - -impl Display { - pub async fn New(window: Window) -> Result { - let size = window.inner_size(); - - let instance = wgpu::Instance::new(wgpu::Backends::all()); - let surface = unsafe { instance.create_surface(&window) }; - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, - ) - .await - .unwrap(); - - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: surface.get_preferred_format(&adapter).unwrap(), - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Fifo, - }; - - surface.configure(&device, &config); - - Ok(Self { - surface, - window, - config, - device, - queue, - }) - } - - pub fn Resize(&mut self, width: u32, height: u32) { - self.config.width = width; - self.config.height = height; - self.surface.configure(&self.device, &self.config); - } -} diff --git a/Source/Render/IndexBuffer.rs b/Source/Render/IndexBuffer.rs new file mode 100644 index 0000000..1e671fa --- /dev/null +++ b/Source/Render/IndexBuffer.rs @@ -0,0 +1,14 @@ +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] +pub struct IndexBuffer { + pub label: Cow<'static, str>, + pub content: Vec, +} + +impl IndexBuffer { + pub fn GetLength(&self) -> u32 { + self.content.len() as u32 + } +} diff --git a/Source/Render/IndexFormat.rs b/Source/Render/IndexFormat.rs new file mode 100644 index 0000000..c494981 --- /dev/null +++ b/Source/Render/IndexFormat.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum IndexFormat { + /// Indices are 16 bit unsigned integers. + UInt16 = 0, + + /// Indices are 32 bit unsigned integers. + UInt32 = 1, +} + +impl Default for IndexFormat { + fn default() -> Self { + Self::UInt32 + } +} diff --git a/Source/Render/PolygonMode.rs b/Source/Render/PolygonMode.rs new file mode 100644 index 0000000..422d946 --- /dev/null +++ b/Source/Render/PolygonMode.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum PolygonMode { + /// Polygons are filled + Fill = 0, + + /// Polygons are drawn as line segments + Line = 1, + + /// Polygons are drawn as points + Point = 2, +} + +impl Default for PolygonMode { + fn default() -> Self { + Self::Fill + } +} diff --git a/Source/Render/Renderer.rs b/Source/Render/Renderer.rs new file mode 100644 index 0000000..2755279 --- /dev/null +++ b/Source/Render/Renderer.rs @@ -0,0 +1,154 @@ +use super::{IndexBuffer, UniformBuffer, VertexBuffer}; +use crate::Shader::Shader; +use anyhow::Result; +use wgpu::util::DeviceExt; +use winit::window::Window; + +pub struct Renderer { + pub surface: wgpu::Surface, + pub window: Window, + pub config: wgpu::SurfaceConfiguration, + pub device: wgpu::Device, + pub queue: wgpu::Queue, +} + +impl Renderer { + pub async fn New(window: Window) -> Result { + let size = window.inner_size(); + + let instance = wgpu::Instance::new(wgpu::Backends::all()); + let surface = unsafe { instance.create_surface(&window) }; + + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, + ) + .await + .unwrap(); + + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: surface.get_preferred_format(&adapter).unwrap(), + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + }; + + surface.configure(&device, &config); + + Ok(Self { + surface, + window, + config, + device, + queue, + }) + } + + pub fn SubmitShader(&self, shader: &Shader) -> wgpu::ShaderModule { + let src = shader.source.WgslToString().unwrap(); + + self.device + .create_shader_module(&wgpu::ShaderModuleDescriptor { + label: Some(&shader.label), + source: wgpu::ShaderSource::Wgsl(src.as_str().into()), + }) + } + + pub fn SubmitVertexBuffer(&self, vertex_buffer: &VertexBuffer) -> wgpu::Buffer { + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&vertex_buffer.label), + contents: vertex_buffer.content.as_ref(), + usage: wgpu::BufferUsages::VERTEX, + }) + } + + pub fn SubmitIndexBuffer(&self, index_buffer: &IndexBuffer) -> wgpu::Buffer { + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&index_buffer.label), + contents: index_buffer.content.as_ref(), + usage: wgpu::BufferUsages::INDEX, + }) + } + + pub fn SubmitUniformBuffer(&self, uniform_buffer: &UniformBuffer) -> wgpu::Buffer { + self.device + .create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some(&uniform_buffer.label), + contents: uniform_buffer.content.as_ref(), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }) + } + + pub fn Draw( + &self, + pipeline: &wgpu::RenderPipeline, + vertex_buffer: &wgpu::Buffer, + index_buffer: &wgpu::Buffer, + num_indices: u32, + ) -> Result<()> { + let output = self.surface.get_current_texture()?; + + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("RENDER_ENCODER"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("RENDER_PASS"), + color_attachments: &[wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }), + store: true, + }, + }], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(pipeline); + render_pass.set_vertex_buffer(0, vertex_buffer.slice(..)); + render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16); + render_pass.draw_indexed(0..num_indices, 0, 0..1); + } + + self.queue.submit(std::iter::once(encoder.finish())); + output.present(); + + Ok(()) + } + + pub fn Resize(&mut self, width: u32, height: u32) { + self.config.width = width; + self.config.height = height; + self.surface.configure(&self.device, &self.config); + } +} diff --git a/Source/Render/StepMode.rs b/Source/Render/StepMode.rs new file mode 100644 index 0000000..78679b3 --- /dev/null +++ b/Source/Render/StepMode.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum StepMode { + Vertex = 0, + Instance = 1, +} + +impl Default for StepMode { + fn default() -> Self { + StepMode::Vertex + } +} + +impl From for wgpu::VertexStepMode { + fn from(step_mode: StepMode) -> Self { + match step_mode { + StepMode::Vertex => wgpu::VertexStepMode::Vertex, + StepMode::Instance => wgpu::VertexStepMode::Instance, + } + } +} diff --git a/Source/Render/UniformBuffer.rs b/Source/Render/UniformBuffer.rs new file mode 100644 index 0000000..ccfa88a --- /dev/null +++ b/Source/Render/UniformBuffer.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] +pub struct UniformBuffer { + pub label: Cow<'static, str>, + pub content: Vec, +} diff --git a/Source/Render/Vertex.rs b/Source/Render/Vertex.rs index d563843..e4df581 100644 --- a/Source/Render/Vertex.rs +++ b/Source/Render/Vertex.rs @@ -1,3 +1,5 @@ +use super::VertexBufferLayout; + pub trait Vertex { - fn GetDescriptor<'a>() -> wgpu::VertexBufferLayout<'a>; + fn GetLayout() -> VertexBufferLayout; } diff --git a/Source/Render/VertexAttribute.rs b/Source/Render/VertexAttribute.rs new file mode 100644 index 0000000..5a3e545 --- /dev/null +++ b/Source/Render/VertexAttribute.rs @@ -0,0 +1,21 @@ +use super::VertexFormat; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub struct VertexAttribute { + pub label: Cow<'static, str>, + pub format: VertexFormat, + pub offset: usize, + pub shader_location: u32, +} + +impl From for wgpu::VertexAttribute { + fn from(attr: VertexAttribute) -> Self { + wgpu::VertexAttribute { + offset: attr.offset as wgpu::BufferAddress, + shader_location: attr.shader_location, + format: attr.format.into(), + } + } +} diff --git a/Source/Render/VertexBuffer.rs b/Source/Render/VertexBuffer.rs new file mode 100644 index 0000000..c67e54b --- /dev/null +++ b/Source/Render/VertexBuffer.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] +pub struct VertexBuffer { + pub label: Cow<'static, str>, + pub content: Vec, +} diff --git a/Source/Render/VertexBufferLayout.rs b/Source/Render/VertexBufferLayout.rs new file mode 100644 index 0000000..84da88b --- /dev/null +++ b/Source/Render/VertexBufferLayout.rs @@ -0,0 +1,11 @@ +use super::{StepMode, VertexAttribute}; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] +pub struct VertexBufferLayout { + pub label: Cow<'static, str>, + pub stride: usize, + pub step_mode: StepMode, + pub attributes: Vec, +} diff --git a/Source/Render/VertexFormat.rs b/Source/Render/VertexFormat.rs new file mode 100644 index 0000000..0f3229a --- /dev/null +++ b/Source/Render/VertexFormat.rs @@ -0,0 +1,190 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum VertexFormat { + /// Two unsigned bytes (u8). `uvec2` in shaders. + UInt8x2 = 0, + + /// Four unsigned bytes (u8). `uvec4` in shaders. + UInt8x4 = 1, + + /// Two signed bytes (i8). `ivec2` in shaders. + Int8x2 = 2, + + /// Four signed bytes (i8). `ivec4` in shaders. + Int8x4 = 3, + + /// Two unsigned bytes (u8). [0, 255] converted to float [0, 1] `vec2` in shaders. + UNorm8x2 = 4, + + /// Four unsigned bytes (u8). [0, 255] converted to float [0, 1] `vec4` in shaders. + UNorm8x4 = 5, + + /// Two signed bytes (i8). [-127, 127] converted to float [-1, 1] `vec2` in shaders. + Norm8x2 = 6, + + /// Four signed bytes (i8). [-127, 127] converted to float [-1, 1] `vec4` in shaders. + Norm8x4 = 7, + + /// Two unsigned shorts (u16). `uvec2` in shaders. + UInt16x2 = 8, + + /// Four unsigned shorts (u16). `uvec4` in shaders. + UInt16x4 = 9, + + /// Two signed shorts (i16). `ivec2` in shaders. + Int16x2 = 10, + + /// Four signed shorts (i16). `ivec4` in shaders. + Int16x4 = 11, + + /// Two unsigned shorts (u16). [0, 65535] converted to float [0, 1] `vec2` in shaders. + UNorm16x2 = 12, + + /// Four unsigned shorts (u16). [0, 65535] converted to float [0, 1] `vec4` in shaders. + UNorm16x4 = 13, + + /// Two signed shorts (i16). [-32767, 32767] converted to float [-1, 1] `vec2` in shaders. + Norm16x2 = 14, + + /// Four signed shorts (i16). [-32767, 32767] converted to float [-1, 1] `vec4` in shaders. + Norm16x4 = 15, + + /// Two half-precision floats (no Rust equiv). `vec2` in shaders. + Float16x2 = 16, + + /// Four half-precision floats (no Rust equiv). `vec4` in shaders. + Float16x4 = 17, + + /// One single-precision float (f32). `float` in shaders. + Float32 = 18, + + /// Two single-precision floats (f32). `vec2` in shaders. + Float32x2 = 19, + + /// Three single-precision floats (f32). `vec3` in shaders. + Float32x3 = 20, + + /// Four single-precision floats (f32). `vec4` in shaders. + Float32x4 = 21, + + /// One unsigned int (u32). `uint` in shaders. + UInt32 = 22, + + /// Two unsigned ints (u32). `uvec2` in shaders. + UInt32x2 = 23, + + /// Three unsigned ints (u32). `uvec3` in shaders. + UInt32x3 = 24, + + /// Four unsigned ints (u32). `uvec4` in shaders. + UInt32x4 = 25, + + /// One signed int (i32). `int` in shaders. + Int32 = 26, + + /// Two signed ints (i32). `ivec2` in shaders. + Int32x2 = 27, + + /// Three signed ints (i32). `ivec3` in shaders. + Int32x3 = 28, + + /// Four signed ints (i32). `ivec4` in shaders. + Int32x4 = 29, + + /// One double-precision float (f64). `double` in shaders. + Float64 = 30, + + /// Two double-precision floats (f64). `dvec2` in shaders. + Float64x2 = 31, + + /// Three double-precision floats (f64). `dvec3` in shaders. + Float64x3 = 32, + + /// Four double-precision floats (f64). `dvec4` in shaders. + Float64x4 = 33, +} + +impl VertexFormat { + /// Returns the size in bytes of this format. + pub const fn GetSize(&self) -> u64 { + match *self { + Self::UInt8x2 => 2, + Self::UInt8x4 => 4, + Self::Int8x2 => 2, + Self::Int8x4 => 4, + Self::UNorm8x2 => 2, + Self::UNorm8x4 => 4, + Self::Norm8x2 => 2, + Self::Norm8x4 => 4, + Self::UInt16x2 => 2 * 2, + Self::UInt16x4 => 4 * 4, + Self::Int16x2 => 2 * 2, + Self::Int16x4 => 2 * 4, + Self::UNorm16x2 => 2 * 2, + Self::UNorm16x4 => 2 * 4, + Self::Norm16x2 => 2 * 2, + Self::Norm16x4 => 2 * 4, + Self::Float16x2 => 2 * 2, + Self::Float16x4 => 2 * 4, + Self::Float32 => 4, + Self::Float32x2 => 4 * 2, + Self::Float32x3 => 4 * 3, + Self::Float32x4 => 4 * 4, + Self::UInt32 => 4, + Self::UInt32x2 => 4 * 2, + Self::UInt32x3 => 4 * 3, + Self::UInt32x4 => 4 * 4, + Self::Int32 => 4, + Self::Int32x2 => 4 * 2, + Self::Int32x3 => 4 * 3, + Self::Int32x4 => 4 * 4, + Self::Float64 => 8, + Self::Float64x2 => 8 * 2, + Self::Float64x3 => 8 * 3, + Self::Float64x4 => 8 * 4, + } + } +} + +impl From for wgpu::VertexFormat { + fn from(format: VertexFormat) -> Self { + match format { + VertexFormat::UInt8x2 => wgpu::VertexFormat::Uint8x2, + VertexFormat::UInt8x4 => wgpu::VertexFormat::Uint8x4, + VertexFormat::Int8x2 => wgpu::VertexFormat::Sint8x2, + VertexFormat::Int8x4 => wgpu::VertexFormat::Sint8x4, + VertexFormat::UNorm8x2 => wgpu::VertexFormat::Unorm8x2, + VertexFormat::UNorm8x4 => wgpu::VertexFormat::Unorm8x4, + VertexFormat::Norm8x2 => wgpu::VertexFormat::Snorm8x2, + VertexFormat::Norm8x4 => wgpu::VertexFormat::Snorm8x4, + VertexFormat::UInt16x2 => wgpu::VertexFormat::Uint16x2, + VertexFormat::UInt16x4 => wgpu::VertexFormat::Uint16x4, + VertexFormat::Int16x2 => wgpu::VertexFormat::Sint16x2, + VertexFormat::Int16x4 => wgpu::VertexFormat::Sint16x4, + VertexFormat::UNorm16x2 => wgpu::VertexFormat::Unorm16x2, + VertexFormat::UNorm16x4 => wgpu::VertexFormat::Unorm16x4, + VertexFormat::Norm16x2 => wgpu::VertexFormat::Snorm16x2, + VertexFormat::Norm16x4 => wgpu::VertexFormat::Snorm16x4, + VertexFormat::Float16x2 => wgpu::VertexFormat::Float16x2, + VertexFormat::Float16x4 => wgpu::VertexFormat::Float16x4, + VertexFormat::Float32 => wgpu::VertexFormat::Float32, + VertexFormat::Float32x2 => wgpu::VertexFormat::Float32x2, + VertexFormat::Float32x3 => wgpu::VertexFormat::Float32x3, + VertexFormat::Float32x4 => wgpu::VertexFormat::Float32x4, + VertexFormat::UInt32 => wgpu::VertexFormat::Uint32, + VertexFormat::UInt32x2 => wgpu::VertexFormat::Uint32x2, + VertexFormat::UInt32x3 => wgpu::VertexFormat::Uint32x3, + VertexFormat::UInt32x4 => wgpu::VertexFormat::Uint32x4, + VertexFormat::Int32 => wgpu::VertexFormat::Sint32, + VertexFormat::Int32x2 => wgpu::VertexFormat::Sint32x2, + VertexFormat::Int32x3 => wgpu::VertexFormat::Sint32x3, + VertexFormat::Int32x4 => wgpu::VertexFormat::Sint32x4, + VertexFormat::Float64 => wgpu::VertexFormat::Float64, + VertexFormat::Float64x2 => wgpu::VertexFormat::Float64x2, + VertexFormat::Float64x3 => wgpu::VertexFormat::Float64x3, + VertexFormat::Float64x4 => wgpu::VertexFormat::Float64x4, + } + } +} diff --git a/Source/Render/mod.rs b/Source/Render/mod.rs index 9745912..0c630dc 100644 --- a/Source/Render/mod.rs +++ b/Source/Render/mod.rs @@ -1,27 +1,67 @@ -#[path = "DrawModel.rs"] -mod _DrawModel; -pub use self::_DrawModel::*; +//#[path = "DrawModel.rs"] +//mod _DrawModel; +//pub use self::_DrawModel::*; -#[path = "Instance.rs"] -mod _Instance; -pub use self::_Instance::*; +#[path = "IndexFormat.rs"] +mod _IndexFormat; +pub use self::_IndexFormat::*; -#[path = "Material.rs"] -mod _Material; -pub use self::_Material::*; +//#[path = "Instance.rs"] +//mod _Instance; +//pub use self::_Instance::*; -#[path = "Model.rs"] -mod _Model; -pub use self::_Model::*; +//#[path = "Material.rs"] +//mod _Material; +//pub use self::_Material::*; -#[path = "Mesh.rs"] -mod _Mesh; -pub use self::_Mesh::*; +//#[path = "Model.rs"] +//mod _Model; +//pub use self::_Model::*; -#[path = "Texture.rs"] -mod _Texture; -pub use self::_Texture::*; +#[path = "PolygonMode.rs"] +mod _PolygonMode; +pub use self::_PolygonMode::*; + +#[path = "Renderer.rs"] +mod _Renderer; +pub use self::_Renderer::*; + +#[path = "StepMode.rs"] +mod _StepMode; +pub use self::_StepMode::*; + +//#[path = "Mesh.rs"] +//mod _Mesh; +//pub use self::_Mesh::*; + +//#[path = "Texture.rs"] +//mod _Texture; +//pub use self::_Texture::*; #[path = "Vertex.rs"] mod _Vertex; pub use self::_Vertex::*; + +#[path = "VertexAttribute.rs"] +mod _VertexAttribute; +pub use self::_VertexAttribute::*; + +#[path = "VertexBufferLayout.rs"] +mod _VertexBufferLayout; +pub use self::_VertexBufferLayout::*; + +#[path = "VertexFormat.rs"] +mod _VertexFormat; +pub use self::_VertexFormat::*; + +#[path = "VertexBuffer.rs"] +mod _VertexBuffer; +pub use self::_VertexBuffer::*; + +#[path = "IndexBuffer.rs"] +mod _IndexBuffer; +pub use self::_IndexBuffer::*; + +#[path = "UniformBuffer.rs"] +mod _UniformBuffer; +pub use self::_UniformBuffer::*; diff --git a/Source/Runtime.rs b/Source/Runtime.rs index 24d060d..92026bb 100644 --- a/Source/Runtime.rs +++ b/Source/Runtime.rs @@ -1,4 +1,5 @@ -use crate::{Display, State}; +use crate::Render::Renderer; +use crate::State; use anyhow::Result; use std::time::Instant; use winit::dpi::LogicalSize; @@ -20,9 +21,9 @@ impl Runtime { .with_inner_size(LogicalSize::new(1280, 720)) .build(&event_loop)?; - let mut display = pollster::block_on(Display::New(window))?; + let mut renderer = pollster::block_on(Renderer::New(window))?; - let mut app = T::Init(&display)?; + let mut app = T::Init(&renderer)?; let mut last_update = Instant::now(); @@ -41,20 +42,20 @@ impl Runtime { Event::Resumed => is_resumed = true, Event::Suspended => is_resumed = false, Event::RedrawRequested(window_id) => { - if window_id == display.window.id() { + if window_id == renderer.window.id() { let now = Instant::now(); let delta = now - last_update; last_update = now; - app.Update(&display, delta); + app.Update(&renderer, delta); - match app.Draw(&mut display) { + match app.Draw(&mut renderer) { Ok(_) => {} // Reconfigure the surface if lost Err(wgpu::SurfaceError::Lost) => { - let size = display.window.inner_size(); - display.Resize(size.width, size.height); - app.Resize(&display); + let size = renderer.window.inner_size(); + renderer.Resize(size.width, size.height); + app.Resize(&renderer); } // The system is out of memory, we should probably quit Err(wgpu::SurfaceError::OutOfMemory) => { @@ -69,15 +70,15 @@ impl Runtime { } Event::MainEventsCleared => { if is_focused && is_resumed && !is_redraw_requested { - display.window.request_redraw(); + renderer.window.request_redraw(); is_redraw_requested = true; } else { // Freeze time while the app is not in the foreground last_update = Instant::now(); } } - Event::WindowEvent { event, window_id } if window_id == display.window.id() => { - if !app.Input(&display, &event) { + Event::WindowEvent { event, window_id } if window_id == renderer.window.id() => { + if !app.Input(&renderer, &event) { match event { WindowEvent::CloseRequested | WindowEvent::KeyboardInput { @@ -91,12 +92,12 @@ impl Runtime { } => *control_flow = ControlFlow::Exit, WindowEvent::Focused(focused) => is_focused = focused, WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - display.Resize(new_inner_size.width, new_inner_size.height); - app.Resize(&display); + renderer.Resize(new_inner_size.width, new_inner_size.height); + app.Resize(&renderer); } WindowEvent::Resized(physical_size) => { - display.Resize(physical_size.width, physical_size.height); - app.Resize(&display); + renderer.Resize(physical_size.width, physical_size.height); + app.Resize(&renderer); } _ => {} } diff --git a/Source/Shader/Shader.rs b/Source/Shader/Shader.rs new file mode 100644 index 0000000..b23689e --- /dev/null +++ b/Source/Shader/Shader.rs @@ -0,0 +1,46 @@ +use super::{ShaderSource, ShaderStage}; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Shader { + pub label: Cow<'static, str>, + pub source: ShaderSource, + pub stage: ShaderStage, +} + +impl Shader { + pub fn New(stage: ShaderStage, source: ShaderSource) -> Self { + Self { + label: "".into(), + source, + stage, + } + } + + /* + pub fn FromSpirv(spirv: &[u8]) -> Self { + Self { + label: "".into(), + source: ShaderSource::Spirv(spirv), + stage, + } + } + */ + + pub fn FromGlsl(stage: ShaderStage, glsl: &str) -> Self { + Self { + label: "".into(), + source: ShaderSource::Glsl(glsl.to_string()), + stage, + } + } + + pub fn FromWgsl(wgsl: &str) -> Self { + Self { + label: "".into(), + source: ShaderSource::Wgsl(wgsl.to_string()), + stage: ShaderStage::Multiple, + } + } +} diff --git a/Source/Shader/ShaderSource.rs b/Source/Shader/ShaderSource.rs new file mode 100644 index 0000000..2365b56 --- /dev/null +++ b/Source/Shader/ShaderSource.rs @@ -0,0 +1,22 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum ShaderSource { + Glsl(String), + Spirv(Vec), + Wgsl(String), +} + +impl ShaderSource { + pub fn SpirvFromBytes(bytes: &[u8]) -> ShaderSource { + ShaderSource::Spirv(Vec::from(bytes)) + } + + pub fn WgslToString(&self) -> Option<&String> { + if let ShaderSource::Wgsl(s) = &self { + Some(s) + } else { + None + } + } +} diff --git a/Source/Shader/ShaderStage.rs b/Source/Shader/ShaderStage.rs new file mode 100644 index 0000000..1d7befc --- /dev/null +++ b/Source/Shader/ShaderStage.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +pub enum ShaderStage { + /// Handles the processing of individual vertices + Vertex = 0, + + /// Process a fragment generated by the rasterization into a set of colors and a single depth value. + Fragment = 1, + + /// Used entirely for computing arbitrary information + Compute = 2, + + /// Multi-stage shader, used with wgsl sources. + Multiple = 3, +} diff --git a/Source/Shader/mod.rs b/Source/Shader/mod.rs new file mode 100644 index 0000000..cbb9808 --- /dev/null +++ b/Source/Shader/mod.rs @@ -0,0 +1,11 @@ +#[path = "Shader.rs"] +mod _Shader; +pub use self::_Shader::*; + +#[path = "ShaderSource.rs"] +mod _ShaderSource; +pub use self::_ShaderSource::*; + +#[path = "ShaderStage.rs"] +mod _ShaderStage; +pub use self::_ShaderStage::*; diff --git a/Source/State.rs b/Source/State.rs index 24164ff..55415bd 100644 --- a/Source/State.rs +++ b/Source/State.rs @@ -1,13 +1,13 @@ -use crate::Display; +use crate::Render::Renderer; use anyhow::Result; use std::time::Duration; use winit::event::*; /// Represents a application with reactive state. pub trait State: Sized + 'static { - fn Init(display: &Display) -> Result; - fn Input(&mut self, display: &Display, event: &WindowEvent) -> bool; - fn Update(&mut self, display: &Display, delta: Duration); - fn Resize(&mut self, display: &Display); - fn Draw(&mut self, display: &mut Display) -> Result<(), wgpu::SurfaceError>; + fn Init(renderer: &Renderer) -> Result; + fn Input(&mut self, renderer: &Renderer, event: &WindowEvent) -> bool; + fn Update(&mut self, renderer: &Renderer, delta: Duration); + fn Resize(&mut self, renderer: &Renderer); + fn Draw(&mut self, renderer: &mut Renderer) -> Result<(), wgpu::SurfaceError>; } diff --git a/Source/lib.rs b/Source/lib.rs index 0a98d61..473430b 100644 --- a/Source/lib.rs +++ b/Source/lib.rs @@ -1,11 +1,9 @@ #![allow(non_snake_case)] pub mod Camera; +pub mod Color; pub mod Render; - -#[path = "Display.rs"] -mod _Display; -pub use self::_Display::*; +pub mod Shader; #[path = "Runtime.rs"] mod _Runtime;