Hardware Vertex Buffer code, is this ok?

Started by
13 comments, last by Muhammad Haggag 19 years, 9 months ago
Context: [DirectX 7 !] [Windows 2000 SP3] [ATi Radeon 9800 Pro] Hi all, Yesterday I posted a message on this forum about my grievances with DirectX (please refer to http://www.gamedev.net/community/forums/topic.asp?topic_id=256351) . Today it's back to work and I'm trying to fix the problem :) I found out that whenever I use DirectX hardware vertex buffers (HVB from now on) my view gets screwed up completely when running my app on my Radeon 9800, though on every nvidia card it works fine. I already heard someone mentioning that nvidia's drivers are way more "forgiving" than ati's, so I'm trying to find out where I'm being "forgiven" by nvidia ;) I'm therefor pasting all my HVB related DirectX code here on this forum. I'm pretty sure something is going wrong here... If you guys could take a short look at it, than I'm most thankful, because I don't know what I'm doing wrong. And ofcourse for anyone with a useful tip there's a bright shining spot on my credits list :) Please remember that I have to use DirectX 7 (outdated customer hardware :-/ ). Again, any help is greatly appreciated!


// the variable m_hardwarebufferdata that I'm using is an instance of this struct:
typedef struct {
	D3DVERTEXBUFFERDESC		desc;
	IDirect3DVertexBuffer7*	source;
	IDirect3DVertexBuffer7*	target;
	int						vertexcount;
	VertexBuffer*			vb;
} ENGINE_RENDERER_DIRECT3D_HWB;



void RendererEngine_Direct3D::SetHVBData(int vertexbuffer)
{
	m_hardwarebufferdata = (ENGINE_RENDERER_DIRECT3D_HWB*)vertexbuffer;
}

void RendererEngine_Direct3D::DrawHVBTriangles(unsigned int start_index, unsigned int triangle_count)
{
	m_d3ddevice->DrawPrimitiveVB(m_rendermode, m_hardwarebufferdata->target, start_index, triangle_count*3, NULL);
}

void RendererEngine_Direct3D::DrawHVBIndexed(unsigned int indices_count, ENGINE_RENDERER_ARRAY_INDICES indices)
{
	m_d3ddevice->DrawIndexedPrimitiveVB(m_rendermode, m_hardwarebufferdata->target, 0, m_hardwarebufferdata->vertexcount, indices, indices_count, NULL);
}

/** Vertex buffer commands */

int RendererEngine_Direct3D::CreateHardwareVertexBuffer(VertexBuffer* vb)
{
	// create struct
	ENGINE_RENDERER_DIRECT3D_HWB* hwb = (ENGINE_RENDERER_DIRECT3D_HWB*)AXEMALLOC(sizeof(ENGINE_RENDERER_DIRECT3D_HWB));
	memset(hwb, 0, sizeof(ENGINE_RENDERER_DIRECT3D_HWB));
	hwb->vb = vb;
	hwb->vertexcount = vb->GetVertexCount();

	// setup vertex buffer descriptor
	ZeroMemory(&hwb->desc, sizeof(D3DVERTEXBUFFERDESC));
	hwb->desc.dwSize		= sizeof(D3DVERTEXBUFFERDESC);
	hwb->desc.dwCaps		= D3DVBCAPS_WRITEONLY;
	hwb->desc.dwFVF			= m_vertexformat;
	hwb->desc.dwNumVertices	= vb->GetVertexCount();

	// try to create the buffer in Direct3D
	if (FAILED(m_d3d->CreateVertexBuffer(&hwb->desc, &hwb->source, 0L))) {
		return -1;
	}

	// lock buffer, fill it, unlock it
	void* bufferdata;
	DWORD buffersize;
	if (FAILED(hwb->source->Lock(DDLOCK_WAIT | DDLOCK_WRITEONLY, &bufferdata, &buffersize))) {
		ENGINE_LOG(("createhardwarevertexbuffer: failed to lock vertex buffer"));
		hwb->source->Release();
		AXEFREE(hwb);
		return -1;
	}
	unsigned int totalsize = hwb->vertexcount*(sizeof(float) * (3 + 3 + 2*m_texturecount));
	if (totalsize != buffersize) {
		ENGINE_LOG(("createhardwarevertexbuffer: invalid vertex buffer size (it's %d, we need %d): is the multitexture count correctly set?", buffersize, totalsize));
		hwb->source->Unlock();
		hwb->source->Release();
		AXEFREE(hwb);
		return -1;
	}
	memcpy(bufferdata, vb->GetBuffer(), totalsize);
	hwb->source->Unlock();

	// optimize buffer for the current device
	hwb->source->Optimize(m_d3ddevice, NULL);

	// create target buffer, which will contain the transformed, processed vertices
	ASSERT(m_texturecount>0, ("CreateHardwareVertexBuffer: m_texturecount is %d", m_texturecount));
	hwb->desc.dwFVF = D3DFVF_XYZRHW | (D3DFVF_TEX1 * m_texturecount);
	if (FAILED(m_d3d->CreateVertexBuffer(&hwb->desc, &hwb->target, 0L))) {
		hwb->source->Release();
		AXEFREE(hwb);
		return -1;
	}
	
	// return pointer to our hardware vertex buffer struct converted to int
	return (int)hwb;
}

void RendererEngine_Direct3D::ProcessHardwareVertexBuffer(int vertexbuffer)
{
	FUNCCALL_COUNTER(m_funccall_processhvb);
	// for performance reasons, we're not checking the input
	ENGINE_RENDERER_DIRECT3D_HWB* hwb = (ENGINE_RENDERER_DIRECT3D_HWB*)vertexbuffer;
	if FAILED(hwb->target->ProcessVertices(D3DVOP_TRANSFORM, 0, hwb->vertexcount, hwb->source, 0, m_d3ddevice, 0)) {
		ENGINE_LOG(("processhardwarevertexbuffer: ProcessVertices failed"));
	}
}

void RendererEngine_Direct3D::DestroyHardwareVertexBuffer(int vertexbuffer)
{
	ENGINE_RENDERER_DIRECT3D_HWB* hwb = (ENGINE_RENDERER_DIRECT3D_HWB*)vertexbuffer;
	if (hwb) {
		if (hwb->source) hwb->source->Release();
		if (hwb->target) hwb->target->Release();
		AXEFREE(hwb);
	}
}


Marc
Advertisement
It would help tremendously to see a screenshot. When you say it screws up the view, do you mean the view is not where it is supposed to be, or the polygons are all trapezoidal instead of square, or pixels are fighting, etc...

Chris
Chris ByersMicrosoft DirectX MVP - 2005
You're totally right! Here they are:

How it's supposed to be (on my laptop with nvidia card)
http://130.89.227.64/foddex/it/good.jpg

What it is (on my game machine with ati card)
http://130.89.227.64/foddex/it/bad.jpg

Marc
Ow! I don't know DX7, but the only time I've seen anything like that is when I inadvertently told the API that my vertexbuffer stride was 18 instead of 20 (it was trying to color info from the previous vertex as vector info for the current one). The app didn't crash, but it got very seriously messed around.

But hang on... the character's face looks a little weird... is it rendering inversely (like the inside of a mask instead of as a face)? It may be that your culling order is wrong.

Always prey on the weak, the timid and the stupid. Otherwise you'll just get your butt kicked
For a tortoise, this is extremely hard to do, but when you get it right... the expression on their faces ...
Quote:Original post by SoaringTortoise
Ow! I don't know DX7, but the only time I've seen anything like that is when I inadvertently told the API that my vertexbuffer stride was 18 instead of 20 (it was trying to color info from the previous vertex as vector info for the current one). The app didn't crash, but it got very seriously messed around.

But hang on... the character's face looks a little weird... is it rendering inversely (like the inside of a mask instead of as a face)? It may be that your culling order is wrong.

Well the stride has to be ok, since my DirectX implementation works just fine on a nvidia. I'm using DrawIndexedPrimitiveVB, maybe the vertexcount in there is too large or something, but then again, why no 'distortion' on nvidia based cards? =[

And the character's face looks weird because a transparent face is drawn incorrectly. If i remove all the HVB rendering calls from my app, *everything* looks ok (except ofcourse for the fact that a lot of faces are missing)... that's why I copy/pasted my HVB code in my opening post.

More ideas? Thanks for the thinking by the way :)

