Sign in to follow this  
tendifo

Problem with buffered device contexts

Recommended Posts

I have two memory device contexts. One is totally blank and is used to clear the main buffer to which I write to. So I perform two BitBlt operations. // i clear the main buffer BitBlt(mainbufferDC, 0, 0, 400, 400, blankDC, 0, 0, SRCCOPY); // i draw to mainbufferDC // then i copy to the screen BitBlt(hdc, 0, 0, 400, 400, mainbufferDC, 0, 0, SRCCOPY); This is inside the windows loop. Now, the problem is that the program locks up whenever I do this. And to try and fix the problem I added Sleep(1) after the final BitBlt. This seemed to do the trick, but sometimes the program ran extremely slow, and sometimes extremely fast. And I tried Sleep(0) as well, but it went back to the same problem. Can someone shed some light on the problem. Maybe I can't do that many BitBlt's that many times in a certain time frame? If you need to see all the code, I can post that as well. Thanks.

Share this post


Link to post
Share on other sites
Here's all of the code. It's a space invaders game by the way, so there's a lot of code. The BitBlt calls are near the end in the windows loop.

#include <windows.h>
#include <cstdlib>

#define MO_MAX 3200

#define MT_BULLET 1
#define MT_ENEMYSM 2
#define MT_ENEMYBULLETSM 3
#define MT_ENEMY 4
#define MT_ENEMYBULLET 5
#define MT_EXPLOSION 100
#define MT_HUGEEXPLOSION 101
#define MT_STAR 900

struct Moveable {
float x;
float y;
int type;
float dx;
float dy;
float speed;
bool active;
int param1;
};

WNDCLASS wincls;
HWND hwnd;
HDC hdc;

HDC mdc;
HBITMAP mbmp;

HDC bdc;
HBITMAP bbmp;

HDC stardc;
HBITMAP starbmp;

Moveable All[MO_MAX];
bool keys[256];

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int findMoveable() {
for (int i = 0; i < MO_MAX; i++) {
if (All[i].active == false) {
return i;
}
}
return -1;
}

int randomInt(int low, int high) {
return low + int((float)high * rand() / (RAND_MAX + 1.0));
}

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{

srand(GetTickCount());

wincls.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wincls.lpfnWndProc = WndProc;
wincls.cbClsExtra = 0;
wincls.cbWndExtra = 0;
wincls.hInstance = hInstance;
wincls.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wincls.hCursor = LoadCursor(NULL, IDC_ARROW);
wincls.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wincls.lpszMenuName = NULL;
wincls.lpszClassName = "wincls";

if (!RegisterClass(&wincls)) {
MessageBox(NULL, "Unable to create windows class", "Err", MB_OK);
return 0;
}

if (hwnd = CreateWindowEx(0,
"wincls",
"Galactic War",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
700, 700,
HWND_DESKTOP,
NULL,
hInstance,
NULL)) {
hdc = GetDC(hwnd);
} else {
MessageBox(NULL, "Unable to create window", "Err", MB_OK);
return 0;
}

mdc = CreateCompatibleDC(hdc);
mbmp = CreateCompatibleBitmap(hdc, 600, 600);
SelectObject(mdc, mbmp);

bdc = CreateCompatibleDC(hdc);
bbmp = CreateCompatibleBitmap(hdc, 600, 600);
SelectObject(bdc, bbmp);

stardc = CreateCompatibleDC(hdc);
starbmp = CreateCompatibleBitmap(hdc, 600, 600);
SelectObject(stardc, starbmp);

ShowWindow(hwnd, true);
SetWindowText(hwnd, "Galactic War");

MSG msg;
bool done = false;

float mx = 300.0;
float my = 480.0;

for (int j = 0; j < MO_MAX; j++) {
All[j].active = false;
}

HPEN rpen = CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
HPEN blueblob = CreatePen(PS_SOLID, 30, RGB(0, 0, 255));
HPEN orangeblob = CreatePen(PS_SOLID, 10, RGB(255, 128, 0));
HPEN greenblob = CreatePen(PS_SOLID, 30, RGB(0, 255, 0));
HPEN blackpen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));

int starx, stary;

SelectObject(stardc, pen);
for (int j = 0; j < 1000; j++) {
starx = randomInt(0, 600);
stary = randomInt(0, 600);
MoveToEx(stardc, starx, stary, NULL);
LineTo(stardc, starx, stary + 1);
}

