more graph work
This commit is contained in:
		
							parent
							
								
									54005fcdd0
								
							
						
					
					
						commit
						79bb83732f
					
				| @ -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<RenderGraph>, | ||||
|     pub renderer: Option<Box<dyn render_graph_2::Renderer>>, | ||||
|     pub render_graph: render_graph_2::RenderGraph, | ||||
|     pub render_graph_builder: render_graph_2::RenderGraphBuilder, | ||||
|     pub system_stages: HashMap<String, Vec<Box<dyn Schedulable>>>, | ||||
|     pub runnable_stages: HashMap<String, Vec<Box<dyn Runnable>>>, | ||||
|     pub stage_order: Vec<String>, | ||||
| @ -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<dyn Schedulable>) -> 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<dyn Schedulable>) -> 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() | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| mod app; | ||||
| mod app_builder; | ||||
| pub mod system_stage; | ||||
| 
 | ||||
| pub use app::App; | ||||
| pub use app_builder::AppBuilder; | ||||
|  | ||||
							
								
								
									
										1
									
								
								src/app/system_stage.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/system_stage.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pub const UPDATE: &str = "update"; | ||||
							
								
								
									
										41
									
								
								src/render/render_graph_2/draw_target.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/render/render_graph_2/draw_target.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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::<AssetStorage<Mesh>>().unwrap(); | ||||
|     let mut last_mesh_id = None; | ||||
|     let mesh_query = | ||||
|         <(Read<ShaderMaterials>, Read<Handle<Mesh>>)>::query().filter(!component::<Instanced>()); | ||||
|     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); | ||||
|     } | ||||
| } | ||||
| @ -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() | ||||
| } | ||||
| @ -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 { | ||||
|  | ||||
							
								
								
									
										44
									
								
								src/render/render_graph_2/pipelines/forward/forward.frag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/render/render_graph_2/pipelines/forward/forward.frag
									
									
									
									
									
										Normal file
									
								
							| @ -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<int(u_NumLights.x) && i<MAX_LIGHTS; ++i) { | ||||
|         Light light = u_Lights[i]; | ||||
|         // compute Lambertian diffuse term | ||||
|         vec3 light_dir = normalize(light.pos.xyz - v_Position.xyz); | ||||
|         float diffuse = max(0.0, dot(normal, light_dir)); | ||||
|         // add light contribution | ||||
|         color += diffuse * light.color.xyz; | ||||
|     } | ||||
|     // multiply the light by material color | ||||
|     o_Target = vec4(color, 1.0) * u_Color; | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/render/render_graph_2/pipelines/forward/forward.vert
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/render/render_graph_2/pipelines/forward/forward.vert
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| #version 450 | ||||
| 
 | ||||
| layout(location = 0) in vec4 a_Pos; | ||||
| layout(location = 1) in vec4 a_Normal; | ||||
| layout(location = 2) in vec4 a_Uv; | ||||
| 
 | ||||
| layout(location = 0) out vec3 v_Normal; | ||||
| layout(location = 1) out vec4 v_Position; | ||||
| 
 | ||||
| layout(set = 0, binding = 0) uniform Globals { | ||||
|     mat4 u_ViewProj; | ||||
|     uvec4 u_NumLights; | ||||
| }; | ||||
| layout(set = 1, binding = 0) uniform Entity { | ||||
|     mat4 u_World; | ||||
|     vec4 u_Color; | ||||
| }; | ||||
| 
 | ||||
| void main() { | ||||
|     v_Normal = mat3(u_World) * vec3(a_Normal.xyz); | ||||
|     v_Position = u_World * vec4(a_Pos); | ||||
|     gl_Position = u_ViewProj * v_Position; | ||||
| } | ||||
							
								
								
									
										92
									
								
								src/render/render_graph_2/pipelines/forward/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/render/render_graph_2/pipelines/forward/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | ||||
| use crate::render::{ | ||||
|     Vertex, | ||||
|     { | ||||
|         render_graph_2::{ | ||||
|             mesh_draw_target, PassDescriptor, PipelineDescriptor, RenderGraphBuilder, | ||||
|             RenderPassColorAttachmentDescriptor, RenderPassDepthStencilAttachmentDescriptor, | ||||
|             resource, | ||||
|         }, | ||||
|         shader::{Shader, ShaderStage}, | ||||
|     }, | ||||
| }; | ||||
| pub trait ForwardPipelineBuilder { | ||||
|     fn add_forward_pipeline(self) -> 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, | ||||
|             }, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										3
									
								
								src/render/render_graph_2/pipelines/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/render/render_graph_2/pipelines/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| mod forward; | ||||
| 
 | ||||
| pub use forward::*; | ||||
| @ -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<String>, | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
|  | ||||
| @ -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::<AssetStorage<Mesh>>().unwrap(); | ||||
|     let mut last_mesh_id = None; | ||||
|     let mesh_query = | ||||
|         <(Read<ShaderMaterials>, Read<Handle<Mesh>>)>::query().filter(!component::<Instanced>()); | ||||
|     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<wgpu::Surface>, | ||||
|     pub swap_chain_descriptor: wgpu::SwapChainDescriptor, | ||||
|     pub render_pipelines: HashMap<String, wgpu::RenderPipeline>, | ||||
|     pub buffers: HashMap<String, wgpu::Buffer>, | ||||
| } | ||||
| 
 | ||||
| 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::<Vec<wgpu::VertexBufferDescriptor>>(), | ||||
|             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::<winit::window::Window>().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::<wgpu::SwapChain>().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<Buffer>;
 | ||||
| //     fn get_texture(name: &str) -> Option<Texture>;
 | ||||
| //     fn get_sampler(name: &str) -> Option<Sampler>;
 | ||||
| // }
 | ||||
| } | ||||
							
								
								
									
										1
									
								
								src/render/render_graph_2/resource/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/render/render_graph_2/resource/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pub mod texture; | ||||
							
								
								
									
										1
									
								
								src/render/render_graph_2/resource/texture.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/render/render_graph_2/resource/texture.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pub const SWAP_CHAIN: &str = "swap_chain"; | ||||
							
								
								
									
										274
									
								
								src/render/render_graph_2/wgpu_renderer.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								src/render/render_graph_2/wgpu_renderer.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<wgpu::Surface>, | ||||
|     pub swap_chain_descriptor: wgpu::SwapChainDescriptor, | ||||
|     pub render_pipelines: HashMap<String, wgpu::RenderPipeline>, | ||||
|     pub buffers: HashMap<String, wgpu::Buffer>, | ||||
|     pub textures: HashMap<String, wgpu::TextureView>, | ||||
| } | ||||
| 
 | ||||
| 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::<Vec<wgpu::VertexBufferDescriptor>>(), | ||||
|             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::<Vec<wgpu::RenderPassColorAttachmentDescriptor>>(), | ||||
|             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::<winit::window::Window>().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::<wgpu::SwapChain>().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); | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Carter Anderson
						Carter Anderson