DrawIndexedPrimitive with Triangle Strips

Started by
10 comments, last by Evil Steve 19 years ago
I've been using D3DX mesh objects since I started 3D programming, and have never really rendered anything complex without it. I'm trying to dump it off, because my lack of it's inner workings prevents me from adding a new feature. I have a few questions for anyone that has time. What is the performance difference between triangle lists and triangle strips? The samples don't seem to show much frame rate difference. Regardless of performance, what do most games use? Are there any problems with either? Use one for one task, and the other for another? Next, is it possible to render any subset in a single DrawIndexedPrimitive call with a single triangle strip? The optimized-mesh sample seems to be doing this (via D3DXConvertMeshSubsetToSingleStrip). Any complex shape, even with disconnected parts, can be a single strip? But how is this possible? If it is, and triangle strips are more efficient, then why ever use lists? Last, these are the steps I've tried to pile together. Do these look correct? And should they all be set on every render call? SetStreamSource( 0, VertexBuffer, 0, VertexSize ); SetIndices( IndexBuffer ); SetVertexDeclaration( VertexDecs ); DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, SubsetVertexCount, 0, SubsetFaceCount ); Thanks for advice
Advertisement
Quote:Original post by Jiia
What is the performance difference between triangle lists and triangle strips? The samples don't seem to show much frame rate difference. Regardless of performance, what do most games use? Are there any problems with either? Use one for one task, and the other for another?
Yes, exactly. If you can, it's best to use strips. There's quite a large over head of calling any of the DrawPrimitive functions, so you want as few calls as possible. If you can get most of your mesh into a single strip, do it. Otherwise, just use a triangle list.
I know Quake II's MD2 models use a triangle list, but it's OpenGL so I don't know if it makes a difference. I don't know about newer games. You could try using D3DSpy (comes with the SDK) to spy on a game, and see what primitive type it uses most.
There's not a lot of point using an index buffer with a triangle strip, since the vertiex to primitive ratio approaches 1:1 as you get more triangles in your strip. I only use index buffers if I'm using a triangle list.

Quote:Original post by Jiia
Next, is it possible to render any subset in a single DrawIndexedPrimitive call with a single triangle strip? The optimized-mesh sample seems to be doing this (via D3DXConvertMeshSubsetToSingleStrip). Any complex shape, even with disconnected parts, can be a single strip? But how is this possible? If it is, and triangle strips are more efficient, then why ever use lists?

Degenerate triangles. Since the vertices for each triangle in a strip need to be a different order (clockwise for the first triangle, anti-clockwise for the second, clockwise for thr third, etc) you can make "invisible" triangles by purposely reversing the order of the vertices in one triangle.
However, if your mesh is quite spread out, then you need a lot of degenerate triangles, which means a lot more vertices, which means worse performance. That's when you'd want to use triangle lists.

Quote:Original post by Jiia
Last, these are the steps I've tried to pile together. Do these look correct? And should they all be set on every render call?

SetStreamSource( 0, VertexBuffer, 0, VertexSize );
SetIndices( IndexBuffer );
SetVertexDeclaration( VertexDecs );
DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP, 0, 0, SubsetVertexCount, 0, SubsetFaceCount );

Thanks for advice

That looks right to me. You only need to set the stream source, indices and vertex declaration if they changed (I.e. if you're rendering from two buffers each frame).
Quote:Original post by Evil Steve
There's not a lot of point using an index buffer with a triangle strip, since the vertiex to primitive ratio approaches 1:1 as you get more triangles in your strip. I only use index buffers if I'm using a triangle list.

I didn't even consider that yet. Really helpful info, thanks.

Quote:Original post by Evil Steve
Degenerate triangles. However, if your mesh is quite spread out, then you need a lot of degenerate triangles, which means a lot more vertices, which means worse performance. That's when you'd want to use triangle lists.

Now it makes sense. So there are actually triangles being rendered (yet culled) to make it all fit together. Ummm, any idea how to detect how many degenerates are too many ([lol])? 3%? 5%? 15%? I'm only asking because I'm not familur the performance gain of strips vs lists yet. But I would even call filling the buffer with backwards triangles, a hack, if I had come up with it myself.

Thanks for your help
Quote:Original post by Jiia
Now it makes sense. So there are actually triangles being rendered (yet culled) to make it all fit together. Ummm, any idea how to detect how many degenerates are too many ([lol])? 3%? 5%? 15%? I'm only asking because I'm not familur the performance gain of strips vs lists yet. But I would even call filling the buffer with backwards triangles, a hack, if I had come up with it myself.
To be honest, I'm not sure. I'd guess at 10% or more, but it'd be best to profile and see when it starts to hamper performance. Degenerate triangles cause performance problems with vertex throughput, since you need more vertices, which means you need larger vertex buffers, which means:
  • If you go over 65536 vertices, you can't use index buffers (unless you want a 32-bit one, which isn't widely supported)
  • You have more data to pass over the AGP bus when you come to use that buffer (with SetStreamSource)
  • More vertices to transform, which is bad for software vertex processing
  • I'll be using this routine (first) to render a skinned mesh. Each subset is around 50 to 300 vertices. The feature I mentioned I wasn't sure how to do with D3DX is actually deleting subsets. I want to load a mesh in with weighted information, attribute tables, etc, then pull it apart and save it as seperate sections. When I load this into my game, I can swap body parts to build several types of characters from a library of body types, have robotic replacements and implants, and even have severe damage effects on limbs [smile]

    I have one other question for anyone who has a clue. What goes on inside of ID3DXMesh::DrawSubset()? Do they use strips, lists, index buffers, etc? That would be really useful info, but I'm not sure how anyone other than Microsoft would know.

    Thanks again for your help
    Quote:Original post by Jiia
    I have one other question for anyone who has a clue. What goes on inside of ID3DXMesh::DrawSubset()? Do they use strips, lists, index buffers, etc? That would be really useful info, but I'm not sure how anyone other than Microsoft would know.
    D3DSpy will be able to tell you. I'll try running it through the SkinnedMesh sample in a momen, and I'll post the results here.
    That would be great. Thanks dude
    Ugh, I'm having issues with D3DSpy (It's not working), sorry.
    I know that ID3DXMesh uses an index buffer (You can get it from the ID3DXMesh interface), but I don't know how it renders the mesh.
    Quote:# You have more data to pass over the AGP bus when you come to use that buffer (with SetStreamSource)
    SetStreamSource doesn't transfer anything. That's done between Lock and Unlock.

    If you use static vertex-/indexbuffers there is no AGP transfer at all after filling the buffers.
    Quote:Original post by noVum
    Quote:# You have more data to pass over the AGP bus when you come to use that buffer (with SetStreamSource)
    SetStreamSource doesn't transfer anything. That's done between Lock and Unlock.

    If you use static vertex-/indexbuffers there is no AGP transfer at all after filling the buffers.
    Ah, my mistake.

    Well, I decided to try and make my own D3DSpy dll. And I discovered why d3dspy wasn't working. The SkinnedMesh sample explicitly asks for the d3d9.dll file fom the system directory.
    Anyway, it turns out that ID3DXMesh uses DrawIndexedPrimitive().
    D3DSpy Output
    The highlighted SetRenderState call was inserted by me just before calling DrawSubset(), jsut to make sure I was looking at the correct call.
    I think it's perfectly possible that ID3DXMesh could use a triangle list in some circumstances too.

    This topic is closed to new replies.

    Advertisement