Reputation: 31
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.
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)?
main.h:
#include <SDL2/SDL.h>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <cstring>
#include <cmath>
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();
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++)
{
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_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}
Upvotes: 3
Views: 677
Reputation: 47954
The best way to figure out a performance problem is to use a profiler so that you know where the time is actually being spent. You can make part of your code really fast, but if that wasn't the bottleneck, it's not going to help.
With graphics, the numbers scale up quickly. 640×480×50 is over 15 million calculations for a single frame. That might be the problem. Ways to deal with this are to find a better algorithm that can do the same work with fewer calculations. From your description, it sounds like you're creating a Voronoi diagram, so you could search to see if there's a faster way to implement that. You might consider using a spatial partitioning structure, which could help you find the nearest point without checking the distance to every one every time.
Generally speaking, sqrt
and pow
can be expensive functions, so that might be the problem. As others have noted in the comments, you don't actually need the sqrt
just to find the closest. It's also unclear whether your compiler's optimizer is smart enough to turn your pow
calls into a (faster) multiply. You do have the optimizer enabled, right?
Or, it might be that setting one pixel at a time isn't a very efficient way to do things. With many graphics APIs, it's the slowest possible thing you can do. But I don't know about SDL in particular, so that may or may not be a problem. In many cases, it's faster to build your bitmap in plain memory and then issue just one graphics API call to transfer the finished bitmap at once. It's even faster to use a shader program to do all the calculation on the GPU so that the GPU can parallelize it and so the image doesn't have to be transported from system memory to graphics memory.
If you don't have easy access to a profiler, you can create simple experiments to help determine where the bottleneck is. For example, write a program that sets every pixel to the same color (without doing the other work). That'll tell you how fast you can set individual pixels even if the calculation could be instantaneous. You could try experimenting with increasing pointsN
and plotting the time per frame. Is it linear, quadratic, or worse?
Upvotes: 3