Initial working but dirty gfx renderer

This commit is contained in:
Joonas Javanainen 2017-02-21 01:01:55 +02:00
parent 9db8cf94cf
commit 4ee27d149c
No known key found for this signature in database
GPG Key ID: D39CCA5CB19B9179
11 changed files with 422 additions and 2 deletions

View File

@ -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

View File

@ -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"]

View File

@ -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"] }

View File

@ -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::<ColorFormat, DepthFormat>(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));
})
}

View File

@ -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
}
}

View File

@ -0,0 +1,13 @@
[package]
name = "imgui-gfx-renderer"
version = "0.0.14-pre"
authors = ["Joonas Javanainen <joonas.javanainen@gmail.com>", "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"] }

View File

@ -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<T> = Result<T, RendererError>;
#[derive(Clone, Debug)]
pub enum RendererError {
Update(gfx::UpdateError<usize>),
}
impl From<gfx::UpdateError<usize>> for RendererError {
fn from(e: gfx::UpdateError<usize>) -> RendererError { RendererError::Update(e) }
}
gfx_defines!{
pipeline pipe {
vertex_buffer: gfx::VertexBuffer<ImDrawVert> = (),
matrix: gfx::Global<[[f32; 4]; 4]> = "matrix",
tex: gfx::TextureSampler<[f32; 4]> = "tex",
out: gfx::BlendTarget<gfx::format::Rgba8> = ("Target0", gfx::state::MASK_ALL, gfx::preset::blend::ALPHA),
scissor: gfx::Scissor = (),
}
}
pub struct Renderer<R: Resources> {
bundle: Bundle<R, pipe::Data<R>>,
index_buffer: Buffer<R, u16>,
}
impl<R: Resources> Renderer<R> {
pub fn init<F: Factory<R>>(imgui: &mut ImGui,
factory: &mut F,
out: RenderTargetView<R, gfx::format::Rgba8>)
-> Renderer<R> {
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::<ImDrawVert>(256,
gfx::buffer::Role::Vertex,
gfx::memory::Usage::Dynamic,
Bind::empty())
.expect("Failed to create vertex buffer");
let index_buffer = factory.create_buffer::<ImDrawIdx>(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::format::Rgba8>(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<R>, C: CommandBuffer<R>>(&mut self,
ui: Ui<'a>,
factory: &mut F,
encoder: &mut Encoder<R, C>)
-> 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<R>, C: CommandBuffer<R>>(&mut self,
ui: &'a Ui<'a>,
factory: &mut F,
encoder: &mut Encoder<R, C>,
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<F: Factory<R>, C: CommandBuffer<R>>(&mut self,
factory: &mut F,
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>(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<F: Factory<R>, C: CommandBuffer<R>>(&mut self,
factory: &mut F,
encoder: &mut Encoder<R, C>,
idx_buffer: &[ImDrawIdx])
-> RendererResult<()> {
if self.index_buffer.len() < idx_buffer.len() {
self.index_buffer = factory.create_buffer::<ImDrawIdx>(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)))
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}