最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - SDL CPU rendering project, rendering error when resizing window: Window surface is invalid - Stack Overflow

programmeradmin4浏览0评论

I was working on a cpu only rendering project with SDL in C. I implemented very good error handling and I got this error when I try to resize the window, "ERROR: SDL Error in render thread: Window surface is invalid, please call SDL_GetWindowSurface() (which is a internal function called automatically when window is resized for rendering) to get a new surface.", I want to fix the root of this problem without removing the error handling.

I think I am drawing to quickly for the SDL_GetWindowSurface() to be called internally while resizing, as resizing invalidates the surface of the window.

THE CODE:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL.h>

#include "windpiaware.h"

#pragma warning(disable: 4100) // Unreferenced formal parameter
#pragma warning(disable: 4026) // Function declaration mismatch
#pragma warning(disable: 4189) // Unreferenced local variable
#pragma warning(disable: 4820) // Padding added to struct

void HandleSDLErrorExit(int log_category, const char* error_title, const char* error_detail, SDL_Window* sdl_window){
    char* error_message = NULL;
    SDL_asprintf(&error_message, "%s: %s", error_detail, SDL_GetError());
    
    SDL_LogError(log_category, error_message);

    SetThreadDpiUnaware(); // To automatically adjust Message Box size according to the DPI
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, error_title, error_message, sdl_window);
    SetThreadDpiAware();

    SDL_free(error_message);

    SDL_Quit();
    SDL_Log("SDL quit");

    #ifdef DEBUG
        exit(EXIT_FAILURE); // used to counter debugger issues
    #endif
}

void DisplaySDLError(int log_category, const char* error_title, const char* error_detail, SDL_Window* sdl_window){
    char* error_message = NULL;
    SDL_asprintf(&error_message, "%s: %s", error_detail, SDL_GetError());
    
    SDL_LogError(log_category, error_message);

    SetThreadDpiUnaware(); // To automatically adjust Message Box size according to the DPI
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, error_title, error_message, sdl_window);
    SetThreadDpiAware();

    SDL_free(error_message);
}

typedef struct 
{
    SDL_Window* sdl;
    unsigned int id;
    bool close;
} Window;

bool dpi_aware = false;

SDL_cond* show_window_cond;
SDL_mutex* show_window_mtx;

SDL_mutex* window_close_mtx;

SDL_mutex* sdl_window_mtx;

SDL_cond* render_cond;
SDL_mutex* render_mtx;

bool resized = false;
SDL_mutex* resized_mtx;

int event_handler(void *data, SDL_Event *event) {
    Window* window = data;

    if (event->type == SDL_WINDOWEVENT && event->window.windowID == window->id){
        if (event->window.event == SDL_WINDOWEVENT_RESIZED){
            SDL_LockMutex(resized_mtx);
                resized = true;
            SDL_UnlockMutex(resized_mtx);
        }
        else if (event->window.event == SDL_WINDOWEVENT_CLOSE){
            SDL_LockMutex(window_close_mtx);
                window->close = true;
            SDL_UnlockMutex(window_close_mtx);
        }
    }

    return 1;
}

int render_t(void* data){
    Window* window = data;

    SDL_Renderer* renderer = SDL_CreateRenderer(window->sdl, -1, SDL_RENDERER_SOFTWARE);
    if (renderer == NULL) {
        SDL_LockMutex(window_close_mtx);
            window->close = true;
        SDL_UnlockMutex(window_close_mtx);
        DisplaySDLError(
            SDL_LOG_CATEGORY_RENDER,
            "Could'nt create SDL renderer",
            "Failed to create SDL renderer",
            window->sdl
        );
        return 1;
    }
    SDL_Log("SDL created renderer");

    SDL_LockMutex(show_window_mtx);
        SDL_CondSignal(show_window_cond);
    SDL_UnlockMutex(show_window_mtx);

    while (true) {
        SDL_LockMutex(window_close_mtx);
        if (window->close){
            SDL_UnlockMutex(window_close_mtx);
            break;
        }
        else {
            SDL_UnlockMutex(window_close_mtx);
        }

        if (SDL_GetError()[0]){
            DisplaySDLError(SDL_LOG_CATEGORY_ERROR, "SDL ERROR", "SDL Error in render thread", window->sdl);
            SDL_ClearError();

            SDL_Event window_close_event;
            window_close_event.type = SDL_WINDOWEVENT;
            window_close_event.window.event = SDL_WINDOWEVENT_CLOSE;
            window_close_event.window.windowID = SDL_GetWindowID(window->sdl);

            SDL_PushEvent(&window_close_event);
            break;
        }
        
        SDL_SetRenderDrawColor(renderer, 255, 0, 128, 255);

        SDL_RenderClear(renderer);

        SDL_RenderPresent(renderer);

        SDL_Delay(8);
    }

    SDL_DestroyRenderer(renderer);
    SDL_Log("SDL destroyed renderer");

    return 0;
}

