Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Device Context memory leaks and poor frame rate 2

Status
Not open for further replies.

GameDude

Programmer
Jun 22, 2004
23
US
I have recently used basic API functions to load a bitmap and make it 'bounce' around the screen. The program implements a wrapper class which does all the loading, blitting, drawing, and creating of the bitmap being used.

I am currently having problems with the "draw" function of my program, which takes as parameters the Device Context of the window to blit to, and the (x, y) location of the window to draw to, then performs the BitBlt function using these parameters as a basis and uses default values for every other argument in the API function. Originally, before learning of the GetDC(), ReleaseDC() system, I simply called the function like this:

myBitmap.draw(x, y, GetDC(hWnd));

This method got me fantastic frame rates and relatively good results, except for the fact that it had a memory leak, responsible for making BitBlt fail ever 9978th time.

After realizing this, I quickly embedded the new ReleaseDC() function into the program after placing the HDC into a variable and passing it into the function. The code then looked something like this:

HDC windowDC;

.
.
.

<start main loop>

windowDC = GetDC(hWnd);
myBitmap.draw(x, y, windowDC);
ReleaseDC(hWnd, windowDC);
UpdateWindow(hWnd);

<end main loop>

.
.
.

DeleteDC(windowDC);

This still compiles, runs, and responds. However, even though the frame rate has technically increased (quite dramatically as a matter of fact), the refresh rate seems to have dropped to a crawl. What at one point was a smooth bouncing action has now become a choppy slide show with the image not behaving with the same physical activity as before. I have posted the code in completion below. If anyone has any comments or possible solutions, they would be greatly appreciated. Thank You.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <String.h>
#include "Bitmap.h"
#include "Input.h"

LRESULT CALLBACK WndProc( HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam );

bool running = true;

const int RESWIDTH = 1280;
const int RESHEIGHT = 1024;
const double XMAXVELOCITY = 750.0;
const double YACC = 1500.0;
const double XACC = 2000.0;
const LPTSTR PATH = "C:/Bounce.bmp";

