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 = 0;
if (m_vertexArrayType == _TLVERTEX) {
pTLVertices = (LPAPP_TL_VERTEX)m_mapVertexArray;
if (pTLVertices == NULL) {
pTLVertices = (APP_TL_VERTEX*) new APP_TL_VERTEX[m_vertexArraySize];
m_mapVertexArray = (void*)pTLVertices;
}
}
else {
pLineVertices = (LPAPP_LINE_VERTEX)m_mapVertexArray;
if (pLineVertices == NULL) {
pLineVertices = (APP_LINE_VERTEX*) new APP_LINE_VERTEX[m_vertexArraySize];
m_mapVertexArray = (void*)pLineVertices;
}
}
DPRINTF("Vertex Array %d Size: %d\n", i, m_vertexArraySize);
}
// 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);
}