mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 13:38:35 +00:00
Pull renderer/draw data updates from 0.1-dev
This commit is contained in:
parent
c37bd8b1f9
commit
42d3c0f6d7
@ -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"
|
||||
|
||||
3
imgui-examples/Cargo.lock
generated
3
imgui-examples/Cargo.lock
generated
@ -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]]
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"] }
|
||||
|
||||
@ -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: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
imgui-glium-examples/Cargo.lock
generated
3
imgui-glium-examples/Cargo.lock
generated
@ -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]]
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"] }
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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>
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
53
src/image.rs
53
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<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())
|
||||
}
|
||||
}
|
||||
|
||||
109
src/lib.rs
109
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<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
283
src/render/draw_data.rs
Normal 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
2
src/render/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod draw_data;
|
||||
pub mod renderer;
|
||||
77
src/render/renderer.rs
Normal file
77
src/render/renderer.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user