mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-16 16:08:33 +00:00
Make HiDPI rendering better
+ use linear filtering in both renderers. Nearest just won't work anymore if we have a non-integer scaling factor (which winit can give us on 1440p screens for example) + pass around FrameSize which has the necessary info with full f64 precision + extra care with conversions to/from f32 and u32 and rounding
This commit is contained in:
parent
1663e66887
commit
aa4ae70054
@ -1,4 +1,4 @@
|
||||
use imgui::{FontGlyphRange, ImFontConfig, ImGui, ImGuiMouseCursor, Ui};
|
||||
use imgui::{FontGlyphRange, FrameSize, ImFontConfig, ImGui, ImGuiMouseCursor, Ui};
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Default)]
|
||||
@ -129,11 +129,14 @@ pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_
|
||||
});
|
||||
}
|
||||
|
||||
let size_points = gl_window.get_inner_size().unwrap();
|
||||
let hdipi = gl_window.get_hidpi_factor();
|
||||
let size_pixels = size_points.to_physical(hdipi);
|
||||
let logical_size = gl_window.get_inner_size().unwrap();
|
||||
|
||||
let ui = imgui.frame(size_points.into(), size_pixels.into(), delta_s);
|
||||
let frame_size = FrameSize {
|
||||
logical_size: logical_size.into(),
|
||||
hidpi_factor: gl_window.get_hidpi_factor(),
|
||||
};
|
||||
|
||||
let ui = imgui.frame(frame_size, delta_s);
|
||||
if !run_ui(&ui) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use imgui::{FontGlyphRange, ImFontConfig, ImGui, ImGuiMouseCursor, ImVec4, Ui};
|
||||
use imgui::{FontGlyphRange, FrameSize, ImFontConfig, ImGui, ImGuiMouseCursor, ImVec4, Ui};
|
||||
use imgui_gfx_renderer::{Renderer, Shaders};
|
||||
use std::time::Instant;
|
||||
|
||||
@ -172,11 +172,14 @@ pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_
|
||||
});
|
||||
}
|
||||
|
||||
let size_points = window.get_inner_size().unwrap();
|
||||
let hdipi = window.get_hidpi_factor();
|
||||
let size_pixels = size_points.to_physical(hdipi);
|
||||
let logical_size = window.get_inner_size().unwrap();
|
||||
|
||||
let ui = imgui.frame(size_points.into(), size_pixels.into(), delta_s);
|
||||
let frame_size = FrameSize {
|
||||
logical_size: logical_size.into(),
|
||||
hidpi_factor: window.get_hidpi_factor(),
|
||||
};
|
||||
|
||||
let ui = imgui.frame(frame_size, delta_s);
|
||||
if !run_ui(&ui) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ use gfx::memory::Bind;
|
||||
use gfx::handle::{Buffer, RenderTargetView};
|
||||
use gfx::texture::{FilterMethod, SamplerInfo, WrapMode};
|
||||
use gfx::traits::FactoryExt;
|
||||
use imgui::{DrawList, ImDrawIdx, ImDrawVert, ImGui, Ui};
|
||||
use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui};
|
||||
|
||||
pub type RendererResult<T> = Result<T, RendererError>;
|
||||
|
||||
@ -129,7 +129,7 @@ impl<R: Resources> Renderer<R> {
|
||||
})?;
|
||||
// TODO: set texture id in imgui
|
||||
let sampler =
|
||||
factory.create_sampler(SamplerInfo::new(FilterMethod::Scale, WrapMode::Clamp));
|
||||
factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp));
|
||||
let data = pipe::Data {
|
||||
vertex_buffer: vertex_buffer,
|
||||
matrix: [
|
||||
@ -168,34 +168,36 @@ impl<R: Resources> Renderer<R> {
|
||||
factory: &mut F,
|
||||
encoder: &mut Encoder<R, C>,
|
||||
) -> RendererResult<()> {
|
||||
let (width, height) = ui.imgui().display_size();
|
||||
let FrameSize { logical_size: (width, height), hidpi_factor } = ui.frame_size();
|
||||
|
||||
if width == 0.0 || height == 0.0 {
|
||||
if !(width > 0.0 && height > 0.0) {
|
||||
return Ok(());
|
||||
}
|
||||
let fb_size = ((width * hidpi_factor) as f32, (height * hidpi_factor) as f32);
|
||||
|
||||
self.bundle.data.matrix = [
|
||||
[2.0 / width as f32, 0.0, 0.0, 0.0],
|
||||
[0.0, -2.0 / height as f32, 0.0, 0.0],
|
||||
[(2.0 / width) as f32, 0.0, 0.0, 0.0],
|
||||
[0.0, (2.0 / -height) as f32, 0.0, 0.0],
|
||||
[0.0, 0.0, -1.0, 0.0],
|
||||
[-1.0, 1.0, 0.0, 1.0],
|
||||
];
|
||||
|
||||
ui.render(|ui, draw_data| {
|
||||
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(ui, factory, encoder, &draw_list)?;
|
||||
self.render_draw_list(factory, encoder, &draw_list, fb_size)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
fn render_draw_list<'a, F: Factory<R>, C: CommandBuffer<R>>(
|
||||
&mut self,
|
||||
ui: &'a Ui<'a>,
|
||||
factory: &mut F,
|
||||
encoder: &mut Encoder<R, C>,
|
||||
draw_list: &DrawList<'a>,
|
||||
fb_size: (f32, f32)
|
||||
) -> RendererResult<()> {
|
||||
let (width, height) = ui.imgui().display_size();
|
||||
let (scale_width, scale_height) = ui.imgui().display_framebuffer_scale();
|
||||
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)?;
|
||||
@ -206,10 +208,10 @@ impl<R: Resources> Renderer<R> {
|
||||
|
||||
self.bundle.slice.end = self.bundle.slice.start + cmd.elem_count;
|
||||
self.bundle.data.scissor = Rect {
|
||||
x: (cmd.clip_rect.x.max(0.0) * scale_width) as u16,
|
||||
y: (cmd.clip_rect.y.max(0.0) * scale_height) as u16,
|
||||
w: ((cmd.clip_rect.z - cmd.clip_rect.x).abs().min(width) * scale_width) as u16,
|
||||
h: ((cmd.clip_rect.w - cmd.clip_rect.y).abs().min(height) * scale_height) as u16,
|
||||
x: cmd.clip_rect.x.max(0.0).round() as u16,
|
||||
y: cmd.clip_rect.y.max(0.0).round() as u16,
|
||||
w: (cmd.clip_rect.z - cmd.clip_rect.x).abs().min(fb_width).round() as u16,
|
||||
h: (cmd.clip_rect.w - cmd.clip_rect.y).abs().min(fb_height).round() as u16,
|
||||
};
|
||||
self.bundle.encode(encoder);
|
||||
self.bundle.slice.start = self.bundle.slice.end;
|
||||
|
||||
@ -8,7 +8,7 @@ use glium::program;
|
||||
use glium::index::{self, PrimitiveType};
|
||||
use glium::texture;
|
||||
use glium::vertex;
|
||||
use imgui::{DrawList, ImDrawIdx, ImDrawVert, ImGui, Ui};
|
||||
use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
@ -64,7 +64,7 @@ pub struct Renderer {
|
||||
|
||||
impl Renderer {
|
||||
pub fn init<F: Facade>(imgui: &mut ImGui, ctx: &F) -> RendererResult<Renderer> {
|
||||
let device_objects = try!(DeviceObjects::init(imgui, ctx));
|
||||
let device_objects = DeviceObjects::init(imgui, ctx)?;
|
||||
Ok(Renderer {
|
||||
ctx: Rc::clone(ctx.get_context()),
|
||||
device_objects: device_objects,
|
||||
@ -73,9 +73,22 @@ impl Renderer {
|
||||
|
||||
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 result = ui.render(|ui, draw_data| {
|
||||
let FrameSize { logical_size: (width, height), hidpi_factor } = ui.frame_size();
|
||||
if !(width > 0.0 && height > 0.0) {
|
||||
return Ok(());
|
||||
}
|
||||
let fb_size = ((width * hidpi_factor) as f32, (height * hidpi_factor) as f32);
|
||||
|
||||
let matrix = [
|
||||
[(2.0 / width) as f32, 0.0, 0.0, 0.0],
|
||||
[0.0, (2.0 / -height) as f32, 0.0, 0.0],
|
||||
[0.0, 0.0, -1.0, 0.0],
|
||||
[-1.0, 1.0, 0.0, 1.0],
|
||||
];
|
||||
let result = ui.render(|ui, mut draw_data| {
|
||||
draw_data.scale_clip_rects(ui.imgui().display_framebuffer_scale());
|
||||
for draw_list in draw_data.into_iter() {
|
||||
self.render_draw_list(surface, ui, &draw_list)?;
|
||||
self.render_draw_list(surface, &draw_list, fb_size, matrix)?;
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
@ -86,34 +99,24 @@ impl Renderer {
|
||||
fn render_draw_list<'a, S: Surface>(
|
||||
&mut self,
|
||||
surface: &mut S,
|
||||
ui: &'a Ui<'a>,
|
||||
draw_list: &DrawList<'a>,
|
||||
fb_size: (f32, f32),
|
||||
matrix: [[f32; 4]; 4],
|
||||
) -> RendererResult<()> {
|
||||
use glium::{Blend, DrawParameters, Rect};
|
||||
use glium::uniforms::{MinifySamplerFilter, MagnifySamplerFilter};
|
||||
|
||||
try!(self.device_objects.upload_vertex_buffer(
|
||||
let (fb_width, fb_height) = fb_size;
|
||||
|
||||
self.device_objects.upload_vertex_buffer(
|
||||
&self.ctx,
|
||||
draw_list.vtx_buffer,
|
||||
));
|
||||
try!(self.device_objects.upload_index_buffer(
|
||||
)?;
|
||||
self.device_objects.upload_index_buffer(
|
||||
&self.ctx,
|
||||
draw_list.idx_buffer,
|
||||
));
|
||||
)?;
|
||||
|
||||
let (width, height) = ui.imgui().display_size();
|
||||
let (scale_width, scale_height) = ui.imgui().display_framebuffer_scale();
|
||||
|
||||
if width == 0.0 || height == 0.0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let matrix = [
|
||||
[2.0 / width as f32, 0.0, 0.0, 0.0],
|
||||
[0.0, 2.0 / -(height as f32), 0.0, 0.0],
|
||||
[0.0, 0.0, -1.0, 0.0],
|
||||
[-1.0, 1.0, 0.0, 1.0],
|
||||
];
|
||||
let font_texture_id = self.device_objects.texture.get_id() as usize;
|
||||
|
||||
let mut idx_start = 0;
|
||||
@ -123,32 +126,30 @@ impl Renderer {
|
||||
|
||||
let idx_end = idx_start + cmd.elem_count as usize;
|
||||
|
||||
try!(
|
||||
surface.draw(
|
||||
&self.device_objects.vertex_buffer,
|
||||
&self.device_objects
|
||||
.index_buffer
|
||||
.slice(idx_start..idx_end)
|
||||
.expect("Invalid index buffer range"),
|
||||
&self.device_objects.program,
|
||||
&uniform! {
|
||||
matrix: matrix,
|
||||
tex: self.device_objects.texture.sampled()
|
||||
.magnify_filter(MagnifySamplerFilter::Nearest)
|
||||
.minify_filter(MinifySamplerFilter::Nearest),
|
||||
},
|
||||
&DrawParameters {
|
||||
blend: Blend::alpha_blending(),
|
||||
scissor: Some(Rect {
|
||||
left: (cmd.clip_rect.x * scale_width) as u32,
|
||||
bottom: ((height - cmd.clip_rect.w) * scale_height) as u32,
|
||||
width: ((cmd.clip_rect.z - cmd.clip_rect.x) * scale_width) as u32,
|
||||
height: ((cmd.clip_rect.w - cmd.clip_rect.y) * scale_height) as u32,
|
||||
}),
|
||||
..DrawParameters::default()
|
||||
},
|
||||
)
|
||||
);
|
||||
surface.draw(
|
||||
&self.device_objects.vertex_buffer,
|
||||
&self.device_objects
|
||||
.index_buffer
|
||||
.slice(idx_start..idx_end)
|
||||
.expect("Invalid index buffer range"),
|
||||
&self.device_objects.program,
|
||||
&uniform! {
|
||||
matrix: matrix,
|
||||
tex: self.device_objects.texture.sampled()
|
||||
.magnify_filter(MagnifySamplerFilter::Linear)
|
||||
.minify_filter(MinifySamplerFilter::Linear),
|
||||
},
|
||||
&DrawParameters {
|
||||
blend: Blend::alpha_blending(),
|
||||
scissor: Some(Rect {
|
||||
left: cmd.clip_rect.x.max(0.0).round() as u32,
|
||||
bottom: (fb_height - cmd.clip_rect.w).max(0.0).round() as u32,
|
||||
width: (cmd.clip_rect.z - cmd.clip_rect.x).abs().max(fb_width).round() as u32,
|
||||
height: (cmd.clip_rect.w - cmd.clip_rect.y).abs().max(fb_height).round() as u32,
|
||||
}),
|
||||
..DrawParameters::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
idx_start = idx_end;
|
||||
}
|
||||
@ -201,15 +202,15 @@ impl DeviceObjects {
|
||||
pub fn init<F: Facade>(im_gui: &mut ImGui, ctx: &F) -> RendererResult<DeviceObjects> {
|
||||
use glium::texture::{ClientFormat, RawImage2d};
|
||||
|
||||
let vertex_buffer = try!(VertexBuffer::empty_dynamic(ctx, 0));
|
||||
let index_buffer = try!(IndexBuffer::empty_dynamic(
|
||||
let vertex_buffer = VertexBuffer::empty_dynamic(ctx, 0)?;
|
||||
let index_buffer = IndexBuffer::empty_dynamic(
|
||||
ctx,
|
||||
PrimitiveType::TrianglesList,
|
||||
0,
|
||||
));
|
||||
)?;
|
||||
|
||||
let program = try!(compile_default_program(ctx));
|
||||
let texture = try!(im_gui.prepare_texture(|handle| {
|
||||
let program = compile_default_program(ctx)?;
|
||||
let texture = im_gui.prepare_texture(|handle| {
|
||||
let data = RawImage2d {
|
||||
data: Cow::Borrowed(handle.pixels),
|
||||
width: handle.width,
|
||||
@ -217,7 +218,7 @@ impl DeviceObjects {
|
||||
format: ClientFormat::U8U8U8U8,
|
||||
};
|
||||
Texture2d::new(ctx, data)
|
||||
}));
|
||||
})?;
|
||||
im_gui.set_texture_id(texture.get_id() as usize);
|
||||
|
||||
Ok(DeviceObjects {
|
||||
@ -237,7 +238,7 @@ impl DeviceObjects {
|
||||
slice.write(vtx_buffer);
|
||||
return Ok(());
|
||||
}
|
||||
self.vertex_buffer = try!(VertexBuffer::dynamic(ctx, vtx_buffer));
|
||||
self.vertex_buffer = VertexBuffer::dynamic(ctx, vtx_buffer)?;
|
||||
let _ = ctx.get_context().insert_debug_marker(&format!(
|
||||
"imgui-rs: resized vertex buffer to {} bytes",
|
||||
self.vertex_buffer.get_size()
|
||||
@ -254,11 +255,11 @@ impl DeviceObjects {
|
||||
slice.write(idx_buffer);
|
||||
return Ok(());
|
||||
}
|
||||
self.index_buffer = try!(IndexBuffer::dynamic(
|
||||
self.index_buffer = IndexBuffer::dynamic(
|
||||
ctx,
|
||||
PrimitiveType::TrianglesList,
|
||||
idx_buffer,
|
||||
));
|
||||
)?;
|
||||
let _ = ctx.get_context().insert_debug_marker(&format!(
|
||||
"imgui-rs: resized index buffer to {} bytes",
|
||||
self.index_buffer.get_size()
|
||||
|
||||
33
src/lib.rs
33
src/lib.rs
@ -99,6 +99,12 @@ pub enum ImMouseButton {
|
||||
Extra2 = 4,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct FrameSize {
|
||||
pub logical_size: (f64, f64),
|
||||
pub hidpi_factor: f64,
|
||||
}
|
||||
|
||||
impl ImGui {
|
||||
pub fn init() -> ImGui {
|
||||
ImGui {
|
||||
@ -357,31 +363,22 @@ impl ImGui {
|
||||
pub fn get_frame_rate(&self) -> f32 { self.io().framerate }
|
||||
pub fn frame<'ui, 'a: 'ui>(
|
||||
&'a mut self,
|
||||
size_points: (u32, u32),
|
||||
size_pixels: (u32, u32),
|
||||
frame_size: FrameSize,
|
||||
delta_time: f32,
|
||||
) -> Ui<'ui> {
|
||||
{
|
||||
let io = self.io_mut();
|
||||
io.display_size.x = size_points.0 as c_float;
|
||||
io.display_size.y = size_points.1 as c_float;
|
||||
io.display_framebuffer_scale.x = if size_points.0 > 0 {
|
||||
size_pixels.0 as c_float / size_points.0 as c_float
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
io.display_framebuffer_scale.y = if size_points.1 > 0 {
|
||||
size_pixels.1 as c_float / size_points.1 as c_float
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
io.display_size.x = frame_size.logical_size.0 as c_float;
|
||||
io.display_size.y = frame_size.logical_size.1 as c_float;
|
||||
io.display_framebuffer_scale.x = frame_size.hidpi_factor as c_float;
|
||||
io.display_framebuffer_scale.y = frame_size.hidpi_factor as c_float;
|
||||
io.delta_time = delta_time;
|
||||
}
|
||||
unsafe {
|
||||
sys::igNewFrame();
|
||||
CURRENT_UI = Some(Ui { imgui: mem::transmute(self as &'a ImGui) });
|
||||
CURRENT_UI = Some(Ui { imgui: mem::transmute(self as &'a ImGui), frame_size });
|
||||
}
|
||||
Ui { imgui: self }
|
||||
Ui { imgui: self, frame_size }
|
||||
}
|
||||
}
|
||||
|
||||
@ -466,6 +463,7 @@ pub struct DrawList<'a> {
|
||||
|
||||
pub struct Ui<'ui> {
|
||||
imgui: &'ui ImGui,
|
||||
frame_size: FrameSize,
|
||||
}
|
||||
|
||||
static FMT: &'static [u8] = b"%s\0";
|
||||
@ -473,6 +471,9 @@ static FMT: &'static [u8] = b"%s\0";
|
||||
fn fmt_ptr() -> *const c_char { FMT.as_ptr() as *const c_char }
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
pub fn frame_size(&self) -> FrameSize {
|
||||
self.frame_size
|
||||
}
|
||||
pub fn imgui(&self) -> &ImGui { self.imgui }
|
||||
pub fn want_capture_mouse(&self) -> bool {
|
||||
let io = self.imgui.io();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user