refactor pipeline compilation into PipelineCompiler

This commit is contained in:
Carter Anderson 2020-03-26 23:40:25 -07:00
parent d1db46ef54
commit 0073f4a58b
31 changed files with 619 additions and 550 deletions

View File

@ -12,6 +12,8 @@ Here is the current list of planned features. All items are sorted in approximat
* Add runtime type safety to uniform bindings (and maybe compile time)
* Inject layout set/bindings into shader source so they don't need to be defined in-shader. Specify set / binding indices in resource providers?
* Pull as much logic as possible from wgpu_renderer into a "render orchestrator" struct/trait
* Separate original/uncompiled/no_defs PipelineDescriptor from compiled PipelineDescriptor conceptually
* Try to make Renderer a resource + system
* Docs
* Add doc comments to code
* Add tutorials

View File

@ -6,7 +6,11 @@ use winit::{
use legion::prelude::*;
use crate::{app::AppBuilder, core::{Window, Time}, render::renderer::Renderer};
use crate::{
app::AppBuilder,
core::{Time, Window},
render::renderer::Renderer,
};
pub struct App {
pub universe: Universe,
@ -90,7 +94,12 @@ impl App {
window.height = size.height;
}
renderer.resize(&mut self.world, &mut self.resources, size.width, size.height);
renderer.resize(
&mut self.world,
&mut self.resources,
size.width,
size.height,
);
} else {
println!("no renderer {} {}", size.width, size.height);
}

View File

@ -13,10 +13,11 @@ use crate::{
};
use bevy_transform::{prelude::LocalToWorld, transform_system_bundle};
use pipeline::PipelineDescriptor;
use pipeline::{PipelineCompiler, PipelineDescriptor, ShaderPipelineAssignments};
use render_graph::{RenderGraph, RenderGraphBuilder};
use render_resource::{
build_entity_render_resource_assignments_system, AssetBatchers, EntityRenderResourceAssignments,
build_entity_render_resource_assignments_system, AssetBatchers,
EntityRenderResourceAssignments, RenderResourceAssignments,
};
use shader::Shader;
use std::collections::HashMap;
@ -165,7 +166,8 @@ impl AppBuilder {
resources.insert(AssetStorage::<StandardMaterial>::new());
resources.insert(AssetStorage::<PipelineDescriptor>::new());
resources.insert(ShaderPipelineAssignments::new());
resources.insert(CompiledShaderMap::new());
resources.insert(PipelineCompiler::new());
resources.insert(RenderResourceAssignments::default());
resources.insert(EntityRenderResourceAssignments::default());
self.batch_types2::<Mesh, StandardMaterial>();
self
@ -212,11 +214,11 @@ impl AppBuilder {
.add_resource_provider(LightResourceProvider::new(10))
.add_resource_provider(UiResourceProvider::new())
.add_resource_provider(MeshResourceProvider::new())
.add_resource_provider(UniformResourceProvider::<StandardMaterial>::new(true))
.add_resource_provider(UniformResourceProvider::<LocalToWorld>::new(true))
.add_resource_provider(UniformResourceProvider::<StandardMaterial>::new(false))
.add_resource_provider(UniformResourceProvider::<LocalToWorld>::new(false))
.add_forward_pass()
.add_forward_pipeline()
.add_ui_pipeline();
.add_forward_pipeline();
// .add_ui_pipeline();
})
}

View File

@ -1,4 +1,4 @@
pub struct Window {
pub width: u32,
pub height: u32,
}
}

View File

