Jump to content
  • Advertisement
  • 10/22/99 12:34 PM
    Sign in to follow this  

    Decoupling the mouse pointer update from the frame rate

    General and Gameplay Programming

    Myopic Rhino
    Users are used to smooth and responsive mouse movement. Slow frame rates (sub 25) can cause the mouse pointer to become jerky and unresponsive. This article describes how to decouple the mouse pointer update from the frame rate through the use of interrupt driven multitasking and old fashion single buffering.

    (What I'm going to describe here is pretty simple, however I felt the need to write this article because I'm still seeing games with jumpy mouse pointers as the frame rate goes south.)

    Traditionally the way to draw the mouse pointer in games as has been to synchronize it with frame buffer flips. You render the scene, draw the mouse pointer, and then flip. This was fine for many 2D games that weren't graphically taxing the system and could keep their frame rates above 25 frames per second. Lately though graphics processing has gotten a lot more intensive (especially with 3D) and even games that can usually keep a high frame rate have periods where they will drop below 15. The mouse pointer then becomes jumpy, hard to control and makes the game feel slow and unresponsive.

    The solution to this problem is to desynchronize the mouse drawing from the buffer flips, and draw the mouse asynchronously when ever the user moves the mouse. This means updating the mouse on the front buffer while at the same time rendering on the back buffer. Here's a operating, graphics API neutral description of how this is done:
    1. Set up a separate thread that gets activated whenever the mouse moves.
    2. When this thread gets activated first restore whatever was under the mouse pointer previously, save the rectangle where you are going to place the mouse pointer, and then draw the mouse pointer. This is all done on the front buffer.
    3. Make sure on frame flips that you also do a save and draw on the back buffer.
    Pretty simple right? The rest of the article deals with some implementation detail of doing this in Windows 95/98 using DirectX.


    [size="5"] Multitasking in Windows:

    I see multitasking as a necessary evil. Sure it can be useful and make some things easier, but it also brings with it a whole nasty class of bugs. (race conditions, re-entrancy issues, etc.) These can occur seemingly at random and can be very hard to debug. Also, I frankly don't trust Microsoft's code to always handle multitasking correctly. How many times have you seen Windows 95/98 grind to a halt as it gets stuck on the Win16Lock? Still if you want to handle asynchronous events you got to multitask. For the most part DirectX seems to handle multitasking OK. However there was one function that I had to help out (I'll get to it later) and there maybe others that have problems that I'm not using.

    I'm using two threads. One for mouse drawing and messaging, one for everything else. I also have two mutexs. One I use to protect the mouse drawing routines from reentrancy, the other I use to flip back and forth from windows message retrieval and the main game loop.

    Here's a code snip:

    hTMain=GetCurrentThread();
    hMThreadFlip=CreateMutex(NULL,TRUE,NULL);
    hMMouseDraw=CreateMutex(NULL,FALSE,NULL);

    hTLoop=CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)MainLoopStart,NULL,NULL,&lpThreadId);

    while(1)
    {
    if (fThreadReleased)
    {
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
    if (msg.message==WM_QUIT) return(msg.wParam);

    DispatchMessage(&msg);

    if (fEnding) continue;
    }

    ReleaseMutex(hMThreadFlip);
    }
    else
    {
    while (PeekMessage(&msg,NULL,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE))
    {
    DispatchMessage(&msg);
    }
    }

    fThreadReleased=(MsgWaitForMultipleObjects(1,&hMThreadFlip,FALSE,INFINITE,QS_MOUSEMOVE)==WAIT_OBJECT_0) ? TRUE : FALSE;
    }

    I only want to process mouse move messages asynchronously. By using the hMThreadFlip mutex along with the fThreadReleased flag and MsgWaitForMultipeObjects I can make sure that non mouse move messages are only processed between game loops.

    Here's snip of the mouse drawing code.

    Draw on WM_MOUSEMOVE code:

    case WM_MOUSEMOVE:
    if (WaitForSingleObject(hMMouseDraw,0)==WAIT_OBJECT_0)
    {
    DrawMouseOnFront();
    ReleaseMutex(hMMouseDraw);
    }
    Draw on frame flip code:

    WaitForSingleObject(hMMouseDraw,INFINITE);

    // Draw Mouse.
    DrawMouseOnBack();

    // Do Flip.
    FlipScreen();

    ReleaseMutex(hMMouseDraw);
    I not going to go into the details of the DrawMouseOnFront and DrawMouseOnBack routines. Basically I use BltFast to restore, save and replace the mouse bitmaps. Its very straight forward.

    One I thing I had to do was place mutexs (blocking calls to DrawMouseOnFront()) around all calls to Surface->GetDC, Surface->ReleaseDC. It appears the GetDC, ReleaseDC functions can't handle multitasking correctly. Before I did this I was getting crashing in DirectX.

    Using this method makes the mouse pointer nice and smooth no matter the frame rate. It can also make your game appear faster because when your keyed in on the mouse you don't notice other jerkiness. The mouse feels responsive so the game feels responsive.

    Questions? Comments? you can email me at [email="bgantt@onegames.com"]bgantt@onegames.com[/email]


      Report Article
    Sign in to follow this  


    User Feedback


    There are no comments to display.



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!