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:
Joonas Javanainen 2018-08-12 13:51:52 +03:00
parent 1663e66887
commit aa4ae70054
No known key found for this signature in database
GPG Key ID: D39CCA5CB19B9179
5 changed files with 108 additions and 98 deletions

View File

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

View File

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

View File

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

View File

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

View File

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