Weird black lines when drawing pixels on backbuffer without locking
Itachihro

I wrote a little program to render a mandelbrot fractal

#SelectExpand
1#include <iostream> 2#include <complex> 3#include <vector> 4#include <allegro5/allegro.h> 5#include <allegro5/allegro_image.h> 6 7const int MANDELBROT_RESOLUTION_W = 1366; 8const int MANDELBROT_RESOLUTION_H = 768; 9 10inline float complex_length_squared(const std::complex<float>& cmplx) { 11 return cmplx.real()*cmplx.real() + cmplx.imag()*cmplx.imag(); 12} 13 14inline float lin_interpol(float p1, float p2, float rate) { 15 return p1 + (p2-p1)*rate; 16} 17 18void mandelBrotStep(const std::vector<std::complex<float>>& setCandidates, 19 std::vector<std::complex<float>>& seriesValues, 20 std::vector<unsigned int>& escapeCheck, 21 unsigned int currentIteration); 22void drawMandelBrot(const std::vector<unsigned int>& escapeCheck, unsigned int currentIteration); 23 24int main() 25{ 26 al_init(); 27 al_install_keyboard(); 28 al_init_image_addon(); 29 al_set_new_display_flags(ALLEGRO_FULLSCREEN_WINDOW); 30 ALLEGRO_DISPLAY* display = al_create_display(MANDELBROT_RESOLUTION_W,MANDELBROT_RESOLUTION_H); 31 al_set_window_title(display, "Mandelbrot (Press SPACE to advance step)"); 32 std::vector<std::complex<float>> setCandidates; 33 setCandidates.reserve(MANDELBROT_RESOLUTION_W * MANDELBROT_RESOLUTION_H); 34 for(int i = 0; i<MANDELBROT_RESOLUTION_W; ++i) { 35 for(int j = 0; j<MANDELBROT_RESOLUTION_H; ++j) { 36 setCandidates.push_back(std::complex<float>(lin_interpol(-2,2,i/static_cast<float>(MANDELBROT_RESOLUTION_W)), 37 lin_interpol(2,-2,j/static_cast<float>(MANDELBROT_RESOLUTION_H)))); 38 } 39 } 40 std::vector<std::complex<float>> seriesValues(MANDELBROT_RESOLUTION_W*MANDELBROT_RESOLUTION_H,0); 41 std::vector<unsigned int> escapeCheck(MANDELBROT_RESOLUTION_W * MANDELBROT_RESOLUTION_H, 0); 42 43 ALLEGRO_EVENT_QUEUE* event_queue = al_create_event_queue(); 44 for(auto& source : {al_get_keyboard_event_source(), al_get_display_event_source(display)}) { 45 al_register_event_source(event_queue, source); 46 } 47 48 unsigned int currentIteration = 1; 49 bool running = true; 50 bool needsRedraw = false; 51 al_clear_to_color(al_map_rgb(0,0,0)); 52 al_flip_display(); 53 while(running) { 54 ALLEGRO_EVENT event; 55 al_wait_for_event(event_queue, &event); 56 switch(event.type) { 57 case ALLEGRO_EVENT_DISPLAY_CLOSE: 58 running = false; 59 break; 60 case ALLEGRO_EVENT_KEY_DOWN: 61 switch(event.keyboard.keycode) { 62 case ALLEGRO_KEY_SPACE: 63 mandelBrotStep(setCandidates, 64 seriesValues, 65 escapeCheck, 66 currentIteration); 67 needsRedraw = true; 68 ++currentIteration; 69 break; 70 case ALLEGRO_KEY_ESCAPE: 71 running=false; 72 break; 73 default: 74 break; 75 } 76 default: 77 break; 78 } 79 if(needsRedraw && al_is_event_queue_empty(event_queue)) { 80 al_clear_to_color(al_map_rgb(0,0,0)); 81 al_lock_bitmap(al_get_backbuffer(display), ALLEGRO_PIXEL_FORMAT_RGB_888, ALLEGRO_LOCK_WRITEONLY); 82 drawMandelBrot(escapeCheck, currentIteration); 83 al_unlock_bitmap(al_get_backbuffer(display)); 84 al_flip_display(); 85 needsRedraw = false; 86 } 87 } 88 89 al_save_bitmap("mandelbrot.png",al_get_backbuffer(display)); 90 al_destroy_event_queue(event_queue); 91 al_destroy_display(display); 92} 93 94void mandelBrotStep(const std::vector<std::complex<float>>& setCandidates, 95 std::vector<std::complex<float>>& seriesValues, 96 std::vector<unsigned int>& escapeCheck, 97 unsigned int iterationNumber) 98{ 99 for(int i = 0; i< MANDELBROT_RESOLUTION_W; ++i) { 100 for(int j=0; j<MANDELBROT_RESOLUTION_H; ++j) { 101 if(escapeCheck[i*MANDELBROT_RESOLUTION_H + j]) continue; 102 seriesValues[i*MANDELBROT_RESOLUTION_H+j] *= seriesValues[i*MANDELBROT_RESOLUTION_H+j]; 103 seriesValues[i*MANDELBROT_RESOLUTION_H+j] += setCandidates[i*MANDELBROT_RESOLUTION_H+j]; 104 float length = complex_length_squared(seriesValues[i*MANDELBROT_RESOLUTION_H+j]); 105 if(length > 4) { 106 escapeCheck[i*MANDELBROT_RESOLUTION_H+j] = iterationNumber; 107 } 108 } 109 } 110} 111 112void drawMandelBrot(const std::vector<unsigned int>& escapeCheck, unsigned int iterationNumber) 113{ 114 for(int i=0; i<MANDELBROT_RESOLUTION_W; ++i) { 115 for(int j=0; j<MANDELBROT_RESOLUTION_H; ++j) { 116 int escaped = escapeCheck[i*MANDELBROT_RESOLUTION_H+j]; 117 if(escaped) { 118 float rate = static_cast<float>(escaped)/iterationNumber; 119 al_draw_pixel(i,j,al_map_rgb(static_cast<unsigned char>(lin_interpol(0,255,rate)), 120 0, 121 static_cast<unsigned char>(lin_interpol(255,0,rate)))); 122 } 123 } 124 } 125}

If I leave the locking/unlocking of the backbuffer out, I get weird black vertical (and sometimes horizontal) lines. Does al_draw_pixel not work properly on video bitmaps? Not that I would normally try to draw individual pixels on a bitmap in video memory (especially not millions of them), but I am a little curious as to why.

Peter Wang

I can't reproduce it here. But the code has some problems:

- you lock with WRITEONLY but don't draw all the pixels

- al_draw_pixel is not supposed to be used when the bitmap is locked. Use al_put_pixel instead, or direct memory access.

- al_draw_pixel takes float parameters. You are drawing to the top-level of the pixel instead of at the pixel centre, which sometimes makes a difference.

Itachihro

Forgive my ignorance, but I thought that al_clear_to_color would ensure that all pixels are drawn? Yeah, put_pixel is probably better in this situation, didn't know there were two functions for drawing pixels.

And I didn't even know al_lock_bitmap had a return value. Makes sense now that I think about it. Thanks!

Peter Wang
Itachihro said:

I thought that al_clear_to_color would ensure that all pixels are drawn?

It does, but then with the al_lock_bitmap call you promised to clobber all the pixels again, by passing the WRITEONLY flag. If you wished to leave some of the pixels as they were, you would need to use the READWRITE flag.

Thread #610167. Printed from Allegro.cc