SDL_RenderClear() performance issue

I noticed an unexpected change in performance while tweaking a program, and put together a test case help clarify the reason for the change.

Basically, on my system, when the render target is set to a texture, using SDL_RenderClear() takes a horrific amount of time (over 100 ms). But on the same target, doing a SDL_RenderFillRect() of the entire texture is quite reasonable (about 1.5 ms). This only happens with a texture as a target, and if I’ve specified SDL_RENDERER_ACCELERATED when creating the renderer. With SDL_RENDERER_SOFTWARE, the times for everything are much longer (no surprise), but they are consistent between SDL_RenderClear() and SDL_RenderFillRect().

I don’t see how this could be an SDL bug - I don’t see anything in the code that could cause SDL_RenderClear() to behave differently for a texture target rather than a screen target. So I’m assuming the bug is at a lower level (Mesa, or the video driver).

For the record - my “play around” system is an older Celeron, using an older Radeon card. System runs Xubuntu Precise, Mesa 1.2, and xserver-xorg-video-radeon 1.6.14.

Could a few others please run this code, and verify that it’s a “my machine” type issue (I suspect the Radeon driver as a culprit, but I’d like some confirmation). Here’s my teset results:

Code:
lloyd at Dell:~/src/test$ ./testcase
Hardware Render to screen - SDL_FillRect, average 1540.02 microseconds
Hardware Render to screen - SDL_RenderClear, average 1618.40 microseconds
Hardware Render to texture - SDL_FillRect, average 1594.33 microseconds
Hardware Render to texture - SDL_RenderClear, average 100435.69 microseconds
Software Render to screen - SDL_FillRect, average 11118.92 microseconds
Software Render to screen - SDL_RenderClear, average 11218.12 microseconds
Software Render to texture - SDL_FillRect, average 11255.74 microseconds
Software Render to texture - SDL_RenderClear, average 11253.91 microseconds
lloyd at Dell:~/src/test$

And the test case code

Code:

#include <stdio.h>
#include <sys/time.h>
#include <SDL2/SDL.h>

void test_loop(struct SDL_Renderer * rend, const char * ident)
{
int x, start, start2, stop, elapsed;
float average1, average2;
struct SDL_Rect rect;
struct timeval tv;
struct timezone tz;

rect.x = 0;
    rect.y = 0;
    rect.h = 480;
    rect.w = 640;
    for(x = 0; x < 255; x++) {
            gettimeofday(&tv, &tz);
            start = tv.tv_usec;
            start2 = tv.tv_sec;
            SDL_SetRenderDrawColor(rend, x, x, x, 255);
            SDL_RenderFillRect(rend, &rect);
            SDL_RenderPresent(rend);

            gettimeofday(&tv, &tz);
            stop = tv.tv_usec;
            if (start2 < tv.tv_sec)
                    stop += ((tv.tv_sec - start2) * 1000000);
            elapsed = stop - start;

            if (x == 0) {
                    average1 = elapsed;
            } else {
                    average1 = ((average1 * x) + elapsed) / (x + 1);
            }
    }
printf("%s - SDL_FillRect, average %9.2f microseconds\n", ident, average1);

for(x = 0; x < 255; x++) {
            gettimeofday(&tv, &tz);
            start = tv.tv_usec;
            start2 = tv.tv_sec;
            SDL_SetRenderDrawColor(rend, (255 - x), (255 - x), (255 - x), 255);
            SDL_RenderClear(rend);
            SDL_RenderPresent(rend);

            gettimeofday(&tv, &tz);
            stop = tv.tv_usec;
            if (start2 < tv.tv_sec)
                    stop += ((tv.tv_sec - start2) * 1000000);
            elapsed = stop - start;

            if (x == 0) {
                    average2 = elapsed;
            } else {
                    average2 = ((average2 * x) + elapsed) / (x + 1);
            }
    }
printf("%s - SDL_RenderClear, average %9.2f microseconds\n", ident, average2);

}

