render: move dynamic_bindings to PipelineSpecialization

This is a temporary step back in ergonomics as we are no longer automatically inferring dynamic bindings from RenderResourceBindings
This commit is contained in:
Carter Anderson 2020-06-17 18:10:29 -07:00
parent 0931fd0266
commit 74d0055a3d
9 changed files with 171 additions and 54 deletions

View file

@ -1,14 +1,16 @@
use crate::{light::Light, material::StandardMaterial}; use crate::{light::Light, material::StandardMaterial, pipelines::FORWARD_PIPELINE_HANDLE};
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_derive::EntityArchetype; use bevy_derive::EntityArchetype;
use bevy_render::{draw::Draw, mesh::Mesh, pipeline::RenderPipelines}; use bevy_render::{
draw::Draw,
mesh::Mesh,
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
};
use bevy_transform::prelude::{Rotation, Scale, Transform, Translation}; use bevy_transform::prelude::{Rotation, Scale, Transform, Translation};
#[derive(EntityArchetype, Default)] #[derive(EntityArchetype)]
pub struct MeshEntity { pub struct MeshEntity {
// #[tag]
pub mesh: Handle<Mesh>, pub mesh: Handle<Mesh>,
// #[tag]
pub material: Handle<StandardMaterial>, pub material: Handle<StandardMaterial>,
pub draw: Draw, pub draw: Draw,
pub render_pipelines: RenderPipelines, pub render_pipelines: RenderPipelines,
@ -18,6 +20,38 @@ pub struct MeshEntity {
pub scale: Scale, pub scale: Scale,
} }
impl Default for MeshEntity {
fn default() -> Self {
Self {
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
FORWARD_PIPELINE_HANDLE,
PipelineSpecialization {
dynamic_bindings: vec![
// Transform
DynamicBinding {
bind_group: 1,
binding: 0,
},
// StandardMaterial_albedo
DynamicBinding {
bind_group: 2,
binding: 0,
},
],
..Default::default()
},
)]),
mesh: Default::default(),
material: Default::default(),
draw: Default::default(),
transform: Default::default(),
translation: Default::default(),
rotation: Default::default(),
scale: Default::default(),
}
}
}
#[derive(EntityArchetype, Default)] #[derive(EntityArchetype, Default)]
pub struct LightEntity { pub struct LightEntity {
pub light: Light, pub light: Light,

View file

@ -1,4 +1,4 @@
use crate::{material::StandardMaterial, nodes::LightsNode, pipelines::build_forward_pipeline}; use crate::{material::StandardMaterial, nodes::LightsNode, pipelines::{FORWARD_PIPELINE_HANDLE, build_forward_pipeline}};
use bevy_asset::Assets; use bevy_asset::Assets;
use bevy_render::{ use bevy_render::{
base_render_graph, base_render_graph,
@ -36,7 +36,7 @@ impl ForwardPbrRenderGraphBuilder for RenderGraph {
self.add_system_node(node::LIGHTS, LightsNode::new(10)); self.add_system_node(node::LIGHTS, LightsNode::new(10));
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap(); let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap(); let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
pipelines.add_default(build_forward_pipeline(&mut shaders)); pipelines.set(FORWARD_PIPELINE_HANDLE, build_forward_pipeline(&mut shaders));
// TODO: replace these with "autowire" groups // TODO: replace these with "autowire" groups
self.add_node_edge(node::STANDARD_MATERIAL, base_render_graph::node::MAIN_PASS) self.add_node_edge(node::STANDARD_MATERIAL, base_render_graph::node::MAIN_PASS)

View file

@ -1,4 +1,4 @@
use bevy_asset::Assets; use bevy_asset::{Handle, Assets};
use bevy_render::{ use bevy_render::{
pipeline::{ pipeline::{
state_descriptors::{ state_descriptors::{
@ -12,6 +12,8 @@ use bevy_render::{
texture::TextureFormat, texture::TextureFormat,
}; };
pub const FORWARD_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
Handle::from_u128(131483623140127713893804825450360211204);
pub fn build_forward_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor { pub fn build_forward_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
PipelineDescriptor { PipelineDescriptor {
rasterization_state: Some(RasterizationStateDescriptor { rasterization_state: Some(RasterizationStateDescriptor {

View file

@ -4,10 +4,9 @@ use super::{
CompareFunction, CullMode, DepthStencilStateDescriptor, FrontFace, IndexFormat, CompareFunction, CullMode, DepthStencilStateDescriptor, FrontFace, IndexFormat,
PrimitiveTopology, RasterizationStateDescriptor, StencilStateFaceDescriptor, PrimitiveTopology, RasterizationStateDescriptor, StencilStateFaceDescriptor,
}, },
BindType, PipelineLayout, VertexBufferDescriptors, BindType, DynamicBinding, PipelineLayout, VertexBufferDescriptors,
}; };
use crate::{ use crate::{
render_resource::{RenderResourceBinding, RenderResourceBindings},
shader::{Shader, ShaderStages}, shader::{Shader, ShaderStages},
texture::TextureFormat, texture::TextureFormat,
}; };
@ -119,17 +118,16 @@ impl PipelineDescriptor {
/// If `bevy_conventions` is true, it will be assumed that the shader follows "bevy shader conventions". These allow /// If `bevy_conventions` is true, it will be assumed that the shader follows "bevy shader conventions". These allow
/// richer reflection, such as inferred Vertex Buffer names and inferred instancing. /// richer reflection, such as inferred Vertex Buffer names and inferred instancing.
/// ///
/// If `dynamic_bindings` has values, shader uniforms will be set to "dynamic" if there is a matching binding in the list
///
/// If `vertex_buffer_descriptors` is set, the pipeline's vertex buffers /// If `vertex_buffer_descriptors` is set, the pipeline's vertex buffers
/// will inherit their layouts from global descriptors, otherwise the layout will be assumed to be complete / local. /// will inherit their layouts from global descriptors, otherwise the layout will be assumed to be complete / local.
///
/// If `render_resource_bindings` is set, shader uniforms will be set to "dynamic" if there is a matching "dynamic uniform"
/// render resource.
pub fn reflect_layout( pub fn reflect_layout(
&mut self, &mut self,
shaders: &Assets<Shader>, shaders: &Assets<Shader>,
bevy_conventions: bool, bevy_conventions: bool,
vertex_buffer_descriptors: Option<&VertexBufferDescriptors>, vertex_buffer_descriptors: Option<&VertexBufferDescriptors>,
render_resource_bindings: Option<&RenderResourceBindings>, dynamic_bindings: &[DynamicBinding],
) { ) {
let vertex_spirv = shaders.get(&self.shader_stages.vertex).unwrap(); let vertex_spirv = shaders.get(&self.shader_stages.vertex).unwrap();
let fragment_spirv = self let fragment_spirv = self
@ -148,18 +146,16 @@ impl PipelineDescriptor {
layout.sync_vertex_buffer_descriptors(vertex_buffer_descriptors); layout.sync_vertex_buffer_descriptors(vertex_buffer_descriptors);
} }
if let Some(render_resource_bindings) = render_resource_bindings { if !dynamic_bindings.is_empty() {
// set binding uniforms to dynamic if render resource bindings use dynamic // set binding uniforms to dynamic if render resource bindings use dynamic
// TODO: this breaks down if different bindings have different "dynamic" status or if the dynamic status changes.
// the fix would be to add "dynamic bindings" to the existing shader_def sets. this would ensure new pipelines are generated
// for all permutations of dynamic/non-dynamic
for bind_group in layout.bind_groups.iter_mut() { for bind_group in layout.bind_groups.iter_mut() {
for binding in bind_group.bindings.iter_mut() { for binding in bind_group.bindings.iter_mut() {
if let Some(RenderResourceBinding::Buffer { let current = DynamicBinding {
dynamic_index: Some(_), bind_group: bind_group.index,
.. binding: binding.index,
}) = render_resource_bindings.get(&binding.name) };
{
if dynamic_bindings.contains(&current) {
if let BindType::Uniform { if let BindType::Uniform {
ref mut dynamic, .. ref mut dynamic, ..
} = binding.bind_type } = binding.bind_type

View file

@ -3,18 +3,19 @@ use super::{
VertexBufferDescriptors, VertexBufferDescriptors,
}; };
use crate::{ use crate::{
render_resource::RenderResourceBindings,
renderer::RenderResourceContext, renderer::RenderResourceContext,
shader::{Shader, ShaderSource}, shader::{Shader, ShaderSource},
}; };
use bevy_asset::{Assets, Handle}; use bevy_asset::{Assets, Handle, AssetEvent};
use legion::prelude::*; use legion::prelude::*;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use bevy_app::Events;
#[derive(Clone, Eq, PartialEq, Debug, Default)] #[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct PipelineSpecialization { pub struct PipelineSpecialization {
pub shader_specialization: ShaderSpecialization, pub shader_specialization: ShaderSpecialization,
pub primitive_topology: PrimitiveTopology, pub primitive_topology: PrimitiveTopology,
pub dynamic_bindings: Vec<DynamicBinding>,
} }
#[derive(Clone, Eq, PartialEq, Debug, Default)] #[derive(Clone, Eq, PartialEq, Debug, Default)]
@ -32,7 +33,12 @@ struct SpecializedPipeline {
specialization: PipelineSpecialization, specialization: PipelineSpecialization,
} }
// TODO: consider using (Typeid, fieldinfo.index) in place of string for hashes #[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct DynamicBinding {
pub bind_group: u32,
pub binding: u32,
}
#[derive(Default)] #[derive(Default)]
pub struct PipelineCompiler { pub struct PipelineCompiler {
specialized_shaders: HashMap<Handle<Shader>, Vec<SpecializedShader>>, specialized_shaders: HashMap<Handle<Shader>, Vec<SpecializedShader>>,
@ -92,7 +98,6 @@ impl PipelineCompiler {
source_pipeline: Handle<PipelineDescriptor>, source_pipeline: Handle<PipelineDescriptor>,
vertex_buffer_descriptors: &VertexBufferDescriptors, vertex_buffer_descriptors: &VertexBufferDescriptors,
pipeline_specialization: &PipelineSpecialization, pipeline_specialization: &PipelineSpecialization,
render_resource_bindings: &RenderResourceBindings,
) -> Handle<PipelineDescriptor> { ) -> Handle<PipelineDescriptor> {
let source_descriptor = pipelines.get(&source_pipeline).unwrap(); let source_descriptor = pipelines.get(&source_pipeline).unwrap();
let mut specialized_descriptor = source_descriptor.clone(); let mut specialized_descriptor = source_descriptor.clone();
@ -117,7 +122,7 @@ impl PipelineCompiler {
shaders, shaders,
true, true,
Some(vertex_buffer_descriptors), Some(vertex_buffer_descriptors),
Some(render_resource_bindings), &pipeline_specialization.dynamic_bindings,
); );
specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology; specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology;
@ -176,7 +181,6 @@ impl PipelineCompiler {
source_pipeline, source_pipeline,
vertex_buffer_descriptors, vertex_buffer_descriptors,
&render_pipeline.specialization, &render_pipeline.specialization,
&render_pipelines.bindings,
) )
}; };
@ -213,15 +217,15 @@ pub fn compile_pipelines_system(
mut pipeline_compiler: ResMut<PipelineCompiler>, mut pipeline_compiler: ResMut<PipelineCompiler>,
mut shaders: ResMut<Assets<Shader>>, mut shaders: ResMut<Assets<Shader>>,
mut pipelines: ResMut<Assets<PipelineDescriptor>>, mut pipelines: ResMut<Assets<PipelineDescriptor>>,
// pipeline_asset_events: Res<Events<AssetEvent<PipelineDescriptor>>>, _pipeline_asset_events: Res<Events<AssetEvent<PipelineDescriptor>>>,
vertex_buffer_descriptors: Res<VertexBufferDescriptors>, vertex_buffer_descriptors: Res<VertexBufferDescriptors>,
render_resource_context: Res<Box<dyn RenderResourceContext>>, render_resource_context: Res<Box<dyn RenderResourceContext>>,
query: &mut Query<Write<RenderPipelines>>, query: &mut Query<Write<RenderPipelines>>,
) { ) {
let render_resource_context = &**render_resource_context; let render_resource_context = &**render_resource_context;
// let default_specialization = PipelineSpecialization::default();
// NOTE: this intentionally only handles events that happened prior to this system during this frame. this results in // NOTE: this intentionally only handles events that happened prior to this system during this frame. this results in
// "specialized pipeline" events being ignored. // "new specialized pipeline" events being ignored.
// let default_specialization = PipelineSpecialization::default();
// for event in pipeline_asset_events.iter_current_update_events() { // for event in pipeline_asset_events.iter_current_update_events() {
// let handle_to_compile = match event { // let handle_to_compile = match event {
// AssetEvent::Created { handle } => Some(*handle), // AssetEvent::Created { handle } => Some(*handle),
@ -229,13 +233,14 @@ pub fn compile_pipelines_system(
// // TODO: clean up old pipelines // // TODO: clean up old pipelines
// Some(*handle) // Some(*handle)
// } // }
// AssetEvent::Removed { handle } => { // AssetEvent::Removed { .. } => {
// // TODO: clean up old pipelines // // TODO: clean up old pipelines
// None // None
// } // }
// }; // };
// if let Some(handle_to_compile) = handle_to_compile { // if let Some(handle_to_compile) = handle_to_compile {
// // TODO: try updating specialization here.
// pipeline_compiler.compile_pipeline( // pipeline_compiler.compile_pipeline(
// render_resource_context, // render_resource_context,
// &mut pipelines, // &mut pipelines,
@ -243,7 +248,6 @@ pub fn compile_pipelines_system(
// handle_to_compile, // handle_to_compile,
// &vertex_buffer_descriptors, // &vertex_buffer_descriptors,
// &default_specialization, // &default_specialization,
// None,
// ); // );
// } // }
// } // }

View file

@ -21,6 +21,14 @@ impl RenderPipeline {
..Default::default() ..Default::default()
} }
} }
pub fn specialized(pipeline: Handle<PipelineDescriptor>, specialization: PipelineSpecialization) -> Self {
RenderPipeline {
pipeline,
specialization,
..Default::default()
}
}
} }
#[derive(Properties)] #[derive(Properties)]
@ -31,6 +39,13 @@ pub struct RenderPipelines {
} }
impl RenderPipelines { impl RenderPipelines {
pub fn from_pipelines(pipelines: Vec<RenderPipeline>) -> Self {
Self {
pipelines,
..Default::default()
}
}
pub fn from_handles<'a, T: IntoIterator<Item = &'a Handle<PipelineDescriptor>>>( pub fn from_handles<'a, T: IntoIterator<Item = &'a Handle<PipelineDescriptor>>>(
handles: T, handles: T,
) -> Self { ) -> Self {

View file

@ -4,7 +4,7 @@ use crate::{
}; };
use bevy_app::EntityArchetype; use bevy_app::EntityArchetype;
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_render::{draw::Draw, mesh::Mesh, pipeline::RenderPipelines}; use bevy_render::{draw::Draw, mesh::Mesh, pipeline::{PipelineSpecialization, RenderPipelines, RenderPipeline, DynamicBinding}};
#[derive(EntityArchetype)] #[derive(EntityArchetype)]
pub struct SpriteEntity { pub struct SpriteEntity {
@ -19,12 +19,12 @@ pub struct SpriteEntity {
impl Default for SpriteEntity { impl Default for SpriteEntity {
fn default() -> Self { fn default() -> Self {
Self { Self {
mesh: QUAD_HANDLE,
render_pipelines: RenderPipelines::from_handles(&[SPRITE_PIPELINE_HANDLE]),
sprite: Default::default(), sprite: Default::default(),
quad: Default::default(), quad: Default::default(),
mesh: QUAD_HANDLE,
material: Default::default(), material: Default::default(),
draw: Default::default(), draw: Default::default(),
render_pipelines: RenderPipelines::from_handles(&[SPRITE_PIPELINE_HANDLE]),
} }
} }
} }
@ -45,11 +45,23 @@ pub struct SpriteSheetEntity {
impl Default for SpriteSheetEntity { impl Default for SpriteSheetEntity {
fn default() -> Self { fn default() -> Self {
Self { Self {
render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
SPRITE_SHEET_PIPELINE_HANDLE,
PipelineSpecialization {
dynamic_bindings: vec![
// TextureAtlasSprite
DynamicBinding {
bind_group: 2,
binding: 0,
},
],
..Default::default()
},
)]),
mesh: QUAD_HANDLE,
sprite: Default::default(), sprite: Default::default(),
texture_atlas: Default::default(), texture_atlas: Default::default(),
draw: Default::default(), draw: Default::default(),
render_pipelines: RenderPipelines::from_handles(&[SPRITE_SHEET_PIPELINE_HANDLE]),
mesh: QUAD_HANDLE,
// transform: Default::default(), // transform: Default::default(),
// translation: Default::default(), // translation: Default::default(),
// rotation: Default::default(), // rotation: Default::default(),

View file

@ -1,5 +1,5 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy_render::base_render_graph; use bevy_render::{pipeline::{PipelineSpecialization, RenderPipeline, DynamicBinding}, base_render_graph};
fn main() { fn main() {
App::build() App::build()
@ -78,7 +78,25 @@ fn setup(
// cube // cube
.add_entity(MeshMaterialEntity::<MyMaterial> { .add_entity(MeshMaterialEntity::<MyMaterial> {
mesh: cube_handle, mesh: cube_handle,
render_pipelines: RenderPipelines::from_handles(&[pipeline_handle]), render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
pipeline_handle,
// NOTE: in the future you wont need to manually declare dynamic bindings
PipelineSpecialization {
dynamic_bindings: vec![
// Transform
DynamicBinding {
bind_group: 1,
binding: 0,
},
// MyMaterial_color
DynamicBinding {
bind_group: 1,
binding: 1,
},
],
..Default::default()
},
)]),
material, material,
translation: Translation::new(0.0, 0.0, 0.0), translation: Translation::new(0.0, 0.0, 0.0),
..Default::default() ..Default::default()

View file

@ -1,5 +1,5 @@
use bevy::{prelude::*, render::shader}; use bevy::{prelude::*, render::shader};
use bevy_render::base_render_graph; use bevy_render::{pipeline::{PipelineSpecialization, RenderPipeline, DynamicBinding}, base_render_graph};
fn main() { fn main() {
App::build() App::build()
@ -18,7 +18,7 @@ struct MyMaterial {
pub color: Color, pub color: Color,
#[render_resources(ignore)] #[render_resources(ignore)]
#[shader_def] #[shader_def]
pub always_red: bool, pub always_blue: bool,
} }
const VERTEX_SHADER: &str = r#" const VERTEX_SHADER: &str = r#"
@ -44,8 +44,8 @@ layout(set = 1, binding = 1) uniform MyMaterial_color {
void main() { void main() {
o_Target = color; o_Target = color;
# ifdef MYMATERIAL_ALWAYS_RED # ifdef MYMATERIAL_ALWAYS_BLUE
o_Target = vec4(0.8, 0.0, 0.0, 1.0); o_Target = vec4(0.0, 0.0, 0.8, 1.0);
# endif # endif
} }
"#; "#;
@ -78,13 +78,13 @@ fn setup(
// Create a green material // Create a green material
let green_material = materials.add(MyMaterial { let green_material = materials.add(MyMaterial {
color: Color::rgb(0.0, 0.8, 0.0), color: Color::rgb(0.0, 0.8, 0.0),
always_red: false, always_blue: false,
}); });
// Create a red material, which uses our "always_red" shader def // Create a blue material, which uses our "always_blue" shader def
let red_material = materials.add(MyMaterial { let red_material = materials.add(MyMaterial {
color: Color::rgb(0.0, 0.0, 0.0), color: Color::rgb(0.0, 0.0, 0.0),
always_red: true, always_blue: true,
}); });
// Create a cube mesh which will use our materials // Create a cube mesh which will use our materials
@ -95,7 +95,25 @@ fn setup(
// cube // cube
.add_entity(MeshMaterialEntity::<MyMaterial> { .add_entity(MeshMaterialEntity::<MyMaterial> {
mesh: cube_handle, mesh: cube_handle,
render_pipelines: RenderPipelines::from_handles(&[pipeline_handle]), render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
pipeline_handle,
// NOTE: in the future you wont need to manually declare dynamic bindings
PipelineSpecialization {
dynamic_bindings: vec![
// Transform
DynamicBinding {
bind_group: 1,
binding: 0,
},
// MyMaterial_color
DynamicBinding {
bind_group: 1,
binding: 1,
},
],
..Default::default()
},
)]),
material: green_material, material: green_material,
translation: Translation::new(-2.0, 0.0, 0.0), translation: Translation::new(-2.0, 0.0, 0.0),
..Default::default() ..Default::default()
@ -103,7 +121,25 @@ fn setup(
// cube // cube
.add_entity(MeshMaterialEntity::<MyMaterial> { .add_entity(MeshMaterialEntity::<MyMaterial> {
mesh: cube_handle, mesh: cube_handle,
render_pipelines: RenderPipelines::from_handles(&[pipeline_handle]), render_pipelines: RenderPipelines::from_pipelines(vec![RenderPipeline::specialized(
pipeline_handle,
// NOTE: in the future you wont need to manually declare dynamic bindings
PipelineSpecialization {
dynamic_bindings: vec![
// Transform
DynamicBinding {
bind_group: 1,
binding: 0,
},
// MyMaterial_color
DynamicBinding {
bind_group: 1,
binding: 1,
},
],
..Default::default()
},
)]),
material: red_material, material: red_material,
translation: Translation::new(2.0, 0.0, 0.0), translation: Translation::new(2.0, 0.0, 0.0),
..Default::default() ..Default::default()