Jump to content
  • Advertisement
Sign in to follow this  
Majikkan

Crashes...but only on Nvidia? (SOLVED)

This topic is 4884 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been reading the "Tricks of the Windows Gaming Gurus" book and writing my own examples as I go. I made a small fullscreen DirectDraw program that makes it look like the user is moving through a starfield. Something is wrong with it. It runs fine for me (ATI 9800 Pro). It runs fine for a friend (ATI 9600), though she gets occasional lurches and hiccups. It does not seem to run for any of my friends who own Nvidia cards. They describe seeing some stars, and then a crash. I cannot figure out what's wrong, but clearly something is. The problem isn't the buffering, as this problem has existed before I even got to using buffering of any kind.
#define WIN32_LEAN_AND_MEAN
#define INITGUID

#include <windows.h>
#include <iostream>
#include <math.h>
#include <memory.h>
#include <ddraw.h>

#define KEYDOWN(vk_code)	((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define RNDF(X)		(double)rand()/((double)RAND_MAX+(double)1)*(X)

#define WINDOW_CLASS_NAME "WINCLASS1"

#define FRAMERATE		30
#define XSIZE		800
#define YSIZE		600
#define BPP			8
#define NUM_STARS		1250
#define SPEED		1
#define CR_WHITE		255
#define CR_BLACK		0

const int shaderange (XSIZE*XSIZE + YSIZE*YSIZE);

using namespace std;

class STAR
{
public:
	STAR();
	inline void PlotStar(UCHAR color, UCHAR *buffer, int pitch);
	inline void NormalizeStar(int xbound, int ybound);
	inline void MoveStar(float x_pos, float y_pos);
	float x;
	float y; 

private:
	int xnorm;
	int ynorm;
};

inline STAR::STAR()
{
	x = float(rand()%XSIZE);
	y = float(rand()%YSIZE);
}

inline void STAR::PlotStar(UCHAR color, UCHAR *buffer, int pitch)
{
	buffer[xnorm + ynorm*pitch] = color;
}

inline void STAR::NormalizeStar(int xbound, int ybound)
{
	xnorm = (int)x*xbound/XSIZE;
	ynorm = (int)y*ybound/YSIZE;
}


inline void STAR::MoveStar(float x_pos, float y_pos)
{
	x = x_pos;
	y = y_pos;
}

// GLOBALS
HINSTANCE		hinstance_app		= NULL;	// Instance handle.
HWND			main_window_handle		= NULL; // Window handle.
HDC			hdc				= NULL; // Device Context handle.
LPDIRECTDRAW		lpdd				= NULL; // DirectDraw 1 object.
LPDIRECTDRAW4	lpdd4				= NULL; // DirectDraw 6 object.
PALETTEENTRY		palette[256];			// 8 bit palette. Unneeded.
LPDIRECTDRAWPALETTE		lpddpal		= NULL; // Palette object.
DDSURFACEDESC2	ddsd;					// Surface description.
LPDIRECTDRAWSURFACE4	lpddsprimary		= NULL;// Primary surface object.
LPDIRECTDRAWSURFACE4	lpddsbackbuffer	= NULL; // Backbuffer surface object.
STAR				starfield[NUM_STARS]; 
int				windowclosed		= 0;

// PROTOTYPES
BOOL Star_Init(void);
BOOL Star_Main(void);
BOOL inline Star_Shutdown(void);
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow)
{
    WNDCLASSEX winclass;
	HWND hwnd;
	MSG msg;

// Declare winclass.

	winclass.cbSize = sizeof(WNDCLASSEX);
	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	winclass.lpfnWndProc = WinProc;
	winclass.cbClsExtra = 0;
	winclass.cbWndExtra = 0;
	winclass.hInstance = hinstance;
	winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winclass.lpszMenuName = NULL;
	winclass.lpszClassName = WINDOW_CLASS_NAME;
	winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

// save and register.

	hinstance_app = hinstance;
	if (!RegisterClassEx(&winclass))
		return(0);

// open the window.

	if (!(hwnd = CreateWindowEx(
		NULL,				// Extended style
		WINDOW_CLASS_NAME,		// Class
		"Starfield",			// Title
		WS_POPUP | WS_VISIBLE,	// Style
		0,0,				// Position
		XSIZE,YSIZE,			// Size
		NULL,				// Parent handle
		NULL,				// Menu handle
		hinstance,			// Instance
		NULL)))			// Extra Parms
	return(0);

	main_window_handle = hwnd;

	if(!Star_Init())
		Star_Shutdown();

// main event loop

	while(TRUE)
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		Star_Main();
	}
	Star_Shutdown();
	return((int)msg.wParam);
}

BOOL Star_Init()
{

// Create DirectDraw4 object.

	if(FAILED(DirectDrawCreate(NULL, &lpdd, NULL)))
		return(0);
	if(FAILED(lpdd->QueryInterface(IID_IDirectDraw7, (LPVOID *)&lpdd4)))
		return(0);
	lpdd->Release();
	lpdd = NULL;

	if(FAILED(lpdd4->SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE | DDSCL_ALLOWMODEX | DDSCL_ALLOWREBOOT)))
		return(0);
	if(FAILED(lpdd4->SetDisplayMode(XSIZE, YSIZE, BPP, 0, 0)))
		return(0);

// Make a palette.

	for (int i = 0; i < 256; i++)
	{
		palette.peRed		= i;
		palette.peGreen		= i;
		palette.peBlue		= i;
		palette.peFlags		= PC_NOCOLLAPSE;
	}

	if(FAILED(lpdd4->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, palette, &lpddpal, NULL)))
		return(0);

// Now we need to generate a surface.
// First we clear out the array just to be safe.

	memset(&ddsd, 0, sizeof(ddsd));

// Then we set the parameters we need to create the surface.

	ddsd.dwSize				= sizeof(ddsd);
	ddsd.dwFlags			= DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	ddsd.ddsCaps.dwCaps		= DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
	ddsd.dwBackBufferCount	= 1;

// Create the surface.

	if(FAILED(lpdd4->CreateSurface(&ddsd, &lpddsprimary, NULL)))
		return(0);

// Attach the backbuffer.

	ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
	if(FAILED(lpddsprimary->GetAttachedSurface(&ddsd.ddsCaps, &lpddsbackbuffer)))
		return(0);
    
// Attach the palette.

	if(FAILED(lpddsprimary->SetPalette(lpddpal)))
		return(0);
	return(1);
}

BOOL Star_Main()
{

// This quits to the desktop.

	if (windowclosed)
		return(0);
	if (KEYDOWN(VK_ESCAPE))
	{
		SendMessage(main_window_handle,WM_CLOSE,0,0);
		windowclosed = 1;
		return(1);
	}

	DWORD start_time = GetTickCount();

	memset(&ddsd, 0, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);

// Lock the backbuffer.

	if(FAILED(lpddsbackbuffer->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)))
		return(0);

	UCHAR *back_buffer = (UCHAR *)ddsd.lpSurface;

// Clear the backbuffer.

	UCHAR * bufferptr = back_buffer;
	if(ddsd.lPitch != XSIZE)
	{	// NON-LINEAR MEMORY MODEL
		for(int i = 0; i < YSIZE; i++)
		{
			memset(bufferptr, 0, XSIZE);
			bufferptr += ddsd.lPitch;
		}
	}
	else
	{	// LINEAR MEMORY MODEL
		memset(bufferptr, 0, XSIZE*YSIZE);
	}

