Skip to content

Commit b5ffab7

Browse files
authored
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
1 parent 3cee95e commit b5ffab7

File tree

14 files changed

+404
-163
lines changed

14 files changed

+404
-163
lines changed

crates/bevy_ecs/src/core/world_builder.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,17 @@ impl<'a> WorldBuilder<'a> {
6262
self.current_entity = Some(self.world.spawn(components));
6363
self
6464
}
65+
66+
#[inline]
67+
pub fn current_entity(&self) -> Option<Entity> {
68+
self.current_entity
69+
}
70+
71+
pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self {
72+
let current_entity = self
73+
.current_entity
74+
.expect("The 'current entity' is not set. You should spawn an entity first.");
75+
f(current_entity);
76+
self
77+
}
6578
}

crates/bevy_render/src/mesh/mesh.rs

Lines changed: 70 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ use crate::{
55
use bevy_app::prelude::{EventReader, Events};
66
use bevy_asset::{AssetEvent, Assets, Handle};
77
use bevy_core::AsBytes;
8-
use bevy_ecs::{Local, Query, Res};
8+
use bevy_ecs::{Changed, Entity, Local, Mut, Query, QuerySet, Res, With};
99
use bevy_math::*;
1010
use bevy_reflect::TypeUuid;
1111
use std::borrow::Cow;
1212

1313
use crate::pipeline::{InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor};
14-
use bevy_utils::HashMap;
14+
use bevy_utils::{HashMap, HashSet};
1515

1616
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
1717
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
@@ -320,17 +320,27 @@ fn remove_current_mesh_resources(
320320
remove_resource_save(render_resource_context, handle, INDEX_BUFFER_ASSET_INDEX);
321321
}
322322

323+
#[derive(Default)]
324+
pub struct MeshEntities {
325+
entities: HashSet<Entity>,
326+
waiting: HashSet<Entity>,
327+
}
328+
323329
#[derive(Default)]
324330
pub struct MeshResourceProviderState {
325331
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
332+
mesh_entities: HashMap<Handle<Mesh>, MeshEntities>,
326333
}
327334

328335
pub fn mesh_resource_provider_system(
329336
mut state: Local<MeshResourceProviderState>,
330337
render_resource_context: Res<Box<dyn RenderResourceContext>>,
331338
meshes: Res<Assets<Mesh>>,
332339
mesh_events: Res<Events<AssetEvent<Mesh>>>,
333-
mut query: Query<(&Handle<Mesh>, &mut RenderPipelines)>,
340+
mut queries: QuerySet<(
341+
Query<&mut RenderPipelines, With<Handle<Mesh>>>,
342+
Query<(Entity, &Handle<Mesh>, &mut RenderPipelines), Changed<Handle<Mesh>>>,
343+
)>,
334344
) {
335345
let mut changed_meshes = bevy_utils::HashSet::<Handle<Mesh>>::default();
336346
let render_resource_context = &**render_resource_context;
@@ -383,39 +393,69 @@ pub fn mesh_resource_provider_system(
383393
)),
384394
VERTEX_ATTRIBUTE_BUFFER_ID,
385395
);
396+
397+
if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) {
398+
for entity in mesh_entities.waiting.drain() {
399+
if let Ok(render_pipelines) = queries.q0_mut().get_mut(entity) {
400+
mesh_entities.entities.insert(entity);
401+
update_entity_mesh(
402+
render_resource_context,
403+
mesh,
404+
changed_mesh_handle,
405+
render_pipelines,
406+
);
407+
}
408+
}
409+
}
386410
}
387411
}
388412

389413
// handover buffers to pipeline
390-
for (handle, mut render_pipelines) in query.iter_mut() {
414+
for (entity, handle, render_pipelines) in queries.q1_mut().iter_mut() {
415+
let mesh_entities = state
416+
.mesh_entities
417+
.entry(handle.clone_weak())
418+
.or_insert_with(MeshEntities::default);
391419
if let Some(mesh) = meshes.get(handle) {
392-
for render_pipeline in render_pipelines.pipelines.iter_mut() {
393-
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
394-
// TODO: don't allocate a new vertex buffer descriptor for every entity
395-
render_pipeline.specialization.vertex_buffer_descriptor =
396-
mesh.get_vertex_buffer_descriptor();
397-
render_pipeline.specialization.index_format = mesh
398-
.indices()
399-
.map(|i| i.into())
400-
.unwrap_or(IndexFormat::Uint32);
401-
}
420+
mesh_entities.entities.insert(entity);
421+
mesh_entities.waiting.remove(&entity);
422+
update_entity_mesh(render_resource_context, mesh, handle, render_pipelines);
423+
} else {
424+
mesh_entities.waiting.insert(entity);
425+
}
426+
}
427+
}
402428

