only update pipelines when macros change. better handle debug print

This commit is contained in:
Carter Anderson 2020-02-15 19:00:30 -08:00
parent 26588d0c41
commit 56e5414b63
8 changed files with 120 additions and 67 deletions

View file

@ -1,4 +1,5 @@
use bevy::{prelude::*, render::render_graph_2::StandardMaterial};
use core::marker::PhantomData;
use bevy::{prelude::*, render::render_graph_2::{Renderable, StandardMaterial}};
use rand::{rngs::StdRng, Rng, SeedableRng};
use std::collections::VecDeque;
@ -83,9 +84,15 @@ fn setup(world: &mut World) {
mesh: cube_handle.clone(),
material: StandardMaterial {
albedo: math::vec4(1.0, 0.0, 0.0, 1.0),
everything_is_red: false,
everything_is_red: true,
},
translation: Translation::new(0.0, 0.0, 1.0),
renderable: Renderable {
pipelines: vec![
Handle::new(0), // Forward Pipeline Handle
],
..Default::default()
},
..Default::default()
})
.add_archetype(NewMeshEntity {

View file

@ -8,7 +8,7 @@ use crate::{
passes::*,
render_graph_2::{
passes::*, pipelines::*, renderers::wgpu_renderer::WgpuRenderer, resource_providers::*,
ShaderAssignments, StandardMaterial,
ShaderPipelineAssignments, StandardMaterial,
},
*,
},
@ -16,7 +16,7 @@ use crate::{
};
use bevy_transform::{prelude::LocalToWorld, transform_system_bundle};
use render_graph_2::{CompiledShaderMap, PipelineDescriptor, resource_name, draw_targets::{ui_draw_target, mesh_draw_target}};
use render_graph_2::{CompiledShaderMap, PipelineDescriptor, resource_name, draw_targets::{ui_draw_target, meshes_draw_target}};
use std::collections::HashMap;
pub struct AppBuilder {
@ -167,7 +167,7 @@ impl AppBuilder {
resources.insert(AssetStorage::<Texture>::new());
resources.insert(AssetStorage::<Shader>::new());
resources.insert(AssetStorage::<PipelineDescriptor>::new());
resources.insert(ShaderAssignments::new());
resources.insert(ShaderPipelineAssignments::new());
resources.insert(CompiledShaderMap::new());
self
}
@ -195,7 +195,7 @@ impl AppBuilder {
.unwrap();
self.render_graph_builder = self
.render_graph_builder
.add_draw_target(resource_name::draw_target::MESHES, mesh_draw_target)
.add_draw_target(resource_name::draw_target::MESHES, meshes_draw_target)
.add_draw_target(resource_name::draw_target::UI, ui_draw_target)
.add_resource_provider(Box::new(CameraResourceProvider))
.add_resource_provider(Box::new(Camera2dResourceProvider))

View file

@ -3,18 +3,29 @@ mod mesh;
mod texture;
pub use self::gltf::load_gltf;
use std::hash::{Hash, Hasher};
pub use mesh::*;
use std::{
fmt::Debug,
hash::{Hash, Hasher},
};
pub use texture::*;
use std::{collections::HashMap, marker::PhantomData};
#[derive(Debug)]
pub struct Handle<T> {
pub id: usize,
marker: PhantomData<T>,
}
impl<T> Handle<T> {
pub fn new(id: usize) -> Self {
Handle {
id,
marker: PhantomData,
}
}
}
impl<T> Hash for Handle<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
@ -29,6 +40,13 @@ impl<T> PartialEq for Handle<T> {
impl<T> Eq for Handle<T> {}
impl<T> Debug for Handle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
let name = std::any::type_name::<T>().split("::").last().unwrap();
write!(f, "Handle<{}>({})", name, self.id)
}
}
// TODO: somehow handle this gracefully in asset managers. or alternatively remove Default
impl<T> Default for Handle<T> {
fn default() -> Self {

View file

@ -9,7 +9,7 @@ use crate::{
use zerocopy::AsBytes;
pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) {
pub fn meshes_draw_target(world: &World, render_pass: &mut dyn RenderPass) {
let mesh_storage = world.resources.get_mut::<AssetStorage<Mesh>>().unwrap();
let mut current_mesh_id = None;
let mut current_mesh_index_length = 0;

View file

@ -1,5 +1,5 @@
mod mesh_draw_target;
mod meshes_draw_target;
mod ui_draw_target;
pub use mesh_draw_target::*;
pub use meshes_draw_target::*;
pub use ui_draw_target::*;

View file

@ -1,6 +1,6 @@
use crate::{
asset::{AssetStorage, Handle},
render::{render_graph_2::RenderGraph, Shader, ShaderStages},
render::{render_graph_2::RenderGraph, Shader, ShaderStages, ShaderSource},
};
use legion::prelude::*;
use std::collections::{HashMap, HashSet};
@ -25,58 +25,114 @@ impl Default for Renderable {
pub struct CompiledShaderMap {
// TODO: need macro hash lookup
pub source_to_compiled: HashMap<Handle<Shader>, Vec<(HashSet<String>, Handle<Shader>)>>,
pub pipeline_to_macro_pipelines: HashMap<Handle<PipelineDescriptor>, Vec<(HashSet<String>, Handle<PipelineDescriptor>)>>,
}
impl CompiledShaderMap {
pub fn new() -> Self {
CompiledShaderMap {
source_to_compiled: HashMap::new(),
pipeline_to_macro_pipelines: HashMap::new(),
}
}
}
pub struct ShaderAssignments {
pub assignments: HashMap<usize, Vec<Entity>>,
pub struct ShaderPipelineAssignments {
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<Entity>>,
}
impl ShaderAssignments {
impl ShaderPipelineAssignments {
pub fn new() -> Self {
ShaderAssignments {
ShaderPipelineAssignments {
assignments: HashMap::new(),
}
}
}
fn try_compiling_shader_with_macros(compiled_shader_map: &mut CompiledShaderMap, shader_storage: &mut AssetStorage<Shader>, renderable: &Renderable, shader_handle: &Handle<Shader>) -> Option<Handle<Shader>> {
if let None = compiled_shader_map.source_to_compiled.get(shader_handle) {
compiled_shader_map
.source_to_compiled
.insert(shader_handle.clone(), Vec::new());
}
let compiled_shaders = compiled_shader_map.source_to_compiled.get_mut(shader_handle).unwrap();
let shader = shader_storage.get(shader_handle).unwrap();
// don't produce new shader if the input source is already spriv
if let ShaderSource::Spirv(_) = shader.source {
return None;
}
if let Some((_shader_defs, compiled_shader)) = compiled_shaders.iter().find(|(shader_defs, _shader)| *shader_defs == renderable.shader_defs) {
Some(compiled_shader.clone())
} else {
let shader_def_vec = renderable.shader_defs.iter().cloned().collect::<Vec<String>>();
let compiled_shader = shader.get_spirv_shader(Some(&shader_def_vec));
compiled_shaders.push((renderable.shader_defs.clone(), shader_handle.clone()));
let compiled_shader_handle = shader_storage.add(compiled_shader);
Some(compiled_shader_handle)
}
}
pub fn update_shader_assignments(world: &mut World, render_graph: &mut RenderGraph) {
// PERF: this seems like a lot of work for things that don't change that often.
// lots of string + hashset allocations. sees uniform_resource_provider for more context
{
let shader_assignments = world.resources.get_mut::<ShaderAssignments>().unwrap();
let mut shader_pipeline_assignments = world.resources.get_mut::<ShaderPipelineAssignments>().unwrap();
let mut compiled_shader_map = world.resources.get_mut::<CompiledShaderMap>().unwrap();
let mut shader_storage = world.resources.get_mut::<AssetStorage<Shader>>().unwrap();
let pipeline_descriptor_storage = world.resources.get_mut::<AssetStorage<PipelineDescriptor>>().unwrap();
let mut pipeline_descriptor_storage = world.resources.get_mut::<AssetStorage<PipelineDescriptor>>().unwrap();
// reset assignments so they are updated every frame
shader_pipeline_assignments.assignments = HashMap::new();
for (entity, renderable) in <Read<Renderable>>::query().iter_entities(world) {
for pipeline_handle in renderable.pipelines.iter() {
let pipeline_descriptor = pipeline_descriptor_storage.get(pipeline_handle).unwrap();
for shader_handle in pipeline_descriptor.shader_stages.iter() {
if let None = compiled_shader_map.source_to_compiled.get(shader_handle) {
compiled_shader_map
.source_to_compiled
.insert(shader_handle.clone(), Vec::new());
}
let compiled_shaders = compiled_shader_map.source_to_compiled.get_mut(shader_handle).unwrap();
if let None = compiled_shaders.iter().find(|(shader_defs, _shader)| *shader_defs == renderable.shader_defs) {
let shader_resource = shader_storage.get(shader_handle).unwrap();
let shader_def_vec = renderable.shader_defs.iter().cloned().collect::<Vec<String>>();
let compiled_shader = shader_resource.get_spirv_shader(Some(&shader_def_vec));
compiled_shaders.push((renderable.shader_defs.clone(), shader_handle.clone()));
let compiled_shader_handle = shader_storage.add(compiled_shader);
// TODO: collecting assigments in a map means they won't be removed when the macro changes
// TODO: need to somehow grab base shader's pipeline, then copy it
// shader_assignments.assignments.insert()
}
if let None = compiled_shader_map.pipeline_to_macro_pipelines.get(pipeline_handle) {
compiled_shader_map
.pipeline_to_macro_pipelines
.insert(pipeline_handle.clone(), Vec::new());
}
let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = compiled_shader_map.pipeline_to_macro_pipelines.get_mut(pipeline_handle).unwrap().iter().find(|(shader_defs, _macroed_pipeline_handle)| *shader_defs == renderable.shader_defs) {
macroed_pipeline_handle.clone()
} else {
println!("Create pipeline {:?} {:?}", pipeline_handle, renderable.shader_defs);
let pipeline_descriptor = pipeline_descriptor_storage.get(pipeline_handle).unwrap();
let macroed_pipeline_handle = {
let mut macroed_vertex_handle = try_compiling_shader_with_macros(&mut compiled_shader_map, &mut shader_storage, &renderable, &pipeline_descriptor.shader_stages.vertex);
let mut macroed_fragment_handle = pipeline_descriptor.shader_stages.fragment.as_ref().map(|fragment| try_compiling_shader_with_macros(&mut compiled_shader_map, &mut shader_storage, &renderable, fragment));
if macroed_vertex_handle.is_some() || macroed_fragment_handle.is_some() {
let mut macroed_pipeline = pipeline_descriptor.clone();
if let Some(vertex) = macroed_vertex_handle.take() {
macroed_pipeline.shader_stages.vertex = vertex;
}
if let Some(fragment) = macroed_fragment_handle.take() {
macroed_pipeline.shader_stages.fragment = fragment;
}
pipeline_descriptor_storage.add(macroed_pipeline)
} else {
pipeline_handle.clone()
}
};
let macro_pipelines = compiled_shader_map.pipeline_to_macro_pipelines.get_mut(pipeline_handle).unwrap();
macro_pipelines.push((renderable.shader_defs.clone(), macroed_pipeline_handle.clone()));
macroed_pipeline_handle
};
// TODO: collecting assigments in a map means they won't be removed when the macro changes
// TODO: this will break down if pipeline layout changes. fix this with "autolayout"
if let None = shader_pipeline_assignments.assignments.get(&final_handle) {
shader_pipeline_assignments.assignments.insert(final_handle.clone(), Vec::new());
}
let assignments = shader_pipeline_assignments.assignments.get_mut(&final_handle).unwrap();
assignments.push(entity);
}
}
}

View file

@ -36,6 +36,7 @@ impl GetBytes for Vec4 {
}
}
// TODO: add ability to specify specific pipeline for uniforms
pub trait AsUniforms {
fn get_uniform_infos(&self) -> &[UniformInfo];
fn get_uniform_info(&self, name: &str) -> Option<&UniformInfo>;

View file

@ -96,33 +96,4 @@ impl ShaderStages {
fragment: None,
}
}
pub fn iter(&self) -> ShaderStagesIter {
ShaderStagesIter {
shader_stages: self,
index: 0,
}
}
}
pub struct ShaderStagesIter<'a> {
pub shader_stages: &'a ShaderStages,
pub index: usize,
}
impl<'a> Iterator for ShaderStagesIter<'a> {
type Item = &'a Handle<Shader>;
fn next(&mut self) -> Option<&'a Handle<Shader>> {
match self.index {
0 => Some(&self.shader_stages.vertex),
1 => {
if let Some(ref fragment) = self.shader_stages.fragment {
Some(fragment)
} else {
None
}
}
_ => None,
}
}
}
}