diff --git a/bevy_render/src/lib.rs b/bevy_render/src/lib.rs index 62824e38e6..3732642b3f 100644 --- a/bevy_render/src/lib.rs +++ b/bevy_render/src/lib.rs @@ -98,6 +98,7 @@ impl AppPlugin for RenderPlugin { app.add_system(build_entity_render_resource_assignments_system()) .build_system_on_stage(stage::POST_UPDATE, camera::camera_update_system) .add_system_to_stage(stage::POST_UPDATE, mesh::mesh_batcher_system()) + .add_system_to_stage(stage::POST_UPDATE, shader::asset_handle_shader_def_system::()) .add_stage_after(stage::POST_UPDATE, RENDER_STAGE) .add_resource(RenderGraph::default()) .add_resource(AssetStorage::::new()) diff --git a/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs b/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs index faddcd4bc9..0117224ad1 100644 --- a/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs +++ b/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs @@ -53,6 +53,15 @@ struct QueuedBufferWrite { } // TODO: make these queries only update changed T components +type UniformQueryRead = Query< + (Read, Read), + EntityFilterTuple< + And<(ComponentFilter, ComponentFilter)>, + And<(Passthrough, Passthrough)>, + And<(Passthrough, Passthrough)>, + >, +>; + type UniformQuery = Query< (Read, Write), EntityFilterTuple< @@ -62,6 +71,15 @@ type UniformQuery = Query< >, >; +type UniformHandleQueryRead = Query< + (Read>, Read), + EntityFilterTuple< + And<(ComponentFilter>, ComponentFilter)>, + And<(Passthrough, Passthrough)>, + And<(Passthrough, Passthrough)>, + >, +>; + type UniformHandleQuery = Query< (Read>, Write), EntityFilterTuple< @@ -82,9 +100,9 @@ where // PERF: somehow remove this HashSet uniform_buffer_status: Vec>, instance_buffer_status: Option, - query: Option>, + query: Option>, query_finish: Option>, - handle_query: Option>, + handle_query: Option>, handle_query_finish: Option>, } @@ -102,14 +120,26 @@ where use_dynamic_uniforms, instance_buffer_status: None, is_instanceable, - query: Some(<(Read, Write)>::query()), + query: Some(<(Read, Read)>::query()), query_finish: Some(<(Read, Write)>::query()), - handle_query: Some(<(Read>, Write)>::query()), + handle_query: Some(<(Read>, Read)>::query()), handle_query_finish: Some(<(Read>, Write)>::query()), _marker: PhantomData, } } + fn update_readonly( + &mut self, + _render_context: &mut dyn RenderContext, + world: &World, + resources: &Resources, + ) { + self.reset_buffer_array_status_counts(); + self.update_uniforms_info(world); + self.update_uniform_handles_info(world, resources); + } + + fn reset_buffer_array_status_counts(&mut self) { for buffer_status in self.uniform_buffer_status.iter_mut() { if let Some((_name, buffer_status)) = buffer_status { @@ -122,9 +152,9 @@ where } } - fn update_uniforms_info(&mut self, world: &mut World) { + fn update_uniforms_info(&mut self, world: &World) { let query = self.query.take().unwrap(); - for (uniforms, mut renderable) in query.iter_mut(world) { + for (uniforms, renderable) in query.iter(world) { if !renderable.is_visible { return; } @@ -141,19 +171,17 @@ where } else { self.increment_uniform_counts(&uniforms); } - - Self::update_shader_defs(&uniforms, &mut renderable.render_resource_assignments); } self.query = Some(query); } - fn update_uniform_handles_info(&mut self, world: &mut World, resources: &Resources) { + fn update_uniform_handles_info(&mut self, world: &World, resources: &Resources) { let handle_query = self.handle_query.take().unwrap(); let assets = resources.get::>(); let mut asset_batchers = resources.get_mut::().unwrap(); if let Some(assets) = assets { - for (entity, (handle, mut renderable)) in handle_query.iter_entities_mut(world) { + for (entity, (handle, renderable)) in handle_query.iter_entities(world) { if !renderable.is_visible { return; } @@ -175,7 +203,6 @@ where let uniforms = assets .get(&handle) .expect("Handle points to a non-existent resource"); - Self::update_shader_defs(uniforms, &mut renderable.render_resource_assignments); self.increment_uniform_counts(&uniforms); } @@ -253,17 +280,6 @@ where BIND_BUFFER_ALIGNMENT * ((data_size as f32 / BIND_BUFFER_ALIGNMENT as f32).ceil() as usize) } - fn update_shader_defs( - uniforms: &T, - render_resource_assignments: &mut RenderResourceAssignments, - ) { - if let Some(shader_defs) = uniforms.get_shader_defs() { - for shader_def in shader_defs { - render_resource_assignments.shader_defs.insert(shader_def); - } - } - } - fn setup_uniform_buffer_resources( &mut self, uniforms: &T, @@ -566,8 +582,6 @@ where resources, &mut batch.render_resource_assignments, ); - - Self::update_shader_defs(&uniforms, &mut batch.render_resource_assignments); } } } @@ -723,9 +737,7 @@ where world: &mut World, resources: &Resources, ) { - self.reset_buffer_array_status_counts(); - self.update_uniforms_info(world); - self.update_uniform_handles_info(world, resources); + self.update_readonly(_render_context, world, resources); } fn finish_update( diff --git a/bevy_render/src/shader/uniform.rs b/bevy_render/src/shader/uniform.rs index 551a9e01f5..c8faf8d332 100644 --- a/bevy_render/src/shader/uniform.rs +++ b/bevy_render/src/shader/uniform.rs @@ -2,10 +2,12 @@ use crate::{ color::ColorSource, pipeline::{BindType, VertexBufferDescriptor}, texture::Texture, + Renderable, }; -use bevy_asset::Handle; +use bevy_asset::{AssetStorage, Handle}; use bevy_core::bytes::GetBytes; +use legion::prelude::*; pub trait AsUniforms { fn get_field_infos() -> &'static [FieldInfo]; @@ -17,6 +19,44 @@ pub trait AsUniforms { fn get_vertex_buffer_descriptor() -> Option<&'static VertexBufferDescriptor>; } +pub fn shader_def_system() -> Box +where + T: AsUniforms + Send + Sync + 'static, +{ + SystemBuilder::new(format!( + "shader_def::{}", + std::any::type_name::() + )) + .with_query(<(Read, Write)>::query()) + .build(|_, world, _, query| { + for (uniforms, mut renderable) in query.iter_mut(world) { + if let Some(shader_defs) = uniforms.get_shader_defs() { + renderable.render_resource_assignments.shader_defs.extend(shader_defs) + } + } + }) +} + +pub fn asset_handle_shader_def_system() -> Box +where + T: AsUniforms + Send + Sync + 'static, +{ + SystemBuilder::new(format!( + "asset_handle_shader_def::{}", + std::any::type_name::() + )) + .read_resource::>() + .with_query(<(Read>, Write)>::query()) + .build(|_, world, asset_storage, query| { + for (uniform_handle, mut renderable) in query.iter_mut(world) { + let uniforms = asset_storage.get(&uniform_handle).unwrap(); + if let Some(shader_defs) = uniforms.get_shader_defs() { + renderable.render_resource_assignments.shader_defs.extend(shader_defs) + } + } + }) +} + pub trait ShaderDefSuffixProvider { fn get_shader_def(&self) -> Option<&'static str>; } diff --git a/examples/shader_custom_material.rs b/examples/shader_custom_material.rs new file mode 100644 index 0000000000..a08d6f10fd --- /dev/null +++ b/examples/shader_custom_material.rs @@ -0,0 +1,110 @@ +use bevy::{prelude::*}; + +fn main() { + App::build() + .add_default_plugins() + .setup(setup) + .run(); +} + +#[derive(Uniforms, Default)] +struct MyMaterial { + pub color: Color, +} + +fn add_shader_to_render_graph(resources: &mut Resources) { + let mut render_graph = resources.get_mut::().unwrap(); + let mut pipelines = resources + .get_mut::>() + .unwrap(); + let mut shaders = resources.get_mut::>().unwrap(); + + render_graph + .build(&mut pipelines, &mut shaders) + .add_resource_provider(UniformResourceProvider::::new(true)) + .add_pipeline_to_pass(resource_name::pass::MAIN, "MyMaterial", |builder| { + builder + .with_vertex_shader(Shader::from_glsl( + ShaderStage::Vertex, + r#" + #version 450 + layout(location = 0) in vec4 Vertex_Position; + layout(location = 0) out vec4 v_Position; + layout(set = 0, binding = 0) uniform Camera { + mat4 ViewProj; + }; + layout(set = 1, binding = 0) uniform Object { + mat4 Model; + }; + void main() { + v_Position = Model * Vertex_Position; + gl_Position = ViewProj * v_Position; + } + "#, + )) + .with_fragment_shader(Shader::from_glsl( + 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; + }; + void main() { + o_Target = color; + } + "#, + )) + .with_default_config(); + }); +} + +fn setup(world: &mut World, resources: &mut Resources) { + // add our shader to the render graph + add_shader_to_render_graph(resources); + + // create materials + let mut material_storage = AssetStorage::::new(); + 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::>() + .unwrap(); + let pipeline_handle = pipeline_storage.get_named("MyMaterial").unwrap(); + + let mut mesh_storage = resources.get_mut::>().unwrap(); + let cube_handle = mesh_storage.add(Mesh::load(MeshType::Cube)); + + world + .build() + // cube + .add_entity(MeshMaterialEntity:: { + mesh: cube_handle, + renderable: Renderable { + pipelines: vec![pipeline_handle], + ..Default::default() + }, + material, + translation: Translation::new(0.0, 0.0, 0.0), + ..Default::default() + }) + // camera + .add_entity(CameraEntity { + local_to_world: LocalToWorld(Mat4::look_at_rh( + Vec3::new(3.0, 8.0, 5.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 0.0, 1.0), + )), + ..Default::default() + }); +} diff --git a/examples/custom_material_shader.rs b/examples/shader_defs.rs similarity index 94% rename from examples/custom_material_shader.rs rename to examples/shader_defs.rs index 42514ec8cd..fb64898230 100644 --- a/examples/custom_material_shader.rs +++ b/examples/shader_defs.rs @@ -1,7 +1,14 @@ -use bevy::prelude::*; +use bevy::{prelude::*, render::shader}; fn main() { - App::build().add_default_plugins().setup(setup).run(); + App::build() + .add_default_plugins() + .setup(setup) + .add_system_to_stage( + stage::POST_UPDATE, + shader::asset_handle_shader_def_system::(), + ) + .run(); } #[derive(Uniforms, Default)] diff --git a/src/prelude.rs b/src/prelude.rs index b7e50a752d..1faa34ee97 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,5 @@ pub use crate::AddDefaultPlugins; -pub use crate::app::{App, AppBuilder, AppPlugin, EntityArchetype, EventReader, Events, GetEventReader}; +pub use crate::app::{App, AppBuilder, AppPlugin, EntityArchetype, EventReader, Events, GetEventReader, stage}; #[cfg(feature = "asset")] pub use crate::asset::{Asset, AssetStorage, Handle}; #[cfg(feature = "derive")]