int main(int argc, char** argv) {

struct SDL_Window * wind[2] = {NULL, NULL};
struct SDL_Renderer * rend[2] = {NULL, NULL};
struct SDL_Texture * text[2] = {NULL, NULL};
struct SDL_RendererInfo info;
int x;

SDL_Init(SDL_INIT_EVERYTHING);

for (x = 0; x < 2; x++) {
	wind[x] = SDL_CreateWindow("Testing", (x * 100) + 100, 100, 
					640, 480, SDL_WINDOW_SHOWN);
	if ( wind[x] == NULL ) {
		printf("Couldn't create window %d - %s\n", x, SDL_GetError());
		return -1;
	}

	if ( x == 0 ) {
		rend[0] = SDL_CreateRenderer(wind[0], -1, SDL_RENDERER_ACCELERATED |
                                           			SDL_RENDERER_TARGETTEXTURE);
		if ( rend[0] == NULL) {
			printf("Error creating hardware renderer  - %s\n", SDL_GetError());
			return -1;
		}
	} else {
		rend[1] = SDL_CreateRenderer(wind[1], -1, SDL_RENDERER_SOFTWARE |
							SDL_RENDERER_TARGETTEXTURE); 
		if ( rend[1] == NULL ) {
			printf("Error creating software renderer - %s\n", SDL_GetError());
			return -1;
		}
	}

	SDL_RenderClear(rend[x]);
	SDL_RenderPresent(rend[x]);

	text[x] = SDL_CreateTexture(rend[x], 0, SDL_TEXTUREACCESS_TARGET, 640, 480);

	if ( text[x] == NULL ) {
		printf("Error creating texutre %d - %s\n", x, SDL_GetError());
		return -1;
	}
}

test_loop(rend[0], "Hardware Render to screen");

SDL_SetRenderTarget(rend[0], text[0]);
test_loop(rend[0], "Hardware Render to texture");

test_loop(rend[1], "Software Render to screen");

SDL_SetRenderTarget(rend[1], text[1]);
test_loop(rend[1], "Software Render to texture");

SDL_DestroyTexture(text[0]);
SDL_DestroyTexture(text[1]);
SDL_DestroyRenderer(rend[0]);
SDL_DestroyRenderer(rend[1]);
SDL_Quit();
return 0;

}

Mac OS X 10.8 on a 2008 Mac Pro:
Hardware Render to screen - SDL_FillRect, average 1402.26 microseconds
Hardware Render to screen - SDL_RenderClear, average 611.11 microseconds
Hardware Render to texture - SDL_FillRect, average 84.64 microseconds
Hardware Render to texture - SDL_RenderClear, average 16.82 microseconds
Software Render to screen - SDL_FillRect, average 1065.36 microseconds
Software Render to screen - SDL_RenderClear, average 1003.36 microseconds
Software Render to texture - SDL_FillRect, average 889.33 microseconds
Software Render to texture - SDL_RenderClear, average 858.78 microsecondsOn Wed, Oct 2, 2013 at 8:01 AM, lloyd_b wrote:

**
I noticed an unexpected change in performance while tweaking a program,
and put together a test case help clarify the reason for the change.

Basically, on my system, when the render target is set to a texture, using
SDL_RenderClear() takes a horrific amount of time (over 100 ms). But on the
same target, doing a SDL_RenderFillRect() of the entire texture is quite
reasonable (about 1.5 ms). This only happens with a texture as a target,
and if I’ve specified SDL_RENDERER_ACCELERATED when creating the renderer.
With SDL_RENDERER_SOFTWARE, the times for everything are much longer (no
surprise), but they are consistent between SDL_RenderClear() and
SDL_RenderFillRect().

I don’t see how this could be an SDL bug - I don’t see anything in the
code that could cause SDL_RenderClear() to behave differently for a texture
target rather than a screen target. So I’m assuming the bug is at a lower
level (Mesa, or the video driver).

For the record - my “play around” system is an older Celeron, using an
older Radeon card. System runs Xubuntu Precise, Mesa 1.2, and
xserver-xorg-video-radeon 1.6.14.

Could a few others please run this code, and verify that it’s a “my
machine” type issue (I suspect the Radeon driver as a culprit, but I’d like
some confirmation). Here’s my teset results:

Code:

lloyd at Dell:~/src/test$ ./testcase
Hardware Render to screen - SDL_FillRect, average 1540.02 microseconds
Hardware Render to screen - SDL_RenderClear, average 1618.40 microseconds
Hardware Render to texture - SDL_FillRect, average 1594.33 microseconds
Hardware Render to texture - SDL_RenderClear, average 100435.69
microseconds
Software Render to screen - SDL_FillRect, average 11118.92 microseconds
Software Render to screen - SDL_RenderClear, average 11218.12 microseconds
Software Render to texture - SDL_FillRect, average 11255.74 microseconds
Software Render to texture - SDL_RenderClear, average 11253.91
microseconds
lloyd at Dell:~/src/test$