[edit]
I can only think of one thing actually. I use a lot of triangle fans - the faces that are drawn incorrectly are all triangle fans. I read something about rendering multiple triangle fans (you have to separate each fan by doing 'something') in a single call, but the DirectX help wasn't really useful, it just mentioned a flag (not what flag). Has anyone got any experience with rendering multiple triangle fans in one single call? To clarify: I'm not rendering multiple triangle fans, but maybe I do something (or do NOT something) which makes my ATi card think I want to render multiple triangle fans?
I don't use tri-fans (in dx8 they're normally a bad idea), but according to the sdk docs you have to render each fan in its own VB, or its own render call. So, if your VB contains (e.g.) 10 fans, you would need 10 render calls, with each render call using a specific offset and count. If your offset is miscalculated (or perhaps not a power-of-2), Nvidia may have some smart correction in the driver while ATI doesn't (sounds a bit far-fetched to me, but you never know what a driver-coder thinks is a good idea or not... e.g. the nvidia driver may automatically enforce a power-of-two offset size, while ATI doesn't, or vice-versa).

Actually, this power-of-two sounds like a good candidate to me. Some of the older cards had major hassles with texture sizes not in ^2, and the SDK for VERTEX optimisation does recommend using ^2 vertex sizes.

Always prey on the weak, the timid and the stupid. Otherwise you'll just get your butt kicked
For a tortoise, this is extremely hard to do, but when you get it right... the expression on their faces ...
Quote:Original post by SoaringTortoise
I don't use tri-fans (in dx8 they're normally a bad idea), but according to the sdk docs you have to render each fan in its own VB, or its own render call. So, if your VB contains (e.g.) 10 fans, you would need 10 render calls, with each render call using a specific offset and count. If your offset is miscalculated (or perhaps not a power-of-2), Nvidia may have some smart correction in the driver while ATI doesn't (sounds a bit far-fetched to me, but you never know what a driver-coder thinks is a good idea or not... e.g. the nvidia driver may automatically enforce a power-of-two offset size, while ATI doesn't, or vice-versa).

