Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Bitmaps and multi-threading

Credits go to SiegeLord for helping out!
This thread is locked; no one can reply to it. rss feed Print
Bitmaps and multi-threading
OICW
Member #4,069
November 2003
avatar

In my project I'm sampling heightmap data into a bitmap and then calculating a normal map out of that bitmap and I'd like to parallelize this process. I know that Allegro is not thread safe (I'm using A5) so one way is calculating the data into a buffer and then copying that buffer in a single thread into the bitmap.

The question is whether this could be possibly avoided so I can do something like this:

#pragma omp parallel for
for (int y = 0; y < height; ++y) {
  for (int x = 0; x < width; ++x) {
    // Either sample procedural generator or the heightmap
    // in case of normal map generation
    ALLEGRO_COLOR c = getColorValue(x, y);
    al_put_pixel(x, y, c);
  }
}

[My website][CppReference][Pixelate][Allegators worldwide][Who's online]
"Final Fantasy XIV, I feel that anything I could say will be repeating myself, so I'm just gonna express my feelings with a strangled noise from the back of my throat. Graaarghhhh..." - Yahtzee
"Uhm... this is a.cc. Did you honestly think this thread WOULDN'T be derailed and ruined?" - BAF
"You can discuss it, you can dislike it, you can disagree with it, but that's all what you can do with it"

SiegeLord
Member #7,827
October 2006
avatar

That should work fine. A shader would work even better depending on the complexity of getColorValue.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

OICW
Member #4,069
November 2003
avatar

The problem is, that I get a segmentation fault when I try to run the code. Full code snippet is here:

#SelectExpand
1//! Store the OpenGL context 2glPushAttrib(GL_ALL_ATTRIB_BITS); 3glPushClientAttrib(GL_ALL_CLIENT_ATTRIB_BITS); 4glMatrixMode(GL_PROJECTION); 5glPushMatrix(); 6glMatrixMode(GL_MODELVIEW); 7ALLEGRO_STATE state; 8al_store_state(&state, ALLEGRO_STATE_TARGET_BITMAP); 9 10al_set_target_bitmap(mBitmap); 11al_lock_bitmap(mBitmap, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_WRITEONLY); 12#pragma omp parallel for // adding this line causes segmentation fault 13for (unsigned int y = 0; y < aHeight; ++y) 14{ 15 for (unsigned int x = 0; x < aWidth; ++x) 16 { 17 ALLEGRO_COLOR c = aTexSampler(x, y); 18 al_put_pixel(x, y, c); 19 } 20} 21al_unlock_bitmap(mBitmap); 22 23al_restore_state(&state); 24glMatrixMode(GL_PROJECTION); 25glPopMatrix(); 26glMatrixMode(GL_MODELVIEW); 27glPopAttrib(); 28glPopClientAttrib();

The single threaded version runs just fine. When I've duplicated the loop, let it run in parallel and store into a ALLEGRO_COLOR[ width * height ] array and then copied that array into the bitmap it ran just as fine. But running this code results in segmentation fault.

Update: I've had that running in the release mode, now that I've run it in the debugger it seems that _al_put_pixel is getting 0x0 as the address of the target bitmap which seems odd. The bitmap is properly created before the execution proceeds towards the loop.

[My website][CppReference][Pixelate][Allegators worldwide][Who's online]
"Final Fantasy XIV, I feel that anything I could say will be repeating myself, so I'm just gonna express my feelings with a strangled noise from the back of my throat. Graaarghhhh..." - Yahtzee
"Uhm... this is a.cc. Did you honestly think this thread WOULDN'T be derailed and ruined?" - BAF
"You can discuss it, you can dislike it, you can disagree with it, but that's all what you can do with it"

SiegeLord
Member #7,827
October 2006
avatar

Oh right, yes, of course that wouldn't work. al_put_pixel draws to the bitmap obtained by al_get_target_bitmap() which is a per-thread setting. I'd either avoid using al_put_pixel and writing directly to the bitmap or perhaps trying something like this:

#SelectExpand
1struct Init 2{ 3 Init(ALLEGRO_BITMAP* bmp) : Bmp(bmp) {} 4 Init(Init& init) { al_set_target_bitmap(init.Bmp); } 5 ALLEGRO_BITMAP* Bmp; 6} init(mBitmap); 7 8#pragma omp parallel for firstprivate(init) 9for (unsigned int y = 0; y < aHeight; ++y) 10{ 11 for (unsigned int x = 0; x < aWidth; ++x) 12 { 13 ALLEGRO_COLOR c = aTexSampler(x, y); 14 al_put_pixel(x, y, c); 15 } 16}

Untested.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

OICW
Member #4,069
November 2003
avatar

Ok, I'll try that out. A side question though, how would I go with direct access to that bitmap you've mentioned?

Update: silly me, some time ago I've asked the question about direct access when I was trying to load bitmap data into a OpenGL cubemap. The answer is via the ALLEGRO_LOCKED_REGION.

Update 2: looking at the ex_draw.c it seems the answer is to use unsigned chars to tamper directly with the data. So unmap the color and write it down. I'm going to try that out.

Update 3: it's getting too late, I'll return back to it in the morning. Anyway, so far thanks for the help. I've tried this code:

#SelectExpand
1 ALLEGRO_LOCKED_REGION *bmp = al_lock_bitmap(mBitmap, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_WRITEONLY); 2 unsigned int size = aWidth * aHeight; 3 4#pragma omp parallel for schedule(guided) 5 for (unsigned int i = 0; i < size; ++i) 6 { 7 unsigned int x = i % aWidth; 8 unsigned int y = i / aWidth; 9 ALLEGRO_COLOR c = aTexSampler(x, y); 10 11 unsigned char r, g, b; 12 al_unmap_rgb(c, &r, &g, &b); 13 unsigned char *texel = (unsigned char *)(bmp->data + i * bmp->pixel_size); 14 texel[ 0 ] = r; 15 texel[ 1 ] = g; 16 texel[ 2 ] = b; 17 }

I still get the segfault at the texel assignment, my pointer arithmetics is most probably to blame. I'll look at it when I'll be more fresh.

[My website][CppReference][Pixelate][Allegators worldwide][Who's online]
"Final Fantasy XIV, I feel that anything I could say will be repeating myself, so I'm just gonna express my feelings with a strangled noise from the back of my throat. Graaarghhhh..." - Yahtzee
"Uhm... this is a.cc. Did you honestly think this thread WOULDN'T be derailed and ruined?" - BAF
"You can discuss it, you can dislike it, you can disagree with it, but that's all what you can do with it"

SiegeLord
Member #7,827
October 2006
avatar

Firstly, you probably want to specify a specific pixel format when locking (or even creating the bitmap). Secondly, you still want to do it with two loops. It'll be something like this:

for (unsigned int y = 0; y < aHeight; ++y)
{
  for (unsigned int x = 0; x < aWidth; ++x)
  {
     unsigned char *texel = (unsigned char *)(bmp->data + x * bmp->pixel_size + y * bmp->pitch);
  }
}

EDIT:

Incidentally, I tested the other approach and it worked just fine on my computer. Consider using it if this direct bitmap access is too annoying. Test code:

#SelectExpand
1#include <allegro5/allegro.h> 2#include <math.h> 3 4ALLEGRO_COLOR getColor() 5{ 6 float f = 0.1; 7 for(int ii = 0; ii < 10000; ii++) 8 { 9 f = sin(f); 10 } 11 return al_map_rgb_f(1, 1, f); 12} 13 14int main() 15{ 16 al_init(); 17 auto d = al_create_display(800, 600); 18 al_clear_to_color(al_map_rgb_f(0, 0, 0)); 19 20 auto mBitmap = al_create_bitmap(300, 400); 21 22 struct Init 23 { 24 Init(ALLEGRO_BITMAP* bmp) : Bmp(bmp) {} 25 Init(Init& init) { al_set_target_bitmap(init.Bmp); } 26 ALLEGRO_BITMAP* Bmp; 27 } init(mBitmap); 28 29 al_set_target_bitmap(mBitmap); 30 al_lock_bitmap(mBitmap, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_WRITEONLY); 31 32 #pragma omp parallel for firstprivate(init) 33 for (unsigned int y = 0; y < al_get_bitmap_height(mBitmap); ++y) 34 { 35 for (unsigned int x = 0; x < al_get_bitmap_width(mBitmap); ++x) 36 { 37 al_put_pixel(x, y, getColor()); 38 } 39 } 40 41 al_unlock_bitmap(mBitmap); 42 43 al_set_target_bitmap(al_get_backbuffer(d)); 44 al_draw_bitmap(mBitmap, 0, 0, 0); 45 46 al_flip_display(); 47 al_rest(1); 48}

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

OICW
Member #4,069
November 2003
avatar

Aw crap, I knew that something was wrong there, I forgot to change the pixel format as the previous version didn't care for it. I've tried the direct approach even with both for loops but to no avail. The problem seems to lie within the negative pitch (at least that's what I guess).

On the second row of the bitmap (y = 1) it throws SEGFAULT because the pointer is out of bounds. I wonder though how's it possible that I'm able to upload bitmap data with negative pitch to the GPU via glTexImage2D.

I'm going to try the other way by providing the pointer to each thread, although I'm not very fond of it. Thanks for help and sorry for not replying earlier.

Update: the problem lied within the negative pitch, because I've used x and y variables as unsigned int which caused "loss" of the negative bit in the pitch variable. Now it's working.

[My website][CppReference][Pixelate][Allegators worldwide][Who's online]
"Final Fantasy XIV, I feel that anything I could say will be repeating myself, so I'm just gonna express my feelings with a strangled noise from the back of my throat. Graaarghhhh..." - Yahtzee
"Uhm... this is a.cc. Did you honestly think this thread WOULDN'T be derailed and ruined?" - BAF
"You can discuss it, you can dislike it, you can disagree with it, but that's all what you can do with it"

Go to: