0

I have a terrain mesh where just the Z value for each vertex needs to be updated every frame. My current method looks like this:

int stepping = CustomVertex.PositionNormalTextured.StrideSize / 4;

//ZPtr points to the Z value of the first PositionNormalTextured in the mesh.  
//This way we don't have to dereference ->Z for each vertex.
float* ZPtr = &(((CustomVertex.PositionNormalTextured*)
    TerrainMesh.LockVertexBuffer(LockFlags.NoOverwrite).InternalDataPointer)->Z);

float* DPtr = TerrainHeight; //point to begin scanning result
float* EndPtr = DPtr + TerrainMesh.NumberVertices; //point to stop scanning result

do { *ZPtr = *DPtr; ZPtr += stepping; } while (++DPtr < EndPtr); //copy data
TerrainMesh.UnlockVertexBuffer(); //unlock

Here, TerrainHeight is a float array created with Marshal.AllocHGlobal representing terrain height. Basically it scans across the entire TerrainHeight array and copies each value to the Z value of the respective PositionNormalTextured in the mesh. I used LockFlags.NoOverwrite to avoid creating a new copy of the array, although that doesn't seem any faster than LockFlags.Discard.

It takes as long or longer to update the mesh than it does to compute the new terrain in the CPU, which leads me to believe there should be a faster way. I've been having trouble finding info on Google about this. Is there a better way to update the vertexbuffer? In case it matters, the size of the mesh is user set and may include over a million vertices (this is accomplished with multiple meshes), but the default settings is 32k verts which is the max for a single D3D mesh.

HypnoToad
  • 585
  • 1
  • 6
  • 18
  • You should definitely avoid updating large vertex buffers each frame. Especially when it includes copying the data. Does your szenario allow a hardware implementation in e.g. a vertex shader with a heightmap? – Nico Schertler Jun 04 '12 at 20:28

1 Answers1

0

It appears you don't understand the ramifications of the Discard and NoOverwrite flags. Have a read of the section Using Dynamic Vertex and Index Buffers under Performance Optimizations in the DirectX SDK help. Assuming you're using a dynamic vertex buffer, then Discard means "I'm replacing the whole buffer" and NoOverwrite means "I'm writing to an unused portion of the buffer and I promise not to change any part I've already used".

With either flag, you'd have to write every component of your terrain vertices, even the ones that haven't changed in your new frame.

If you don't use a dynamic vertex buffer, then you might experience a stall when you attempt to lock the vertex buffer for the next frame, if your GPU is still using it. In that case you'll need to use multiple vertex buffers, and lock, update, unlock and render with a different buffer each frame that the terrain height changes. You will also have to initialize all of these buffers with all your terrain vertex data.

I'd suggest separating the z values for your mesh into their own vertex buffer - assuming that you're not updating x and y in position, your normal (which might be incorrect if z changes) or your texture coordinates. That way you can use your PositionNormalTextured (without position z) vertex buffer untouched every frame, and fill your dynamic z-position buffer each frame, from the beginning, using the Discard flag, without the stride to each Z value. Since the stride is gone, you can do that with a flat memory copy.

You'll supply your vertex shader with the z-position values with SetStreamSource( 1, ZPositionVB, ... ). You'll need to adjust your vertex declaration to read the z-position values from stream 1, and your vertex shader to combine the z-position values before transformation.

Apologies if some of this is a bad fit for C#.

Slagh
  • 342
  • 2
  • 8
  • I didn't know you could do that! It sounds like a much more efficient way to go about it, although I guess I would have to manually compute the normals, perhaps I could do that in the vertex shader. Thanks! – HypnoToad Jan 15 '13 at 04:30