Skip to content

Commit

Permalink
Renderer Optimization Round 1 (#958)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
cart authored Dec 1, 2020
1 parent 3cee95e commit b5ffab7
Show file tree
Hide file tree
Showing 14 changed files with 404 additions and 163 deletions.
13 changes: 13 additions & 0 deletions crates/bevy_ecs/src/core/world_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,17 @@ impl<'a> WorldBuilder<'a> {
self.current_entity = Some(self.world.spawn(components));
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
}
}
100 changes: 70 additions & 30 deletions crates/bevy_render/src/mesh/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use crate::{
use bevy_app::prelude::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets, Handle};
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_reflect::TypeUuid;
use std::borrow::Cow;

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 VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
Expand Down Expand Up @@ -320,17 +320,27 @@ fn remove_current_mesh_resources(
remove_resource_save(render_resource_context, handle, INDEX_BUFFER_ASSET_INDEX);
}

#[derive(Default)]
pub struct MeshEntities {
entities: HashSet<Entity>,
waiting: HashSet<Entity>,
}

#[derive(Default)]
pub struct MeshResourceProviderState {
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
mesh_entities: HashMap<Handle<Mesh>, MeshEntities>,
}

pub fn mesh_resource_provider_system(
mut state: Local<MeshResourceProviderState>,
render_resource_context: Res<Box<dyn RenderResourceContext>>,
meshes: Res<Assets<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 render_resource_context = &**render_resource_context;
Expand Down Expand Up @@ -383,39 +393,69 @@ pub fn mesh_resource_provider_system(
)),
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
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) {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
// TODO: don't allocate a new vertex buffer descriptor for every entity
render_pipeline.specialization.vertex_buffer_descriptor =
mesh.get_vertex_buffer_descriptor();
render_pipeline.specialization.index_format = mesh
.indices()
.map(|i| i.into())
.unwrap_or(IndexFormat::Uint32);
}
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);
}
}
}

if let Some(RenderResourceId::Buffer(index_buffer_resource)) =
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
{
// set index buffer into binding
render_pipelines
.bindings
.set_index_buffer(index_buffer_resource);
}
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() {
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
// TODO: don't allocate a new vertex buffer descriptor for every entity
render_pipeline.specialization.vertex_buffer_descriptor =
mesh.get_vertex_buffer_descriptor();
render_pipeline.specialization.index_format = mesh
.indices()
.map(|i| i.into())
.unwrap_or(IndexFormat::Uint32);
}

if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) =
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
{
// set index buffer into binding
render_pipelines.bindings.vertex_attribute_buffer =
Some(vertex_attribute_buffer_resource);
}
}
if let Some(RenderResourceId::Buffer(index_buffer_resource)) =
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
{
// set index buffer into binding
render_pipelines
.bindings
.set_index_buffer(index_buffer_resource);
}

if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) =
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
{
// set index buffer into binding
render_pipelines.bindings.vertex_attribute_buffer = Some(vertex_attribute_buffer_resource);
}
}
45 changes: 36 additions & 9 deletions crates/bevy_render/src/render_graph/nodes/pass_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ where
for render_command in draw.render_commands.iter() {
match render_command {
RenderCommand::SetPipeline { pipeline } => {
// TODO: Filter pipelines
if draw_state.is_pipeline_set(pipeline.clone_weak()) {
continue;
}
render_pass.set_pipeline(pipeline);
let descriptor = pipelines.get(pipeline).unwrap();
draw_state.set_pipeline(pipeline, descriptor);
Expand Down Expand Up @@ -290,18 +292,27 @@ where
offset,
slot,
} => {
if draw_state.is_vertex_buffer_set(*slot, *buffer, *offset) {
continue;
}
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 } => {
if draw_state.is_index_buffer_set(*buffer, *offset) {
continue;
}
render_pass.set_index_buffer(*buffer, *offset);
draw_state.set_index_buffer(*buffer)
draw_state.set_index_buffer(*buffer, *offset)
}
RenderCommand::SetBindGroup {
index,
bind_group,
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 layout = pipeline.get_layout().unwrap();
let bind_group_descriptor = layout.get_bind_group(*index).unwrap();
Expand Down Expand Up @@ -329,21 +340,33 @@ where
struct DrawState {
pipeline: Option<Handle<PipelineDescriptor>>,
bind_groups: Vec<Option<BindGroupId>>,
vertex_buffers: Vec<Option<BufferId>>,
index_buffer: Option<BufferId>,
vertex_buffers: Vec<Option<(BufferId, u64)>>,
index_buffer: Option<(BufferId, u64)>,
}

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

pub fn set_vertex_buffer(&mut self, index: u32, buffer: BufferId) {
self.vertex_buffers[index as usize] = Some(buffer);
pub fn is_bind_group_set(&self, index: u32, bind_group: BindGroupId) -> bool {
self.bind_groups[index as usize] == Some(bind_group)
}

pub fn set_vertex_buffer(&mut self, index: u32, buffer: BufferId, offset: u64) {
self.vertex_buffers[index as usize] = Some((buffer, offset));
}

pub fn set_index_buffer(&mut self, buffer: BufferId) {
self.index_buffer = Some(buffer);
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 {
Expand All @@ -355,6 +378,10 @@ impl DrawState {
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(
&mut self,
handle: &Handle<PipelineDescriptor>,
Expand Down
Loading

0 comments on commit b5ffab7

Please sign in to comment.