From 7fe22888141be8b63b15788f8d9282d40ff52189 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 19 Apr 2020 19:06:41 -0700 Subject: [PATCH] pipeline specialization (support different primitive topologies within the same pipeline) --- bevy_asset/src/lib.rs | 2 +- bevy_render/src/lib.rs | 8 +- bevy_render/src/mesh.rs | 20 ++- bevy_render/src/pipeline/pipeline_compiler.rs | 69 +++++++--- .../src/pipeline/pipelines/forward/mod.rs | 2 +- bevy_render/src/pipeline/state_descriptors.rs | 12 ++ .../entity_render_resource_assignments.rs | 2 +- .../render_resource_assignments.rs | 4 +- .../mesh_resource_provider.rs | 126 ++++++++++-------- bevy_render/src/shader/uniform.rs | 4 + examples/scene.rs | 8 -- examples/shader_custom_material.rs | 18 +-- examples/shader_defs.rs | 15 +-- 13 files changed, 172 insertions(+), 118 deletions(-) diff --git a/bevy_asset/src/lib.rs b/bevy_asset/src/lib.rs index ff2b1d8aac..985c9af567 100644 --- a/bevy_asset/src/lib.rs +++ b/bevy_asset/src/lib.rs @@ -118,7 +118,7 @@ impl AssetStorage { AssetStorage { assets: HashMap::new(), names: HashMap::new(), - current_index: 1, + current_index: DEFAULT_HANDLE_ID + 1, // ensure we don't start on the default handle id } } diff --git a/bevy_render/src/lib.rs b/bevy_render/src/lib.rs index b101772e52..f83cc0e255 100644 --- a/bevy_render/src/lib.rs +++ b/bevy_render/src/lib.rs @@ -39,7 +39,7 @@ use self::{ }, render_graph::RenderGraph, render_resource::{ - build_entity_render_resource_assignments_system, + entity_render_resource_assignments_system, resource_providers::{ Camera2dResourceProvider, CameraResourceProvider, LightResourceProvider, UniformResourceProvider, @@ -111,8 +111,12 @@ impl AppPlugin for RenderPlugin { .add_resource(EntityRenderResourceAssignments::default()) .add_resource(asset_batchers) // core systems - .add_system(build_entity_render_resource_assignments_system()) + .add_system(entity_render_resource_assignments_system()) .add_system_to_stage_init(stage::POST_UPDATE, camera::camera_update_system) + .add_system_to_stage( + stage::POST_UPDATE, + mesh::mesh_specializer_system(), + ) .add_system_to_stage(stage::POST_UPDATE, mesh::mesh_batcher_system()) .add_system_to_stage( stage::POST_UPDATE, diff --git a/bevy_render/src/mesh.rs b/bevy_render/src/mesh.rs index 806575490c..bd96cd6139 100644 --- a/bevy_render/src/mesh.rs +++ b/bevy_render/src/mesh.rs @@ -6,7 +6,7 @@ use crate::{ render_resource::AssetBatchers, Renderable, }; -use bevy_asset::Handle; +use bevy_asset::{AssetStorage, Handle}; use glam::*; use legion::prelude::*; use std::borrow::Cow; @@ -224,7 +224,7 @@ pub mod shape { ]; Mesh { - primitive_topology: PrimitiveTopology::TriangleStrip, + primitive_topology: PrimitiveTopology::TriangleList, attributes: vec![ VertexAttribute::position(positions), VertexAttribute::normal(normals), @@ -283,7 +283,7 @@ pub mod shape { } Mesh { - primitive_topology: PrimitiveTopology::TriangleStrip, + primitive_topology: PrimitiveTopology::TriangleList, attributes: vec![ VertexAttribute::position(positions), VertexAttribute::normal(normals), @@ -321,6 +321,20 @@ pub fn mesh_batcher_system() -> Box { }) } +pub fn mesh_specializer_system() -> Box { + SystemBuilder::new("mesh_batcher") + .read_resource::>() + .with_query( + <(Read>, Write)>::query().filter(changed::>() | changed::()), + ) + .build(|_, world, meshes, query| { + for (mesh_handle, mut renderable) in query.iter_mut(world) { + let mesh = meshes.get(&mesh_handle).unwrap(); + renderable.render_resource_assignments.pipeline_specialization.primitive_topology = mesh.primitive_topology; + } + }) +} + #[cfg(test)] mod tests { use crate::{Vertex, pipeline::state_descriptors::PrimitiveTopology, shader::AsUniforms}; diff --git a/bevy_render/src/pipeline/pipeline_compiler.rs b/bevy_render/src/pipeline/pipeline_compiler.rs index 7b2c941273..c2e8025412 100644 --- a/bevy_render/src/pipeline/pipeline_compiler.rs +++ b/bevy_render/src/pipeline/pipeline_compiler.rs @@ -1,5 +1,6 @@ use super::{ - BindType, PipelineDescriptor, PipelineLayout, PipelineLayoutType, VertexBufferDescriptors, + state_descriptors::PrimitiveTopology, BindType, PipelineDescriptor, PipelineLayout, + PipelineLayoutType, VertexBufferDescriptors, }; use crate::{ render_resource::{ @@ -14,11 +15,25 @@ use std::collections::{HashMap, HashSet}; use legion::prelude::*; +#[derive(Clone, Eq, PartialEq, Debug, Default)] +pub struct PipelineSpecialization { + pub shader_specialization: ShaderSpecialization, + pub primitive_topology: PrimitiveTopology, +} + +#[derive(Clone, Eq, PartialEq, Debug, Default)] +pub struct ShaderSpecialization { + pub shader_defs: HashSet, +} + // TODO: consider using (Typeid, fieldinfo.index) in place of string for hashes pub struct PipelineCompiler { - pub shader_source_to_compiled: HashMap, Vec<(HashSet, Handle)>>, - pub pipeline_source_to_compiled: - HashMap, Vec<(HashSet, Handle)>>, + pub shader_source_to_compiled: + HashMap, Vec<(ShaderSpecialization, Handle)>>, + pub pipeline_source_to_compiled: HashMap< + Handle, + Vec<(PipelineSpecialization, Handle)>, + >, } impl PipelineCompiler { @@ -86,7 +101,7 @@ impl PipelineCompiler { &mut self, shader_storage: &mut AssetStorage, shader_handle: &Handle, - shader_defs: &HashSet, + shader_specialization: &ShaderSpecialization, ) -> Handle { let compiled_shaders = self .shader_source_to_compiled @@ -100,18 +115,26 @@ impl PipelineCompiler { return *shader_handle; } - if let Some((_compiled_shader_defs, compiled_shader)) = compiled_shaders - .iter() - .find(|(compiled_shader_defs, _compiled_shader)| *compiled_shader_defs == *shader_defs) + if let Some((_shader_specialization, compiled_shader)) = + compiled_shaders + .iter() + .find(|(current_shader_specialization, _compiled_shader)| { + *current_shader_specialization == *shader_specialization + }) { // if shader has already been compiled with current configuration, use existing shader *compiled_shader } else { // if no shader exists with the current configuration, create new shader and compile - let shader_def_vec = shader_defs.iter().cloned().collect::>(); + let shader_def_vec = shader_specialization + .shader_defs + .iter() + .cloned() + .collect::>(); let compiled_shader = shader.get_spirv_shader(Some(&shader_def_vec)); - compiled_shaders.push((shader_defs.clone(), *shader_handle)); - shader_storage.add(compiled_shader) + let compiled_handle = shader_storage.add(compiled_shader); + compiled_shaders.push((shader_specialization.clone(), compiled_handle)); + compiled_handle } } @@ -128,7 +151,9 @@ impl PipelineCompiler { compiled_pipeline_descriptor.shader_stages.vertex = self.compile_shader( shader_storage, &pipeline_descriptor.shader_stages.vertex, - &render_resource_assignments.shader_defs, + &render_resource_assignments + .pipeline_specialization + .shader_specialization, ); compiled_pipeline_descriptor.shader_stages.fragment = pipeline_descriptor .shader_stages @@ -138,7 +163,9 @@ impl PipelineCompiler { self.compile_shader( shader_storage, fragment, - &render_resource_assignments.shader_defs, + &render_resource_assignments + .pipeline_specialization + .shader_specialization, ) }); @@ -150,6 +177,9 @@ impl PipelineCompiler { render_resource_assignments, ); + compiled_pipeline_descriptor.primitive_topology = render_resource_assignments + .pipeline_specialization + .primitive_topology; compiled_pipeline_descriptor } @@ -174,8 +204,8 @@ impl PipelineCompiler { .get_mut(pipeline_handle) .unwrap() .iter() - .find(|(shader_defs, _macroed_pipeline_handle)| { - *shader_defs == render_resource_assignments.shader_defs + .find(|(pipeline_specialization, _macroed_pipeline_handle)| { + *pipeline_specialization == render_resource_assignments.pipeline_specialization }) { *macroed_pipeline_handle } else { @@ -194,7 +224,7 @@ impl PipelineCompiler { .get_mut(pipeline_handle) .unwrap(); macro_pipelines.push(( - render_resource_assignments.shader_defs.clone(), + render_resource_assignments.pipeline_specialization.clone(), compiled_pipeline_handle, )); compiled_pipeline_handle @@ -277,7 +307,12 @@ pub fn update_shader_assignments( ); // reset shader_defs so they can be changed next frame - renderable.render_resource_assignments.shader_defs.clear(); + renderable + .render_resource_assignments + .pipeline_specialization + .shader_specialization + .shader_defs + .clear(); } } } diff --git a/bevy_render/src/pipeline/pipelines/forward/mod.rs b/bevy_render/src/pipeline/pipelines/forward/mod.rs index 2290a36b45..e05eff207c 100644 --- a/bevy_render/src/pipeline/pipelines/forward/mod.rs +++ b/bevy_render/src/pipeline/pipelines/forward/mod.rs @@ -55,7 +55,7 @@ impl<'a, 'b, 'c> ForwardPipelineBuilder for RenderGraphBuilder<'a, 'b, 'c> { }, write_mask: ColorWrite::ALL, }) - .add_draw_target(resource_name::draw_target::ASSIGNED_BATCHES); + .add_draw_target(resource_name::draw_target::ASSIGNED_MESHES); }) } } diff --git a/bevy_render/src/pipeline/state_descriptors.rs b/bevy_render/src/pipeline/state_descriptors.rs index 26c90c5b97..29568d5fa2 100644 --- a/bevy_render/src/pipeline/state_descriptors.rs +++ b/bevy_render/src/pipeline/state_descriptors.rs @@ -60,6 +60,12 @@ pub enum PrimitiveTopology { TriangleStrip = 4, } +impl Default for PrimitiveTopology { + fn default() -> Self { + PrimitiveTopology::TriangleList + } +} + #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum FrontFace { Ccw = 0, @@ -172,3 +178,9 @@ pub enum IndexFormat { Uint16 = 0, Uint32 = 1, } + +impl Default for IndexFormat { + fn default() -> Self { + IndexFormat::Uint16 + } +} diff --git a/bevy_render/src/render_resource/entity_render_resource_assignments.rs b/bevy_render/src/render_resource/entity_render_resource_assignments.rs index b572d18caf..151e20d1e4 100644 --- a/bevy_render/src/render_resource/entity_render_resource_assignments.rs +++ b/bevy_render/src/render_resource/entity_render_resource_assignments.rs @@ -19,7 +19,7 @@ impl EntityRenderResourceAssignments { } // TODO: make sure this runs right before rendering -pub fn build_entity_render_resource_assignments_system() -> Box { +pub fn entity_render_resource_assignments_system() -> Box { SystemBuilder::new("entity_render_resource_assignments") .write_resource::() .with_query(>::query().filter(changed::())) diff --git a/bevy_render/src/render_resource/render_resource_assignments.rs b/bevy_render/src/render_resource/render_resource_assignments.rs index e4022188ac..bd604ef82f 100644 --- a/bevy_render/src/render_resource/render_resource_assignments.rs +++ b/bevy_render/src/render_resource/render_resource_assignments.rs @@ -1,5 +1,5 @@ use super::RenderResource; -use crate::pipeline::{BindGroupDescriptor, BindGroupDescriptorId}; +use crate::pipeline::{BindGroupDescriptor, BindGroupDescriptorId, PipelineSpecialization}; use std::{ collections::{hash_map::DefaultHasher, HashMap, HashSet}, hash::{Hash, Hasher}, @@ -15,7 +15,7 @@ pub struct RenderResourceAssignments { bind_group_resource_sets: HashMap>)>, dirty_bind_groups: HashSet, - pub(crate) shader_defs: HashSet, + pub pipeline_specialization: PipelineSpecialization, } impl RenderResourceAssignments { diff --git a/bevy_render/src/render_resource/resource_providers/mesh_resource_provider.rs b/bevy_render/src/render_resource/resource_providers/mesh_resource_provider.rs index f65cd4d08a..db98dc3535 100644 --- a/bevy_render/src/render_resource/resource_providers/mesh_resource_provider.rs +++ b/bevy_render/src/render_resource/resource_providers/mesh_resource_provider.rs @@ -1,80 +1,96 @@ use crate::{ mesh::{self, Mesh}, - pipeline::{state_descriptors::IndexFormat, VertexBufferDescriptors}, - render_resource::{AssetBatchers, BufferInfo, BufferUsage}, - renderer_2::GlobalRenderResourceContext, + pipeline::{state_descriptors::IndexFormat, VertexBufferDescriptor, VertexBufferDescriptors}, + render_resource::{AssetBatchers, BufferInfo, BufferUsage, RenderResourceAssignments}, + renderer_2::{GlobalRenderResourceContext, RenderResourceContext}, shader::AsUniforms, - Vertex, + Renderable, Vertex, }; -use bevy_asset::AssetStorage; +use bevy_asset::{AssetStorage, Handle}; use legion::prelude::*; +fn setup_mesh_resource( + render_resources: &dyn RenderResourceContext, + render_resource_assignments: &mut RenderResourceAssignments, + vertex_buffer_descriptor: &VertexBufferDescriptor, + handle: Handle, + meshes: &AssetStorage, +) { + log::trace!("setup mesh for {:?}", render_resource_assignments.id); + let index_format = IndexFormat::Uint16; + let (vertex_buffer, index_buffer) = if let Some(vertex_buffer) = + render_resources.get_asset_resource(handle, mesh::VERTEX_BUFFER_ASSET_INDEX) + { + ( + vertex_buffer, + render_resources.get_asset_resource(handle, mesh::INDEX_BUFFER_ASSET_INDEX), + ) + } else { + let mesh_asset = meshes.get(&handle).unwrap(); + let vertex_bytes = mesh_asset + .get_vertex_buffer_bytes(&vertex_buffer_descriptor) + .unwrap(); + // TODO: use a staging buffer here + let vertex_buffer = render_resources.create_buffer_with_data( + BufferInfo { + buffer_usage: BufferUsage::VERTEX, + ..Default::default() + }, + &vertex_bytes, + ); + let index_bytes = mesh_asset.get_index_buffer_bytes(index_format).unwrap(); + let index_buffer = render_resources.create_buffer_with_data( + BufferInfo { + buffer_usage: BufferUsage::INDEX, + ..Default::default() + }, + &index_bytes, + ); + + render_resources.set_asset_resource(handle, vertex_buffer, mesh::VERTEX_BUFFER_ASSET_INDEX); + render_resources.set_asset_resource(handle, index_buffer, mesh::INDEX_BUFFER_ASSET_INDEX); + (vertex_buffer, Some(index_buffer)) + }; + + render_resource_assignments.set_vertex_buffer("Vertex", vertex_buffer, index_buffer); +} + pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box { let mut vertex_buffer_descriptors = resources.get_mut::().unwrap(); // TODO: allow pipelines to specialize on vertex_buffer_descriptor and index_format let vertex_buffer_descriptor = Vertex::get_vertex_buffer_descriptor().unwrap(); - let index_format = IndexFormat::Uint16; vertex_buffer_descriptors.set(vertex_buffer_descriptor.clone()); SystemBuilder::new("mesh_resource_provider") .read_resource::() .read_resource::>() .write_resource::() + .with_query(<(Read>, Write)>::query()) .build( - move |_, _, (render_resource_context, meshes, asset_batchers), _| { - let render_resources = &render_resource_context.context; + move |_, world, (render_resource_context, meshes, asset_batchers), query| { + let render_resources = &*render_resource_context.context; if let Some(batches) = asset_batchers.get_handle_batches_mut::() { for batch in batches { let handle = batch.get_handle::().unwrap(); - log::trace!("setup mesh for {:?}", batch.render_resource_assignments.id); - let (vertex_buffer, index_buffer) = if let Some(vertex_buffer) = - render_resources - .get_asset_resource(handle, mesh::VERTEX_BUFFER_ASSET_INDEX) - { - ( - vertex_buffer, - render_resources - .get_asset_resource(handle, mesh::INDEX_BUFFER_ASSET_INDEX), - ) - } else { - let mesh_asset = meshes.get(&handle).unwrap(); - let vertex_bytes = mesh_asset.get_vertex_buffer_bytes(&vertex_buffer_descriptor).unwrap(); - // TODO: use a staging buffer here - let vertex_buffer = render_resources.create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::VERTEX, - ..Default::default() - }, - &vertex_bytes, - ); - let index_bytes = mesh_asset.get_index_buffer_bytes(index_format).unwrap(); - let index_buffer = render_resources.create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::INDEX, - ..Default::default() - }, - &index_bytes, - ); - - render_resources.set_asset_resource( - handle, - vertex_buffer, - mesh::VERTEX_BUFFER_ASSET_INDEX, - ); - render_resources.set_asset_resource( - handle, - index_buffer, - mesh::INDEX_BUFFER_ASSET_INDEX, - ); - (vertex_buffer, Some(index_buffer)) - }; - - batch.render_resource_assignments.set_vertex_buffer( - "Vertex", - vertex_buffer, - index_buffer, + setup_mesh_resource( + render_resources, + &mut batch.render_resource_assignments, + &vertex_buffer_descriptor, + handle, + &meshes, ); } } + + // TODO: remove this once batches are pipeline specific and deprecate assigned_meshes draw target + for (handle, mut renderable) in query.iter_mut(world) { + setup_mesh_resource( + render_resources, + &mut renderable.render_resource_assignments, + &vertex_buffer_descriptor, + *handle, + &meshes, + ); + } }, ) } diff --git a/bevy_render/src/shader/uniform.rs b/bevy_render/src/shader/uniform.rs index 49c6cb05a5..8973f07e7b 100644 --- a/bevy_render/src/shader/uniform.rs +++ b/bevy_render/src/shader/uniform.rs @@ -31,6 +31,8 @@ where if let Some(shader_defs) = uniforms.get_shader_defs() { renderable .render_resource_assignments + .pipeline_specialization + .shader_specialization .shader_defs .extend(shader_defs) } @@ -58,6 +60,8 @@ where if let Some(shader_defs) = uniforms.get_shader_defs() { renderable .render_resource_assignments + .pipeline_specialization + .shader_specialization .shader_defs .extend(shader_defs) } diff --git a/examples/scene.rs b/examples/scene.rs index 619e3296b7..cf98056f8d 100644 --- a/examples/scene.rs +++ b/examples/scene.rs @@ -10,7 +10,6 @@ fn main() { /// set up a simple scene fn setup(world: &mut World, resources: &mut Resources) { - env_logger::init(); // create a cube and a plane mesh let mut mesh_storage = resources.get_mut::>().unwrap(); let cube_handle = mesh_storage.add(Mesh::from(shape::Cube)); @@ -45,13 +44,6 @@ fn setup(world: &mut World, resources: &mut Resources) { translation: Translation::new(0.0, 0.0, 1.0), ..Default::default() }) - // cube - .add_entity(MeshEntity { - mesh: cube_handle, - material: cube_material_handle, - translation: Translation::new(2.0, 0.0, 1.0), - ..Default::default() - }) // light .add_entity(LightEntity { translation: Translation::new(4.0, -4.0, 5.0), diff --git a/examples/shader_custom_material.rs b/examples/shader_custom_material.rs index a01933b154..55b09a7f0b 100644 --- a/examples/shader_custom_material.rs +++ b/examples/shader_custom_material.rs @@ -1,13 +1,9 @@ -use bevy::{prelude::*, render::shader}; +use bevy::prelude::*; fn main() { App::build() .add_default_plugins() .add_startup_system(setup) - .add_system_to_stage( - stage::POST_UPDATE, - shader::asset_handle_batcher_system::(), - ) .run(); } @@ -32,8 +28,7 @@ fn add_shader_to_render_graph(resources: &mut Resources) { ShaderStage::Vertex, r#" #version 450 - layout(location = 0) in vec4 Vertex_Position; - layout(location = 0) out vec4 v_Position; + layout(location = 0) in vec3 Vertex_Position; layout(set = 0, binding = 0) uniform Camera { mat4 ViewProj; }; @@ -41,8 +36,7 @@ fn add_shader_to_render_graph(resources: &mut Resources) { mat4 Model; }; void main() { - v_Position = Model * Vertex_Position; - gl_Position = ViewProj * v_Position; + gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0); } "#, )) @@ -50,7 +44,6 @@ fn add_shader_to_render_graph(resources: &mut Resources) { ShaderStage::Fragment, r#" #version 450 - layout(location = 0) in vec4 v_Position; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 1) uniform MyMaterial_color { vec4 color; @@ -73,13 +66,8 @@ fn setup(world: &mut World, resources: &mut Resources) { let material = material_storage.add(MyMaterial { color: Color::rgb(0.0, 0.8, 0.0), }); - resources.insert(material_storage); - // batch materials to improve performance - let mut asset_batchers = resources.get_mut::().unwrap(); - asset_batchers.batch_types2::(); - // get a handle to our newly created shader pipeline let mut pipeline_storage = resources .get_mut::>() diff --git a/examples/shader_defs.rs b/examples/shader_defs.rs index 9eac143a1d..8bd692fe7e 100644 --- a/examples/shader_defs.rs +++ b/examples/shader_defs.rs @@ -4,10 +4,6 @@ fn main() { App::build() .add_default_plugins() .add_startup_system(setup) - .add_system_to_stage( - stage::POST_UPDATE, - shader::asset_handle_batcher_system::(), - ) .add_system_to_stage( stage::POST_UPDATE, shader::asset_handle_shader_def_system::(), @@ -38,8 +34,7 @@ fn add_shader_to_render_graph(resources: &mut Resources) { ShaderStage::Vertex, r#" #version 450 - layout(location = 0) in vec4 Vertex_Position; - layout(location = 0) out vec4 v_Position; + layout(location = 0) in vec3 Vertex_Position; layout(set = 0, binding = 0) uniform Camera { mat4 ViewProj; }; @@ -47,8 +42,7 @@ fn add_shader_to_render_graph(resources: &mut Resources) { mat4 Model; }; void main() { - v_Position = Model * Vertex_Position; - gl_Position = ViewProj * v_Position; + gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0); } "#, )) @@ -56,7 +50,6 @@ fn add_shader_to_render_graph(resources: &mut Resources) { ShaderStage::Fragment, r#" #version 450 - layout(location = 0) in vec4 v_Position; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 1) uniform MyMaterial_color { vec4 color; @@ -92,10 +85,6 @@ fn setup(world: &mut World, resources: &mut Resources) { resources.insert(material_storage); - // batch materials to improve performance - let mut asset_batchers = resources.get_mut::().unwrap(); - asset_batchers.batch_types2::(); - // get a handle to our newly created shader pipeline let mut pipeline_storage = resources .get_mut::>()