BitBlt() experiment

Pages: 12
closed account (3pj6b7Xj)
Hey all, first I'm new here, I don't consider myself a proffesional C++ programmer but I can say I am very fluent with it. I got into Windows Programming about a month ago and I must say it is a lot of fun, I am almost finished working on my Squibbles game which is a mock up of the original Qbasic Nibbles...all the drawing is done via GDI CreateRoundRect and FillRgn commands as well as others, it works well for now.

Anyway, I starting messing around with BitBlt() a week ago and created a double-buffered animation with two rectangles, one was controled by a variable int xpos which moved the box left and right xpos-- or xpos++ which ever and the other rectangle was controled via the mouse position or followed the position of the mouse.

I noticed a few shocking discoveries. When I removed my timer from the loop, the square animated by xpos showed no change in speed, it was slow, really slow, however, the mouse that followed mouse position moved around really fast when ever I moved the mouse, so I started thinking...

Why in the world is the rectangle animted by xpos so slow and the one on mouse position input so fast?

I guess what im trying to get at is this, if I can some how accelerate the iteration of xpos minus or plus I could probably get the rectangle to move faster but that seems almost impossible!!

If anyone has any feed back, please comment, thanks guys! Glad to be here.
GDI is supposed to be slow in general, I watched a video on the new features of Visual Studio 2010 and it said GDI will actually use DirectX. It had a comparison of the two, GDI isn't even close to DirectX.

That may or may not be the cause of your problem, I'm just trying to help.
closed account (3pj6b7Xj)
I have already compiled the example I talked about in several implementations, GDI, DirectX, OpenGL and Direct2D. I must be doing something wrong because even with DirectX I cannot get the darn rectangle moving fast on the screen unless I step the iteration of the variable (i.e x =+ 2) I want to do it at just 1 step.

Thanks for the reply, hopefully things will become clear for me.
Personally I wouldn't use WinAPI for this anyway. I tend to favor libs geared towards gaming because they are typically cross platform and about a million times easier to use. I'm a big fan of SFML ( http://www.sfml-dev.org )


Other factors that might be causing this to be slow is if you're VSyncing. I doubt WinGDI VSyncs, but if you're using DirectX it might be. Basically VSync means your drawing code waits until the monitor is ready before refreshing. So if your monitor is going at 60 Hz, and you're moving 1 pixel per frame... then you're moving a max of 60 pixels per second, even if your code can run faster. The only way to speed it up would be to move more than 1 pixel per frame (or to kill VSync, but then you have image tearing)
closed account (3pj6b7Xj)
I would post the code for the GDI version but then Id have to post; hdcbuff.h, createapp.h and bitblitex.cpp might be a little too much those are just files that I created that my program uses...hdcbuff.h is just a header with a class that creates a DC off-screen buffer using BitBlt() and can write to the buffer and present it to the screen, sort of like direct x...createapp.h is yet another header with a class that creates a default windows application, something I created so I wouldn't have worry about the inner workings of a windows app unless I specifically wanted to change any of its parameters which I can with the class before I present the window.......there is that present again, lol and bitblitex.cpp just means bitblit example, whatever..it has code that uses the class in hdcbuff.h to create 3 squares; red, blue and green...the red square moves left and right, the green square is still and the blue square follows mouse position.

Should I post the mass of code or not? Maybe you guys can spot something I did wrong, I don't mind showing my entire implementation, althought I must admit, i'm a little shy having everyone looking at my coding habits, lol.
Wait a moment... I just read your OP in more detail.

The mouse input is going fast, you said, but the timer is slow?

How exactly are you animating the slow one? Are you using WM_TIMER or something?
closed account (3pj6b7Xj)
Nope, not using WM_TIMER, just calling InvalidateRect(hWnd,NULL,false); to force a re-draw and keep the animation going. I'm animating the red square via one variable xpos or actually...

1
2
3
4
5
int xpos = 10; // horizontal position
int direction = 0; // direction; 0 left 1 is right

if (direction == 0) { xpos--; if (xpos < 10) direction = 1; } // moving left
if (direction == 1) {xpos++; if (xpos > 600) direction = 0; } // moving right 


I had thrown in a for loop at first to slow the animation down but when I removed
the for loop, I found there was no change in the animation, the square simply continued
to move smoothly about the screen. I was actually expecting it to move left and right so fast that I could almost not see it but it didn't work this way, if it did, I would have used some timer to slow it down.

The square that follows mouse position moves to were ever I move the mouse. I could circle about the screen with the square really fast and it still follows the mouse position with no noticeable slow down.

so I believe that the square is slow not because bitblt() is slow it is because the modification of the xpos variable itself is being done slowly or in other wods, it is adding and taking away at a slow pace.

I was thinking that there may be a way to accelerate the counting of xpos by using some kind of high resolution timer, so that xpos increments or decrements at the same exact frequency fo the high resolution timer, then x could iterate from 0 to 600 in less than a millisecond so would the square but this is were it seems impossible, it seems that input would have to be from an outside source of some kind.

I am open to ideas and we all know there is usually a way to do things.
Okay let me rephrase.

Where are you modifying xpos? This is sounding more like a program flow problem.


The thing with WinAPI is that the program flow is often driven by the message queue. InvalidateRect simply sends redraw messages that are processed by the message pump.

You must be doing one of two things:

1) You have your code running in some message handler (if not WM_TIMER, then where?)

