high-dpi mode

Hi
is there a doc explaining what the SDL_WINDOW_ALLOW_HIGHDPI flag does
(I mean, what the high-dpi mode does)?

More specifically, I have scaling problems reported by users on Mac with Retina screen.
As I cannot test this config myself, what do you think is the best flag combination for CreateWindow on such a machine?

Currently, on my High-dpi linux Laptop I have no problem, but on the Mac the user is reporting that the content of the window is displayed on the top left corner only, and is very tiny of course. Moreover, the mouse position is not aligned with the graphics (when you click on the middle, the program reports that you click on the top left part.)

My windows are created with SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN

(there are shown after initialization, of course)

thanks for any hint

High DPI on Macs works the same way it does on iOS. Screen coordinates are in points, not pixels (in fact, system APIs take floating-point values, not ints). On Macs with Retina screens, you get a 2x scaling factor. So if you create a window in SDL that’s 800x600, it gives you a window that’s 800x600 points, but is actually 1600x1200 pixels. SDL_Renderer still treats coordinates as pixel values, though, at least on macOS, which is why your app’s content is in the top left corner.

Solutions:

  1. Disable high-DPI. Not really a great solution.
  2. At run-time, check the size of the SDL_Renderer against the size of the window you requested, and then set a scale factor based on that (this is what I do). Something like:
// Put this somewhere after you create the SDL_Renderer but before your game
// loop
// windowWidth/windowHeight are the logical size of the window, aka what was
// sent to SDL_CreateWindow() (or current size if it's resizable)
int rw = 0, rh = 0;
SDL_GetRendererOutputSize(render, &rw, &rh);
if(rw != windowWidth) {
	float widthScale = (float)rw / (float) windowWidth;
	float heightScale = (float)rh / (float) windowHeight;

	if(widthScale != heightScale) {
		fprintf(stderr, "WARNING: width scale != height scale\n");
	}

	SDL_RenderSetScale(render, widthScale, heightScale);
}
1 Like

thanks a lot for the explanation!

I tried both solutions, and indeed they “work”.
however the result is a bit “blurry”. With the second solution, it is understandable: using SDL_RenderSetScale(renderer,2,2) will scale all textures by 2, hence the result will loose precision. It is easily seen when the texture contains text (I’l using SDL_ttf on a target texture, and at each step I’m rendering the texture).

With the first solution (disable high-DPI) the window is of course smaller, but what is strange is that rendering is equally “blurred”. I don’t really understand why.

I guess the only solution for me would be to enable high-DPI, but not use SDL_RenderSetScale; instead, I will have to scale appropriate things manually.

For non-pixel-art apps and games, the solution typically used on iOS is to have a second set of textures that are 2x the dimensions (and 3x, for the larger iPhones) of the non-Retina ones, and thus will be the same size on screen but still sharp.

Would depend on your app/game whether or not it’s feasible to go this route.

Because the display is still high DPI. So while your framebuffer everything is getting rendered to is only, say, 800x600 pixels, making it take up 800x600 points on screen means it gets stretched to 1600x1200.

I’d recommend keeping SDL_RenderSetScale and then for stuff like text using a font size scaled by the render scale, so your text textures won’t need to be stretched. Scaling everything manually will still result in non-high-DPI art assets being “blurry”

1 Like

Thanks again for the explanations. I don’t understand this point. If I set SDL_RenderSetScale is this correct that all SDL textures will be stretched upon rendering?
Then everything I do will be “blurry”, right?
(I’m rendering text to a target texture and then blit the texture on the renderer. I’m not using direct openGL calls)

All SDL_RenderSetScale does is cause SDL_Renderer to scale the destination coordinates and size given to it in SDL_RenderCopy etc by the specified scale. If the texture is smaller than the final result then yes, it’ll get stretched.

If you scale your font size by the render scale and create a texture filled with some text at this bigger font size, but then draw the texture as if it were @1x font size, the texture will be the size of the final, scaled size and no stretching will happen.

In other words, if you have a texture filled with text that would be 200x100 pixels on a non-Retina display, make it 400x200 pixels on Retina, but draw it as if it were 200x100 (its point size). Since you’ve set SDL_RenderSetScale() to 2, the coordinates will get stretched to 400x200, which matches its pixel size, and it will take up 200x100 “points” on screen and appear sharp.

Something like

... SDL_RenderSetScale() code from before
... render scale is in a float called renderScale
SDL_Texture *text = CreateTextureFromText("Hello, world!", someFont, fontSize * renderScale);
int textWidth, textHeight;
SDL_QueryTexture(text, NULL, NULL, &textWidth, &textHeight);
SDL_FRect destRect;
destRect.x = 100.0f;    // or wherever else you want it to appear on screen
destRect.y = 100.0f;
// Give us the texture size *in points* not pixels.
// On non-Retina displays (or with high DPI disabled) this is also the pixel
// size and so this code will still work on those displays
destRect.w = (float)textWidth / renderScale;
destRect.h = (float)textHeight / renderScale;
SDL_RenderCopyF(myRenderer, text, NULL, &destRect);

And that should give you sharp text.