// Calculate motion.

	const float xcenter = XSIZE/2;
	const float ycenter = YSIZE/2;
	float r, x, y, theta;
	int pitch = ddsd.lPitch;
	UCHAR *video_buffer = (UCHAR *)ddsd.lpSurface;
	for (int i = 0; i<NUM_STARS; i++)

	{
		x			= starfield.x - xcenter;
		y			= starfield.y - ycenter;
		theta		       = atan(y/x);
		r			= (x/abs(x))*(sqrt((x*x)+(y*y)) + SPEED);

// Move star coordinates.
		x			= r*cos(theta) + xcenter; 
		y			= r*sin(theta) + ycenter;

// Replace stars that have drifted off the screen.

		if (x > XSIZE || x < 0 || y > YSIZE || y < 0)
		{
			theta = (float)(RNDF(360));
			r = (float)(RNDF(XSIZE/4));
			x = XSIZE/2 - cos(theta)*r;
			y = YSIZE/2 - sin(theta)*r;
		}

// Animate a frame.

		int color = sqrt(262144*r*r/shaderange);	// Normalizes color from 0 (middle of screen)
                    			// to 255 (corners of screen) 													

		starfield.MoveStar(x, y);			// Change the coordinates.
		starfield.NormalizeStar(XSIZE, YSIZE);	// Adjust them to the screen size.
		starfield.PlotStar(color,back_buffer, pitch);	// And print them to the buffer.
	}

// Pageflip

	if(FAILED(lpddsbackbuffer->Unlock(NULL)))
		return(0);

	while(FAILED(lpddsprimary->Flip(NULL, DDFLIP_WAIT)));

// Sit still for a frame.

	while(start_time - GetTickCount() < 1000/FRAMERATE) Sleep(0);
}

BOOL Star_Shutdown()
{
	if(lpddpal)
	{
		lpddpal->Release();
		lpddpal = NULL;
	}
	if(lpddsbackbuffer)
	{
		lpddsbackbuffer->Release();
		lpddsbackbuffer = NULL;
	}
	if(lpddsprimary)
	{
		lpddsprimary->Release();
		lpddsprimary = NULL;
	}
	if(lpdd4)
	{
		lpdd4->Release();
		lpdd4 = NULL;
	}
	return(1); 
}
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	PAINTSTRUCT ps;
	switch(msg)
	{
	case WM_CREATE:
		{
			return(0);
		} break;
	case WM_PAINT:
		{
			BeginPaint(hwnd, &ps);
			EndPaint(hwnd, &ps);
			return(0);
		} break;
	case WM_CLOSE:
		{
			PostMessage(hwnd, WM_DESTROY, NULL, NULL);
			return(0);
		} break;
	case WM_DESTROY:
		{
			PostQuitMessage(0);
			return(0);
		} break;
	default: break;
	}
	return(DefWindowProc(hwnd, msg, wparam, lparam));
}



[Edited by - Majikkan on June 7, 2005 12:02:21 PM]

Share this post


Link to post
Share on other sites
Advertisement
Best thing to do it take the workspace over to your friends place, and debug it on their machine. These bugs are never fun, I'm afraid.

Matt Hughson

Share this post


Link to post
Share on other sites
How long do they have to wait for the crash? Because I just built that source code with MSVC++, and I waited for over 30 seconds and it never crashed, and I have a GeForce 5200.

Share this post


Link to post
Share on other sites
The plot code looks ok, but:

On your check if a star is offscreen, you should check:


if ( ( x >= XSIZE )
|| ( x < 0 )
|| ( y >= YSIZE )
|| ( y < 0 ) )



Otherwise you may end drawing a pixel inside the pitch area (which some cards do not like at all).

Share this post


Link to post
Share on other sites
erm, can you explain "pitch area" please? I thought cards clipped to the screen automatically, or is that something OpenGL does?

Share this post


Link to post
Share on other sites
Assuming in reference to buffer[xnorm + ynorm*pitch] = color;, then no, you're playing around with raw memory. As always, using a bad index on an array can cause your program to crash, or corrupt memory and cause your program to seem to work and crash later in a seemingly unrelated piece of code, creating for you a debugging nightmare.

Share this post


Link to post
Share on other sites
The pitch area is the area to the right of your screen buffer if the pitch is bigger than the width of your surface.
I noticed that even when it would be safe you shouldn't write inside there. With the old if-test you could write one pixel too far right or too far out of the bottom.

Share this post


Link to post
Share on other sites
The crash normally occurs "nearly instantly". After some stars are on the screen, but before they've really had a chance to move...and now I understand why.

*groan* Computers count from 0, not 1. Dummy.

Thanks for pointing out the problem, guys. Lesson learned. I'll fix this, throw it at my friends, and maybe not crash their computers anymore. :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

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

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!