3

I was surprised when I found this out, initially I thought that something's wrong with my algorithms, but after closer inspections I found out that the more you change the colors the more it has an impact on the performance. Why is that?

Here's the (all) code:

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

const int WIDTH = 1024;
const int HEIGHT = 768;

int main(int argc, char *argv[])
{
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *texture;
    SDL_Event event;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
        return 3;
    }

    window = SDL_CreateWindow("SDL_CreateTexture",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              1024, 768,
                              SDL_WINDOW_RESIZABLE);

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);

    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, WIDTH, HEIGHT);

    bool alive = true;
    while (alive)
    {
        while(SDL_PollEvent(&event) > 0)
        {
            switch(event.type)
            {
                case SDL_QUIT:
                    alive = false;
                break;
            }
        }

        const Uint64 start = SDL_GetPerformanceCounter();

        SDL_SetRenderTarget(renderer, texture);
        SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
        SDL_RenderClear(renderer);

        for(int i = 0; i < 10000; ++i)
        {
            SDL_SetRenderDrawColor(renderer, rand() % 255, rand() % 255, rand() % 255, 255);
            SDL_RenderDrawPoint(renderer, rand() % WIDTH, rand() % HEIGHT);
        }



        SDL_SetRenderTarget(renderer, NULL);
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);


        const Uint64 end = SDL_GetPerformanceCounter();
        const static Uint64 freq = SDL_GetPerformanceFrequency();
        const double seconds = ( end - start ) / static_cast< double >( freq );
        std::cout << "Frame time: " << seconds * 1000.0 << "ms" << std::endl;
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

The problem is this block of code:

for(int i = 0; i < 10000; ++i)
{
    SDL_SetRenderDrawColor(renderer, rand() % 255, rand() % 255, rand() % 255, 255);
    SDL_RenderDrawPoint(renderer, rand() % WIDTH, rand() % HEIGHT);
}

Here's the performance with this code:

enter image description here

And here's the performance with this code:

SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
for(int i = 0; i < 10000; ++i)
{
    SDL_RenderDrawPoint(renderer, rand() % WIDTH, rand() % HEIGHT);
}

enter image description here

As you can see, there's a quite big performance impact when you change the colors a lot. In fact it gets over 100 times slower. What am I doing wrong? Or is this how it's supposed work?

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
Marat Isaw
  • 131
  • 3
  • 10
  • C and C++ are different languages. Seeing as you use `std::cout`, it looks like you are using C++ and not C. – François Andrieux Nov 05 '19 at 21:08
  • 3
    Maybe calling `rand()` so many more times to generate colors has an impact. – François Andrieux Nov 05 '19 at 21:09
  • Consider using [C++11 random number generation features](https://stackoverflow.com/questions/19665818/generate-random-numbers-using-c11-random-library) instead of `rand()`. While `rand()` is fine for simple test cases, in practice it has many problems (some of which are listed [here](https://stackoverflow.com/questions/52869166/why-is-the-use-of-rand-considered-bad)). – François Andrieux Nov 05 '19 at 21:12
  • @FrançoisAndrieux Sadly no, I replaced `rand() % 255` with `i % 255` and the performance is still very low. – Marat Isaw Nov 05 '19 at 21:12
  • Then it's hard to say. You'd have to see what exactly `SDL_SetRenderDrawColor`. Maybe inspecting the source code will provide leads. – François Andrieux Nov 05 '19 at 21:14
  • 2
    `SDL_SetRenderDrawColor()` [runs a sanity check (looks fairly cheap) and then just sets fields of a struct](https://github.com/spurious/SDL-mirror/blob/e4d2f905ebf2e3fa9984980b62210977999c8510/src/render/SDL_render.c#L2139-L2150). – Sean Bright Nov 05 '19 at 21:19
  • OS? SDL version? Repro-able using `SDL_RENDERER_SOFTWARE`? What about using `rand() & 0xFF` instead of `rand() % 255`? – genpfault Nov 05 '19 at 22:10
  • The problem is that `SDL_Texture` works on the GPU and I am iterating over pixel. Working pixel by pixel is better on the CPU so you have to use `SDL_Surface`. (See my answer). – Marat Isaw Nov 07 '19 at 18:45

2 Answers2

0

Obviously the SDL_SetRenderDrawColor function takes some time to execute and change the colors. Maybe the color data even has to be send to the GPU, which is super slow compared to regular memory access. Also the rand function is taking some performance.

Using your data there is a difference of about 550ms between one SDL_SetRenderDrawColor and 10000. So every call of this function costs about 55µs. This is super tiny and calling it a few dozen times isn't really messing with the performance but 10000 calls are obviously way more.

If you agree that one call transmits 4 bytes to the GPU, you are transmitting 40kB already just for the colors.

user11914177
  • 885
  • 11
  • 33
-1

I asked a guy(Gorbit99) who knows stuff about SDL, and he told me that the problem was lying in using textures and SDL_SetRenderDrawColor which is working on the GPU, but per pixel interaction is faster on the CPU, so instead of using SDL_Texture you use SDL_Surface. And now this is my final code (performance ~2ms).

SDL_Surface surface = SDL_CreateRGBSurfaceWithFormat(0, WIDTH, HEIGHT, 32, SDL_PIXELFORMAT_ABGR32);
surface = SDL_CreateRGBSurfaceWithFormat(0, WIDTH, HEIGHT, 32, SDL_PIXELFORMAT_ABGR32);
uint32_t* colors = (uint32_t*)surface->pixels;

for( unsigned int i = 0; i < 1000; i++ )
{
    int x = rand() % WIDTH;
    int y = rand() % HEIGHT;

    int offset = ( WIDTH * y ) + x;
    colors[ offset ] = 0x00ff00ff; // 0xrrggbbaa
}

SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);

SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);

SDL_DestroyTexture(texture);
SDL_FreeSurface(surface);
Marat Isaw
  • 131
  • 3
  • 10
  • Sorry but reads like "GPU is super slow so we'll use CPU instead", which is not true. Breaking batches hits CPU a lot, GPU is starving with no commands to execute. With e.g. opengl and colour array you can go much faster, but even if you decide to stick with textures - please create dynamic texture once and lock it for update instead of recreating it each frame. – keltar Nov 06 '19 at 04:10
  • I am not saying that the CPU is faster than the GPU in general or something. I say that the CPU is faster when dealing with pixel precision access. But you're right about locking the texture. – Marat Isaw Nov 08 '19 at 18:25
  • Thanks for the down vote as this is working for me as I intended. – Marat Isaw Nov 13 '19 at 20:37