or

2) You have your code running outside the message pump.


#2 is the right way to go. But you might have an ill formed message pump.

If you're doing #2, show me where you're updating your code and what your message pump looks like. (The message pump is typically GetMessage or PeekMessage in a loop, followed by TranslateMessage and DispatchMessage)


EDIT:

Of course, again I would recommend you ditch WinAPI completely and just use SFML. Much easier, much more capable, faster, and cross platform. win/win/win/win.
Last edited on
closed account (3pj6b7Xj)
This is what it looks like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int xpos = 10; // horizontal position
int direction = 0; // direction; 0 left 1 is right

while (GetMessage(Msg, hWnd, 0,0))
{
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);

    if (direction == 0) { xpos--; if (xpos < 10) direction = 1; } // moving left
    if (direction == 1) {xpos++; if (xpos > 600) direction = 0; } // moving right 

    /* code to move square left and right goes here. */

    if (Msg.message == WM_MOUSEMOVE)
    {
        /* code for square that follows mouse goes here. */
    }

    /* code for implementation that displays off-screen DC buffer goes here. */

    InvalidateRect(hWnd,NULL,false);
}


By the way, DispatchMessage() is seriously slow. I actually created a mock up of the Nibbles game in the original QBAsic and it works well but comes to a crawl when I add DispatchMessage() into the loop but with out, the program behaves erratically.

The off-screen DC buffer thing, I am drawing the squares in the off-screen DC and then blitting them with BitBlt() to the main client window.

I will take a look at SFML when I'm done posting this. Will await your next reply :) thanks for the great help!
Last edited on
closed account (3pj6b7Xj)
Hey I just took a look at SFML and it definately got me drooling on the keyboard. I downloaded the SDK and documentation, thanks man! I'll be busy reading and fooling around all night long with this!

And they are right, I saw one of the examples, it can't get any easier than that!
Yeah your message pump is all wrong.

For starters you probably shouldn't be checking Msg there. You should be checking the WM_MOUSEMOVE message in your windproc.

Furthermore, GetMessage sleeps until a new message becomes available. So if your program isn't getting any messages, it basically stops. This is why your other code was slow -- it was only being run when there were pending messages.

But anyway yeah, screw WinAPI. Dig into SFML. Some people have trouble getting it installed, but once you get that out of the way it's much, much easier.
closed account (3pj6b7Xj)
Hmm, really, I know that DispatchMessage() only calls the windows procedure...I will re-write the code and put into WndProc but i'm going to sleep now, its late, night.
Well don't put it all in the wndproc. Only put message handling stuff there (ie, your WM_MOUSEMOVE stuff).

The actual game logic shouldn't go in the wndproc because it's not message driven. I would do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
while( program_is_running )
{
  while( PeekMessage(&Msg,hWnd,0,0,PM_REMOVE) )
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }

  // .. put code here so slow down your game or to regulate it to a certain speed

  
  // .. put game logic code here
}


If you don't put code to slow-down/regulate the speed, your program will run as fast as the computer will allow it (sucking up 100% CPU time in the process). Typically programs will poll the time with some mechanism (GetTickCount is easy) and Sleep() until X time has passed (Don't use Sleep alone to regulate the time -- it may sleep for a longer time than you give it!). So basically you run the game logic code once every X milliseconds.

You'll also note I use PeekMessage instead of GetMessage. The difference here is that GetMessage will wait (sleep) indefinitely until another message is sent. However PeekMessage will return immediately whether or not there are any pending messages. By throwing it in a while loop like I have it, I'm basically processing all messages in the queue, then proceeding to game logic code once the queue is empty.



You do something similar to the above in game libs, also (SFML is set up the same way, only they're called "Events" instead of messages)
Last edited on
closed account (3pj6b7Xj)
1
2
3
4
5
while( PeekMessage(&Msg,hWnd,0,0,PM_REMOVE) )
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }


So PeekMessage will look, get any messages and return immediately, if it finds any, they are passed to TranslateMessage() & DispatchMessage(). However, if there aren't any messages, the loop will not execute and flow continues to "slow down the game" and then "the game logic code". You know, I've never throught of it that way, i'll try this out and i'll get back to you and see what results I get, thanks!
Yeah that's basically it.

PeekMessage returns true if there was a message pending (in which case the loop executes and the message is processed). It returns false is there were no pending messages. So that code snippit keeps looping and processing all message until there aren't any more.


