From 79bb83732fd70d446704e51715e1b0440dac310f Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 20 Jan 2020 00:57:54 -0800 Subject: [PATCH] more graph work --- src/app/app_builder.rs | 35 ++- src/app/mod.rs | 1 + src/app/system_stage.rs | 1 + src/render/render_graph_2/draw_target.rs | 41 +++ src/render/render_graph_2/example.rs | 52 ---- src/render/render_graph_2/mod.rs | 6 +- .../pipelines/forward/forward.frag | 44 +++ .../pipelines/forward/forward.vert | 23 ++ .../render_graph_2/pipelines/forward/mod.rs | 92 ++++++ src/render/render_graph_2/pipelines/mod.rs | 3 + src/render/render_graph_2/render_graph.rs | 16 +- src/render/render_graph_2/renderer.rs | 252 +--------------- src/render/render_graph_2/resource/mod.rs | 1 + src/render/render_graph_2/resource/texture.rs | 1 + src/render/render_graph_2/wgpu_renderer.rs | 274 ++++++++++++++++++ 15 files changed, 516 insertions(+), 326 deletions(-) create mode 100644 src/app/system_stage.rs create mode 100644 src/render/render_graph_2/draw_target.rs delete mode 100644 src/render/render_graph_2/example.rs create mode 100644 src/render/render_graph_2/pipelines/forward/forward.frag create mode 100644 src/render/render_graph_2/pipelines/forward/forward.vert create mode 100644 src/render/render_graph_2/pipelines/forward/mod.rs create mode 100644 src/render/render_graph_2/pipelines/mod.rs create mode 100644 src/render/render_graph_2/resource/mod.rs create mode 100644 src/render/render_graph_2/resource/texture.rs create mode 100644 src/render/render_graph_2/wgpu_renderer.rs diff --git a/src/app/app_builder.rs b/src/app/app_builder.rs index 130b7afd02..a2b6ddee2e 100644 --- a/src/app/app_builder.rs +++ b/src/app/app_builder.rs @@ -1,24 +1,23 @@ use crate::{ - app::App, + app::{App, system_stage}, asset::*, core::Time, legion::prelude::{Runnable, Schedulable, Schedule, Universe, World}, - render::{passes::*, *}, render::render_graph_2, + render::render_graph_2::{pipelines::*, wgpu_renderer::WgpuRenderer}, + render::{passes::*, *}, ui, }; use bevy_transform::transform_system_bundle; use std::collections::HashMap; -pub const UPDATE: &str = "update"; - pub struct AppBuilder { pub world: World, pub universe: Universe, pub legacy_render_graph: Option, pub renderer: Option>, - pub render_graph: render_graph_2::RenderGraph, + pub render_graph_builder: render_graph_2::RenderGraphBuilder, pub system_stages: HashMap>>, pub runnable_stages: HashMap>>, pub stage_order: Vec, @@ -31,7 +30,7 @@ impl AppBuilder { AppBuilder { universe, world, - render_graph: render_graph_2::RenderGraph::default(), + render_graph_builder: render_graph_2::RenderGraphBuilder::new(), legacy_render_graph: None, renderer: None, system_stages: HashMap::new(), @@ -66,7 +65,7 @@ impl AppBuilder { schedule_builder.build(), self.legacy_render_graph, self.renderer, - self.render_graph, + self.render_graph_builder.build(), ) } @@ -85,7 +84,7 @@ impl AppBuilder { } pub fn add_system(self, system: Box) -> Self { - self.add_system_to_stage(UPDATE, system) + self.add_system_to_stage(system_stage::UPDATE, system) } pub fn add_system_to_stage(mut self, stage_name: &str, system: Box) -> Self { @@ -171,23 +170,31 @@ impl AppBuilder { self } + pub fn add_render_graph_defaults(mut self) -> Self { + self.render_graph_builder = self + .render_graph_builder + .add_forward_pass() + .add_forward_pipeline(); + + self + } + pub fn add_wgpu_renderer(mut self) -> Self { - self.renderer = Some(Box::new(render_graph_2::WgpuRenderer::new())); + self.renderer = Some(Box::new(WgpuRenderer::new())); self } pub fn add_defaults_legacy(self) -> Self { - self - .with_legacy_render_graph() + self.with_legacy_render_graph() .add_default_resources() .add_default_passes() .add_default_systems() } pub fn add_defaults(self) -> Self { - self - .add_default_resources() + self.add_default_resources() .add_default_systems() + .add_render_graph_defaults() .add_wgpu_renderer() } -} \ No newline at end of file +} diff --git a/src/app/mod.rs b/src/app/mod.rs index 17f0331cde..996ed037fd 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,5 +1,6 @@ mod app; mod app_builder; +pub mod system_stage; pub use app::App; pub use app_builder::AppBuilder; diff --git a/src/app/system_stage.rs b/src/app/system_stage.rs new file mode 100644 index 0000000000..06781be0bc --- /dev/null +++ b/src/app/system_stage.rs @@ -0,0 +1 @@ +pub const UPDATE: &str = "update"; \ No newline at end of file diff --git a/src/render/render_graph_2/draw_target.rs b/src/render/render_graph_2/draw_target.rs new file mode 100644 index 0000000000..b14de073c1 --- /dev/null +++ b/src/render/render_graph_2/draw_target.rs @@ -0,0 +1,41 @@ +use crate::{ + asset::{AssetStorage, Handle, Mesh}, + legion::prelude::*, + render::{ + render_graph_2::{ShaderMaterials, RenderPass}, + Instanced, + }, +}; + +// A set of draw calls. ex: get + draw meshes, get + draw instanced meshes, draw ui meshes, etc +pub type DrawTarget = fn(world: &World, render_pass: &mut dyn RenderPass); + +pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) { + let mut mesh_storage = world.resources.get_mut::>().unwrap(); + let mut last_mesh_id = None; + let mesh_query = + <(Read, Read>)>::query().filter(!component::()); + for (material, mesh) in mesh_query.iter(world) { + let current_mesh_id = mesh.id; + + let mut should_load_mesh = last_mesh_id == None; + if let Some(last) = last_mesh_id { + should_load_mesh = last != current_mesh_id; + } + + if should_load_mesh { + if let Some(mesh_asset) = mesh_storage.get(mesh.id) { + // render_pass.load_mesh(mesh.id, mesh_asset); + // render_pass.set_index_buffer(mesh_asset.index_buffer.as_ref().unwrap(), 0); + // render_pass.set_vertex_buffers(0, &[(&mesh_asset.vertex_buffer.as_ref().unwrap(), 0)]); + }; + } + + if let Some(ref mesh_asset) = mesh_storage.get(mesh.id) { + // pass.set_bind_group(1, material.bind_group.as_ref().unwrap(), &[]); + // pass.draw_indexed(0..mesh_asset.indices.len() as u32, 0, 0..1); + }; + + last_mesh_id = Some(current_mesh_id); + } +} \ No newline at end of file diff --git a/src/render/render_graph_2/example.rs b/src/render/render_graph_2/example.rs deleted file mode 100644 index 082dffec56..0000000000 --- a/src/render/render_graph_2/example.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::render::{render_graph_2::*, shader::{Shader, ShaderStage}, Vertex}; - -fn build_example_graph() -> RenderGraph { - // TODO: read this from swap chain - let swap_chain_color_format = wgpu::TextureFormat::Bgra8UnormSrgb; - RenderGraph::build() - .add_pass( - "main", - PassDescriptor { - color_attachments: Vec::new(), - depth_stencil_attachment: None, - sample_count: 1, - }, - ) - .add_pipeline( - "forward", - PipelineDescriptor::build(Shader::from_glsl( - include_str!("../passes/forward/forward.vert"), - ShaderStage::Vertex, - )) - .with_fragment_shader(Shader::from_glsl( - include_str!("../passes/forward/forward.vert"), - ShaderStage::Fragment, - )) - .with_rasterization_state(wgpu::RasterizationStateDescriptor { - front_face: wgpu::FrontFace::Ccw, - cull_mode: wgpu::CullMode::Back, - depth_bias: 0, - depth_bias_slope_scale: 0.0, - depth_bias_clamp: 0.0, - }) - .with_depth_stencil_state(wgpu::DepthStencilStateDescriptor { - format: wgpu::TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, - stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, - stencil_read_mask: 0, - stencil_write_mask: 0, - }) - .with_color_state(wgpu::ColorStateDescriptor { - format: swap_chain_color_format, - color_blend: wgpu::BlendDescriptor::REPLACE, - alpha_blend: wgpu::BlendDescriptor::REPLACE, - write_mask: wgpu::ColorWrite::ALL, - }) - .with_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor()) - .with_draw_target(mesh_draw_target) - .build() - ) - .build() -} diff --git a/src/render/render_graph_2/mod.rs b/src/render/render_graph_2/mod.rs index 37133bb1fe..dba455f4ca 100644 --- a/src/render/render_graph_2/mod.rs +++ b/src/render/render_graph_2/mod.rs @@ -1,15 +1,19 @@ +pub mod pipelines; +pub mod resource; +pub mod wgpu_renderer; mod pipeline; mod pass; mod renderer; mod shader; mod render_graph; -mod example; +mod draw_target; pub use pipeline::*; pub use pass::*; pub use renderer::*; pub use shader::*; pub use render_graph::*; +pub use draw_target::*; // a named graphics resource provided by a resource provider pub struct Resource { diff --git a/src/render/render_graph_2/pipelines/forward/forward.frag b/src/render/render_graph_2/pipelines/forward/forward.frag new file mode 100644 index 0000000000..3fecd3efd0 --- /dev/null +++ b/src/render/render_graph_2/pipelines/forward/forward.frag @@ -0,0 +1,44 @@ +#version 450 + +const int MAX_LIGHTS = 10; + +layout(location = 0) in vec3 v_Normal; +layout(location = 1) in vec4 v_Position; + +layout(location = 0) out vec4 o_Target; + +struct Light { + mat4 proj; + vec4 pos; + vec4 color; +}; + +layout(set = 0, binding = 0) uniform Globals { + mat4 u_ViewProj; + uvec4 u_NumLights; +}; +layout(set = 0, binding = 1) uniform Lights { + Light u_Lights[MAX_LIGHTS]; +}; + +layout(set = 1, binding = 0) uniform Entity { + mat4 u_World; + vec4 u_Color; +}; + +void main() { + vec3 normal = normalize(v_Normal); + vec3 ambient = vec3(0.05, 0.05, 0.05); + // accumulate color + vec3 color = ambient; + for (int i=0; i Self; +} + +impl ForwardPipelineBuilder for RenderGraphBuilder { + fn add_forward_pipeline(self) -> Self { + self.add_pipeline( + "forward", + PipelineDescriptor::build(Shader::from_glsl( + include_str!("forward.vert"), + ShaderStage::Vertex, + )) + .with_fragment_shader(Shader::from_glsl( + include_str!("forward.frag"), + ShaderStage::Fragment, + )) + .with_rasterization_state(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }) + .with_depth_stencil_state(wgpu::DepthStencilStateDescriptor { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, + stencil_read_mask: 0, + stencil_write_mask: 0, + }) + .with_color_state(wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }) + .with_vertex_buffer_descriptor(Vertex::get_vertex_buffer_descriptor()) + .with_draw_target(mesh_draw_target) + .build(), + ) + } +} + +pub trait ForwardPassBuilder { + fn add_forward_pass(self) -> Self; +} + +impl ForwardPassBuilder for RenderGraphBuilder { + fn add_forward_pass(self) -> Self { + self.add_pass( + "main", + PassDescriptor { + color_attachments: vec![RenderPassColorAttachmentDescriptor { + attachment: resource::texture::SWAP_CHAIN.to_string(), + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.3, + g: 0.4, + b: 0.5, + a: 1.0, + }, + }], + depth_stencil_attachment: None, + // depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor { + // attachment: "forward_depth".to_string(), + // depth_load_op: wgpu::LoadOp::Clear, + // depth_store_op: wgpu::StoreOp::Store, + // stencil_load_op: wgpu::LoadOp::Clear, + // stencil_store_op: wgpu::StoreOp::Store, + // clear_depth: 1.0, + // clear_stencil: 0, + // }), + sample_count: 1, + }, + ) + } +} diff --git a/src/render/render_graph_2/pipelines/mod.rs b/src/render/render_graph_2/pipelines/mod.rs new file mode 100644 index 0000000000..f36f84ce95 --- /dev/null +++ b/src/render/render_graph_2/pipelines/mod.rs @@ -0,0 +1,3 @@ +mod forward; + +pub use forward::*; \ No newline at end of file diff --git a/src/render/render_graph_2/render_graph.rs b/src/render/render_graph_2/render_graph.rs index 9220ba395f..53afe1c9b6 100644 --- a/src/render/render_graph_2/render_graph.rs +++ b/src/render/render_graph_2/render_graph.rs @@ -17,21 +17,19 @@ impl Default for RenderGraph { } } -impl RenderGraph { - pub fn build() -> RenderGraphBuilder { - RenderGraphBuilder { - render_graph: RenderGraph::default(), - current_pass: None, - } - } -} - pub struct RenderGraphBuilder { render_graph: RenderGraph, current_pass: Option, } impl RenderGraphBuilder { + pub fn new() -> Self { + RenderGraphBuilder { + render_graph: RenderGraph::default(), + current_pass: None, + } + } + pub fn add_pass(mut self, name: &str, pass: PassDescriptor) -> Self { self.current_pass = Some(name.to_string()); self.render_graph diff --git a/src/render/render_graph_2/renderer.rs b/src/render/render_graph_2/renderer.rs index 36248d813e..538d09c0ca 100644 --- a/src/render/render_graph_2/renderer.rs +++ b/src/render/render_graph_2/renderer.rs @@ -1,50 +1,4 @@ -use crate::{ - asset::{AssetStorage, Handle, Mesh}, - legion::prelude::*, - render::{ - render_graph_2::{PassDescriptor, PipelineDescriptor, RenderGraph, ShaderMaterials}, - Instanced, - }, -}; -use std::{collections::HashMap, ops::Deref}; -use zerocopy::AsBytes; - -// A set of draw calls. ex: get + draw meshes, get + draw instanced meshes, draw ui meshes, etc -// Mesh target -// trait DrawTarget { -// fn draw(device: &wgpu::Device); -// } -pub type DrawTarget = fn(world: &World, render_pass: &mut dyn RenderPass); - -pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) { - let mut mesh_storage = world.resources.get_mut::>().unwrap(); - let mut last_mesh_id = None; - let mesh_query = - <(Read, Read>)>::query().filter(!component::()); - for (material, mesh) in mesh_query.iter(world) { - let current_mesh_id = mesh.id; - - let mut should_load_mesh = last_mesh_id == None; - if let Some(last) = last_mesh_id { - should_load_mesh = last != current_mesh_id; - } - - if should_load_mesh { - if let Some(mesh_asset) = mesh_storage.get(mesh.id) { - // render_pass.load_mesh(mesh.id, mesh_asset); - // render_pass.set_index_buffer(mesh_asset.index_buffer.as_ref().unwrap(), 0); - // render_pass.set_vertex_buffers(0, &[(&mesh_asset.vertex_buffer.as_ref().unwrap(), 0)]); - }; - } - - if let Some(ref mesh_asset) = mesh_storage.get(mesh.id) { - // pass.set_bind_group(1, material.bind_group.as_ref().unwrap(), &[]); - // pass.draw_indexed(0..mesh_asset.indices.len() as u32, 0, 0..1); - }; - - last_mesh_id = Some(current_mesh_id); - } -} +use crate::{asset::Mesh, legion::prelude::*, render::render_graph_2::RenderGraph}; pub trait Renderer { fn initialize(&mut self, world: &mut World); @@ -53,208 +7,6 @@ pub trait Renderer { fn load_mesh(&mut self, asset_id: usize, mesh: &Mesh); } -pub struct WgpuRenderer { - pub device: wgpu::Device, - pub surface: Option, - pub swap_chain_descriptor: wgpu::SwapChainDescriptor, - pub render_pipelines: HashMap, - pub buffers: HashMap, -} - -impl WgpuRenderer { - pub fn new() -> Self { - let adapter = wgpu::Adapter::request( - &wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::Default, - }, - wgpu::BackendBit::PRIMARY, - ) - .unwrap(); - - let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { - extensions: wgpu::Extensions { - anisotropic_filtering: false, - }, - limits: wgpu::Limits::default(), - }); - - let swap_chain_descriptor = wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: 0, - height: 0, - present_mode: wgpu::PresentMode::Vsync, - }; - - WgpuRenderer { - device, - surface: None, - swap_chain_descriptor, - render_pipelines: HashMap::new(), - buffers: HashMap::new(), - } - } - - pub fn create_render_pipeline( - pipeline_descriptor: &PipelineDescriptor, - device: &wgpu::Device, - ) -> wgpu::RenderPipeline { - let vertex_shader_module = pipeline_descriptor - .shader_stages - .vertex - .create_shader_module(device); - let fragment_shader_module = match pipeline_descriptor.shader_stages.fragment { - Some(ref fragment_shader) => Some(fragment_shader.create_shader_module(device)), - None => None, - }; - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - bind_group_layouts: &[], - }); - let render_pipeline_descriptor = wgpu::RenderPipelineDescriptor { - layout: &pipeline_layout, - vertex_stage: wgpu::ProgrammableStageDescriptor { - module: &vertex_shader_module, - entry_point: &pipeline_descriptor.shader_stages.vertex.entry_point, - }, - fragment_stage: match pipeline_descriptor.shader_stages.fragment { - Some(ref fragment_shader) => Some(wgpu::ProgrammableStageDescriptor { - entry_point: &fragment_shader.entry_point, - module: fragment_shader_module.as_ref().unwrap(), - }), - None => None, - }, - rasterization_state: pipeline_descriptor.rasterization_state.clone(), - primitive_topology: pipeline_descriptor.primitive_topology, - color_states: &pipeline_descriptor.color_states, - depth_stencil_state: pipeline_descriptor.depth_stencil_state.clone(), - index_format: pipeline_descriptor.index_format, - vertex_buffers: &pipeline_descriptor - .vertex_buffer_descriptors - .iter() - .map(|v| v.into()) - .collect::>(), - sample_count: pipeline_descriptor.sample_count, - sample_mask: pipeline_descriptor.sample_mask, - alpha_to_coverage_enabled: pipeline_descriptor.alpha_to_coverage_enabled, - }; - - device.create_render_pipeline(&render_pipeline_descriptor) - } - - pub fn create_render_pass<'a>( - pass_descriptor: &PassDescriptor, - encoder: &'a mut wgpu::CommandEncoder, - frame: &'a wgpu::SwapChainOutput, - ) -> wgpu::RenderPass<'a> { - // TODO: fill this in - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[], - depth_stencil_attachment: None, - }) - } -} - -impl Renderer for WgpuRenderer { - fn initialize(&mut self, world: &mut World) { - let (surface, window_size) = { - let window = world.resources.get::().unwrap(); - let surface = wgpu::Surface::create(window.deref()); - let window_size = window.inner_size(); - (surface, window_size) - }; - - self.surface = Some(surface); - self.resize(world, window_size.width, window_size.height); - } - - fn resize(&mut self, world: &mut World, width: u32, height: u32) { - let swap_chain = self - .device - .create_swap_chain(self.surface.as_ref().unwrap(), &self.swap_chain_descriptor); - self.swap_chain_descriptor.width = width; - self.swap_chain_descriptor.height = height; - - // WgpuRenderer can't own swap_chain without creating lifetime ergonomics issues - world.resources.insert(swap_chain); - } - - fn process_render_graph(&mut self, render_graph: &RenderGraph, world: &mut World) { - let mut swap_chain = world.resources.get_mut::().unwrap(); - let frame = swap_chain - .get_next_texture() - .expect("Timeout when acquiring next swap chain texture"); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); - - for (pass_name, pass_descriptor) in render_graph.pass_descriptors.iter() { - let mut render_pass = - WgpuRenderer::create_render_pass(pass_descriptor, &mut encoder, &frame); - if let Some(pass_pipelines) = render_graph.pass_pipelines.get(pass_name) { - for pass_pipeline in pass_pipelines.iter() { - if let Some(pipeline_descriptor) = - render_graph.pipeline_descriptors.get(pass_pipeline) - { - if let None = self.render_pipelines.get(pass_pipeline) { - let render_pipeline = WgpuRenderer::create_render_pipeline( - pipeline_descriptor, - &self.device, - ); - self.render_pipelines - .insert(pass_pipeline.to_string(), render_pipeline); - } - - let mut render_pass = WgpuRenderPass { - render_pass: &mut render_pass, - renderer: &self, - }; - for draw_target in pipeline_descriptor.draw_targets.iter() { - draw_target(world, &mut render_pass); - } - } - } - } - } - } - - fn load_mesh(&mut self, asset_id: usize, mesh: &Mesh) { - if let None = mesh.vertex_buffer { - self.buffers.insert( - format!("meshv{}", asset_id), - self.device - .create_buffer_with_data(mesh.vertices.as_bytes(), wgpu::BufferUsage::VERTEX), - ); - } - - if let None = mesh.index_buffer { - self.buffers.insert( - format!("meshi{}", asset_id), - self.device - .create_buffer_with_data(mesh.indices.as_bytes(), wgpu::BufferUsage::INDEX), - ); - } - } -} - pub trait RenderPass { fn set_index_buffer(&mut self, buffer: &wgpu::Buffer, offset: wgpu::BufferAddress); -} - -pub struct WgpuRenderPass<'a, 'b, 'c> { - pub render_pass: &'b mut wgpu::RenderPass<'a>, - pub renderer: &'c WgpuRenderer, -} - -impl<'a, 'b, 'c> RenderPass for WgpuRenderPass<'a, 'b, 'c> { - fn set_index_buffer(&mut self, buffer: &wgpu::Buffer, offset: wgpu::BufferAddress) { - self.render_pass.set_index_buffer(buffer, offset); - } -} - -// pub trait RenderResources { -// fn get_buffer(name: &str) -> Option; -// fn get_texture(name: &str) -> Option; -// fn get_sampler(name: &str) -> Option; -// } +} \ No newline at end of file diff --git a/src/render/render_graph_2/resource/mod.rs b/src/render/render_graph_2/resource/mod.rs new file mode 100644 index 0000000000..f41981d2ba --- /dev/null +++ b/src/render/render_graph_2/resource/mod.rs @@ -0,0 +1 @@ +pub mod texture; \ No newline at end of file diff --git a/src/render/render_graph_2/resource/texture.rs b/src/render/render_graph_2/resource/texture.rs new file mode 100644 index 0000000000..9ec8fd3bdd --- /dev/null +++ b/src/render/render_graph_2/resource/texture.rs @@ -0,0 +1 @@ +pub const SWAP_CHAIN: &str = "swap_chain"; \ No newline at end of file diff --git a/src/render/render_graph_2/wgpu_renderer.rs b/src/render/render_graph_2/wgpu_renderer.rs new file mode 100644 index 0000000000..b7609b1047 --- /dev/null +++ b/src/render/render_graph_2/wgpu_renderer.rs @@ -0,0 +1,274 @@ +use crate::{ + asset::Mesh, + legion::prelude::*, + render::render_graph_2::{ + resource, PassDescriptor, PipelineDescriptor, RenderGraph, RenderPass, + RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, Renderer, + }, +}; +use std::{collections::HashMap, ops::Deref}; +use zerocopy::AsBytes; + +pub struct WgpuRenderer { + pub device: wgpu::Device, + pub queue: wgpu::Queue, + pub surface: Option, + pub swap_chain_descriptor: wgpu::SwapChainDescriptor, + pub render_pipelines: HashMap, + pub buffers: HashMap, + pub textures: HashMap, +} + +impl WgpuRenderer { + pub fn new() -> Self { + let adapter = wgpu::Adapter::request( + &wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::Default, + }, + wgpu::BackendBit::PRIMARY, + ) + .unwrap(); + + let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: wgpu::Limits::default(), + }); + + let swap_chain_descriptor = wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: 0, + height: 0, + present_mode: wgpu::PresentMode::Vsync, + }; + + WgpuRenderer { + device, + queue, + surface: None, + swap_chain_descriptor, + render_pipelines: HashMap::new(), + buffers: HashMap::new(), + textures: HashMap::new(), + } + } + + pub fn create_render_pipeline( + pipeline_descriptor: &PipelineDescriptor, + device: &wgpu::Device, + ) -> wgpu::RenderPipeline { + let vertex_shader_module = pipeline_descriptor + .shader_stages + .vertex + .create_shader_module(device); + let fragment_shader_module = match pipeline_descriptor.shader_stages.fragment { + Some(ref fragment_shader) => Some(fragment_shader.create_shader_module(device)), + None => None, + }; + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[], + }); + let render_pipeline_descriptor = wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vertex_shader_module, + entry_point: &pipeline_descriptor.shader_stages.vertex.entry_point, + }, + fragment_stage: match pipeline_descriptor.shader_stages.fragment { + Some(ref fragment_shader) => Some(wgpu::ProgrammableStageDescriptor { + entry_point: &fragment_shader.entry_point, + module: fragment_shader_module.as_ref().unwrap(), + }), + None => None, + }, + rasterization_state: pipeline_descriptor.rasterization_state.clone(), + primitive_topology: pipeline_descriptor.primitive_topology, + color_states: &pipeline_descriptor.color_states, + depth_stencil_state: pipeline_descriptor.depth_stencil_state.clone(), + index_format: pipeline_descriptor.index_format, + vertex_buffers: &pipeline_descriptor + .vertex_buffer_descriptors + .iter() + .map(|v| v.into()) + .collect::>(), + sample_count: pipeline_descriptor.sample_count, + sample_mask: pipeline_descriptor.sample_mask, + alpha_to_coverage_enabled: pipeline_descriptor.alpha_to_coverage_enabled, + }; + + device.create_render_pipeline(&render_pipeline_descriptor) + } + + pub fn create_render_pass<'a>( + &self, + pass_descriptor: &PassDescriptor, + encoder: &'a mut wgpu::CommandEncoder, + frame: &'a wgpu::SwapChainOutput, + ) -> wgpu::RenderPass<'a> { + // TODO: fill this in + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &pass_descriptor + .color_attachments + .iter() + .map(|c| self.create_wgpu_color_attachment_descriptor(c, frame)) + .collect::>(), + depth_stencil_attachment: pass_descriptor + .depth_stencil_attachment + .as_ref() + .map(|d| self.create_wgpu_depth_stencil_attachment_descriptor(d, frame)), + }) + } + + fn create_wgpu_color_attachment_descriptor<'a>( + &'a self, + color_attachment_descriptor: &RenderPassColorAttachmentDescriptor, + frame: &'a wgpu::SwapChainOutput, + ) -> wgpu::RenderPassColorAttachmentDescriptor<'a> { + let attachment = match color_attachment_descriptor.attachment.as_str() { + resource::texture::SWAP_CHAIN => &frame.view, + _ => self + .textures + .get(&color_attachment_descriptor.attachment) + .unwrap(), + }; + + let resolve_target = match color_attachment_descriptor.resolve_target { + Some(ref target) => match target.as_str() { + resource::texture::SWAP_CHAIN => Some(&frame.view), + _ => Some(&frame.view), + }, + None => None, + }; + + wgpu::RenderPassColorAttachmentDescriptor { + store_op: color_attachment_descriptor.store_op, + load_op: color_attachment_descriptor.load_op, + clear_color: color_attachment_descriptor.clear_color, + attachment, + resolve_target, + } + } + + fn create_wgpu_depth_stencil_attachment_descriptor<'a>( + &'a self, + depth_stencil_attachment_descriptor: &RenderPassDepthStencilAttachmentDescriptor, + frame: &'a wgpu::SwapChainOutput, + ) -> wgpu::RenderPassDepthStencilAttachmentDescriptor<&'a wgpu::TextureView> { + let attachment = match depth_stencil_attachment_descriptor.attachment.as_str() { + resource::texture::SWAP_CHAIN => &frame.view, + _ => self + .textures + .get(&depth_stencil_attachment_descriptor.attachment) + .unwrap(), + }; + + wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment, + clear_depth: depth_stencil_attachment_descriptor.clear_depth, + clear_stencil: depth_stencil_attachment_descriptor.clear_stencil, + depth_load_op: depth_stencil_attachment_descriptor.depth_load_op, + depth_store_op: depth_stencil_attachment_descriptor.depth_store_op, + stencil_load_op: depth_stencil_attachment_descriptor.stencil_load_op, + stencil_store_op: depth_stencil_attachment_descriptor.stencil_store_op, + } + } +} + +impl Renderer for WgpuRenderer { + fn initialize(&mut self, world: &mut World) { + let (surface, window_size) = { + let window = world.resources.get::().unwrap(); + let surface = wgpu::Surface::create(window.deref()); + let window_size = window.inner_size(); + (surface, window_size) + }; + + self.surface = Some(surface); + self.resize(world, window_size.width, window_size.height); + } + + fn resize(&mut self, world: &mut World, width: u32, height: u32) { + let swap_chain = self + .device + .create_swap_chain(self.surface.as_ref().unwrap(), &self.swap_chain_descriptor); + self.swap_chain_descriptor.width = width; + self.swap_chain_descriptor.height = height; + + // WgpuRenderer can't own swap_chain without creating lifetime ergonomics issues + world.resources.insert(swap_chain); + } + + fn process_render_graph(&mut self, render_graph: &RenderGraph, world: &mut World) { + let mut swap_chain = world.resources.get_mut::().unwrap(); + let frame = swap_chain + .get_next_texture() + .expect("Timeout when acquiring next swap chain texture"); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + + for (pass_name, pass_descriptor) in render_graph.pass_descriptors.iter() { + let mut render_pass = self.create_render_pass(pass_descriptor, &mut encoder, &frame); + if let Some(pass_pipelines) = render_graph.pass_pipelines.get(pass_name) { + for pass_pipeline in pass_pipelines.iter() { + if let Some(pipeline_descriptor) = + render_graph.pipeline_descriptors.get(pass_pipeline) + { + if let None = self.render_pipelines.get(pass_pipeline) { + let render_pipeline = WgpuRenderer::create_render_pipeline( + pipeline_descriptor, + &self.device, + ); + self.render_pipelines + .insert(pass_pipeline.to_string(), render_pipeline); + } + + let mut render_pass = WgpuRenderPass { + render_pass: &mut render_pass, + renderer: &self, + }; + for draw_target in pipeline_descriptor.draw_targets.iter() { + draw_target(world, &mut render_pass); + } + } + } + } + } + + let command_buffer = encoder.finish(); + self.queue.submit(&[command_buffer]); + } + + fn load_mesh(&mut self, asset_id: usize, mesh: &Mesh) { + if let None = mesh.vertex_buffer { + self.buffers.insert( + format!("meshv{}", asset_id), + self.device + .create_buffer_with_data(mesh.vertices.as_bytes(), wgpu::BufferUsage::VERTEX), + ); + } + + if let None = mesh.index_buffer { + self.buffers.insert( + format!("meshi{}", asset_id), + self.device + .create_buffer_with_data(mesh.indices.as_bytes(), wgpu::BufferUsage::INDEX), + ); + } + } +} + +pub struct WgpuRenderPass<'a, 'b, 'c> { + pub render_pass: &'b mut wgpu::RenderPass<'a>, + pub renderer: &'c WgpuRenderer, +} + +impl<'a, 'b, 'c> RenderPass for WgpuRenderPass<'a, 'b, 'c> { + fn set_index_buffer(&mut self, buffer: &wgpu::Buffer, offset: wgpu::BufferAddress) { + self.render_pass.set_index_buffer(buffer, offset); + } +}