@ -4,10 +4,12 @@ use crate::{
render::{
draw_target::DrawTarget,
mesh::Mesh,
pipeline::PipelineDescriptor,
render_resource::{resource_name, EntityRenderResourceAssignments, ResourceInfo},
pipeline::{PipelineDescriptor, ShaderPipelineAssignments},
render_resource::{
resource_name, EntityRenderResourceAssignments, RenderResourceAssignments, ResourceInfo,
},
renderer::{RenderPass, Renderer},
Renderable, ShaderPipelineAssignments,
Renderable,
},
};
@ -27,6 +29,9 @@ impl DrawTarget for AssignedMeshesDrawTarget {
resources.get::<EntityRenderResourceAssignments>().unwrap();
let mut current_mesh_handle = None;
let mut current_mesh_index_len = 0;
let global_render_resource_assignments =
resources.get::<RenderResourceAssignments>().unwrap();
render_pass.set_render_resources(&global_render_resource_assignments);
let assigned_render_resource_assignments = shader_pipeline_assignments
.assignments
@ -87,6 +92,9 @@ impl DrawTarget for AssignedMeshesDrawTarget {
.get(&pipeline_handle);
let pipeline_storage = resources.get::<AssetStorage<PipelineDescriptor>>().unwrap();
let pipeline_descriptor = pipeline_storage.get(&pipeline_handle).unwrap();
let mut global_render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
renderer.setup_bind_groups(&mut global_render_resource_assignments, pipeline_descriptor);
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?

View File

@ -1,5 +1,5 @@
use crate::{
asset::{Asset, Handle},
asset::{Asset, AssetStorage, Handle},
legion::prelude::*,
math,
prelude::MeshType,
@ -7,7 +7,10 @@ use crate::{
draw_target::DrawTarget,
mesh::Mesh,
pipeline::PipelineDescriptor,
render_resource::{resource_name, BufferInfo, BufferUsage, RenderResource, ResourceInfo},
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceInfo,
},
renderer::{RenderPass, Renderer},
},
};
@ -30,12 +33,9 @@ impl DrawTarget for UiDrawTarget {
render_pass: &mut dyn RenderPass,
_pipeline_handle: Handle<PipelineDescriptor>,
) {
let render_resource_assignments = resources.get::<RenderResourceAssignments>().unwrap();
let ui_instances_buffer = {
let renderer = render_pass.get_renderer();
match renderer
.get_render_resources()
.get_named_resource(resource_name::buffer::UI_INSTANCES)
{
match render_resource_assignments.get(resource_name::buffer::UI_INSTANCES) {
Some(buffer) => buffer,
None => return,
}
@ -54,24 +54,25 @@ impl DrawTarget for UiDrawTarget {
}
};
// TODO: set global render resources
// render_pass.set_render_resources(None);
// render_pass.set_index_buffer(self.mesh_index_buffer.unwrap(), 0);
// render_pass.set_vertex_buffer(0, self.mesh_vertex_buffer.unwrap(), 0);
// render_pass.set_vertex_buffer(1, ui_instances_buffer, 0);
// render_pass.draw_indexed(
// 0..self.mesh_index_length as u32,
// 0,
// 0..(index_count.unwrap() as u32),
// );
let global_render_resource_assignments =
resources.get::<RenderResourceAssignments>().unwrap();
render_pass.set_render_resources(&global_render_resource_assignments);
render_pass.set_index_buffer(self.mesh_index_buffer.unwrap(), 0);
render_pass.set_vertex_buffer(0, self.mesh_vertex_buffer.unwrap(), 0);
render_pass.set_vertex_buffer(1, ui_instances_buffer, 0);
render_pass.draw_indexed(
0..self.mesh_index_length as u32,
0,
0..(index_count.unwrap() as u32),
);
}
fn setup(
&mut self,
_world: &mut World,
_resources: &Resources,
resources: &Resources,
renderer: &mut dyn Renderer,
_pipeline_handle: Handle<PipelineDescriptor>,
pipeline_handle: Handle<PipelineDescriptor>,
) {
// don't create meshes if they have already been created
if let Some(_) = self.mesh_vertex_buffer {
@ -99,6 +100,12 @@ impl DrawTarget for UiDrawTarget {
quad.indices.as_bytes(),
));
self.mesh_index_length = quad.indices.len();
let mut global_render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
let pipeline_storage = resources.get::<AssetStorage<PipelineDescriptor>>().unwrap();
let pipeline_descriptor = pipeline_storage.get(&pipeline_handle).unwrap();
renderer.setup_bind_groups(&mut global_render_resource_assignments, pipeline_descriptor);
}
fn get_name(&self) -> String {
resource_name::draw_target::UI.to_string()

View File

@ -1,6 +1,7 @@
mod bind_group;
mod binding;
mod pipeline;
mod pipeline_compiler;
mod pipeline_layout;
pub mod pipelines;
pub mod state_descriptors;
@ -10,6 +11,7 @@ mod vertex_format;
pub use bind_group::*;
pub use binding::*;
pub use pipeline::*;
pub use pipeline_compiler::*;
pub use pipeline_layout::*;
pub use vertex_buffer_descriptor::*;
pub use vertex_format::*;

View File

@ -33,7 +33,6 @@ pub struct PipelineDescriptor {
pub draw_targets: Vec<String>,
pub layout: PipelineLayoutType,
pub shader_stages: ShaderStages,
pub reflect_vertex_buffer_descriptors: bool,
pub rasterization_state: Option<RasterizationStateDescriptor>,
/// The primitive topology used to interpret vertices.
@ -48,9 +47,6 @@ pub struct PipelineDescriptor {
/// The format of any index buffers used with this pipeline.
pub index_format: IndexFormat,
/// The format of any vertex buffers used with this pipeline.
pub vertex_buffer_descriptors: Vec<VertexBufferDescriptor>,
/// The number of samples calculated per pixel (for MSAA).
pub sample_count: u32,
@ -74,7 +70,6 @@ impl PipelineDescriptor {
depth_stencil_state: None,
draw_targets: Vec::new(),
shader_stages: ShaderStages::new(vertex_shader),
vertex_buffer_descriptors: Vec::new(),
rasterization_state: Some(RasterizationStateDescriptor {
front_face: FrontFace::Ccw,
cull_mode: CullMode::Back,
@ -82,7 +77,6 @@ impl PipelineDescriptor {
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
}),
reflect_vertex_buffer_descriptors: true,
primitive_topology: PrimitiveTopology::TriangleList,
index_format: IndexFormat::Uint16,
sample_count: 1,
@ -170,12 +164,12 @@ impl<'a> PipelineBuilder<'a> {
}
pub fn add_bind_group(&mut self, bind_group: BindGroupDescriptor) -> &mut Self {
if let PipelineLayoutType::Reflected(_) = self.pipeline.as_ref().unwrap().layout {
self.pipeline.as_mut().unwrap().layout =
PipelineLayoutType::Manual(PipelineLayout::new());
let pipeline = self.pipeline.as_mut().unwrap();
if let PipelineLayoutType::Reflected(_) = pipeline.layout {
pipeline.layout = PipelineLayoutType::Manual(PipelineLayout::default());
}
if let PipelineLayoutType::Manual(ref mut layout) = self.pipeline.as_mut().unwrap().layout {
if let PipelineLayoutType::Manual(ref mut layout) = pipeline.layout {
layout.bind_groups.push(bind_group);
}
@ -187,10 +181,16 @@ impl<'a> PipelineBuilder<'a> {
vertex_buffer_descriptor: VertexBufferDescriptor,
) -> &mut Self {
let pipeline = self.pipeline.as_mut().unwrap();
pipeline.reflect_vertex_buffer_descriptors = false;
pipeline
.vertex_buffer_descriptors
.push(vertex_buffer_descriptor);
if let PipelineLayoutType::Reflected(_) = pipeline.layout {
pipeline.layout = PipelineLayoutType::Manual(PipelineLayout::default());
}
if let PipelineLayoutType::Manual(ref mut layout) = pipeline.layout {
layout
.vertex_buffer_descriptors
.push(vertex_buffer_descriptor);
}
self
}

View File

@ -0,0 +1,242 @@
use super::{PipelineDescriptor, PipelineLayout, PipelineLayoutType};
use crate::{
asset::{AssetStorage, Handle},
prelude::{Renderable, Resources, Shader, World},
render::{
render_graph::RenderGraph,
render_resource::{RenderResourceAssignments, RenderResourceAssignmentsId},
shader::ShaderSource,
},
};
use std::collections::{HashMap, HashSet};
use legion::prelude::*;
// TODO: consider using (Typeid, fieldinfo.index) in place of string for hashes
pub struct PipelineCompiler {
pub shader_source_to_compiled: HashMap<Handle<Shader>, Vec<(HashSet<String>, Handle<Shader>)>>,
pub pipeline_source_to_compiled:
HashMap<Handle<PipelineDescriptor>, Vec<(HashSet<String>, Handle<PipelineDescriptor>)>>,
}
impl PipelineCompiler {
pub fn new() -> Self {
PipelineCompiler {
shader_source_to_compiled: HashMap::new(),
pipeline_source_to_compiled: HashMap::new(),
}
}
fn reflect_layout(
shader_storage: &AssetStorage<Shader>,
render_graph: &RenderGraph,
pipeline_descriptor: &mut PipelineDescriptor,
) {
let vertex_spirv = shader_storage
.get(&pipeline_descriptor.shader_stages.vertex)
.unwrap();
let fragment_spirv = pipeline_descriptor
.shader_stages
.fragment
.as_ref()
.map(|handle| &*shader_storage.get(&handle).unwrap());
let mut layouts = vec![vertex_spirv.reflect_layout().unwrap()];
if let Some(ref fragment_spirv) = fragment_spirv {
layouts.push(fragment_spirv.reflect_layout().unwrap());
}
let mut layout = PipelineLayout::from_shader_layouts(&mut layouts);
layout.sync_vertex_buffer_descriptors_with_render_graph(render_graph);
for mut _bind_group in layout.bind_groups.iter_mut() {
// TODO: set dynamic here
}
pipeline_descriptor.layout = PipelineLayoutType::Reflected(Some(layout));
}
fn compile_shader(
&mut self,
shader_storage: &mut AssetStorage<Shader>,
shader_handle: &Handle<Shader>,
shader_defs: &HashSet<String>,
) -> Handle<Shader> {
let compiled_shaders = self
.shader_source_to_compiled
.entry(*shader_handle)
.or_insert_with(|| Vec::new());
let shader = shader_storage.get(shader_handle).unwrap();
// don't produce new shader if the input source is already spirv
if let ShaderSource::Spirv(_) = shader.source {
return *shader_handle;
}
if let Some((_compiled_shader_defs, compiled_shader)) = compiled_shaders
.iter()
.find(|(compiled_shader_defs, _compiled_shader)| *compiled_shader_defs == *shader_defs)
{
// if shader has already been compiled with current configuration, use existing shader
*compiled_shader
} else {
// if no shader exists with the current configuration, create new shader and compile
let shader_def_vec = shader_defs.iter().cloned().collect::<Vec<String>>();
let compiled_shader = shader.get_spirv_shader(Some(&shader_def_vec));
compiled_shaders.push((shader_defs.clone(), *shader_handle));
shader_storage.add(compiled_shader)
}
}
fn compile_pipeline(
&mut self,
render_graph: &RenderGraph,
shader_storage: &mut AssetStorage<Shader>,
pipeline_descriptor: &PipelineDescriptor,
shader_defs: &HashSet<String>,
) -> PipelineDescriptor {
let mut compiled_pipeline_descriptor = pipeline_descriptor.clone();
compiled_pipeline_descriptor.shader_stages.vertex = self.compile_shader(
shader_storage,
&pipeline_descriptor.shader_stages.vertex,
&shader_defs,
);
compiled_pipeline_descriptor.shader_stages.fragment = pipeline_descriptor
.shader_stages
.fragment
.as_ref()
.map(|fragment| self.compile_shader(shader_storage, fragment, &shader_defs));
Self::reflect_layout(
shader_storage,
render_graph,
&mut compiled_pipeline_descriptor,
);
compiled_pipeline_descriptor
}
fn update_shader_assignments(
&mut self,
render_graph: &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_source_to_compiled.get(pipeline_handle) {
self.pipeline_source_to_compiled
.insert(*pipeline_handle, Vec::new());
}
let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = self
.pipeline_source_to_compiled
.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 compiled_pipeline = self.compile_pipeline(
render_graph,
shader_storage,
pipeline_descriptor,
&render_resource_assignments.shader_defs,
);
let compiled_pipeline_handle = pipeline_storage.add(compiled_pipeline);
let macro_pipelines = self
.pipeline_source_to_compiled
.get_mut(pipeline_handle)
.unwrap();
macro_pipelines.push((
render_resource_assignments.shader_defs.clone(),
compiled_pipeline_handle,
));
compiled_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.id);
}
}
pub fn iter_compiled_pipelines(
&self,
pipeline_handle: Handle<PipelineDescriptor>,
) -> Option<impl Iterator<Item = &Handle<PipelineDescriptor>>> {
if let Some(compiled_pipelines) = self.pipeline_source_to_compiled.get(&pipeline_handle) {
Some(compiled_pipelines.iter().map(|(_, handle)| handle))
} else {
None
}
}
}
pub struct ShaderPipelineAssignments {
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<RenderResourceAssignmentsId>>,
}
impl ShaderPipelineAssignments {
pub fn new() -> Self {
ShaderPipelineAssignments {
assignments: HashMap::new(),
}
}
}
// TODO: make this a system
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
{
let mut shader_pipeline_assignments =
resources.get_mut::<ShaderPipelineAssignments>().unwrap();
let mut pipeline_compiler = resources.get_mut::<PipelineCompiler>().unwrap();
let mut shader_storage = resources.get_mut::<AssetStorage<Shader>>().unwrap();
let mut render_graph = resources.get_mut::<RenderGraph>().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();
// 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 {
continue;
}
pipeline_compiler.update_shader_assignments(
&mut render_graph,
&mut shader_pipeline_assignments,
&mut pipeline_descriptor_storage,
&mut shader_storage,
&renderable.pipelines,
&renderable.render_resource_assignments,
);
// reset shader_defs so they can be changed next frame
renderable.render_resource_assignments.shader_defs.clear();
}
}
}

View File

@ -1,22 +1,18 @@
use super::BindGroupDescriptor;
use crate::render::shader::ShaderLayout;
use super::{BindGroupDescriptor, VertexBufferDescriptor};
use crate::render::{render_graph::RenderGraph, shader::ShaderLayout};
use std::{collections::HashMap, hash::Hash};
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct PipelineLayout {
pub bind_groups: Vec<BindGroupDescriptor>,
pub vertex_buffer_descriptors: Vec<VertexBufferDescriptor>,
}
impl PipelineLayout {
pub fn new() -> Self {
PipelineLayout {
bind_groups: Vec::new(),
}
}
pub fn from_shader_layouts(shader_layouts: &mut [ShaderLayout]) -> Self {
let mut bind_groups = HashMap::<u32, BindGroupDescriptor>::new();
for shader_layout in shader_layouts {
let mut vertex_buffer_descriptors = Vec::new();
for shader_layout in shader_layouts.iter_mut() {
for shader_bind_group in shader_layout.bind_groups.iter_mut() {
match bind_groups.get_mut(&shader_bind_group.index) {
Some(bind_group) => {
@ -40,15 +36,38 @@ impl PipelineLayout {
}
}
}
for vertex_buffer_descriptor in shader_layouts[0].vertex_buffer_descriptors.iter() {
vertex_buffer_descriptors.push(vertex_buffer_descriptor.clone());
}
let mut bind_groups_result = bind_groups
.drain()
.map(|(_, value)| value)
.collect::<Vec<BindGroupDescriptor>>();
// NOTE: for some reason bind groups need to be sorted by index. this is likely an issue with bevy and not with wgpu
// TODO: try removing this
bind_groups_result.sort_by(|a, b| a.index.partial_cmp(&b.index).unwrap());
PipelineLayout {
bind_groups: bind_groups_result,
vertex_buffer_descriptors,
}
}
pub fn sync_vertex_buffer_descriptors_with_render_graph(&mut self, render_graph: &RenderGraph) {
for vertex_buffer_descriptor in self.vertex_buffer_descriptors.iter_mut() {
if let Some(graph_descriptor) =
render_graph.get_vertex_buffer_descriptor(&vertex_buffer_descriptor.name)
{
vertex_buffer_descriptor.sync_with_descriptor(graph_descriptor);
} else {
panic!(
"Encountered unsupported Vertex Buffer: {}",
vertex_buffer_descriptor.name
);
}
}
}
}

View File

@ -23,6 +23,7 @@ layout(set = 0, binding = 1) uniform Lights {
Light SceneLights[MAX_LIGHTS];
};
// TODO: this should be binding = 0 right?
layout(set = 2, binding = 1) uniform StandardMaterial_albedo {
vec4 Albedo;
};

View File

@ -56,7 +56,7 @@ impl<'a> ForwardPipelineBuilder for RenderGraphBuilder<'a> {
write_mask: ColorWrite::ALL,
})
.add_draw_target(resource_name::draw_target::ASSIGNED_MESHES);
// .add_draw_target(resource_name::draw_target::ASSIGNED_BATCHES);
// .add_draw_target(resource_name::draw_target::ASSIGNED_BATCHES);
})
}
}

View File

@ -1,9 +1,7 @@
use crate::{
asset::{HandleId, HandleUntyped},
};
use super::{AssetBatcher, Batch};
use crate::asset::{HandleId, HandleUntyped};
use legion::prelude::Entity;
use std::{any::TypeId, collections::HashMap, hash::Hash};
use super::{AssetBatcher, Batch};
// TODO: if/when const generics land, revisit this design in favor of generic array lengths
@ -141,4 +139,4 @@ impl AssetBatcher for AssetSetBatcher2 {
fn get_batches_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut Batch> + 'a> {
Box::new(self.set_batches.values_mut())
}
}
}

View File

@ -1,6 +1,6 @@
use crate::{asset::HandleUntyped, render::render_resource::RenderResourceAssignments};
use legion::prelude::Entity;
use std::collections::{HashSet, HashMap};
use std::collections::{HashMap, HashSet};
#[derive(PartialEq, Eq, Debug, Default)]
pub struct Batch {
@ -18,7 +18,8 @@ impl Batch {
pub fn add_instanced_entity(&mut self, entity: Entity) {
if let None = self.instanced_entity_indices.get(&entity) {
self.instanced_entity_indices.insert(entity, self.current_instanced_entity_index);
self.instanced_entity_indices
.insert(entity, self.current_instanced_entity_index);
self.current_instanced_entity_index += 1;
}
}

View File

@ -4,4 +4,4 @@ mod batch;
pub use asset_batcher::*;
pub use asset_batcher2::*;
pub use batch::*;
pub use batch::*;

View File

@ -25,7 +25,7 @@ pub fn build_entity_render_resource_assignments_system() -> Box<dyn Schedulable>
.with_query(<Write<Renderable>>::query().filter(changed::<Renderable>()))
.build(|_, world, entity_assignments, query| {
for (entity, renderable) in query.iter_entities_mut(world) {
entity_assignments.set(renderable.render_resource_assignments.id, entity);
entity_assignments.set(renderable.render_resource_assignments.id, entity);
}
})
}

View File

@ -3,7 +3,6 @@ use crate::{
render::{mesh::Mesh, texture::Texture},
};
use std::collections::HashMap;
use super::RenderResourceAssignments;
#[derive(Copy, Clone, Hash, Debug, Eq, PartialEq)]
pub struct RenderResource(pub u64);
@ -12,7 +11,6 @@ pub struct RenderResource(pub u64);
// the overlap could cause accidents.
#[derive(Default)]
pub struct RenderResources {
pub global_assignments: RenderResourceAssignments,
pub texture_to_resource: HashMap<Handle<Texture>, RenderResource>,
pub texture_to_sampler_resource: HashMap<Handle<Texture>, RenderResource>,
pub mesh_to_vertices_resource: HashMap<Handle<Mesh>, RenderResource>,
@ -21,14 +19,6 @@ pub struct RenderResources {
}
impl RenderResources {
pub fn set_named_resource(&mut self, name: &str, resource: RenderResource) {
self.global_assignments.set(name, resource);
}
pub fn get_named_resource(&self, name: &str) -> Option<RenderResource> {
self.global_assignments.get(name).map(|(r, _i)| r)
}
pub fn set_texture_resource(&mut self, texture: Handle<Texture>, resource: RenderResource) {
self.texture_to_resource.insert(texture, resource);
}

View File

@ -20,7 +20,11 @@ pub struct RenderResourceAssignments {
}
impl RenderResourceAssignments {
pub fn get(&self, name: &str) -> Option<(RenderResource, Option<u32>)> {
pub fn get(&self, name: &str) -> Option<RenderResource> {
self.render_resources.get(name).map(|(r, _i)| *r)
}
pub fn get_indexed(&self, name: &str) -> Option<(RenderResource, Option<u32>)> {
self.render_resources.get(name).cloned()
}
@ -84,7 +88,7 @@ impl RenderResourceAssignments {
} else {
self.bind_group_resource_sets
.get(&bind_group_descriptor.id)
.map(|(set_id, indices)| *set_id)
.map(|(set_id, _indices)| *set_id)
}
}
@ -102,7 +106,7 @@ impl RenderResourceAssignments {
let mut hasher = DefaultHasher::new();
let mut indices = Vec::new();
for binding_descriptor in bind_group_descriptor.bindings.iter() {
if let Some((render_resource, index)) = self.get(&binding_descriptor.name) {
if let Some((render_resource, index)) = self.get_indexed(&binding_descriptor.name) {
render_resource.hash(&mut hasher);
if let Some(index) = index {
indices.push(index);
@ -112,7 +116,14 @@ impl RenderResourceAssignments {
}
}
Some((RenderResourceSetId(hasher.finish()), if indices.is_empty() { None } else { Some(indices) }))
Some((
RenderResourceSetId(hasher.finish()),
if indices.is_empty() {
None
} else {
Some(indices)
},
))
}
}
@ -188,7 +199,8 @@ mod tests {
assert_ne!(different_set_id, None);
assert_ne!(different_set_id, set_id);
let equal_set_id = equal_assignments.get_or_update_render_resource_set_id(&bind_group_descriptor);
let equal_set_id =
equal_assignments.get_or_update_render_resource_set_id(&bind_group_descriptor);
assert_ne!(equal_set_id, None);
assert_eq!(equal_set_id, set_id);

View File

@ -1,5 +1,8 @@
use crate::render::{
render_resource::{resource_name, BufferInfo, BufferUsage, RenderResource, ResourceProvider},
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceProvider,
},
renderer::Renderer,
ActiveCamera2d, Camera,
};
@ -17,7 +20,7 @@ impl ResourceProvider for Camera2dResourceProvider {
&mut self,
renderer: &mut dyn Renderer,
_world: &mut World,
_resources: &Resources,
resources: &Resources,
) {
let buffer = renderer.create_buffer(BufferInfo {
size: std::mem::size_of::<[[f32; 4]; 4]>(),
@ -25,9 +28,9 @@ impl ResourceProvider for Camera2dResourceProvider {
..Default::default()
});
renderer
.get_render_resources_mut()
.set_named_resource(resource_name::uniform::CAMERA2D, buffer);
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
render_resource_assignments.set(resource_name::uniform::CAMERA2D, buffer);
self.camera_buffer = Some(buffer);
}

View File

@ -1,5 +1,8 @@
use crate::render::{
render_resource::{resource_name, BufferInfo, BufferUsage, RenderResource, ResourceProvider},
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceProvider,
},
renderer::Renderer,
ActiveCamera, Camera,
};
@ -18,7 +21,7 @@ impl ResourceProvider for CameraResourceProvider {
&mut self,
renderer: &mut dyn Renderer,
_world: &mut World,
_resources: &Resources,
resources: &Resources,
) {
let buffer = renderer.create_buffer(BufferInfo {
size: std::mem::size_of::<[[f32; 4]; 4]>(),
@ -26,9 +29,9 @@ impl ResourceProvider for CameraResourceProvider {
..Default::default()
});
renderer
.get_render_resources_mut()
.set_named_resource(resource_name::uniform::CAMERA, buffer);
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
render_resource_assignments.set(resource_name::uniform::CAMERA, buffer);
self.camera_buffer = Some(buffer);
}

View File

@ -1,7 +1,11 @@
use crate::{
core::Window,
prelude::World,
render::{render_resource::ResourceProvider, renderer::Renderer, texture::TextureDescriptor},
render::{
render_resource::{RenderResourceAssignments, ResourceProvider},
renderer::Renderer,
texture::TextureDescriptor,
},
};
use legion::prelude::Resources;
@ -23,17 +27,14 @@ impl FrameTextureResourceProvider {
self.descriptor.size.width = window.width;
self.descriptor.size.height = window.height;
if let Some(old_resource) = renderer
.get_render_resources()
.get_named_resource(&self.name)
{
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
if let Some(old_resource) = render_resource_assignments.get(&self.name) {
renderer.remove_texture(old_resource);
}
let texture_resource = renderer.create_texture(&self.descriptor, None);
renderer
.get_render_resources_mut()
.set_named_resource(&self.name, texture_resource);
render_resource_assignments.set(&self.name, texture_resource);
}
}

View File

@ -1,5 +1,8 @@
use crate::render::{
render_resource::{resource_name, BufferInfo, BufferUsage, RenderResource, ResourceProvider},
render_resource::{
resource_name, BufferInfo, BufferUsage, RenderResource, RenderResourceAssignments,
ResourceProvider,
},
renderer::Renderer,
Light, LightRaw,
};
@ -38,7 +41,7 @@ impl ResourceProvider for LightResourceProvider {
&mut self,
renderer: &mut dyn Renderer,
_world: &mut World,
_resources: &Resources,
resources: &Resources,
) {
let light_uniform_size =
std::mem::size_of::<LightCount>() + self.max_lights * std::mem::size_of::<LightRaw>();
@ -48,9 +51,9 @@ impl ResourceProvider for LightResourceProvider {
buffer_usage: BufferUsage::UNIFORM | BufferUsage::COPY_SRC | BufferUsage::COPY_DST,
..Default::default()
});
renderer
.get_render_resources_mut()
.set_named_resource(resource_name::uniform::LIGHTS, buffer);
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
render_resource_assignments.set(resource_name::uniform::LIGHTS, buffer);
self.light_buffer = Some(buffer);
}

View File

@ -89,12 +89,12 @@ impl ResourceProvider for MeshResourceProvider {
&mut self,
_renderer: &mut dyn Renderer,
_world: &mut World,
resources: &Resources,
_resources: &Resources,
) {
let mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
let mut asset_batchers = resources.get_mut::<AssetBatchers>().unwrap();
for batch in asset_batchers.get_handle_batches::<Mesh>() {
// batch.render_resource_assignments.
}
// TODO: assign vertex buffers
// let mesh_storage = resources.get_mut::<AssetStorage<Mesh>>().unwrap();
// let mut asset_batchers = resources.get_mut::<AssetBatchers>().unwrap();
// for batch in asset_batchers.get_handle_batches::<Mesh>() {
// }
}
}

View File

@ -6,7 +6,7 @@ use crate::{
render_graph::RenderGraph,
render_resource::{
resource_name, BufferArrayInfo, BufferInfo, BufferUsage, RenderResource,
ResourceProvider,
RenderResourceAssignments, ResourceProvider,
},
renderer::Renderer,
shader::AsUniforms,
@ -41,7 +41,7 @@ impl UiResourceProvider {
}
}
pub fn update(&mut self, renderer: &mut dyn Renderer, world: &World) {
pub fn update(&mut self, renderer: &mut dyn Renderer, world: &World, resources: &Resources) {
let node_query = <Read<Node>>::query().filter(!component::<Parent>());
let mut data = Vec::new();
@ -99,9 +99,9 @@ impl UiResourceProvider {
data.as_bytes(),
);
renderer
.get_render_resources_mut()
.set_named_resource(resource_name::buffer::UI_INSTANCES, buffer);
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
render_resource_assignments.set(resource_name::buffer::UI_INSTANCES, buffer);
self.instance_buffer = Some(buffer);
}
}
@ -118,7 +118,7 @@ impl ResourceProvider for UiResourceProvider {
.set_vertex_buffer_descriptor(Rect::get_vertex_buffer_descriptor().cloned().unwrap());
}
fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World, _resources: &Resources) {
self.update(renderer, world);
fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World, resources: &Resources) {
self.update(renderer, world, resources);
}
}

View File

@ -4,8 +4,7 @@ use crate::{
render_graph::RenderGraph,
render_resource::{
AssetBatchers, BufferArrayInfo, BufferInfo, BufferUsage, RenderResource,
RenderResourceAssignments, ResourceInfo,
ResourceProvider,
RenderResourceAssignments, ResourceInfo, ResourceProvider,
},
renderer::Renderer,
shader::{AsUniforms, FieldBindType},
@ -122,10 +121,7 @@ where
self.increment_uniform_counts(&uniforms);
}
Self::update_shader_defs(
&uniforms,
&mut renderable.render_resource_assignments,
);
Self::update_shader_defs(&uniforms, &mut renderable.render_resource_assignments);
}
self.query = Some(query);
@ -158,10 +154,7 @@ where
let uniforms = assets
.get(&handle)
.expect("Handle points to a non-existent resource");
Self::update_shader_defs(
uniforms,
&mut renderable.render_resource_assignments,
);
Self::update_shader_defs(uniforms, &mut renderable.render_resource_assignments);
self.increment_uniform_counts(&uniforms);
}
@ -264,9 +257,13 @@ where
..
})) = renderer.get_resource_info_mut(buffer)
{
let index = array_info
.get_or_assign_index(render_resource_assignments.id);
render_resource_assignments.set_indexed(&field_info.uniform_name, buffer, index as u32);
let index =
array_info.get_or_assign_index(render_resource_assignments.id);
render_resource_assignments.set_indexed(
&field_info.uniform_name,
buffer,
(index * array_info.item_size) as u32,
);
(buffer, index * uniform_buffer_status.aligned_size)
} else {
panic!("Expected a dynamic uniform buffer");
@ -275,7 +272,7 @@ where
let resource = match render_resource_assignments
.get(field_info.uniform_name)
{
Some((render_resource, _index)) => render_resource,
Some(render_resource) => render_resource,
None => {
let resource = renderer.create_buffer(BufferInfo {
size,
@ -470,12 +467,9 @@ where
fn setup_buffer_arrays(&mut self, renderer: &mut dyn Renderer) {
for buffer_array_status in self.uniform_buffer_status.iter_mut() {
if let Some((name, buffer_array_status)) = buffer_array_status {
if let Some((_name, buffer_array_status)) = buffer_array_status {
if self.use_dynamic_uniforms {
Self::setup_buffer_array(buffer_array_status, renderer, true);
renderer
.get_render_resources_mut()
.set_named_resource(name, buffer_array_status.buffer.unwrap());
}
buffer_array_status.queued_buffer_writes =
@ -623,7 +617,7 @@ where
let mut staging_buffer: [u8; 0] = [];
self.setup_uniforms_resources(world, resources, renderer, &mut staging_buffer);
self.setup_handles_resources(world, resources, renderer, &mut staging_buffer);
// self.setup_batched_resources(world, resources, renderer, &mut staging_buffer);
// self.setup_batched_resources(world, resources, renderer, &mut staging_buffer);
} else {
let staging_buffer = renderer.create_buffer_mapped(
BufferInfo {

View File

@ -1,14 +1,5 @@
use super::{
pipeline::PipelineDescriptor,
render_resource::{RenderResourceAssignments, RenderResourceAssignmentsId},
shader::{Shader, ShaderSource},
};
use crate::{
asset::{AssetStorage, Handle},
render::{render_graph::RenderGraph, render_resource::resource_name},
};
use legion::prelude::*;
use std::collections::{HashMap, HashSet};
use super::render_resource::RenderResourceAssignments;
use crate::{asset::Handle, prelude::PipelineDescriptor};
pub struct Renderable {
pub is_visible: bool,
@ -40,206 +31,3 @@ impl Default for Renderable {
}
}
}
// 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>)>>,
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(),
}
}
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.id);
}
}
}
pub struct ShaderPipelineAssignments {
pub assignments: HashMap<Handle<PipelineDescriptor>, Vec<RenderResourceAssignmentsId>>,
}
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>,
assignments: &RenderResourceAssignments,
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 spirv
if let ShaderSource::Spirv(_) = shader.source {
return None;
}
if let Some((_shader_defs, compiled_shader)) = compiled_shaders
.iter()
.find(|(shader_defs, _shader)| *shader_defs == assignments.shader_defs)
{
Some(compiled_shader.clone())
} else {
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((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
{
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 render_graph = resources.get_mut::<RenderGraph>().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();
// 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 {
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,
);
// reset shader_defs so they can be changed next frame
renderable
.render_resource_assignments
.shader_defs
.clear();
}
}
}

View File

@ -55,5 +55,8 @@ pub trait RenderPass {
fn set_index_buffer(&mut self, resource: RenderResource, offset: u64);
fn set_vertex_buffer(&mut self, start_slot: u32, resource: RenderResource, offset: u64);
fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);
fn set_render_resources(&mut self, render_resource_assignments: &RenderResourceAssignments) -> Option<Range<u32>>;
fn set_render_resources(
&mut self,
render_resource_assignments: &RenderResourceAssignments,
) -> Option<Range<u32>>;
}

View File

@ -1,7 +1,7 @@
use super::{WgpuRenderer, WgpuResources};
use crate::render::{
pipeline::{BindType, PipelineDescriptor},
render_resource::{BufferInfo, RenderResource, RenderResourceAssignments, ResourceInfo},
pipeline::PipelineDescriptor,
render_resource::{RenderResource, RenderResourceAssignments},
renderer::{RenderPass, Renderer},
};
use std::ops::Range;
@ -53,11 +53,18 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
{
// TODO: check to see if bind group is already set
let empty = &[];
let dynamic_uniform_indices = if let Some(dynamic_uniform_indices) = dynamic_uniform_indices {
dynamic_uniform_indices.as_slice()
} else {
empty
};
let dynamic_uniform_indices =
if let Some(dynamic_uniform_indices) = dynamic_uniform_indices {
dynamic_uniform_indices.as_slice()
} else {
empty
};
// TODO: remove this
// if dynamic_uniform_indices.len() == 0 && bind_group.index > 0 {
// continue;
// }
self.render_pass.set_bind_group(
bind_group.index,
&wgpu_bind_group,

View File

@ -8,7 +8,7 @@ use crate::{
PassDescriptor, RenderPassColorAttachmentDescriptor,
RenderPassDepthStencilAttachmentDescriptor,
},
pipeline::{BindType, PipelineDescriptor, PipelineLayout, PipelineLayoutType},
pipeline::{update_shader_assignments, PipelineCompiler, PipelineDescriptor},
render_graph::RenderGraph,
render_resource::{
resource_name, BufferInfo, RenderResource, RenderResourceAssignments, RenderResources,
@ -17,7 +17,6 @@ use crate::{
renderer::Renderer,
shader::Shader,
texture::{SamplerDescriptor, TextureDescriptor},
update_shader_assignments,
},
};
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
@ -65,7 +64,7 @@ impl WgpuRenderer {
encoder: None,
intialized: false,
swap_chain_descriptor,
wgpu_resources: WgpuResources::new(),
wgpu_resources: WgpuResources::default(),
render_pipelines: HashMap::new(),
}
}
@ -88,102 +87,14 @@ impl WgpuRenderer {
self.intialized = true;
}
pub fn setup_vertex_buffer_descriptors(
render_graph: &RenderGraph,
vertex_spirv: &Shader,
pipeline_descriptor: &PipelineDescriptor,
) -> Vec<OwnedWgpuVertexBufferDescriptor> {
let mut reflected_vertex_layout = if pipeline_descriptor.reflect_vertex_buffer_descriptors {
Some(vertex_spirv.reflect_layout().unwrap())
} else {
None
};
let vertex_buffer_descriptors = if let Some(ref mut layout) = reflected_vertex_layout {
for vertex_buffer_descriptor in layout.vertex_buffer_descriptors.iter_mut() {
if let Some(graph_descriptor) =
render_graph.get_vertex_buffer_descriptor(&vertex_buffer_descriptor.name)
{
vertex_buffer_descriptor.sync_with_descriptor(graph_descriptor);
} else {
panic!(
"Encountered unsupported Vertex Buffer: {}",
vertex_buffer_descriptor.name
);
}
}
&layout.vertex_buffer_descriptors
} else {
&pipeline_descriptor.vertex_buffer_descriptors
};
vertex_buffer_descriptors
.iter()
.map(|v| v.into())
.collect::<Vec<OwnedWgpuVertexBufferDescriptor>>()
}
pub fn create_render_pipeline(
wgpu_resources: &mut WgpuResources,
pipeline_descriptor: &mut PipelineDescriptor,
shader_storage: &AssetStorage<Shader>,
device: &wgpu::Device,
render_graph: &RenderGraph,
vertex_shader: &Shader,
fragment_shader: Option<&Shader>,
) -> wgpu::RenderPipeline {
let vertex_spirv = vertex_shader.get_spirv_shader(None);
let fragment_spirv = fragment_shader.map(|f| f.get_spirv_shader(None));
let vertex_shader_module = Self::create_shader_module(device, &vertex_spirv, None);
let fragment_shader_module = match fragment_shader {
Some(fragment_spirv) => Some(Self::create_shader_module(device, fragment_spirv, None)),
None => None,
};
if let PipelineLayoutType::Reflected(None) = pipeline_descriptor.layout {
let mut layouts = vec![vertex_spirv.reflect_layout().unwrap()];
if let Some(ref fragment_spirv) = fragment_spirv {
layouts.push(fragment_spirv.reflect_layout().unwrap());
}
let mut layout = PipelineLayout::from_shader_layouts(&mut layouts);
// set each uniform binding to dynamic if there is a matching dynamic uniform buffer info
for mut bind_group in layout.bind_groups.iter_mut() {
bind_group.bindings = bind_group
.bindings
.iter()
.cloned()
.map(|mut binding| {
if let BindType::Uniform {
ref mut dynamic, ..
} = binding.bind_type
{
if let Some(resource) = wgpu_resources
.render_resources
.get_named_resource(&binding.name)
{
if let Some(ResourceInfo::Buffer(buffer_info)) =
wgpu_resources.resource_info.get(&resource)
{
*dynamic = buffer_info.is_dynamic;
}
}
}
binding
})
.collect();
}
pipeline_descriptor.layout = PipelineLayoutType::Reflected(Some(layout));
}
let layout = pipeline_descriptor.get_layout_mut().unwrap();
// setup new bind group layouts
for bind_group in layout.bind_groups.iter_mut() {
let layout = pipeline_descriptor.get_layout().unwrap();
for bind_group in layout.bind_groups.iter() {
if let None = wgpu_resources.bind_group_layouts.get(&bind_group.id) {
let bind_group_layout_binding = bind_group
.bindings
@ -194,18 +105,18 @@ impl WgpuRenderer {
ty: (&binding.bind_type).into(),
})
.collect::<Vec<wgpu::BindGroupLayoutBinding>>();
let bind_group_layout =
let wgpu_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: bind_group_layout_binding.as_slice(),
});
wgpu_resources
.bind_group_layouts
.insert(bind_group.id, bind_group_layout);
.insert(bind_group.id, wgpu_bind_group_layout);
}
}
// collect bind group layout references
// setup and collect bind group layouts
let bind_group_layouts = layout
.bind_groups
.iter()
@ -221,8 +132,11 @@ impl WgpuRenderer {
bind_group_layouts: bind_group_layouts.as_slice(),
});
let owned_vertex_buffer_descriptors =
Self::setup_vertex_buffer_descriptors(render_graph, &vertex_spirv, pipeline_descriptor);
let owned_vertex_buffer_descriptors = layout
.vertex_buffer_descriptors
.iter()
.map(|v| v.into())
.collect::<Vec<OwnedWgpuVertexBufferDescriptor>>();
let color_states = pipeline_descriptor
.color_states
@ -230,13 +144,42 @@ impl WgpuRenderer {
.map(|c| c.into())
.collect::<Vec<wgpu::ColorStateDescriptor>>();
if let None = wgpu_resources
.shader_modules
.get(&pipeline_descriptor.shader_stages.vertex)
{
wgpu_resources.create_shader_module(
device,
pipeline_descriptor.shader_stages.vertex,
shader_storage,
);
}
if let Some(fragment_handle) = pipeline_descriptor.shader_stages.fragment {
if let None = wgpu_resources.shader_modules.get(&fragment_handle) {
wgpu_resources.create_shader_module(device, fragment_handle, shader_storage);
}
};
let vertex_shader_module = wgpu_resources
.shader_modules
.get(&pipeline_descriptor.shader_stages.vertex)
.unwrap();
let fragment_shader_module = match pipeline_descriptor.shader_stages.fragment {
Some(fragment_handle) => {
Some(wgpu_resources.shader_modules.get(&fragment_handle).unwrap())
}
None => None,
};
let mut render_pipeline_descriptor = wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout,
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vertex_shader_module,
entry_point: "main",
},
fragment_stage: match fragment_shader {
fragment_stage: match pipeline_descriptor.shader_stages.fragment {
Some(_) => Some(wgpu::ProgrammableStageDescriptor {
entry_point: "main",
module: fragment_shader_module.as_ref().unwrap(),
@ -269,6 +212,7 @@ impl WgpuRenderer {
pub fn create_render_pass<'a>(
wgpu_resources: &'a WgpuResources,
pass_descriptor: &PassDescriptor,
global_render_resource_assignments: &RenderResourceAssignments,
encoder: &'a mut wgpu::CommandEncoder,
frame: &'a wgpu::SwapChainOutput,
) -> wgpu::RenderPass<'a> {
@ -276,25 +220,37 @@ impl WgpuRenderer {
color_attachments: &pass_descriptor
.color_attachments
.iter()
.map(|c| Self::create_wgpu_color_attachment_descriptor(wgpu_resources, c, frame))
.map(|c| {
Self::create_wgpu_color_attachment_descriptor(
wgpu_resources,
global_render_resource_assignments,
c,
frame,
)
})
.collect::<Vec<wgpu::RenderPassColorAttachmentDescriptor>>(),
depth_stencil_attachment: pass_descriptor.depth_stencil_attachment.as_ref().map(|d| {
Self::create_wgpu_depth_stencil_attachment_descriptor(wgpu_resources, d, frame)
Self::create_wgpu_depth_stencil_attachment_descriptor(
wgpu_resources,
global_render_resource_assignments,
d,
frame,
)
}),
})
}
fn create_wgpu_color_attachment_descriptor<'a>(
wgpu_resources: &'a WgpuResources,
global_render_resource_assignments: &RenderResourceAssignments,
color_attachment_descriptor: &RenderPassColorAttachmentDescriptor,
frame: &'a wgpu::SwapChainOutput,
) -> wgpu::RenderPassColorAttachmentDescriptor<'a> {
let attachment = match color_attachment_descriptor.attachment.as_str() {
resource_name::texture::SWAP_CHAIN => &frame.view,
_ => {
match wgpu_resources
.render_resources
.get_named_resource(&color_attachment_descriptor.attachment)
match global_render_resource_assignments
.get(&color_attachment_descriptor.attachment)
{
Some(resource) => wgpu_resources.textures.get(&resource).unwrap(),
None => panic!(
@ -308,10 +264,7 @@ impl WgpuRenderer {
let resolve_target = match color_attachment_descriptor.resolve_target {
Some(ref target) => match target.as_str() {
resource_name::texture::SWAP_CHAIN => Some(&frame.view),
_ => match wgpu_resources
.render_resources
.get_named_resource(target.as_str())
{
_ => match global_render_resource_assignments.get(target.as_str()) {
Some(resource) => Some(wgpu_resources.textures.get(&resource).unwrap()),
None => panic!(
"Color attachment {} does not exist",
@ -333,15 +286,15 @@ impl WgpuRenderer {
fn create_wgpu_depth_stencil_attachment_descriptor<'a>(
wgpu_resources: &'a WgpuResources,
global_render_resource_assignments: &RenderResourceAssignments,
depth_stencil_attachment_descriptor: &RenderPassDepthStencilAttachmentDescriptor,
frame: &'a wgpu::SwapChainOutput,
) -> wgpu::RenderPassDepthStencilAttachmentDescriptor<'a> {
let attachment = match depth_stencil_attachment_descriptor.attachment.as_str() {
resource_name::texture::SWAP_CHAIN => &frame.view,
_ => {
match wgpu_resources
.render_resources
.get_named_resource(&depth_stencil_attachment_descriptor.attachment)
match global_render_resource_assignments
.get(&depth_stencil_attachment_descriptor.attachment)
{
Some(ref resource) => wgpu_resources.textures.get(&resource).unwrap(),
None => panic!(
@ -363,14 +316,6 @@ impl WgpuRenderer {
}
}
pub fn create_shader_module(
device: &wgpu::Device,
shader: &Shader,
macros: Option<&[String]>,
) -> wgpu::ShaderModule {
device.create_shader_module(&shader.get_spirv(macros))
}
pub fn initialize_resource_providers(&mut self, world: &mut World, resources: &mut Resources) {
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
self.encoder = Some(
@ -400,11 +345,11 @@ impl WgpuRenderer {
pub fn create_queued_textures(&mut self, resources: &mut Resources) {
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
let mut render_resource_assignments =
resources.get_mut::<RenderResourceAssignments>().unwrap();
for (name, texture_descriptor) in render_graph.queued_textures.drain(..) {
let resource = self.create_texture(&texture_descriptor, None);
self.wgpu_resources
.render_resources
.set_named_resource(&name, resource);
render_resource_assignments.set(&name, resource);
}
}
@ -471,33 +416,31 @@ impl Renderer for WgpuRenderer {
let shader_storage = resources.get::<AssetStorage<Shader>>().unwrap();
let render_graph = resources.get::<RenderGraph>().unwrap();
let mut render_graph_mut = resources.get_mut::<RenderGraph>().unwrap();
let global_render_resource_assignments =
resources.get::<RenderResourceAssignments>().unwrap();
let pipeline_compiler = resources.get::<PipelineCompiler>().unwrap();
for pipeline_descriptor_handle in render_graph.pipeline_descriptors.iter() {
let pipeline_descriptor = pipeline_storage
.get_mut(pipeline_descriptor_handle)
.unwrap();
// create pipelines
if !self
.render_pipelines
.contains_key(pipeline_descriptor_handle)
if let Some(compiled_pipelines_iter) =
pipeline_compiler.iter_compiled_pipelines(*pipeline_descriptor_handle)
{
let vertex_shader = shader_storage
.get(&pipeline_descriptor.shader_stages.vertex)
.unwrap();
let fragment_shader = pipeline_descriptor
.shader_stages
.fragment
.as_ref()
.map(|handle| &*shader_storage.get(&handle).unwrap());
let render_pipeline = WgpuRenderer::create_render_pipeline(
&mut self.wgpu_resources,
pipeline_descriptor,
&self.device.borrow(),
&render_graph,
vertex_shader,
fragment_shader,
);
self.render_pipelines
.insert(*pipeline_descriptor_handle, render_pipeline);
for compiled_pipeline_handle in compiled_pipelines_iter {
// create pipelines
// TODO: merge this into "setup draw targets" loop
if !self.render_pipelines.contains_key(compiled_pipeline_handle) {
let compiled_pipeline_descriptor =
pipeline_storage.get_mut(compiled_pipeline_handle).unwrap();
let render_pipeline = WgpuRenderer::create_render_pipeline(
&mut self.wgpu_resources,
compiled_pipeline_descriptor,
&shader_storage,
&self.device.borrow(),
);
self.render_pipelines
.insert(*compiled_pipeline_handle, render_pipeline);
}
}
}
}
@ -505,13 +448,25 @@ impl Renderer for WgpuRenderer {
for (pass_name, _pass_descriptor) in render_graph.pass_descriptors.iter() {
if let Some(pass_pipelines) = render_graph.pass_pipelines.get(pass_name) {
for pass_pipeline in pass_pipelines.iter() {
let pipeline_descriptor = pipeline_storage.get(pass_pipeline).unwrap();
for draw_target_name in pipeline_descriptor.draw_targets.iter() {
let draw_target = render_graph_mut
.draw_targets
.get_mut(draw_target_name)
.unwrap();
draw_target.setup(world, resources, self, *pass_pipeline);
if let Some(compiled_pipelines_iter) =
pipeline_compiler.iter_compiled_pipelines(*pass_pipeline)
{
for compiled_pipeline_handle in compiled_pipelines_iter {
let pipeline_descriptor =
pipeline_storage.get(compiled_pipeline_handle).unwrap();
for draw_target_name in pipeline_descriptor.draw_targets.iter() {
let draw_target = render_graph_mut
.draw_targets
.get_mut(draw_target_name)
.unwrap();
draw_target.setup(
world,
resources,
self,
*compiled_pipeline_handle,
);
}
}
}
}
}
@ -522,25 +477,40 @@ impl Renderer for WgpuRenderer {
let mut render_pass = Self::create_render_pass(
&self.wgpu_resources,
pass_descriptor,
&global_render_resource_assignments,
&mut encoder,
&frame,
);
if let Some(pass_pipelines) = render_graph.pass_pipelines.get(pass_name) {
for pass_pipeline in pass_pipelines.iter() {
let pipeline_descriptor = pipeline_storage.get(pass_pipeline).unwrap();
let render_pipeline = self.render_pipelines.get(pass_pipeline).unwrap();
render_pass.set_pipeline(render_pipeline);
if let Some(compiled_pipelines_iter) =
pipeline_compiler.iter_compiled_pipelines(*pass_pipeline)
{
for compiled_pipeline_handle in compiled_pipelines_iter {
let pipeline_descriptor =
pipeline_storage.get(compiled_pipeline_handle).unwrap();
let render_pipeline =
self.render_pipelines.get(compiled_pipeline_handle).unwrap();
render_pass.set_pipeline(render_pipeline);
let mut wgpu_render_pass = WgpuRenderPass {
render_pass: &mut render_pass,
pipeline_descriptor,
wgpu_resources: &self.wgpu_resources,
renderer: &self,
};
let mut wgpu_render_pass = WgpuRenderPass {
render_pass: &mut render_pass,
pipeline_descriptor,
wgpu_resources: &self.wgpu_resources,
renderer: &self,
};
for draw_target_name in pipeline_descriptor.draw_targets.iter() {
let draw_target = render_graph.draw_targets.get(draw_target_name).unwrap();
draw_target.draw(world, resources, &mut wgpu_render_pass, *pass_pipeline);
for draw_target_name in pipeline_descriptor.draw_targets.iter() {
let draw_target =
render_graph.draw_targets.get(draw_target_name).unwrap();
draw_target.draw(
world,
resources,
&mut wgpu_render_pass,
*compiled_pipeline_handle,
);
}
}
}
}
}

View File

@ -1,12 +1,16 @@
use super::WgpuRenderer;
use crate::render::{
pipeline::{BindGroupDescriptor, BindGroupDescriptorId, BindType},
render_resource::{
BufferInfo, RenderResource, RenderResourceAssignments, RenderResourceSetId,
RenderResources, ResourceInfo,
use crate::{
asset::{AssetStorage, Handle},
prelude::Shader,
render::{
pipeline::{BindGroupDescriptor, BindGroupDescriptorId, BindType},
render_resource::{
BufferInfo, RenderResource, RenderResourceAssignments, RenderResourceSetId,
RenderResources, ResourceInfo,
},
renderer::Renderer,
texture::{SamplerDescriptor, TextureDescriptor},
},
renderer::Renderer,
texture::{SamplerDescriptor, TextureDescriptor},
};
use std::collections::HashMap;
@ -15,29 +19,19 @@ pub struct WgpuBindGroupInfo {
pub bind_groups: HashMap<RenderResourceSetId, wgpu::BindGroup>,
}
#[derive(Default)]
pub struct WgpuResources {
pub render_resources: RenderResources,
pub buffers: HashMap<RenderResource, wgpu::Buffer>,
pub textures: HashMap<RenderResource, wgpu::TextureView>,
pub samplers: HashMap<RenderResource, wgpu::Sampler>,
pub resource_info: HashMap<RenderResource, ResourceInfo>,
pub shader_modules: HashMap<Handle<Shader>, wgpu::ShaderModule>,
pub bind_groups: HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>,
pub bind_group_layouts: HashMap<BindGroupDescriptorId, wgpu::BindGroupLayout>,
}
impl WgpuResources {
pub fn new() -> Self {
WgpuResources {
buffers: HashMap::new(),
textures: HashMap::new(),
samplers: HashMap::new(),
resource_info: HashMap::new(),
bind_groups: HashMap::new(),
bind_group_layouts: HashMap::new(),
render_resources: RenderResources::default(),
}
}
pub fn add_resource_info(&mut self, resource: RenderResource, resource_info: ResourceInfo) {
self.resource_info.insert(resource, resource_info);
}
@ -67,7 +61,7 @@ impl WgpuResources {
.bindings
.iter()
.map(|binding| {
if let Some((resource, index)) = render_resource_assignments.get(&binding.name) {
if let Some(resource) = render_resource_assignments.get(&binding.name) {
let resource_info = self.resource_info.get(&resource).unwrap();
wgpu::Binding {
binding: binding.index,
@ -121,9 +115,7 @@ impl WgpuResources {
};
let bind_group = device.create_bind_group(&wgpu_bind_group_descriptor);
// TODO: storing a large number entity bind groups might actually be really bad. make sure this is ok
let mut bind_group_info = self
let bind_group_info = self
.bind_groups
.entry(bind_group_descriptor.id)
.or_insert_with(|| WgpuBindGroupInfo::default());
@ -212,6 +204,18 @@ impl WgpuResources {
encoder.copy_buffer_to_buffer(source, source_offset, destination, destination_offset, size);
}
pub fn create_shader_module(
&mut self,
device: &wgpu::Device,
shader_handle: Handle<Shader>,
shader_storage: &AssetStorage<Shader>,
) {
self.shader_modules.entry(shader_handle).or_insert_with(|| {
let shader = shader_storage.get(&shader_handle).unwrap();
device.create_shader_module(&shader.get_spirv(None))
});
}
pub fn create_sampler(
&mut self,
device: &wgpu::Device,

View File

@ -1,7 +1,7 @@
use crate::render::{
pipeline::{
BindGroupDescriptor, BindType, BindingDescriptor, InputStepMode, UniformProperty, UniformPropertyType,
VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
BindGroupDescriptor, BindType, BindingDescriptor, InputStepMode, UniformProperty,
UniformPropertyType, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
},
texture::TextureViewDimension,
};