Avoid cloning texture Arcs for every draw call

This commit is contained in:
Tad Hardesty 2018-09-10 22:01:26 -07:00
parent e1a4bbc638
commit d202872280

View File

@ -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<T> = Result<T, RendererError>;
@ -43,7 +44,42 @@ impl From<gfx::CombinedError> 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<R>>::Data,)*
}
impl<'a, R: Resources> gfx::pso::PipelineData<R> for BorrowedData<'a, R> {
type Meta = pipe::Meta;
fn bake_to(&self, out: &mut RawDataSet<R>, meta: &Self::Meta, man: &mut gfx::handle::Manager<R>, access: &mut AccessInfo<R>) {
$(meta.$field.bind_to(out, &self.$field, man, access);)*
}
}
}
}
}
extended_defines! {
pipeline pipe {
vertex_buffer: gfx::VertexBuffer<ImDrawVert> = (),
matrix: gfx::Global<[[f32; 4]; 4]> = "matrix",
@ -100,6 +136,7 @@ pub struct Renderer<R: Resources> {
bundle: Bundle<R, pipe::Data<R>>,
index_buffer: Buffer<R, u16>,
textures: Textures<Texture<R>>,
backup_texture: Texture<R>,
}
impl<R: Resources> Renderer<R> {
@ -140,23 +177,6 @@ impl<R: Resources> Renderer<R> {
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<R: Resources> Renderer<R> {
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<R, gfx::format::Rgba8>) {
self.bundle.data.out = out;
self.bundle.out = out;
}
pub fn textures(&mut self) -> &mut Textures<Texture<R>> {
@ -198,7 +224,7 @@ impl<R: Resources> Renderer<R> {
(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<R: Resources> Renderer<R> {
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<R: Resources> Renderer<R> {
encoder: &mut Encoder<R, C>,
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<R: Resources> Renderer<R> {
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<R: Resources> Renderer<R> {
.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<R: Resources> Renderer<R> {
encoder: &mut Encoder<R, C>,
vtx_buffer: &[ImDrawVert],
) -> RendererResult<()> {
if self.bundle.data.vertex_buffer.len() < vtx_buffer.len() {
self.bundle.data.vertex_buffer = factory.create_buffer::<ImDrawVert>(
if self.bundle.vertex_buffer.len() < vtx_buffer.len() {
self.bundle.vertex_buffer = factory.create_buffer::<ImDrawVert>(
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<F: Factory<R>, C: CommandBuffer<R>>(
&mut self,
@ -284,3 +315,10 @@ impl<R: Resources> Renderer<R> {
Ok(encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?)
}
}
struct Bundle<R: Resources, Data: PipelineData<R>> {
slice: Slice<R>,
pso: PipelineState<R, Data::Meta>,
vertex_buffer: Buffer<R, ImDrawVert>,
out: RenderTargetView<R, gfx::format::Rgba8>,
}