Joymaker
Joymaker

Reputation: 1428

SDL2: what determines single vs double buffering?

I am trying to revive an old cross-platform graphics library that I wrote for my students over 20 years ago. After Apple pulled the plug on Carbon, I reimplemented the Mac side on top of SDL2, which was working about 10 years ago. This library is built around a persistent-screen model rather than a persistent-object model: pixels once drawn stay drawn until erased. Obviously, this model won't work with double buffering. So I have rebuilt around this answer in order to get single buffering.

Standing alone, the code below works as intended, single-buffering, yielding this image:

enter image description here

Copied VERBATIM into my larger main file of my library in place of that file’s main(), it somehow executes in a double-buffering mode, with an obvious pattern of alternate lines being drawn into alternate buffers. And furthermore, showing evidence that SDL_RenderDrawLine is drawing into some sort of uninitialized memory and then being blitted piecemeal onto the main display texture. (Although I have included minimal headers here for brevity, the stand-alone version continues to work as intended even when I include EXACTLY the same headers (a much larger set) used in the full main file of the library.)

enter image description here enter image description here

Obviously I can't expect someone to debug my larger program, but does anyone know: where exactly is the "switch" in SDL2 that determines whether it will operate in single-buffer or double-buffer mode? Or have I perhaps exposed a bug? Something really delicate seems to be making the determination.

MacOS 14.2.1; SDL2 version 2.30.4

The code:

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

#include <unistd.h>

SDL_Window* gWindow = NULL;             //The window we'll be rendering to
SDL_Renderer* gRenderer = NULL;         // Renders to an offscreen "texture" (frequent)
static SDL_Texture *target;
SDL_Event event;

void DrawNow();

int points[][2] = {
    {50, 50},
    {200, 100},
    {50, 300},
    {400, 400},
    {300, 70},
    {70, 450}
};

void AutoDraw() {   // "User" graphics
    filledCircleColor(gRenderer, 100, 100, 40, 0xff00ff00);
    SDL_SetRenderDrawColor(gRenderer, 0xff, 0xaa, 0x00, 0xFF );
    for (int i = 1; i < 6; i++) {
        SDL_RenderDrawLine( gRenderer, 
            points[i - 1][0], points[i - 1][1],
            points[i][0], points[i][1]);
        DrawNow();
        sleep(1);
    }
}

void DrawNow() {
    while (SDL_PollEvent(&event)) {
        printf("Event: %d\n", event.type);
    }

    fprintf(stderr, "step  ");
    SDL_SetRenderTarget(gRenderer, NULL);
    SDL_RenderCopy(gRenderer, target, NULL, NULL);
    SDL_RenderPresent(gRenderer);
    SDL_SetRenderTarget(gRenderer, target);
}

int main() {
    //Initialize SDL
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ) {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
        return 1;
    }
    gWindow = SDL_CreateWindow("Drawbox", 10, 10, 500, 500, 0);
    if( gWindow == NULL )    {
        printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
        return -1;
    }
    //Create renderer for window
    gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
    if( gRenderer == NULL )
    {
        printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
        return -1;
    }
    /* Create texture for display buffering */
    target = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 500, 500);
    SDL_SetRenderTarget(gRenderer, target);

    //Clear buffer
    SDL_SetRenderDrawColor( gRenderer, 0, 0, 0, 0xff );
    SDL_RenderClear( gRenderer );

    // handle window creation events: A MUST!
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        printf("Event: %d\n", event.type);
    }

    circleColor(gRenderer, 100, 100, 50, 0x00ff00ff);
    DrawNow();

    AutoDraw();    
    sleep(15);
    return 0;
}

Upvotes: 1

Views: 102

Answers (1)

genpfault
genpfault

Reputation: 52157

where exactly is the "switch" in SDL2 that determines whether it will operate in single-buffer or double-buffer mode?

There is no switch, always act as if it's double-buffered (emphasis mine):

SDL_RenderPresent():

SDL's rendering functions operate on a backbuffer; that is, calling a rendering function such as SDL_RenderDrawLine() does not directly put a line on the screen, but rather updates the backbuffer. As such, you compose your entire scene and present the composed backbuffer to the screen as a complete picture.

Therefore, when using SDL's rendering API, one does all drawing intended for the frame, and then calls this function once per frame to present the final drawing to the user.

The backbuffer should be considered invalidated after each present; do not assume that previous contents will exist between frames. You are strongly encouraged to call SDL_RenderClear() to initialize the backbuffer before starting each new frame's drawing, even if you plan to overwrite every pixel.

So for DrawNow() you'd need a SDL_RenderClear() before the SDL_RenderCopy() "blit" to clear out whatever garbage may or may not be in the backbuffer:

void DrawNow() {
    while (SDL_PollEvent(&event)) {
        printf("Event: %d\n", event.type);
    }

    fprintf(stderr, "step  ");
    SDL_SetRenderTarget(gRenderer, NULL);

    // clear backbuffer
    SDL_SetRenderDrawColor( gRenderer, 0, 0, 0, 0xff );
    SDL_RenderClear( gRenderer )

    SDL_RenderCopy(gRenderer, target, NULL, NULL);
    SDL_RenderPresent(gRenderer);
    SDL_SetRenderTarget(gRenderer, target);
}

Upvotes: 0

Related Questions