But I still recommend you get away from WinAPI for this. SFML is much easier/better for other reasons.
closed account (3pj6b7Xj)
Guess waht Disch, it speeded up but only by 13% I would say, rough estimate...still, it wasn't as fast as I hoped. I've also run into another problem. I noticed that GetMessage() can pick up WM_QUIT but for some reason PeekMessage() doesn't pick it up. I had to force shut down my program every time I compiled it! It works if I look for SC_CLOSE from WM_SYSCOMMAND but thats only from the control menu that shows size, move, restore, minimize, maximize and close but for some reason the windows minimize, restore and close buttons that are located to the right of a windows box have no effect at all. I can check for WM_CLOSE, WM_QUIT, WM_DESTROY, WM_SYSCOMMAND, SC_CLOSE with PeekMessage() and I won't pick up any of those messages! Call me crazy but I think those buttons are sending different messages, since the Win32 API is pretty old could it be a possibility that these buttons send different messages? There must be something i'm doing wrong here.
closed account (3pj6b7Xj)
Yeah man, it wont quit at all, serious problem here...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool bQuit = false;

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (!bQuit)
    {
        while(PeekMessage (&messages, NULL, 0, 0, PM_REMOVE))
        {
            if (messages.message == WM_QUIT) bQuit = true;
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    }


Just some message pump I created fooling around. I looked for WM_QUIT in the message pump and it is even checked in the window procedure, application still stays running after you click the X button .. however, this isn't a problem if GetMessage() is used, strange!
Guess waht Disch, it speeded up but only by 13% I would say, rough estimate...still, it wasn't as fast as I hoped.


I wonder if you aren't validating your window properly. Failure to validate would cause an endless stream of WM_PAINT messages to be sent, causing your program to waste all it's time redrawing the same thing over and over.

What does your WM_PAINT handler look like?

I noticed that GetMessage() can pick up WM_QUIT but for some reason PeekMessage() doesn't pick it up.[snip]It works if I look for SC_CLOSE from WM_SYSCOMMAND but thats only from the control menu


I have always had this problem with WinAPI. Always always. I never understood how anyone ever got WM_QUIT to work right -- I never could.

I always looked for SC_CLOSE via WM_SYSCOMMAND anyway. It is sent by everything that tries to close the program -- and can be vetoed, whereas other messages are too late in the process to veto. (For example if you ask the user if they want to save changes before quitting, and they hit "cancel" indicating they want to leave the program running -- SC_CLOSE can be cancelled, but if you got WM_QUIT it's already too late and the program will quit anyway)

but for some reason the windows minimize, restore and close buttons that are located to the right of a windows box have no effect at all.


Welcome to WinAPI hell. This is another reason why I recommend you use something else. XD Especially for a game.

Anyway, does your WndProc call DefWindowProc? If not that might explain it.

Maybe you should just post you whole WndProc.

Call me crazy but I think those buttons are sending different messages, since the Win32 API is pretty old could it be a possibility that these buttons send different messages?

I couldn't begin to tell you all the messages everything sends. What's more, some things send multiple messages, and some messages trigger other messages to be sent. It's a huge tangled mess. The best you can do is pick out the important messages and ignore the rest.

Here, WM_SYSCOMMAND + SC_CLOSE is the important one. Forget about the others (WM_DESTROY, WM_CLOSE, WM_QUIT -- all useless. Well maybe WM_DESTROY would be good for cleanup....)
closed account (3pj6b7Xj)
I got PeekMessage to pick up WM_QUIT when you close the window with the upper right X button!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
bool bQuit = false;

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (!bQuit)
    {
        while(PeekMessage (&messages, NULL, 0, 0,PM_REMOVE))
        {
            if (messages.message == WM_QUIT) bQuit = true;
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    }


Above is the message pump...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_QUIT:
            PostQuitMessage(0);
            break;
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}


And that wasa the Window Procedure, the only difference is I added "case WM_QUIT: PostQuitMessage(0); break;"

And yes I was using DefWindowProc but that still doesn't explain why it works with GetMessage() and not PeekMessage()!? From what it seems, PeekMessage() will only pick up WM_QUIT if it is posted directly to the message queue, try it, it works.
closed account (3pj6b7Xj)
Well this is weird!!! It only works if WM_DESTROY is present!! For the X button to close your program and not leave it running you must proccess a WM_QUIT & WM_DESTROY in proper sequence...you are definately right man, this is crazy!

Ok, i've confirmed it. The X button sends a WM_DESTROY message. iN THE default windows procedure, this is designed to call DestroyWindow() were as in my window procedure, I send a WM_QUIT message instead into the message que. To properly intercept this message usinsg PeekMessage() you must check WM_QUIT and use WM_DESTROY to send the WM_QUIT message with PostQuitMessage(0); The X button only forces the window to close so perhaps the best solution is GREY out that button?? So that the program is quit by something from within
Last edited on
Pages: 12