int WINAPI WinMain(HINSTANCE hInst, /*Win32 entry-point routine */
HINSTANCE hPreInst,
LPSTR lpszCmdLine,
int nCmdShow)
{
#pragma unused(lpszCmdLine)
HWND hWnd;
MSG lpMsg;
WNDCLASS wc;
clsBitmap testBitmap;
clsKeyboard keyboard;
DEVMODE dm;
HDC windowHDC;
double currentTick = 0;
double x = 0;
double y = 0;
long numFrames = 0;
double totalTime = 0;
double averageFR = 0;
double currentTime = 0;
bool forward = true;
bool up = false;
double yVelocity = 0;
double xVelocity = 0;
bool acc;

if(!hPreInst) /*set up window class and register it */
{
wc.lpszClassName = szProgName;
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.lpszMenuName = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;

if(!RegisterClass(&wc))
return false;
}

hWnd = CreateWindow(szProgName, /* now create the window */
"CodeWarrior Win32 stationery",
WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,
0,
RESWIDTH,
RESHEIGHT,
(HWND)NULL,
(HMENU)NULL,
hInst,
(LPSTR)NULL);

ShowWindow(hWnd, nCmdShow );
UpdateWindow( hWnd );

memset(&dm,0,sizeof(dm));
if(!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
{
MessageBox(NULL, "Could Not Enum Display Settings", "Error", MB_OK);
}
dm.dmPelsWidth = RESWIDTH;
dm.dmPelsHeight = RESHEIGHT;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
int result = ChangeDisplaySettings(&dm,CDS_FULLSCREEN);

RECT windowRect;
GetWindowRect(hWnd, &windowRect);

testBitmap.load(PATH);

ShowCursor(false);

windowHDC = GetDC(hWnd);

totalTime = GetTickCount();
currentTick = GetTickCount();
while(running) /* begin the message loop */
{
if(keyboard.keyIsDown(VK_ESCAPE))
running = false;
currentTime = GetTickCount() - currentTick;
currentTick = GetTickCount();

if((keyboard.keyIsDown(VK_RIGHT) && !keyboard.keyIsDown(VK_LEFT)))
{
xVelocity += XACC * (currentTime / 1000);
if(xVelocity > XMAXVELOCITY)
xVelocity = XMAXVELOCITY;
}
else if(xVelocity > 0)
{
xVelocity -= XACC * (currentTime / 1000);
if(xVelocity < 0)
xVelocity = 0;
}

if(keyboard.keyIsDown(VK_LEFT) && !keyboard.keyIsDown(VK_RIGHT))
{
xVelocity -= XACC * (currentTime / 1000);
if(xVelocity < -XMAXVELOCITY)
xVelocity = -XMAXVELOCITY;
}
else if(xVelocity < 0)
{
xVelocity += XACC * (currentTime / 1000);
if(xVelocity > 0)
xVelocity = 0;
}

if(x < windowRect.left)
{
xVelocity = 0;
x = windowRect.left;
}
else if(x > (windowRect.right - windowRect.left) - testBitmap.getWidth())
{
xVelocity = 0;
x = (windowRect.right - windowRect.left) - testBitmap.getWidth();
}

x += xVelocity * (currentTime / 1000);
if(acc)
yVelocity += YACC * (currentTime / 1000);
else
{
yVelocity -= YACC * (currentTime / 1000);
if(yVelocity <= 0)
{
yVelocity = -yVelocity;
acc = !acc;
up = !up;
}
}
if(up)
y -= yVelocity * (currentTime / 1000);
else
y += yVelocity * (currentTime / 1000);
if((!up && y >= (windowRect.bottom - windowRect.top) - testBitmap.getHeight()))
{
acc = !acc;
up = !up;
y = 2 * (windowRect.bottom - windowRect.top - testBitmap.getHeight()) - y;
}
windowDC = GetDC(hWnd);
if(!testBitmap.draw(x, y, windowHDC))
{
running = false;
ReleaseDC(hWnd, windowDC);
}
else
{
if(GetMessage(&lpMsg, NULL, 0, 0))
{
TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
}
UpdateWindow(hWnd);
numFrames++;
}

}
DeleteDC(hWnd, windowHDC);
totalTime = GetTickCount() - totalTime;
ShowCursor(true);
ChangeDisplaySettings(NULL, 0);
averageFR = numFrames / (totalTime / 1000);
return(lpMsg.wParam);
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT messg, /*callback procedure */
WPARAM wParam, LPARAM lParam )
{
switch(messg)
{
case WM_PAINT:
break;

case WM_DESTROY:
PostQuitMessage(0);
running = false;
break;

default:
return( DefWindowProc( hWnd, messg, wParam, lParam ) );
}

return(0L);
}
 
First of all, I would like to thank you for the link to that article. It was an interesting read, and will do me nothing but good in getting things to animate.

With that said, I finally found out that the real problem with my program (and the reason I could never figure out what was wrong with my device context) had nothing to do with the Device Context at all!!! It was a time related issue!!

The function I was using to calculate how much time had passed (GetTickCount()) is only accurate down to the millisecond. My frame rate was astronomical, somewhere around 35,000 frames per second. This means that over 90% of the time, the amount of time that had passed since the last frame was zero!!! Zero time means zero movement.

As for that 10% of the time when it was not zero, the bitmap seemed to do a "leap" to catch up to where it should be. These occational leaps the bitmap made gave the illusion of a slow refresh rate!! I was able to fix the problem by putting a restriction on the frame rate.
 
>aGetTickCount ... accurate down to the millisecond

Might be worth pointing out that it isn't...it has a tick rate equal to that of the system timer, which is approx 55ms on W95/98/Me, 10ms on NT/2000/2003, and generally 15ms on XP
 
This is a nice 'stopwatch' class for timing out things with high precision.
Does nice for an animation.

filename: sw.h
[tt]


#ifndef SW_H
#define SW_H

#define WIN32_LEAN_AND_MEAN

#include <windows.h>


/*
=================================================
class SW

Measures time intervals with best precision
available under windows.

Returns time values in seconds with fractions
e.g.: 0.00000012345 and 1.20001752 seconds

=================================================
*/
class SW
{
private:
LARGE_INTEGER liFreq;
LARGE_INTEGER liStart;
LARGE_INTEGER liStop;

public:
BOOL bAvailable;

public:
SW()
{
liStart.QuadPart = liStop.QuadPart = 0;
bAvailable = QueryPerformanceFrequency(&liFreq);
}

void Start()
{
QueryPerformanceCounter(&liStart);
}

void Stop(double& dSeconds)
{
QueryPerformanceCounter(&liStop);
dSeconds = (((double)liStop.QuadPart - (double)liStart.QuadPart) / (double)liFreq.QuadPart);
}

void Reset()
{
liStart.QuadPart = liStop.QuadPart = 0;
}
};


#endif //SW_H

[/tt]

Here some pseudo code sample in how to use it:

[tt]

BYTE bContinue = 1;
double dElapsed, dInterval = 0.04; //== (1/25) seconds means 25 fps.
SW sw;

sw.Start();

while(bContinue){

sw.Stop(dElapsed);

if(dElapsed < dInterval) continue;


BitBlt(...);

sw.Start();
}

[/tt]

I also use it a lot for measuring performance of a piece of code :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top