Pull renderer/draw data updates from 0.1-dev

This commit is contained in:
Joonas Javanainen 2019-06-27 22:02:48 +03:00
parent c37bd8b1f9
commit 42d3c0f6d7
No known key found for this signature in database
GPG Key ID: D39CCA5CB19B9179
18 changed files with 929 additions and 641 deletions

View File

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

View File

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

View File

@ -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<F: FnMut(&Ui) -> 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::<ColorFormat, DepthFormat>(window, context, &events_loop)
.expect("Failed to initalize graphics");
gfx_window_glutin::init::<ColorFormat, DepthFormat>(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<F: FnMut(&Ui) -> 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<F: FnMut(&Ui) -> 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<F: FnMut(&Ui) -> 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<F: FnMut(&Ui) -> 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,

View File

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

View File

@ -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<T> = Result<T, RendererError>;
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<usize>),
Buffer(gfx::buffer::CreationError),
Pipeline(gfx::PipelineStateError<String>),
Combined(gfx::CombinedError),
BadTexture(ImTexture),
BadTexture(TextureId),
}
impl From<gfx::UpdateError<usize>> for RendererError {
fn from(e: gfx::UpdateError<usize>) -> RendererError {
RendererError::Update(e)
impl From<gfx::UpdateError<usize>> for GfxRendererError {
fn from(e: gfx::UpdateError<usize>) -> GfxRendererError {
GfxRendererError::Update(e)
}
}
impl From<gfx::buffer::CreationError> for RendererError {
fn from(e: gfx::buffer::CreationError) -> RendererError {
RendererError::Buffer(e)
impl From<gfx::buffer::CreationError> for GfxRendererError {
fn from(e: gfx::buffer::CreationError) -> GfxRendererError {
GfxRendererError::Buffer(e)
}
}
impl From<gfx::PipelineStateError<String>> for RendererError {
fn from(e: gfx::PipelineStateError<String>) -> RendererError {
RendererError::Pipeline(e)
impl From<gfx::PipelineStateError<String>> for GfxRendererError {
fn from(e: gfx::PipelineStateError<String>) -> GfxRendererError {
GfxRendererError::Pipeline(e)
}
}
impl From<gfx::CombinedError> 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<R>>::Data,)*
}
impl<'a, R: Resources> gfx::pso::PipelineData<R> for BorrowedData<'a, R> {
type Meta = pipe::Meta;
fn bake_to(&self, out: &mut RawDataSet<R>, meta: &Self::Meta, man: &mut gfx::handle::Manager<R>, access: &mut AccessInfo<R>) {
$(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<ImDrawVert> = (),
matrix: gfx::Global<[[f32; 4]; 4]> = "matrix",
tex: gfx::TextureSampler<[f32; 4]> = "tex",
out: gfx::BlendTarget<gfx::format::Rgba8> = (
"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<ImDrawVert> = (),
constants: gfx::ConstantBuffer<constants::Constants> = "Constants",
tex: gfx::TextureSampler<[f32; 4]> = "tex",
out: gfx::BlendTarget<gfx::format::Rgba8> = (
"Target0",
gfx::state::ColorMask::all(),
gfx::preset::blend::ALPHA,
),
scissor: gfx::Scissor = (),
impl From<gfx::CombinedError> for GfxRendererError {
fn from(e: gfx::CombinedError) -> GfxRendererError {
GfxRendererError::Combined(e)
}
}
@ -180,36 +101,42 @@ pub type Texture<R> = (
gfx::handle::Sampler<R>,
);
pub struct Renderer<R: Resources> {
bundle: Bundle<R, pipe::Data<R>>,
index_buffer: Buffer<R, u16>,
pub struct GfxRenderer<Cf: BlendFormat, R: Resources> {
vertex_buffer: Buffer<R, DrawVert>,
index_buffer: Buffer<R, DrawIdx>,
slice: Slice<R>,
pso: PipelineState<R, pipeline::Meta<Cf>>,
font_texture: Texture<R>,
textures: Textures<Texture<R>>,
#[cfg(feature = "directx")]
constants: Buffer<R, constants::Constants>,
}
impl<R: Resources> Renderer<R> {
impl<Cf, R> GfxRenderer<Cf, R>
where
Cf: BlendFormat,
R: Resources,
{
pub fn init<F: Factory<R>>(
imgui: &mut Context,
ctx: &mut imgui::Context,
factory: &mut F,
shaders: Shaders,
out: RenderTargetView<R, gfx::format::Rgba8>,
) -> RendererResult<Renderer<R>> {
) -> Result<GfxRenderer<Cf, R>, 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::<ImDrawVert>(
let pso = factory.create_pipeline_simple(vs_code, ps_code, pipeline::new::<Cf>())?;
let vertex_buffer = factory.create_buffer::<DrawVert>(
256,
gfx::buffer::Role::Vertex,
gfx::memory::Usage::Dynamic,
Bind::empty(),
)?;
let index_buffer = factory.create_buffer::<ImDrawIdx>(
let index_buffer = factory.create_buffer::<DrawIdx>(
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::format::Rgba8>(
gfx::texture::Kind::D2(
handle.width as u16,
@ -222,9 +149,7 @@ impl<R: Resources> Renderer<R> {
})?;
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<R: Resources> Renderer<R> {
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<R, gfx::format::Rgba8>) {
self.bundle.out = out;
}
pub fn textures(&mut self) -> &mut Textures<Texture<R>> {
&mut self.textures
}
pub fn render<'a, F: Factory<R>, C: CommandBuffer<R>>(
pub fn render<'ui, F: Factory<R>, C: CommandBuffer<R>>(
&mut self,
ui: Ui<'a>,
factory: &mut F,
encoder: &mut Encoder<R, C>,
) -> 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<R, Cf>,
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<R>, C: CommandBuffer<R>>(
&mut self,
factory: &mut F,
encoder: &mut Encoder<R, C>,
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<R: Resources> Renderer<R> {
&mut self,
factory: &mut F,
encoder: &mut Encoder<R, C>,
vtx_buffer: &[ImDrawVert],
) -> RendererResult<()> {
if self.bundle.vertex_buffer.len() < vtx_buffer.len() {
self.bundle.vertex_buffer = factory.create_buffer::<ImDrawVert>(
vtx_buffer: &[DrawVert],
) -> Result<(), GfxRendererError> {
if self.vertex_buffer.len() < vtx_buffer.len() {
self.vertex_buffer = factory.create_buffer::<DrawVert>(
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<F: Factory<R>, C: CommandBuffer<R>>(
&mut self,
factory: &mut F,
encoder: &mut Encoder<R, C>,
idx_buffer: &[ImDrawIdx],
) -> RendererResult<()> {
idx_buffer: &[DrawIdx],
) -> Result<(), GfxRendererError> {
if self.index_buffer.len() < idx_buffer.len() {
self.index_buffer = factory.create_buffer::<ImDrawIdx>(
self.index_buffer = factory.create_buffer::<DrawIdx>(
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<R>, 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<R: Resources, Data: PipelineData<R>> {
slice: Slice<R>,
pso: PipelineState<R, Data::Meta>,
vertex_buffer: Buffer<R, ImDrawVert>,
out: RenderTargetView<R, gfx::format::Rgba8>,
#[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 <gfx::VertexBuffer<DrawVert> as DataBind<R>>::Data,
#[cfg(not(feature = "directx"))]
pub matrix: &'a <gfx::Global<[[f32; 4]; 4]> as DataBind<R>>::Data,
#[cfg(feature = "directx")]
pub constants: &'a <gfx::ConstantBuffer<super::constants::Constants> as DataBind<R>>::Data,
pub tex: &'a <gfx::TextureSampler<[f32; 4]> as DataBind<R>>::Data,
pub target: &'a <gfx::BlendTarget<Cf> as DataBind<R>>::Data,
pub scissor: &'a <gfx::Scissor as DataBind<R>>::Data,
}
#[derive(Clone, Debug, Hash, PartialEq)]
pub struct Meta<Cf: BlendFormat> {
vertex_buffer: gfx::VertexBuffer<DrawVert>,
#[cfg(not(feature = "directx"))]
matrix: gfx::Global<[[f32; 4]; 4]>,
#[cfg(feature = "directx")]
constants: gfx::ConstantBuffer<super::constants::Constants>,
tex: gfx::TextureSampler<[f32; 4]>,
target: gfx::BlendTarget<Cf>,
scissor: gfx::Scissor,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Init<'a, Cf: BlendFormat> {
vertex_buffer: <gfx::VertexBuffer<DrawVert> as DataLink<'a>>::Init,
#[cfg(not(feature = "directx"))]
matrix: <gfx::Global<[[f32; 4]; 4]> as DataLink<'a>>::Init,
#[cfg(feature = "directx")]
constants: <gfx::ConstantBuffer<super::constants::Constants> as DataLink<'a>>::Init,
tex: <gfx::TextureSampler<[f32; 4]> as DataLink<'a>>::Init,
target: <gfx::BlendTarget<Cf> as DataLink<'a>>::Init,
scissor: <gfx::Scissor as DataLink<'a>>::Init,
}
impl<'a, Cf: BlendFormat> PipelineInit for Init<'a, Cf> {
type Meta = Meta<Cf>;
fn link_to<'s>(
&self,
desc: &mut Descriptor,
info: &'s ProgramInfo,
) -> Result<Meta<Cf>, 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<R> for Data<'a, R, Cf> {
type Meta = Meta<Cf>;
fn bake_to(
&self,
out: &mut RawDataSet<R>,
meta: &Meta<Cf>,
man: &mut Manager<R>,
access: &mut AccessInfo<R>,
) {
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<Cf: BlendFormat>() -> 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: (),
}
}
}

View File

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

View File

@ -7,7 +7,7 @@ use imgui_winit_support::{HiDpiMode, WinitPlatform};
use std::rc::Rc;
use std::time::Instant;
pub type Textures = imgui::Textures<Texture2d>;
pub type Textures = imgui::Textures<Rc<Texture2d>>;
pub fn run<F>(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;

View File

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

View File

@ -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<T> = Result<T, RendererError>;
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<vertex::BufferCreationError> for RendererError {
fn from(e: vertex::BufferCreationError) -> RendererError {
RendererError::Vertex(e)
impl From<vertex::BufferCreationError> for GliumRendererError {
fn from(e: vertex::BufferCreationError) -> GliumRendererError {
GliumRendererError::Vertex(e)
}
}
impl From<index::BufferCreationError> for RendererError {
fn from(e: index::BufferCreationError) -> RendererError {
RendererError::Index(e)
impl From<index::BufferCreationError> for GliumRendererError {
fn from(e: index::BufferCreationError) -> GliumRendererError {
GliumRendererError::Index(e)
}
}
impl From<program::ProgramChooserCreationError> for RendererError {
fn from(e: program::ProgramChooserCreationError) -> RendererError {
RendererError::Program(e)
impl From<ProgramChooserCreationError> for GliumRendererError {
fn from(e: ProgramChooserCreationError) -> GliumRendererError {
GliumRendererError::Program(e)
}
}
impl From<texture::TextureCreationError> for RendererError {
fn from(e: texture::TextureCreationError) -> RendererError {
RendererError::Texture(e)
impl From<TextureCreationError> for GliumRendererError {
fn from(e: TextureCreationError) -> GliumRendererError {
GliumRendererError::Texture(e)
}
}
impl From<DrawError> for RendererError {
fn from(e: DrawError) -> RendererError {
RendererError::Draw(e)
impl From<DrawError> for GliumRendererError {
fn from(e: DrawError) -> GliumRendererError {
GliumRendererError::Draw(e)
}
}
pub struct Renderer {
pub struct GliumRenderer {
ctx: Rc<Context>,
device_objects: DeviceObjects,
program: Program,
font_texture: Texture2d,
textures: Textures<Rc<Texture2d>>,
}
impl Renderer {
pub fn init<F: Facade>(imgui: &mut imgui::Context, ctx: &F) -> RendererResult<Renderer> {
let device_objects = DeviceObjects::init(imgui, ctx)?;
Ok(Renderer {
ctx: Rc::clone(ctx.get_context()),
device_objects,
impl GliumRenderer {
pub fn init<F: Facade>(
ctx: &mut imgui::Context,
facade: &F,
) -> Result<GliumRenderer, GliumRendererError> {
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<Texture2d> {
&mut self.device_objects.textures
pub fn textures(&mut self) -> &mut Textures<Rc<Texture2d>> {
&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<Texture2d>,
}
fn compile_default_program<F: Facade>(
ctx: &F,
) -> Result<Program, program::ProgramChooserCreationError> {
fn compile_default_program<F: Facade>(facade: &F) -> Result<Program, ProgramChooserCreationError> {
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<F: Facade>(
},
)
}
impl DeviceObjects {
pub fn init<F: Facade>(im_gui: &mut imgui::Context, ctx: &F) -> RendererResult<DeviceObjects> {
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 })
}
}

View File

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

View File

@ -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<Format> for ImDrawVert {
fn query(name: &str) -> Option<Element<Format>> {
// 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::<ImDrawVert>() as ElemOffset),
)
}
None => (name, 0),
}
};
let dummy: &ImDrawVert = unsafe { mem::transmute(0usize) };
match sub_name {
"pos" => Some(Element {
format: <ImVec2 as Formatted>::get_format(),
offset: unsafe { mem::transmute::<_, usize>(&dummy.pos) } as ElemOffset
+ big_offset,
}),
"uv" => Some(Element {
format: <ImVec2 as Formatted>::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<Float>
}

View File

@ -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),
<ImVec2 as Attribute>::get_type(),
false,
),
(
"uv".into(),
mem::transmute(&dummy.uv),
<ImVec2 as Attribute>::get_type(),
false,
),
(
"col".into(),
mem::transmute(&dummy.col),
AttributeType::U8U8U8U8,
false,
),
])
}
}
}

View File

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

View File

@ -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<usize> 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<S>(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self
pub fn new<S>(_: &Ui<'ui>, texture_id: TextureId, size: S) -> Self
where
S: Into<ImVec2>,
{
@ -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<S>(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self
pub fn new<S>(_: &Ui<'ui>, texture_id: TextureId, size: S) -> Self
where
S: Into<ImVec2>,
{
@ -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<T> Textures<T> {
}
}
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<T> {
self.textures.insert(id.0, texture)
pub fn replace(&mut self, id: TextureId, texture: T) -> Option<T> {
self.textures.insert(id.id(), texture)
}
pub fn remove(&mut self, id: ImTexture) -> Option<T> {
self.textures.remove(&id.0)
pub fn remove(&mut self, id: TextureId) -> Option<T> {
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())
}
}

View File

@ -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<S: Into<ImVec2>>(&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::Item> {
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<F, E>(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<S>(&self, texture: ImTexture, size: S) -> Image
pub fn image<S>(&self, texture: TextureId, size: S) -> Image
where
S: Into<ImVec2>,
{
@ -1248,7 +1163,7 @@ impl<'ui> Ui<'ui> {
// ImageButton
impl<'ui> Ui<'ui> {
pub fn image_button<S>(&self, texture: ImTexture, size: S) -> ImageButton
pub fn image_button<S>(&self, texture: TextureId, size: S) -> ImageButton
where
S: Into<ImVec2>,
{

283
src/render/draw_data.rs Normal file
View File

@ -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<sys::ImDrawData> 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::Item> {
self.iter.next().map(|&ptr| unsafe { &*ptr })
}
}
#[test]
fn test_drawdata_memory_layout() {
use std::mem;
assert_eq!(
mem::size_of::<DrawData>(),
mem::size_of::<sys::ImDrawData>()
);
assert_eq!(
mem::align_of::<DrawData>(),
mem::align_of::<sys::ImDrawData>()
);
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::Item> {
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<gfx::format::Format> for DrawVert {
fn query(name: &str) -> Option<gfx::pso::buffer::Element<gfx::format::Format>> {
// 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::<DrawVert>() 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::<DrawVert>(),
mem::size_of::<sys::ImDrawVert>()
);
assert_eq!(
mem::align_of::<DrawVert>(),
mem::align_of::<sys::ImDrawVert>()
);
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);
}

2
src/render/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod draw_data;
pub mod renderer;

77
src/render/renderer.rs Normal file
View File

@ -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<usize> for TextureId {
fn from(id: usize) -> Self {
TextureId(id)
}
}
impl<T> From<*const T> for TextureId {
fn from(ptr: *const T) -> Self {
TextureId(ptr as usize)
}
}
impl<T> 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::<TextureId>(),
mem::size_of::<sys::ImTextureID>()
);
assert_eq!(
mem::align_of::<TextureId>(),
mem::align_of::<sys::ImTextureID>()
);
}
/// Generic texture mapping for use by renderers.
#[derive(Debug, Default)]
pub struct Textures<T> {
textures: HashMap<usize, T>,
next: usize,
}
impl<T> Textures<T> {
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<T> {
self.textures.insert(id.0, texture)
}
pub fn remove(&mut self, id: TextureId) -> Option<T> {
self.textures.remove(&id.0)
}
pub fn get(&self, id: TextureId) -> Option<&T> {
self.textures.get(&id.0)
}
}