Fast drawing to screen (Win32/GDI)

Started by
14 comments, last by Darragh 18 years ago
Hi everyone. I'm currently working on a little software renderer for a project, doing stuff like polygon filling, clipping and gouraud shading. I've been having problems as regards speed though. Previously I did the project using Borland C++ Builder 6 and i used a Graphics::TBitmap object to hold my screen buffer and then used Borlands TCanvas object to draw this screen buffer to the screen. If you are not familiar with Borlands classes, these are just basically easier to use wrappers around the Win32 API and the GDI. This worked fine, however it was a little slow. Most of the time was not being spent figuring out what color the pixel was, but just writing it to the screen buffer. I put most of this down to Borlands runtime checking etc.. so i've decided to port it over to the GCC compiler instead and use DevC++ as my IDE. Grand, i'm after porting over the code- making changes where necessary and it compiles nicely. I've hit one major stumbling block though- getting actual screen buffer onto the screen... Ok, I know how to create a Bitmap in memory- so thats a start. I also know how to blit a bitmap onto the screen- great.. But whats completely stumped me is how i can write to that bitmap and modify it memory. The windows BITMAP structure holds a pointer which supposedly points to the actual pixel data in memory. I've tried using this pointer to reference the pixel data and write to it, but my program bombs out if I attempt to even change so much as the first pixel in the array. So is it possible at all to modify BITMAPS while they are in memory- or is that a definite no no ? All I want to have is some part of memory that i can write to directly and then whack out onto the screen when I'm done with it. Of course nothing's ever that simple with Windows!- is it? [headshake] I'm sorry but I don't have any source code on hand at the moment- i don't think its really necessary anyway. If anyone could describe to me a way of getting stuff out of memory and onto the screen quickly and efficiently then I would be greatfull. Oh and any good links are welcome too.. Thanks for the help, Darragh
Advertisement
2 second google search for BITMAP found me this:
http://www.codeproject.com/bitmap/rplcolor.asp

Seems to be what you want.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

I guess you'll also find TinyPTC usefull. Even if you don't use it, you can at least see how they do the work.

Regards,
At work I had a project not too long ago with similar requirements, and I ended up with a very fast solution:

1) Create a device independant bitmap using CreateDIBSection (use a negative heigh to make the DIB top->down so the pointer is to pixel (0,0) in the upper left corner)
2) Create a memory DC using CreateCompatibleDC with the DC for your window (which should have the 'Own DC' flag set in the window class)
3) Select the DIB into the DC creatd in step 2

4) Create a device dependant bitmap using CreateCompatibleBitmap with the DC for your window
5) Create a memory DC using CreateCompatibleDC with the DC for your window
6) SelectObject the bitmap into the DC creatd in step 5

loop
7) Call FlushGDI() to ensure nothing is messing with the DIB (mainly relevant if you're using GDI functions to draw things onto the DIB)
8) Draw to the DIB created in 1 using the pointer given by CreateDIBSection (don't forget each line is a multiple of 4 bytes, even if you're using 24-bit color, so if your line would be 15 bytes {5 pixels @ 24bpp}, increment the pointer by 16 to get to next line)
9) Use BitBlt or StretchBlt to copy from the DC in step 2 to copy to the DC in step 5
10) Use BitBlt or StretchBlt to copy from the DC in step 5 to your window
endloop

11) SelectObject old bitmaps to their DCs, DeleteDC on both created DCs, DeleteObject on the bitmap and DIB

The reason for the separate bitmap is basically as a double buffer (it stores the complete scene, so you could draw it on the WM_PAINT event to ensure the window is redrawn with the latest frame, for example), but IME it also helps speed things up for some reason. The reason I didn't use StretchDIBits to blit the DIB is that it seemed to be completely sporadic - sometimes it didn't work at all, sometimes it would bring the program to a crawl, and sometimes it worked just fine. It could be the video drivers or something like that, but whatever the cause it was not reliable.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
That should read "GdiFlush" above in step 7.

