raysan5 / raylib

A simple and easy-to-use library to enjoy videogames programming
http://www.raylib.com
zlib License
22.92k stars 2.29k forks source link

[rcore] Incorrect monitor resolution #4522

Open xroberx opened 5 days ago

xroberx commented 5 days ago

Issue description

The monitor of my laptop has a resolution of 2560x1600. I have configured scaling to 1.25x through Xfce, which sets virtual resolution to 3328x2080 as can be seen in the output of the xrandr command. However, when I call GetMonitorWidth()/GetMonitorHeight(), I get the physical resolution of the monitor (2560x1600), not the virtual one. Also, GetWindowScaleDPI() returns 1.

SDL3 returns the virtual resolution on X11, so I don't think it's a misconfiguration on my part.

Environment

Hardware: Ryzen 7 8845HS laptop with Radeon 780M integrated graphics, 2560x1600 pixels monitor Software: Linux 6.11.9, XFCE 4.18, Xorg-server 21.1.4

Issue Screenshot

xrandr

Code Example

InitWindow(WIDTH, HEIGHT, "Test"); int w = GetMonitorWidth(0); int scale = GetWindowScaleDPI();

xroberx commented 5 days ago

Those are the correct values. GetMonitorWidth(), GetMonitorHeight() are returning the exact sizes provided by GLFW (ref, ref) or SDL (ref, ref), as they should.

