From 456258524ee93081b2d5044ff6a76413659f32bd Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 10 Sep 2018 00:40:02 -0700 Subject: [PATCH 1/8] Add simplest possible image support for gfx renderer --- imgui-gfx-renderer/src/lib.rs | 32 +++++++++++-- src/image.rs | 90 +++++++++++++++++++++++++++++++++++ src/lib.rs | 12 +++++ 3 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/image.rs diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 0d7e74e..c6c135e 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -7,7 +7,9 @@ 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 imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui}; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, ImTexture}; + +use std::collections::HashMap; pub type RendererResult = Result; @@ -94,9 +96,13 @@ impl Shaders { } } +pub type Texture = (gfx::handle::ShaderResourceView, gfx::handle::Sampler); + pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, + textures: HashMap>, + next_texture: usize, } impl Renderer { @@ -131,9 +137,13 @@ impl Renderer { &[handle.pixels], ) })?; - // TODO: set texture id in imgui let sampler = factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp)); + let pair = (texture, sampler); + let mut textures = HashMap::new(); + textures.insert(0, pair.clone()); + imgui.set_texture_id(0); + let data = pipe::Data { vertex_buffer: vertex_buffer, matrix: [ @@ -142,7 +152,7 @@ impl Renderer { [0.0, 0.0, -1.0, 0.0], [-1.0, 1.0, 0.0, 1.0], ], - tex: (texture, sampler), + tex: pair, out: out, scissor: Rect { x: 0, @@ -161,11 +171,22 @@ impl Renderer { Ok(Renderer { bundle: Bundle::new(slice, pso, data), index_buffer: index_buffer, + textures, + next_texture: 1, }) } + pub fn update_render_target(&mut self, out: RenderTargetView) { self.bundle.data.out = out; } + + pub fn add_texture(&mut self, texture: Texture) -> ImTexture { + let id = self.next_texture; + self.textures.insert(id, texture); + self.next_texture += 1; + id + } + pub fn render<'a, F: Factory, C: CommandBuffer>( &mut self, ui: Ui<'a>, @@ -214,7 +235,10 @@ impl Renderer { self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { - // TODO: check cmd.texture_id + if let Some(tex) = self.textures.get(&(cmd.texture_id as usize)) { + // cloning handles is okay, since they're Arcs internally + self.bundle.data.tex = tex.clone(); + } self.bundle.slice.end = self.bundle.slice.start + cmd.elem_count; self.bundle.data.scissor = Rect { diff --git a/src/image.rs b/src/image.rs new file mode 100644 index 0000000..b8cf978 --- /dev/null +++ b/src/image.rs @@ -0,0 +1,90 @@ +use super::{ImVec2, ImVec4, Ui}; +use std::marker::PhantomData; +use std::os::raw::c_void; +use sys; + +pub type ImTexture = usize; + +/// Represent an image about to be drawn. +/// See [`Ui::image`]. +/// +/// Create your image using the builder pattern then [`Image::build`] it. +pub struct Image<'ui> { + /// we use Result to allow postponing any construction errors to the build call + texture_id: ImTexture, + size: ImVec2, + uv0: ImVec2, + uv1: ImVec2, + tint_col: ImVec4, + border_col: ImVec4, + _phantom: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> Image<'ui> { + pub fn new(_: &Ui<'ui>, texture_id: usize, size: S) -> Self + where + S: Into, + { + const DEFAULT_UV0: ImVec2 = ImVec2 { x: 0.0, y: 0.0 }; + const DEFAULT_UV1: ImVec2 = ImVec2 { x: 1.0, y: 1.0 }; + const DEFAULT_TINT_COL: ImVec4 = ImVec4 { + x: 1.0, + y: 1.0, + z: 1.0, + w: 1.0, + }; + const DEFAULT_BORDER_COL: ImVec4 = ImVec4 { + x: 0.0, + y: 0.0, + z: 0.0, + w: 0.0, + }; + Image { + texture_id: texture_id, + size: size.into(), + uv0: DEFAULT_UV0, + uv1: DEFAULT_UV1, + tint_col: DEFAULT_TINT_COL, + border_col: DEFAULT_BORDER_COL, + _phantom: PhantomData, + } + } + /// Set size (default based on texture) + pub fn size>(mut self, size: T) -> Self { + self.size = size.into(); + self + } + /// Set uv0 (default `[0.0, 0.0]`) + pub fn uv0>(mut self, uv0: T) -> Self { + self.uv0 = uv0.into(); + self + } + /// Set uv1 (default `[1.0, 1.0]`) + pub fn uv1>(mut self, uv1: T) -> Self { + self.uv1 = uv1.into(); + self + } + /// Set tint color (default: no tint color) + pub fn tint_col>(mut self, tint_col: T) -> Self { + self.tint_col = tint_col.into(); + self + } + /// Set border color (default: no border) + pub fn border_col>(mut self, border_col: T) -> Self { + self.border_col = border_col.into(); + self + } + /// Draw image where the cursor currently is + pub fn build(self) { + unsafe { + sys::igImage( + self.texture_id as *mut c_void, + self.size, + self.uv0, + self.uv1, + self.tint_col, + self.border_col, + ); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e7887bc..4e9688f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub use drag::{ DragInt4, DragIntRange2, }; pub use fonts::{FontGlyphRange, ImFont, ImFontAtlas, ImFontConfig}; +pub use image::{ImTexture, Image}; pub use input::{ InputFloat, InputFloat2, InputFloat3, InputFloat4, InputInt, InputInt2, InputInt3, InputInt4, InputText, InputTextMultiline, @@ -45,6 +46,7 @@ mod child_frame; mod color_editors; mod drag; mod fonts; +mod image; mod input; mod menus; mod plothistogram; @@ -1301,6 +1303,16 @@ impl<'ui> Ui<'ui> { } } +// Image +impl<'ui> Ui<'ui> { + pub fn image(&self, texture: ImTexture, size: S) -> Image + where + S: Into, + { + Image::new(self, texture, size) + } +} + impl<'ui> Ui<'ui> { /// Calculate the size required for a given text string. /// From fb7ea044e204e9cc8a700720b91ea8438adefc63 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 10 Sep 2018 00:54:00 -0700 Subject: [PATCH 2/8] Split the ImTexture allocator into a generic struct --- imgui-gfx-renderer/src/lib.rs | 24 ++++++++--------------- src/image.rs | 36 +++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index c6c135e..d3aa98a 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -7,9 +7,7 @@ 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 imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, ImTexture}; - -use std::collections::HashMap; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures}; pub type RendererResult = Result; @@ -101,8 +99,7 @@ pub type Texture = (gfx::handle::ShaderResourceView, gfx::handle pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, - textures: HashMap>, - next_texture: usize, + textures: Textures>, } impl Renderer { @@ -140,9 +137,8 @@ impl Renderer { let sampler = factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp)); let pair = (texture, sampler); - let mut textures = HashMap::new(); - textures.insert(0, pair.clone()); - imgui.set_texture_id(0); + let mut textures = Textures::new(); + imgui.set_texture_id(textures.insert(pair.clone())); let data = pipe::Data { vertex_buffer: vertex_buffer, @@ -171,8 +167,7 @@ impl Renderer { Ok(Renderer { bundle: Bundle::new(slice, pso, data), index_buffer: index_buffer, - textures, - next_texture: 1, + textures: textures, }) } @@ -180,11 +175,8 @@ impl Renderer { self.bundle.data.out = out; } - pub fn add_texture(&mut self, texture: Texture) -> ImTexture { - let id = self.next_texture; - self.textures.insert(id, texture); - self.next_texture += 1; - id + pub fn textures(&mut self) -> &mut Textures> { + &mut self.textures } pub fn render<'a, F: Factory, C: CommandBuffer>( @@ -235,7 +227,7 @@ impl Renderer { self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { - if let Some(tex) = self.textures.get(&(cmd.texture_id as usize)) { + if let Some(tex) = self.textures.get(cmd.texture_id as usize) { // cloning handles is okay, since they're Arcs internally self.bundle.data.tex = tex.clone(); } diff --git a/src/image.rs b/src/image.rs index b8cf978..2326ef4 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,5 +1,6 @@ use super::{ImVec2, ImVec4, Ui}; use std::marker::PhantomData; +use std::collections::HashMap; use std::os::raw::c_void; use sys; @@ -88,3 +89,38 @@ impl<'ui> Image<'ui> { } } } + +/// Generic texture mapping for use by renderers. +pub struct Textures { + textures: HashMap, + next: usize, +} + +impl Textures { + pub fn new() -> Self { + Textures { + textures: HashMap::new(), + next: 0, + } + } + + pub fn insert(&mut self, texture: T) -> ImTexture { + let id = self.next; + self.textures.insert(id, texture); + self.next += 1; + id + } + + pub fn replace(&mut self, id: ImTexture, texture: T) { + assert!(self.textures.contains_key(&id)); + self.textures.insert(id, texture); + } + + pub fn remove(&mut self, id: ImTexture) { + self.textures.remove(&id); + } + + pub fn get(&self, id: ImTexture) -> Option<&T> { + self.textures.get(&id) + } +} diff --git a/src/lib.rs b/src/lib.rs index 4e9688f..55aa8e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ pub use drag::{ DragInt4, DragIntRange2, }; pub use fonts::{FontGlyphRange, ImFont, ImFontAtlas, ImFontConfig}; -pub use image::{ImTexture, Image}; +pub use image::{ImTexture, Image, Textures}; pub use input::{ InputFloat, InputFloat2, InputFloat3, InputFloat4, InputInt, InputInt2, InputInt3, InputInt4, InputText, InputTextMultiline, From e9b1552f0500a79e3e3866bbaadb3dd1cd8a64ac Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 10 Sep 2018 00:59:55 -0700 Subject: [PATCH 3/8] Make ImTexture a newtype for additional type safety --- imgui-gfx-renderer/src/lib.rs | 2 +- imgui-glium-renderer/src/lib.rs | 30 ++++++++++++++++++---------- src/image.rs | 35 +++++++++++++++++++++++++-------- src/lib.rs | 4 ++-- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index d3aa98a..178be63 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -227,7 +227,7 @@ impl Renderer { self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { - if let Some(tex) = self.textures.get(cmd.texture_id as usize) { + 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(); } diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index cd560c6..8665b99 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -7,8 +7,8 @@ use glium::index::{self, PrimitiveType}; use glium::program; use glium::texture; use glium::vertex; -use glium::{DrawError, GlObject, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; -use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui}; +use glium::{DrawError, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures}; use std::borrow::Cow; use std::fmt; use std::rc::Rc; @@ -81,6 +81,10 @@ impl Renderer { }) } + pub fn textures(&mut self) -> &mut Textures { + &mut self.device_objects.textures + } + pub fn render<'a, S: Surface>(&mut self, surface: &mut S, ui: Ui<'a>) -> RendererResult<()> { let _ = self.ctx.insert_debug_marker("imgui-rs: starting rendering"); let FrameSize { @@ -128,12 +132,17 @@ impl Renderer { self.device_objects .upload_index_buffer(&self.ctx, draw_list.idx_buffer)?; - let font_texture_id = self.device_objects.texture.get_id() as usize; - let mut idx_start = 0; for cmd in draw_list.cmd_buffer { - // We don't support custom textures...yet! - assert!(cmd.texture_id as usize == font_texture_id); + let texture = match self.device_objects.textures.get(cmd.texture_id.into()) { + Some(tex) => tex, + // if an invalid ID is supplied, fall back to the font + None => match self.device_objects.textures.get(0.into()) { + Some(tex) => tex, + // if the font is missing, which should never happen, skip + None => continue, + } + }; let idx_end = idx_start + cmd.elem_count as usize; @@ -147,7 +156,7 @@ impl Renderer { &self.device_objects.program, &uniform! { matrix: matrix, - tex: self.device_objects.texture.sampled() + tex: texture.sampled() }, &DrawParameters { blend: Blend::alpha_blending(), @@ -178,7 +187,7 @@ pub struct DeviceObjects { vertex_buffer: VertexBuffer, index_buffer: IndexBuffer, program: Program, - texture: Texture2d, + textures: Textures, } fn compile_default_program( @@ -231,13 +240,14 @@ impl DeviceObjects { }; Texture2d::new(ctx, data) })?; - im_gui.set_texture_id(texture.get_id() as usize); + let mut textures = Textures::new(); + im_gui.set_texture_id(textures.insert(texture)); Ok(DeviceObjects { vertex_buffer: vertex_buffer, index_buffer: index_buffer, program: program, - texture: texture, + textures: textures, }) } pub fn upload_vertex_buffer( diff --git a/src/image.rs b/src/image.rs index 2326ef4..ab44f29 100644 --- a/src/image.rs +++ b/src/image.rs @@ -4,7 +4,26 @@ use std::collections::HashMap; use std::os::raw::c_void; use sys; -pub type ImTexture = usize; +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ImTexture(usize); + +impl ImTexture { + pub fn id(&self) -> usize { + self.0 + } +} + +impl From for ImTexture { + fn from(id: usize) -> Self { + ImTexture(id) + } +} + +impl From<*mut c_void> for ImTexture { + fn from(ptr: *mut c_void) -> Self { + ImTexture(ptr as usize) + } +} /// Represent an image about to be drawn. /// See [`Ui::image`]. @@ -22,7 +41,7 @@ pub struct Image<'ui> { } impl<'ui> Image<'ui> { - pub fn new(_: &Ui<'ui>, texture_id: usize, size: S) -> Self + pub fn new(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self where S: Into, { @@ -79,7 +98,7 @@ impl<'ui> Image<'ui> { pub fn build(self) { unsafe { sys::igImage( - self.texture_id as *mut c_void, + self.texture_id.0 as *mut c_void, self.size, self.uv0, self.uv1, @@ -108,19 +127,19 @@ impl Textures { let id = self.next; self.textures.insert(id, texture); self.next += 1; - id + ImTexture(id) } pub fn replace(&mut self, id: ImTexture, texture: T) { - assert!(self.textures.contains_key(&id)); - self.textures.insert(id, texture); + assert!(self.textures.contains_key(&id.0)); + self.textures.insert(id.0, texture); } pub fn remove(&mut self, id: ImTexture) { - self.textures.remove(&id); + self.textures.remove(&id.0); } pub fn get(&self, id: ImTexture) -> Option<&T> { - self.textures.get(&id) + self.textures.get(&id.0) } } diff --git a/src/lib.rs b/src/lib.rs index 55aa8e8..c269039 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,8 +172,8 @@ impl ImGui { }) } } - pub fn set_texture_id(&mut self, value: usize) { - self.fonts().set_texture_id(value); + pub fn set_texture_id(&mut self, value: ImTexture) { + self.fonts().set_texture_id(value.id()); } pub fn set_ini_filename(&mut self, value: Option) { { From e1a4bbc6386a364fa7fd4b7ffb945ff8edd4b94d Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 10 Sep 2018 01:25:20 -0700 Subject: [PATCH 4/8] Add must_use to Image --- src/image.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/image.rs b/src/image.rs index ab44f29..db81e27 100644 --- a/src/image.rs +++ b/src/image.rs @@ -29,6 +29,7 @@ impl From<*mut c_void> for ImTexture { /// See [`Ui::image`]. /// /// Create your image using the builder pattern then [`Image::build`] it. +#[must_use] pub struct Image<'ui> { /// we use Result to allow postponing any construction errors to the build call texture_id: ImTexture, From d202872280fa085ee1e316dbb21cf37b135c8e16 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Mon, 10 Sep 2018 22:01:26 -0700 Subject: [PATCH 5/8] 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, +} From bdbdfe58e32bcd6f091174c36a2fdf47f296704c Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 19 Sep 2018 22:27:27 -0700 Subject: [PATCH 6/8] Return previous values from replace and remove --- src/image.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/image.rs b/src/image.rs index db81e27..18bb692 100644 --- a/src/image.rs +++ b/src/image.rs @@ -31,7 +31,6 @@ impl From<*mut c_void> for ImTexture { /// Create your image using the builder pattern then [`Image::build`] it. #[must_use] pub struct Image<'ui> { - /// we use Result to allow postponing any construction errors to the build call texture_id: ImTexture, size: ImVec2, uv0: ImVec2, @@ -131,13 +130,12 @@ impl Textures { ImTexture(id) } - pub fn replace(&mut self, id: ImTexture, texture: T) { - assert!(self.textures.contains_key(&id.0)); - self.textures.insert(id.0, texture); + pub fn replace(&mut self, id: ImTexture, texture: T) -> Option { + self.textures.insert(id.0, texture) } - pub fn remove(&mut self, id: ImTexture) { - self.textures.remove(&id.0); + pub fn remove(&mut self, id: ImTexture) -> Option { + self.textures.remove(&id.0) } pub fn get(&self, id: ImTexture) -> Option<&T> { From a2b69395d13baf57aeba55121d096a8149717e14 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 19 Sep 2018 22:29:04 -0700 Subject: [PATCH 7/8] Rename set_texture_id to set_font_texture_id --- imgui-gfx-renderer/src/lib.rs | 2 +- imgui-glium-renderer/src/lib.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 8ed4e4d..7b9b717 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -175,7 +175,7 @@ impl Renderer { factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp)); let pair = (texture, sampler); let mut textures = Textures::new(); - imgui.set_texture_id(textures.insert(pair.clone())); + imgui.set_font_texture_id(textures.insert(pair.clone())); let slice = Slice { start: 0, diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index 8665b99..6fe20a2 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -241,7 +241,7 @@ impl DeviceObjects { Texture2d::new(ctx, data) })?; let mut textures = Textures::new(); - im_gui.set_texture_id(textures.insert(texture)); + im_gui.set_font_texture_id(textures.insert(texture)); Ok(DeviceObjects { vertex_buffer: vertex_buffer, diff --git a/src/lib.rs b/src/lib.rs index c269039..507fd04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,7 +172,7 @@ impl ImGui { }) } } - pub fn set_texture_id(&mut self, value: ImTexture) { + pub fn set_font_texture_id(&mut self, value: ImTexture) { self.fonts().set_texture_id(value.id()); } pub fn set_ini_filename(&mut self, value: Option) { From d90d377e702553bfd96144cb89adb0e2d92afbe2 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 19 Sep 2018 22:34:03 -0700 Subject: [PATCH 8/8] Report rather than swallowing bad texture ids --- imgui-gfx-renderer/src/lib.rs | 11 ++++++----- imgui-glium-renderer/src/lib.rs | 16 ++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 7b9b717..97437cc 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -8,7 +8,7 @@ use gfx::texture::{FilterMethod, SamplerInfo, WrapMode}; use gfx::traits::FactoryExt; use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; use gfx::pso::{PipelineData, PipelineState}; -use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures}; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures, ImTexture}; pub type RendererResult = Result; @@ -18,6 +18,7 @@ pub enum RendererError { Buffer(gfx::buffer::CreationError), Pipeline(gfx::PipelineStateError), Combined(gfx::CombinedError), + BadTexture(ImTexture), } impl From> for RendererError { @@ -136,7 +137,6 @@ pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, textures: Textures>, - backup_texture: Texture, } impl Renderer { @@ -175,7 +175,7 @@ impl Renderer { factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp)); let pair = (texture, sampler); let mut textures = Textures::new(); - imgui.set_font_texture_id(textures.insert(pair.clone())); + imgui.set_font_texture_id(textures.insert(pair)); let slice = Slice { start: 0, @@ -193,7 +193,6 @@ impl Renderer { }, index_buffer: index_buffer, textures: textures, - backup_texture: pair, }) } @@ -254,7 +253,9 @@ impl Renderer { self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { - let tex = self.textures.get(cmd.texture_id.into()).unwrap_or(&self.backup_texture); + let texture_id = cmd.texture_id.into(); + let tex = self.textures.get(texture_id) + .ok_or_else(|| RendererError::BadTexture(texture_id))?; self.bundle.slice.end = self.bundle.slice.start + cmd.elem_count; let scissor = Rect { diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index 6fe20a2..9091103 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -8,7 +8,7 @@ use glium::program; use glium::texture; use glium::vertex; use glium::{DrawError, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; -use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures}; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures, ImTexture}; use std::borrow::Cow; use std::fmt; use std::rc::Rc; @@ -22,6 +22,7 @@ pub enum RendererError { Program(program::ProgramChooserCreationError), Texture(texture::TextureCreationError), Draw(DrawError), + BadTexture(ImTexture), } impl fmt::Display for RendererError { @@ -33,6 +34,7 @@ impl fmt::Display for RendererError { Program(ref e) => write!(f, "Program creation failed: {}", e), Texture(_) => write!(f, "Texture creation failed"), Draw(ref e) => write!(f, "Drawing failed: {}", e), + BadTexture(ref t) => write!(f, "Bad texture ID: {}", t.id()), } } } @@ -134,15 +136,9 @@ impl Renderer { let mut idx_start = 0; for cmd in draw_list.cmd_buffer { - let texture = match self.device_objects.textures.get(cmd.texture_id.into()) { - Some(tex) => tex, - // if an invalid ID is supplied, fall back to the font - None => match self.device_objects.textures.get(0.into()) { - Some(tex) => tex, - // if the font is missing, which should never happen, skip - None => continue, - } - }; + let texture_id = cmd.texture_id.into(); + let texture = self.device_objects.textures.get(texture_id) + .ok_or_else(|| RendererError::BadTexture(texture_id))?; let idx_end = idx_start + cmd.elem_count as usize;