cache bind group hashes

This commit is contained in:
Carter Anderson 2020-01-28 01:53:28 -08:00
parent 599d30d861
commit cd1fb92a7a
4 changed files with 102 additions and 38 deletions

View file

@ -1,8 +1,10 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::render_graph_2::{StandardMaterial, ShaderUniforms, uniform_selector}; use bevy::render::render_graph_2::{StandardMaterial, ShaderUniforms, uniform_selector};
use std::collections::VecDeque;
use rand::{rngs::StdRng, Rng, SeedableRng};
fn main() { fn main() {
AppBuilder::new().add_defaults().add_system(build_move_system()).setup_world(setup).run(); AppBuilder::new().add_defaults().add_system(build_move_system()).add_system(build_print_status_system()).setup_world(setup).run();
} }
fn build_move_system() -> Box<dyn Schedulable> { fn build_move_system() -> Box<dyn Schedulable> {
@ -17,6 +19,35 @@ fn build_move_system() -> Box<dyn Schedulable> {
}) })
} }
fn build_print_status_system() -> Box<dyn Schedulable> {
let mut elapsed = 0.0;
let mut frame_time_total = 0.0;
let mut frame_time_count = 0;
let frame_time_max = 10;
let mut frame_time_values = VecDeque::new();
SystemBuilder::new("PrintStatus")
.read_resource::<Time>()
.build(move |_, _world, time, _queries| {
elapsed += time.delta_seconds;
frame_time_values.push_front(time.delta_seconds);
frame_time_total += time.delta_seconds;
frame_time_count += 1;
if frame_time_count > frame_time_max {
frame_time_count = frame_time_max;
frame_time_total -= frame_time_values.pop_back().unwrap();
}
if elapsed > 1.0 {
if frame_time_count > 0 && frame_time_total > 0.0 {
println!(
"fps: {}",
1.0 / (frame_time_total / frame_time_count as f32)
)
}
elapsed = 0.0;
}
})
}
fn setup(world: &mut World) { fn setup(world: &mut World) {
let cube = Mesh::load(MeshType::Cube); let cube = Mesh::load(MeshType::Cube);
let plane = Mesh::load(MeshType::Plane { size: 10.0 }); let plane = Mesh::load(MeshType::Plane { size: 10.0 });
@ -26,7 +57,7 @@ fn setup(world: &mut World) {
(mesh_storage.add(cube), mesh_storage.add(plane)) (mesh_storage.add(cube), mesh_storage.add(plane))
}; };
world.build() let mut builder = world.build()
// plane // plane
.add_archetype(NewMeshEntity { .add_archetype(NewMeshEntity {
mesh: plane_handle.clone(), mesh: plane_handle.clone(),
@ -102,6 +133,26 @@ fn setup(world: &mut World) {
Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, 0.0),
Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0),
)), )),
});
let mut rng = StdRng::from_entropy();
for _ in 0..10000 {
builder = builder.add_archetype(NewMeshEntity {
mesh: cube_handle.clone(),
material: StandardMaterial {
albedo: math::vec4(0.0, 1.0, 0.0, 1.0),
},
shader_uniforms: ShaderUniforms {
uniform_selectors: vec![
uniform_selector::<StandardMaterial>,
uniform_selector::<LocalToWorld>,
],
},
local_to_world: LocalToWorld::identity(),
translation: Translation::new(rng.gen_range(-50.0, 50.0), rng.gen_range(-50.0, 50.0), 0.0),
}) })
.build(); }
builder.build();
} }

View file

