renderable owns render resource assignments. refactor shader assignment logic. renderers are responsible for intializing during updates.

This commit is contained in:
Carter Anderson 2020-03-22 18:22:35 -07:00
parent 55130bbe1c
commit 55745b0812
12 changed files with 260 additions and 227 deletions

View file

@ -70,6 +70,8 @@ pub fn derive_entity_archetype(input: TokenStream) -> TokenStream {
}
})
}
// TODO: ensure shader_def and instance/vertex are mutually exclusive
#[derive(FromMeta, Debug, Default)]
struct UniformAttributeArgs {
#[darling(default)]
@ -329,6 +331,7 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream {
}
}
// TODO: move this to field_info and add has_shader_def(&self, &str) -> bool
// TODO: this will be very allocation heavy. find a way to either make this allocation free
// or alternatively only run it when the shader_defs have changed
fn get_shader_defs(&self) -> Option<Vec<String>> {

View file

@ -65,12 +65,6 @@ impl App {
self.resources.insert(window);
log::info!("Initializing the example...");
if let Some(ref mut renderer) = self.renderer {
renderer.initialize(&mut self.world, &mut self.resources);
}
log::info!("Entering render loop...");
event_loop.run(move |event, _, control_flow| {
*control_flow = if cfg!(feature = "metal-auto-capture") {

View file

@ -16,7 +16,8 @@ use bevy_transform::{prelude::LocalToWorld, transform_system_bundle};
use pipeline::PipelineDescriptor;
use render_graph::{RenderGraph, RenderGraphBuilder};
use render_resource::{
AssetBatchers, EntityRenderResourceAssignments, RenderResourceAssignmentsProvider,
build_entity_render_resource_assignments_system, AssetBatchers,
EntityRenderResourceAssignments, RenderResourceAssignmentsProvider,
};
use shader::Shader;
use std::collections::HashMap;
@ -175,6 +176,7 @@ impl AppBuilder {
}
pub fn add_default_systems(&mut self) -> &mut Self {
self.add_system(build_entity_render_resource_assignments_system());
self.add_system(ui::ui_update_system::build_ui_update_system());
for transform_system in
transform_system_bundle::build(self.world.as_mut().unwrap()).drain(..)

View file

@ -28,13 +28,16 @@ impl DrawTarget for AssignedMeshesDrawTarget {
let mut current_mesh_handle = None;
let mut current_mesh_index_len = 0;
let assigned_entities = shader_pipeline_assignments
let assigned_render_resource_assignments = shader_pipeline_assignments
.assignments
.get(&pipeline_handle);
if let Some(assigned_entities) = assigned_entities {
for entity in assigned_entities.iter() {
if let Some(assigned_render_resource_assignments) = assigned_render_resource_assignments {
for assignment_id in assigned_render_resource_assignments.iter() {
// TODO: hopefully legion has better random access apis that are more like queries?
let entity = entity_render_resource_assignments
.get(*assignment_id)
.unwrap();
let renderable = world.get_component::<Renderable>(*entity).unwrap();
if !renderable.is_visible || renderable.is_instanced {
continue;
@ -63,8 +66,7 @@ impl DrawTarget for AssignedMeshesDrawTarget {
}
// TODO: validate bind group properties against shader uniform properties at least once
let render_resource_assignments = entity_render_resource_assignments.get(*entity);
render_pass.set_bind_groups(render_resource_assignments);
render_pass.set_bind_groups(renderable.render_resource_assignments.as_ref());
render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1);
}
}
@ -78,26 +80,28 @@ impl DrawTarget for AssignedMeshesDrawTarget {
pipeline_handle: Handle<PipelineDescriptor>,
) {
let shader_pipeline_assignments = resources.get::<ShaderPipelineAssignments>().unwrap();
let assigned_entities = shader_pipeline_assignments
let entity_render_resource_assignments =
resources.get::<EntityRenderResourceAssignments>().unwrap();
let assigned_render_resource_assignments = shader_pipeline_assignments
.assignments
.get(&pipeline_handle);
let pipeline_storage = resources.get::<AssetStorage<PipelineDescriptor>>().unwrap();
let entity_render_resource_assignments =
resources.get::<EntityRenderResourceAssignments>().unwrap();
let pipeline_descriptor = pipeline_storage.get(&pipeline_handle).unwrap();
if let Some(assigned_entities) = assigned_entities {
for entity in assigned_entities.iter() {
if let Some(assigned_render_resource_assignments) = assigned_render_resource_assignments {
for assignment_id in assigned_render_resource_assignments.iter() {
// TODO: hopefully legion has better random access apis that are more like queries?
let entity = entity_render_resource_assignments
.get(*assignment_id)
.unwrap();
let renderable = world.get_component::<Renderable>(*entity).unwrap();
if !renderable.is_visible || renderable.is_instanced {
continue;
}
if let Some(render_resource_assignments) =
entity_render_resource_assignments.get(*entity)
{
renderer.setup_bind_groups(render_resource_assignments, pipeline_descriptor);
}
renderer.setup_bind_groups(
renderable.render_resource_assignments.as_ref().unwrap(),
pipeline_descriptor,
);
}
}
}

View file

@ -5,7 +5,7 @@ use crate::{
draw_target::DrawTarget,
mesh::Mesh,
pipeline::PipelineDescriptor,
render_resource::{resource_name, EntityRenderResourceAssignments, ResourceInfo},
render_resource::{resource_name, ResourceInfo},
renderer::RenderPass,
Renderable,
},
@ -18,17 +18,14 @@ impl DrawTarget for MeshesDrawTarget {
fn draw(
&self,
world: &World,
resources: &Resources,
_resources: &Resources,
render_pass: &mut dyn RenderPass,
_pipeline_handle: Handle<PipelineDescriptor>,
) {
let mut current_mesh_handle = None;
let mut current_mesh_index_len = 0;
let mesh_query = <(Read<Handle<Mesh>>, Read<Renderable>)>::query();
let entity_render_resource_assignments =
resources.get::<EntityRenderResourceAssignments>().unwrap();
for (entity, (mesh, renderable)) in mesh_query.iter_entities(world) {
for (mesh, renderable) in mesh_query.iter(world) {
if !renderable.is_visible || renderable.is_instanced {
continue;
}
@ -55,8 +52,7 @@ impl DrawTarget for MeshesDrawTarget {
}
// TODO: validate bind group properties against shader uniform properties at least once
let render_resource_assignments = entity_render_resource_assignments.get(entity);
render_pass.set_bind_groups(render_resource_assignments);
render_pass.set_bind_groups(renderable.render_resource_assignments.as_ref());
render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1);
}
}

View file

@ -1,22 +1,37 @@
use super::RenderResourceAssignments;
use legion::prelude::Entity;
use super::{RenderResourceAssignmentsId, RenderResourceAssignmentsProvider};
use crate::prelude::Renderable;
use legion::prelude::*;
use std::collections::HashMap;
#[derive(Default)]
pub struct EntityRenderResourceAssignments {
entity_assignments: HashMap<Entity, RenderResourceAssignments>,
entity_assignments: HashMap<RenderResourceAssignmentsId, Entity>,
}
impl EntityRenderResourceAssignments {
pub fn set(&mut self, entity: Entity, assignments: RenderResourceAssignments) {
self.entity_assignments.insert(entity, assignments);
pub fn set(&mut self, id: RenderResourceAssignmentsId, entity: Entity) {
self.entity_assignments.insert(id, entity);
}
pub fn get(&self, entity: Entity) -> Option<&RenderResourceAssignments> {
self.entity_assignments.get(&entity)
}
pub fn get_mut(&mut self, entity: Entity) -> Option<&mut RenderResourceAssignments> {
self.entity_assignments.get_mut(&entity)
pub fn get(&self, id: RenderResourceAssignmentsId) -> Option<&Entity> {
self.entity_assignments.get(&id)
}
}
pub fn build_entity_render_resource_assignments_system() -> Box<dyn Schedulable> {
SystemBuilder::new("EntityRenderResourceAssignments")
.write_resource::<EntityRenderResourceAssignments>()
.write_resource::<RenderResourceAssignmentsProvider>()
.with_query(<Write<Renderable>>::query().filter(changed::<Renderable>()))
.build(|_, world, (entity_assignments, provider), query| {
for (entity, mut renderable) in query.iter_entities_mut(world) {
if renderable.is_instanced {
renderable.render_resource_assignments = None;
} else if let None = renderable.render_resource_assignments {
let render_resource_assignments = provider.next();
entity_assignments.set(render_resource_assignments.get_id(), entity);
renderable.render_resource_assignments = Some(render_resource_assignments);
}
}
})
}

View file

@ -1,12 +1,14 @@
use super::RenderResource;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
// TODO: consider merging this with entity_uniform_resource
// PERF: if the assignments are scoped to a specific pipeline layout, then names could be replaced with indices here for a perf boost
#[derive(Eq, PartialEq, Debug)]
pub struct RenderResourceAssignments {
id: RenderResourceAssignmentsId,
render_resources: HashMap<String, RenderResource>,
pub(crate) shader_defs: HashSet<String>,
// TODO: move offsets here to reduce hashing costs?
// render_resource_offsets: HashMap<String, >,
}
impl RenderResourceAssignments {
@ -36,6 +38,7 @@ impl RenderResourceAssignmentsProvider {
let assignments = RenderResourceAssignments {
id: RenderResourceAssignmentsId(self.current_id),
render_resources: HashMap::new(),
shader_defs: HashSet::new(),
};
self.current_id += 1;

View file

@ -5,9 +5,8 @@ use crate::{
render_graph::RenderGraph,
render_resource::{
AssetBatchers, BufferArrayInfo, BufferDynamicUniformInfo, BufferInfo, BufferUsage,
EntityRenderResourceAssignments, RenderResource, RenderResourceAssignments,
RenderResourceAssignmentsId, RenderResourceAssignmentsProvider, ResourceInfo,
ResourceProvider,
RenderResource, RenderResourceAssignments, RenderResourceAssignmentsId,
RenderResourceAssignmentsProvider, ResourceInfo, ResourceProvider,
},
renderer::Renderer,
shader::{AsUniforms, UniformInfoIter},
@ -39,7 +38,7 @@ where
>,
asset_resources: HashMap<Handle<T>, HashMap<String, RenderResource>>,
resource_query: Query<
(Read<T>, Read<Renderable>),
(Read<T>, Write<Renderable>),
EntityFilterTuple<
And<(ComponentFilter<T>, ComponentFilter<Renderable>)>,
And<(Passthrough, Passthrough)>,
@ -48,7 +47,7 @@ where
>,
handle_query: Option<
Query<
(Read<Handle<T>>, Read<Renderable>),
(Read<Handle<T>>, Write<Renderable>),
EntityFilterTuple<
And<(ComponentFilter<Handle<T>>, ComponentFilter<Renderable>)>,
And<(Passthrough, Passthrough)>,
@ -67,46 +66,31 @@ where
uniform_buffer_info_resources: Default::default(),
asset_resources: Default::default(),
_marker: PhantomData,
resource_query: <(Read<T>, Read<Renderable>)>::query(),
handle_query: Some(<(Read<Handle<T>>, Read<Renderable>)>::query()),
resource_query: <(Read<T>, Write<Renderable>)>::query(),
handle_query: Some(<(Read<Handle<T>>, Write<Renderable>)>::query()),
}
}
fn update_asset_uniforms(
&mut self,
renderer: &mut dyn Renderer,
world: &World,
world: &mut World,
resources: &Resources,
) {
let handle_query = self.handle_query.take().unwrap();
let mut asset_batchers = resources.get_mut::<AssetBatchers>().unwrap();
let mut entity_render_resource_assignments = resources
.get_mut::<EntityRenderResourceAssignments>()
.unwrap();
let mut render_resource_assignments_provider = resources
.get_mut::<RenderResourceAssignmentsProvider>()
.unwrap();
// TODO: only update handle values when Asset value has changed
if let Some(asset_storage) = resources.get::<AssetStorage<T>>() {
for (entity, (handle, renderable)) in handle_query.iter_entities(world) {
for (entity, (handle, mut renderable)) in handle_query.iter_entities_mut(world) {
if renderable.is_instanced {
asset_batchers.set_entity_handle(entity, *handle);
} else {
let render_resource_assignments = if let Some(assignments) =
entity_render_resource_assignments.get_mut(entity)
{
assignments
} else {
entity_render_resource_assignments
.set(entity, render_resource_assignments_provider.next());
entity_render_resource_assignments.get_mut(entity).unwrap()
};
if let Some(uniforms) = asset_storage.get(&handle) {
self.setup_uniform_resources(
uniforms,
renderer,
resources,
render_resource_assignments,
renderable.render_resource_assignments.as_mut().unwrap(),
true,
Some(*handle),
)
@ -127,8 +111,7 @@ where
dynamic_unforms: bool,
asset_handle: Option<Handle<T>>,
) {
let field_infos = uniforms.get_field_infos();
for uniform_info in UniformInfoIter::new(field_infos, uniforms.deref()) {
for uniform_info in UniformInfoIter::new(uniforms.deref()) {
match uniform_info.bind_type {
BindType::Uniform { .. } => {
if dynamic_unforms {
@ -273,11 +256,9 @@ where
fn setup_dynamic_uniform_buffers(
&mut self,
renderer: &mut dyn Renderer,
world: &World,
world: &mut World,
resources: &Resources,
) {
let entity_render_resource_assignments =
resources.get::<EntityRenderResourceAssignments>().unwrap();
// allocate uniform buffers
for (name, (resource, count, _entities)) in self.uniform_buffer_info_resources.iter_mut() {
let count = *count as u64;
@ -330,40 +311,40 @@ where
// TODO: check if index has changed. if it has, then entity should be updated
// TODO: only mem-map entities if their data has changed
// PERF: These hashmap inserts are pretty expensive (10 fps for 10000 entities)
for (entity, (_, renderable)) in self.resource_query.iter_entities(world) {
for (_, renderable) in self.resource_query.iter_mut(world) {
if renderable.is_instanced {
continue;
}
let id = renderable
.render_resource_assignments
.as_ref()
.unwrap()
.get_id();
// this unwrap is safe because the assignments were created in the calling function
let render_resource_assignments =
entity_render_resource_assignments.get(entity).unwrap();
if !entities.contains(&render_resource_assignments.get_id()) {
if !entities.contains(&id) {
continue;
}
dynamic_uniform_info
.offsets
.insert(render_resource_assignments.get_id(), offset as u32);
dynamic_uniform_info.offsets.insert(id, offset as u32);
offset += alignment;
}
for (entity, (_, renderable)) in
self.handle_query.as_ref().unwrap().iter_entities(world)
{
for (_, renderable) in self.handle_query.as_ref().unwrap().iter_mut(world) {
if renderable.is_instanced {
continue;
}
let render_resource_assignments =
entity_render_resource_assignments.get(entity).unwrap();
if !entities.contains(&render_resource_assignments.get_id()) {
continue;
}
dynamic_uniform_info
.offsets
.insert(render_resource_assignments.get_id(), offset as u32);
dynamic_uniform_info.offsets.insert(
renderable
.render_resource_assignments
.as_ref()
.unwrap()
.get_id(),
offset as u32,
);
offset += alignment;
}
@ -379,16 +360,18 @@ where
&mut |mapped| {
let alignment = BIND_BUFFER_ALIGNMENT as usize;
let mut offset = 0usize;
for (entity, (uniforms, renderable)) in
self.resource_query.iter_entities(world)
{
for (uniforms, renderable) in self.resource_query.iter_mut(world) {
if renderable.is_instanced {
continue;
}
let render_resource_assignments =
entity_render_resource_assignments.get(entity).unwrap();
if !entities.contains(&render_resource_assignments.get_id()) {
if !entities.contains(
&renderable
.render_resource_assignments
.as_ref()
.unwrap()
.get_id(),
) {
continue;
}
if let Some(uniform_bytes) = uniforms.get_uniform_bytes_ref(&name) {
@ -403,16 +386,20 @@ where
}
if let Some(asset_storage) = resources.get::<AssetStorage<T>>() {
for (entity, (handle, renderable)) in
self.handle_query.as_ref().unwrap().iter_entities(world)
for (handle, renderable) in
self.handle_query.as_ref().unwrap().iter_mut(world)
{
if renderable.is_instanced {
continue;
}
let render_resource_assignments =
entity_render_resource_assignments.get(entity).unwrap();
if !entities.contains(&render_resource_assignments.get_id()) {
if !entities.contains(
&renderable
.render_resource_assignments
.as_ref()
.unwrap()
.get_id(),
) {
continue;
}
@ -471,7 +458,7 @@ where
// (2) if we create new buffers, the old bind groups will be invalid
// reset all uniform buffer info counts
let query = <(Read<T>, Read<Renderable>)>::query();
let query = <(Read<T>, Write<Renderable>)>::query();
for (_name, (resource, count, _entities)) in self.uniform_buffer_info_resources.iter_mut() {
if let Some(ResourceInfo::Buffer(BufferInfo {
array_info: Some(array_info),
@ -486,30 +473,16 @@ where
self.update_asset_uniforms(renderer, world, resources);
let mut entity_render_resource_assignments = resources
.get_mut::<EntityRenderResourceAssignments>()
.unwrap();
let mut render_resource_assignments_provider = resources
.get_mut::<RenderResourceAssignmentsProvider>()
.unwrap();
for (entity, (uniforms, renderable)) in query.iter_entities(world) {
for (uniforms, mut renderable) in query.iter_mut(world) {
if renderable.is_instanced {
continue;
}
let render_resource_assignments =
if let Some(assignments) = entity_render_resource_assignments.get_mut(entity) {
assignments
} else {
entity_render_resource_assignments
.set(entity, render_resource_assignments_provider.next());
entity_render_resource_assignments.get_mut(entity).unwrap()
};
self.setup_uniform_resources(
&uniforms,
renderer,
resources,
render_resource_assignments,
renderable.render_resource_assignments.as_mut().unwrap(),
true,
None,
);
@ -521,7 +494,12 @@ where
for (uniforms, mut renderable) in <(Read<T>, Write<Renderable>)>::query().iter_mut(world) {
if let Some(shader_defs) = uniforms.get_shader_defs() {
for shader_def in shader_defs {
renderable.shader_defs.insert(shader_def);
renderable
.render_resource_assignments
.as_mut()
.unwrap()
.shader_defs
.insert(shader_def);
}
}
}
@ -533,7 +511,12 @@ where
let uniforms = asset_storage.get(&handle).unwrap();
if let Some(shader_defs) = uniforms.get_shader_defs() {
for shader_def in shader_defs {
renderable.shader_defs.insert(shader_def);
renderable
.render_resource_assignments
.as_mut()
.unwrap()
.shader_defs
.insert(shader_def);
}
}
}

View file

@ -1,5 +1,6 @@
use super::{
pipeline::PipelineDescriptor,
render_resource::{RenderResourceAssignments, RenderResourceAssignmentsId},
shader::{Shader, ShaderSource},
};
use crate::{
@ -12,8 +13,10 @@ use std::collections::{HashMap, HashSet};
pub struct Renderable {
pub is_visible: bool,
pub is_instanced: bool,
// TODO: make these hidden if possible
pub pipelines: Vec<Handle<PipelineDescriptor>>,
pub shader_defs: HashSet<String>,
pub render_resource_assignments: Option<RenderResourceAssignments>,
}
impl Renderable {
@ -32,12 +35,13 @@ impl Default for Renderable {
pipelines: vec![
Handle::new(0), // TODO: this could be better
],
shader_defs: HashSet::new(),
render_resource_assignments: None,
is_instanced: false,
}
}
}
// TODO: consider using (Typeid, fieldinfo.index) in place of string for hashes
pub struct CompiledShaderMap {
// TODO: need macro hash lookup
pub source_to_compiled: HashMap<Handle<Shader>, Vec<(HashSet<String>, Handle<Shader>)>>,
@ -52,10 +56,102 @@ impl CompiledShaderMap {
pipeline_to_macro_pipelines: HashMap::new(),
}
}
fn update_shader_assignments(
&mut self,
render_graph: &mut RenderGraph,
shader_pipeline_assignments: &mut ShaderPipelineAssignments,
pipeline_storage: &mut AssetStorage<PipelineDescriptor>,
shader_storage: &mut AssetStorage<Shader>,
pipelines: &[Handle<PipelineDescriptor>],
render_resource_assignments: &RenderResourceAssignments,
) {
for pipeline_handle in pipelines.iter() {
if let None = self.pipeline_to_macro_pipelines.get(pipeline_handle) {
self.pipeline_to_macro_pipelines
.insert(*pipeline_handle, Vec::new());
}
let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = self
.pipeline_to_macro_pipelines
.get_mut(pipeline_handle)
.unwrap()
.iter()
.find(|(shader_defs, _macroed_pipeline_handle)| {
*shader_defs == render_resource_assignments.shader_defs
}) {
*macroed_pipeline_handle
} else {
let pipeline_descriptor = pipeline_storage.get(pipeline_handle).unwrap();
let macroed_pipeline_handle = {
let mut macroed_vertex_handle = try_compiling_shader_with_macros(
self,
shader_storage,
&render_resource_assignments,
&pipeline_descriptor.shader_stages.vertex,
);
let mut macroed_fragment_handle = pipeline_descriptor
.shader_stages
.fragment
.as_ref()
.map(|fragment| {
try_compiling_shader_with_macros(
self,
shader_storage,
&render_resource_assignments,
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_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 = self
.pipeline_to_macro_pipelines
.get_mut(pipeline_handle)
.unwrap();
macro_pipelines.push((
render_resource_assignments.shader_defs.clone(),
macroed_pipeline_handle,
));
macroed_pipeline_handle
};
// TODO: this will break down if pipeline layout changes. fix this with "auto-layout"
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(render_resource_assignments.get_id());
}
}
}
pub struct ShaderPipelineAssignments {
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<Entity>>,
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<RenderResourceAssignmentsId>>,
}
impl ShaderPipelineAssignments {
@ -69,7 +165,7 @@ impl ShaderPipelineAssignments {
fn try_compiling_shader_with_macros(
compiled_shader_map: &mut CompiledShaderMap,
shader_storage: &mut AssetStorage<Shader>,
renderable: &Renderable,
assignments: &RenderResourceAssignments,
shader_handle: &Handle<Shader>,
) -> Option<Handle<Shader>> {
if let None = compiled_shader_map.source_to_compiled.get(shader_handle) {
@ -91,21 +187,22 @@ fn try_compiling_shader_with_macros(
if let Some((_shader_defs, compiled_shader)) = compiled_shaders
.iter()
.find(|(shader_defs, _shader)| *shader_defs == renderable.shader_defs)
.find(|(shader_defs, _shader)| *shader_defs == assignments.shader_defs)
{
Some(compiled_shader.clone())
} else {
let shader_def_vec = renderable
let shader_def_vec = assignments
.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));
compiled_shaders.push((assignments.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) {
// 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
@ -122,102 +219,29 @@ pub fn update_shader_assignments(world: &mut World, resources: &mut Resources) {
// reset assignments so they are updated every frame
shader_pipeline_assignments.assignments = HashMap::new();
for (entity, mut renderable) in <Write<Renderable>>::query().iter_entities_mut(world) {
// if instancing is enabled, set the def here
// TODO: only update when renderable is changed
for mut renderable in <Write<Renderable>>::query().iter_mut(world) {
// skip instanced entities. their batched RenderResourceAssignments will handle shader assignments
if renderable.is_instanced {
renderable.shader_defs.insert("INSTANCING".to_string());
continue;
}
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());
}
compiled_shader_map.update_shader_assignments(
&mut render_graph,
&mut shader_pipeline_assignments,
&mut pipeline_descriptor_storage,
&mut shader_storage,
&renderable.pipelines,
renderable.render_resource_assignments.as_ref().unwrap(),
);
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 "auto-layout"
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);
}
// reset shader_defs so they can be changed next frame
renderable
.render_resource_assignments
.as_mut()
.unwrap()
.shader_defs
.clear();
}
}
// 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();
}
}

View file

@ -11,7 +11,6 @@ use crate::{
use std::ops::Range;
pub trait Renderer {
fn initialize(&mut self, world: &mut World, resources: &mut Resources);
fn resize(&mut self, world: &mut World, resources: &mut Resources);
fn update(&mut self, world: &mut World, resources: &mut Resources);
fn create_buffer_with_data(&mut self, buffer_info: BufferInfo, data: &[u8]) -> RenderResource;

View file

@ -29,6 +29,7 @@ pub struct WgpuRenderer {
pub swap_chain_descriptor: wgpu::SwapChainDescriptor,
pub render_pipelines: HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>,
pub wgpu_resources: WgpuResources,
pub intialized: bool,
}
impl WgpuRenderer {
@ -61,12 +62,25 @@ impl WgpuRenderer {
queue,
surface: None,
encoder: None,
intialized: false,
swap_chain_descriptor,
wgpu_resources: WgpuResources::new(),
render_pipelines: HashMap::new(),
}
}
fn initialize(&mut self, world: &mut World, resources: &mut Resources) {
if self.intialized {
return;
}
self.create_surface(resources);
self.initialize_resource_providers(world, resources);
self.resize(world, resources);
self.intialized = true;
}
pub fn setup_vertex_buffer_descriptors(
render_graph: &RenderGraph,
vertex_spirv: &Shader,
@ -396,12 +410,6 @@ impl WgpuRenderer {
}
impl Renderer for WgpuRenderer {
fn initialize(&mut self, world: &mut World, resources: &mut Resources) {
self.create_surface(resources);
self.initialize_resource_providers(world, resources);
self.resize(world, resources);
}
fn resize(&mut self, world: &mut World, resources: &mut Resources) {
let window_size = {
let window = resources.get::<winit::window::Window>().unwrap();
@ -436,6 +444,7 @@ impl Renderer for WgpuRenderer {
}
fn update(&mut self, world: &mut World, resources: &mut Resources) {
self.initialize(world, resources);
// TODO: this self.encoder handoff is a bit gross, but its here to give resource providers access to buffer copies without
// exposing the wgpu renderer internals to ResourceProvider traits. if this can be made cleaner that would be pretty cool.
self.encoder = Some(

View file

@ -36,6 +36,7 @@ pub enum FieldBindType {
Texture,
}
// TODO: Remove this
pub struct UniformInfoIter<'a, T: AsUniforms> {
pub uniforms: &'a T,
pub index: usize,