int main(void) {
    // Log compiled and runtime SDL versions

    SDL_Log("Compiled SDL version: %u.%u.%u", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
    SDL_version sdl_version;
    SDL_GetVersion(&sdl_version);
    SDL_Log("Current SDL version: %u.%u.%u", sdl_version.major, sdl_version.minor, sdl_version.patch);

    // Ends
    
    // Configures DPI awareness

    #ifdef __ANDROID__
        dpi_aware = true; // Android apps are DPI-aware by default
    #else
        #ifdef _WIN32
            dpi_aware = SetProcessDpiAware(); // for Windows
        #else
            dpi_aware = SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0"); // for Linux, MacOS and others
        #endif
    #endif
    
    SDL_Log(dpi_aware ? "DPI aware" : "DPI unaware");

    // Ends

    // Configures linux compositor settings (if applicable)

    #if defined(__linux__) && SDL_VERSION_ATLEAST(2, 0, 8)
        // "1" for low latency and "0" for better compatibility
        #define BYPASS_COMPOSITOR 1

        #if BYPASS_COMPOSITOR == 1
            if (SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1")) {
                SDL_Log("SDL bypassed X11 NET Window Manager Compositor");
            }
            else {
                SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "SDL could'nt bypass X11 NET Window Manager Compositor which could increase latency");
            }
        #else
            if (SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")) {
                SDL_Log("SDL disabled X11 NET Window Manager Compositor bypass");
            }
            else {
                SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "SDL could'nt disable X11 NET Window Manager Compositor bypass which could cause compatibility issues");
            }
        #endif
    #endif

    // Ends

    // Initializes SDL subsystems

    // trys to disables potential for SDL_Surface (frame buffer / pixel data) GPU Acceleration
    SDL_SetHint("SDL_HINT_FRAMEBUFFER_ACCELERATION", "0");

    if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO) < 0){
        HandleSDLErrorExit(
            SDL_LOG_CATEGORY_VIDEO, 
            "Failed to initialize SDL", 
            "SDL failed to initialize events or/and video subsystem(s)",
            NULL
        );
        return EXIT_FAILURE;
    }
    SDL_Log("SDL initialized events and video subsystems");

    // Ends

    // Detects video displays

    if (SDL_GetNumVideoDisplays() < 1){
        HandleSDLErrorExit(
            SDL_LOG_CATEGORY_VIDEO, 
            "No video displays detected", 
            "SDL failed to detect any video display",
            NULL
        );
        return EXIT_FAILURE;
    }
    SDL_Log("Primary display detected: %s", SDL_GetDisplayName(0));

    // Ends

    // Retrieves primary display modes

    // Highest quality mode
    SDL_DisplayMode primary_display;
    SDL_GetDisplayMode(0, 0, &primary_display);

    SDL_Log(
        "Best primary display mode:"
        "\n        width: %dpx, height: %dpx"
        "\n        max refresh rate: %dHz"
        "\n        format: %s",
        primary_display.w, primary_display.h, 
        primary_display.refresh_rate,
        SDL_GetPixelFormatName(primary_display.format)
    );

    // Current desktop mode
    SDL_DisplayMode primary_desktop_display;
    SDL_GetDesktopDisplayMode(0, &primary_desktop_display);

    // dpi = {horizontal, vertical, diagonal};
    float dpi[3];
    if (SDL_GetDisplayDPI(0, &dpi[2], &dpi[0], &dpi[1]) < 0){
        SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "DPI not found, defaulting to 96: %s", SDL_GetError());
        dpi[0] = 96.0f;
        dpi[1] = 96.0f;
        dpi[2] = 96.0f;
    }
    // Calculates DSF (Display Scaling Factor)
    // dsf = {horizontal, vertical, diagonal};
    float dsf[] = {dpi[0] / 96.0f, dpi[1] / 96.0f, dpi[2] / 96.0f};

    SDL_Log(
        "Primary desktop display mode:"
        "\n        width: %dpx, height: %dpx"
        "\n        refresh rate: %dHz"
        "\n        format: %s"
        "\n        DPI:"
        "\n            horizontal: %.2f"
        "\n            vertical: %.2f"
        "\n            diagonal: %.2f"
        "\n        Display Scaling Factor:"
        "\n            horizontal: %.2f"
        "\n            vertical: %.2f"
        "\n            diagonal: %.2f",
        primary_desktop_display.w, primary_desktop_display.h, 
        primary_desktop_display.refresh_rate,
        SDL_GetPixelFormatName(primary_desktop_display.format),
        dpi[0], dpi[1], dpi[2],
        dsf[0], dsf[1], dsf[2]
    );

    // Ends

    // Produces Window

    Window window = {NULL, 0, false};

    const float initial_window_size_scale = 0.3125f; // scaling factor for window size, range [0, 1]
    window.sdl = SDL_CreateWindow(
        "Window", 
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 
        // Calculate window size using the scaling factor, desktop display mode.
        (int)(primary_desktop_display.w * initial_window_size_scale * dsf[0]), 
        (int)(primary_desktop_display.h * initial_window_size_scale * dsf[1]), 
        // ends
        SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
    );
    
    if (window.sdl == NULL) {
        HandleSDLErrorExit(
            SDL_LOG_CATEGORY_VIDEO,
            "Failed to create window",
            "SDL failed to create window",
            NULL
        );
        return EXIT_FAILURE;
    }
    SDL_Log("SDL created window");

    // Ends

    // Initialises render thread

    show_window_cond = SDL_CreateCond();
    show_window_mtx = SDL_CreateMutex();

    window_close_mtx = SDL_CreateMutex();

    sdl_window_mtx = SDL_CreateMutex();

    render_cond = SDL_CreateCond();
    render_mtx = SDL_CreateMutex();

    resized_mtx = SDL_CreateMutex();

    SDL_Thread* render_thread = SDL_CreateThread(render_t, "render", &window);

    SDL_LockMutex(show_window_mtx);
        SDL_CondWait(show_window_cond, show_window_mtx);
    SDL_UnlockMutex(show_window_mtx);

    // Ends

    SDL_ShowWindow(window.sdl);

    window.id = SDL_GetWindowID(window.sdl);

    SDL_AddEventWatch(event_handler, &window);

    SDL_Event event;
    
    while (true)
    {
        SDL_LockMutex(window_close_mtx);
        if (window.close){
            SDL_UnlockMutex(window_close_mtx);
            break;
        }
        else {
            SDL_UnlockMutex(window_close_mtx);
        }

        if (SDL_GetError()[0]){
            DisplaySDLError(SDL_LOG_CATEGORY_ERROR, "SDL ERROR", "SDL Error in main thread (Window Manager thread)", window.sdl);
            SDL_ClearError();

            SDL_LockMutex(window_close_mtx);
                window.close = true;
            SDL_UnlockMutex(window_close_mtx);
            break;
        }
        
        SDL_WaitEvent(&event);
    }

    int exit_code = EXIT_SUCCESS;

    // Cleans up

    int render_thread_status;
    SDL_WaitThread(render_thread, &render_thread_status);
    exit_code = render_thread_status;

    SDL_DelEventWatch(event_handler, &window);

    // destroy mutexes and conditions
    SDL_DestroyCond(show_window_cond);
    SDL_DestroyMutex(show_window_mtx);

    SDL_DestroyMutex(window_close_mtx);

    SDL_DestroyMutex(sdl_window_mtx);

    SDL_DestroyCond(render_cond);
    SDL_DestroyMutex(render_mtx);

    SDL_DestroyMutex(resized_mtx);

    SDL_DestroyWindow(window.sdl);
    SDL_Log("SDL destroyed window");

    SDL_Quit();
    SDL_Log("SDL quit");

    // Ends

    #ifdef DEBUG
        exit(exit_code); // used to counter debugger issues
    #else
        return exit_code;
    #endif
}

LOGS:

INFO: Compiled SDL version: 2.30.11
INFO: Current SDL version: 2.30.11
INFO: DPI aware
INFO: SDL initialized events and video subsystems
INFO: Primary display detected: Generic PnP Monitor
INFO: Best primary display mode:
        width: 2880px, height: 1800px
        max refresh rate: 120Hz
        format: SDL_PIXELFORMAT_RGB888
INFO: Primary desktop display mode:
        width: 2880px, height: 1800px
        refresh rate: 120Hz
        format: SDL_PIXELFORMAT_RGB888
        DPI:
            horizontal: 192.00
            vertical: 192.00
            diagonal: 192.00
        Display Scaling Factor:
            horizontal: 2.00
            vertical: 2.00
            diagonal: 2.00
INFO: SDL created window
INFO: SDL created renderer
ERROR: SDL Error in render thread: Window surface is invalid, please call SDL_GetWindowSurface() to get a new surface
INFO: SDL destroyed renderer
INFO: SDL destroyed window
INFO: SDL quit

I tried removing error handling, resizing and rendering worked in parallel with no issues and I also tried GPU acceleration with error handling, and it worked even better.

I want the window to keep rendering whilst I resize without compromising the error handling and the goal of the project (CPU rendering).

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论