First, a bit of cleanup of your test code:
#include<windows.h>
// number of pixels written in each run
#define NUM_PIXELS 50000
// range of pixel coordinates
#define MIN_RANGE 100
#define MAX_RANGE 1000
#define RANGE_MULT 10
// pause after each run to allow DWM to do its things
#define DWM_PAUSE 20 // seconds
HDC DC;
void bench(int range, int pause)
{
int i, start;
// let DWM digest previous pixels
Sleep(pause*1000);
// feed more pixels into the system
start = GetTickCount();
for (i = 0; i != NUM_PIXELS; i++)
{
SetPixel(DC, rand()%range, rand()%range, 0);
}
printf ("pause %d range %d duration %d\n", pause, range, GetTickCount()-start);
}
int main (void)
{
DC=GetDC(GetDesktopWindow());
int range;
for (range = MIN_RANGE; range <= MAX_RANGE; range *= RANGE_MULT) bench(range, 0);
for (range = MAX_RANGE; range >= MIN_RANGE; range /= RANGE_MULT) bench(range, 0);
for (range = MIN_RANGE; range <= MAX_RANGE; range *= RANGE_MULT) bench(range, DWM_PAUSE);
for (range = MAX_RANGE; range >= MIN_RANGE; range /= RANGE_MULT) bench(range, DWM_PAUSE);
return 0;
}
Running this program on Win7 with Aero desktop enabled produced this:
c:\Dev\PHP\_StackOverflow\C++\SlowSetPixel\Release>SlowSetPixel.exe
pause 0 range 100 duration 1404
pause 0 range 1000 duration 5273
pause 0 range 1000 duration 8377
pause 0 range 100 duration 3713
pause 20 range 100 duration 3089
pause 20 range 1000 duration 6942
pause 20 range 1000 duration 8455
pause 20 range 100 duration 3151
Same program running with Aero disabled:
c:\Dev\PHP\_StackOverflow\C++\SlowSetPixel\Release>SlowSetPixel.exe
pause 0 range 100 duration 47
pause 0 range 1000 duration 31
pause 0 range 1000 duration 31
pause 0 range 100 duration 31
pause 20 range 100 duration 63
pause 20 range 1000 duration 47
pause 20 range 1000 duration 47
pause 20 range 100 duration 62
Did somebody steal my CPU?
Yessir, and I caught the culprit in the act.
These tests are best used with a task manager open to watch the dreadful dwm.exe
Desktop Window (Inept) Manager at work.
During the first execution, dwm.exe
was stuck at 100% CPU (using one of the 4 cores of my PC) and its memory consumption rose to ludicrous amounts (it went from about 28 Mb to 112 Mb).
Even with a 20 seconds pause, the bloody DWM was not even finished digesting the pixels. That is why the second part of the test shows slightly longer execution times.
Without Aero, the SetPixel function basically does nothing. The DC is not invalid, but SetPixel
does not carry out any (visible) modification.
What the heck?
The probable reason why all this happens is that with the flashy "new" (since Vista) desktop interface, composition of the final desktop bitmap is done by this dwm.exe
process. Each window has its own graphic buffer, and dwm.exe
gets notified of any changes and recomputes the final aspect of each pixel in the background.
Writing pixels directly into the desktop window basically screws up that little scheme, since an external program accesses what is supposedly the private playground of dwm.exe
.
I don't know how the Microsoft guys handled that case, but obviously they did not do it in anything like an efficient way.
It looks like multiple copies of the desktop are loaded into memory, probably to allow one to be modified while the others are integrated in the composition chain.
The amount of memory gobbled by dwm.exe
is roughly 25 times the size of a 1000*1000 RGBA bitmap.
The test shows this amount varies with the surface of the modified area.
I suspect the silly process samples the screen 20 or 30 times per second and creates a new copy of the modified portion of the screen when it sees something strange (like a SetPixel
call) has happened.
With such a crappy result, I wonder why they allowed to access the desktop DC in the first place, except to allow people to smudge the screen with 10 lines of code.
Efficient screen access now requires the use of DirectX to bypass the awful crappy layer of backward-compatibility you must go through to manipulate bitmaps with the antiquated Win32 GDI.