Bad performance with lots of distance calculations and drawing pixels

Hey!
I’m testing out SDL and made the following test project to see the performance. I want to make an array of points randomly on the screen, then for every pixel in every frame, find the nearest point and set the pixel’s color to that point’s color.
As is probably obvious, I’m new to C++ and used to higher-level languages.
It works, but I’m getting about 1fps. Any obvious mistakes I made? Is this expected speed for the amount of operations I’m doing (50 points, 680x480 pixels)?
Thanks in advance.

main.h:
#include <SDL2/SDL.h>
#include <stdio.h>
#include
#include
#include
#include
#include

const int pointsN = 50;

union Color
{
    struct
    {
        uint8_t r, g, b, a;
    } color;
    uint32_t color_int;
};

struct Point
{
    int x;
    int y;
    Color color;
};

void set_pixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    Uint32 *const target_pixel = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch + x * surface->format->BytesPerPixel);
    *target_pixel = pixel;
}

float dist(int x1, int y1, int x2, int y2)
{
    return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}

Point nearestPoint(struct Point (& points)[pointsN], int x, int y)
{
    float smallestSize = -1;
    float thisDist;
    int closestIndex;
    for (size_t i = 0; i < pointsN; i++)
    {
        thisDist = dist(x, y, points[i].x, points[i].y);
        if(thisDist < smallestSize || smallestSize == -1)
        {
            closestIndex = i;
            smallestSize = thisDist;
        }
            
        
    }
    return points[closestIndex];
}

main.cpp:
#include “main.h”

int main(int argv, char **args)
{
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
    {
        printf("Error");
        return 0;
    }

    const int w = 680;
    const int h = 480;

    SDL_Window *window = SDL_CreateWindow("SDL2 Window",
                                          SDL_WINDOWPOS_CENTERED,
                                          SDL_WINDOWPOS_CENTERED,
                                          w, h,
                                          0);
    if (!window)
    {
        std::cout << "Failed to create window\n";
        return -1;
    }

    /*SDL_Surface *window_surface = SDL_GetWindowSurface(window);

    if (!window_surface)
    {
        std::cout << "Failed to get the surface from the window\n";
        return -1;
    }
    */

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

    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, w, h);

    Uint32 pixels[w*h];

    Point points[pointsN];

    for (size_t i = 0; i < pointsN; i++)
    {
        Point newpoint;
        newpoint.x = rand() % w;
        newpoint.y = rand() % h;
        newpoint.color.color.r = rand() % 255;
        newpoint.color.color.g = rand() % 255;
        newpoint.color.color.b = rand() % 255;
        newpoint.color.color.a = 255;

        points[i] = newpoint;
    }

    SDL_Event e;
    bool keep_window_open = true;

    unsigned int frames = 0;
    Uint64 start = SDL_GetPerformanceCounter();

    //bool mouseDown = false;
    while (keep_window_open)
    {

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);

        while (SDL_PollEvent(&e) > 0)
        {
            switch (e.type)
            {
            case SDL_QUIT:
                keep_window_open = false;
                break;
            }
        }

        for (size_t y = 0; y < h; y++)
        {
            for (size_t x = 0; x < w; x++)
            {
                /*const unsigned int offset = (texWidth * 4 * y) + x * 4;
                pixels[offset + 0] = rand() % 256;     // b
                pixels[offset + 1] = rand() % 256;     // g
                pixels[offset + 2] = rand() % 256;     // r
                pixels[offset + 3] = SDL_ALPHA_OPAQUE; // a
                */
                pixels[(w * y) + x] = nearestPoint(points, x, y).color.color_int;
            }
        }

        for (size_t i = 0; i < pointsN; i++)
        {
            points[i].x += (rand() % 50 + 1) - 25;
            points[i].y += (rand() % 50 + 1) - 25;
        }

        SDL_UpdateTexture(
            texture,
            NULL,
            pixels,
            w * sizeof(Uint32));

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


        frames++;
        const Uint64 end = SDL_GetPerformanceCounter();
        const static Uint64 freq = SDL_GetPerformanceFrequency();
        const double seconds = (end - start) / static_cast<double>(freq);

        printf("FPS: %f\n", frames / seconds);

        /*SDL_UpdateTexture(texture, NULL, pixels, w * sizeof(Uint32));

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);
*/
        //SDL_UpdateWindowSurface(window);
    }

    //delete[] pixels;
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);

    SDL_Quit();
    return 0;
}

Brushing over your code I can see quite a serious bottle-neck. Each frame you are visiting every pixel of a 680x480 array in a for loop? Each step of the for loop has a lot of maths for each pixel (326400), even though you only have 50 points? pointsN = 50?

surface->pixels can be very fast, you should be able to manipulate every pixel and still get 60fps, but not with so much math calculations for every pixel. rand() and sqrt() and pow() are very slow. Optimizing this would be very involved. With surface->pixels use a pointer and increment it, so you’re not calculating the x/y offset every pixel - at least for every scan line. Calculate distance without complex maths and only do it for the 50 points, not for every pixel on screen? Just the first areas of optimization that stand out without digging too deep.

This good considering you’re new to C++ though - good job and good luck.

just a remark, since you compare distances, you don’t need to compute the square root.
OK, don’t expect to get a big speedup from this, maybe 2 ou 3 % :wink:

AH, also you are using “pow” which is very slow.
Instead, use:

float dist(int x1, int y1, int x2, int y2)
{
  return ((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}

you will get almost 10x speed-up!

Change the direction you are attacking the problem.
This is just a hint on how to do this. create an array the size of the screen use it to store a distance value.
Then start from your first generated point and create a growing circle until the circle includes another point. All the points inside the circle set the color and change the distance value to the distance it is from the circle point you just used. Then go to the next point do the same thing. The difference only if the distance from it to the point is less does it change the color and set the value.
In truth you may need to go till you hit a point on the other side of the screen or you may need to just go to the first point depending on if it is near the center. That will depend also on screen shape and size and how the points lay out.

There are probably better methods of doing this but it should reduce the work load quite a bit.