If you want to factor in arbitrary user defined/applied scales you'll have to multiply it on client-side. GLFW has it (ref), SDL3 has it (ref), SDL2 doesn't (ref). ~But nobody implemented a GetMonitorScale() yet for raylib, although it probably should be added at some point.~ (see #4525)

Thank you for your response. I just merged your pull request locally and I get 1.000000 from GetMonitorScale(), exactly the same value I get from GetWindowScaleDPI().

The thing is SDL3 returns the virtual size, which is the size I have to work with:

SDL_DisplayID display = SDL_GetPrimaryDisplay();
const SDL_DisplayMode *displayMode = SDL_GetCurrentDisplayMode(display);
SDL_Log("Display size: %dx%d\n", displayMode->w, displayMode->h);

Output: Display size: 3328x2080

UPDATE: I just recompiled the same test program for Windows and it shows a Display Size of 2560x1600 (the physical size of the monitor). As for GetMonitorScale() and GetWindowScaleDPI(), both return 2.0. And setting the size of the window to 2560x1600 displays a full screen window on Windows, but a smaller one on Linux. To be honest, the virtual display size on X11 looks like a mess to me, because you need to use that size instead of the physical size of the monitor and it seems there is some kind of internal interpolation (correct me if I am wrong).

xroberx commented 5 days ago

I didn't add implementation for SDL, just GLFW and web.

Yes, I know. I used the GLFW backend. The SDL3 test program is not using raylib, it is using SDL3 directly.

No idea why SDL3 is returning the virtual size there.

I don't know either, I just know that it's the size I have to work with under X11. I think Wayland is different...

xroberx commented 4 days ago

Tested on Linux Mint 22 MATE Edition with X11 and everything is working correctly here. Logs

$ lsb_release -a
No LSB modules are available.
Distributor ID: Linuxmint
Description:    Linux Mint 22
Release:        22
Codename:       wilma
$ xdpyinfo | grep version
version number:    11.0
X.Org version: 21.1.11
$ apt-cache policy mate-desktop
mate-desktop:
  Installed: 1.26.2-1.1build3
  Candidate: 1.26.2-1.1build3
  Version table:
 *** 1.26.2-1.1build3 500
        500 http://archive.ubuntu.com/ubuntu noble/universe amd64 Packages
        100 /var/lib/dpkg/status

Out of curiosity, which distro are you using? Maybe the issue could be related to the way XFCE is setting that scale.

Interesting, I just installed Linux Mint 22 and tried different scaling settings. When I use an integer scaling (200%), the display resolution shown by xrandr matches the physical display resolution (2560x1600) and GetWindowScaleDPI() returns 2, which is correct. But when I use fractional scaling (say 150%), the resolution shown by xrandr is scaled accordingly (so it does not match the physical resolution of the monitor anymore) and GetWindowScaleDPI() still shows 2.

SDL3 works as expected in this case, because it seems fractional scaling is accomplished by altering the physical resolution of the monitor (at least on X11). This is why you have to work with a "virtual" resolution. On Windows you can use fractional scaling but still work with the physical resolution of the monitor.

xroberx commented 4 days ago

I have been doing some more testing... I have recompiled raylib with the new RGFW backend and now it behaves likes SDL3. Now GetMonitorWidth()/GetMonitorHeight() return the "virtual" resolution of the monitor (3328x2080), the same resolution shown by xrandr.

HOWEVER, now I cannot use SetWindowSize() after InitWindow(), it is just ignored.

xroberx commented 3 days ago

Hi there again!

I finally found the issue. It is an upstream issue of GLFW -> https://github.com/glfw/glfw/issues/1961

As I said, both the SDL3 and RGFW backends return the proper values. It's a shame I can't use either of those because RGFW seems to be terribly broken and the framerate with SDL3 is not stable (I don't know why, but it is something that also happens using SDL3 directly).

ColleagueRiley commented 2 days ago

@xroberx

RGFW seems to be terribly broken

What specifically is broken?

HOWEVER, now I cannot use SetWindowSize() after InitWindow(), it is just ignored.

That's strange, I'll look into this ASAP

xroberx commented 2 days ago

@xroberx

RGFW seems to be terribly broken

What specifically is broken?

Apart from SetWindowSize() not working, try the following code:

#include <raylib.h>                                                                                                             

int main() {                                                                                                                    
    InitWindow(800, 600, "TEST");                                                                                               
    SetTargetFPS(60);                                                                                                           

    while (!WindowShouldClose()) {                                                                                              
        BeginDrawing();                                                                                                         
        ClearBackground(BLACK);                                                                                                 
        DrawRectangleLines(0, 0, 800, 600, WHITE);                                                                              
        EndDrawing();                                                                                                           
    }                                                                                                                           
    CloseWindow();                                                                                                              

    return 0;                                                                                                                   
} 

This is what I get using the RGFW backend:

rgfw-window

This is the LOG output using the RGFW backend:

rgfw

And this is the LOG using the default GLFW backend, which works fine:

glfw

orcmid commented 2 days ago

I was curious about this example and discovered a tangential issue.

I used the @xroberx code exactly and compiled it (using my VCrayApp script) using Visual Studio Build Tools and Visual Studio 2022 Developer Command Prompt v17.11.5 (latest) for x64. The raylib/src/ used to build a cache of *.obj files is the current main 5.6-dev repo.

Here is the closer-to-expected result on my Windows 10 desktop. RGFW-Resolution c

The screen capture was with HyperSnap Version 9.5.2 (64-bit). I used the capture mode for the complete current Window (having the cursor, which is also captured).

An interesting feature of the captured PNG image is that it is 802 pixels wide and 639 pixels deep. And that includes the GUI title bar and frame of the application area: a (single pixel?) frame of the same default color as the title bar. This might not be apparent in the GitHub rendering of this image, but I attest that it is present and visible when viewing the PNG off-line.

We can see that the drawn rectangle overlays the top and right edge of the frame, but no rectangle appears at (or atop) the left and bottom frame lines. My speculation is that those parts of the rectangle are outside the frame and do not appear.

Here's an example used to confirm that a cache of raylib 5.6-dev code compiles correctly and is demonstrated by a simple program using an InitWindow(800,450). The displayed and captured application window is 802 wide by 489 pixels high. The Windows 10 GUI single pixel frame around the application area is apparent. VCrayConfirm-5 6-dev-2024-11-23-1728

The @xroberx problem is not confirmed in this case. But we now have something concerning the precision of framing and how there may be rounding issues in the box drawing at the edge of the displayed application area.

ColleagueRiley commented 2 days ago

@xroberx Do you mind opening the RGFW issue separately so I can look into it?

orcmid commented 2 days ago

@xroberx @raysan5 @ColleagueRiley

I was curious about this example and discovered a tangential issue. [ ... ] The @xroberx problem is not confirmed in this case. But we now have something concerning the precision of framing and how there may be rounding issues in the box drawing at the edge of the displayed application area.

I have dug around and found the defect to be a problem in DrawRectangleLines(). There will be a separate issue on that. The crux of the defect is that, for

   DrawRectangleLines(0, 0, w, h, color)

The coordinates of the initial corner point should be (0, 0) and the far corner from there should be at coordinates (w-1, h-1) at default settings (and w, h both greater than 0). DrawRectangleLines makes a mess of that.

ColleagueRiley commented 2 days ago

The coordinates of the initial corner point should be (0, 0) and the far corner fro

@orcmid Yes, but, strangely, the RGFW one is given a viewport but the GLFW one isn't. Is that the proper behavior?

orcmid commented 2 days ago

@ColleagueRiley

The coordinates of the initial corner point should be (0, 0) and the far corner fro

@orcmid Yes, but, strangely, the RGFW one is given a viewport but the GLFW one isn't. Is that the proper behavior?

I don't know enough to go that deep. I am relying entirely on the definition of DrawRectangleLines() in rshapes.c where rlGetmatrixModelview() is relied upon, along with a sketchy zoomFactor. (I assume mat.m0 is likely 1.0 in a default setup as used by @xroberx in his demonstration. If there's something uninitialized or set to something other than the Matrix identity values, that might be a clue for the @xroberx problem.

ColleagueRiley commented 2 days ago

@orcmid I did a quick test and I think there might be a problem with SetupFramebuffer for the RGFW platform. Maybe it's just not something that platform needs (RGFW can do scaling internally properly)

orcmid commented 2 days ago

@orcmid I did a quick test and I think there might be a problem with SetupFramebuffer for the RGFW platform. Maybe it's just not something that platform needs (RGFW can do scaling internally properly)

It seems to me that the definition of rlGetMatrixModelview() in rlgl.h and used by rshapes.c is clear enough, unless there is a setup error for RLGL.State.modelview. That would be worth looking at.

I find it weird that DrawRectangleLines() defines a zoomFactor that is apparently a rounding fudge; if the model view is the identity matrix, it is simply 0.5f and used to fiddle integer-valued pixel coordinates.