From 4ee27d149cc387fdfe1656fbabc544d798f288f7 Mon Sep 17 00:00:00 2001 From: Joonas Javanainen Date: Tue, 21 Feb 2017 01:01:55 +0200 Subject: [PATCH] Initial working but dirty gfx renderer --- CHANGELOG.markdown | 2 + Cargo.toml | 2 +- imgui-examples/Cargo.toml | 6 +- imgui-examples/examples/hello_gfx.rs | 73 +++++++++ imgui-examples/examples/support_gfx/mod.rs | 114 ++++++++++++++ imgui-gfx-renderer/Cargo.toml | 13 ++ imgui-gfx-renderer/src/lib.rs | 160 ++++++++++++++++++++ imgui-gfx-renderer/src/shader/frag_110.glsl | 10 ++ imgui-gfx-renderer/src/shader/frag_140.glsl | 12 ++ imgui-gfx-renderer/src/shader/vert_110.glsl | 16 ++ imgui-gfx-renderer/src/shader/vert_140.glsl | 16 ++ 11 files changed, 422 insertions(+), 2 deletions(-) create mode 100644 imgui-examples/examples/hello_gfx.rs create mode 100644 imgui-examples/examples/support_gfx/mod.rs create mode 100644 imgui-gfx-renderer/Cargo.toml create mode 100644 imgui-gfx-renderer/src/lib.rs create mode 100644 imgui-gfx-renderer/src/shader/frag_110.glsl create mode 100644 imgui-gfx-renderer/src/shader/frag_140.glsl create mode 100644 imgui-gfx-renderer/src/shader/vert_110.glsl create mode 100644 imgui-gfx-renderer/src/shader/vert_140.glsl diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index e100ba8..f6e74e1 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -32,6 +32,8 @@ - Support for 2-4 -element float sliders - `ImVec4::zero()` - `Into` array and tuple conversions for ImVec2 and ImVec4 +- gfx 0.14 support in imgui-sys +- gfx 0.14 renderer implementation ### Changed diff --git a/Cargo.toml b/Cargo.toml index 12a5211..e0f1302 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,4 @@ travis-ci = { repository = "gekkio/imgui-rs" } imgui-sys = { version = "0.0.14-pre", path = "imgui-sys" } [workspace] -members = ["imgui-examples", "imgui-sys", "imgui-glium-renderer"] +members = ["imgui-examples", "imgui-sys", "imgui-gfx-renderer", "imgui-glium-renderer"] diff --git a/imgui-examples/Cargo.toml b/imgui-examples/Cargo.toml index dd40129..4cb2853 100644 --- a/imgui-examples/Cargo.toml +++ b/imgui-examples/Cargo.toml @@ -9,7 +9,11 @@ license = "MIT/Apache-2.0" publish = false [dev-dependencies] +gfx = "0.14" +gfx_window_glutin = "0.14" glium = { version = "0.16", default-features = true } +glutin = "0.7" imgui = { version = "0.0.14-pre", path = "../" } +imgui-gfx-renderer = { version = "0.0.14-pre", path = "../imgui-gfx-renderer" } imgui-glium-renderer = { version = "0.0.14-pre", path = "../imgui-glium-renderer" } -imgui-sys = { version = "0.0.14-pre", path = "../imgui-sys", features = ["glium"] } +imgui-sys = { version = "0.0.14-pre", path = "../imgui-sys", features = ["gfx", "glium"] } diff --git a/imgui-examples/examples/hello_gfx.rs b/imgui-examples/examples/hello_gfx.rs new file mode 100644 index 0000000..b38535d --- /dev/null +++ b/imgui-examples/examples/hello_gfx.rs @@ -0,0 +1,73 @@ +extern crate gfx; +extern crate gfx_window_glutin; +extern crate glutin; + +#[macro_use] +extern crate imgui; +extern crate imgui_gfx_renderer; +extern crate imgui_sys; + +use gfx::Device; + +use imgui::*; +use imgui_gfx_renderer::Renderer; + +mod support_gfx; + +const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0]; + +pub type ColorFormat = gfx::format::Rgba8; +pub type DepthFormat = gfx::format::DepthStencil; + +pub fn main() { + let mut support = support_gfx::Support::init(); + let builder = glutin::WindowBuilder::new() + .with_title("Hello World (GFX)".to_string()) + .with_dimensions(1024, 768) + .with_vsync(); + let (window, mut device, mut factory, mut main_color, mut main_depth) = + gfx_window_glutin::init::(builder); + let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into(); + let mut renderer = Renderer::init(&mut support.imgui, &mut factory, main_color.clone()); + + 'main: loop { + for event in window.poll_events() { + support.update_event(&event); + match event { + glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) | + glutin::Event::Closed => break 'main, + glutin::Event::Resized(_width, _height) => { + gfx_window_glutin::update_views(&window, &mut main_color, &mut main_depth); + } + _ => (), + } + } + + support.update_mouse(); + + let size_points = window.get_inner_size_points().unwrap(); + let size_pixels = window.get_inner_size_pixels().unwrap(); + let ui = support.imgui.frame(size_points, size_pixels, 1.0 / 16.0); + hello_world(&ui); + + encoder.clear(&mut main_color, CLEAR_COLOR); + + renderer.render(ui, &mut factory, &mut encoder) + .expect("Rendering failed"); + encoder.flush(&mut device); + window.swap_buffers().unwrap(); + device.cleanup(); + } +} + +fn hello_world<'a>(ui: &Ui<'a>) { + ui.window(im_str!("Hello world")) + .size((300.0, 100.0), ImGuiSetCond_FirstUseEver) + .build(|| { + ui.text(im_str!("Hello world!")); + ui.text(im_str!("This...is...imgui-rs!")); + ui.separator(); + let mouse_pos = ui.imgui().mouse_pos(); + ui.text(im_str!("Mouse Position: ({:.1},{:.1})", mouse_pos.0, mouse_pos.1)); + }) +} diff --git a/imgui-examples/examples/support_gfx/mod.rs b/imgui-examples/examples/support_gfx/mod.rs new file mode 100644 index 0000000..5dcdaf9 --- /dev/null +++ b/imgui-examples/examples/support_gfx/mod.rs @@ -0,0 +1,114 @@ +use glutin; +use glutin::{ElementState, Event, MouseButton, MouseScrollDelta, VirtualKeyCode, TouchPhase}; +use imgui::{ImGui, ImGuiKey}; + +pub struct Support { + pub imgui: ImGui, + mouse_pos: (i32, i32), + mouse_pressed: (bool, bool, bool), + mouse_wheel: f32, +} + +impl Support { + pub fn init() -> Support { + let mut imgui = ImGui::init(); + + imgui.set_imgui_key(ImGuiKey::Tab, 0); + imgui.set_imgui_key(ImGuiKey::LeftArrow, 1); + imgui.set_imgui_key(ImGuiKey::RightArrow, 2); + imgui.set_imgui_key(ImGuiKey::UpArrow, 3); + imgui.set_imgui_key(ImGuiKey::DownArrow, 4); + imgui.set_imgui_key(ImGuiKey::PageUp, 5); + imgui.set_imgui_key(ImGuiKey::PageDown, 6); + imgui.set_imgui_key(ImGuiKey::Home, 7); + imgui.set_imgui_key(ImGuiKey::End, 8); + imgui.set_imgui_key(ImGuiKey::Delete, 9); + imgui.set_imgui_key(ImGuiKey::Backspace, 10); + imgui.set_imgui_key(ImGuiKey::Enter, 11); + imgui.set_imgui_key(ImGuiKey::Escape, 12); + imgui.set_imgui_key(ImGuiKey::A, 13); + imgui.set_imgui_key(ImGuiKey::C, 14); + imgui.set_imgui_key(ImGuiKey::V, 15); + imgui.set_imgui_key(ImGuiKey::X, 16); + imgui.set_imgui_key(ImGuiKey::Y, 17); + imgui.set_imgui_key(ImGuiKey::Z, 18); + + Support { + imgui: imgui, + mouse_pos: (0, 0), + mouse_pressed: (false, false, false), + mouse_wheel: 0.0, + } + } + + pub fn update_mouse(&mut self) { + let scale = self.imgui.display_framebuffer_scale(); + self.imgui.set_mouse_pos(self.mouse_pos.0 as f32 / scale.0, + self.mouse_pos.1 as f32 / scale.1); + self.imgui.set_mouse_down(&[self.mouse_pressed.0, + self.mouse_pressed.1, + self.mouse_pressed.2, + false, + false]); + self.imgui.set_mouse_wheel(self.mouse_wheel / scale.1); + self.mouse_wheel = 0.0; + } + + pub fn update_event(&mut self, event: &glutin::Event) -> bool { + match *event { + Event::Closed => return false, + Event::KeyboardInput(state, _, code) => { + let pressed = state == ElementState::Pressed; + match code { + Some(VirtualKeyCode::Tab) => self.imgui.set_key(0, pressed), + Some(VirtualKeyCode::Left) => self.imgui.set_key(1, pressed), + Some(VirtualKeyCode::Right) => self.imgui.set_key(2, pressed), + Some(VirtualKeyCode::Up) => self.imgui.set_key(3, pressed), + Some(VirtualKeyCode::Down) => self.imgui.set_key(4, pressed), + Some(VirtualKeyCode::PageUp) => self.imgui.set_key(5, pressed), + Some(VirtualKeyCode::PageDown) => self.imgui.set_key(6, pressed), + Some(VirtualKeyCode::Home) => self.imgui.set_key(7, pressed), + Some(VirtualKeyCode::End) => self.imgui.set_key(8, pressed), + Some(VirtualKeyCode::Delete) => self.imgui.set_key(9, pressed), + Some(VirtualKeyCode::Back) => self.imgui.set_key(10, pressed), + Some(VirtualKeyCode::Return) => self.imgui.set_key(11, pressed), + Some(VirtualKeyCode::Escape) => self.imgui.set_key(12, pressed), + Some(VirtualKeyCode::A) => self.imgui.set_key(13, pressed), + Some(VirtualKeyCode::C) => self.imgui.set_key(14, pressed), + Some(VirtualKeyCode::V) => self.imgui.set_key(15, pressed), + Some(VirtualKeyCode::X) => self.imgui.set_key(16, pressed), + Some(VirtualKeyCode::Y) => self.imgui.set_key(17, pressed), + Some(VirtualKeyCode::Z) => self.imgui.set_key(18, pressed), + Some(VirtualKeyCode::LControl) | + Some(VirtualKeyCode::RControl) => self.imgui.set_key_ctrl(pressed), + Some(VirtualKeyCode::LShift) | + Some(VirtualKeyCode::RShift) => self.imgui.set_key_shift(pressed), + Some(VirtualKeyCode::LAlt) | + Some(VirtualKeyCode::RAlt) => self.imgui.set_key_alt(pressed), + Some(VirtualKeyCode::LWin) | + Some(VirtualKeyCode::RWin) => self.imgui.set_key_super(pressed), + _ => {} + } + } + Event::MouseMoved(x, y) => self.mouse_pos = (x, y), + Event::MouseInput(state, MouseButton::Left) => { + self.mouse_pressed.0 = state == ElementState::Pressed + } + Event::MouseInput(state, MouseButton::Right) => { + self.mouse_pressed.1 = state == ElementState::Pressed + } + Event::MouseInput(state, MouseButton::Middle) => { + self.mouse_pressed.2 = state == ElementState::Pressed + } + Event::MouseWheel(MouseScrollDelta::LineDelta(_, y), TouchPhase::Moved) => { + self.mouse_wheel = y + } + Event::MouseWheel(MouseScrollDelta::PixelDelta(_, y), TouchPhase::Moved) => { + self.mouse_wheel = y + } + Event::ReceivedCharacter(c) => self.imgui.add_input_character(c), + _ => (), + } + true + } +} diff --git a/imgui-gfx-renderer/Cargo.toml b/imgui-gfx-renderer/Cargo.toml new file mode 100644 index 0000000..fe0caf3 --- /dev/null +++ b/imgui-gfx-renderer/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "imgui-gfx-renderer" +version = "0.0.14-pre" +authors = ["Joonas Javanainen ", "imgui-rs contributors"] +description = "gfx renderer for the imgui crate" +homepage = "https://github.com/gekkio/imgui-rs" +repository = "https://github.com/gekkio/imgui-rs" +license = "MIT/Apache-2.0" + +[dependencies] +gfx = "0.14" +imgui = { version = "0.0.14-pre", path = "../" } +imgui-sys = { version = "0.0.14-pre", path = "../imgui-sys", features = ["gfx"] } diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs new file mode 100644 index 0000000..b8d750e --- /dev/null +++ b/imgui-gfx-renderer/src/lib.rs @@ -0,0 +1,160 @@ +#[macro_use] +extern crate gfx; +extern crate imgui; + +use gfx::{Bind, Bundle, CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; +use gfx::handle::{Buffer, RenderTargetView}; +use gfx::traits::FactoryExt; +use imgui::{DrawList, ImDrawIdx, ImDrawVert, ImGui, Ui}; + +pub type RendererResult = Result; + +#[derive(Clone, Debug)] +pub enum RendererError { + Update(gfx::UpdateError), +} + +impl From> for RendererError { + fn from(e: gfx::UpdateError) -> RendererError { RendererError::Update(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::MASK_ALL, gfx::preset::blend::ALPHA), + scissor: gfx::Scissor = (), + } +} + +pub struct Renderer { + bundle: Bundle>, + index_buffer: Buffer, +} + +impl Renderer { + pub fn init>(imgui: &mut ImGui, + factory: &mut F, + out: RenderTargetView) + -> Renderer { + let pso = factory.create_pipeline_simple(include_bytes!("shader/vert_110.glsl"), + include_bytes!("shader/frag_110.glsl"), + pipe::new()) + .expect("Failed to setup PSO"); + let vertex_buffer = factory.create_buffer::(256, + gfx::buffer::Role::Vertex, + gfx::memory::Usage::Dynamic, + Bind::empty()) + .expect("Failed to create vertex buffer"); + let index_buffer = factory.create_buffer::(256, + gfx::buffer::Role::Index, + gfx::memory::Usage::Dynamic, + Bind::empty()) + .expect("Failed to create index buffer"); + 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), &[handle.pixels]) + }).expect("Failed to create texture"); + // TODO: set texture id in imgui + let sampler = factory.create_sampler_linear(); + 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: (texture, sampler), + out: out.clone(), + 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), + }; + Renderer { + bundle: Bundle::new(slice, pso, data), + index_buffer: index_buffer, + } + } + pub fn render<'a, F: Factory, C: CommandBuffer>(&mut self, + ui: Ui<'a>, + factory: &mut F, + encoder: &mut Encoder) + -> RendererResult<()> { + let (width, height) = ui.imgui().display_size(); + + if width == 0.0 || height == 0.0 { + return Ok(()); + } + 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, draw_list| self.render_draw_list(ui, factory, encoder, draw_list)) + } + fn render_draw_list<'a, F: Factory, C: CommandBuffer>(&mut self, + ui: &'a Ui<'a>, + factory: &mut F, + encoder: &mut Encoder, + draw_list: DrawList<'a>) + -> RendererResult<()> { + let (scale_width, scale_height) = ui.imgui().display_framebuffer_scale(); + + self.bundle.slice.start = 0; + for cmd in draw_list.cmd_buffer { + // TODO: check cmd.texture_id + + self.upload_vertex_buffer(factory, encoder, draw_list.vtx_buffer)?; + self.upload_index_buffer(factory, encoder, draw_list.idx_buffer)?; + + self.bundle.slice.end = self.bundle.slice.start + cmd.elem_count; + self.bundle.data.scissor = Rect { + x: (cmd.clip_rect.x * scale_width) as u16, + y: (cmd.clip_rect.y * scale_height) as u16, + w: ((cmd.clip_rect.z - cmd.clip_rect.x).abs() * scale_width) as u16, + h: ((cmd.clip_rect.w - cmd.clip_rect.y).abs() * scale_height) 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()) + .expect("Failed to create vertex buffer"); + } + Ok(try!(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()) + .expect("Failed to create index buffer"); + self.bundle.slice.buffer = self.index_buffer.clone().into_index_buffer(factory); + } + Ok(try!(encoder.update_buffer(&self.index_buffer, idx_buffer, 0))) + } +} diff --git a/imgui-gfx-renderer/src/shader/frag_110.glsl b/imgui-gfx-renderer/src/shader/frag_110.glsl new file mode 100644 index 0000000..8bdec78 --- /dev/null +++ b/imgui-gfx-renderer/src/shader/frag_110.glsl @@ -0,0 +1,10 @@ +#version 110 + +uniform sampler2D tex; + +varying vec2 f_uv; +varying vec4 f_color; + +void main() { + gl_FragColor = f_color * texture2D(tex, f_uv.st); +} diff --git a/imgui-gfx-renderer/src/shader/frag_140.glsl b/imgui-gfx-renderer/src/shader/frag_140.glsl new file mode 100644 index 0000000..7fbd223 --- /dev/null +++ b/imgui-gfx-renderer/src/shader/frag_140.glsl @@ -0,0 +1,12 @@ +#version 140 + +uniform sampler2D tex; + +in vec2 f_uv; +in vec4 f_color; + +out vec4 Target0; + +void main() { + Target0 = f_color * texture(tex, f_uv.st); +} diff --git a/imgui-gfx-renderer/src/shader/vert_110.glsl b/imgui-gfx-renderer/src/shader/vert_110.glsl new file mode 100644 index 0000000..99ee8a5 --- /dev/null +++ b/imgui-gfx-renderer/src/shader/vert_110.glsl @@ -0,0 +1,16 @@ +#version 110 + +uniform mat4 matrix; + +attribute vec2 pos; +attribute vec2 uv; +attribute vec4 col; + +varying vec2 f_uv; +varying vec4 f_color; + +void main() { + f_uv = uv; + f_color = col; + gl_Position = matrix * vec4(pos.xy, 0, 1); +} diff --git a/imgui-gfx-renderer/src/shader/vert_140.glsl b/imgui-gfx-renderer/src/shader/vert_140.glsl new file mode 100644 index 0000000..be63844 --- /dev/null +++ b/imgui-gfx-renderer/src/shader/vert_140.glsl @@ -0,0 +1,16 @@ +#version 140 + +uniform mat4 matrix; + +in vec2 pos; +in vec2 uv; +in vec4 col; + +out vec2 f_uv; +out vec4 f_color; + +void main() { + f_uv = uv; + f_color = col; + gl_Position = matrix * vec4(pos.xy, 0, 1); +}