403-
if let Some(RenderResourceId::Buffer(index_buffer_resource)) =
404-
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
405-
{
406-
// set index buffer into binding
407-
render_pipelines
408-
.bindings
409-
.set_index_buffer(index_buffer_resource);
410-
}
429+
fn update_entity_mesh(
430+
render_resource_context: &dyn RenderResourceContext,
431+
mesh: &Mesh,
432+
handle: &Handle<Mesh>,
433+
mut render_pipelines: Mut<RenderPipelines>,
434+
) {
435+
for render_pipeline in render_pipelines.pipelines.iter_mut() {
436+
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
437+
// TODO: don't allocate a new vertex buffer descriptor for every entity
438+
render_pipeline.specialization.vertex_buffer_descriptor =
439+
mesh.get_vertex_buffer_descriptor();
440+
render_pipeline.specialization.index_format = mesh
441+
.indices()
442+
.map(|i| i.into())
443+
.unwrap_or(IndexFormat::Uint32);
444+
}
411445

412-
if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) =
413-
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
414-
{
415-
// set index buffer into binding
416-
render_pipelines.bindings.vertex_attribute_buffer =
417-
Some(vertex_attribute_buffer_resource);
418-
}
419-
}
446+
if let Some(RenderResourceId::Buffer(index_buffer_resource)) =
447+
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
448+
{
449+
// set index buffer into binding
450+
render_pipelines
451+
.bindings
452+
.set_index_buffer(index_buffer_resource);
453+
}
454+
455+
if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) =
456+
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
457+
{
458+
// set index buffer into binding
459+
render_pipelines.bindings.vertex_attribute_buffer = Some(vertex_attribute_buffer_resource);
420460
}
421461
}

crates/bevy_render/src/render_graph/nodes/pass_node.rs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,9 @@ where
244244
for render_command in draw.render_commands.iter() {
245245
match render_command {
246246
RenderCommand::SetPipeline { pipeline } => {
247-
// TODO: Filter pipelines
247+
if draw_state.is_pipeline_set(pipeline.clone_weak()) {
248+
continue;
249+
}
248250
render_pass.set_pipeline(pipeline);
249251
let descriptor = pipelines.get(pipeline).unwrap();
250252
draw_state.set_pipeline(pipeline, descriptor);
@@ -290,18 +292,27 @@ where
290292
offset,
291293
slot,
292294
} => {
295+
if draw_state.is_vertex_buffer_set(*slot, *buffer, *offset) {
296+
continue;
297+
}
293298
render_pass.set_vertex_buffer(*slot, *buffer, *offset);
294-
draw_state.set_vertex_buffer(*slot, *buffer);
299+
draw_state.set_vertex_buffer(*slot, *buffer, *offset);
295300
}
296301
RenderCommand::SetIndexBuffer { buffer, offset } => {
302+
if draw_state.is_index_buffer_set(*buffer, *offset) {
303+
continue;
304+
}
297305
render_pass.set_index_buffer(*buffer, *offset);
298-
draw_state.set_index_buffer(*buffer)
306+
draw_state.set_index_buffer(*buffer, *offset)
299307
}
300308
RenderCommand::SetBindGroup {
301309
index,
302310
bind_group,
303311
dynamic_uniform_indices,
304312
} => {
313+
if dynamic_uniform_indices.is_none() && draw_state.is_bind_group_set(*index, *bind_group) {
314+
continue;
315+
}
305316
let pipeline = pipelines.get(draw_state.pipeline.as_ref().unwrap()).unwrap();
306317
let layout = pipeline.get_layout().unwrap();
307318
let bind_group_descriptor = layout.get_bind_group(*index).unwrap();
@@ -329,21 +340,33 @@ where
329340
struct DrawState {
330341
pipeline: Option<Handle<PipelineDescriptor>>,
331342
bind_groups: Vec<Option<BindGroupId>>,
332-
vertex_buffers: Vec<Option<BufferId>>,
333-
index_buffer: Option<BufferId>,
343+
vertex_buffers: Vec<Option<(BufferId, u64)>>,
344+
index_buffer: Option<(BufferId, u64)>,
334345
}
335346

336347
impl DrawState {
337348
pub fn set_bind_group(&mut self, index: u32, bind_group: BindGroupId) {
338349
self.bind_groups[index as usize] = Some(bind_group);
339350
}
340351

341-
pub fn set_vertex_buffer(&mut self, index: u32, buffer: BufferId) {
342-
self.vertex_buffers[index as usize] = Some(buffer);
352+
pub fn is_bind_group_set(&self, index: u32, bind_group: BindGroupId) -> bool {
353+
self.bind_groups[index as usize] == Some(bind_group)
354+
}
355+
356+
pub fn set_vertex_buffer(&mut self, index: u32, buffer: BufferId, offset: u64) {
357+
self.vertex_buffers[index as usize] = Some((buffer, offset));
343358
}
344359

345-
pub fn set_index_buffer(&mut self, buffer: BufferId) {
346-
self.index_buffer = Some(buffer);
360+
pub fn is_vertex_buffer_set(&self, index: u32, buffer: BufferId, offset: u64) -> bool {
361+
self.vertex_buffers[index as usize] == Some((buffer, offset))
362+
}
363+
364+
pub fn set_index_buffer(&mut self, buffer: BufferId, offset: u64) {
365+
self.index_buffer = Some((buffer, offset));
366+
}
367+
368+
pub fn is_index_buffer_set(&self, buffer: BufferId, offset: u64) -> bool {
369+
self.index_buffer == Some((buffer, offset))
347370
}
348371

349372
pub fn can_draw(&self) -> bool {
@@ -355,6 +378,10 @@ impl DrawState {
355378
self.can_draw() && self.index_buffer.is_some()
356379
}
357380

381+
pub fn is_pipeline_set(&self, pipeline: Handle<PipelineDescriptor>) -> bool {
382+
self.pipeline == Some(pipeline)
383+
}
384+
358385
pub fn set_pipeline(
359386
&mut self,
360387
handle: &Handle<PipelineDescriptor>,

0 commit comments

Comments
 (0)