Actually, this power-of-two sounds like a good candidate to me. Some of the older cards had major hassles with texture sizes not in ^2, and the SDK for VERTEX optimisation does recommend using ^2 vertex sizes.


Wait a minute, hold the phone! Power of two? I know the problems with non-^2-textures, but do those occur with vertex buffers as well?!?
Quote:Original post by Marc aka Foddex
Wait a minute, hold the phone! Power of two? I know the problems with non-^2-textures, but do those occur with vertex buffers as well?!?

As far as I know, vertex-sizes-not-power-of-2 might affect performance, but they don't mess up output...
But who knows?

If they do mess up output, rest assured someone will kick in and say so (and if you're lucky, that'd be S1CA [smile])

Quote:Original post by Marc aka Foddex
You're totally right! Here they are:

How it's supposed to be (on my laptop with nvidia card)
http://130.89.227.64/foddex/it/good.jpg

What it is (on my game machine with ati card)
http://130.89.227.64/foddex/it/bad.jpg

Marc

What is the entity that's causing the havoc when being rendered?
Try for example rendering static geometry alone (the office) or the dude alone...That should narrow things down a little.

By the way, are you sure your projection matrix is set to something reasonable? (i.e. zNear > 0, zFar/zNear < 1000)
Incorrect zNear distance (mostly 0, or very small values) do mess up things sometimes...

Drivers are very complex beasts, so anything might cause anything [smile].

Umm, I don't think it's a problem per se, but the sdk does mention performance impact of using non ^2, and I thought maybe there was a connection (especially perhaps on older cards).

But seriously:

1) The problem appears to be a problem with the geometry so-
2) Either the VB isn't being understood (^2, strides, etc) or -
3) A render-state is causing ATI to mis-interpret the geometry (culling mode, FVF flag)

It's also important to check that you have no 'unallowed' render state combinations. I once was using a combination of texture states that worked fine on Nvidia but completely failed on ATI (because nvidia was detecting a bad render-state combination and reseting to a default position).

Much more than that, I wouldn't know where to look. For debugging, just start culling out sections of code until you revert to a useable state
Always prey on the weak, the timid and the stupid. Otherwise you'll just get your butt kicked
For a tortoise, this is extremely hard to do, but when you get it right... the expression on their faces ...

This topic is closed to new replies.

Advertisement