vegastrike / Vega-Strike-Engine-Source

Vega Strike Engine
Other
256 stars 42 forks source link

Game video output not recognizing scaled displays. #884

Open evertvorster opened 3 days ago

evertvorster commented 3 days ago

One of my monitors is fairly high resolution, and so it is scaled so that it is usable. Unfortunately this causes issues with VegaStrike which is strictly pixel based.

SDL has a fairly straightforward way of dealing with this that should not impact non-scaled displays: https://wiki.libsdl.org/SDL2/SDL_HINT_WINDOWS_DPI_SCALING

royfalk commented 3 days ago

What happens if you try 1024x768 (or some other low resolution) in full screen? Why do we have both this and #883 ?

evertvorster commented 3 days ago

Maybe a screenshot will show you what I mean. Here I used 1366x768, on a 4K monitor with 125% scaling: image

evertvorster commented 3 days ago

A little nuance here. When picking a display size of 3840x2160 which is the monitor's native resolution, it displays too big, because the display is being scaled by the window manager.

Obligatory screenshot: image

To get the properly sized display, I have to get the display resolution from a website that checks this sort of thing, and that says my display size is 3200x1800. That resolution does not exist in the vesettings, and so I have to edit vegastrike.config, make the resolution, and then only can I get a proper full screen resolution.

If I have to make a new screen resolution for every scaling factor, the amount of resolutions in the config file becomes a little cumbersome.

So, it is a different problem than the full screen not being scaled to the full monitor in fullscreen mode.

royfalk commented 3 days ago

OK. See if you can find a reference in SDL as well as a discussion or code snippet to implement this. If you can find it, I'll implement this. Contingent on approval by @BenjamenMeyer.

evertvorster commented 3 days ago

I asked ChatGPT about it, and here is the response: In SDL (Simple DirectMedia Layer), handling various screen resolutions and scaled displays involves detecting available screen resolutions, setting the display mode accordingly, and managing proper scaling for different resolutions. Here’s a step-by-step approach to achieve this:

1. Detect Available Screen Resolutions

SDL provides a function to get the available display modes for each connected monitor. You can query the modes and then allow the user to select the preferred resolution.

Here’s an example of how to detect the available screen resolutions:

#include <SDL2/SDL.h>
#include <stdio.h>

void listAvailableResolutions(int displayIndex) {
    int numDisplayModes = SDL_GetNumDisplayModes(displayIndex);
    if (numDisplayModes < 0) {
        printf("SDL_GetNumDisplayModes failed: %s\n", SDL_GetError());
        return;
    }

    printf("Available resolutions for display %d:\n", displayIndex);
    for (int i = 0; i < numDisplayModes; i++) {
        SDL_DisplayMode mode;
        if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0) {
            printf("SDL_GetDisplayMode failed: %s\n", SDL_GetError());
            continue;
        }
        printf("%d x %d @ %dHz\n", mode.w, mode.h, mode.refresh_rate);
    }
}

int main() {
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        printf("SDL_Init Error: %s\n", SDL_GetError());
        return 1;
    }

    int numDisplays = SDL_GetNumVideoDisplays();
    if (numDisplays < 0) {
        printf("SDL_GetNumVideoDisplays failed: %s\n", SDL_GetError());
        return 1;
    }

    // List available resolutions for each display
    for (int i = 0; i < numDisplays; i++) {
        listAvailableResolutions(i);
    }

    SDL_Quit();
    return 0;
}

This code initializes SDL, retrieves the available resolutions for each display, and prints them out. You can select a resolution from this list and apply it later.

2. Set Display Mode

Once you've selected a resolution, you can set the SDL window to that resolution. Here’s how you do it:

void setDisplayMode(int displayIndex, int width, int height) {
    SDL_Window *window = SDL_CreateWindow("Game Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_FULLSCREEN_DESKTOP);
    if (!window) {
        printf("SDL_CreateWindow failed: %s\n", SDL_GetError());
        return;
    }

    SDL_DisplayMode displayMode;
    if (SDL_GetCurrentDisplayMode(displayIndex, &displayMode) != 0) {
        printf("SDL_GetCurrentDisplayMode failed: %s\n", SDL_GetError());
    } else {
        SDL_SetWindowDisplayMode(window, &displayMode);
    }

    // Game loop and rendering goes here...

    SDL_DestroyWindow(window);
}

Here, SDL_CreateWindow sets the window to fullscreen with the desktop resolution, and then you apply the selected display mode using SDL_SetWindowDisplayMode.

3. Handle Scaled Displays

If you are working with different resolutions, SDL provides an easy way to render at a consistent resolution, regardless of the screen size, using the SDL_RenderSetLogicalSize() function. This function scales the rendering output to match the window’s resolution.

Example for handling scaling:

void setLogicalScaling(SDL_Renderer *renderer, int logicalWidth, int logicalHeight) {
    SDL_RenderSetLogicalSize(renderer, logicalWidth, logicalHeight);
}

int main() {
    SDL_Window *window;
    SDL_Renderer *renderer;

    SDL_Init(SDL_INIT_VIDEO);

    // Create window at some base resolution, here 1920x1080
    window = SDL_CreateWindow("Game Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080, SDL_WINDOW_SHOWN);

    // Create renderer
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

    // Set logical size to scale rendering to 1920x1080 regardless of window resolution
    setLogicalScaling(renderer, 1920, 1080);

    // Rendering loop
    int running = 1;
    SDL_Event event;
    while (running) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = 0;
            }
        }

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        // Your game rendering logic here...

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

4. Fullscreen and Windowed Mode Handling

To switch between fullscreen and windowed modes dynamically, you can use SDL_SetWindowFullscreen:

void toggleFullscreen(SDL_Window *window) {
    Uint32 fullscreenFlag = SDL_WINDOW_FULLSCREEN_DESKTOP;
    int isFullscreen = SDL_GetWindowFlags(window) & fullscreenFlag;
    SDL_SetWindowFullscreen(window, isFullscreen ? 0 : fullscreenFlag);
}

Summary:

By combining these techniques, your game engine can detect and adapt to various screen resolutions and handle proper scaling across different display setups.

royfalk commented 3 days ago

OK. That looks good. Let's give Ben a chance to think about this.

BenjamenMeyer commented 3 hours ago
  1. We probably need to take a stance about AI generators (Copilot, Gemini, ChatGPT, etc); most open source projects have bans on them (namely due to liability issues). So while the comment above is helpful and informative we should use it as a reference and not directly to code. We probably should open a separate discussion about AI generators.
  2. We should have something in VS Settings sooner than later. I'm good with that. Ultimately I think we should integrate VS Settings into the game itself so you don't have to leave the game to change things.
  3. We should probably scale the fonts separate from the resolution so we can support small/medium/large/extra large fonts to avoid issues with either fonts being too small or too large. Mapping it to the resolution does not necessarily resolve that.