View Full Version : Drawing pixels
knutsj
05-15-2008, 03:52 AM
Hi. I'm using ClanLib 0.8 on a linux machine, and I'm wondering;
What is the fastest way to draw lots of pixels to a surface?
Do I:
- draw_pixel on a pixelbuffer, then use surface.set_pixelbuffer
- get a canvas for the surface, get the graphic context by canvas.get_gc(), call graphicContext->draw_pixel (This is what I'm currently doing, but it seems slow.)
- get a canvas for the surface, draw on a pixelbuffer, use canvas.set_pixelbuffer
- something else?
knutsj
05-18-2008, 10:07 PM
Looks like I'll be having a monologue here...
I tested my three suggestions, and they all ran at the same speed.
(~4.7 fps, drawing 512x512 pixels)
Commenting out just the line with draw_pixel gives 23 fps.
Commenting out most of the non-graphics code gives 5.5 fps.
For fun I tried drawing 512x512 one-pixel sprites, resulting in 3.6 fps.
Is there a way to draw several pixels at once? (I imagine that would be faster, as there is probably some overhead costs for each draw_pixel)
If there isn't, would you be interested if I made it?
Is there any point in writing the pixel array to memory manually and constructing a pixelbuffer using a pointer to the data? I'd rather not do that, though...
rombust
05-19-2008, 01:47 PM
I depends how many pixels you are going to draw. For my game, i write 320*256 pixels to a pixel buffer, which is nice and fast
See http://methane.sourceforge.net
CL_PixelBuffer pixel_screen(SCR_WIDTH, SCR_HEIGHT, SCR_WIDTH*4, CL_PixelFormat::rgba8888);
CL_Surface game_screen(pixel_screen);
CL_Canvas game_canvas(game_screen);
pixel_screen.lock();
Game.MainLoop(pixel_screen.get_data()
pixel_screen.unlock();
game_screen.set_pixeldata(pixel_screen);
CL_Rect rect(0,0,(int)CL_Display::get_width(), (int)CL_Display::get_height());
game_screen.draw(rect);
knutsj
05-20-2008, 06:59 PM
I depends how many pixels you are going to draw. For my game, i write 320*256 pixels to a pixel buffer, which is nice and fast
See http://methane.sourceforge.net
I took a look at the source code of your game, and if I read it correctly this is what you do (roughly) :
- lock the pixelbuffer and get a pointer to the data from pixel_screen.get_data()
- draw stuff directly to memory using your own routines.
- unlock the buffer, update stuff and show it on screen.
It also looks like you are using 8 bit pixels, which means there is only 1/4 as much data to move around. (I'm using 32bit pixels)
Whereas I'm using ClanLibs own routines, calling draw_pixel() once per pixel I draw.
Your way is obviously more effective, but I'd rather not have to manipulate memory directly myself.
When I get some time, I think I will try to add some functionality to CL_PixelBuffer to let me draw many pixels at once. If it works out I'll post the results here.
If I don't find a good way to do it, I guess I'll do what you do.
rombust
05-21-2008, 05:40 AM
I am using 32 bit pixels, see CL_PixelFormat::rgba8888 (8 + 8 +8 + 8 = 32). Internally the game uses only 32 colours iirc.
Have a look at ClanLib 0.9 - Maybe it will be better to use opengl, and write your own pixel shader (using opengl shader language - via CL_ProgramObject)?
knutsj
06-17-2008, 11:43 PM
In case anyone's interested, I made this;
void CL_PixelBuffer::draw_pixels(int *x, int *y, const CL_Color *color, int pixelcount)
{
lock();
cl_uint8* buf = static_cast<cl_uint8*>(impl->get_data());
CL_PixelFormat format = get_format();
if (format.get_type() == pixelformat_index)
{
throw CL_Error("Direct settings of CL_Colors pixels in paletted mode is not supported.");
}
else if (format.get_type() == pixelformat_rgba)
{
int i;
int depth = format.get_depth ();
int bytes_per_pixel = (depth + 7)/8;
cl_uint8 *pos;
int red_shift = format.get_mask_shift(format.get_red_mask()),
green_shift = format.get_mask_shift(format.get_green_mask()),
blue_shift = format.get_mask_shift(format.get_blue_mask()),
alpha_shift = format.get_mask_shift(format.get_alpha_mask());
switch (bytes_per_pixel)
{
case 4:
case 3:
{
for(i=0;i<pixelcount;i++)
{
pos = &buf[y[i] * impl->pitch + x[i] * bytes_per_pixel]
cl_uint32 c = (color[i].get_red() << red_shift) | (color[i].get_green() << green_shift) | (color[i].get_blue() << blue_shift) | (color[i].get_alpha() << alpha_shift);
memcpy (pos, &c, bytes_per_pixel);
}
}
break;
case 2:
{
for(i=0;i<pixelcount;i++)
{
pos = &buf[y[i] * impl->pitch + x[i] * bytes_per_pixel]
cl_uint16 c = (color[i].get_red() << red_shift) | (color[i].get_green() << green_shift) | (color[i].get_blue() << blue_shift) | (color[i].get_alpha() << alpha_shift);
memcpy (pos, &c, bytes_per_pixel);
}
}
break;
case 1:
{
for(i=0;i<pixelcount;i++)
{
pos = &buf[y[i] * impl->pitch + x[i] * bytes_per_pixel]
cl_uint8 c = (color[i].get_red() << red_shift) | (color[i].get_green() << green_shift) | (color[i].get_blue() << blue_shift) | (color[i].get_alpha() << alpha_shift);
*pos = c;
}
}
break;
default:
throw CL_Error("Unsuported pixel format depth for draw_pixels.");
}
}
unlock();
}
which draws an array of pixels of arbitrary positions and colors. ( Note: There was some amount of CRTL-C from CL_PixelBuffer::draw_pixel :) )
It does expect pixelcount to be set to the correct value and will behave strangely if it is too large.
Using this roughly doubles my fps.
Getting a pointer to the memory used by the pixelbuffer and writing stuff directly to memory is slightly(~20-30%) faster than this, but needs to be redone for each particular use.
I haven't looked into using a pixel shader. If someone does, and compares it to draw_pixel, please make a post about it. :)
Powered by vBulletin® Version 4.1.10 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.