HPEN explos[200];
for (int k = 0; k < 200; k++) {
explos[k] = CreatePen(PS_SOLID, k + 1, RGB(255, 200 - k, 0));
}

int score = 0;
int livesleft = 9;
int tillnext = 0;

int lastbullet = 0;
int bside = 1;

int lastEnemySm = 0;
int lastEnemySmBullet = 0;

int lastEnemy = 0;
int lastEnemyBullet = 0;

int a;
char string[100];
char livesleftstring[] = "Lives Left";
char aboutstring[] = " Created by ";

float startop = 0;

long finalwait = 0;

RECT textbox;
textbox.left = 10;
textbox.top = 10;
textbox.right = 580;
textbox.bottom = 50;

HFONT hf = CreateFont(-MulDiv(14, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
SelectObject(mdc, hf);

while (!done) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
done = true;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else {

if (tillnext >= 0) {
tillnext--;
}
if (tillnext == 0) {
mx = 300.0;
my = 480.0;
}

// update title bar
for (int i = 0; i < 100; i++) {
string[i] = 0;
}

string[0] = 'S'; string[1] = 'c'; string[2] = 'o'; string[3] = 'r'; string[4] = 'e';
string[5] = ' '; string[6] = '0'; string[7] = '0'; string[8] = '0'; string[9] = '0';

for (int j = 0; j < score; j++) {
string[9]++;
if (string[9] > '9') {
string[8]++;
string[9] = '0';
if (string[8] > '9') {
string[7]++;
string[8] = '0';
if (string[7] > '9') {
string[6]++;
string[7] = '0';
}
}
}
}

string[10] = ' ';

for (int i = 0; i < 5; i++) {
string[11 + i] = ' ';
}

for (int i = 0; i < 10; i++) {
string[16 + i] = livesleftstring[i];
}

string[26] = ' ';
string[27] = '0' + livesleft;

//SetWindowText(hwnd, string);

// catch all key events

if (keys[VK_RIGHT]) {
mx = mx + (float)0.5;
}
if (keys[VK_LEFT]) {
mx = mx - (float)0.5;
}
if (keys[VK_UP]) {
my = my - (float)0.5;
}
if (keys[VK_DOWN]) {
my = my + (float)0.5;
}

if (keys[VK_CONTROL] && tillnext < 0) {
if (GetTickCount() - lastbullet >= 150) {
lastbullet = GetTickCount();
bside = bside * -1;
a = findMoveable();
if (a >= 0) {
if (All[a].active == false) {
All[a].active = true;
All[a].x = mx + bside * 10;
All[a].y = my - 10;
All[a].dx = 0;
All[a].dy = -1;
All[a].type = MT_BULLET;
}
}
}
}

// create a Enemy 1
if (GetTickCount() - lastEnemySm >= 300 && tillnext < 0) {
lastEnemySm = GetTickCount();
a = findMoveable();
if (a >= 0) {
All[a].active = true;
All[a].x = (float)randomInt(0, 600);
All[a].y = 0;
All[a].dx = 0;
All[a].dy = 0.2f;
All[a].type = MT_ENEMYSM;
}
}

// create bullets for Enemy 1
if (GetTickCount() - lastEnemySmBullet >= 200) {
lastEnemySmBullet = GetTickCount();
for (int i = 0; i < MO_MAX; i++) {
if (All[i].type == MT_ENEMYSM && All[i].active == true) {
if (randomInt(0, 50) > 40) {
a = findMoveable();
All[a].active = true;
All[a].x = All[i].x;
All[a].y = All[i].y + 40;
All[a].dx = (float)randomInt(-10, 20) / 100;
All[a].dy = 0.7;
All[a].type = MT_ENEMYBULLETSM;
}
}
}
}

// clear buffer
BitBlt(mdc, 0, 0, 600, 600, bdc, 0, 0, SRCCOPY);

// draw the stars
startop = startop + 0.05;
BitBlt(mdc, 0, (int)startop, 600, 600, stardc, 0, 0, SRCCOPY);
BitBlt(mdc, 0, (int)startop - 600, 600, 600, stardc, 0, 0, SRCCOPY);
if (startop > 600) {
startop = 0;
}

// draw scene
SelectObject(mdc, pen);

if (tillnext < 0) {

MoveToEx(mdc, (int)mx, (int)my, NULL);
LineTo(mdc, (int)mx + 10, (int)my + 10);

MoveToEx(mdc, (int)mx, (int)my, NULL);
LineTo(mdc, (int)mx - 10, (int)my + 10);

MoveToEx(mdc, (int)mx + 10, (int)my + 10, NULL);
LineTo(mdc, (int)mx + 10, (int)my - 5);

MoveToEx(mdc, (int)mx - 10, (int)my + 10, NULL);
LineTo(mdc, (int)mx - 10, (int)my - 5);
}

// move and draw objects
for (int i = 0; i < MO_MAX; i++) {
if (All[i].active == true) {

if (All[i].type == MT_BULLET) {
SelectObject(mdc, rpen);
MoveToEx(mdc, (int)All[i].x, (int)All[i].y, NULL);
LineTo(mdc, (int)All[i].x, (int)All[i].y + 5);
if (All[i].y < 0) {
All[i].active = false;
}
// does this bullet hit an enemy?
for (int j = 0; j < MO_MAX; j++) {
if (All[j].type == MT_ENEMYSM && All[j].active == true) {
if ((All[j].x - All[i].x) * (All[j].x - All[i].x) + (All[j].y - All[i].y) * (All[j].y - All[i].y) < 400) {
All[j].active = false;
All[i].active = false;
score = score + 1;
a = findMoveable();
if (a >= 0) {
All[a].active = true;
All[a].type = MT_EXPLOSION;
All[a].x = All[i].x;
All[a].y = All[i].y;
All[a].dx = 0;
All[a].dy = 0;
All[a].param1 = 0;
}
break;
}
}
}
}

if (All[i].type == MT_HUGEEXPLOSION) {
if (All[i].param1 <= 199) {
SelectObject(mdc, explos[All[i].param1]);
MoveToEx(mdc, (int)All[i].x, (int)All[i].y, NULL);
LineTo(mdc, (int)All[i].x, (int)All[i].y);
All[i].param1++;
} else {
All[i].active = false;
}
}

if (All[i].type == MT_ENEMYSM) {
SelectObject(mdc, blueblob);
MoveToEx(mdc, (int)All[i].x, (int)All[i].y, NULL);
LineTo(mdc, (int)All[i].x, (int)All[i].y);
if (All[i].y > 600) {
All[i].active = false;
}
}


if (All[i].type == MT_ENEMYBULLETSM) {
SelectObject(mdc, orangeblob);
MoveToEx(mdc, (int)All[i].x, (int)All[i].y, NULL);
LineTo(mdc, (int)All[i].x, (int)All[i].y);
if (All[i].y > 600) {
All[i].active = false;
}
if ((All[i].x - mx) * (All[i].x - mx) + (All[i].y - my) * (All[i].y - my) < 160) {
// we got hit by enemy fire
for (int j = 0; j < MO_MAX; j++) {
All[j].active = false;
}
a = findMoveable();
if (a >= 0) {
All[a].active = true;
All[a].type = MT_HUGEEXPLOSION;
All[a].x = All[i].x;
All[a].y = All[i].y;
All[a].dx = 0;
All[a].dy = 0;
All[a].param1 = 0;
}
livesleft--;
if (livesleft < 0) {
MessageBox(NULL, "You have no more lives left.. you lost", "You lost", MB_OK);
done = true; // you lost the game
}
tillnext = 400;
break;
}
}

if (All[i].type == MT_EXPLOSION) {
if (All[i].param1 <= 99) {
SelectObject(mdc, explos[All[i].param1]);
MoveToEx(mdc, (int)All[i].x, (int)All[i].y, NULL);
LineTo(mdc, (int)All[i].x, (int)All[i].y);
All[i].param1++;
} else {
All[i].active = false;
}
}

All[i].x = All[i].x + All[i].dx;
All[i].y = All[i].y + All[i].dy;
}
}

// show the score
SetBkColor(mdc, RGB(0, 0, 0));
SetTextColor(mdc, RGB(255, 255, 255));
DrawText(mdc, string, -1, &textbox, DT_TOP | DT_LEFT);

// copy buffer to screen
BitBlt(hdc, 0, 0, 600, 600, mdc, 0, 0, SRCCOPY);
}
}

DeleteDC(mdc);
DeleteObject(mbmp);
DeleteDC(bdc);
DeleteObject(bbmp);

// delete the pens
for (int i = 0; i < 200; i++) {
DeleteObject(explos[i]);
}

return (int)msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_KEYDOWN:
keys[wParam] = true;
return 0;
case WM_KEYUP:
keys[wParam] = false;
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hwnd,message,wParam,lParam));
}

Share this post


Link to post
Share on other sites
I compiled your code and ran the program. The only slowdown I noticed was due to the massive amount of stuff you are drawing. If you can reduce the amount of stuff or if you switch to use something like SDL you should notice a speedup.

A separate comment on your code: SelectObject() returns the original GDI object that was selected into the DC. Before you clean up the DC you must select the original object back into the DC otherwise you leak GDI resources. That is, your code would look something like


// This doesnt apply to only fonts, but to bitmaps, pens, etc.
HFONT hfold = (HFONT)SelectObject(mdc, hf);

// Use the DC and such
// Then at the end of the program before destroying mdc

SelectObject(mdc, hfold); // Put the original font back into the DC

Share this post


Link to post
Share on other sites
Yea, I realize now that I do a massive amount of work between the BitBlt() operations. But take this example.. its the same base windows code, but no extraneous work is being done. In VC++ .NET the program locks up. And with Dev C++, when I compile and run it takes a few seconds before a key event causes any movement. Maybe it's just my computer?


#include <windows.h>

WNDCLASS wincls;
HWND hwnd;
HDC hdc;

HDC mdc;
HBITMAP mbmp;

HDC bdc;
HBITMAP bbmp;

bool keys[256];

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
wincls.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wincls.lpfnWndProc = WndProc;
wincls.cbClsExtra = 0;
wincls.cbWndExtra = 0;
wincls.hInstance = hInstance;
wincls.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wincls.hCursor = LoadCursor(NULL, IDC_ARROW);
wincls.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wincls.lpszMenuName = NULL;
wincls.lpszClassName = "wincls";

