Sign in to follow this  
steve coward

Release version graphics bug - need help!

Recommended Posts

My game is an isometric tile based RTS game written in C++ MS 2005 Express. The graphics engine (dx9 based) uses 5 different vertex arrays of varying sizes and two vertex buffers - 1 for triangles and 1 for lines. My game has a graphics bug that occurs only when compiled with /MD or /MT flags. The bug is not present when /MDd or /MTd is used. When the bug is present, the problem disappears if I limit the number of vertices contained in the 3rd vertex array - this array holds vertices of shroud or fog tiles - by revealing some portion of the map through unit movement. The bug causes extra, random polygons to be drawn to the screen. The polygons appear to be from my font textures only. Also, at short intervals, certain polygons are not drawn - such as the frame around the game window. I am at a loss at how to begin to debug this problem. It seems like some sort of vertex overflow problem, but I am unable to see any problems. I have included main code for the creation of the vertex buffers and for drawing the vertices. I am not sure what additional code snippets might be useful for others to help. Any ideas would be greatly appreciated. thanks
HRESULT CD3DApplication::CreateVertexBuffers()
{
	HRESULT hr;
	int i;

	AccessCriticalSection(g_csDrawDatabase1, true, CRITICALSECTION_DRAW, __LINE__, __FILE__);

	InitDrawQueue();

	// m_viewIsoTileLength = number x tiles in view
	// m_viewIsoTileWidth  = number y tiles in view
	//
	// terrain vertices drawn first for each draw iteration.
	// shroud vertices drawn towards very end of draw iteration.
	// The contents of these two arrays tend to change infrequently, they are not necessarily updated each draw iteration.
	m_vertexArrayType[TERRAIN_VERTICES] = _TLVERTEX;
	m_vertexArrayType[SHROUD_VERTICES] = _TLVERTEX;
	m_vertexArrayType[GENERIC_VERTICES] = _TLVERTEX;
	m_vertexArrayType[LINE_VERTICES] = _LINEVERTEX;
	m_vertexArrayType[FRAME_VERTICES] = _TLVERTEX;
	m_vertexArrayType[OVERLAY_VERTICES] = _TLVERTEX;

	//  drawing loop looks like
	//	for (y=ptItiStart.y;y<=ptItiEnd.y;y++) {
	//		for (x=max(0,ptItiStart.x-1);x<=ptItiEnd.x;x++) {
	//		}
	//	}
	m_vertexArraySize[TERRAIN_VERTICES] = (m_viewIsoTileLength+3) * (m_viewIsoTileWidth+1) * NUM_VERTICES_PER_QUAD * 4;
	m_vertexArraySize[SHROUD_VERTICES]	= (m_viewIsoTileLength+3) * (m_viewIsoTileWidth+1) * NUM_VERTICES_PER_QUAD;
	m_vertexArraySize[GENERIC_VERTICES] = MAX_GENERIC_QUADS_PERDRAW * NUM_VERTICES_PER_QUAD;
	m_vertexArraySize[LINE_VERTICES] = MAX_LINE_VERTICES_PERDRAW * NUM_VERTICES_PER_LINE;
	m_vertexArraySize[FRAME_VERTICES] = MAX_FRAME_QUADS_PERDRAW * NUM_VERTICES_PER_QUAD;
	m_vertexArraySize[OVERLAY_VERTICES] = MAX_OVERLAY_VERTICES_PERDRAW * NUM_VERTICES_PER_QUAD;
		
	m_vertexArrayStatic[TERRAIN_VERTICES] = 1;
	m_vertexArrayStatic[SHROUD_VERTICES] = 1;
	m_vertexArrayStatic[GENERIC_VERTICES] = 0;
	m_vertexArrayStatic[LINE_VERTICES] = 0;
	m_vertexArrayStatic[FRAME_VERTICES] = 0;
	m_vertexArrayStatic[OVERLAY_VERTICES] = 0;


	LPAPP_TL_VERTEX pTLVertices;
	LPAPP_LINE_VERTEX pLineVertices;
	
	for (i=0;i<NUM_VERTEX_ARRAYS;i++) {
		m_vertexArrayNum[i] = 0;
		
		if (m_vertexArrayType[i] == _TLVERTEX) {
			pTLVertices = (LPAPP_TL_VERTEX)m_mapVertexArray[i];
			
			if (pTLVertices == NULL) {
				pTLVertices = (APP_TL_VERTEX*) new APP_TL_VERTEX[m_vertexArraySize[i]];
				m_mapVertexArray[i] = (void*)pTLVertices;
			}
		}
		else {
			pLineVertices = (LPAPP_LINE_VERTEX)m_mapVertexArray[i];
			
			if (pLineVertices == NULL) {
				pLineVertices = (APP_LINE_VERTEX*) new APP_LINE_VERTEX[m_vertexArraySize[i]];
				m_mapVertexArray[i] = (void*)pLineVertices;
			}
		}

		DPRINTF("Vertex Array %d Size: %d\n", i, m_vertexArraySize[i]);
	}
	
	// one vertex buffer for each type of vertex used by the app
	// they are sized equal to the largest vertex array of that type
	hr = m_pd3dDevice->CreateVertexBuffer(sizeof(APP_TL_VERTEX) * m_vertexArraySize[TERRAIN_VERTICES], m_dwVBUsage,
		D3DFVF_TLVERTEX, D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL);
	if (FAILED(hr))
		DisplayErrorMsg(hr, MSG_NONE);
	hr = m_pd3dDevice->CreateVertexBuffer(sizeof(APP_LINE_VERTEX) * m_vertexArraySize[LINE_VERTICES], m_dwVBUsage,
		D3DFVF_LINEVERTEX, D3DPOOL_DEFAULT, &m_pLineVertexBuffer, NULL);
	if (FAILED(hr))
		DisplayErrorMsg(hr, MSG_NONE);

	m_pd3dDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(APP_TL_VERTEX));

	AccessCriticalSection(g_csDrawDatabase1, false, CRITICALSECTION_DRAW, __LINE__, __FILE__);

	return(hr);
}