And the test case code

Code:

#include **
#include **
#include **

void test_loop(struct SDL_Renderer * rend, const char * ident)
{
int x, start, start2, stop, elapsed;
float average1, average2;
struct SDL_Rect rect;
struct timeval tv;
struct timezone tz;

rect.x = 0;
    rect.y = 0;
    rect.h = 480;
    rect.w = 640;
    for(x = 0; x < 255; x++) {
            gettimeofday(&tv, &tz);
            start = tv.tv_usec;
            start2 = tv.tv_sec;
            SDL_SetRenderDrawColor(rend, x, x, x, 255);
            SDL_RenderFillRect(rend, &rect);
            SDL_RenderPresent(rend);

            gettimeofday(&tv, &tz);
            stop = tv.tv_usec;
            if (start2 < tv.tv_sec)
                    stop += ((tv.tv_sec - start2) * 1000000);
            elapsed = stop - start;

            if (x == 0) {
                    average1 = elapsed;
            } else {
                    average1 = ((average1 * x) + elapsed) / (x + 1);
            }
    }

printf("%s - SDL_FillRect, average %9.2f microseconds\n", ident,
average1);

for(x = 0; x < 255; x++) {
gettimeofday(&tv, &tz);
start = tv.tv_usec;
start2 = tv.tv_sec;
SDL_SetRenderDrawColor(rend, (255 - x), (255 - x), (255 -
x), 255);
SDL_RenderClear(rend);
SDL_RenderPresent(rend);

            gettimeofday(&tv, &tz);
            stop = tv.tv_usec;
            if (start2 < tv.tv_sec)
                    stop += ((tv.tv_sec - start2) * 1000000);
            elapsed = stop - start;

            if (x == 0) {
                    average2 = elapsed;
            } else {
                    average2 = ((average2 * x) + elapsed) / (x + 1);
            }
    }

printf("%s - SDL_RenderClear, average %9.2f microseconds\n", ident,
average2);
}

int main(int argc, char** argv) {

struct SDL_Window * wind[2] = {NULL, NULL};
struct SDL_Renderer * rend[2] = {NULL, NULL};
struct SDL_Texture * text[2] = {NULL, NULL};
struct SDL_RendererInfo info;
int x;

SDL_Init(SDL_INIT_EVERYTHING);

for (x = 0; x < 2; x++) {
wind[x] = SDL_CreateWindow(“Testing”, (x * 100) + 100, 100,
640, 480, SDL_WINDOW_SHOWN);
if ( wind[x] == NULL ) {
printf(“Couldn’t create window %d - %s\n”, x, SDL_GetError());
return -1;
}

  if ( x == 0 ) {
     rend[0] = SDL_CreateRenderer(wind[0], -1,

SDL_RENDERER_ACCELERATED |

SDL_RENDERER_TARGETTEXTURE);
if ( rend[0] == NULL) {
printf(“Error creating hardware renderer - %s\n”,
SDL_GetError());
return -1;
}
} else {
rend[1] = SDL_CreateRenderer(wind[1], -1, SDL_RENDERER_SOFTWARE |
SDL_RENDERER_TARGETTEXTURE);
if ( rend[1] == NULL ) {
printf(“Error creating software renderer - %s\n”,
SDL_GetError());
return -1;
}
}

  SDL_RenderClear(rend[x]);
  SDL_RenderPresent(rend[x]);

  text[x] = SDL_CreateTexture(rend[x], 0, SDL_TEXTUREACCESS_TARGET,

640, 480);

  if ( text[x] == NULL ) {
     printf("Error creating texutre %d - %s\n", x, SDL_GetError());
     return -1;
  }

}

test_loop(rend[0], “Hardware Render to screen”);

SDL_SetRenderTarget(rend[0], text[0]);
test_loop(rend[0], “Hardware Render to texture”);

test_loop(rend[1], “Software Render to screen”);

SDL_SetRenderTarget(rend[1], text[1]);
test_loop(rend[1], “Software Render to texture”);

SDL_DestroyTexture(text[0]);
SDL_DestroyTexture(text[1]);
SDL_DestroyRenderer(rend[0]);
SDL_DestroyRenderer(rend[1]);
SDL_Quit();
return 0;
}


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org