Optimize Text rendering / SharedBuffers (#972)
optimize Text rendering / SharedBuffers
This commit is contained in:
		
							parent
							
								
									4833c2a7f4
								
							
						
					
					
						commit
						ccb31bc949
					
				@ -1,7 +1,7 @@
 | 
				
			|||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    pipeline::{PipelineCompiler, PipelineDescriptor, PipelineLayout, PipelineSpecialization},
 | 
					    pipeline::{PipelineCompiler, PipelineDescriptor, PipelineLayout, PipelineSpecialization},
 | 
				
			||||||
    renderer::{
 | 
					    renderer::{
 | 
				
			||||||
        BindGroup, BindGroupId, BufferId, BufferUsage, RenderResource, RenderResourceBinding,
 | 
					        BindGroup, BindGroupId, BufferId, RenderResource, RenderResourceBinding,
 | 
				
			||||||
        RenderResourceBindings, RenderResourceContext, SharedBuffers,
 | 
					        RenderResourceBindings, RenderResourceContext, SharedBuffers,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    shader::Shader,
 | 
					    shader::Shader,
 | 
				
			||||||
@ -125,7 +125,7 @@ pub struct DrawContext<'a> {
 | 
				
			|||||||
    pub shaders: ResMut<'a, Assets<Shader>>,
 | 
					    pub shaders: ResMut<'a, Assets<Shader>>,
 | 
				
			||||||
    pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
 | 
					    pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
 | 
				
			||||||
    pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
 | 
					    pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
 | 
				
			||||||
    pub shared_buffers: Res<'a, SharedBuffers>,
 | 
					    pub shared_buffers: ResMut<'a, SharedBuffers>,
 | 
				
			||||||
    #[system_param(ignore)]
 | 
					    #[system_param(ignore)]
 | 
				
			||||||
    pub current_pipeline: Option<Handle<PipelineDescriptor>>,
 | 
					    pub current_pipeline: Option<Handle<PipelineDescriptor>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -135,19 +135,11 @@ pub struct FetchDrawContext;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl<'a> DrawContext<'a> {
 | 
					impl<'a> DrawContext<'a> {
 | 
				
			||||||
    pub fn get_uniform_buffer<T: RenderResource>(
 | 
					    pub fn get_uniform_buffer<T: RenderResource>(
 | 
				
			||||||
        &self,
 | 
					        &mut self,
 | 
				
			||||||
        render_resource: &T,
 | 
					        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> {
 | 
					    ) -> Result<RenderResourceBinding, DrawError> {
 | 
				
			||||||
        self.shared_buffers
 | 
					        self.shared_buffers
 | 
				
			||||||
            .get_buffer(render_resource, buffer_usage)
 | 
					            .get_uniform_buffer(&**self.render_resource_context, render_resource)
 | 
				
			||||||
            .ok_or(DrawError::BufferAllocationFailure)
 | 
					            .ok_or(DrawError::BufferAllocationFailure)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,8 +16,7 @@ impl Node for SharedBuffersNode {
 | 
				
			|||||||
        _input: &ResourceSlots,
 | 
					        _input: &ResourceSlots,
 | 
				
			||||||
        _output: &mut ResourceSlots,
 | 
					        _output: &mut ResourceSlots,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        let shared_buffers = resources.get::<SharedBuffers>().unwrap();
 | 
					        let mut shared_buffers = resources.get_mut::<SharedBuffers>().unwrap();
 | 
				
			||||||
        let mut command_queue = shared_buffers.reset_command_queue();
 | 
					        shared_buffers.apply(render_context);
 | 
				
			||||||
        command_queue.execute(render_context);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,77 +1,106 @@
 | 
				
			|||||||
use super::{BufferId, BufferInfo, RenderResource, RenderResourceBinding};
 | 
					use super::{BufferId, BufferInfo, RenderResource, RenderResourceBinding};
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    render_graph::CommandQueue,
 | 
					    render_graph::CommandQueue,
 | 
				
			||||||
    renderer::{BufferUsage, RenderResourceContext},
 | 
					    renderer::{BufferUsage, RenderContext, RenderResourceContext},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use bevy_ecs::Res;
 | 
					use bevy_ecs::{Res, ResMut};
 | 
				
			||||||
use parking_lot::RwLock;
 | 
					 | 
				
			||||||
use std::sync::Arc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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 {
 | 
					pub struct SharedBuffers {
 | 
				
			||||||
    render_resource_context: Box<dyn RenderResourceContext>,
 | 
					    staging_buffer: Option<BufferId>,
 | 
				
			||||||
    buffers: Arc<RwLock<Vec<BufferId>>>,
 | 
					    uniform_buffer: Option<BufferId>,
 | 
				
			||||||
    command_queue: Arc<RwLock<CommandQueue>>,
 | 
					    buffers_to_free: Vec<BufferId>,
 | 
				
			||||||
 | 
					    buffer_size: usize,
 | 
				
			||||||
 | 
					    initial_size: usize,
 | 
				
			||||||
 | 
					    current_offset: usize,
 | 
				
			||||||
 | 
					    command_queue: CommandQueue,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SharedBuffers {
 | 
					impl SharedBuffers {
 | 
				
			||||||
    pub fn new(render_resource_context: Box<dyn RenderResourceContext>) -> Self {
 | 
					    pub fn new(initial_size: usize) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            render_resource_context,
 | 
					            staging_buffer: None,
 | 
				
			||||||
            buffers: Default::default(),
 | 
					            uniform_buffer: None,
 | 
				
			||||||
 | 
					            buffer_size: 0,
 | 
				
			||||||
 | 
					            current_offset: 0,
 | 
				
			||||||
 | 
					            initial_size,
 | 
				
			||||||
 | 
					            buffers_to_free: Default::default(),
 | 
				
			||||||
            command_queue: Default::default(),
 | 
					            command_queue: Default::default(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn get_buffer<T: RenderResource>(
 | 
					    pub fn grow(
 | 
				
			||||||
        &self,
 | 
					        &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,
 | 
					        render_resource: &T,
 | 
				
			||||||
        buffer_usage: BufferUsage,
 | 
					 | 
				
			||||||
    ) -> Option<RenderResourceBinding> {
 | 
					    ) -> Option<RenderResourceBinding> {
 | 
				
			||||||
        if let Some(size) = render_resource.buffer_byte_len() {
 | 
					        if let Some(size) = render_resource.buffer_byte_len() {
 | 
				
			||||||
            let aligned_size = self
 | 
					            // TODO: overlap alignment if/when possible
 | 
				
			||||||
                .render_resource_context
 | 
					            let aligned_size = render_resource_context.get_aligned_uniform_size(size, true);
 | 
				
			||||||
                .get_aligned_uniform_size(size, false);
 | 
					            let mut new_offset = self.current_offset + aligned_size;
 | 
				
			||||||
            // PERF: this buffer will be slow
 | 
					            if new_offset > self.buffer_size {
 | 
				
			||||||
            let staging_buffer = self.render_resource_context.create_buffer(BufferInfo {
 | 
					                self.grow(render_resource_context, aligned_size);
 | 
				
			||||||
                size: aligned_size,
 | 
					                new_offset = aligned_size;
 | 
				
			||||||
                buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
 | 
					            }
 | 
				
			||||||
                mapped_at_creation: true,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            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,
 | 
					                staging_buffer,
 | 
				
			||||||
                0..size as u64,
 | 
					                range.clone(),
 | 
				
			||||||
                &mut |data, _renderer| {
 | 
					                &mut |data, _renderer| {
 | 
				
			||||||
                    render_resource.write_buffer_bytes(data);
 | 
					                    render_resource.write_buffer_bytes(data);
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.render_resource_context.unmap_buffer(staging_buffer);
 | 
					            self.command_queue.copy_buffer_to_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(
 | 
					 | 
				
			||||||
                staging_buffer,
 | 
					                staging_buffer,
 | 
				
			||||||
                0,
 | 
					                self.current_offset as u64,
 | 
				
			||||||
                destination_buffer,
 | 
					                uniform_buffer,
 | 
				
			||||||
                0,
 | 
					                self.current_offset as u64,
 | 
				
			||||||
                size as u64,
 | 
					                aligned_size as u64,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let mut buffers = self.buffers.write();
 | 
					            self.current_offset = new_offset;
 | 
				
			||||||
            buffers.push(staging_buffer);
 | 
					 | 
				
			||||||
            buffers.push(destination_buffer);
 | 
					 | 
				
			||||||
            Some(RenderResourceBinding::Buffer {
 | 
					            Some(RenderResourceBinding::Buffer {
 | 
				
			||||||
                buffer: destination_buffer,
 | 
					                buffer: uniform_buffer,
 | 
				
			||||||
                range: 0..aligned_size as u64,
 | 
					                range,
 | 
				
			||||||
                dynamic_index: None,
 | 
					                dynamic_index: None,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@ -79,21 +108,29 @@ impl SharedBuffers {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: remove this when this actually uses shared buffers
 | 
					    pub fn update(&mut self, render_resource_context: &dyn RenderResourceContext) {
 | 
				
			||||||
    pub fn free_buffers(&self) {
 | 
					        self.current_offset = 0;
 | 
				
			||||||
        let mut buffers = self.buffers.write();
 | 
					        for buffer in self.buffers_to_free.drain(..) {
 | 
				
			||||||
        for buffer in buffers.drain(..) {
 | 
					            render_resource_context.remove_buffer(buffer)
 | 
				
			||||||
            self.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 {
 | 
					    pub fn apply(&mut self, render_context: &mut dyn RenderContext) {
 | 
				
			||||||
        let mut command_queue = self.command_queue.write();
 | 
					        if let Some(staging_buffer) = self.staging_buffer {
 | 
				
			||||||
        std::mem::take(&mut *command_queue)
 | 
					            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 shared_buffers_update_system(
 | 
				
			||||||
pub fn free_shared_buffers_system(shared_buffers: Res<SharedBuffers>) {
 | 
					    mut shared_buffers: ResMut<SharedBuffers>,
 | 
				
			||||||
    shared_buffers.free_buffers();
 | 
					    render_resource_context: Res<Box<dyn RenderResourceContext>>,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    shared_buffers.update(&**render_resource_context);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,10 +5,7 @@ use bevy_render::{
 | 
				
			|||||||
    mesh,
 | 
					    mesh,
 | 
				
			||||||
    pipeline::{PipelineSpecialization, VertexBufferDescriptor},
 | 
					    pipeline::{PipelineSpecialization, VertexBufferDescriptor},
 | 
				
			||||||
    prelude::Msaa,
 | 
					    prelude::Msaa,
 | 
				
			||||||
    renderer::{
 | 
					    renderer::{AssetRenderResourceBindings, BindGroup, RenderResourceBindings, RenderResourceId},
 | 
				
			||||||
        AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings,
 | 
					 | 
				
			||||||
        RenderResourceId,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use bevy_sprite::TextureAtlasSprite;
 | 
					use bevy_sprite::TextureAtlasSprite;
 | 
				
			||||||
use glyph_brush_layout::{HorizontalAlign, VerticalAlign};
 | 
					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 = Mat4::from_translation(self.position + tv.position.extend(0.));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let transform_buffer = context
 | 
					            let transform_buffer = context.get_uniform_buffer(&transform).unwrap();
 | 
				
			||||||
                .shared_buffers
 | 
					            let sprite_buffer = context.get_uniform_buffer(&sprite).unwrap();
 | 
				
			||||||
                .get_buffer(&transform, BufferUsage::UNIFORM)
 | 
					 | 
				
			||||||
                .unwrap();
 | 
					 | 
				
			||||||
            let sprite_buffer = context
 | 
					 | 
				
			||||||
                .shared_buffers
 | 
					 | 
				
			||||||
                .get_buffer(&sprite, BufferUsage::UNIFORM)
 | 
					 | 
				
			||||||
                .unwrap();
 | 
					 | 
				
			||||||
            let sprite_bind_group = BindGroup::build()
 | 
					            let sprite_bind_group = BindGroup::build()
 | 
				
			||||||
                .add_binding(0, transform_buffer)
 | 
					                .add_binding(0, transform_buffer)
 | 
				
			||||||
                .add_binding(1, sprite_buffer)
 | 
					                .add_binding(1, sprite_buffer)
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ pub use wgpu_resources::*;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use bevy_app::prelude::*;
 | 
					use bevy_app::prelude::*;
 | 
				
			||||||
use bevy_ecs::{Resources, World};
 | 
					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;
 | 
					use renderer::WgpuRenderResourceContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
@ -22,7 +22,10 @@ impl Plugin for WgpuPlugin {
 | 
				
			|||||||
    fn build(&self, app: &mut AppBuilder) {
 | 
					    fn build(&self, app: &mut AppBuilder) {
 | 
				
			||||||
        let render_system = get_wgpu_render_system(app.resources_mut());
 | 
					        let render_system = get_wgpu_render_system(app.resources_mut());
 | 
				
			||||||
        app.add_system_to_stage(bevy_render::stage::RENDER, render_system)
 | 
					        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);
 | 
					        .unwrap_or_else(WgpuOptions::default);
 | 
				
			||||||
    let mut wgpu_renderer = future::block_on(WgpuRenderer::new(options));
 | 
					    let mut wgpu_renderer = future::block_on(WgpuRenderer::new(options));
 | 
				
			||||||
    let resource_context = WgpuRenderResourceContext::new(wgpu_renderer.device.clone());
 | 
					    let resource_context = WgpuRenderResourceContext::new(wgpu_renderer.device.clone());
 | 
				
			||||||
    resources.insert::<Box<dyn RenderResourceContext>>(Box::new(resource_context.clone()));
 | 
					    resources.insert::<Box<dyn RenderResourceContext>>(Box::new(resource_context));
 | 
				
			||||||
    resources.insert(SharedBuffers::new(Box::new(resource_context)));
 | 
					    resources.insert(SharedBuffers::new(4096));
 | 
				
			||||||
    move |world, resources| {
 | 
					    move |world, resources| {
 | 
				
			||||||
        wgpu_renderer.update(world, resources);
 | 
					        wgpu_renderer.update(world, resources);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user