HRESULT CD3DApplication::DrawQueue()
{
	HRESULT ddrval;
	int vertexArray;
	bool bAdded;
	bool bDone;
	bool bAtEnd;
	DWORD numArrayVertices;
	int numEntriesProcessed;
	unitID ID;
	LISTDRAWQUEUE* pListDrawQueue;
	LISTDRAWQUEUE::iterator itr, begin, end;
	LPAPP_QUEUE_VERTEX lpQueueEntry;
	LPAPP_QUEUE_VERTEX lpQueueNextEntry;

	
	ddrval = D3D_OK;
	  
	AccessCriticalSection(g_csDrawDatabase1, true, CRITICALSECTION_DRAW, __LINE__, __FILE__);

	//TANK_ASSERT((m_vertexArrayNum[TERRAIN_VERTICES] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[GENERIC_VERTICES] == 0), "");
	//TANK_ASSERT((m_vertexArrayNum[SHROUD_VERTICES] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[FRAME_VERTICES] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[LINE_VERTICES] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[OVERLAY_VERTICES] == 0), "");
	
	// this for loop enforces vertex type draw order
	for (vertexArray=0;vertexArray<NUM_QUEUE_TYPES;vertexArray++) {

		numArrayVertices = 0;
		numEntriesProcessed = 0;

		if ((m_vertexArrayStatic[vertexArray] == 1) && (m_vertexArrayNum[vertexArray] > 0)) {
			// nonempty static array of vertices

			pListDrawQueue = (LISTDRAWQUEUE*)m_mapVertexQueue[vertexArray];
			ddrval = DrawArrayVertices(vertexArray, (*pListDrawQueue->begin())->lpSrcTexture, (*pListDrawQueue->begin())->lpDestTexture, (*pListDrawQueue->begin())->ID, 0, m_vertexArrayNum[vertexArray]);
			TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
		}
		else if ((pListDrawQueue = (LISTDRAWQUEUE*)m_mapVertexQueue[vertexArray]) != NULL) {
			begin = pListDrawQueue->begin();
			end = pListDrawQueue->end();
			for (itr=begin;itr!=end;itr++) {

				lpQueueEntry = *itr;

				if (lpQueueEntry->vertexType != vertexArray) {
					DPRINTF("DrawQueue type mismatch\n");
				}
					
				itr++;
				bAtEnd = (itr == end);
				if (bAtEnd) {
					lpQueueNextEntry = NULL;
				}
				else {
					lpQueueNextEntry = *itr;
				}
				itr--;

				ID = lpQueueEntry->ID;
				
				if (m_vertexArrayType[vertexArray] == _LINEVERTEX) {
					bAdded = AddD3DLine(
						CCltPoint(lpQueueEntry->rcDest.left, lpQueueEntry->rcDest.top),
						CCltPoint(lpQueueEntry->rcDest.right, lpQueueEntry->rcDest.bottom),
						lpQueueEntry->vertexColor,
						ID
						);
				}
				else {	
					bAdded = AddD3DQuad(
						lpQueueEntry->vertexType,
						lpQueueEntry->lpSrcTexture,
						&lpQueueEntry->srcVertices,
						&lpQueueEntry->rcDest,
						lpQueueEntry->vertexColor,
						lpQueueEntry->flags,
						lpQueueEntry->rotate,
						ID
						);
				}

				numEntriesProcessed += bAdded;
				
				bDone = bAtEnd && bAdded;
				
				if (bAdded) {
					
					if (m_vertexArrayStatic[vertexArray] == 0) {
						m_listFreeQueueVertices.push_back(lpQueueEntry);
					}

					if (m_vertexArrayType[vertexArray] == _LINEVERTEX) {
						numArrayVertices += NUM_VERTICES_PER_LINE;
					}
					else {
						numArrayVertices += NUM_VERTICES_PER_QUAD;
					}
					
					if (bDone || (lpQueueEntry->lpSrcTexture != lpQueueNextEntry->lpSrcTexture) || (lpQueueEntry->lpDestTexture != lpQueueNextEntry->lpDestTexture) || (numArrayVertices == m_vertexArraySize[vertexArray])) {
						TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
						ddrval = DrawArrayVertices(vertexArray, lpQueueEntry->lpSrcTexture, lpQueueEntry->lpDestTexture, ID, 0, numArrayVertices);
						TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
						//DPRINTF("DrawArrayVertices type:%d num:%d\n", vertexArray, numArrayVertices);

						TANK_ASSERT((numArrayVertices < m_vertexArraySize[vertexArray]) || (m_vertexArrayStatic[vertexArray] != 1) || bDone, "Static arrays must be sized to draw all static vertices in one pass.");

						d3dPerfStats[NUM_TEXTURE_CHANGES] += !bDone;
						numArrayVertices = 0;
						if (m_vertexArrayStatic[vertexArray] != 1) {
							m_vertexArrayNum[vertexArray] = 0;
						}
					}
				}
				else {
					ddrval = DrawArrayVertices(vertexArray, lpQueueEntry->lpSrcTexture, lpQueueEntry->lpDestTexture, ID, 0, numArrayVertices);
					TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
					numArrayVertices = 0;
					if (m_vertexArrayStatic[vertexArray] != 1) {
						m_vertexArrayNum[vertexArray] = 0;
					}
					else {
						DPRINTF("should not be here.\n");
					}

					itr--;
				}
			}

			if (m_vertexArrayStatic[vertexArray] == 0) {
				pListDrawQueue->clear();
			}
		}
	}

	//TANK_ASSERT((m_vertexArrayNum[0] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[1] == 0), "");
	//TANK_ASSERT((m_vertexArrayNum[2] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[3] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[4] == 0), "");
	TANK_ASSERT((m_vertexArrayNum[5] == 0), "");

	AccessCriticalSection(g_csDrawDatabase1, false, CRITICALSECTION_DRAW, __LINE__, __FILE__);

	return(ddrval);
}

