I want to draw pixels on the monitor which change frequently after certain parameters. E.G. if a Red and Green Pixel collide, they would both vanish, etc.
In every frame I have to manipulate about 100 - 1000 pixels. I have a multi-threaded approach here, which wont give me 30FPS (what I want). Currently I store a Pixel array in the RAM which contains all Pixels and have a SDL_Surface
. When a pixel in the array changes, it gets changed in the Surface too and is then after all manipulation is done gets blitted to the screen. My current approach is too slow and I did a bit of thinking on how I could increase the Speed.
My current thoughts are:
SDL_Surface
or SDL_Texture
Are there any other approaches on how I could manipulate Pixels in a fast manner?
SDL_CreateTexture()
w/SDL_TEXTUREACCESS_STREAMING
+ SDL_UpdateTexture()
seems to work well enough with the right pixel format.
On my system using the default renderer:
Renderer name: direct3d
Texture formats:
SDL_PIXELFORMAT_ARGB8888
SDL_PIXELFORMAT_YV12
SDL_PIXELFORMAT_IYUV
(though the opengl
info is the same:)
Renderer name: opengl
Texture formats:
SDL_PIXELFORMAT_ARGB8888
SDL_PIXELFORMAT_YV12
SDL_PIXELFORMAT_IYUV
SDL_PIXELFORMAT_ARGB8888
gives me ~1ms/frame:
// g++ main.cpp `pkg-config --cflags --libs sdl2`
#include <SDL.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <cstring>
int main( int argc, char** argv )
{
SDL_Init( SDL_INIT_EVERYTHING );
SDL_Window* window = SDL_CreateWindow
(
"SDL2",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
600, 600,
SDL_WINDOW_SHOWN
);
SDL_Renderer* renderer = SDL_CreateRenderer
(
window,
-1,
SDL_RENDERER_ACCELERATED
);
SDL_RendererInfo info;
SDL_GetRendererInfo( renderer, &info );
std::cout << "Renderer name: " << info.name << std::endl;
std::cout << "Texture formats: " << std::endl;
for( Uint32 i = 0; i < info.num_texture_formats; i++ )
{
std::cout << SDL_GetPixelFormatName( info.texture_formats[i] ) << std::endl;
}
const unsigned int texWidth = 1024;
const unsigned int texHeight = 1024;
SDL_Texture* texture = SDL_CreateTexture
(
renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
texWidth, texHeight
);
std::vector< unsigned char > pixels( texWidth * texHeight * 4, 0 );
SDL_Event event;
bool running = true;
bool useLocktexture = false;
unsigned int frames = 0;
Uint64 start = SDL_GetPerformanceCounter();
while( running )
{
SDL_SetRenderDrawColor( renderer, 0, 0, 0, SDL_ALPHA_OPAQUE );
SDL_RenderClear( renderer );
while( SDL_PollEvent( &event ) )
{
if( ( SDL_QUIT == event.type ) ||
( SDL_KEYDOWN == event.type && SDL_SCANCODE_ESCAPE == event.key.keysym.scancode ) )
{
running = false;
break;
}
if( SDL_KEYDOWN == event.type && SDL_SCANCODE_L == event.key.keysym.scancode )
{
useLocktexture = !useLocktexture;
std::cout << "Using " << ( useLocktexture ? "SDL_LockTexture() + memcpy()" : "SDL_UpdateTexture()" ) << std::endl;
}
}
// splat down some random pixels
for( unsigned int i = 0; i < 1000; i++ )
{
const unsigned int x = rand() % texWidth;
const unsigned int y = rand() % texHeight;
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
}
if( useLocktexture )
{
unsigned char* lockedPixels = nullptr;
int pitch = 0;
SDL_LockTexture
(
texture,
NULL,
reinterpret_cast< void** >( &lockedPixels ),
&pitch
);
std::memcpy( lockedPixels, pixels.data(), pixels.size() );
SDL_UnlockTexture( texture );
}
else
{
SDL_UpdateTexture
(
texture,
NULL,
pixels.data(),
texWidth * 4
);
}
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 );
if( seconds > 2.0 )
{
std::cout
<< frames << " frames in "
<< std::setprecision(1) << std::fixed << seconds << " seconds = "
<< std::setprecision(1) << std::fixed << frames / seconds << " FPS ("
<< std::setprecision(3) << std::fixed << ( seconds * 1000.0 ) / frames << " ms/frame)"
<< std::endl;
start = end;
frames = 0;
}
}
SDL_DestroyRenderer( renderer );
SDL_DestroyWindow( window );
SDL_Quit();
return 0;
}
Make sure you don't have have vsync enabled (forced in the driver, running a compositor, etc.) or else all your frame times will be ~16ms (or whatever your display refresh is set to).