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_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};
#[derive(EntityArchetype, Default)]
#[derive(EntityArchetype)]
pub struct MeshEntity {
// #[tag]
pub mesh: Handle<Mesh>,
// #[tag]
pub material: Handle<StandardMaterial>,
pub draw: Draw,
pub render_pipelines: RenderPipelines,
@ -18,6 +20,38 @@ pub struct MeshEntity {
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)]
pub struct LightEntity {
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_render::{
base_render_graph,
@ -36,7 +36,7 @@ impl ForwardPbrRenderGraphBuilder for RenderGraph {
self.add_system_node(node::LIGHTS, LightsNode::new(10));
let mut shaders = resources.get_mut::<Assets<Shader>>().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
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::{
pipeline::{
state_descriptors::{
@ -12,6 +12,8 @@ use bevy_render::{
texture::TextureFormat,
};
pub const FORWARD_PIPELINE_HANDLE: Handle<PipelineDescriptor> =
Handle::from_u128(131483623140127713893804825450360211204);
pub fn build_forward_pipeline(shaders: &mut Assets<Shader>) -> PipelineDescriptor {
PipelineDescriptor {
rasterization_state: Some(RasterizationStateDescriptor {

View file

@ -4,10 +4,9 @@ use super::{
CompareFunction, CullMode, DepthStencilStateDescriptor, FrontFace, IndexFormat,
PrimitiveTopology, RasterizationStateDescriptor, StencilStateFaceDescriptor,
},
BindType, PipelineLayout, VertexBufferDescriptors,
BindType, DynamicBinding, PipelineLayout, VertexBufferDescriptors,
};
use crate::{
render_resource::{RenderResourceBinding, RenderResourceBindings},
shader::{Shader, ShaderStages},
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
/// 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
/// 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(
&mut self,
shaders: &Assets<Shader>,
bevy_conventions: bool,
vertex_buffer_descriptors: Option<&VertexBufferDescriptors>,
render_resource_bindings: Option<&RenderResourceBindings>,
dynamic_bindings: &[DynamicBinding],
) {
let vertex_spirv = shaders.get(&self.shader_stages.vertex).unwrap();
let fragment_spirv = self
@ -148,18 +146,16 @@ impl PipelineDescriptor {
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
// 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 binding in bind_group.bindings.iter_mut() {
if let Some(RenderResourceBinding::Buffer {
dynamic_index: Some(_),
..
}) = render_resource_bindings.get(&binding.name)
{
let current = DynamicBinding {
bind_group: bind_group.index,
binding: binding.index,
};
if dynamic_bindings.contains(&current) {
if let BindType::Uniform {
ref mut dynamic, ..
} = binding.bind_type

View file

@ -3,18 +3,19 @@ use super::{
VertexBufferDescriptors,
};
use crate::{
render_resource::RenderResourceBindings,
renderer::RenderResourceContext,
shader::{Shader, ShaderSource},
};
use bevy_asset::{Assets, Handle};
use bevy_asset::{Assets, Handle, AssetEvent};
use legion::prelude::*;
use std::collections::{HashMap, HashSet};
use bevy_app::Events;
#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct PipelineSpecialization {
pub shader_specialization: ShaderSpecialization,
pub primitive_topology: PrimitiveTopology,
pub dynamic_bindings: Vec<DynamicBinding>,
}
#[derive(Clone, Eq, PartialEq, Debug, Default)]
@ -32,7 +33,12 @@ struct SpecializedPipeline {
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)]
pub struct PipelineCompiler {
specialized_shaders: HashMap<Handle<Shader>, Vec<SpecializedShader>>,
@ -92,7 +98,6 @@ impl PipelineCompiler {
source_pipeline: Handle<PipelineDescriptor>,
vertex_buffer_descriptors: &VertexBufferDescriptors,
pipeline_specialization: &PipelineSpecialization,
render_resource_bindings: &RenderResourceBindings,
) -> Handle<PipelineDescriptor> {
let source_descriptor = pipelines.get(&source_pipeline).unwrap();
let mut specialized_descriptor = source_descriptor.clone();
@ -117,7 +122,7 @@ impl PipelineCompiler {
shaders,
true,
Some(vertex_buffer_descriptors),
Some(render_resource_bindings),
&pipeline_specialization.dynamic_bindings,
);
specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology;
@ -176,7 +181,6 @@ impl PipelineCompiler {
source_pipeline,
vertex_buffer_descriptors,
&render_pipeline.specialization,
&render_pipelines.bindings,
)
};
@ -213,15 +217,15 @@ pub fn compile_pipelines_system(
mut pipeline_compiler: ResMut<PipelineCompiler>,
mut shaders: ResMut<Assets<Shader>>,
mut pipelines: ResMut<Assets<PipelineDescriptor>>,
// pipeline_asset_events: Res<Events<AssetEvent<PipelineDescriptor>>>,
_pipeline_asset_events: Res<Events<AssetEvent<PipelineDescriptor>>>,
vertex_buffer_descriptors: Res<VertexBufferDescriptors>,
render_resource_context: Res<Box<dyn RenderResourceContext>>,
query: &mut Query<Write<RenderPipelines>>,
) {
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
// "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() {
// let handle_to_compile = match event {
// AssetEvent::Created { handle } => Some(*handle),
@ -229,13 +233,14 @@ pub fn compile_pipelines_system(
// // TODO: clean up old pipelines
// Some(*handle)
// }
// AssetEvent::Removed { handle } => {
// AssetEvent::Removed { .. } => {
// // TODO: clean up old pipelines
// None
// }
// };
// if let Some(handle_to_compile) = handle_to_compile {
// // TODO: try updating specialization here.
// pipeline_compiler.compile_pipeline(
// render_resource_context,
// &mut pipelines,
@ -243,7 +248,6 @@ pub fn compile_pipelines_system(
// handle_to_compile,
// &vertex_buffer_descriptors,
// &default_specialization,
// None,
// );
// }
// }

View file

@ -21,6 +21,14 @@ impl RenderPipeline {
..Default::default()
}
}
pub fn specialized(pipeline: Handle<PipelineDescriptor>, specialization: PipelineSpecialization) -> Self {
RenderPipeline {
pipeline,
specialization,
..Default::default()
}
}
}
#[derive(Properties)]
@ -31,6 +39,13 @@ pub struct 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>>>(
handles: T,
) -> Self {

View file

@ -4,7 +4,7 @@ use crate::{
};
use bevy_app::EntityArchetype;
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)]
pub struct SpriteEntity {
@ -19,12 +19,12 @@ pub struct SpriteEntity {
impl Default for SpriteEntity {
fn default() -> Self {
Self {
mesh: QUAD_HANDLE,
render_pipelines: RenderPipelines::from_handles(&[SPRITE_PIPELINE_HANDLE]),
sprite: Default::default(),
quad: Default::default(),
mesh: QUAD_HANDLE,
material: Default::default(),
draw: Default::default(),
render_pipelines: RenderPipelines::from_handles(&[SPRITE_PIPELINE_HANDLE]),
}
}
}
@ -36,20 +36,32 @@ pub struct SpriteSheetEntity {
pub draw: Draw,
pub render_pipelines: RenderPipelines,
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
// pub transform: Transform,
// pub translation: Translation,
// pub rotation: Rotation,
// pub scale: Scale,
// pub transform: Transform,
// pub translation: Translation,
// pub rotation: Rotation,
// pub scale: Scale,
}
impl Default for SpriteSheetEntity {
fn default() -> 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(),
texture_atlas: Default::default(),
draw: Default::default(),
render_pipelines: RenderPipelines::from_handles(&[SPRITE_SHEET_PIPELINE_HANDLE]),
mesh: QUAD_HANDLE,
// transform: Default::default(),
// translation: Default::default(),
// rotation: Default::default(),

View file

@ -1,5 +1,5 @@
use bevy::prelude::*;
use bevy_render::base_render_graph;
use bevy_render::{pipeline::{PipelineSpecialization, RenderPipeline, DynamicBinding}, base_render_graph};
fn main() {
App::build()
@ -78,7 +78,25 @@ fn setup(
// cube
.add_entity(MeshMaterialEntity::<MyMaterial> {
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,
translation: Translation::new(0.0, 0.0, 0.0),
..Default::default()

View file

@ -1,5 +1,5 @@
use bevy::{prelude::*, render::shader};
use bevy_render::base_render_graph;
use bevy_render::{pipeline::{PipelineSpecialization, RenderPipeline, DynamicBinding}, base_render_graph};
fn main() {
App::build()
@ -18,7 +18,7 @@ struct MyMaterial {
pub color: Color,
#[render_resources(ignore)]
#[shader_def]
pub always_red: bool,
pub always_blue: bool,
}
const VERTEX_SHADER: &str = r#"
@ -44,8 +44,8 @@ layout(set = 1, binding = 1) uniform MyMaterial_color {
void main() {
o_Target = color;
# ifdef MYMATERIAL_ALWAYS_RED
o_Target = vec4(0.8, 0.0, 0.0, 1.0);
# ifdef MYMATERIAL_ALWAYS_BLUE
o_Target = vec4(0.0, 0.0, 0.8, 1.0);
# endif
}
"#;
@ -78,13 +78,13 @@ fn setup(
// Create a green material
let green_material = materials.add(MyMaterial {
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 {
color: Color::rgb(0.0, 0.0, 0.0),
always_red: true,
always_blue: true,
});
// Create a cube mesh which will use our materials
@ -95,7 +95,25 @@ fn setup(
// cube
.add_entity(MeshMaterialEntity::<MyMaterial> {
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,
translation: Translation::new(-2.0, 0.0, 0.0),
..Default::default()
@ -103,7 +121,25 @@ fn setup(
// cube
.add_entity(MeshMaterialEntity::<MyMaterial> {
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,
translation: Translation::new(2.0, 0.0, 0.0),
..Default::default()