@ -1,3 +1,7 @@
use std::{
collections::{hash_map::DefaultHasher},
hash::{Hash, Hasher},
};
pub struct PipelineLayout { pub struct PipelineLayout {
pub bind_groups: Vec<BindGroup>, pub bind_groups: Vec<BindGroup>,
} }
@ -12,7 +16,27 @@ impl PipelineLayout {
#[derive(Hash)] #[derive(Hash)]
pub struct BindGroup { pub struct BindGroup {
pub bindings: Vec<Binding> pub bindings: Vec<Binding>,
hash: Option<u64>,
}
impl BindGroup {
pub fn new(bindings: Vec<Binding>) -> Self {
BindGroup {
bindings,
hash: None,
}
}
pub fn get_hash(&self) -> u64 {
self.hash.unwrap()
}
pub fn update_hash(&mut self) {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
self.hash = Some(hasher.finish());
}
} }
#[derive(Hash)] #[derive(Hash)]

View file

@ -24,8 +24,8 @@ impl ForwardPipelineBuilder for RenderGraphBuilder {
include_str!("forward.frag"), include_str!("forward.frag"),
ShaderStage::Fragment, ShaderStage::Fragment,
)) ))
.add_bind_group(BindGroup { .add_bind_group(BindGroup::new(
bindings: vec![ vec![
Binding { Binding {
name: "Camera".to_string(), name: "Camera".to_string(),
bind_type: BindType::Uniform { bind_type: BindType::Uniform {
@ -39,9 +39,9 @@ impl ForwardPipelineBuilder for RenderGraphBuilder {
} }
}, },
] ]
}) ))
.add_bind_group(BindGroup { .add_bind_group(BindGroup::new(
bindings: vec![ vec![
Binding { Binding {
name: "Object".to_string(), name: "Object".to_string(),
bind_type: BindType::Uniform { bind_type: BindType::Uniform {
@ -67,7 +67,7 @@ impl ForwardPipelineBuilder for RenderGraphBuilder {
} }
}, },
] ]
}) ))
.with_rasterization_state(wgpu::RasterizationStateDescriptor { .with_rasterization_state(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back, cull_mode: wgpu::CullMode::Back,

View file

@ -7,11 +7,7 @@ use crate::{
TextureDimension, TextureDimension,
}, },
}; };
use std::{ use std::{collections::HashMap, ops::Deref};
collections::{hash_map::DefaultHasher, HashMap},
hash::{Hash, Hasher},
ops::Deref,
};
pub struct DynamicUniformBufferInfo { pub struct DynamicUniformBufferInfo {
pub indices: HashMap<usize, Entity>, pub indices: HashMap<usize, Entity>,
@ -76,7 +72,7 @@ impl WgpuRenderer {
} }
pub fn create_render_pipeline( pub fn create_render_pipeline(
pipeline_descriptor: &PipelineDescriptor, pipeline_descriptor: &mut PipelineDescriptor,
bind_group_layouts: &mut HashMap<u64, wgpu::BindGroupLayout>, bind_group_layouts: &mut HashMap<u64, wgpu::BindGroupLayout>,
device: &wgpu::Device, device: &wgpu::Device,
) -> wgpu::RenderPipeline { ) -> wgpu::RenderPipeline {
@ -90,10 +86,9 @@ impl WgpuRenderer {
}; };
// setup new bind group layouts // setup new bind group layouts
for bind_group in pipeline_descriptor.pipeline_layout.bind_groups.iter() { for bind_group in pipeline_descriptor.pipeline_layout.bind_groups.iter_mut() {
let mut hasher = DefaultHasher::new(); bind_group.update_hash();
bind_group.hash(&mut hasher); let bind_group_id = bind_group.get_hash();
let bind_group_id = hasher.finish();
if let None = bind_group_layouts.get(&bind_group_id) { if let None = bind_group_layouts.get(&bind_group_id) {
let bind_group_layout_binding = bind_group let bind_group_layout_binding = bind_group
.bindings .bindings
@ -120,10 +115,7 @@ impl WgpuRenderer {
.bind_groups .bind_groups
.iter() .iter()
.map(|bind_group| { .map(|bind_group| {
let mut hasher = DefaultHasher::new(); let bind_group_id = bind_group.get_hash();
bind_group.hash(&mut hasher);
let bind_group_id = hasher.finish();
bind_group_layouts.get(&bind_group_id).unwrap() bind_group_layouts.get(&bind_group_id).unwrap()
}) })
.collect::<Vec<&wgpu::BindGroupLayout>>(); .collect::<Vec<&wgpu::BindGroupLayout>>();
@ -132,7 +124,7 @@ impl WgpuRenderer {
bind_group_layouts: bind_group_layouts.as_slice(), bind_group_layouts: bind_group_layouts.as_slice(),
}); });
let render_pipeline_descriptor = wgpu::RenderPipelineDescriptor { let mut render_pipeline_descriptor = wgpu::RenderPipelineDescriptor {
layout: &pipeline_layout, layout: &pipeline_layout,
vertex_stage: wgpu::ProgrammableStageDescriptor { vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vertex_shader_module, module: &vertex_shader_module,
@ -160,7 +152,7 @@ impl WgpuRenderer {
alpha_to_coverage_enabled: pipeline_descriptor.alpha_to_coverage_enabled, alpha_to_coverage_enabled: pipeline_descriptor.alpha_to_coverage_enabled,
}; };
device.create_render_pipeline(&render_pipeline_descriptor) device.create_render_pipeline(&mut render_pipeline_descriptor)
} }
pub fn create_render_pass<'a>( pub fn create_render_pass<'a>(
@ -242,10 +234,7 @@ impl WgpuRenderer {
// TODO: consider moving this to a resource provider // TODO: consider moving this to a resource provider
fn setup_bind_group(&mut self, bind_group: &BindGroup) -> u64 { fn setup_bind_group(&mut self, bind_group: &BindGroup) -> u64 {
// TODO: cache hash result in bind_group? let bind_group_id = bind_group.get_hash();
let mut hasher = DefaultHasher::new();
bind_group.hash(&mut hasher);
let bind_group_id = hasher.finish();
if let None = self.bind_groups.get(&bind_group_id) { if let None = self.bind_groups.get(&bind_group_id) {
let mut unset_uniforms = Vec::new(); let mut unset_uniforms = Vec::new();
@ -368,10 +357,10 @@ impl WgpuRenderer {
continue; continue;
} }
if info.count >= info.capacity && info.capacity != 0 { if info.count >= info.capacity && info.capacity != 0 {
panic!("resizing dynamic uniform buffers isn't supported yet. we still need to support updating bind groups"); panic!("resizing dynamic uniform buffers isn't supported yet. we still need to support updating bind groups");
} }
// allocate enough space for twice as many entities as there are currently; // allocate enough space for twice as many entities as there are currently;
info.capacity = info.count * 2; info.capacity = info.count * 2;
let size = wgpu::BIND_BUFFER_ALIGNMENT * info.capacity; let size = wgpu::BIND_BUFFER_ALIGNMENT * info.capacity;
@ -406,7 +395,10 @@ impl WgpuRenderer {
let alignment = wgpu::BIND_BUFFER_ALIGNMENT as usize; let alignment = wgpu::BIND_BUFFER_ALIGNMENT as usize;
let mut offset = 0usize; let mut offset = 0usize;
for (i, (entity, shader_uniforms)) in <Read<ShaderUniforms>>::query().iter_entities(world).enumerate() { for (i, (entity, shader_uniforms)) in <Read<ShaderUniforms>>::query()
.iter_entities(world)
.enumerate()
{
// TODO: check if index has changed. if it has, then entity should be updated // TODO: check if index has changed. if it has, then entity should be updated
// TODO: only mem-map entities if their data has changed // TODO: only mem-map entities if their data has changed
info.offsets.insert(entity, offset as u64); info.offsets.insert(entity, offset as u64);
@ -477,7 +469,7 @@ impl Renderer for WgpuRenderer {
self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder); self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder);
// setup, pipelines, bind groups, and resources // setup, pipelines, bind groups, and resources
for (pipeline_name, pipeline_descriptor) in render_graph.pipeline_descriptors.iter() { for (pipeline_name, pipeline_descriptor) in render_graph.pipeline_descriptors.iter_mut() {
// create pipelines // create pipelines
if let None = self.render_pipelines.get(pipeline_name) { if let None = self.render_pipelines.get(pipeline_name) {
let render_pipeline = WgpuRenderer::create_render_pipeline( let render_pipeline = WgpuRenderer::create_render_pipeline(
@ -613,10 +605,7 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
.iter() .iter()
.enumerate() .enumerate()
{ {
// TODO: cache hash result in bind_group? let bind_group_id = bind_group.get_hash();
let mut hasher = DefaultHasher::new();
bind_group.hash(&mut hasher);
let bind_group_id = hasher.finish();
let bind_group_info = self.renderer.bind_groups.get(&bind_group_id).unwrap(); let bind_group_info = self.renderer.bind_groups.get(&bind_group_id).unwrap();
let mut dynamic_uniform_indices = Vec::new(); let mut dynamic_uniform_indices = Vec::new();