Undesired double-buffering behavior with garbage pixels

Years ago I built a cross-platform C++ graphics library for use by beginning programming students. Since Apple pulled the rug out from under the Carbon interface, I decided to implement on top of SDL2.

The library assumes a persistent-buffer graphics model, rather than a persistent-object model in which the buffer is redrawn at every frame. Pixels in the buffer/screen stay the way they are until the caller explicitly erases them. Obviously this won’t work with a double-buffering model, so I started with this example for a single-buffering model.

Applying that sequence of operations in my library got very different results: alternate draws going to alternating screens and lines that were obviously drawn onto uninitialized memory and then blitted piecemeal to the main texture - although the main texture IS being cleared. So I boiled it down to a simple example program. Unfortunately, in isolation, that simple program works! So I can’t ask you, “Please help me find my bug.” Instead I ask you, “Can you suggest what rock to look under?” Among other things, SDL_RenderDrawLine is clearly drawing into uninitialized memory, at what level would that memory be located? Main memory? Graphics memory?

The working version draws a simple polygonal orange line.

But when run in the context of the larger library and program, using the selfsame SDL initialization code and the selfsame AutoDraw function, it draws alternating lines to different buffers and produces an ugly mess like this:


Has anyone seen this particular graphical bug before? Again, any suggestions on where to look to hunt this down would be most helpful.

Using sdl2 version 2.30.4 running on MacOS 10.14 on an Apple Silicon Mac. Here’s the code:

#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#include "Drawbox.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;

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

void AutoDraw() {	// "User" graphics
	Point p(points[0][0], points[0][1]);
	Point q(0, 0);
	filledCircleColor(gRenderer, 100, 100, 40, 0x00ff00ff);
    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);
	}
	sleep(60);
}

void DrawNow() {
	fprintf(stderr, "Single");
	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();    
    return 0;
}