#[macro_use] extern crate gfx; extern crate imgui; 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 imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures}; pub type RendererResult = Result; #[derive(Clone, Debug)] pub enum RendererError { Update(gfx::UpdateError), Buffer(gfx::buffer::CreationError), Pipeline(gfx::PipelineStateError), Combined(gfx::CombinedError), } impl From> for RendererError { fn from(e: gfx::UpdateError) -> RendererError { RendererError::Update(e) } } impl From for RendererError { fn from(e: gfx::buffer::CreationError) -> RendererError { RendererError::Buffer(e) } } impl From> for RendererError { fn from(e: gfx::PipelineStateError) -> RendererError { RendererError::Pipeline(e) } } impl From for RendererError { fn from(e: gfx::CombinedError) -> RendererError { RendererError::Combined(e) } } gfx_defines!{ pipeline pipe { vertex_buffer: gfx::VertexBuffer = (), matrix: gfx::Global<[[f32; 4]; 4]> = "matrix", tex: gfx::TextureSampler<[f32; 4]> = "tex", out: gfx::BlendTarget = ( "Target0", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA, ), scissor: gfx::Scissor = (), } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Shaders { GlSl400, // OpenGL 4.0+ GlSl130, // OpenGL 3.0+ GlSl110, // OpenGL 2.0+ GlSlEs300, // OpenGL ES 3.0+ GlSlEs100, // OpenGL ES 2.0+ } impl Shaders { fn get_program_code(self) -> (&'static [u8], &'static [u8]) { use Shaders::*; match self { GlSl400 => ( include_bytes!("shader/glsl_400.vert"), include_bytes!("shader/glsl_400.frag"), ), GlSl130 => ( include_bytes!("shader/glsl_130.vert"), include_bytes!("shader/glsl_130.frag"), ), GlSl110 => ( include_bytes!("shader/glsl_110.vert"), include_bytes!("shader/glsl_110.frag"), ), GlSlEs300 => ( include_bytes!("shader/glsles_300.vert"), include_bytes!("shader/glsles_300.frag"), ), GlSlEs100 => ( include_bytes!("shader/glsles_100.vert"), include_bytes!("shader/glsles_100.frag"), ), } } } pub type Texture = (gfx::handle::ShaderResourceView, gfx::handle::Sampler); pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, textures: Textures>, } impl Renderer { pub fn init>( imgui: &mut ImGui, factory: &mut F, shaders: Shaders, out: RenderTargetView, ) -> RendererResult> { let (vs_code, ps_code) = shaders.get_program_code(); let pso = factory.create_pipeline_simple(vs_code, ps_code, pipe::new())?; let vertex_buffer = factory.create_buffer::( 256, gfx::buffer::Role::Vertex, gfx::memory::Usage::Dynamic, Bind::empty(), )?; let index_buffer = factory.create_buffer::( 256, gfx::buffer::Role::Index, gfx::memory::Usage::Dynamic, Bind::empty(), )?; let (_, texture) = imgui.prepare_texture(|handle| { factory.create_texture_immutable_u8::( gfx::texture::Kind::D2( handle.width as u16, handle.height as u16, gfx::texture::AaMode::Single, ), gfx::texture::Mipmap::Provided, &[handle.pixels], ) })?; let sampler = 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())); 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, base_vertex: 0, instances: None, buffer: index_buffer.clone().into_index_buffer(factory), }; Ok(Renderer { bundle: Bundle::new(slice, pso, data), index_buffer: index_buffer, textures: textures, }) } pub fn update_render_target(&mut self, out: RenderTargetView) { self.bundle.data.out = out; } pub fn textures(&mut self) -> &mut Textures> { &mut self.textures } pub fn render<'a, F: Factory, C: CommandBuffer>( &mut self, ui: Ui<'a>, factory: &mut F, encoder: &mut Encoder, ) -> RendererResult<()> { let FrameSize { logical_size: (width, height), hidpi_factor, } = ui.frame_size(); if !(width > 0.0 && height > 0.0) { return Ok(()); } let fb_size = ( (width * hidpi_factor) as f32, (height * hidpi_factor) as f32, ); self.bundle.data.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], ]; 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)?; } Ok(()) }) } fn render_draw_list<'a, F: Factory, C: CommandBuffer>( &mut self, factory: &mut F, encoder: &mut Encoder, draw_list: &DrawList<'a>, fb_size: (f32, f32), ) -> RendererResult<()> { let (fb_width, fb_height) = fb_size; self.upload_vertex_buffer(factory, encoder, draw_list.vtx_buffer)?; self.upload_index_buffer(factory, encoder, draw_list.idx_buffer)?; self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { 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 { 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) .abs() .min(fb_width) .round() as u16, h: (cmd.clip_rect.w - cmd.clip_rect.y) .abs() .min(fb_height) .round() as u16, }; self.bundle.encode(encoder); self.bundle.slice.start = self.bundle.slice.end; } Ok(()) } fn upload_vertex_buffer, C: CommandBuffer>( &mut self, factory: &mut F, 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::( 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)?) } fn upload_index_buffer, C: CommandBuffer>( &mut self, factory: &mut F, encoder: &mut Encoder, idx_buffer: &[ImDrawIdx], ) -> RendererResult<()> { if self.index_buffer.len() < idx_buffer.len() { self.index_buffer = factory.create_buffer::( idx_buffer.len(), gfx::buffer::Role::Index, gfx::memory::Usage::Dynamic, Bind::empty(), )?; self.bundle.slice.buffer = self.index_buffer.clone().into_index_buffer(factory); } Ok(encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?) } }