Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add high DPI support to GLFW example #287

Closed
wants to merge 1 commit into from
Closed

Add high DPI support to GLFW example #287

wants to merge 1 commit into from

Conversation

GHF
Copy link
Contributor

@GHF GHF commented Jul 27, 2015

  • Pass framebuffer size to "GL screen space" (i.e. pixels) calls:
    glViewport, glScissor
  • Pass OS screen coordinates to rendering and mouse IO
  • Use GL_NEAREST magnification for font atlas since it's all sharp
    raster images

glfw_hidpi_osx

- Pass framebuffer size to "GL screen space" (i.e. pixels) calls:
  glViewport, glScissor
- Pass OS screen coordinates to rendering and mouse IO
- Use GL_NEAREST magnification for font atlas since it's all sharp
  raster images
@ocornut
Copy link
Owner

ocornut commented Aug 1, 2015

Thanks!
I am a little confused with the behaviour of retina display and don't have one to test.
Would it fix what's being reported here? (it was my assumption in the tweet reply but I am not sure anymore)
https://twitter.com/mmalex/status/626084583407116288

Not sure about this:

Use GL_NEAREST magnification for font atlas since it's all sharp raster images

We need the bilinear for fonts that are oversampled and sub-pixel positioned (not the case of the default font). If you zoom the font past its oversampling point the difference will be visible.
You can test it by loading a TTF, double-clicking on Global Scale and setting it to 3.0 (default horizontal oversample is 3).
I added this in my render loop to quickly test:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ImGui::GetIO().KeyCtrl ? GL_NEAREST : GL_LINEAR);

How does your change behave with the default font if you keep MAG_FILTER to GL_LINEAR ?

I'm thinking perhaps it would make sense to include that scale multiplier in the ImGuiIO structure, say, DisplayFramebufferScale, so that ImGui can scale the clipping rectangle for you. That would also make the example a little more neat.

@GHF
Copy link
Contributor Author

GHF commented Aug 2, 2015

Yep, the behavior in that tweet is exactly what I was trying to address.

To be exact, the behavior of the Retina display with respect to GLFW is that it creates an OpenGL context at the display's physical resolution (i.e. double in each dimension to the window size requested). However, mouse coordinate are still in window coordinates, not the double pixel coordinates.

Setting the global scale factor to 2.0 fixes most things, but not all:

screen shot 2015-08-01 at 23 26 22

- Window position and size aren't scaled - "Pixel" scale layout dimensions like the spacing between those checkboxes ("no title," "no border," etc.) and the width of the right-hand side text labels ("bg alpha") aren't scaled - The active region of southeast window corners isn't scaled (but the drawing is) - Line widths aren't scaled