I do the same in my software 3D engine except that I don't bother with the double-buffering (removing steps 4-6 and combining 9 & 10). I haven't tried that way so I can't comment definately on its speed. It could be faster though because at the point it copies it to screen, it is already in the right format.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Thanks everyone for your replies. So the device independent bitmap is pretty much the only way i can directly write to a bitmap ? That sounds grand..

Yeah i've been looking and looking across the internet for the best way to do this- god the Win32 API is such a maze of functions its not even funny! I'm coming from a previous JAVA background where you had a nice and logical API (not to mention well documented!) to do all this sort of stuff. Of course i'm doing nothing but C/C++ programming these days so i'm pretty much limited to the libraries available for the language. Gosh, doing graphics in assembly under mode 13h in DOS was much easier than doing the same for windows!- it sounds insane but its completely true- as any of you who have messed around with ASM would know...

Sure I'll definitely give this a shot and see how it works out- i'll reckon it should be faster than under Borland anyway at the least. As I said earlier the main overhead in the program was just writing to the screen buffer- not doing gouraud shading or clipping or any other stuff like that, so if I can get this working correctly I should make some gains in speed at least.

One thing I haven't considered thus far is the DirectDraw API, Would it be a better idea to investigate this instead as a means of writing to the screen ? I admit to knowing absolutely nothing of the API, but if it can provide the sort of quick and direct access to the screen buffer I need then it might well be worth investiating. Just what can you do with DirectDraw ? The absolute ideal situation would be where I would have an array of 32-bit integers for my screen buffer and just use some sort of API function whereby i could swap that buffer onto the screen. Another good situation would be where i could get a pointer to a system managed screen buffer in memory and just write directly into that, just like the old days of DOS where you could directly write into a screen buffer in memory. Again I know nothing of Direct Draw so I don't know if this is even possible- but it would be superb if it was.

Anyhow i'll give the DIB method a shot, and if you have any better sugestions then i'm all ears.

Thanks for your help
-Darragh
The width if your DIB must be a multiple of 4, otherwise all hell rains down!

Quote:Original post by Thevenin
The width if your DIB must be a multiple of 4, otherwise all hell rains down!



That sort of image looks familiar... I tried using the CreateBitmap() function before under borland before and got a similar disjointed image to that one. I think that was because though the CreateBitmap() function was making a monochrome image and not a '32bit' image as i hoped for- everything ended up in black and white anyway!.. I should be ok though-i'll be using 640x480 fullscreen as my resolution so no problems there.

If you really want to read/write the pixels directly, look up SetDIBits and GetDIBits.
Hmmm.. I'm after attempting a slightly different method to what Extrarius posted- and it works nicely. Firstly I create a device dependent bitmap on initialising the renderer which is 32bits in color depth, and save its handle. I also create a memory DC on startup and save that handle. Then I write away to my own internal color buffer which and when I'm finished I swap this into the Bitmap using the 'SetBitmapBits' function.

I wonder should I use the method posted by Extrarius though ? I don't like this idea of swaping into a DIB, and then swapping into a DDB and then finally swapping onto the screen- it seems like a lot of needless overhead for nothing. On the other hand though SetBitmapBits is a legacy function from 16-bit versions of Windows, so unless its been rewritten to take advantage of the extra data width offered by modern processors then its not going to be really the most efficient either- its 50/50 as to which method is faster...

None of this is really good. What about DirectDraw ? I think i'll look into that if it offers more direct access to the screen buffer. I think I'm gonna be dissapointed though- i doubt its gonna just give me a pointer to a screen surface and let me write away.

On the other hand i've had some good speed improvements in other areas- so its not all bad. The port from Borland improved the code speed somewhere in the region of 15%. I've also rewritten my function to clear the screen (which consumed a good bit of time) in assembly and almost halfed its execution time- god what sort of code was the compiler generating at all ?

Anyhow i think i'll look into DirectDraw and see what kind of capabilities it offers. If anyone has any more suggestions then post away! I want to get this screen buffer stuff running as fast as possible before i turn my attention to the rasteriser and begin optimising that. Phew! Who would have thought writing fast code was such work ? [wink]

This topic is closed to new replies.

Advertisement