Jump to content
  • Advertisement
Sign in to follow this  
LevyDee

D3D9 Drawing Performance question

This topic is 2437 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Before I design and code a specific way to draw a special format of an object I am working on, I would like a little opinion from you guys.

If an object is composed of multiple parts(to allow for 'paper dolling' or skinning i guess is the 3d term, is it faster(performance) to say, make 5 draw calls for the 5 pieces, or lock a single vertex buffer and parse your vertice data in from all 5 parts, then make a single draw call?

Thanks =)

Share this post


Link to post
Share on other sites
Advertisement
How about 1 draw call, but instead of locking a VB, you use upload an array of matrices into vertex-shader constants.

Share this post


Link to post
Share on other sites
Single draw call, definitely.

Constants or VB? It depends. Constants can be faster (and easier to work with) but - depending on your hardware - you have limited constants space (and it may be very limited), so you may find yourself needing to split into multiple calls if you go for constants.

Share this post


Link to post
Share on other sites
Please explain a little further. How does setting a matrix array inside my shader allow me to replace 5 different vertex buffers with 1. The reason I have this object "ripped" into 5 different pieces is to be able to interchange different parts of the object. So for example, you could have 10 different armor sets or w/e, and your character could wear pieces from the 10 different sets. "Mix and Match"

Share this post


Link to post
Share on other sites
Sorry, I was assuming that the reason for your 5 draw-calls was because you wanted to individually move the separate parts -- in that case, you'd give each part it's own matrix (and use a vertex attribute to index the array of matrices).

If you're doing this so that you can procedurally build a character out of multiple parts, then you don't have to re-build the player every frame -- you can rebuild their VB whenever they change their gear, and then reuse that data frame to frame.

Also, if possible, you could instead put the vertices for many different pieces of gear into the same VB, and then generate a IB data instead -- e.g. the VB holds the verts for 100 parts, and then the character's IB data contains the indices of the verts belonging to the current 5 parts.

Share this post


Link to post
Share on other sites
Draw calls by themselves are not very expensive.

Changing other state between draw calls is what increases the cost. Either directly (changing textures & samplers), or indirectly (changing shader constants, changing shaders, changing vertex streams). For Borderlands we have weapon models containing all the various weapon parts in the same vertex and index buffer. Each part has its indices grouped together so each sub-part is a single draw call, but the only thing changing is the arguments to the DrawIndexedPrimitive (starting index and count), it renders very fast this way, and is very much like how mesh instancing is actually implemented behind the scenes.

We render the terrain in a similar manner. Each layer has its own index buffer to reduce optimize our fillrate usage as our terrain is multi-pass. The terrain geometry is ordered in such a way it can be frustum culled efficiently, and still render with a relatively small number of draw calls (each draw call represents a jump where it had to restart, as we use tri-stripped terrain instead of tri-lists to save space on the indices). This same approach also lets us cull the terrain to a point or spot light source with the same code, and render a fairly minimal set of geometry affected by the light, which is traditionally an annoying problem with a forward renderer.

Share this post


Link to post
Share on other sites
Thank you for the input. There are some idea's here that I had not considered. Thank you.

Edit: Hah, Zoner are you really a dev with gearbox and worked on Borderlands? I loved that game! Good job =p

Share this post


Link to post
Share on other sites
So I had a thought after thinking about both of your guy's input to my question. This sound really good in theory, to parse everything into a VB and only update the VB when "changing equipment", but if you are using a single VB, you can Draw once(which is the goal), but doing this allows for no way to change your texture for individual pieces. The only way to do so is to construct a new texture as well by parsing together all of the textures from each individual piece and updating the UV coordinates for each vertex. So that alone adds over head, OR you could construct your VB once, and Draw parts of the VB, updating the texture in between draw calls, at which case, you are no longer drawing only once.

What are your thoughts? Sorry to keep this question on going, but this is pretty interesting stuff to discuss.

Thanks.

EDIT: Also, since I am going to have to figure out how to do this, and I can find no obvious way from looking through MSDN, is there a "simple" way to take one big texture, and split it into multiple, smaller textures? I looked into IDirect3DTexture9->LockRect(), and after receiving the texture data, I see no way to create a texture using it. Any thoughts? Thanks for all the help guys!

Share this post


Link to post
Share on other sites

So I had a thought after thinking about both of your guy's input to my question. This sound really good in theory, to parse everything into a VB and only update the VB when "changing equipment", but if you are using a single VB, you can Draw once(which is the goal), but doing this allows for no way to change your texture for individual pieces. The only way to do so is to construct a new texture as well by parsing together all of the textures from each individual piece and updating the UV coordinates for each vertex. So that alone adds over head, OR you could construct your VB once, and Draw parts of the VB, updating the texture in between draw calls, at which case, you are no longer drawing only once.

What are your thoughts? Sorry to keep this question on going, but this is pretty interesting stuff to discuss.

Thanks.

EDIT: Also, since I am going to have to figure out how to do this, and I can find no obvious way from looking through MSDN, is there a "simple" way to take one big texture, and split it into multiple, smaller textures? I looked into IDirect3DTexture9->LockRect(), and after receiving the texture data, I see no way to create a texture using it. Any thoughts? Thanks for all the help guys!


That's not *strictly* necessary. Remember that D3D lets you supply an offset and index count for DIP and friends, so you could theoretically keep all your vertex/index buffers, etc. bound and just swap out the texture(s) as needed. It's not going to be quite as CPU-friendly as, say, atlasing, but it should scale a hell of a lot better than generating a unique ubertexture per character. On that note, you can actually just make one gigundo superbuffer for all your vertex data and then just do some black magic with the indices to further cut down on state changes SO LONG AS your vertex layout (memory-wise) remains reasonably uniform. Anecdotally I think CryEngine 3 (or at least Crysis 2) shipped with something very much like this, as there are some cvars adjusting the pooled buffer sizes.

Re: making textures: you... make a new texture? Lock the destination region and use your favorite memcpy() variant to populate your new chunk o' VRAM.

Thirdly: PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE PROFILE! If this is not your bottleneck you're wasting engineering effort and may potentially limit your options later on down the line.

EDIT: Derp, I see you mention the swap trick. Rest of it should still apply, though.

Share this post


Link to post
Share on other sites

Re: making textures: you... make a new texture? Lock the destination region and use your favorite memcpy() variant to populate your new chunk o' VRAM.


This is what I was thinking, but I haven't developed my class functions enough yet to be able to test this. I have my doubts that this works.


D3DLOCKED_RECT* regionData;
myBigTexture->LockRect(0, regionData, regionToLock, 0);

//TODO Almost positive this isn't how to do what I want.
IDirect3DTexture9* newTextureChunk;
memcpy(newTextureChunk, regionData->pBits, sizeof(IDirect3DTexture9))

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!