Dynamic VB Crash Problem

Started by
12 comments, last by Agony 19 years, 8 months ago
I know that this is a problem stemming from my own pathetic ignorance of dynamic vertex buffers, but I've tried a hundred different things and searched these forums for answers and can't seem to get this to work. I'm trying to fix my missile trails, which are dynamically created vertex buffers that create a sort of "ribbon" that follows my missiles. I've had the code working beautifully on all nVidia cards for a while now, and I'm trying to fix a crash-to-desktop problem I'm having on most ATI cards. Here is the code that works on nVidia cards, starting with the VB creation:
// Create the vertex buffer.
if (FAILED(g_pD3Ddevice->CreateVertexBuffer(MAXAMMO * MAX_VAPORTRAIL_SECTIONS * 2 * sizeof(VAPOR_VERTEX), 0, D3DFVF_VAPORVERTEX, D3DPOOL_DEFAULT, &pr_vaporVB)))
{
	MessageBox(NULL, "Vapor trail vertex buffer creation failure.", "Error", MB_OK);
}
And now the actual VB render code:
// Create all of the vertices for the vertex buffer
for (i = 0; i < MAXAMMO; i++)
{
	if (pr_is_active)
	{
		for (int c = 0; c < pr_maxVaporTrailSections; c++)
		{
			D3DXVECTOR3 tempvector, laservert0, laservert1;
			D3DXVECTOR3 laserEnd0;
			D3DXVECTOR3 laserEnd1;
			if (c == 0)
			{
				laserEnd0 = tail_positions[c+1];
				laserEnd1 = tail_positions[c];
			}
			else
			{
				laserEnd0 = tail_positions[c];
				laserEnd1 = tail_positions[c-1];
			}
			D3DXVECTOR3 firstPart = laserEnd0 - laserEnd1;
			D3DXVECTOR3 lastPart = laserEnd0 - cameraPos;
			D3DXVec3Cross(&tempvector, &firstPart, &lastPart);
			D3DXVec3Normalize(&tempvector, &tempvector);

			laservert0 = laserEnd0 + (tempvector * 1);
			laservert1 = laserEnd0 - (tempvector * 1);

			vaporVerts[(pr_maxVaporTrailSections * i + c) * 2].x				= laservert0.x; 
			vaporVerts[(pr_maxVaporTrailSections * i + c) * 2].y				= laservert0.y;
			vaporVerts[(pr_maxVaporTrailSections * i + c) * 2].z				= laservert0.z;
			vaporVerts[(pr_maxVaporTrailSections * i + c) * 2].diffuse		= 0xFFFFFFFF;
			vaporVerts[(pr_maxVaporTrailSections * i + c) * 2].tu			= 0.0f;
			vaporVerts[(pr_maxVaporTrailSections * i + c) * 2].tv			= (float)(1.0f / (pr_maxVaporTrailSections - 1) * c);

			vaporVerts[((pr_maxVaporTrailSections * i + c) * 2) + 1].x		= laservert1.x; 
			vaporVerts[((pr_maxVaporTrailSections * i + c) * 2) + 1].y		= laservert1.y;
			vaporVerts[((pr_maxVaporTrailSections * i + c) * 2) + 1].z		= laservert1.z;
			vaporVerts[((pr_maxVaporTrailSections * i + c) * 2) + 1].diffuse = 0xFFFFFFFF;
			vaporVerts[((pr_maxVaporTrailSections * i + c) * 2) + 1].tu		= 1.0f;
			vaporVerts[((pr_maxVaporTrailSections * i + c) * 2) + 1].tv		= (float)(1.0f / (pr_maxVaporTrailSections - 1) * c);
		}
	}
}

// Fill the vertex buffer.
VOID* pVertices;
if( FAILED(pr_vaporVB->Lock( 0, sizeof(vaporVerts), (BYTE**)&pVertices, D3DLOCK_DISCARD)))
    MessageBox(NULL, "Billboard vertex buffer locking failure.", "Error", MB_OK);

memcpy(pVertices, vaporVerts, sizeof(vaporVerts));

