   Optimising Mesh Draw (Reducing Subsets/Materials)
  I'm looking at optimising my mesh loader to be able to deal with meshs containing large numbers of subsets.I noticed the problem when one of my artists provided me with a large cityscape mesh that he'd made in 3ds Max, I imported it into my "engine" (for want of a better word) and it had 882 subsets! Which with my current loader/draw code is 882 draws calls... which is far too many.He's not used a large number of materials (somewhere around 30), so I was thinking, would it be possible to combine the mesh subsets by material (and thus end up with only 32 subsets).If so, how would I go about doing this? I know information about the subsets is in the attribute table, but that is about it. How would I go about manipulating it to reduce the subsets?I really want to be able to do this without having to go back to the artist (he did it as a favour, and has already done more than I asked), and I'm sure it must be possible to do within my code.I'm working with .X files if it makes any difference, using the standard D3DX functions/methods.   
Quote:would it be possible to combine the mesh subsets by material?Yes, that's exactly what subsets are for (as you apparently know).Quote:How would I go about manipulating (the attribute table) to reduce the subsets?I'm assuming you're loading the mesh into a DirectX mesh.The attribute table lists the material number (subset) for each face in the mesh (as you apparently know).You'll have to determine what materials you want to use.That is, if material #4 is the same as material #18, go through the attribute table and change all 18's to 4's.It's been a long time since I did that. But resetting the attribute table that way may be adequate. If not, do a CreateIndexedBlendedMesh(Optimize? OptimizeInPlace?) from the revised mesh. Once again, I'm not sure (sorry), but if you create another mesh, you may have to recompute the normals if you want smooth normals.   ;  Changing the attribute IDs and then setting them on the mesh again hasn't worked, it only draws the first subset (from the previous 800) for each of the revised subsets.I'm currently working on reordering the vertex buffer so that instead of just renumbering the subsets, I can actually combine them.   ;  Looking at some old code, it appears it can be done this way:1. Lock the attribute buffer, revise it, unlock it.2. call mesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT,...);;  I've tried that, but it just generates a new id for everything, basically invalidating the work I've done previously.Thanks though.   ;Quote:it just generates a new id for everythingNot sure what you mean by "new id." You know which materials are which in order to change the attributes. In any case, it's what I used to reduce duplicate materials.;Quote:Original post by BuckeyeQuote:it just generates a new id for everythingNot sure what you mean by "new id." You know which materials are which in order to change the attributes. In any case, it's what I used to reduce duplicate materials.Okay, say I have the following, after reassigning the IDs.AttributeBuffer[0].AttribId = 0AttributeBuffer[1].AttribId = 0AttributeBuffer[2].AttribId = 0AttributeBuffer[3].AttribId = 1After calling OptomizeInPlace I end up with:AttributeBuffer[0].AttribId = 0AttributeBuffer[1].AttribId = 1AttributeBuffer[2].AttribId = 2AttributeBuffer[3].AttribId = 3Which is exactly what I started with.If I don't call Optomize and then I call DrawSubset(0), only the subset corresponding to AttributeBuffer[0] is drawn, and likewise, DrawSubset(1) results in AttributeBuffer[3] being drawn.If I Optomize I end up with 800+ draw calls, same as before.   ;  Something's not being done correctly.Are you using D3DXLoadMeshFromX to generate the mesh initially?Do you generate the adjacency after you revise the attribute buffer, and then use that adjacency in the OptimizeInPlace call?Do you check the HRESULT returned from the OptimizeInPlace call?   ;  I'd suggest one of two things:1) Ditch D3DX and the ID3DXMesh interface and create your own using vertex/index buffers and then pack all the vertices for each material into one large VB/IB per material (be careful of 16-bit indices tho, don't pack more than 65535 vertices into one VB).2) Follow the multi-animation sample example and overload the frame and meshcontainer structures and allocation heierarchy methods as shown. Then use D3DLoadMeshHierarchyFromX(). During the meshcontainer loads, linear search for a pre-existing eligible mesh-container and concatenate the vertices into its buffer.Of course I'm not sure how many different properties your materials have. It's easier for you to define a "material equality operator" if it boils down to just having the same texture as another. If your materials are also differentiated by render/sampler state changes and shaders, then bookkeeping becomes important and your job becomes harder.This may also likely be the source of why D3DX can't combine the meshes together for you, because perhaps some of the meshes while ostensibly sharing the same texture, may have different vertex formats (i.e. two meshes use a darkwood.dds texture, but the first one has PosNormalTex format and the second one only has PosTex format, therefore they can never be joined into the same attribute set without first creating normals for the second mesh).   ;Quote:Original post by BuckeyeAre you using D3DXLoadMeshFromX to generate the mesh initially?I'm using Mesh.FromFile, since I'm working in SlimDX, and I'm unsure how to create an XFileData object to use for the Mesh.LoadFromX function. It should give me the same input though, right?Quote:Original post by BuckeyeDo you generate the adjacency after you revise the attribute buffer, and then use that adjacency in the OptimizeInPlace call?I've been generating adjacency before revising the attribute beffer, and then using it in the Optomize call, I'll try moving that to after the attribute revision.Quote:Original post by BuckeyeDo you check the HRESULT returned from the OptimizeInPlace call?Yes, and it returns D3D_OK.Quote:Original post by Steve_SegretoI'd suggest one of two things:1) Ditch D3DX and the ID3DXMesh interface and create your own using vertex/index buffers and then pack all the vertices for each material into one large VB/IB per material (be careful of 16-bit indices tho, don't pack more than 65535 vertices into one VB).Any reason why I can't go for 32-bit indices? The types of machines I'm targeting shouldn't have any problems with them.I'm trying to avoid rolling my own at the moment, as model loading is not what I'm trying to work on right now. I just want something that works better than what I have now, I don't care if it's quick and dirty and not perfect, but 800+ draw calls prohibits me doing other things that I am working on as it drops my frame rate too much, which is why it needs at least some optomisation.Quote:Original post by Steve_Segreto2) Follow the multi-animation sample example and overload the frame and meshcontainer structures and allocation heierarchy methods as shown. Then use D3DLoadMeshHierarchyFromX(). During the meshcontainer loads, linear search for a pre-existing eligible mesh-container and concatenate the vertices into its buffer.I'll take a look at the sample when I can, but as I said above I'm really trying to avoid rolling my own full solution right now.Quote:Original post by Steve_SegretoOf course I'm not sure how many different properties your materials have. It's easier for you to define a "material equality operator" if it boils down to just having the same texture as another. If your materials are also differentiated by render/sampler state changes and shaders, then bookkeeping becomes important and your job becomes harder.Thankfully at this stage it's fairly simple, each material consists of a texture/diffuse colour, a normal map (or if missing the shader interpolates mesh normals) and a specular power.Quote:Original post by Steve_SegretoThis may also likely be the source of why D3DX can't combine the meshes together for you, because perhaps some of the meshes while ostensibly sharing the same texture, may have different vertex formats (i.e. two meshes use a darkwood.dds texture, but the first one has PosNormalTex format and the second one only has PosTex format, therefore they can never be joined into the same attribute set without first creating normals for the second mesh).Normals and tangents are generated earlier in my code if the mesh does not already have them, the vertex elements for each subset are also converted to a common format.   