SDL icon indicating copy to clipboard operation
SDL copied to clipboard

SDL_RenderReadPixels doesn't give the same data depending on if the app is fullscreen or not

Open CorruptVoidSoul opened this issue 1 year ago • 5 comments

If my app is on windowed then everything works fine, when I go to fullscreen, I get random garbage. Anything that's in the rectangle of pixel the function reads, is either white, orange (#FFA040), or cyan (#42E2FF) I sent the part of my code that output values in this order : pixel value, red, green, blue, alpha components and does collision with it. What's also weird is that I have to use SDL_PIXELFORMAT_ARGB8888 instead of SDL_PIXELFORMAT_RGBA8888 if I want it to work at all. Here are the results of my testing and the function that reads it (I'm making a color-based collision system).

What is going on ? Thanks for your answer. Version is SDL 2.30.4

fullscreen.txt not fullscreen.txt part of the code that does it.txt

CorruptVoidSoul avatar Jun 25 '24 21:06 CorruptVoidSoul

Make sure you read before you call SDL_RenderPresent(). Also, this is going to be incredibly slow. You should use some other way of tracking collision for your game.

slouken avatar Jun 25 '24 21:06 slouken

Yes, I do call it before SDL_RenderPresent(), in fact it also has to put the player sprite after reading a 7x10 pixels rectangle, and this is the only way I found to make collisions that didn't make me lose my sanity.

CorruptVoidSoul avatar Jun 25 '24 22:06 CorruptVoidSoul

What platform are you running on and what renderer is being used?

slouken avatar Jun 26 '24 00:06 slouken

I am running that on Android with a CPU renderer because the OS won't give it an accelerated one (I work within Termux), and I make tests with a friend running Windows 11 and on his PC it is using a GPU renderer. The same thing happens on both machines.

I'm thinking of doing a workaround that would just open a second window for fullscreen and copying the content from the first one to this one.

(sorry for the delay I was sleeping)

CorruptVoidSoul avatar Jun 26 '24 07:06 CorruptVoidSoul

Something I would like to add : SDL3 is not available yet in the Termux repositories so I cannot make the switch for now

CorruptVoidSoul avatar Jun 29 '24 19:06 CorruptVoidSoul

Can you post a simple example or a modification to the SDL tests that shows this bug?

slouken avatar Jul 04 '24 15:07 slouken

While stitching together a quick-and-dirty example made out of my code and it not reproducing itself, I found out what was causing the bug. The pitch argument for SDL_RenderReadPixels won't scale up if a SDL_SetLogicalSize has been done before going into fullscreen. If I want to fix this, I need to obtain the new width, divide it by the old width and multiply it by the read area's width. I'm going to add it to the example code that I will send.

CorruptVoidSoul avatar Jul 05 '24 18:07 CorruptVoidSoul

Here is the code, I've already set it to something that shows the bug (400x400 window with 200x200 logical size). Github doesn't like .c files so it's a .txt The only functions that matter are the main, checkcolorcollision and SDL_CalculatePitch. Others are event gestion and stuff like that. I got that last function somewhere online but I think it'd be a nice addition to the SDL lib itself.

Launch it via command line after compiling it, as it'll show in this order : Pixel value, r, g, b, a components and if the player collides with a certain attack. testinglebug.txt

CorruptVoidSoul avatar Jul 05 '24 22:07 CorruptVoidSoul

FYI, the pitch is defined as the number of bytes between rows of your image, so in your case you shouldn't use the SDL_CalculatePitch() function, instead the value would be sizeof(colors) / positionplayer.h

This is unrelated to the bug, I thought it might be helpful to know.

slouken avatar Jul 06 '24 04:07 slouken

Reading the surface back is going to be a performance problem later, but your issue is fixed in SDL3.

Here's your sample, converted to SDL3:

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>

SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_FRect positionplayer;
SDL_FRect positionboolet[3];
SDL_PixelFormat *format = NULL;

enum
{
    KEY_Z,
    KEY_X,
    KEY_C,
    KEY_UP,
    KEY_DOWN,
    KEY_LEFT,
    KEY_RIGHT,
    KEY_ESC,
    KEY_R
};
int keyrepeat[9];
int speed = 2;
int quitting = 0;

void checkcolorcollision()
{
    int i = 0;
    Uint8 r, g, b, a;
    SDL_Rect rect;
    rect.x = (int)positionplayer.x;
    rect.y = (int)positionplayer.y;
    rect.w = (int)positionplayer.w;
    rect.h = (int)positionplayer.h;
    SDL_Surface *surface = SDL_RenderReadPixels(renderer, &rect);
    if (!surface) {
        // This happens when positionplayer is offscreen
        //SDL_ShowSimpleMessageBox(0, "wat", SDL_GetError(), NULL);
        return;
    }
    Uint32 *colors = (Uint32 *)surface->pixels;

    while (i < positionplayer.w * positionplayer.h) {
        SDL_GetRGBA(colors[i], surface->format, &r, &g, &b, &a);
        //SDL_Log("\n%d %d %d %d %d\n", colors[i], r, g, b, a);
        if (r == 255 && g == 255 && b == 255) {
            SDL_Log("normal atacc \n");
            return;
        }
        if (r == 0xFF && g == 0xA0 && b == 0x40) {
            SDL_Log("orange atacc \n");
            return;
        }
        if (r == 0x42 && g == 0xE2 && b == 0xFF) {
            SDL_Log("blue atacc \n");
            return;
        }
        i++;
    }
    SDL_DestroySurface(surface);
}

void countrepeats(const Uint8 *keypressed)
{
    if (keypressed[SDL_GetScancodeFromKey(SDLK_Z, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_Z]++;
    } else if (keyrepeat[KEY_Z]) {
        keyrepeat[KEY_Z] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_C, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_C]++;
    } else if (keyrepeat[KEY_C]) {
        keyrepeat[KEY_C] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_Z, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_X]++;
    } else if (keyrepeat[KEY_X]) {
        keyrepeat[KEY_X] = 0;
        speed = 2;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_LEFT, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_LEFT]++;
    } else if (keyrepeat[KEY_LEFT]) {
        keyrepeat[KEY_LEFT] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_RIGHT, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_RIGHT]++;
    } else if (keyrepeat[KEY_RIGHT]) {
        keyrepeat[KEY_RIGHT] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_UP, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_UP]++;
    } else if (keyrepeat[KEY_UP]) {
        keyrepeat[KEY_UP] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_DOWN, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_DOWN]++;
    } else if (keyrepeat[KEY_DOWN]) {
        keyrepeat[KEY_DOWN] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_ESCAPE, SDL_KMOD_NONE)] || keypressed[SDL_GetScancodeFromKey(SDLK_Q, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_ESC]++;
    } else if (keyrepeat[KEY_ESC]) {
        keyrepeat[KEY_ESC] = 0;
    }
    if (keypressed[SDL_GetScancodeFromKey(SDLK_R, SDL_KMOD_NONE)]) {
        keyrepeat[KEY_R]++;
    } else if (keyrepeat[KEY_R]) {
        keyrepeat[KEY_R] = 0;
    }
}

void eventgestion()
{
    const Uint8 *keypressed = SDL_GetKeyboardState(NULL);
    SDL_PumpEvents();
    countrepeats(keypressed);
    SDL_PumpEvents();
    //SDL_Log("%d %d %d %d %d %d \n", keyrepeat[KEY_Z], keyrepeat[KEY_X], keyrepeat[KEY_UP], keyrepeat[KEY_DOWN], keyrepeat[KEY_LEFT], keyrepeat[KEY_RIGHT]);

    if (keyrepeat[KEY_UP] > 0)
        positionplayer.y -= speed;
    if (keyrepeat[KEY_DOWN] > 0)
        positionplayer.y += speed;
    if (keyrepeat[KEY_LEFT] > 0)
        positionplayer.x -= speed;
    if (keyrepeat[KEY_RIGHT] > 0)
        positionplayer.x += speed;
    if (keyrepeat[KEY_ESC] > 0) {
        quitting = 1;
    }
}

int main(int argc, char *argv[])
{
    SDL_Rect positionwindow = { 50, 50, 400, 400 };
    int r[3] = { 0xff, 0xff, 0x42 };
    int g[3] = { 0xff, 0xa0, 0xe2 };
    int b[3] = { 0xff, 0x40, 0xff };
    int i = 0;

    window = SDL_CreateWindow("le bug", positionwindow.w, positionwindow.h, 0);
    renderer = SDL_CreateRenderer(window, NULL);
    SDL_SetRenderVSync(renderer, 1);

    //Uncomment for fullscreen
    //SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);

    SDL_SetRenderLogicalPresentation(renderer, 200, 200, SDL_LOGICAL_PRESENTATION_LETTERBOX, SDL_SCALEMODE_LINEAR);

    positionplayer.w = 16;
    positionplayer.h = 16;
    positionplayer.x = 50;
    positionplayer.y = 150;

    positionboolet[0].w = 32;
    positionboolet[0].h = 32;
    positionboolet[0].x = 32;
    positionboolet[0].y = 32;
    positionboolet[1] = positionboolet[0];
    positionboolet[1].x += 64;
    positionboolet[2] = positionboolet[1];
    positionboolet[2].x += 64;

    while (!quitting) {
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        i = 0;
        while (i < 3) {
            SDL_SetRenderDrawColor(renderer, r[i], g[i], b[i], 255);
            SDL_RenderFillRect(renderer, &positionboolet[i]);
            i++;
        }

        eventgestion();
        checkcolorcollision();

        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        SDL_RenderFillRect(renderer, &positionplayer);
        SDL_Delay(10);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    return 0;
}

slouken avatar Jul 06 '24 04:07 slouken

Alright, thanks for the answer. I'll be waiting for the addition of SDL3 and its add-ons to the Termux repos.

CorruptVoidSoul avatar Jul 06 '24 08:07 CorruptVoidSoul