pr_vaporVB->Unlock();
It's that blasted memcpy line that crashes almost all the ATI cards I've tested it on, but this code run PERFECTLY on every nVidia card I've tested. I know ATI cards are more finicky about the correct parameters being in the DirectX calls, but I can't find where I'm going wrong. None of the DirectX calls are failing, and that memcpy operation crashes the game to the desktop. I could be doing this whole dynamic VB in the completely wrong way... Any ideas?

Ryan Buhr

Technical Lead | Sector 13

Reactor Interactive, LLC

Facebook | Twitter

Advertisement
When you create your VB, you need to specify D3DUSAGE_DYNAMIC as the Usage parameter. Otherwise, as the docs say, using either D3DLOCK_DISCARD or D3DLOCK_NOOVERWRITE when you lock the buffer should be invalid. nVidia is apparently just lazy when it comes to checking things like that.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Oh, woops, I missed that. Thanks for the tip. However, this didn't seem to fix the problem, and now the memcpy operation crashes on both nVidia cards and ATI cards. [oh]

Ryan Buhr

Technical Lead | Sector 13

Reactor Interactive, LLC

Facebook | Twitter

Bummer. I've looked, but I can't see anything else. All your sizes look healthy, no off-by-one errors or similar that I can see. I suppose vaporVerts is an array on the stack, not dynamically allocated. Otherwise, sizeof(vaporVerts) would cause a problem. Of course, if that were the problem, then you wouldn't actually be able to see anything anyway, on either card. (I'm assuming that you had it running, and running correctly on nVidia cards previous to this.)
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Yes, vaporVerts is indeed an appropriately sized array created on the stack. Yeah, the frustrating part is that it did work perfectly before on nVidia cards (and it looked so cool! [smile]). What kinds of things could possibly cause memcpy to fail? Is there an alternative to this whole algorithm, perhaps one that eliminates the need to even use memcpy?

Ryan Buhr

Technical Lead | Sector 13

Reactor Interactive, LLC

Facebook | Twitter

Well, since you're using D3DLOCK_DISCARD, you should be able to lock the VB without stalling the GPU, so you could lock the VB before the loop, write the vertices directly into the VB, and then unlock at the end of the loop. D3DLOCK_DISCARD will, if necessary, create an alternate VB in memory for you to write to, while keeping the original as it was, in case the GPU is still using it. Then, when the original data is no longer needed, and the VB is unlocked, the GPU will get the new data. It's called DISCARD because you don't have access to the original data, due to the possibility of getting a new chunk of memory.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
I'm worried, though, that if it crashes on memcpy(), then simply avoiding memcpy() won't fix the real issue, whatever it may be. But it might be worth a try, I suppose.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Oh, terrific. Please forgive my ignorance, but how would I write directly to the VB? Is this correct?

if( FAILED(pr_vaporVB->Lock( 0, sizeof(vaporVerts), (BYTE**)&vaporVerts, D3DLOCK_DISCARD)))    MessageBox(NULL, "Vapor trail vertex buffer locking failure.", "Error", MB_OK);// fill the vaporVerts array like normal here, in the looppr_vaporVB->Unlock();

Ryan Buhr

Technical Lead | Sector 13

Reactor Interactive, LLC

Facebook | Twitter

You don't even need an allocated (heap or stack) array. Just a pointer. Then you provide the address of that pointer to Lock(), and access that pointer like an array, since Lock() will actually point you to already allocated memory.

VAPOR_VERTEX* pVertices;if( FAILED(pr_vaporVB->Lock( 0, MAXAMMO * MAX_VAPORTRAIL_SECTIONS * 2 * sizeof(VAPOR_VERTEX), (VOID**)&pVertices, D3DLOCK_DISCARD)))    MessageBox(NULL, "Vapor trail vertex buffer locking failure.", "Error", MB_OK);// fill the pVertices, as though it was an array, like normal here, in the looppr_vaporVB->Unlock();
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Thank you so much for the help, Agony. Unfortunately, I won't be able to try this until later tonight (I'm at work now), but I'll let you know how it turns out. [smile]

Ryan Buhr

Technical Lead | Sector 13

Reactor Interactive, LLC

Facebook | Twitter

This topic is closed to new replies.

Advertisement