mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 15:14:50 +00:00
Optimize Text rendering / SharedBuffers (#972)
optimize Text rendering / SharedBuffers
This commit is contained in:
parent
4833c2a7f4
commit
ccb31bc949
5 changed files with 110 additions and 88 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
pipeline::{PipelineCompiler, PipelineDescriptor, PipelineLayout, PipelineSpecialization},
|
||||
renderer::{
|
||||
BindGroup, BindGroupId, BufferId, BufferUsage, RenderResource, RenderResourceBinding,
|
||||
BindGroup, BindGroupId, BufferId, RenderResource, RenderResourceBinding,
|
||||
RenderResourceBindings, RenderResourceContext, SharedBuffers,
|
||||
},
|
||||
shader::Shader,
|
||||
|
@ -125,7 +125,7 @@ pub struct DrawContext<'a> {
|
|||
pub shaders: ResMut<'a, Assets<Shader>>,
|
||||
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
|
||||
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
|
||||
pub shared_buffers: Res<'a, SharedBuffers>,
|
||||
pub shared_buffers: ResMut<'a, SharedBuffers>,
|
||||
#[system_param(ignore)]
|
||||
pub current_pipeline: Option<Handle<PipelineDescriptor>>,
|
||||
}
|
||||
|
@ -135,19 +135,11 @@ pub struct FetchDrawContext;
|
|||
|
||||
impl<'a> DrawContext<'a> {
|
||||
pub fn get_uniform_buffer<T: RenderResource>(
|
||||
&self,
|
||||
&mut self,
|
||||
render_resource: &T,
|
||||
) -> Result<RenderResourceBinding, DrawError> {
|
||||
self.get_buffer(render_resource, BufferUsage::UNIFORM)
|
||||
}
|
||||
|
||||
pub fn get_buffer<T: RenderResource>(
|
||||
&self,
|
||||
render_resource: &T,
|
||||
buffer_usage: BufferUsage,
|
||||
) -> Result<RenderResourceBinding, DrawError> {
|
||||
self.shared_buffers
|
||||
.get_buffer(render_resource, buffer_usage)
|
||||
.get_uniform_buffer(&**self.render_resource_context, render_resource)
|
||||
.ok_or(DrawError::BufferAllocationFailure)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,7 @@ impl Node for SharedBuffersNode {
|
|||
_input: &ResourceSlots,
|
||||
_output: &mut ResourceSlots,
|
||||
) {
|
||||
let shared_buffers = resources.get::<SharedBuffers>().unwrap();
|
||||
let mut command_queue = shared_buffers.reset_command_queue();
|
||||
command_queue.execute(render_context);
|
||||
let mut shared_buffers = resources.get_mut::<SharedBuffers>().unwrap();
|
||||
shared_buffers.apply(render_context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +1,106 @@
|
|||
use super::{BufferId, BufferInfo, RenderResource, RenderResourceBinding};
|
||||
use crate::{
|
||||
render_graph::CommandQueue,
|
||||
renderer::{BufferUsage, RenderResourceContext},
|
||||
renderer::{BufferUsage, RenderContext, RenderResourceContext},
|
||||
};
|
||||
use bevy_ecs::Res;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
use bevy_ecs::{Res, ResMut};
|
||||
|
||||
// TODO: Instead of allocating small "exact size" buffers each frame, this should use multiple large shared buffers and probably
|
||||
// a long-living "cpu mapped" staging buffer. Im punting that for now because I don't know the best way to use wgpu's new async
|
||||
// buffer mapping yet.
|
||||
pub struct SharedBuffers {
|
||||
render_resource_context: Box<dyn RenderResourceContext>,
|
||||
buffers: Arc<RwLock<Vec<BufferId>>>,
|
||||
command_queue: Arc<RwLock<CommandQueue>>,
|
||||
staging_buffer: Option<BufferId>,
|
||||
uniform_buffer: Option<BufferId>,
|
||||
buffers_to_free: Vec<BufferId>,
|
||||
buffer_size: usize,
|
||||
initial_size: usize,
|
||||
current_offset: usize,
|
||||
command_queue: CommandQueue,
|
||||
}
|
||||
|
||||
impl SharedBuffers {
|
||||
pub fn new(render_resource_context: Box<dyn RenderResourceContext>) -> Self {
|
||||
pub fn new(initial_size: usize) -> Self {
|
||||
Self {
|
||||
render_resource_context,
|
||||
buffers: Default::default(),
|
||||
staging_buffer: None,
|
||||
uniform_buffer: None,
|
||||
buffer_size: 0,
|
||||
current_offset: 0,
|
||||
initial_size,
|
||||
buffers_to_free: Default::default(),
|
||||
command_queue: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_buffer<T: RenderResource>(
|
||||
&self,
|
||||
pub fn grow(
|
||||
&mut self,
|
||||
render_resource_context: &dyn RenderResourceContext,
|
||||
required_space: usize,
|
||||
) {
|
||||
while self.buffer_size < self.current_offset + required_space {
|
||||
self.buffer_size = if self.buffer_size == 0 {
|
||||
self.initial_size
|
||||
} else {
|
||||
self.buffer_size * 2
|
||||
};
|
||||
}
|
||||
|
||||
self.current_offset = 0;
|
||||
|
||||
if let Some(staging_buffer) = self.staging_buffer.take() {
|
||||
render_resource_context.unmap_buffer(staging_buffer);
|
||||
self.buffers_to_free.push(staging_buffer);
|
||||
}
|
||||
|
||||
if let Some(uniform_buffer) = self.uniform_buffer.take() {
|
||||
self.buffers_to_free.push(uniform_buffer);
|
||||
}
|
||||
|
||||
self.staging_buffer = Some(render_resource_context.create_buffer(BufferInfo {
|
||||
size: self.buffer_size,
|
||||
buffer_usage: BufferUsage::MAP_WRITE | BufferUsage::COPY_SRC,
|
||||
mapped_at_creation: true,
|
||||
}));
|
||||
self.uniform_buffer = Some(render_resource_context.create_buffer(BufferInfo {
|
||||
size: self.buffer_size,
|
||||
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
|
||||
mapped_at_creation: false,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn get_uniform_buffer<T: RenderResource>(
|
||||
&mut self,
|
||||
render_resource_context: &dyn RenderResourceContext,
|
||||
render_resource: &T,
|
||||
buffer_usage: BufferUsage,
|
||||
) -> Option<RenderResourceBinding> {
|
||||
if let Some(size) = render_resource.buffer_byte_len() {
|
||||
let aligned_size = self
|
||||
.render_resource_context
|
||||
.get_aligned_uniform_size(size, false);
|
||||
// PERF: this buffer will be slow
|
||||
let staging_buffer = self.render_resource_context.create_buffer(BufferInfo {
|
||||
size: aligned_size,
|
||||
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
|
||||
mapped_at_creation: true,
|
||||
});
|
||||
// TODO: overlap alignment if/when possible
|
||||
let aligned_size = render_resource_context.get_aligned_uniform_size(size, true);
|
||||
let mut new_offset = self.current_offset + aligned_size;
|
||||
if new_offset > self.buffer_size {
|
||||
self.grow(render_resource_context, aligned_size);
|
||||
new_offset = aligned_size;
|
||||
}
|
||||
|
||||
self.render_resource_context.write_mapped_buffer(
|
||||
let range = self.current_offset as u64..new_offset as u64;
|
||||
let staging_buffer = self.staging_buffer.unwrap();
|
||||
let uniform_buffer = self.uniform_buffer.unwrap();
|
||||
render_resource_context.write_mapped_buffer(
|
||||
staging_buffer,
|
||||
0..size as u64,
|
||||
range.clone(),
|
||||
&mut |data, _renderer| {
|
||||
render_resource.write_buffer_bytes(data);
|
||||
},
|
||||
);
|
||||
|
||||
self.render_resource_context.unmap_buffer(staging_buffer);
|
||||
|
||||
let destination_buffer = self.render_resource_context.create_buffer(BufferInfo {
|
||||
size: aligned_size,
|
||||
buffer_usage: BufferUsage::COPY_DST | buffer_usage,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let mut command_queue = self.command_queue.write();
|
||||
command_queue.copy_buffer_to_buffer(
|
||||
self.command_queue.copy_buffer_to_buffer(
|
||||
staging_buffer,
|
||||
0,
|
||||
destination_buffer,
|
||||
0,
|
||||
size as u64,
|
||||
self.current_offset as u64,
|
||||
uniform_buffer,
|
||||
self.current_offset as u64,
|
||||
aligned_size as u64,
|
||||
);
|
||||
|
||||
let mut buffers = self.buffers.write();
|
||||
buffers.push(staging_buffer);
|
||||
buffers.push(destination_buffer);
|
||||
self.current_offset = new_offset;
|
||||
Some(RenderResourceBinding::Buffer {
|
||||
buffer: destination_buffer,
|
||||
range: 0..aligned_size as u64,
|
||||
buffer: uniform_buffer,
|
||||
range,
|
||||
dynamic_index: None,
|
||||
})
|
||||
} else {
|
||||
|
@ -79,21 +108,29 @@ impl SharedBuffers {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: remove this when this actually uses shared buffers
|
||||
pub fn free_buffers(&self) {
|
||||
let mut buffers = self.buffers.write();
|
||||
for buffer in buffers.drain(..) {
|
||||
self.render_resource_context.remove_buffer(buffer)
|
||||
pub fn update(&mut self, render_resource_context: &dyn RenderResourceContext) {
|
||||
self.current_offset = 0;
|
||||
for buffer in self.buffers_to_free.drain(..) {
|
||||
render_resource_context.remove_buffer(buffer)
|
||||
}
|
||||
|
||||
if let Some(staging_buffer) = self.staging_buffer {
|
||||
render_resource_context.map_buffer(staging_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_command_queue(&self) -> CommandQueue {
|
||||
let mut command_queue = self.command_queue.write();
|
||||
std::mem::take(&mut *command_queue)
|
||||
pub fn apply(&mut self, render_context: &mut dyn RenderContext) {
|
||||
if let Some(staging_buffer) = self.staging_buffer {
|
||||
render_context.resources().unmap_buffer(staging_buffer);
|
||||
}
|
||||
let mut command_queue = std::mem::take(&mut self.command_queue);
|
||||
command_queue.execute(render_context);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this when this actually uses shared buffers
|
||||
pub fn free_shared_buffers_system(shared_buffers: Res<SharedBuffers>) {
|
||||
shared_buffers.free_buffers();
|
||||
pub fn shared_buffers_update_system(
|
||||
mut shared_buffers: ResMut<SharedBuffers>,
|
||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||
) {
|
||||
shared_buffers.update(&**render_resource_context);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,7 @@ use bevy_render::{
|
|||
mesh,
|
||||
pipeline::{PipelineSpecialization, VertexBufferDescriptor},
|
||||
prelude::Msaa,
|
||||
renderer::{
|
||||
AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings,
|
||||
RenderResourceId,
|
||||
},
|
||||
renderer::{AssetRenderResourceBindings, BindGroup, RenderResourceBindings, RenderResourceId},
|
||||
};
|
||||
use bevy_sprite::TextureAtlasSprite;
|
||||
use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
|
||||
|
@ -108,14 +105,8 @@ impl<'a> Drawable for DrawableText<'a> {
|
|||
|
||||
let transform = Mat4::from_translation(self.position + tv.position.extend(0.));
|
||||
|
||||
let transform_buffer = context
|
||||
.shared_buffers
|
||||
.get_buffer(&transform, BufferUsage::UNIFORM)
|
||||
.unwrap();
|
||||
let sprite_buffer = context
|
||||
.shared_buffers
|
||||
.get_buffer(&sprite, BufferUsage::UNIFORM)
|
||||
.unwrap();
|
||||
let transform_buffer = context.get_uniform_buffer(&transform).unwrap();
|
||||
let sprite_buffer = context.get_uniform_buffer(&sprite).unwrap();
|
||||
let sprite_bind_group = BindGroup::build()
|
||||
.add_binding(0, transform_buffer)
|
||||
.add_binding(1, sprite_buffer)
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use wgpu_resources::*;
|
|||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::{Resources, World};
|
||||
use bevy_render::renderer::{free_shared_buffers_system, RenderResourceContext, SharedBuffers};
|
||||
use bevy_render::renderer::{shared_buffers_update_system, RenderResourceContext, SharedBuffers};
|
||||
use renderer::WgpuRenderResourceContext;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -22,7 +22,10 @@ impl Plugin for WgpuPlugin {
|
|||
fn build(&self, app: &mut AppBuilder) {
|
||||
let render_system = get_wgpu_render_system(app.resources_mut());
|
||||
app.add_system_to_stage(bevy_render::stage::RENDER, render_system)
|
||||
.add_system_to_stage(bevy_render::stage::POST_RENDER, free_shared_buffers_system);
|
||||
.add_system_to_stage(
|
||||
bevy_render::stage::POST_RENDER,
|
||||
shared_buffers_update_system,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,8 +35,8 @@ pub fn get_wgpu_render_system(resources: &mut Resources) -> impl FnMut(&mut Worl
|
|||
.unwrap_or_else(WgpuOptions::default);
|
||||
let mut wgpu_renderer = future::block_on(WgpuRenderer::new(options));
|
||||
let resource_context = WgpuRenderResourceContext::new(wgpu_renderer.device.clone());
|
||||
resources.insert::<Box<dyn RenderResourceContext>>(Box::new(resource_context.clone()));
|
||||
resources.insert(SharedBuffers::new(Box::new(resource_context)));
|
||||
resources.insert::<Box<dyn RenderResourceContext>>(Box::new(resource_context));
|
||||
resources.insert(SharedBuffers::new(4096));
|
||||
move |world, resources| {
|
||||
wgpu_renderer.update(world, resources);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue