#[macro_use] extern crate glium; extern crate imgui; use glium::{DrawError, GlObject, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; use glium::backend::{Context, Facade}; use glium::program; use glium::index::{self, PrimitiveType}; use glium::texture; use glium::vertex; use imgui::{DrawList, ImDrawIdx, ImDrawVert, ImGui, Ui}; use std::borrow::Cow; use std::fmt; use std::rc::Rc; pub type RendererResult = Result; #[derive(Clone, Debug)] pub enum RendererError { Vertex(vertex::BufferCreationError), Index(index::BufferCreationError), Program(program::ProgramChooserCreationError), Texture(texture::TextureCreationError), Draw(DrawError), } impl fmt::Display for RendererError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::RendererError::*; match *self { Vertex(_) => write!(f, "Vertex buffer creation failed"), Index(_) => write!(f, "Index buffer creation failed"), Program(ref e) => write!(f, "Program creation failed: {}", e), Texture(_) => write!(f, "Texture creation failed"), Draw(ref e) => write!(f, "Drawing failed: {}", e), } } } impl From for RendererError { fn from(e: vertex::BufferCreationError) -> RendererError { RendererError::Vertex(e) } } impl From for RendererError { fn from(e: index::BufferCreationError) -> RendererError { RendererError::Index(e) } } impl From for RendererError { fn from(e: program::ProgramChooserCreationError) -> RendererError { RendererError::Program(e) } } impl From for RendererError { fn from(e: texture::TextureCreationError) -> RendererError { RendererError::Texture(e) } } impl From for RendererError { fn from(e: DrawError) -> RendererError { RendererError::Draw(e) } } pub struct Renderer { ctx: Rc, device_objects: DeviceObjects, } impl Renderer { pub fn init(imgui: &mut ImGui, ctx: &F) -> RendererResult { let device_objects = try!(DeviceObjects::init(imgui, ctx)); Ok(Renderer { ctx: ctx.get_context().clone(), device_objects: device_objects, }) } 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 result = ui.render(|ui, draw_list| self.render_draw_list(surface, ui, draw_list)); let _ = self.ctx.insert_debug_marker("imgui-rs: rendering finished"); result } fn render_draw_list<'a, S: Surface>(&mut self, surface: &mut S, ui: &'a Ui<'a>, draw_list: DrawList<'a>) -> RendererResult<()> { use glium::{Blend, DrawParameters, Rect}; use glium::uniforms::MagnifySamplerFilter; try!(self.device_objects.upload_vertex_buffer(&self.ctx, draw_list.vtx_buffer)); try!(self.device_objects.upload_index_buffer(&self.ctx, draw_list.idx_buffer)); let (width, height) = ui.imgui().display_size(); let (scale_width, scale_height) = ui.imgui().display_framebuffer_scale(); if width == 0.0 || height == 0.0 { return Ok(()); } 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], [-1.0, 1.0, 0.0, 1.0]]; 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 idx_end = idx_start + cmd.elem_count as usize; try!(surface.draw(&self.device_objects.vertex_buffer, &self.device_objects .index_buffer .slice(idx_start..idx_end) .expect("Invalid index buffer range"), &self.device_objects.program, &uniform! { matrix: matrix, tex: self.device_objects.texture.sampled() .magnify_filter(MagnifySamplerFilter::Nearest), }, &DrawParameters { blend: Blend::alpha_blending(), scissor: Some(Rect { left: (cmd.clip_rect.x * scale_width) as u32, bottom: ((height - cmd.clip_rect.w) * scale_height) as u32, width: ((cmd.clip_rect.z - cmd.clip_rect.x) * scale_width) as u32, height: ((cmd.clip_rect.w - cmd.clip_rect.y) * scale_height) as u32, }), ..DrawParameters::default() })); idx_start = idx_end; } Ok(()) } } pub struct DeviceObjects { vertex_buffer: VertexBuffer, index_buffer: IndexBuffer, program: Program, texture: Texture2d, } fn compile_default_program(ctx: &F) -> Result { program!( ctx, 140 => { vertex: include_str!("shader/vert_140.glsl"), fragment: include_str!("shader/frag_140.glsl"), outputs_srgb: true, }, 110 => { vertex: include_str!("shader/vert_110.glsl"), fragment: include_str!("shader/frag_110.glsl"), outputs_srgb: true, }, ) } impl DeviceObjects { pub fn init(im_gui: &mut ImGui, ctx: &F) -> RendererResult { use glium::texture::{ClientFormat, RawImage2d}; let vertex_buffer = try!(VertexBuffer::empty_dynamic(ctx, 0)); let index_buffer = try!(IndexBuffer::empty_dynamic(ctx, PrimitiveType::TrianglesList, 0)); let program = try!(compile_default_program(ctx)); let texture = try!(im_gui.prepare_texture(|handle| { let data = RawImage2d { data: Cow::Borrowed(handle.pixels), width: handle.width, height: handle.height, format: ClientFormat::U8U8U8U8, }; Texture2d::new(ctx, data) })); im_gui.set_texture_id(texture.get_id() as usize); Ok(DeviceObjects { vertex_buffer: vertex_buffer, index_buffer: index_buffer, program: program, texture: texture, }) } pub fn upload_vertex_buffer(&mut self, ctx: &F, vtx_buffer: &[ImDrawVert]) -> RendererResult<()> { self.vertex_buffer.invalidate(); if let Some(slice) = self.vertex_buffer.slice_mut(0..vtx_buffer.len()) { slice.write(vtx_buffer); return Ok(()); } self.vertex_buffer = try!(VertexBuffer::dynamic(ctx, vtx_buffer)); let _ = ctx.get_context() .insert_debug_marker(&format!("imgui-rs: resized vertex buffer to {} bytes", self.vertex_buffer.get_size())); Ok(()) } pub fn upload_index_buffer(&mut self, ctx: &F, idx_buffer: &[ImDrawIdx]) -> RendererResult<()> { self.index_buffer.invalidate(); if let Some(slice) = self.index_buffer.slice_mut(0..idx_buffer.len()) { slice.write(idx_buffer); return Ok(()); } self.index_buffer = try!(IndexBuffer::dynamic(ctx, PrimitiveType::TrianglesList, idx_buffer)); let _ = ctx.get_context() .insert_debug_marker(&format!("imgui-rs: resized index buffer to {} bytes", self.index_buffer.get_size())); Ok(()) } }