Reputation: 1428
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:
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.)
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
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'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