From d202872280fa085ee1e316dbb21cf37b135c8e16 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 10 Sep 2018 22:01:26 -0700 Subject: [PATCH] Avoid cloning texture Arcs for every draw call --- imgui-gfx-renderer/src/lib.rs | 102 +++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 178be63..8ed4e4d 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -6,7 +6,8 @@ use gfx::handle::{Buffer, RenderTargetView}; use gfx::memory::Bind; use gfx::texture::{FilterMethod, SamplerInfo, WrapMode}; use gfx::traits::FactoryExt; -use gfx::{Bundle, CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; +use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; +use gfx::pso::{PipelineData, PipelineState}; use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures}; pub type RendererResult = Result; @@ -43,7 +44,42 @@ impl From for RendererError { } } -gfx_defines!{ +// Based on gfx_defines! / gfx_pipeline!, to allow for not having to clone Arcs +// every draw call when selecting which texture is going to be shown. +macro_rules! extended_defines { + (pipeline $module:ident { $( $field:ident: $ty:ty = $value:expr, )* }) => { + #[allow(missing_docs)] + mod $module { + #[allow(unused_imports)] + use super::*; + #[allow(unused_imports)] + use super::gfx; + gfx_pipeline_inner!{ $( + $field: $ty, + )*} + + pub fn new() -> Init<'static> { + Init { + $( $field: $value, )* + } + } + + pub struct BorrowedData<'a, R: Resources> { + $(pub $field: &'a <$ty as DataBind>::Data,)* + } + + impl<'a, R: Resources> gfx::pso::PipelineData for BorrowedData<'a, R> { + type Meta = pipe::Meta; + + fn bake_to(&self, out: &mut RawDataSet, meta: &Self::Meta, man: &mut gfx::handle::Manager, access: &mut AccessInfo) { + $(meta.$field.bind_to(out, &self.$field, man, access);)* + } + } + } + } +} + +extended_defines! { pipeline pipe { vertex_buffer: gfx::VertexBuffer = (), matrix: gfx::Global<[[f32; 4]; 4]> = "matrix", @@ -100,6 +136,7 @@ pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, textures: Textures>, + backup_texture: Texture, } impl Renderer { @@ -140,23 +177,6 @@ impl Renderer { let mut textures = Textures::new(); imgui.set_texture_id(textures.insert(pair.clone())); - let data = pipe::Data { - vertex_buffer: vertex_buffer, - matrix: [ - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, -1.0, 0.0], - [-1.0, 1.0, 0.0, 1.0], - ], - tex: pair, - out: out, - scissor: Rect { - x: 0, - y: 0, - w: 0, - h: 0, - }, - }; let slice = Slice { start: 0, end: 0, @@ -165,14 +185,20 @@ impl Renderer { buffer: index_buffer.clone().into_index_buffer(factory), }; Ok(Renderer { - bundle: Bundle::new(slice, pso, data), + bundle: Bundle { + slice: slice, + pso: pso, + vertex_buffer: vertex_buffer, + out: out, + }, index_buffer: index_buffer, textures: textures, + backup_texture: pair, }) } pub fn update_render_target(&mut self, out: RenderTargetView) { - self.bundle.data.out = out; + self.bundle.out = out; } pub fn textures(&mut self) -> &mut Textures> { @@ -198,7 +224,7 @@ impl Renderer { (height * hidpi_factor) as f32, ); - self.bundle.data.matrix = [ + let matrix = [ [(2.0 / width) as f32, 0.0, 0.0, 0.0], [0.0, (2.0 / -height) as f32, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], @@ -208,7 +234,7 @@ impl Renderer { ui.render(|ui, mut draw_data| { draw_data.scale_clip_rects(ui.imgui().display_framebuffer_scale()); for draw_list in &draw_data { - self.render_draw_list(factory, encoder, &draw_list, fb_size)?; + self.render_draw_list(factory, encoder, &draw_list, fb_size, &matrix)?; } Ok(()) }) @@ -219,6 +245,7 @@ impl Renderer { encoder: &mut Encoder, draw_list: &DrawList<'a>, fb_size: (f32, f32), + matrix: &[[f32; 4]; 4], ) -> RendererResult<()> { let (fb_width, fb_height) = fb_size; @@ -227,13 +254,10 @@ impl Renderer { self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { - if let Some(tex) = self.textures.get(cmd.texture_id.into()) { - // cloning handles is okay, since they're Arcs internally - self.bundle.data.tex = tex.clone(); - } + let tex = self.textures.get(cmd.texture_id.into()).unwrap_or(&self.backup_texture); self.bundle.slice.end = self.bundle.slice.start + cmd.elem_count; - self.bundle.data.scissor = Rect { + let scissor = Rect { x: cmd.clip_rect.x.max(0.0).min(fb_width).round() as u16, y: cmd.clip_rect.y.max(0.0).min(fb_height).round() as u16, w: (cmd.clip_rect.z - cmd.clip_rect.x) @@ -245,7 +269,14 @@ impl Renderer { .min(fb_height) .round() as u16, }; - self.bundle.encode(encoder); + let data = pipe::BorrowedData { + vertex_buffer: &self.bundle.vertex_buffer, + matrix: matrix, + tex: tex, + out: &self.bundle.out, + scissor: &scissor, + }; + encoder.draw(&self.bundle.slice, &self.bundle.pso, &data); self.bundle.slice.start = self.bundle.slice.end; } Ok(()) @@ -256,15 +287,15 @@ impl Renderer { encoder: &mut Encoder, vtx_buffer: &[ImDrawVert], ) -> RendererResult<()> { - if self.bundle.data.vertex_buffer.len() < vtx_buffer.len() { - self.bundle.data.vertex_buffer = factory.create_buffer::( + if self.bundle.vertex_buffer.len() < vtx_buffer.len() { + self.bundle.vertex_buffer = factory.create_buffer::( vtx_buffer.len(), gfx::buffer::Role::Vertex, gfx::memory::Usage::Dynamic, Bind::empty(), )?; } - Ok(encoder.update_buffer(&self.bundle.data.vertex_buffer, vtx_buffer, 0)?) + Ok(encoder.update_buffer(&self.bundle.vertex_buffer, vtx_buffer, 0)?) } fn upload_index_buffer, C: CommandBuffer>( &mut self, @@ -284,3 +315,10 @@ impl Renderer { Ok(encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?) } } + +struct Bundle> { + slice: Slice, + pso: PipelineState, + vertex_buffer: Buffer, + out: RenderTargetView, +}