From 42d3c0f6d7fb4b9ab54590718cfab2d83043d6e8 Mon Sep 17 00:00:00 2001 From: Joonas Javanainen Date: Thu, 27 Jun 2019 22:02:48 +0300 Subject: [PATCH] Pull renderer/draw data updates from 0.1-dev --- Cargo.toml | 2 + imgui-examples/Cargo.lock | 3 +- imgui-examples/examples/support_gfx/mod.rs | 35 +- imgui-gfx-renderer/Cargo.toml | 3 +- imgui-gfx-renderer/src/lib.rs | 570 ++++++++++++------- imgui-glium-examples/Cargo.lock | 3 +- imgui-glium-examples/examples/support/mod.rs | 6 +- imgui-glium-renderer/Cargo.toml | 3 +- imgui-glium-renderer/src/lib.rs | 310 +++++----- imgui-sys/Cargo.toml | 4 - imgui-sys/src/gfx_support.rs | 55 -- imgui-sys/src/glium_support.rs | 46 -- imgui-sys/src/lib.rs | 6 - src/image.rs | 53 +- src/lib.rs | 109 +--- src/render/draw_data.rs | 283 +++++++++ src/render/mod.rs | 2 + src/render/renderer.rs | 77 +++ 18 files changed, 929 insertions(+), 641 deletions(-) delete mode 100644 imgui-sys/src/gfx_support.rs delete mode 100644 imgui-sys/src/glium_support.rs create mode 100644 src/render/draw_data.rs create mode 100644 src/render/mod.rs create mode 100644 src/render/renderer.rs diff --git a/Cargo.toml b/Cargo.toml index a4a8ad5..10356bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ travis-ci = { repository = "Gekkio/imgui-rs" } [dependencies] bitflags = "1.0" +glium = { version = "0.25", default-features = false, optional = true } +gfx = { version = "0.18", optional = true } imgui-sys = { version = "0.0.24-pre", path = "imgui-sys" } lazy_static = "1.1" parking_lot = "0.8" diff --git a/imgui-examples/Cargo.lock b/imgui-examples/Cargo.lock index f9dbc16..42d4610 100644 --- a/imgui-examples/Cargo.lock +++ b/imgui-examples/Cargo.lock @@ -356,6 +356,7 @@ name = "imgui" version = "0.0.24-pre" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "gfx 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "imgui-sys 0.0.24-pre", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -380,7 +381,6 @@ version = "0.0.24-pre" dependencies = [ "gfx 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", "imgui 0.0.24-pre", - "imgui-sys 0.0.24-pre", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -389,7 +389,6 @@ name = "imgui-sys" version = "0.0.24-pre" dependencies = [ "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "gfx 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/imgui-examples/examples/support_gfx/mod.rs b/imgui-examples/examples/support_gfx/mod.rs index a1c1348..94530a4 100644 --- a/imgui-examples/examples/support_gfx/mod.rs +++ b/imgui-examples/examples/support_gfx/mod.rs @@ -1,24 +1,25 @@ +use gfx::Device; +use glutin::{Event, WindowEvent}; use imgui::{FontGlyphRange, ImFontConfig, Context, Ui}; -use imgui_gfx_renderer::{Renderer, Shaders}; +use imgui_gfx_renderer::{GfxRenderer, Shaders}; use imgui_winit_support::{WinitPlatform, HiDpiMode}; use std::time::Instant; #[cfg(feature = "opengl")] pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ui: F) { - use gfx::{Device}; - type ColorFormat = gfx::format::Rgba8; type DepthFormat = gfx::format::DepthStencil; let mut events_loop = glutin::EventsLoop::new(); let context = glutin::ContextBuilder::new().with_vsync(true); - let window = glutin::WindowBuilder::new() - .with_title(title) + let builder = glutin::WindowBuilder::new() + .with_title(title.to_owned()) .with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64)); let (window, mut device, mut factory, mut main_color, mut main_depth) = - gfx_window_glutin::init::(window, context, &events_loop) - .expect("Failed to initalize graphics"); + gfx_window_glutin::init::(builder, context, &events_loop) + .expect("Failed to initalize graphics"); let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into(); + let shaders = { let version = device.get_info().shading_language; if version.is_embedded { @@ -84,7 +85,7 @@ pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ imgui.set_font_global_scale((1.0 / hidpi_factor) as f32); - let mut renderer = Renderer::init(&mut imgui, &mut factory, shaders, main_color.clone()) + let mut renderer = GfxRenderer::init(&mut imgui, &mut factory, shaders) .expect("Failed to initialize renderer"); let mut last_frame = Instant::now(); @@ -92,20 +93,14 @@ pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ loop { events_loop.poll_events(|event| { - use glutin::{ - Event, - WindowEvent::{CloseRequested, Resized}, - }; - platform.handle_event(imgui.io_mut(), &window, &event); if let Event::WindowEvent { event, .. } = event { match event { - Resized(_) => { - gfx_window_glutin::update_views(&window, &mut main_color, &mut main_depth); - renderer.update_render_target(main_color.clone()); - } - CloseRequested => quit = true, + WindowEvent::Resized(_) => { + gfx_window_glutin::update_views(&window, &mut main_color, &mut main_depth) + }, + WindowEvent::CloseRequested => quit = true, _ => (), } } @@ -125,7 +120,7 @@ pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ encoder.clear(&main_color, clear_color); renderer - .render(ui, &mut factory, &mut encoder) + .render(&mut factory, &mut encoder, &mut main_color, ui) .expect("Rendering failed"); encoder.flush(&mut device); window.swap_buffers().unwrap(); @@ -195,7 +190,7 @@ pub fn run bool>(title: String, clear_color: [f32; 4], mut run_ imgui.set_font_global_scale((1.0 / hidpi_factor) as f32); - let mut renderer = Renderer::init( + let mut renderer = GfxRenderer::init( &mut imgui, &mut factory, Shaders::HlslSm40, diff --git a/imgui-gfx-renderer/Cargo.toml b/imgui-gfx-renderer/Cargo.toml index 495036b..b61abaa 100644 --- a/imgui-gfx-renderer/Cargo.toml +++ b/imgui-gfx-renderer/Cargo.toml @@ -19,8 +19,7 @@ travis-ci = { repository = "Gekkio/imgui-rs" } [dependencies] gfx = "0.18" -imgui = { version = "0.0.24-pre", path = "../" } -imgui-sys = { version = "0.0.24-pre", path = "../imgui-sys", features = ["gfx"] } +imgui = { version = "0.0.24-pre", path = "../", features = ["gfx"] } [target.'cfg(windows)'.build-dependencies] winapi = { version = "0.3", features = ["d3dcompiler"] } diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 4f1ca5d..7edaf5c 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -1,123 +1,44 @@ +use gfx::format::BlendFormat; use gfx::handle::{Buffer, RenderTargetView}; use gfx::memory::Bind; -use gfx::pso::{PipelineData, PipelineState}; +use gfx::pso::PipelineState; use gfx::texture::{FilterMethod, SamplerInfo, WrapMode}; use gfx::traits::FactoryExt; use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; -use imgui::{Context, DrawList, ImDrawIdx, ImDrawVert, ImTexture, Textures, Ui}; - -pub type RendererResult = Result; +use imgui::internal::RawWrapper; +use imgui::{DrawCmd, DrawCmdParams, DrawIdx, DrawVert, ImString, TextureId, Textures, Ui}; +use std::usize; #[derive(Clone, Debug)] -pub enum RendererError { +pub enum GfxRendererError { Update(gfx::UpdateError), Buffer(gfx::buffer::CreationError), Pipeline(gfx::PipelineStateError), Combined(gfx::CombinedError), - BadTexture(ImTexture), + BadTexture(TextureId), } -impl From> for RendererError { - fn from(e: gfx::UpdateError) -> RendererError { - RendererError::Update(e) +impl From> for GfxRendererError { + fn from(e: gfx::UpdateError) -> GfxRendererError { + GfxRendererError::Update(e) } } -impl From for RendererError { - fn from(e: gfx::buffer::CreationError) -> RendererError { - RendererError::Buffer(e) +impl From for GfxRendererError { + fn from(e: gfx::buffer::CreationError) -> GfxRendererError { + GfxRendererError::Buffer(e) } } -impl From> for RendererError { - fn from(e: gfx::PipelineStateError) -> RendererError { - RendererError::Pipeline(e) +impl From> for GfxRendererError { + fn from(e: gfx::PipelineStateError) -> GfxRendererError { + GfxRendererError::Pipeline(e) } } -impl From for RendererError { - fn from(e: gfx::CombinedError) -> RendererError { - RendererError::Combined(e) - } -} - -// 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 crate::*; - #[allow(unused_imports)] - use gfx::gfx_pipeline_inner; - 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);)* - } - } - } - } -} - -#[cfg(feature = "directx")] -mod constants { - use gfx::gfx_constant_struct_meta; - use gfx::gfx_impl_struct_meta; - - gfx::gfx_constant_struct! { - Constants { - // `matrix` is a reserved keyword in HLSL - matrix: [[f32; 4]; 4] = "matrix_", - } - } -} - -// This version of `pipe` uses a single uniform for `matrix`, used in GLSL shaders -#[cfg(not(feature = "directx"))] -extended_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 = (), - } -} - -// This version of `pipe` uses a constant buffer containing `matrix`, used in the HLSL shader -#[cfg(feature = "directx")] -extended_defines! { - pipeline pipe { - vertex_buffer: gfx::VertexBuffer = (), - constants: gfx::ConstantBuffer = "Constants", - tex: gfx::TextureSampler<[f32; 4]> = "tex", - out: gfx::BlendTarget = ( - "Target0", - gfx::state::ColorMask::all(), - gfx::preset::blend::ALPHA, - ), - scissor: gfx::Scissor = (), +impl From for GfxRendererError { + fn from(e: gfx::CombinedError) -> GfxRendererError { + GfxRendererError::Combined(e) } } @@ -180,36 +101,42 @@ pub type Texture = ( gfx::handle::Sampler, ); -pub struct Renderer { - bundle: Bundle>, - index_buffer: Buffer, +pub struct GfxRenderer { + vertex_buffer: Buffer, + index_buffer: Buffer, + slice: Slice, + pso: PipelineState>, + font_texture: Texture, textures: Textures>, #[cfg(feature = "directx")] constants: Buffer, } -impl Renderer { +impl GfxRenderer +where + Cf: BlendFormat, + R: Resources, +{ pub fn init>( - imgui: &mut Context, + ctx: &mut imgui::Context, factory: &mut F, shaders: Shaders, - out: RenderTargetView, - ) -> RendererResult> { + ) -> Result, GfxRendererError> { 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::( + let pso = factory.create_pipeline_simple(vs_code, ps_code, pipeline::new::())?; + let vertex_buffer = factory.create_buffer::( 256, gfx::buffer::Role::Vertex, gfx::memory::Usage::Dynamic, Bind::empty(), )?; - let index_buffer = factory.create_buffer::( + let index_buffer = factory.create_buffer::( 256, gfx::buffer::Role::Index, gfx::memory::Usage::Dynamic, Bind::empty(), )?; - let (_, texture) = imgui.prepare_texture(|handle| { + let (_, texture) = ctx.prepare_texture(|handle| { factory.create_texture_immutable_u8::( gfx::texture::Kind::D2( handle.width as u16, @@ -222,9 +149,7 @@ impl Renderer { })?; let sampler = 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)); + ctx.set_font_texture_id(TextureId::from(usize::MAX)); let slice = Slice { start: 0, @@ -233,113 +158,113 @@ impl Renderer { instances: None, buffer: index_buffer.clone().into_index_buffer(factory), }; - Ok(Renderer { - bundle: Bundle { - slice, - pso, - vertex_buffer, - out, - }, + ctx.set_renderer_name(Some(ImString::from(format!( + "imgui-gfx-renderer {}", + env!("CARGO_PKG_VERSION") + )))); + Ok(GfxRenderer { + vertex_buffer, index_buffer, - textures, + slice, + pso, + font_texture: (texture, sampler), + textures: Textures::new(), #[cfg(feature = "directx")] constants: factory.create_constant_buffer(1), }) } - - pub fn update_render_target(&mut self, out: RenderTargetView) { - self.bundle.out = out; - } - pub fn textures(&mut self) -> &mut Textures> { &mut self.textures } - - pub fn render<'a, F: Factory, C: CommandBuffer>( + pub fn render<'ui, F: Factory, C: CommandBuffer>( &mut self, - ui: Ui<'a>, factory: &mut F, encoder: &mut Encoder, - ) -> RendererResult<()> { - let [width, height] = ui.io().display_size; - let hidpi_factor = ui.io().display_framebuffer_scale[0]; - - if !(width > 0.0 && height > 0.0) { + target: &mut RenderTargetView, + ui: Ui<'ui>, + ) -> Result<(), GfxRendererError> { + let draw_data = ui.render(); + let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0]; + let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1]; + if !(fb_width > 0.0 && fb_height > 0.0) { return Ok(()); } - let fb_size = ( - (width * hidpi_factor) as f32, - (height * hidpi_factor) as f32, - ); - + let left = draw_data.display_pos[0]; + let right = draw_data.display_pos[0] + draw_data.display_size[0]; + let top = draw_data.display_pos[1]; + let bottom = draw_data.display_pos[1] + draw_data.display_size[1]; let matrix = [ - [(2.0 / width) as f32, 0.0, 0.0, 0.0], - [0.0, (2.0 / -height) as f32, 0.0, 0.0], + [(2.0 / (right - left)), 0.0, 0.0, 0.0], + [0.0, (2.0 / (top - bottom)), 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], - [-1.0, 1.0, 0.0, 1.0], + [ + (right + left) / (left - right), + (top + bottom) / (bottom - top), + 0.0, + 1.0, + ], ]; + let clip_off = draw_data.display_pos; + let clip_scale = draw_data.framebuffer_scale; + for draw_list in draw_data.draw_lists() { + self.upload_vertex_buffer(factory, encoder, draw_list.vtx_buffer())?; + self.upload_index_buffer(factory, encoder, draw_list.idx_buffer())?; + self.slice.start = 0; + for cmd in draw_list.commands() { + match cmd { + DrawCmd::Elements { + count, + cmd_params: + DrawCmdParams { + clip_rect, + texture_id, + }, + } => { + let clip_rect = [ + (clip_rect[0] - clip_off[0]) * clip_scale[0], + (clip_rect[1] - clip_off[1]) * clip_scale[1], + (clip_rect[2] - clip_off[0]) * clip_scale[0], + (clip_rect[3] - clip_off[1]) * clip_scale[1], + ]; - 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, &matrix)?; + self.slice.end = self.slice.start + count as u32; + + if clip_rect[0] < fb_width + && clip_rect[1] < fb_height + && clip_rect[2] >= 0.0 + && clip_rect[3] >= 0.0 + { + let scissor = Rect { + x: f32::max(0.0, clip_rect[0]).floor() as u16, + y: f32::max(0.0, clip_rect[1]).floor() as u16, + w: (clip_rect[2] - clip_rect[0]).abs().ceil() as u16, + h: (clip_rect[3] - clip_rect[1]).abs().ceil() as u16, + }; + let tex = self.lookup_texture(texture_id)?; + #[cfg(feature = "directx")] + { + let constants = constants::Constants { matrix }; + encoder.update_constant_buffer(&self.constants, &constants); + } + let data = pipeline::Data { + vertex_buffer: &self.vertex_buffer, + #[cfg(not(feature = "directx"))] + matrix: &matrix, + #[cfg(feature = "directx")] + constants: &self.constants, + tex, + scissor: &scissor, + target, + }; + encoder.draw(&self.slice, &self.pso, &data); + } + self.slice.start = self.slice.end; + } + DrawCmd::RawCallback { callback, raw_cmd } => unsafe { + callback(draw_list.raw(), raw_cmd) + }, + } } - 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), - matrix: &[[f32; 4]; 4], - ) -> 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 { - let texture_id = cmd.TextureId.into(); - let tex = self - .textures - .get(texture_id) - .ok_or_else(|| RendererError::BadTexture(texture_id))?; - - self.bundle.slice.end = self.bundle.slice.start + cmd.ElemCount; - let scissor = Rect { - x: cmd.ClipRect.x.max(0.0).min(fb_width).round() as u16, - y: cmd.ClipRect.y.max(0.0).min(fb_height).round() as u16, - w: (cmd.ClipRect.z - cmd.ClipRect.x) - .abs() - .min(fb_width) - .round() as u16, - h: (cmd.ClipRect.w - cmd.ClipRect.y) - .abs() - .min(fb_height) - .round() as u16, - }; - - #[cfg(feature = "directx")] - { - let constants = constants::Constants { matrix: *matrix }; - encoder.update_constant_buffer(&self.constants, &constants); - } - - let data = pipe::BorrowedData { - vertex_buffer: &self.bundle.vertex_buffer, - #[cfg(not(feature = "directx"))] - matrix, - #[cfg(feature = "directx")] - constants: &self.constants, - 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(()) } @@ -347,42 +272,257 @@ impl Renderer { &mut self, factory: &mut F, encoder: &mut Encoder, - vtx_buffer: &[ImDrawVert], - ) -> RendererResult<()> { - if self.bundle.vertex_buffer.len() < vtx_buffer.len() { - self.bundle.vertex_buffer = factory.create_buffer::( + vtx_buffer: &[DrawVert], + ) -> Result<(), GfxRendererError> { + if self.vertex_buffer.len() < vtx_buffer.len() { + self.vertex_buffer = factory.create_buffer::( vtx_buffer.len(), gfx::buffer::Role::Vertex, gfx::memory::Usage::Dynamic, Bind::empty(), )?; } - encoder.update_buffer(&self.bundle.vertex_buffer, vtx_buffer, 0)?; + encoder.update_buffer(&self.vertex_buffer, vtx_buffer, 0)?; Ok(()) } fn upload_index_buffer, C: CommandBuffer>( &mut self, factory: &mut F, encoder: &mut Encoder, - idx_buffer: &[ImDrawIdx], - ) -> RendererResult<()> { + idx_buffer: &[DrawIdx], + ) -> Result<(), GfxRendererError> { if self.index_buffer.len() < idx_buffer.len() { - self.index_buffer = factory.create_buffer::( + 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); + self.slice.buffer = self.index_buffer.clone().into_index_buffer(factory); } encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?; Ok(()) } + fn lookup_texture(&self, texture_id: TextureId) -> Result<&Texture, GfxRendererError> { + if texture_id.id() == usize::MAX { + Ok(&self.font_texture) + } else if let Some(texture) = self.textures.get(texture_id) { + Ok(texture) + } else { + Err(GfxRendererError::BadTexture(texture_id)) + } + } } -struct Bundle> { - slice: Slice, - pso: PipelineState, - vertex_buffer: Buffer, - out: RenderTargetView, +#[cfg(feature = "directx")] +mod constants { + use gfx::gfx_constant_struct_meta; + use gfx::gfx_impl_struct_meta; + + gfx::gfx_constant_struct! { + Constants { + // `matrix` is a reserved keyword in HLSL + matrix: [[f32; 4]; 4] = "matrix_", + } + } +} + +// This is basically what gfx_pipeline generates, but with some changes: +// +// * Data struct contains references to avoid copying +// * Everything is parameterized with BlendFormat +// * Pipeline init is specialized for our structs +mod pipeline { + use gfx::format::BlendFormat; + use gfx::handle::Manager; + use gfx::preset::blend; + use gfx::pso::{ + AccessInfo, DataBind, DataLink, Descriptor, InitError, PipelineData, PipelineInit, + RawDataSet, + }; + use gfx::state::ColorMask; + use gfx::{ProgramInfo, Resources}; + use imgui::DrawVert; + + #[derive(Clone, Debug, PartialEq)] + pub struct Data<'a, R: Resources, Cf: BlendFormat + 'a> { + pub vertex_buffer: &'a as DataBind>::Data, + #[cfg(not(feature = "directx"))] + pub matrix: &'a as DataBind>::Data, + #[cfg(feature = "directx")] + pub constants: &'a as DataBind>::Data, + pub tex: &'a as DataBind>::Data, + pub target: &'a as DataBind>::Data, + pub scissor: &'a >::Data, + } + + #[derive(Clone, Debug, Hash, PartialEq)] + pub struct Meta { + vertex_buffer: gfx::VertexBuffer, + #[cfg(not(feature = "directx"))] + matrix: gfx::Global<[[f32; 4]; 4]>, + #[cfg(feature = "directx")] + constants: gfx::ConstantBuffer, + tex: gfx::TextureSampler<[f32; 4]>, + target: gfx::BlendTarget, + scissor: gfx::Scissor, + } + + #[derive(Clone, Debug, PartialEq)] + pub struct Init<'a, Cf: BlendFormat> { + vertex_buffer: as DataLink<'a>>::Init, + #[cfg(not(feature = "directx"))] + matrix: as DataLink<'a>>::Init, + #[cfg(feature = "directx")] + constants: as DataLink<'a>>::Init, + tex: as DataLink<'a>>::Init, + target: as DataLink<'a>>::Init, + scissor: >::Init, + } + + impl<'a, Cf: BlendFormat> PipelineInit for Init<'a, Cf> { + type Meta = Meta; + fn link_to<'s>( + &self, + desc: &mut Descriptor, + info: &'s ProgramInfo, + ) -> Result, InitError<&'s str>> { + let mut meta = Meta { + vertex_buffer: DataLink::new(), + #[cfg(not(feature = "directx"))] + matrix: DataLink::new(), + #[cfg(feature = "directx")] + constants: DataLink::new(), + tex: DataLink::new(), + target: DataLink::new(), + scissor: DataLink::new(), + }; + if let Some(d) = meta + .vertex_buffer + .link_vertex_buffer(0, &self.vertex_buffer) + { + assert!(meta.vertex_buffer.is_active()); + desc.vertex_buffers[0] = Some(d); + } + for at in &info.vertex_attributes { + match meta.vertex_buffer.link_input(at, &self.vertex_buffer) { + Some(Ok(d)) => { + assert!(meta.vertex_buffer.is_active()); + desc.attributes[at.slot as usize] = Some(d); + continue; + } + Some(Err(fm)) => return Err(InitError::VertexImport(&at.name, Some(fm))), + None => return Err(InitError::VertexImport(&at.name, None)), + } + } + for gc in &info.globals { + #[cfg(not(feature = "directx"))] + { + match meta.matrix.link_global_constant(gc, &self.matrix) { + Some(Ok(())) => assert!(meta.matrix.is_active()), + Some(Err(e)) => return Err(InitError::GlobalConstant(&gc.name, Some(e))), + None => return Err(InitError::GlobalConstant(&gc.name, None)), + } + } + #[cfg(feature = "directx")] + { + match meta.constants.link_global_constant(gc, &self.constants) { + Some(Ok(())) => assert!(meta.constants.is_active()), + Some(Err(e)) => return Err(InitError::GlobalConstant(&gc.name, Some(e))), + None => return Err(InitError::GlobalConstant(&gc.name, None)), + } + } + } + for srv in &info.textures { + match meta.tex.link_resource_view(srv, &self.tex) { + Some(Ok(d)) => { + assert!(meta.tex.is_active()); + desc.resource_views[srv.slot as usize] = Some(d); + } + Some(Err(_)) => return Err(InitError::ResourceView(&srv.name, Some(()))), + None => return Err(InitError::ResourceView(&srv.name, None)), + } + } + for sm in &info.samplers { + match meta.tex.link_sampler(sm, &self.tex) { + Some(d) => { + assert!(meta.tex.is_active()); + desc.samplers[sm.slot as usize] = Some(d); + } + None => return Err(InitError::Sampler(&sm.name, None)), + } + } + for out in &info.outputs { + match meta.target.link_output(out, &self.target) { + Some(Ok(d)) => { + assert!(meta.target.is_active()); + desc.color_targets[out.slot as usize] = Some(d); + } + Some(Err(fm)) => return Err(InitError::PixelExport(&out.name, Some(fm))), + None => return Err(InitError::PixelExport(&out.name, None)), + } + } + if !info.knows_outputs { + use gfx::shade::core::*; + let mut out = OutputVar { + name: String::new(), + slot: 0, + base_type: BaseType::F32, + container: ContainerType::Vector(4), + }; + match meta.target.link_output(&out, &self.target) { + Some(Ok(d)) => { + assert!(meta.target.is_active()); + desc.color_targets[out.slot as usize] = Some(d); + out.slot += 1; + } + Some(Err(fm)) => return Err(InitError::PixelExport(&"!known", Some(fm))), + None => (), + } + } + if meta.scissor.link_scissor() { + assert!(meta.scissor.is_active()); + desc.scissor = true; + } + Ok(meta) + } + } + + impl<'a, R: Resources, Cf: BlendFormat> PipelineData for Data<'a, R, Cf> { + type Meta = Meta; + fn bake_to( + &self, + out: &mut RawDataSet, + meta: &Meta, + man: &mut Manager, + access: &mut AccessInfo, + ) { + meta.vertex_buffer + .bind_to(out, self.vertex_buffer, man, access); + #[cfg(not(feature = "directx"))] + { + meta.matrix.bind_to(out, self.matrix, man, access); + } + #[cfg(feature = "directx")] + { + meta.constants.bind_to(out, self.constants, man, access); + } + meta.tex.bind_to(out, self.tex, man, access); + meta.target.bind_to(out, self.target, man, access); + meta.scissor.bind_to(out, self.scissor, man, access); + } + } + + pub fn new() -> Init<'static, Cf> { + Init { + vertex_buffer: (), + #[cfg(not(feature = "directx"))] + matrix: "matrix", + #[cfg(feature = "directx")] + constants: "constants", + tex: "tex", + target: ("Target0", ColorMask::all(), blend::ALPHA), + scissor: (), + } + } } diff --git a/imgui-glium-examples/Cargo.lock b/imgui-glium-examples/Cargo.lock index b0e333d..bf26d60 100644 --- a/imgui-glium-examples/Cargo.lock +++ b/imgui-glium-examples/Cargo.lock @@ -372,6 +372,7 @@ name = "imgui" version = "0.0.24-pre" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "glium 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "imgui-sys 0.0.24-pre", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -394,7 +395,6 @@ version = "0.0.24-pre" dependencies = [ "glium 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "imgui 0.0.24-pre", - "imgui-sys 0.0.24-pre", ] [[package]] @@ -402,7 +402,6 @@ name = "imgui-sys" version = "0.0.24-pre" dependencies = [ "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", - "glium 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/imgui-glium-examples/examples/support/mod.rs b/imgui-glium-examples/examples/support/mod.rs index e730a5e..f750e1d 100644 --- a/imgui-glium-examples/examples/support/mod.rs +++ b/imgui-glium-examples/examples/support/mod.rs @@ -7,7 +7,7 @@ use imgui_winit_support::{HiDpiMode, WinitPlatform}; use std::rc::Rc; use std::time::Instant; -pub type Textures = imgui::Textures; +pub type Textures = imgui::Textures>; pub fn run(title: String, clear_color: [f32; 4], mut run_ui: F) where @@ -15,7 +15,7 @@ where { use glium::glutin; use glium::{Display, Surface}; - use imgui_glium_renderer::Renderer; + use imgui_glium_renderer::GliumRenderer; let mut events_loop = glutin::EventsLoop::new(); let context = glutin::ContextBuilder::new().with_vsync(true); @@ -55,7 +55,7 @@ where imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; - let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer"); + let mut renderer = GliumRenderer::init(&mut imgui, &display).expect("Failed to initialize renderer"); let mut last_frame = Instant::now(); let mut quit = false; diff --git a/imgui-glium-renderer/Cargo.toml b/imgui-glium-renderer/Cargo.toml index d456eaf..8fa8a42 100644 --- a/imgui-glium-renderer/Cargo.toml +++ b/imgui-glium-renderer/Cargo.toml @@ -14,5 +14,4 @@ travis-ci = { repository = "Gekkio/imgui-rs" } [dependencies] glium = { version = "0.25", default-features = false } -imgui = { version = "0.0.24-pre", path = "../" } -imgui-sys = { version = "0.0.24-pre", path = "../imgui-sys", features = ["glium"] } +imgui = { version = "0.0.24-pre", path = "../", features = ["glium"] } diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index 6de0826..9104ef9 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -1,29 +1,32 @@ use glium::backend::{Context, Facade}; use glium::index::{self, PrimitiveType}; -use glium::program; -use glium::texture; -use glium::vertex; -use glium::{uniform, DrawError, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; -use imgui::{self, DrawList, ImTexture, Textures, Ui}; +use glium::program::ProgramChooserCreationError; +use glium::texture::{ClientFormat, RawImage2d, TextureCreationError}; +use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter}; +use glium::{ + program, uniform, vertex, Blend, DrawError, DrawParameters, IndexBuffer, Program, Rect, + Surface, Texture2d, VertexBuffer, +}; +use imgui::internal::RawWrapper; +use imgui::{DrawCmd, DrawCmdParams, ImString, TextureId, Textures, Ui}; use std::borrow::Cow; use std::fmt; use std::rc::Rc; - -pub type RendererResult = Result; +use std::usize; #[derive(Clone, Debug)] -pub enum RendererError { +pub enum GliumRendererError { Vertex(vertex::BufferCreationError), Index(index::BufferCreationError), - Program(program::ProgramChooserCreationError), - Texture(texture::TextureCreationError), + Program(ProgramChooserCreationError), + Texture(TextureCreationError), Draw(DrawError), - BadTexture(ImTexture), + BadTexture(TextureId), } -impl fmt::Display for RendererError { +impl fmt::Display for GliumRendererError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::RendererError::*; + use self::GliumRendererError::*; match *self { Vertex(_) => write!(f, "Vertex buffer creation failed"), Index(_) => write!(f, "Index buffer creation failed"), @@ -35,157 +38,183 @@ impl fmt::Display for RendererError { } } -impl From for RendererError { - fn from(e: vertex::BufferCreationError) -> RendererError { - RendererError::Vertex(e) +impl From for GliumRendererError { + fn from(e: vertex::BufferCreationError) -> GliumRendererError { + GliumRendererError::Vertex(e) } } -impl From for RendererError { - fn from(e: index::BufferCreationError) -> RendererError { - RendererError::Index(e) +impl From for GliumRendererError { + fn from(e: index::BufferCreationError) -> GliumRendererError { + GliumRendererError::Index(e) } } -impl From for RendererError { - fn from(e: program::ProgramChooserCreationError) -> RendererError { - RendererError::Program(e) +impl From for GliumRendererError { + fn from(e: ProgramChooserCreationError) -> GliumRendererError { + GliumRendererError::Program(e) } } -impl From for RendererError { - fn from(e: texture::TextureCreationError) -> RendererError { - RendererError::Texture(e) +impl From for GliumRendererError { + fn from(e: TextureCreationError) -> GliumRendererError { + GliumRendererError::Texture(e) } } -impl From for RendererError { - fn from(e: DrawError) -> RendererError { - RendererError::Draw(e) +impl From for GliumRendererError { + fn from(e: DrawError) -> GliumRendererError { + GliumRendererError::Draw(e) } } -pub struct Renderer { +pub struct GliumRenderer { ctx: Rc, - device_objects: DeviceObjects, + program: Program, + font_texture: Texture2d, + textures: Textures>, } -impl Renderer { - pub fn init(imgui: &mut imgui::Context, ctx: &F) -> RendererResult { - let device_objects = DeviceObjects::init(imgui, ctx)?; - Ok(Renderer { - ctx: Rc::clone(ctx.get_context()), - device_objects, +impl GliumRenderer { + pub fn init( + ctx: &mut imgui::Context, + facade: &F, + ) -> Result { + let program = compile_default_program(facade)?; + let font_texture = ctx.prepare_texture(|handle| { + let data = RawImage2d { + data: Cow::Borrowed(handle.pixels), + width: handle.width, + height: handle.height, + format: ClientFormat::U8U8U8U8, + }; + Texture2d::new(facade, data) + })?; + ctx.set_font_texture_id(TextureId::from(usize::MAX)); + ctx.set_renderer_name(Some(ImString::from(format!( + "imgui-glium-renderer {}", + env!("CARGO_PKG_VERSION") + )))); + Ok(GliumRenderer { + ctx: Rc::clone(facade.get_context()), + program, + font_texture, + textures: Textures::new(), }) } - - pub fn textures(&mut self) -> &mut Textures { - &mut self.device_objects.textures + pub fn textures(&mut self) -> &mut Textures> { + &mut self.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 [width, height] = ui.io().display_size; - let hidpi_factor = ui.io().display_framebuffer_scale[0]; - if !(width > 0.0 && height > 0.0) { + fn lookup_texture(&self, texture_id: TextureId) -> Result<&Texture2d, GliumRendererError> { + if texture_id.id() == usize::MAX { + Ok(&self.font_texture) + } else if let Some(texture) = self.textures.get(texture_id) { + Ok(texture) + } else { + Err(GliumRendererError::BadTexture(texture_id)) + } + } + pub fn render<'ui, T: Surface>( + &mut self, + target: &mut T, + ui: Ui<'ui>, + ) -> Result<(), GliumRendererError> { + let draw_data = ui.render(); + let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0]; + let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1]; + if !(fb_width > 0.0 && fb_height > 0.0) { return Ok(()); } - let fb_size = ( - (width * hidpi_factor) as f32, - (height * hidpi_factor) as f32, - ); - + let _ = self.ctx.insert_debug_marker("imgui-rs: starting rendering"); + let left = draw_data.display_pos[0]; + let right = draw_data.display_pos[0] + draw_data.display_size[0]; + let top = draw_data.display_pos[1]; + let bottom = draw_data.display_pos[1] + draw_data.display_size[1]; let matrix = [ - [(2.0 / width) as f32, 0.0, 0.0, 0.0], - [0.0, (2.0 / -height) as f32, 0.0, 0.0], + [(2.0 / (right - left)), 0.0, 0.0, 0.0], + [0.0, (2.0 / (top - bottom)), 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], - [-1.0, 1.0, 0.0, 1.0], + [ + (right + left) / (left - right), + (top + bottom) / (bottom - top), + 0.0, + 1.0, + ], ]; - let result = 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(surface, &draw_list, fb_size, matrix)?; - } - Ok(()) - }); - let _ = self.ctx.insert_debug_marker("imgui-rs: rendering finished"); - result - } - - fn render_draw_list<'a, S: Surface>( - &mut self, - surface: &mut S, - draw_list: &DrawList<'a>, - fb_size: (f32, f32), - matrix: [[f32; 4]; 4], - ) -> RendererResult<()> { - use glium::{Blend, DrawParameters, Rect}; - - let (fb_width, fb_height) = fb_size; - - let vtx_buffer = VertexBuffer::immutable(&self.ctx, draw_list.vtx_buffer)?; - let idx_buffer = IndexBuffer::immutable( - &self.ctx, - PrimitiveType::TrianglesList, - draw_list.idx_buffer, - )?; - - let mut idx_start = 0; - for cmd in draw_list.cmd_buffer { - let texture_id = cmd.TextureId.into(); - let texture = self - .device_objects - .textures - .get(texture_id) - .ok_or_else(|| RendererError::BadTexture(texture_id))?; - - let idx_end = idx_start + cmd.ElemCount as usize; - - surface.draw( - &vtx_buffer, - &idx_buffer - .slice(idx_start..idx_end) - .expect("Invalid index buffer range"), - &self.device_objects.program, - &uniform! { - matrix: matrix, - tex: texture.sampled() - }, - &DrawParameters { - blend: Blend::alpha_blending(), - scissor: Some(Rect { - left: cmd.ClipRect.x.max(0.0).min(fb_width).round() as u32, - bottom: (fb_height - cmd.ClipRect.w).max(0.0).min(fb_width).round() as u32, - width: (cmd.ClipRect.z - cmd.ClipRect.x) - .abs() - .min(fb_width) - .round() as u32, - height: (cmd.ClipRect.w - cmd.ClipRect.y) - .abs() - .min(fb_height) - .round() as u32, - }), - ..DrawParameters::default() - }, + let clip_off = draw_data.display_pos; + let clip_scale = draw_data.framebuffer_scale; + for draw_list in draw_data.draw_lists() { + let vtx_buffer = VertexBuffer::immutable(&self.ctx, draw_list.vtx_buffer())?; + let idx_buffer = IndexBuffer::immutable( + &self.ctx, + PrimitiveType::TrianglesList, + draw_list.idx_buffer(), )?; + let mut idx_start = 0; + for cmd in draw_list.commands() { + match cmd { + DrawCmd::Elements { + count, + cmd_params: + DrawCmdParams { + clip_rect, + texture_id, + }, + } => { + let idx_end = idx_start + count; + let clip_rect = [ + (clip_rect[0] - clip_off[0]) * clip_scale[0], + (clip_rect[1] - clip_off[1]) * clip_scale[1], + (clip_rect[2] - clip_off[0]) * clip_scale[0], + (clip_rect[3] - clip_off[1]) * clip_scale[1], + ]; - idx_start = idx_end; + if clip_rect[0] < fb_width + && clip_rect[1] < fb_height + && clip_rect[2] >= 0.0 + && clip_rect[3] >= 0.0 + { + target.draw( + &vtx_buffer, + &idx_buffer + .slice(idx_start..idx_end) + .expect("Invalid index buffer range"), + &self.program, + &uniform! { + matrix: matrix, + tex: self.lookup_texture(texture_id)?.sampled() + .minify_filter(MinifySamplerFilter::Linear) + .magnify_filter(MagnifySamplerFilter::Linear) + }, + &DrawParameters { + blend: Blend::alpha_blending(), + scissor: Some(Rect { + left: f32::max(0.0, clip_rect[0]).floor() as u32, + bottom: f32::max(0.0, fb_height - clip_rect[3]).floor() + as u32, + width: (clip_rect[2] - clip_rect[0]).abs().ceil() as u32, + height: (clip_rect[3] - clip_rect[1]).abs().ceil() as u32, + }), + ..DrawParameters::default() + }, + )?; + } + idx_start = idx_end; + } + DrawCmd::RawCallback { callback, raw_cmd } => unsafe { + callback(draw_list.raw(), raw_cmd) + }, + } + } } - + let _ = self.ctx.insert_debug_marker("imgui-rs: rendering finished"); Ok(()) } } -pub struct DeviceObjects { - program: Program, - textures: Textures, -} - -fn compile_default_program( - ctx: &F, -) -> Result { +fn compile_default_program(facade: &F) -> Result { program!( - ctx, + facade, 400 => { vertex: include_str!("shader/glsl_400.vert"), fragment: include_str!("shader/glsl_400.frag"), @@ -218,24 +247,3 @@ fn compile_default_program( }, ) } - -impl DeviceObjects { - pub fn init(im_gui: &mut imgui::Context, ctx: &F) -> RendererResult { - use glium::texture::{ClientFormat, RawImage2d}; - - let program = compile_default_program(ctx)?; - let texture = 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) - })?; - let mut textures = Textures::new(); - im_gui.set_font_texture_id(textures.insert(texture)); - - Ok(DeviceObjects { program, textures }) - } -} diff --git a/imgui-sys/Cargo.toml b/imgui-sys/Cargo.toml index 54bbbd1..d811393 100644 --- a/imgui-sys/Cargo.toml +++ b/imgui-sys/Cargo.toml @@ -13,9 +13,5 @@ build = "build.rs" [badges] travis-ci = { repository = "Gekkio/imgui-rs" } -[dependencies] -glium = { version = "0.25", default-features = false, optional = true } -gfx = { version = "0.18", optional = true } - [build-dependencies] cc = "1.0" diff --git a/imgui-sys/src/gfx_support.rs b/imgui-sys/src/gfx_support.rs deleted file mode 100644 index 5fae1fc..0000000 --- a/imgui-sys/src/gfx_support.rs +++ /dev/null @@ -1,55 +0,0 @@ -use gfx::format::{Format, Formatted, U8Norm}; -use gfx::gfx_format; -use gfx::pso::buffer::{ElemOffset, Element, Structure}; -use gfx::traits::Pod; -use std::mem; - -use crate::{ImDrawVert, ImVec2}; - -unsafe impl Pod for ImDrawVert {} - -impl Structure for ImDrawVert { - fn query(name: &str) -> Option> { - // array query hack from gfx_impl_struct_meta macro - let (sub_name, big_offset) = { - let mut split = name.split(|c| c == '[' || c == ']'); - let _ = split.next().unwrap(); - match split.next() { - Some(s) => { - let array_id: ElemOffset = s.parse().unwrap(); - let sub_name = match split.next() { - Some(s) if s.starts_with('.') => &s[1..], - _ => name, - }; - ( - sub_name, - array_id * (mem::size_of::() as ElemOffset), - ) - } - None => (name, 0), - } - }; - let dummy: &ImDrawVert = unsafe { mem::transmute(0usize) }; - match sub_name { - "pos" => Some(Element { - format: ::get_format(), - offset: unsafe { mem::transmute::<_, usize>(&dummy.pos) } as ElemOffset - + big_offset, - }), - "uv" => Some(Element { - format: ::get_format(), - offset: unsafe { mem::transmute::<_, usize>(&dummy.uv) } as ElemOffset + big_offset, - }), - "col" => Some(Element { - format: <[U8Norm; 4] as Formatted>::get_format(), - offset: unsafe { mem::transmute::<_, usize>(&dummy.col) } as ElemOffset - + big_offset, - }), - _ => None, - } - } -} - -gfx_format! { - ImVec2: R32_G32 = Vec2 -} diff --git a/imgui-sys/src/glium_support.rs b/imgui-sys/src/glium_support.rs deleted file mode 100644 index 0f16955..0000000 --- a/imgui-sys/src/glium_support.rs +++ /dev/null @@ -1,46 +0,0 @@ -use glium::vertex::{Attribute, AttributeType, Vertex, VertexFormat}; -use std::borrow::Cow; -use std::mem; -use std::os::raw::c_float; - -use crate::{ImDrawVert, ImVec2, ImVec4}; - -unsafe impl Attribute for ImVec2 { - fn get_type() -> AttributeType { - <(c_float, c_float) as Attribute>::get_type() - } -} - -unsafe impl Attribute for ImVec4 { - fn get_type() -> AttributeType { - <(c_float, c_float, c_float, c_float) as Attribute>::get_type() - } -} - -impl Vertex for ImDrawVert { - fn build_bindings() -> VertexFormat { - unsafe { - let dummy: &ImDrawVert = mem::transmute(0usize); - Cow::Owned(vec![ - ( - "pos".into(), - mem::transmute(&dummy.pos), - ::get_type(), - false, - ), - ( - "uv".into(), - mem::transmute(&dummy.uv), - ::get_type(), - false, - ), - ( - "col".into(), - mem::transmute(&dummy.col), - AttributeType::U8U8U8U8, - false, - ), - ]) - } - } -} diff --git a/imgui-sys/src/lib.rs b/imgui-sys/src/lib.rs index 84bab15..1bd9614 100644 --- a/imgui-sys/src/lib.rs +++ b/imgui-sys/src/lib.rs @@ -2,12 +2,6 @@ mod bindings; pub use crate::bindings::*; -#[cfg(feature = "gfx")] -mod gfx_support; - -#[cfg(feature = "glium")] -mod glium_support; - impl ImVec2 { #[inline] pub fn new(x: f32, y: f32) -> ImVec2 { diff --git a/src/image.rs b/src/image.rs index 0b8a282..a829afd 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,29 +1,10 @@ -use super::{ImVec2, ImVec4, Ui}; use std::collections::HashMap; use std::marker::PhantomData; use std::os::raw::c_void; -use sys; -#[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) - } -} +use crate::render::renderer::TextureId; +use crate::sys; +use crate::{ImVec2, ImVec4, Ui}; /// Represent an image about to be drawn. /// See [`Ui::image`]. @@ -31,7 +12,7 @@ impl From<*mut c_void> for ImTexture { /// Create your image using the builder pattern then [`Image::build`] it. #[must_use] pub struct Image<'ui> { - texture_id: ImTexture, + texture_id: TextureId, size: ImVec2, uv0: ImVec2, uv1: ImVec2, @@ -41,7 +22,7 @@ pub struct Image<'ui> { } impl<'ui> Image<'ui> { - pub fn new(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self + pub fn new(_: &Ui<'ui>, texture_id: TextureId, size: S) -> Self where S: Into, { @@ -98,7 +79,7 @@ impl<'ui> Image<'ui> { pub fn build(self) { unsafe { sys::igImage( - self.texture_id.0 as *mut c_void, + self.texture_id.id() as *mut c_void, self.size, self.uv0, self.uv1, @@ -115,7 +96,7 @@ impl<'ui> Image<'ui> { /// Create your image button using the builder pattern then [`ImageButton::build`] it. #[must_use] pub struct ImageButton<'ui> { - texture_id: ImTexture, + texture_id: TextureId, size: ImVec2, uv0: ImVec2, uv1: ImVec2, @@ -126,7 +107,7 @@ pub struct ImageButton<'ui> { } impl<'ui> ImageButton<'ui> { - pub fn new(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self + pub fn new(_: &Ui<'ui>, texture_id: TextureId, size: S) -> Self where S: Into, { @@ -193,7 +174,7 @@ impl<'ui> ImageButton<'ui> { pub fn build(self) -> bool { unsafe { sys::igImageButton( - self.texture_id.0 as *mut c_void, + self.texture_id.id() as *mut c_void, self.size, self.uv0, self.uv1, @@ -220,22 +201,22 @@ impl Textures { } } - pub fn insert(&mut self, texture: T) -> ImTexture { + pub fn insert(&mut self, texture: T) -> TextureId { let id = self.next; self.textures.insert(id, texture); self.next += 1; - ImTexture(id) + TextureId::from(id) } - pub fn replace(&mut self, id: ImTexture, texture: T) -> Option { - self.textures.insert(id.0, texture) + pub fn replace(&mut self, id: TextureId, texture: T) -> Option { + self.textures.insert(id.id(), texture) } - pub fn remove(&mut self, id: ImTexture) -> Option { - self.textures.remove(&id.0) + pub fn remove(&mut self, id: TextureId) -> Option { + self.textures.remove(&id.id()) } - pub fn get(&self, id: ImTexture) -> Option<&T> { - self.textures.get(&id.0) + pub fn get(&self, id: TextureId) -> Option<&T> { + self.textures.get(&id.id()) } } diff --git a/src/lib.rs b/src/lib.rs index 59f086a..db22170 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,7 @@ pub extern crate imgui_sys as sys; extern crate lazy_static; use std::ffi::CStr; -use std::mem; -use std::os::raw::{c_char, c_float, c_int, c_uchar, c_void}; +use std::os::raw::{c_char, c_int, c_uchar, c_void}; use std::ptr; use std::slice; use std::str; @@ -21,7 +20,7 @@ pub use self::drag::{ DragInt4, DragIntRange2, }; pub use self::fonts::{FontGlyphRange, ImFont, ImFontAtlas, ImFontConfig}; -pub use self::image::{ImTexture, Image, ImageButton, Textures}; +pub use self::image::{Image, ImageButton}; pub use self::input::keyboard::*; pub use self::input::mouse::*; pub use self::input_widget::{ @@ -35,16 +34,15 @@ pub use self::plothistogram::PlotHistogram; pub use self::plotlines::PlotLines; pub use self::popup_modal::PopupModal; pub use self::progressbar::ProgressBar; +pub use self::render::draw_data::*; +pub use self::render::renderer::*; pub use self::sliders::{ SliderFloat, SliderFloat2, SliderFloat3, SliderFloat4, SliderInt, SliderInt2, SliderInt3, SliderInt4, }; pub use self::string::{ImStr, ImString}; pub use self::style::*; -pub use self::sys::{ - ImDrawIdx, ImDrawVert, ImVec2, - ImVec4, -}; +pub use self::sys::{ImVec2, ImVec4}; pub use self::trees::{CollapsingHeader, TreeNode}; pub use self::window::Window; pub use self::window_draw_list::{ChannelsSplit, ImColor, WindowDrawList}; @@ -58,7 +56,7 @@ mod fonts; mod image; mod input; mod input_widget; -mod internal; +pub mod internal; mod io; mod legacy; mod menus; @@ -66,6 +64,7 @@ mod plothistogram; mod plotlines; mod popup_modal; mod progressbar; +mod render; mod sliders; mod string; mod style; @@ -116,7 +115,7 @@ impl Context { }) } } - pub fn set_font_texture_id(&mut self, value: ImTexture) { + pub fn set_font_texture_id(&mut self, value: TextureId) { self.fonts().set_texture_id(value.id()); } pub fn set_ini_saving_rate(&mut self, value: f32) { @@ -310,82 +309,6 @@ impl Context { } } -pub struct DrawData<'a> { - raw: &'a mut sys::ImDrawData, -} - -impl<'a> DrawData<'a> { - pub fn is_valid(&self) -> bool { - self.raw.Valid - } - pub fn draw_list_count(&self) -> usize { - self.raw.CmdListsCount as usize - } - pub fn total_vtx_count(&self) -> usize { - self.raw.TotalVtxCount as usize - } - pub fn total_idx_count(&self) -> usize { - self.raw.TotalIdxCount as usize - } - pub fn deindex_all_buffers(&mut self) { - unsafe { - sys::ImDrawData_DeIndexAllBuffers(self.raw); - } - } - pub fn scale_clip_rects>(&mut self, sc: S) { - unsafe { - sys::ImDrawData_ScaleClipRects(self.raw, sc.into()); - } - } -} - -impl<'a> IntoIterator for &'a DrawData<'a> { - type Item = DrawList<'a>; - type IntoIter = DrawListIterator<'a>; - - fn into_iter(self) -> Self::IntoIter { - unsafe { - let cmd_lists = slice::from_raw_parts( - self.raw.CmdLists as *const *const sys::ImDrawList, - self.raw.CmdListsCount as usize, - ); - DrawListIterator { - iter: cmd_lists.iter(), - } - } - } -} - -pub struct DrawListIterator<'a> { - iter: std::slice::Iter<'a, *const sys::ImDrawList>, -} - -impl<'a> Iterator for DrawListIterator<'a> { - type Item = DrawList<'a>; - - fn next(&mut self) -> Option { - self.iter.next().map(|&ptr| unsafe { - let cmd_buffer = - slice::from_raw_parts((*ptr).CmdBuffer.Data, (*ptr).CmdBuffer.Size as usize); - let idx_buffer = - slice::from_raw_parts((*ptr).IdxBuffer.Data, (*ptr).IdxBuffer.Size as usize); - let vtx_buffer = - slice::from_raw_parts((*ptr).VtxBuffer.Data, (*ptr).VtxBuffer.Size as usize); - DrawList { - cmd_buffer, - idx_buffer, - vtx_buffer, - } - }) - } -} - -pub struct DrawList<'a> { - pub cmd_buffer: &'a [sys::ImDrawCmd], - pub idx_buffer: &'a [sys::ImDrawIdx], - pub vtx_buffer: &'a [sys::ImDrawVert], -} - pub struct Ui<'ui> { ctx: &'ui Context, } @@ -432,19 +355,11 @@ impl<'ui> Ui<'ui> { let io = self.io(); io.metrics_active_windows } - pub fn render(self, f: F) -> Result<(), E> - where - F: FnOnce(&Ui, DrawData) -> Result<(), E>, - { + pub fn render(self) -> &'ui DrawData { unsafe { sys::igRender(); - - let draw_data = DrawData { - raw: &mut *sys::igGetDrawData(), - }; - f(&self, draw_data)?; + &*(sys::igGetDrawData() as *mut DrawData) } - Ok(()) } pub fn show_user_guide(&self) { unsafe { sys::igShowUserGuide() }; @@ -1238,7 +1153,7 @@ impl<'ui> Ui<'ui> { // Image impl<'ui> Ui<'ui> { - pub fn image(&self, texture: ImTexture, size: S) -> Image + pub fn image(&self, texture: TextureId, size: S) -> Image where S: Into, { @@ -1248,7 +1163,7 @@ impl<'ui> Ui<'ui> { // ImageButton impl<'ui> Ui<'ui> { - pub fn image_button(&self, texture: ImTexture, size: S) -> ImageButton + pub fn image_button(&self, texture: TextureId, size: S) -> ImageButton where S: Into, { diff --git a/src/render/draw_data.rs b/src/render/draw_data.rs new file mode 100644 index 0000000..c7e0adc --- /dev/null +++ b/src/render/draw_data.rs @@ -0,0 +1,283 @@ +use std::mem; +use std::slice; + +use crate::internal::{RawCast, RawWrapper}; +use crate::render::renderer::TextureId; +use crate::sys; + +/// All draw data required to render a frame. +#[repr(C)] +pub struct DrawData { + valid: bool, + cmd_lists: *mut *mut DrawList, + cmd_lists_count: i32, + /// For convenience, sum of all draw list index buffer sizes + pub total_idx_count: i32, + /// For convenience, sum of all draw list vertex buffer sizes + pub total_vtx_count: i32, + /// Upper-left position of the viewport to render. + /// + /// (= upper-left corner of the orthogonal projection matrix to use) + pub display_pos: [f32; 2], + /// Size of the viewport to render. + /// + /// (= display_pos + display_size == lower-right corner of the orthogonal matrix to use) + pub display_size: [f32; 2], + /// Amount of pixels for each unit of display_size. + /// + /// Based on io.display_frame_buffer_scale. Typically [1.0, 1.0] on normal displays, and + /// [2.0, 2.0] on Retina displays, but fractional values are also possible. + pub framebuffer_scale: [f32; 2], +} + +unsafe impl RawCast for DrawData {} + +impl DrawData { + /// Returns an iterator over the draw lists included in the draw data + pub fn draw_lists(&self) -> DrawListIterator { + unsafe { + DrawListIterator { + iter: self.cmd_lists().iter(), + } + } + } + pub(crate) unsafe fn cmd_lists(&self) -> &[*const DrawList] { + slice::from_raw_parts( + self.cmd_lists as *const *const DrawList, + self.cmd_lists_count as usize, + ) + } + /// Converts all buffers from indexed to non-indexed, in case you cannot render indexed + /// buffers. + /// + /// **This is slow and most likely a waste of resources. Always prefer indexed rendering!** + pub fn deindex_all_buffers(&mut self) { + unsafe { + sys::ImDrawData_DeIndexAllBuffers(self.raw_mut()); + } + } + /// Scales the clip rect of each draw command. + /// + /// Can be used if your final output buffer is at a different scale than imgui-rs expects, or + /// if there is a difference between your window resolution and framebuffer resolution. + pub fn scale_clip_rects(&mut self, fb_scale: [f32; 2]) { + unsafe { + sys::ImDrawData_ScaleClipRects(self.raw_mut(), fb_scale.into()); + } + } +} + +/// Iterator over draw lists +pub struct DrawListIterator<'a> { + iter: std::slice::Iter<'a, *const DrawList>, +} + +impl<'a> Iterator for DrawListIterator<'a> { + type Item = &'a DrawList; + + fn next(&mut self) -> Option { + self.iter.next().map(|&ptr| unsafe { &*ptr }) + } +} + +#[test] +fn test_drawdata_memory_layout() { + use std::mem; + assert_eq!( + mem::size_of::(), + mem::size_of::() + ); + assert_eq!( + mem::align_of::(), + mem::align_of::() + ); + use memoffset::offset_of; + macro_rules! assert_field_offset { + ($l:ident, $r:ident) => { + assert_eq!(offset_of!(DrawData, $l), offset_of!(sys::ImDrawData, $r)); + }; + }; + assert_field_offset!(valid, Valid); + assert_field_offset!(cmd_lists, CmdLists); + assert_field_offset!(cmd_lists_count, CmdListsCount); + assert_field_offset!(total_idx_count, TotalIdxCount); + assert_field_offset!(total_vtx_count, TotalVtxCount); + assert_field_offset!(display_pos, DisplayPos); + assert_field_offset!(display_size, DisplaySize); + assert_field_offset!(framebuffer_scale, FramebufferScale); +} + +/// Draw command list +#[repr(transparent)] +pub struct DrawList(sys::ImDrawList); + +impl RawWrapper for DrawList { + type Raw = sys::ImDrawList; + unsafe fn raw(&self) -> &sys::ImDrawList { + &self.0 + } + unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList { + &mut self.0 + } +} + +impl DrawList { + pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] { + slice::from_raw_parts( + self.0.CmdBuffer.Data as *const sys::ImDrawCmd, + self.0.CmdBuffer.Size as usize, + ) + } + pub fn idx_buffer(&self) -> &[DrawIdx] { + unsafe { + slice::from_raw_parts( + self.0.IdxBuffer.Data as *const DrawIdx, + self.0.IdxBuffer.Size as usize, + ) + } + } + pub fn vtx_buffer(&self) -> &[DrawVert] { + unsafe { + slice::from_raw_parts( + self.0.VtxBuffer.Data as *const DrawVert, + self.0.VtxBuffer.Size as usize, + ) + } + } + pub fn commands(&self) -> DrawCmdIterator { + unsafe { + DrawCmdIterator { + iter: self.cmd_buffer().iter(), + } + } + } +} + +pub struct DrawCmdIterator<'a> { + iter: std::slice::Iter<'a, sys::ImDrawCmd>, +} + +impl<'a> Iterator for DrawCmdIterator<'a> { + type Item = DrawCmd; + + fn next(&mut self) -> Option { + self.iter.next().map(|cmd| { + let cmd_params = DrawCmdParams { + clip_rect: cmd.ClipRect.into(), + texture_id: TextureId::from(cmd.TextureId), + }; + if let Some(raw_callback) = cmd.UserCallback { + DrawCmd::RawCallback { + callback: raw_callback, + raw_cmd: cmd, + } + } else { + DrawCmd::Elements { + count: cmd.ElemCount as usize, + cmd_params, + } + } + }) + } +} + +pub type DrawIdx = sys::ImDrawIdx; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct DrawCmdParams { + pub clip_rect: [f32; 4], + pub texture_id: TextureId, +} + +pub enum DrawCmd { + Elements { + count: usize, + cmd_params: DrawCmdParams, + }, + RawCallback { + callback: unsafe extern "C" fn(*const sys::ImDrawList, cmd: *const sys::ImDrawCmd), + raw_cmd: *const sys::ImDrawCmd, + }, +} + +/// A single vertex +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct DrawVert { + pub pos: [f32; 2], + pub uv: [f32; 2], + pub col: [u8; 4], +} + +#[cfg(feature = "glium")] +use glium::implement_vertex; +#[cfg(feature = "glium")] +implement_vertex!(DrawVert, pos, uv, col); + +#[cfg(feature = "gfx")] +unsafe impl gfx::traits::Pod for DrawVert {} +#[cfg(feature = "gfx")] +impl gfx::pso::buffer::Structure for DrawVert { + fn query(name: &str) -> Option> { + // array query hack from gfx_impl_struct_meta macro + use gfx::format::Formatted; + use gfx::pso::buffer::{ElemOffset, Element}; + use std::mem::{size_of, transmute}; + // using "1" here as a simple non-zero pointer addres + let tmp: &DrawVert = unsafe { transmute(1usize) }; + let base = tmp as *const _ as usize; + //HACK: special treatment of array queries + let (sub_name, big_offset) = { + let mut split = name.split(|c| c == '[' || c == ']'); + let _ = split.next().unwrap(); + match split.next() { + Some(s) => { + let array_id: ElemOffset = s.parse().unwrap(); + let sub_name = match split.next() { + Some(s) if s.starts_with('.') => &s[1..], + _ => name, + }; + (sub_name, array_id * (size_of::() as ElemOffset)) + } + None => (name, 0), + } + }; + match sub_name { + "pos" => Some(Element { + format: <[f32; 2] as Formatted>::get_format(), + offset: ((&tmp.pos as *const _ as usize) - base) as ElemOffset + big_offset, + }), + "uv" => Some(Element { + format: <[f32; 2] as Formatted>::get_format(), + offset: ((&tmp.uv as *const _ as usize) - base) as ElemOffset + big_offset, + }), + "col" => Some(Element { + format: <[gfx::format::U8Norm; 4] as Formatted>::get_format(), + offset: ((&tmp.col as *const _ as usize) - base) as ElemOffset + big_offset, + }), + _ => None, + } + } +} + +#[test] +fn test_drawvert_memory_layout() { + use std::mem; + assert_eq!( + mem::size_of::(), + mem::size_of::() + ); + assert_eq!( + mem::align_of::(), + mem::align_of::() + ); + use memoffset::offset_of; + macro_rules! assert_field_offset { + ($l:ident, $r:ident) => { + assert_eq!(offset_of!(DrawVert, $l), offset_of!(sys::ImDrawVert, $r)); + }; + }; + assert_field_offset!(pos, pos); + assert_field_offset!(uv, uv); + assert_field_offset!(col, col); +} diff --git a/src/render/mod.rs b/src/render/mod.rs new file mode 100644 index 0000000..0ace104 --- /dev/null +++ b/src/render/mod.rs @@ -0,0 +1,2 @@ +pub mod draw_data; +pub mod renderer; diff --git a/src/render/renderer.rs b/src/render/renderer.rs new file mode 100644 index 0000000..246e19d --- /dev/null +++ b/src/render/renderer.rs @@ -0,0 +1,77 @@ +use std::collections::HashMap; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct TextureId(usize); + +impl TextureId { + pub fn id(self) -> usize { + self.0 + } +} + +impl From for TextureId { + fn from(id: usize) -> Self { + TextureId(id) + } +} + +impl From<*const T> for TextureId { + fn from(ptr: *const T) -> Self { + TextureId(ptr as usize) + } +} + +impl From<*mut T> for TextureId { + fn from(ptr: *mut T) -> Self { + TextureId(ptr as usize) + } +} + +#[test] +fn test_texture_id_memory_layout() { + use std::mem; + assert_eq!( + mem::size_of::(), + mem::size_of::() + ); + assert_eq!( + mem::align_of::(), + mem::align_of::() + ); +} + +/// Generic texture mapping for use by renderers. +#[derive(Debug, Default)] +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) -> TextureId { + let id = self.next; + self.textures.insert(id, texture); + self.next += 1; + TextureId::from(id) + } + + pub fn replace(&mut self, id: TextureId, texture: T) -> Option { + self.textures.insert(id.0, texture) + } + + pub fn remove(&mut self, id: TextureId) -> Option { + self.textures.remove(&id.0) + } + + pub fn get(&self, id: TextureId) -> Option<&T> { + self.textures.get(&id.0) + } +}