if (!RegisterClass(&wincls)) {
MessageBox(NULL, "Unable to create windows class", "Err", MB_OK);
return 0;
}

if (hwnd = CreateWindowEx(0, "wincls", "Clockwork", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, HWND_DESKTOP, NULL, hInstance, NULL)) {
hdc = GetDC(hwnd);
} else {
MessageBox(NULL, "Unable to create window", "Err", MB_OK);
return 0;
}

if (mdc = CreateCompatibleDC(hdc)) {
if (mbmp = CreateCompatibleBitmap(hdc, 300, 300)) {
SelectObject(mdc, mbmp);
} else {
MessageBox(NULL, "Unable to create memory bitmap", "Err", MB_OK);
return 0;
}
} else {
MessageBox(NULL, "Unable to create memory device context", "Err", MB_OK);
return 0;
}

if (bdc = CreateCompatibleDC(hdc)) {
if (bbmp = CreateCompatibleBitmap(hdc, 300, 300)) {
SelectObject(bdc, bbmp);
} else {
MessageBox(NULL, "Unable to create memory blank bitmap", "Err", MB_OK);
return 0;
}
} else {
MessageBox(NULL, "Unable to create memory blank device context", "Err", MB_OK);
return 0;
}

ShowWindow(hwnd, true);
SetWindowText(hwnd, "Clockwork");

MSG msg;
bool done = false;

float mx = 150.0f;
float my = 150.0f;

while (!done) {
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
done = true;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else {

if (keys[VK_RIGHT]) {
mx += 0.1f;
}
if (keys[VK_LEFT]) {
mx -= 0.1f;
}
if (keys[VK_DOWN]) {
my += 0.1f;
}
if (keys[VK_UP]) {
my -= 0.1f;
}

BitBlt(mdc, 0, 0, 300, 300, bdc, 0, 0, SRCCOPY);
SetPixel(mdc, (int)mx, (int)my, RGB(255, 255, 255));

BitBlt(hdc, 0, 0, 300, 300, mdc, 0, 0, SRCCOPY);
}
}

return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_KEYDOWN:
keys[wParam] = true;
return 0;
case WM_KEYUP:
keys[wParam] = false;
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return (DefWindowProc(hwnd,message,wParam,lParam));
}

Share this post


Link to post
Share on other sites
Ah! I see now. Originally I tested the program on Windows XP and it worked. I tried it on Win2K and now I see the program lock up.

So now comes my second answer. XP must have made changes in the way it draws things since GDI on XP is much faster than GDI on 2000. I believe the reason your program locks up is because you're generating requests to blit faster than Windows can handle them creating a backlog . If you use Sleep() after the BitBlt()s as you were before but with a much higher number (say, 100 for example) the problem does not arise since Windows has time to process those requests.

You might not want to hear this but, in my humble opinion, the real solution is to not use GDI. It was never meant to be used in this manner. Again, try using SDL or some other library that was designed to be used as often as possible.

Share this post


Link to post
Share on other sites

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

Sign in to follow this