mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
renderable owns render resource assignments. refactor shader assignment logic. renderers are responsible for intializing during updates.
This commit is contained in:
parent
55130bbe1c
commit
55745b0812
12 changed files with 260 additions and 227 deletions
|
@ -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>> {
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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(..)
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
if renderable.is_instanced {
|
||||
renderable.shader_defs.insert("INSTANCING".to_string());
|
||||
}
|
||||
|
||||
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 "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup entity shader_defs so next frame they can be refreshed
|
||||
// TODO: only update when renderable is changed
|
||||
for mut renderable in <Write<Renderable>>::query().iter_mut(world) {
|
||||
renderable.shader_defs = HashSet::new();
|
||||
// skip instanced entities. their batched RenderResourceAssignments will handle shader assignments
|
||||
if renderable.is_instanced {
|
||||
continue;
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
|
||||
// reset shader_defs so they can be changed next frame
|
||||
renderable
|
||||
.render_resource_assignments
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.shader_defs
|
||||
.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -36,6 +36,7 @@ pub enum FieldBindType {
|
|||
Texture,
|
||||
}
|
||||
|
||||
// TODO: Remove this
|
||||
pub struct UniformInfoIter<'a, T: AsUniforms> {
|
||||
pub uniforms: &'a T,
|
||||
pub index: usize,
|
||||
|
|
Loading…
Reference in a new issue