HRESULT CD3DApplication::DrawArrayVertices(int vertexID, LPDIRECT3DTEXTURE9 lpSrcTexture, LPDIRECT3DTEXTURE9 lpDestTexture, unitID ID, DWORD startVertex, DWORD numVertices)
{
	HRESULT ddrval;
	LPDIRECT3DSURFACE9 lpD3DSurfDest = NULL;
	LPDIRECT3DSURFACE9 pBackBuffer = NULL;

	ddrval = D3D_OK;
	
	if (lpDestTexture != NULL) {							
		ddrval = m_pd3dDevice->GetRenderTarget(0, &pBackBuffer);
		TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");

		ddrval = lpDestTexture->GetSurfaceLevel(0, &lpD3DSurfDest);
		TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
		
		ddrval = m_pd3dDevice->SetRenderTarget(0, lpD3DSurfDest);
		TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
	}
	
	// issue do not special case this - instead track pipeline state changes
	if ((ID == MINIMAPSHROUD_ID))
	{	
		m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA);
		m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
		// for white shroud:
		// white areas of shroud texture show as white, black as transparent
		//m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR);
		//m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
	}
	
	TANK_ASSERT(startVertex <= m_vertexArraySize[vertexID], "");
	
	if (vertexID == LINE_VERTICES) {
		ddrval = BlitBatchLinesD3D(vertexID, startVertex, numVertices);
	}
	else {
		ddrval = BlitBatchTrianglesD3D(lpSrcTexture, vertexID, startVertex, numVertices);
	}
	TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
	
	if ((ID == MINIMAPSHROUD_ID))
	{	
		m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
		m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	}
	
	if (lpDestTexture != NULL) {		
		ddrval = m_pd3dDevice->SetRenderTarget(0, pBackBuffer);
		TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");

		pBackBuffer->Release();
		
		ddrval = lpD3DSurfDest->Release();
		TANK_ASSERT(!ShowError(ddrval, __LINE__, __FILE__), "");
	}
	
	return(ddrval);
}

