bevy/src/render/render_graph/renderable.rs
2020-03-08 23:19:07 -07:00

211 lines
7.9 KiB
Rust

use super::PipelineDescriptor;
use crate::{
asset::{AssetStorage, Handle},
render::{
render_graph::{resource_name, RenderGraph},
Shader, ShaderSource,
},
};
use legion::prelude::*;
use std::collections::{HashMap, HashSet};
pub struct Renderable {
pub is_visible: bool,
pub pipelines: Vec<Handle<PipelineDescriptor>>,
pub shader_defs: HashSet<String>,
}
impl Default for Renderable {
fn default() -> Self {
Renderable {
is_visible: true,
pipelines: vec![
Handle::new(0), // TODO: this could be better
],
shader_defs: HashSet::new(),
}
}
}
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 ShaderPipelineAssignments {
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<Entity>>,
}
impl ShaderPipelineAssignments {
pub fn new() -> Self {
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, 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));
let compiled_shader_handle = shader_storage.add(compiled_shader);
Some(compiled_shader_handle)
}
}
pub fn update_shader_assignments(
world: &mut World,
resources: &mut Resources,
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 mut shader_pipeline_assignments =
resources.get_mut::<ShaderPipelineAssignments>().unwrap();
let mut compiled_shader_map = resources.get_mut::<CompiledShaderMap>().unwrap();
let mut shader_storage = resources.get_mut::<AssetStorage<Shader>>().unwrap();
let mut pipeline_descriptor_storage = 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() {
if let None = compiled_shader_map
.pipeline_to_macro_pipelines
.get(pipeline_handle)
{
compiled_shader_map
.pipeline_to_macro_pipelines
.insert(*pipeline_handle, 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
} else {
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;
}
let macroed_pipeline_handle =
pipeline_descriptor_storage.add(macroed_pipeline);
// TODO: get correct pass name
render_graph
.add_pipeline(resource_name::pass::MAIN, macroed_pipeline_handle);
macroed_pipeline_handle
} else {
*pipeline_handle
}
};
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));
macroed_pipeline_handle
};
// 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, Vec::new());
}
let assignments = shader_pipeline_assignments
.assignments
.get_mut(&final_handle)
.unwrap();
assignments.push(entity);
}
}
}
// cleanup entity shader_defs so next frame they can be refreshed
for mut renderable in <Write<Renderable>>::query().iter_mut(world) {
renderable.shader_defs = HashSet::new();
}
}