Faster alternative to map_put_pixel() ?

Started by ruckage, April 26, 2011, 06:08:21 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ruckage

Hello,

I've been messing around with coding some old skool graphical effects but I've hit a bit of a brick wall.
For many effects you need to manipulate the whole 'screen/map' each frame at the pixel level and 'put_pixel/map_put_pixel' seem to be the only obvious ways I can do this but unfortunately its quite slow.
Any ideas for alternative ways to manipulate the entire screen at the pixel level?  I've already created look up tables for any complex calculations and I can't think of any other ways to speed things up.

Any ideas greatly appreciated,

Ruckage

FreeYourMind


ruckage

Thanks for the reply FreeYourMind,

I did try map_block_copy just in case it was faster, I've used it in the past and it is fast when copying larger blocks but when copying a single pixel its slow. 
Map_xput would be no good as thats for drawing one graph into another, you could have single pixel graphs but it would suffer from the same speed issues as map_block_copy  I imagine.

I'm wondering if some of the memory manipulation commands can be utilised for this and whether they would actually be faster or not.  I've never used any of those commands though so it's completely new ground to me.

Sandman

Try using map_buffer(file,graph), this returns a pointer to the pixel data. The size of this pixel data depends on the depth, height, width and pitch of the map. IIRC it starts at the top left pixel, then first going right. Then moving to the second row, etc. The size of the pixel data in bits would be depth*height*pitch.

E.g., if you have an 8bit 4wx8h graph for example, to change the pixel in the second column, third row, you would do: map_buffer(file,graph)[1+2*4] = 0; Here 1 and 2 are the coordinates and 4 is the pitch. You can obtain the pitch of a graphic using graphic_info(file,graph,G_PITCH).

Using the pointer obtained by map_buffer(), you can use memset() and other variants like memsetw() and memseti(). Of course memcmp(), memcopy() and memmove() are useful as well.
-- Sandman

SplinterGU

Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Sandman

-- Sandman

ruckage

Thanks Sandman,

That was extremely informative.  My initial tests aren't working quite as I expected and the graphics end up garbled but it's probably just an oversight on my part and I'll keep tinkering until I get it working. 
Unfortunately though it doesn't look like it will be any faster but it's still very useful to know and great to learn something new.

Thanks again,

Ruckage

Sandman

Manipulating many pixels every frame tends to be slow in an interpreted language like Bennu. Two ways to fix this: make Bennu use JIT or create a module that does what you want, using a language like C. As the first one isn't really feasible, what's left is the second. It is not an easy solution, as you need to compile it for every platform you want your game to run on. This is possible even for a platform like GP2X, but for the Wii this is less feasible. So your best bet is probably to avoid it as much as possible.

Note that even an OpenGL backend would suffer from using these pixel operations, as 3D hardware was not designed for this. Manipulating images per-pixel in VRAM is costly and so is fetching the image, manipulating it and sending it back.
-- Sandman

ruckage

Hi Sandman,

I'd figured as much but thought it was worth exploring.  I had success with my sine wave distortion in 'snakes on dope' which runs really fast but the difference is that that works by manipulating 'strips' of the screen  which only equates to 240 operations (little less actually as it doesn't cover the full height of the screen), manipulating every pixel in the screen is obviously far more operations (76800) which is just more than fenix can comfortably handle.  But it's not really designed for that kind of thing so I'm not criticizing - just nice to push things a bit.

Anyway I've been experimenting some more and with some other optimisations and a few compromises I think I can get the effect I want working at a decent speed.

Sandman

That is good news. :) Do you have a demo, video or picture?
-- Sandman

ruckage

Not yet but I'll post something a bit further down the line when it's working exactly as I want, I need to do a few tests to get it working at its best on the Caanoo as that's the target platform.  I'm just using the effect for a cool background for a little game I'm working on as it's been far too long since I released something new.

handsource-dyko

The amiga computer and most game consoles had hardware to do bitblit operations. As far as I know vga never had this sort
of hardware.  I wonder if modern gpu's are suitable for this.

FreeYourMind

Quote from: ruckage on April 27, 2011, 08:35:32 PM
Hi Sandman,

I'd figured as much but thought it was worth exploring.  I had success with my sine wave distortion in 'snakes on dope' which runs really fast but the difference is that that works by manipulating 'strips' of the screen  which only equates to 240 operations (little less actually as it doesn't cover the full height of the screen), manipulating every pixel in the screen is obviously far more operations (76800) which is just more than fenix can comfortably handle.  But it's not really designed for that kind of thing so I'm not criticizing - just nice to push things a bit.

Anyway I've been experimenting some more and with some other optimisations and a few compromises I think I can get the effect I want working at a decent speed.

Can you share with us, your wave distortion in 'snakes on dope'  ?

Sandman

To be sure, you're caching the pointer returned by map_buffer(), right? Like:

byte* pdata = map_buffer(...);
for(...)
    pdata[...] = ...;
end

Note that byte is used. For 32bit maps, it's better to use ints.

If you really want to get into optimizing, you could look into avoiding indexing the pointer (using the []), but modifying the pointer each iteration instead. For example, a copier could be:

byte* pdata1 = map_buffer(...);
byte* pdata2 = map_buffer(...);
for(...)
    *pdata1++ = *pdata2++;
end

Although I don't know if Bennu supports this. Moreover, this isn't faster per se.
-- Sandman