HRESULT CD3DApplication::BlitBatchTrianglesD3D(LPDIRECT3DTEXTURE9 texture, int iActiveVertex, int iStartVertex, DWORD numVertices)
{
	HRESULT ddrval;
	LPAPP_TL_VERTEX pVertices;
	LPAPP_TL_VERTEX vertices;
	DWORD dwLockFlags;
	
	ddrval = S_OK;
	
	TANK_ASSERT(numVertices <= m_vertexArraySize[iActiveVertex], "need larger vertex buffer\n");
	TANK_ASSERT(numVertices%NUM_VERTICES_PER_TRIANGLE == 0, "number of vertices must be multiple of three\n");
	
	if (numVertices > 0) {
		//DPRINTF("batch draw\n");

		pVertices = (LPAPP_TL_VERTEX)m_mapVertexArray[iActiveVertex];

		if ((m_dwFirstValidVBEntry + numVertices >= m_vertexArraySize[iActiveVertex])) {
			//DPRINTF("discarding vertices next Entry (AGP) %d start vertex %d new vertices %d \n", m_dwFirstValidVBEntry, dwStartVertex, dwNumVertices);
			dwLockFlags = D3DLOCK_DISCARD;
			m_dwFirstValidVBEntry = 0;
			d3dPerfStats[NUM_DISCARD_LOCKS]++;
		}
		else {
			dwLockFlags = D3DLOCK_NOOVERWRITE;
			d3dPerfStats[NUM_NOOVERWRITE_LOCKS]++;
		}
		
		//Set texture
		ddrval = m_pd3dDevice->SetTexture (0, texture);
		TANK_ASSERT(ddrval == 0, "error");
		ddrval = m_pd3dDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(APP_TL_VERTEX));
		TANK_ASSERT(ddrval == 0, "error");
		ddrval = m_pd3dDevice->SetVertexShader(NULL);
		TANK_ASSERT(ddrval == 0, "error");
		ddrval = m_pd3dDevice->SetFVF(D3DFVF_TLVERTEX);
		TANK_ASSERT(ddrval == 0, "error");
		
		ddrval = m_pVertexBuffer->Lock(m_dwFirstValidVBEntry * sizeof(APP_TL_VERTEX), numVertices * sizeof(APP_TL_VERTEX), (void**)&vertices, dwLockFlags);
		TANK_ASSERT(ddrval == 0, "error");
		memcpy(vertices, pVertices+iStartVertex, numVertices * sizeof(APP_TL_VERTEX));
		//Unlock the vertex buffer
		ddrval = m_pVertexBuffer->Unlock();
		TANK_ASSERT(ddrval == 0, "error");
		
		//DPRINTF("drawing %d vertices\n", dwNumVertices);
		ddrval = m_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLELIST, m_dwFirstValidVBEntry, numVertices/NUM_VERTICES_PER_TRIANGLE);
		TANK_ASSERT(ddrval == 0, "error");

		d3dPerfStats[NUM_DRAW_PRIMITIVE_CALLS]++;
		d3dPerfStats[iActiveVertex]++;
		d3dPerfStats[iActiveVertex+NUM_VERTEX_ARRAYS] += (m_vertexArraySize[iActiveVertex] - numVertices);
		d3dPerfStats[iActiveVertex+2*NUM_VERTEX_ARRAYS] = max(d3dPerfStats[iActiveVertex+2*NUM_VERTEX_ARRAYS], numVertices);
		
		m_dwFirstValidVBEntry += numVertices;
	}

	return(ddrval);
}

Share this post


Link to post
Share on other sites
A few points:
1. Are you using the Debug Runtimes? Any relevant debug output from them?
2. Checking return values against 0 isn't the correct way to check for D3D errors - that's only checking 1 of the 2 billion possible success codes. The correct way is to use the SUCCEEDED or FAILED macros.
3. I hope that your asserts do something in release mode - if IDirect3DVertexBuffer9::Lock fails in release (Which it can do for several reasons), you'll end up using a bad pointer and probably crashing your app.
4. The m_mapVertexArray variable looks suspicious - are you absolutely sure that you're not overrunning that array?
5. Are you using D3D from multiple threads? If so, are you passing the D3DCREATE_MULTITHREADED flag to CreateDevice()?

Share this post


Link to post
Share on other sites
Thanks for the reply.

I can reply to a few of the points quickly.

2. I changed my code to use my ShowError() routine (which
does use FAILED()) for all cases of "ddrval == 0".

