SDL2 - 快速改变颜色时出现奇怪的性能问题
SDL2 - Strange performance hit when rapidly changing colors
当我发现这一点时我很惊讶,最初我认为我的算法有问题,但在仔细检查后我发现你改变颜色越多,它对性能的影响就越大。这是为什么?
这是(全部)代码:
#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;
}
问题是这段代码:
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_SetRenderDrawColor(renderer, 255, 255, 255, 255);
for(int i = 0; i < 10000; ++i)
{
SDL_RenderDrawPoint(renderer, rand() % WIDTH, rand() % HEIGHT);
}
如您所见,大量更改颜色会对性能产生很大影响。事实上,它会慢 100 多倍。
我究竟做错了什么?或者这是它应该如何工作?
显然 SDL_SetRenderDrawColor
函数需要一些时间来执行和更改颜色。也许颜色数据甚至必须发送到 GPU,这与常规内存访问相比非常慢。 rand 函数也取得了一些性能。
使用您的数据,1 SDL_SetRenderDrawColor
和 10000 之间大约有 550ms
的差异。因此每次调用此函数的成本约为 55µs
。这是非常小的,调用它几十次并不会真正影响性能,但 10000 次调用显然要多得多。
如果您同意一次调用向 GPU 传输 4 个字节,那么您已经传输 40kB 只是为了颜色。
我问了一个了解 SDL 的人 (Gorbit99),他告诉我问题出在使用纹理和 SDL_SetRenderDrawColor
,它在 GPU 上工作,但每个像素的交互速度更快CPU,所以不用 SDL_Texture
,而是使用 SDL_Surface
。现在这是我的最终代码(性能 ~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);
当我发现这一点时我很惊讶,最初我认为我的算法有问题,但在仔细检查后我发现你改变颜色越多,它对性能的影响就越大。这是为什么?
这是(全部)代码:
#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;
}
问题是这段代码:
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_SetRenderDrawColor(renderer, 255, 255, 255, 255);
for(int i = 0; i < 10000; ++i)
{
SDL_RenderDrawPoint(renderer, rand() % WIDTH, rand() % HEIGHT);
}
如您所见,大量更改颜色会对性能产生很大影响。事实上,它会慢 100 多倍。 我究竟做错了什么?或者这是它应该如何工作?
显然 SDL_SetRenderDrawColor
函数需要一些时间来执行和更改颜色。也许颜色数据甚至必须发送到 GPU,这与常规内存访问相比非常慢。 rand 函数也取得了一些性能。
使用您的数据,1 SDL_SetRenderDrawColor
和 10000 之间大约有 550ms
的差异。因此每次调用此函数的成本约为 55µs
。这是非常小的,调用它几十次并不会真正影响性能,但 10000 次调用显然要多得多。
如果您同意一次调用向 GPU 传输 4 个字节,那么您已经传输 40kB 只是为了颜色。
我问了一个了解 SDL 的人 (Gorbit99),他告诉我问题出在使用纹理和 SDL_SetRenderDrawColor
,它在 GPU 上工作,但每个像素的交互速度更快CPU,所以不用 SDL_Texture
,而是使用 SDL_Surface
。现在这是我的最终代码(性能 ~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);