Renderer Optimization Round 1 (#958)
* only update global transforms when they (or their ancestors) have changed * only update render resource nodes when they have changed (quality check plz) * only update entity mesh specialization when mesh (or mesh component) has changed * only update sprite size when changed * remove stale bind groups * fix setting size of loading sprites * store unmatched render resource binding results * reduce state changes * cargo fmt + clippy * remove cached "NoMatch" results when new bindings are added to RenderResourceBindings * inline current_entity in world_builder * try creating bind groups even when they havent changed * render_resources_node: update all entities when resized * fmt
This commit is contained in:
parent
3cee95e59a
commit
b5ffab7135
@ -62,4 +62,17 @@ impl<'a> WorldBuilder<'a> {
|
|||||||
self.current_entity = Some(self.world.spawn(components));
|
self.current_entity = Some(self.world.spawn(components));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn current_entity(&self) -> Option<Entity> {
|
||||||
|
self.current_entity
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self {
|
||||||
|
let current_entity = self
|
||||||
|
.current_entity
|
||||||
|
.expect("The 'current entity' is not set. You should spawn an entity first.");
|
||||||
|
f(current_entity);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,13 @@ use crate::{
|
|||||||
use bevy_app::prelude::{EventReader, Events};
|
use bevy_app::prelude::{EventReader, Events};
|
||||||
use bevy_asset::{AssetEvent, Assets, Handle};
|
use bevy_asset::{AssetEvent, Assets, Handle};
|
||||||
use bevy_core::AsBytes;
|
use bevy_core::AsBytes;
|
||||||
use bevy_ecs::{Local, Query, Res};
|
use bevy_ecs::{Changed, Entity, Local, Mut, Query, QuerySet, Res, With};
|
||||||
use bevy_math::*;
|
use bevy_math::*;
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::pipeline::{InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor};
|
use crate::pipeline::{InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::{HashMap, HashSet};
|
||||||
|
|
||||||
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
|
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
|
||||||
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
|
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
|
||||||
@ -320,9 +320,16 @@ fn remove_current_mesh_resources(
|
|||||||
remove_resource_save(render_resource_context, handle, INDEX_BUFFER_ASSET_INDEX);
|
remove_resource_save(render_resource_context, handle, INDEX_BUFFER_ASSET_INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct MeshEntities {
|
||||||
|
entities: HashSet<Entity>,
|
||||||
|
waiting: HashSet<Entity>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MeshResourceProviderState {
|
pub struct MeshResourceProviderState {
|
||||||
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
|
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
|
||||||
|
mesh_entities: HashMap<Handle<Mesh>, MeshEntities>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mesh_resource_provider_system(
|
pub fn mesh_resource_provider_system(
|
||||||
@ -330,7 +337,10 @@ pub fn mesh_resource_provider_system(
|
|||||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||||
meshes: Res<Assets<Mesh>>,
|
meshes: Res<Assets<Mesh>>,
|
||||||
mesh_events: Res<Events<AssetEvent<Mesh>>>,
|
mesh_events: Res<Events<AssetEvent<Mesh>>>,
|
||||||
mut query: Query<(&Handle<Mesh>, &mut RenderPipelines)>,
|
mut queries: QuerySet<(
|
||||||
|
Query<&mut RenderPipelines, With<Handle<Mesh>>>,
|
||||||
|
Query<(Entity, &Handle<Mesh>, &mut RenderPipelines), Changed<Handle<Mesh>>>,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
let mut changed_meshes = bevy_utils::HashSet::<Handle<Mesh>>::default();
|
let mut changed_meshes = bevy_utils::HashSet::<Handle<Mesh>>::default();
|
||||||
let render_resource_context = &**render_resource_context;
|
let render_resource_context = &**render_resource_context;
|
||||||
@ -383,12 +393,45 @@ pub fn mesh_resource_provider_system(
|
|||||||
)),
|
)),
|
||||||
VERTEX_ATTRIBUTE_BUFFER_ID,
|
VERTEX_ATTRIBUTE_BUFFER_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) {
|
||||||
|
for entity in mesh_entities.waiting.drain() {
|
||||||
|
if let Ok(render_pipelines) = queries.q0_mut().get_mut(entity) {
|
||||||
|
mesh_entities.entities.insert(entity);
|
||||||
|
update_entity_mesh(
|
||||||
|
render_resource_context,
|
||||||
|
mesh,
|
||||||
|
changed_mesh_handle,
|
||||||
|
render_pipelines,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handover buffers to pipeline
|
// handover buffers to pipeline
|
||||||
for (handle, mut render_pipelines) in query.iter_mut() {
|
for (entity, handle, render_pipelines) in queries.q1_mut().iter_mut() {
|
||||||
|
let mesh_entities = state
|
||||||
|
.mesh_entities
|
||||||
|
.entry(handle.clone_weak())
|
||||||
|
.or_insert_with(MeshEntities::default);
|
||||||
if let Some(mesh) = meshes.get(handle) {
|
if let Some(mesh) = meshes.get(handle) {
|
||||||
|
mesh_entities.entities.insert(entity);
|
||||||
|
mesh_entities.waiting.remove(&entity);
|
||||||
|
update_entity_mesh(render_resource_context, mesh, handle, render_pipelines);
|
||||||
|
} else {
|
||||||
|
mesh_entities.waiting.insert(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_entity_mesh(
|
||||||
|
render_resource_context: &dyn RenderResourceContext,
|
||||||
|
mesh: &Mesh,
|
||||||
|
handle: &Handle<Mesh>,
|
||||||
|
mut render_pipelines: Mut<RenderPipelines>,
|
||||||
|
) {
|
||||||
for render_pipeline in render_pipelines.pipelines.iter_mut() {
|
for render_pipeline in render_pipelines.pipelines.iter_mut() {
|
||||||
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
|
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
|
||||||
// TODO: don't allocate a new vertex buffer descriptor for every entity
|
// TODO: don't allocate a new vertex buffer descriptor for every entity
|
||||||
@ -413,9 +456,6 @@ pub fn mesh_resource_provider_system(
|
|||||||
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
|
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
|
||||||
{
|
{
|
||||||
// set index buffer into binding
|
// set index buffer into binding
|
||||||
render_pipelines.bindings.vertex_attribute_buffer =
|
render_pipelines.bindings.vertex_attribute_buffer = Some(vertex_attribute_buffer_resource);
|
||||||
Some(vertex_attribute_buffer_resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,9 @@ where
|
|||||||
for render_command in draw.render_commands.iter() {
|
for render_command in draw.render_commands.iter() {
|
||||||
match render_command {
|
match render_command {
|
||||||
RenderCommand::SetPipeline { pipeline } => {
|
RenderCommand::SetPipeline { pipeline } => {
|
||||||
// TODO: Filter pipelines
|
if draw_state.is_pipeline_set(pipeline.clone_weak()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
render_pass.set_pipeline(pipeline);
|
render_pass.set_pipeline(pipeline);
|
||||||
let descriptor = pipelines.get(pipeline).unwrap();
|
let descriptor = pipelines.get(pipeline).unwrap();
|
||||||
draw_state.set_pipeline(pipeline, descriptor);
|
draw_state.set_pipeline(pipeline, descriptor);
|
||||||
@ -290,18 +292,27 @@ where
|
|||||||
offset,
|
offset,
|
||||||
slot,
|
slot,
|
||||||
} => {
|
} => {
|
||||||
|
if draw_state.is_vertex_buffer_set(*slot, *buffer, *offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
render_pass.set_vertex_buffer(*slot, *buffer, *offset);
|
render_pass.set_vertex_buffer(*slot, *buffer, *offset);
|
||||||
draw_state.set_vertex_buffer(*slot, *buffer);
|
draw_state.set_vertex_buffer(*slot, *buffer, *offset);
|
||||||
}
|
}
|
||||||
RenderCommand::SetIndexBuffer { buffer, offset } => {
|
RenderCommand::SetIndexBuffer { buffer, offset } => {
|
||||||
|
if draw_state.is_index_buffer_set(*buffer, *offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
render_pass.set_index_buffer(*buffer, *offset);
|
render_pass.set_index_buffer(*buffer, *offset);
|
||||||
draw_state.set_index_buffer(*buffer)
|
draw_state.set_index_buffer(*buffer, *offset)
|
||||||
}
|
}
|
||||||
RenderCommand::SetBindGroup {
|
RenderCommand::SetBindGroup {
|
||||||
index,
|
index,
|
||||||
bind_group,
|
bind_group,
|
||||||
dynamic_uniform_indices,
|
dynamic_uniform_indices,
|
||||||
} => {
|
} => {
|
||||||
|
if dynamic_uniform_indices.is_none() && draw_state.is_bind_group_set(*index, *bind_group) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let pipeline = pipelines.get(draw_state.pipeline.as_ref().unwrap()).unwrap();
|
let pipeline = pipelines.get(draw_state.pipeline.as_ref().unwrap()).unwrap();
|
||||||
let layout = pipeline.get_layout().unwrap();
|
let layout = pipeline.get_layout().unwrap();
|
||||||
let bind_group_descriptor = layout.get_bind_group(*index).unwrap();
|
let bind_group_descriptor = layout.get_bind_group(*index).unwrap();
|
||||||
@ -329,8 +340,8 @@ where
|
|||||||
struct DrawState {
|
struct DrawState {
|
||||||
pipeline: Option<Handle<PipelineDescriptor>>,
|
pipeline: Option<Handle<PipelineDescriptor>>,
|
||||||
bind_groups: Vec<Option<BindGroupId>>,
|
bind_groups: Vec<Option<BindGroupId>>,
|
||||||
vertex_buffers: Vec<Option<BufferId>>,
|
vertex_buffers: Vec<Option<(BufferId, u64)>>,
|
||||||
index_buffer: Option<BufferId>,
|
index_buffer: Option<(BufferId, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawState {
|
impl DrawState {
|
||||||
@ -338,12 +349,24 @@ impl DrawState {
|
|||||||
self.bind_groups[index as usize] = Some(bind_group);
|
self.bind_groups[index as usize] = Some(bind_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_vertex_buffer(&mut self, index: u32, buffer: BufferId) {
|
pub fn is_bind_group_set(&self, index: u32, bind_group: BindGroupId) -> bool {
|
||||||
self.vertex_buffers[index as usize] = Some(buffer);
|
self.bind_groups[index as usize] == Some(bind_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_index_buffer(&mut self, buffer: BufferId) {
|
pub fn set_vertex_buffer(&mut self, index: u32, buffer: BufferId, offset: u64) {
|
||||||
self.index_buffer = Some(buffer);
|
self.vertex_buffers[index as usize] = Some((buffer, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_vertex_buffer_set(&self, index: u32, buffer: BufferId, offset: u64) -> bool {
|
||||||
|
self.vertex_buffers[index as usize] == Some((buffer, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_index_buffer(&mut self, buffer: BufferId, offset: u64) {
|
||||||
|
self.index_buffer = Some((buffer, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_index_buffer_set(&self, buffer: BufferId, offset: u64) -> bool {
|
||||||
|
self.index_buffer == Some((buffer, offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_draw(&self) -> bool {
|
pub fn can_draw(&self) -> bool {
|
||||||
@ -355,6 +378,10 @@ impl DrawState {
|
|||||||
self.can_draw() && self.index_buffer.is_some()
|
self.can_draw() && self.index_buffer.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_pipeline_set(&self, pipeline: Handle<PipelineDescriptor>) -> bool {
|
||||||
|
self.pipeline == Some(pipeline)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_pipeline(
|
pub fn set_pipeline(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &Handle<PipelineDescriptor>,
|
handle: &Handle<PipelineDescriptor>,
|
||||||
|
@ -10,7 +10,10 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use bevy_asset::{Asset, Assets, Handle, HandleId};
|
use bevy_asset::{Asset, Assets, Handle, HandleId};
|
||||||
use bevy_ecs::{Commands, Entity, IntoSystem, Local, Query, Res, ResMut, Resources, System, World};
|
use bevy_ecs::{
|
||||||
|
Changed, Commands, Entity, IntoSystem, Local, Query, QuerySet, Res, ResMut, Resources, System,
|
||||||
|
World,
|
||||||
|
};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use renderer::{AssetRenderResourceBindings, BufferId, RenderResourceType, RenderResources};
|
use renderer::{AssetRenderResourceBindings, BufferId, RenderResourceType, RenderResources};
|
||||||
use std::{hash::Hash, marker::PhantomData, ops::DerefMut};
|
use std::{hash::Hash, marker::PhantomData, ops::DerefMut};
|
||||||
@ -80,13 +83,14 @@ impl<I: Hash + Eq> BufferArray<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, render_resource_context: &dyn RenderResourceContext) {
|
pub fn resize(&mut self, render_resource_context: &dyn RenderResourceContext) -> bool {
|
||||||
if self.len <= self.buffer_capacity {
|
if self.len <= self.buffer_capacity {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.allocate_buffer(render_resource_context);
|
self.allocate_buffer(render_resource_context);
|
||||||
// TODO: allow shrinking
|
// TODO: allow shrinking
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate_buffer(&mut self, render_resource_context: &dyn RenderResourceContext) {
|
pub fn allocate_buffer(&mut self, render_resource_context: &dyn RenderResourceContext) {
|
||||||
@ -189,12 +193,29 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resize BufferArray buffers if they aren't large enough
|
/// Resize BufferArray buffers if they aren't large enough
|
||||||
fn resize_buffer_arrays(&mut self, render_resource_context: &dyn RenderResourceContext) {
|
fn resize_buffer_arrays(
|
||||||
|
&mut self,
|
||||||
|
render_resource_context: &dyn RenderResourceContext,
|
||||||
|
) -> bool {
|
||||||
|
let mut resized = false;
|
||||||
for buffer_array in self.buffer_arrays.iter_mut() {
|
for buffer_array in self.buffer_arrays.iter_mut() {
|
||||||
if let Some(buffer_array) = buffer_array {
|
if let Some(buffer_array) = buffer_array {
|
||||||
buffer_array.resize(render_resource_context);
|
resized |= buffer_array.resize(render_resource_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resized
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_required_staging_buffer_size_to_max(&mut self) {
|
||||||
|
let mut new_size = 0;
|
||||||
|
for buffer_array in self.buffer_arrays.iter() {
|
||||||
|
if let Some(buffer_array) = buffer_array {
|
||||||
|
new_size += buffer_array.item_size * buffer_array.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.required_staging_buffer_size = new_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the staging buffer to provide enough space to copy data to target buffers.
|
/// Update the staging buffer to provide enough space to copy data to target buffers.
|
||||||
@ -238,12 +259,10 @@ where
|
|||||||
staging_buffer: &mut [u8],
|
staging_buffer: &mut [u8],
|
||||||
) {
|
) {
|
||||||
for (i, render_resource) in uniforms.iter().enumerate() {
|
for (i, render_resource) in uniforms.iter().enumerate() {
|
||||||
match render_resource.resource_type() {
|
if let Some(RenderResourceType::Buffer) = render_resource.resource_type() {
|
||||||
Some(RenderResourceType::Buffer) => {
|
|
||||||
let size = render_resource.buffer_byte_len().unwrap();
|
let size = render_resource.buffer_byte_len().unwrap();
|
||||||
let render_resource_name = uniforms.get_render_resource_name(i).unwrap();
|
let render_resource_name = uniforms.get_render_resource_name(i).unwrap();
|
||||||
let aligned_size =
|
let aligned_size = render_resource_context.get_aligned_uniform_size(size, false);
|
||||||
render_resource_context.get_aligned_uniform_size(size, false);
|
|
||||||
let buffer_array = self.buffer_arrays[i].as_mut().unwrap();
|
let buffer_array = self.buffer_arrays[i].as_mut().unwrap();
|
||||||
let range = 0..aligned_size as u64;
|
let range = 0..aligned_size as u64;
|
||||||
let (target_buffer, target_offset) = if dynamic_uniforms {
|
let (target_buffer, target_offset) = if dynamic_uniforms {
|
||||||
@ -279,9 +298,7 @@ where
|
|||||||
matching_buffer
|
matching_buffer
|
||||||
} else {
|
} else {
|
||||||
let mut usage = BufferUsage::UNIFORM;
|
let mut usage = BufferUsage::UNIFORM;
|
||||||
if let Some(render_resource_hints) =
|
if let Some(render_resource_hints) = uniforms.get_render_resource_hints(i) {
|
||||||
uniforms.get_render_resource_hints(i)
|
|
||||||
{
|
|
||||||
if render_resource_hints.contains(RenderResourceHints::BUFFER) {
|
if render_resource_hints.contains(RenderResourceHints::BUFFER) {
|
||||||
usage = BufferUsage::STORAGE
|
usage = BufferUsage::STORAGE
|
||||||
}
|
}
|
||||||
@ -320,10 +337,6 @@ where
|
|||||||
});
|
});
|
||||||
self.current_staging_buffer_offset += size;
|
self.current_staging_buffer_offset += size;
|
||||||
}
|
}
|
||||||
Some(RenderResourceType::Texture) => { /* ignore textures */ }
|
|
||||||
Some(RenderResourceType::Sampler) => { /* ignore samplers */ }
|
|
||||||
None => { /* ignore None */ }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,22 +434,25 @@ impl<I, T: RenderResources> Default for RenderResourcesNodeState<I, T> {
|
|||||||
fn render_resources_node_system<T: RenderResources>(
|
fn render_resources_node_system<T: RenderResources>(
|
||||||
mut state: Local<RenderResourcesNodeState<Entity, T>>,
|
mut state: Local<RenderResourcesNodeState<Entity, T>>,
|
||||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||||
mut query: Query<(Entity, &T, &Draw, &mut RenderPipelines)>,
|
mut queries: QuerySet<(
|
||||||
|
Query<(Entity, &T, &Draw, &mut RenderPipelines), Changed<T>>,
|
||||||
|
Query<(Entity, &T, &Draw, &mut RenderPipelines)>,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
let state = state.deref_mut();
|
let state = state.deref_mut();
|
||||||
let uniform_buffer_arrays = &mut state.uniform_buffer_arrays;
|
let uniform_buffer_arrays = &mut state.uniform_buffer_arrays;
|
||||||
let render_resource_context = &**render_resource_context;
|
let render_resource_context = &**render_resource_context;
|
||||||
uniform_buffer_arrays.begin_update();
|
uniform_buffer_arrays.begin_update();
|
||||||
// initialize uniform buffer arrays using the first RenderResources
|
// initialize uniform buffer arrays using the first RenderResources
|
||||||
if let Some((_, first, _, _)) = query.iter_mut().next() {
|
if let Some((_, first, _, _)) = queries.q0_mut().iter_mut().next() {
|
||||||
uniform_buffer_arrays.initialize(first, render_resource_context);
|
uniform_buffer_arrays.initialize(first, render_resource_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
for entity in query.removed::<T>() {
|
for entity in queries.q0().removed::<T>() {
|
||||||
uniform_buffer_arrays.remove_bindings(*entity);
|
uniform_buffer_arrays.remove_bindings(*entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entity, uniforms, draw, mut render_pipelines) in query.iter_mut() {
|
for (entity, uniforms, draw, mut render_pipelines) in queries.q0_mut().iter_mut() {
|
||||||
if !draw.is_visible {
|
if !draw.is_visible {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -449,7 +465,10 @@ fn render_resources_node_system<T: RenderResources>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
uniform_buffer_arrays.resize_buffer_arrays(render_resource_context);
|
let resized = uniform_buffer_arrays.resize_buffer_arrays(render_resource_context);
|
||||||
|
if resized {
|
||||||
|
uniform_buffer_arrays.set_required_staging_buffer_size_to_max()
|
||||||
|
}
|
||||||
uniform_buffer_arrays.resize_staging_buffer(render_resource_context);
|
uniform_buffer_arrays.resize_staging_buffer(render_resource_context);
|
||||||
|
|
||||||
if let Some(staging_buffer) = state.uniform_buffer_arrays.staging_buffer {
|
if let Some(staging_buffer) = state.uniform_buffer_arrays.staging_buffer {
|
||||||
@ -458,7 +477,11 @@ fn render_resources_node_system<T: RenderResources>(
|
|||||||
staging_buffer,
|
staging_buffer,
|
||||||
0..state.uniform_buffer_arrays.staging_buffer_size as u64,
|
0..state.uniform_buffer_arrays.staging_buffer_size as u64,
|
||||||
&mut |mut staging_buffer, _render_resource_context| {
|
&mut |mut staging_buffer, _render_resource_context| {
|
||||||
for (entity, uniforms, draw, mut render_pipelines) in query.iter_mut() {
|
// if the buffer array was resized, write all entities to the new buffer, otherwise only write changes
|
||||||
|
if resized {
|
||||||
|
for (entity, uniforms, draw, mut render_pipelines) in
|
||||||
|
queries.q1_mut().iter_mut()
|
||||||
|
{
|
||||||
if !draw.is_visible {
|
if !draw.is_visible {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -472,6 +495,24 @@ fn render_resources_node_system<T: RenderResources>(
|
|||||||
&mut staging_buffer,
|
&mut staging_buffer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (entity, uniforms, draw, mut render_pipelines) in
|
||||||
|
queries.q0_mut().iter_mut()
|
||||||
|
{
|
||||||
|
if !draw.is_visible {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.uniform_buffer_arrays.write_uniform_buffers(
|
||||||
|
entity,
|
||||||
|
&uniforms,
|
||||||
|
state.dynamic_uniforms,
|
||||||
|
render_resource_context,
|
||||||
|
&mut render_pipelines.bindings,
|
||||||
|
&mut staging_buffer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
render_resource_context.unmap_buffer(staging_buffer);
|
render_resource_context.unmap_buffer(staging_buffer);
|
||||||
|
@ -152,4 +152,6 @@ impl RenderResourceContext for HeadlessRenderResourceContext {
|
|||||||
fn get_specialized_shader(&self, shader: &Shader, _macros: Option<&[String]>) -> Shader {
|
fn get_specialized_shader(&self, shader: &Shader, _macros: Option<&[String]>) -> Shader {
|
||||||
shader.clone()
|
shader.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_stale_bind_groups(&self) {}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,10 @@ impl RenderResourceBindings {
|
|||||||
self.dirty_bind_groups.insert(*id);
|
self.dirty_bind_groups.insert(*id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// unmatched bind group descriptors might now match
|
||||||
|
self.bind_group_descriptors
|
||||||
|
.retain(|_, value| value.is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +124,7 @@ impl RenderResourceBindings {
|
|||||||
self.bind_group_descriptors.insert(descriptor.id, Some(id));
|
self.bind_group_descriptors.insert(descriptor.id, Some(id));
|
||||||
BindGroupStatus::Changed(id)
|
BindGroupStatus::Changed(id)
|
||||||
} else {
|
} else {
|
||||||
|
self.bind_group_descriptors.insert(descriptor.id, None);
|
||||||
BindGroupStatus::NoMatch
|
BindGroupStatus::NoMatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,10 +163,9 @@ impl RenderResourceBindings {
|
|||||||
.expect("RenderResourceSet was just changed, so it should exist");
|
.expect("RenderResourceSet was just changed, so it should exist");
|
||||||
render_resource_context.create_bind_group(bind_group_descriptor.id, bind_group);
|
render_resource_context.create_bind_group(bind_group_descriptor.id, bind_group);
|
||||||
}
|
}
|
||||||
// TODO: Don't re-create bind groups if they havent changed. this will require cleanup of orphan bind groups and
|
|
||||||
// removal of global context.clear_bind_groups()
|
|
||||||
// PERF: see above
|
|
||||||
BindGroupStatus::Unchanged(id) => {
|
BindGroupStatus::Unchanged(id) => {
|
||||||
|
// PERF: this is only required because RenderResourceContext::remove_stale_bind_groups doesn't inform RenderResourceBindings
|
||||||
|
// when a stale bind group has been removed
|
||||||
let bind_group = self
|
let bind_group = self
|
||||||
.get_bind_group(id)
|
.get_bind_group(id)
|
||||||
.expect("RenderResourceSet was just changed, so it should exist");
|
.expect("RenderResourceSet was just changed, so it should exist");
|
||||||
|
@ -62,6 +62,7 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
|
|||||||
bind_group: &BindGroup,
|
bind_group: &BindGroup,
|
||||||
);
|
);
|
||||||
fn clear_bind_groups(&self);
|
fn clear_bind_groups(&self);
|
||||||
|
fn remove_stale_bind_groups(&self);
|
||||||
/// Reflects the pipeline layout from its shaders.
|
/// Reflects the pipeline layout from its shaders.
|
||||||
///
|
///
|
||||||
/// If `bevy_conventions` is true, it will be assumed that the shader follows "bevy shader conventions". These allow
|
/// If `bevy_conventions` is true, it will be assumed that the shader follows "bevy shader conventions". These allow
|
||||||
|
@ -50,7 +50,11 @@ pub fn sprite_system(
|
|||||||
let material = materials.get(handle).unwrap();
|
let material = materials.get(handle).unwrap();
|
||||||
if let Some(ref texture_handle) = material.texture {
|
if let Some(ref texture_handle) = material.texture {
|
||||||
if let Some(texture) = textures.get(texture_handle) {
|
if let Some(texture) = textures.get(texture_handle) {
|
||||||
sprite.size = texture.size.as_vec3().truncate();
|
let texture_size = texture.size.as_vec3().truncate();
|
||||||
|
// only set sprite size if it has changed (this check prevents change detection from triggering)
|
||||||
|
if sprite.size != texture_size {
|
||||||
|
sprite.size = texture_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,15 @@ impl<'a, 'b> WorldChildBuilder<'a, 'b> {
|
|||||||
pub fn current_entity(&self) -> Option<Entity> {
|
pub fn current_entity(&self) -> Option<Entity> {
|
||||||
self.world_builder.current_entity
|
self.world_builder.current_entity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self {
|
||||||
|
let current_entity = self
|
||||||
|
.world_builder
|
||||||
|
.current_entity
|
||||||
|
.expect("The 'current entity' is not set. You should spawn an entity first.");
|
||||||
|
f(current_entity);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BuildWorldChildren {
|
pub trait BuildWorldChildren {
|
||||||
|
@ -3,22 +3,29 @@ use bevy_ecs::prelude::*;
|
|||||||
|
|
||||||
pub fn transform_propagate_system(
|
pub fn transform_propagate_system(
|
||||||
mut root_query: Query<
|
mut root_query: Query<
|
||||||
(Option<&Children>, &Transform, &mut GlobalTransform),
|
(Entity, Option<&Children>, &Transform, &mut GlobalTransform),
|
||||||
(Without<Parent>, With<GlobalTransform>),
|
(Without<Parent>, With<GlobalTransform>),
|
||||||
>,
|
>,
|
||||||
mut transform_query: Query<(&Transform, &mut GlobalTransform), With<Parent>>,
|
mut transform_query: Query<(&Transform, &mut GlobalTransform), With<Parent>>,
|
||||||
|
changed_transform_query: Query<Entity, Changed<Transform>>,
|
||||||
children_query: Query<Option<&Children>, (With<Parent>, With<GlobalTransform>)>,
|
children_query: Query<Option<&Children>, (With<Parent>, With<GlobalTransform>)>,
|
||||||
) {
|
) {
|
||||||
for (children, transform, mut global_transform) in root_query.iter_mut() {
|
for (entity, children, transform, mut global_transform) in root_query.iter_mut() {
|
||||||
|
let mut changed = false;
|
||||||
|
if changed_transform_query.get(entity).is_ok() {
|
||||||
*global_transform = GlobalTransform::from(*transform);
|
*global_transform = GlobalTransform::from(*transform);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(children) = children {
|
if let Some(children) = children {
|
||||||
for child in children.0.iter() {
|
for child in children.0.iter() {
|
||||||
propagate_recursive(
|
propagate_recursive(
|
||||||
&global_transform,
|
&global_transform,
|
||||||
|
&changed_transform_query,
|
||||||
&mut transform_query,
|
&mut transform_query,
|
||||||
&children_query,
|
&children_query,
|
||||||
*child,
|
*child,
|
||||||
|
changed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,13 +34,19 @@ pub fn transform_propagate_system(
|
|||||||
|
|
||||||
fn propagate_recursive(
|
fn propagate_recursive(
|
||||||
parent: &GlobalTransform,
|
parent: &GlobalTransform,
|
||||||
|
changed_transform_query: &Query<Entity, Changed<Transform>>,
|
||||||
transform_query: &mut Query<(&Transform, &mut GlobalTransform), With<Parent>>,
|
transform_query: &mut Query<(&Transform, &mut GlobalTransform), With<Parent>>,
|
||||||
children_query: &Query<Option<&Children>, (With<Parent>, With<GlobalTransform>)>,
|
children_query: &Query<Option<&Children>, (With<Parent>, With<GlobalTransform>)>,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
|
mut changed: bool,
|
||||||
) {
|
) {
|
||||||
|
changed |= changed_transform_query.get(entity).is_ok();
|
||||||
|
|
||||||
let global_matrix = {
|
let global_matrix = {
|
||||||
if let Ok((transform, mut global_transform)) = transform_query.get_mut(entity) {
|
if let Ok((transform, mut global_transform)) = transform_query.get_mut(entity) {
|
||||||
|
if changed {
|
||||||
*global_transform = parent.mul_transform(*transform);
|
*global_transform = parent.mul_transform(*transform);
|
||||||
|
}
|
||||||
*global_transform
|
*global_transform
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@ -42,7 +55,14 @@ fn propagate_recursive(
|
|||||||
|
|
||||||
if let Ok(Some(children)) = children_query.get(entity) {
|
if let Ok(Some(children)) = children_query.get(entity) {
|
||||||
for child in children.0.iter() {
|
for child in children.0.iter() {
|
||||||
propagate_recursive(&global_matrix, transform_query, children_query, *child);
|
propagate_recursive(
|
||||||
|
&global_matrix,
|
||||||
|
changed_transform_query,
|
||||||
|
transform_query,
|
||||||
|
children_query,
|
||||||
|
*child,
|
||||||
|
changed,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +70,7 @@ fn propagate_recursive(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::hierarchy::{parent_update_system, BuildChildren};
|
use crate::hierarchy::{parent_update_system, BuildChildren, BuildWorldChildren};
|
||||||
use bevy_ecs::{Resources, Schedule, World};
|
use bevy_ecs::{Resources, Schedule, World};
|
||||||
use bevy_math::Vec3;
|
use bevy_math::Vec3;
|
||||||
|
|
||||||
@ -65,30 +85,36 @@ mod test {
|
|||||||
schedule.add_system_to_stage("update", transform_propagate_system);
|
schedule.add_system_to_stage("update", transform_propagate_system);
|
||||||
|
|
||||||
// Root entity
|
// Root entity
|
||||||
let parent = world.spawn((
|
world.spawn((
|
||||||
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
|
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
|
||||||
GlobalTransform::identity(),
|
GlobalTransform::identity(),
|
||||||
));
|
));
|
||||||
let children = world
|
|
||||||
.spawn_batch(vec![
|
let mut children = Vec::new();
|
||||||
(
|
world
|
||||||
|
.build()
|
||||||
|
.spawn((
|
||||||
|
Transform::from_translation(Vec3::new(1.0, 0.0, 0.0)),
|
||||||
|
GlobalTransform::identity(),
|
||||||
|
))
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent
|
||||||
|
.spawn((
|
||||||
Transform::from_translation(Vec3::new(0.0, 2.0, 0.)),
|
Transform::from_translation(Vec3::new(0.0, 2.0, 0.)),
|
||||||
Parent(parent),
|
|
||||||
GlobalTransform::identity(),
|
GlobalTransform::identity(),
|
||||||
),
|
))
|
||||||
(
|
.for_current_entity(|entity| children.push(entity))
|
||||||
|
.spawn((
|
||||||
Transform::from_translation(Vec3::new(0.0, 0.0, 3.)),
|
Transform::from_translation(Vec3::new(0.0, 0.0, 3.)),
|
||||||
Parent(parent),
|
|
||||||
GlobalTransform::identity(),
|
GlobalTransform::identity(),
|
||||||
),
|
))
|
||||||
])
|
.for_current_entity(|entity| children.push(entity));
|
||||||
.collect::<Vec<Entity>>();
|
});
|
||||||
// we need to run the schedule two times because components need to be filled in
|
// we need to run the schedule two times because components need to be filled in
|
||||||
// to resolve this problem in code, just add the correct components, or use Commands
|
// to resolve this problem in code, just add the correct components, or use Commands
|
||||||
// which adds all of the components needed with the correct state (see next test)
|
// which adds all of the components needed with the correct state (see next test)
|
||||||
schedule.initialize(&mut world, &mut resources);
|
schedule.initialize(&mut world, &mut resources);
|
||||||
schedule.run(&mut world, &mut resources);
|
schedule.run(&mut world, &mut resources);
|
||||||
schedule.run(&mut world, &mut resources);
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*world.get::<GlobalTransform>(children[0]).unwrap(),
|
*world.get::<GlobalTransform>(children[0]).unwrap(),
|
||||||
|
@ -522,6 +522,10 @@ impl RenderResourceContext for WgpuRenderResourceContext {
|
|||||||
self.resources.bind_groups.write().clear();
|
self.resources.bind_groups.write().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_stale_bind_groups(&self) {
|
||||||
|
self.resources.remove_stale_bind_groups();
|
||||||
|
}
|
||||||
|
|
||||||
fn get_buffer_info(&self, buffer: BufferId) -> Option<BufferInfo> {
|
fn get_buffer_info(&self, buffer: BufferId) -> Option<BufferInfo> {
|
||||||
self.resources.buffer_infos.read().get(&buffer).cloned()
|
self.resources.buffer_infos.read().get(&buffer).cloned()
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,10 @@ impl<'a> RenderPass for WgpuRenderPass<'a> {
|
|||||||
} else {
|
} else {
|
||||||
EMPTY
|
EMPTY
|
||||||
};
|
};
|
||||||
|
self.wgpu_resources
|
||||||
|
.used_bind_group_sender
|
||||||
|
.send(bind_group)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"set bind group {:?} {:?}: {:?}",
|
"set bind group {:?} {:?}: {:?}",
|
||||||
|
@ -115,6 +115,6 @@ impl WgpuRenderer {
|
|||||||
|
|
||||||
let render_resource_context = resources.get::<Box<dyn RenderResourceContext>>().unwrap();
|
let render_resource_context = resources.get::<Box<dyn RenderResourceContext>>().unwrap();
|
||||||
render_resource_context.drop_all_swap_chain_textures();
|
render_resource_context.drop_all_swap_chain_textures();
|
||||||
render_resource_context.clear_bind_groups();
|
render_resource_context.remove_stale_bind_groups();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ use bevy_render::{
|
|||||||
};
|
};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use bevy_window::WindowId;
|
use bevy_window::WindowId;
|
||||||
|
use crossbeam_channel::{Receiver, Sender, TryRecvError};
|
||||||
use parking_lot::{RwLock, RwLockReadGuard};
|
use parking_lot::{RwLock, RwLockReadGuard};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ pub struct WgpuResourcesReadLock<'a> {
|
|||||||
pub render_pipelines:
|
pub render_pipelines:
|
||||||
RwLockReadGuard<'a, HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>>,
|
RwLockReadGuard<'a, HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>>,
|
||||||
pub bind_groups: RwLockReadGuard<'a, HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>,
|
pub bind_groups: RwLockReadGuard<'a, HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>,
|
||||||
|
pub used_bind_group_sender: Sender<BindGroupId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> WgpuResourcesReadLock<'a> {
|
impl<'a> WgpuResourcesReadLock<'a> {
|
||||||
@ -55,6 +57,7 @@ impl<'a> WgpuResourcesReadLock<'a> {
|
|||||||
swap_chain_frames: &self.swap_chain_frames,
|
swap_chain_frames: &self.swap_chain_frames,
|
||||||
render_pipelines: &self.render_pipelines,
|
render_pipelines: &self.render_pipelines,
|
||||||
bind_groups: &self.bind_groups,
|
bind_groups: &self.bind_groups,
|
||||||
|
used_bind_group_sender: &self.used_bind_group_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +70,7 @@ pub struct WgpuResourceRefs<'a> {
|
|||||||
pub swap_chain_frames: &'a HashMap<TextureId, wgpu::SwapChainFrame>,
|
pub swap_chain_frames: &'a HashMap<TextureId, wgpu::SwapChainFrame>,
|
||||||
pub render_pipelines: &'a HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>,
|
pub render_pipelines: &'a HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>,
|
||||||
pub bind_groups: &'a HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>,
|
pub bind_groups: &'a HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>,
|
||||||
|
pub used_bind_group_sender: &'a Sender<BindGroupId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
@ -85,6 +89,7 @@ pub struct WgpuResources {
|
|||||||
pub bind_groups: Arc<RwLock<HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>>,
|
pub bind_groups: Arc<RwLock<HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>>,
|
||||||
pub bind_group_layouts: Arc<RwLock<HashMap<BindGroupDescriptorId, wgpu::BindGroupLayout>>>,
|
pub bind_group_layouts: Arc<RwLock<HashMap<BindGroupDescriptorId, wgpu::BindGroupLayout>>>,
|
||||||
pub asset_resources: Arc<RwLock<HashMap<(HandleUntyped, u64), RenderResourceId>>>,
|
pub asset_resources: Arc<RwLock<HashMap<(HandleUntyped, u64), RenderResourceId>>>,
|
||||||
|
pub bind_group_counter: BindGroupCounter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WgpuResources {
|
impl WgpuResources {
|
||||||
@ -95,6 +100,7 @@ impl WgpuResources {
|
|||||||
swap_chain_frames: self.swap_chain_frames.read(),
|
swap_chain_frames: self.swap_chain_frames.read(),
|
||||||
render_pipelines: self.render_pipelines.read(),
|
render_pipelines: self.render_pipelines.read(),
|
||||||
bind_groups: self.bind_groups.read(),
|
bind_groups: self.bind_groups.read(),
|
||||||
|
used_bind_group_sender: self.bind_group_counter.used_bind_group_sender.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,4 +115,64 @@ impl WgpuResources {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_stale_bind_groups(&self) {
|
||||||
|
let mut bind_groups = self.bind_groups.write();
|
||||||
|
self.bind_group_counter
|
||||||
|
.remove_stale_bind_groups(&mut bind_groups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BindGroupCounter {
|
||||||
|
pub used_bind_group_sender: Sender<BindGroupId>,
|
||||||
|
pub used_bind_group_receiver: Receiver<BindGroupId>,
|
||||||
|
pub bind_group_usage_counts: Arc<RwLock<HashMap<BindGroupId, u64>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BindGroupCounter {
|
||||||
|
pub fn remove_stale_bind_groups(
|
||||||
|
&self,
|
||||||
|
bind_groups: &mut HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>,
|
||||||
|
) {
|
||||||
|
let mut bind_group_usage_counts = self.bind_group_usage_counts.write();
|
||||||
|
loop {
|
||||||
|
let bind_group = match self.used_bind_group_receiver.try_recv() {
|
||||||
|
Ok(bind_group) => bind_group,
|
||||||
|
Err(TryRecvError::Empty) => break,
|
||||||
|
Err(TryRecvError::Disconnected) => panic!("used bind group channel disconnected"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let count = bind_group_usage_counts.entry(bind_group).or_insert(0);
|
||||||
|
// free every two frames
|
||||||
|
*count = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for info in bind_groups.values_mut() {
|
||||||
|
info.bind_groups.retain(|id, _| {
|
||||||
|
let retain = {
|
||||||
|
// if a value hasn't been counted yet, give it two frames of leeway
|
||||||
|
let count = bind_group_usage_counts.entry(*id).or_insert(2);
|
||||||
|
*count -= 1;
|
||||||
|
*count > 0
|
||||||
|
};
|
||||||
|
if !retain {
|
||||||
|
bind_group_usage_counts.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
retain
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BindGroupCounter {
|
||||||
|
fn default() -> Self {
|
||||||
|
let (send, recv) = crossbeam_channel::unbounded();
|
||||||
|
BindGroupCounter {
|
||||||
|
used_bind_group_sender: send,
|
||||||
|
used_bind_group_receiver: recv,
|
||||||
|
bind_group_usage_counts: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user