4. mapVertexArray is a map of VertexArrays, not an array.

typedef std::map<WORD, void*> MAPVertexArray;
MAPVertexArray m_mapVertexArray;

It always holds exactly 6 void pointers to vertex arrays.

Does this ease your concerns?

5. I use threads only in my path search algorithm.
No part of my graphics engine is threaded.

Share this post


Link to post
Share on other sites
Quote:
Original post by steve coward
4. mapVertexArray is a map of VertexArrays, not an array.

typedef std::map<WORD, void*> MAPVertexArray;
MAPVertexArray m_mapVertexArray;

It always holds exactly 6 void pointers to vertex arrays.
Sorry, I meant the second part of the map, the void* arrays. If you're overrunning one of them, all sorts of weird things could happen.

Share this post


Link to post
Share on other sites
1. Are you using the Debug Runtimes? Any relevant debug output from them?

I am now using the Debug Runtimes. I get the following messages:

Direct3D9: :====> ENTER: DLLMAIN(011fe6e0): Process Attach: 00000098, tid=000005b8
Direct3D9: :====> EXIT: DLLMAIN(011fe6e0): Process Attach: 00000098
Direct3D9: (INFO) :Direct3D9 Debug Runtime selected.
Direct3D9: (WARN) :No SW device has been registered. GetAdapterCaps fails.
D3D9 Helper: IDirect3D9::GetDeviceCaps failed: D3DERR_NOTAVAILABLE
'TankBattle.exe': Loaded 'C:\WINDOWS\SYSTEM32\d3dref9.dll', No symbols loaded.
Direct3D9: (INFO) :======================= Hal HWVP Pure device selected

Direct3D9: (INFO) :HalDevice Driver style 9

Direct3D9: :DoneExclusiveMode
Direct3D9: (INFO) :Failed to create driver indexbuffer


3. I hope that your asserts do something in release mode.

Yes they do print an error message. None of the asserts in my code
are triggered by this bug.



4. The m_mapVertexArray variable looks suspicious - are you absolutely sure that you're not overrunning that array?

Mot absolutely sure so I need to look at this in more detail.
However, I do have several asserts monitoring the number of entries in
each vertex array.

Share this post


Link to post
Share on other sites
I am now compiling with /RTC options.
This is the first time that I have used it.
Thank you for pointing that out to me.
Amazingly only 1 inconsequential problem was
uncovered in the process.

So I have yet to make headway against the original bug.

Share this post


Link to post
Share on other sites
That info message you got from the debug runtimes according to this site is caused by the card not supporting index buffers in video ram.

I also noticed that you're creating your non dynamic vertex buffers in the default pool. It's usually best to create as many resources (textures, vertex buffer, and index buffers) as possible in the managed pool so that D3D will handle the allocation of video card memory for you. It also makes lost devices easier to handle.

Share this post


Link to post
Share on other sites
I have narrowed the occurrence of the bug to builds with both
/O2 and /MT compiler switches present. The other 3 combinations
do not exhibit the buggy behavior. Does anybody have any ideas
what may be the problem? Is this possibly a compiler issue?
Are these two switches incompatible?


- fails
/O2 /D "WIN32" /D "_WINDOWS" /D "_VC80_UPGRADE=0x0600" /D "_MBCS"
/GF /Gm /EHsc /MT /Gy /Fp".\Debug/TankBattle.pch" /Fo".\Debug/"
/Fd".\Debug/" /W3 /nologo /c /Zi /TP /wd4117 /errorReport:prompt

- works
/Od /D "WIN32" /D "_WINDOWS" /D "_VC80_UPGRADE=0x0600" /D "_MBCS"
/GF /Gm /EHsc /MT /Gy /Fp".\Debug/TankBattle.pch" /Fo".\Debug/"
/Fd".\Debug/" /W3 /nologo /c /Zi /TP /wd4117 /errorReport:prompt

- works
/O2 /D "WIN32" /D "_WINDOWS" /D "_VC80_UPGRADE=0x0600" /D "_MBCS"
/GF /Gm /EHsc /MTd /Gy /Fp".\Debug/TankBattle.pch" /Fo".\Debug/"
/Fd".\Debug/" /W3 /nologo /c /Zi /TP /wd4117 /errorReport:prompt

- works
/Od /D "WIN32" /D "_WINDOWS" /D "_VC80_UPGRADE=0x0600" /D "_MBCS"
/GF /Gm /EHsc /MTd /Gy /Fp".\Debug/TankBattle.pch" /Fo".\Debug/"
/Fd".\Debug/" /W3 /nologo /c /Zi /TP /wd4117 /errorReport:prompt

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