Search code examples
c++windowsperformancegraphicsallegro

Weirdly behaving pixel by pixel 3d graphing program [Faster drawing by dragging the window]


So I was coding a simple 3d graphing program in C++ using Allegro 4 libraries. I made it in the most simple way, by drawing pixel by pixel. Usually, putting single pixels on the screen is pretty slow because of how Allegro works and to plot a graph in resolution of 640x480 I have to wait a minute or two.

So I was rendering an image for my buddy to show and so I was dragging windows around to have a good screenshot and I discovered that by dragging the window with the rendering picture, it greatly speeds up as long as I hold the window. From 2 minutes it draws everything in 10 seconds.

What's the cause of this bizzare behavior? Is it something related to Windows' windows or is it caused by Allegro itself? Is there any explanation for this?

Screenshot

Also my code

#include <allegro.h>
#include <iostream>
#include <math.h>

using namespace std;

float MAX_Z = 1;
float MIN_Z =-1;
float SCALE =50;

inline void init(unsigned int width, unsigned int height)
{
    allegro_init();
    set_color_depth(24);
    set_gfx_mode(GFX_AUTODETECT_WINDOWED, width, height, 0, 0);

    install_timer();
    install_keyboard();
    install_mouse();
}

inline void deinit()
{
    clear_keybuf();
    allegro_exit();
}

int get_z_color (float z)
{
    if (z >= 0)
    {
        return makecol(255, (z/MAX_Z)*255, (z/MAX_Z)*255);
    }
    else
    {
        return makecol(255 - (z/MIN_Z)*255,0, 0);
    }
}

float get_z (float x, float y)
{
    return sin(sqrt(pow(x,2)+pow(y,2)));
}

float int_to_float (int a)
{
    return a;
}

int main()
{
    unsigned int res_width, res_height;

    cout << "Window size (W,H): ";
    cin >> res_width >> res_height;
    cout << endl << "Initiating in " << res_width << "x" << res_height << " resolution..." << endl;

    init(res_width,res_height);

    cout << "Success! Drawing graph..." << endl;

    for (int y=0; y<res_height; y++)
    {
        for (int x=0; x<res_width; x++)
        {
            float valued_x = (int_to_float(x)-(int_to_float(res_width)/2))/SCALE;
            float valued_y = (int_to_float(-y)+(int_to_float(res_height)/2))/SCALE;
            _putpixel24(screen,x,y,get_z_color(get_z(valued_x,valued_y)));
            //cout << "Drawing (" << valued_x << "," << valued_y << ")" << endl;
        }
    }

    cout << "Graph drawn." << endl;

    cin >> new char;

    cout << "Closing...";

    deinit();
    return 0;
}
END_OF_MAIN()

Solution

  • Drawing on screen surfaces, whatever library you're using, is always a costly operation.

    I never used Allegro (I'm using the SDL), but I would guess that windows redraws the screen surface each time a single pixel is put on it, and by dragging the windows around, you prevent the redrawing from actually happening.

    If you want your program to get a dramatic increase in performance, you should always draw to an off-screen surface, and blit the entire surface to the screen surface once the drawing is over (this is a basic double buffering technique).

    Like I said, I never used Allegro, but from what I could gather, you could do something like that :

    int main()
    {
      unsigned int res_width, res_height;
    
      cout << "Window size (W,H): ";
      cin >> res_width >> res_height;
      cout << endl << "Initiating in " << res_width << "x" << res_height << " resolution..." << endl;
    
      init(res_width,res_height);
    
      BITMAP *temporaryBitmap = create_bitmap(res_width, res_height);
    
      cout << "Success! Drawing graph..." << endl;
    
      for (int y=0; y<res_height; y++)
      {
        for (int x=0; x<res_width; x++)
        {
            float valued_x = (int_to_float(x)-(int_to_float(res_width)/2))/SCALE;
            float valued_y = (int_to_float(-y)+(int_to_float(res_height)/2))/SCALE;
            _putpixel24(temporaryBitmap,x,y,get_z_color(get_z(valued_x,valued_y)));
            //cout << "Drawing (" << valued_x << "," << valued_y << ")" << endl;
        }
      }
    
      blit(temporaryBitmap, screen, 0, 0, 0, 0, temporaryBitmap->w, temporaryBitmap->h);
    
      cout << "Graph drawn." << endl;
    
      cin >> new char;
    
      cout << "Closing...";
    
      destroy_bitmap(temporaryBitmap);
    
      deinit();
      return 0;
    }