My change essentially provides imgui with window coordinates only then scales them to physical framebuffer coordinates when drawing. This works great for everything but the two cases you mentioned: clipping using glScissor (that's in viewport coordinates) and texture sampling (which is of course still per-pixel, but now from textures a quarter of the desired size).

I see your point about the MAG_FILTER. With that in mind, I agree that changing to GL_NEAREST is not an elegant solution. The side effect of using GL_LINEAR at double resolution is what you would expect, that sharp lines get blurry:

screen shot 2015-08-01 at 23 57 36

I also tried translating by half a pixel in X and Y, which only partially works.

I think the ideal solution would be to maintain the framebuffer scale as you mentioned and also render the raster assets at the scaled resolution. In fact, then it can also take into account all of the framebuffer * global * window * font scaling to produce an atlas at the same DPI as the output framebuffer for the sharpest rendering.

As for the clipping rectangle, I don't agree. The clipping rectangle being in framebuffer coordinates seems like an OpenGL specific behavior, and I don't think imgui is responsible for that scaling.

By the way, you can test Retina behavior on any Mac using Retina DisplayMenu (RDM.app): http://www.everymac.com/systems/apple/macbook_pro/macbook-pro-retina-display-faq/macbook-pro-retina-display-hack-to-run-native-resolution.html

ocornut added a commit that referenced this pull request Aug 2, 2015
@ocornut
Copy link
Owner

ocornut commented Aug 2, 2015

Thanks for your detailed answer.

To clarify the "global scale" is a font-only scale, I should clarify that in the labelling. There's no actual "global scale" at the moment. "The active region of southeast window corners isn't scaled (but the drawing is)" was a for bug which I've committed a fix for. The reason I said to set it to 3 for testing is better only above 2 will start using the mag filtering because TTF fonts are already oversampled by a factor of 2.

In the code for ImGui::AddFontDefault() could you try (just for the test) to set OversampleH and OversampleV to 2 and see how that affects the sampling with GL_LINEAR? Another solution that would work only for integer uniform scale (here 2) would be to render the default font with a size of 26.0f instead of 13.0f.

As for the clipping rectangle, I don't agree. The clipping rectangle being in framebuffer coordinates seems like an OpenGL specific behavior, and I don't think imgui is responsible for that scaling.

I suppose it is the application decision to scale the display. But the point of moving those variables g_FramebufferScale inside ImGui is to help handling a common pattern.

Perhaps what we can do is:

  • Add DisplayFramebufferScale in ImGuiIO. This will be used solely to scale rasterized font data.
  • Add a helper ImDrawData::ScaleClipRects() that would go and scale all clip rectangles, documented as a helper if you want to render to a different resolution. The advantage would merely be that it would be 1 line at the top of the GL render function without adding extra confusion to the handling of clipping rectangle (which is a common source of confusion to the user).

(Sorry this is going slow/cautious, I have to play it blind. When I get back home in a few weeks I have a 2011 iMac here and hope I can use the tools you linked for testing. I have never compiled ImGui for Mac myself.)

@emoon
Copy link
Contributor

emoon commented Aug 7, 2015

Im interested in getting this sorted as well (my new:ish MacBook Pro has a Retina Display so I run my stuff scaled currently)

@ocornut
Copy link
Owner

ocornut commented Aug 7, 2015

Shouldn't the easiest solution (at least for the examples) to ensure creating a non-scaled framebuffer and scale it over the display? Not sure how to do that with glfw.

Daniel, if you can help testing the font stuff above it would help?
Setting OversampleH = OversampleV = 2 in AddFontDefault() and see if it makes the font crisp. In which case, I can multiply oversampling factor by the newly introduced FramebufferScale variable.
(and perhaps confirm if the rest makes sense)

@emoon
Copy link
Contributor

emoon commented Aug 9, 2015

I will try to test this out early next week. Is there anything specific I need to do except changing what you suggest? By default Mac OS will scale all applications 2x unless they state that they should run in 'native' res

@ocornut
Copy link
Owner

ocornut commented Aug 9, 2015

My bad - actually I don't need those tests to be done, I can do them on any computer.

I am still unsure what to do with the font. Oversampling 2 the default font create a blurry output. It needs to be rendered at twice the size 26 instead of 13 (which gives a crisp result unlike oversampling) and then "undersampled" aka adjusting the output glyph size to render them back at half the size. I believe that may work. Would need some testing.

It'd be easier to use that 2x scale that @emoon mention and have an unscaled framebuffer that you scale over the window, unless you really want the double resolution?

@emoon
Copy link
Contributor

emoon commented Aug 9, 2015

The default 2x rescale as it looks right now looks quite awful (it looks bad in all apps that doesn't support it) I'm not sure what the best way is but to have it looks as good as it gets one has to render in native res (I suppose) and adapt to the current res (even if it would eat more GPU)

@ocornut
Copy link
Owner

ocornut commented Aug 9, 2015

We can merge a modified version of this PR already (with my suggestions) and tackle the default font later. I'll look into it at some point.

@emoon
Copy link
Contributor

emoon commented Aug 9, 2015

Sounds good!

@GHF
Copy link
Contributor Author

GHF commented Aug 13, 2015

Oops sorry I was busy. :)

With OversampleH × 2 and OversampleV × 2, the fonts don't get crisper. It looks like the font renderer isn't actually sampling at a higher resolution when those are set, but only drawing glyphs with quadruple size.

screen shot 2015-08-13 at 11 54 20

screen shot 2015-08-13 at 11 42 41

You can actually tell this is happening without high-DPI mode. As you increase OversampleH and OversampleV, you would expect higher-frequency components in each glyph (since it's sampled at higher resolution). But we're not sampling the font texture at higher resolution as we do this, so we should observe aliasing, i.e. the on-screen rendering skips texels. Instead, fonts stay smooth as we increase Oversample.

This is a 1× DPI rendering with OversampleH = OversampleV = 8.

screen shot 2015-08-13 at 11 58 31

But this is what I expect to happen with greater Oversample.

screen shot 2015-08-13 at 12 00 43

(heh the packing is tighter...)

Rendering at double font size at you suggested works great in Retina mode:

screen shot 2015-08-13 at 11 46 11

I'm not 100% sure but it looks like stb_truetype implements its oversample parameter as a resolution scale and a box filter of "oversample" in size to allow subpixel-size shifts without shimmering caused by aliasing. I don't think it's intended for logical/display resolution transfers.

What we want is double texture resolution and normal size glyph rects, which seems simple with a DisplayFramebufferScale variable.

I also agree with adding helper to scale the clip rects.

As for @emoon's suggestion about OS X defaults, normally that works but GLFW seems to always create a native-resolution display.

@ocornut
Copy link
Owner

ocornut commented Aug 27, 2015

See two commits above - manually recreated, not tested on Retina yet.
Haven't worked on the font yet.

I'm concerned that oversampling + frame-buffer scale (x2 ? does x3 or x4 exist?) + huge glyph ranges such as Chinese range will be a problem. I'll probably need to tackle the dynamic atlas. Maybe the scale value for font can be a different value so user can lower it back if needed.

@ocornut
Copy link
Owner

ocornut commented Aug 27, 2015

What we want is double texture resolution and normal size glyph rects, which seems simple with a DisplayFramebufferScale variable.

For smooth fonts but not for the default font, where we want double font size?

@emoon
Copy link
Contributor

emoon commented Sep 18, 2015

Sorry for the late reply but this seems to work fine :) not sure how to do about the fonts though as you point out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants