by Brian Gantt 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:
Pretty simple right? The rest of the article deals with some implementation detail of doing this in Windows 95/98 using DirectX. 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, reentrancy 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 bgantt@onegames.com Discuss this article in the forums
See Also: © 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
|