简体   繁体   中英

Fastest way to update a Direct3D mesh's vertex buffer?

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.

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#.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM