microsoft / DirectXTK

The DirectX Tool Kit (aka DirectXTK) is a collection of helper classes for writing DirectX 11.x code in C++

Home Page:https://walbourn.github.io/directxtk/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add GPU instancing support to NormalMapEffect and PBREffect

alekasm opened this issue · comments

commented

My program has a lot of repeating models and I've been trying to use DrawInstanced. It seems like this function can be accessed by sort of recreating Model::Draw -> ModelMeshPart::Draw but then replacing DrawIndexed with DrawInstanced. If there is a better/more appropriate way please let me know.

The current issue is that my models appear to be stacked all at the origin and have some sort of color/alpha issue. I will make a side-by-side comparison of the code and output.

image

image

Additionally, it may be helpful to include slightly more documentation around instanceCount and startInstanceLocation. I'm assuming instanceCount is how many of the same model do I have, no idea about startInstanceLocation.

commented

Also I forgot to mention that it appears I don't need to touch anything regarding setting the index/vertex buffers because that is handled in the DrawInstanced function (a DirectXTK bonus for DX newbies like myself).

DirectX Tool Kit does include a DrawInstanced method on ModelMeshPart as you noticed, but I leave the rest of instancing up to the user because there are many different strategies for handling instancing that are typically a combination of extra data in the vertex buffer through multi-streaming rendering, and shader logic to append a position and/or full matrix information per-instance.

None of the "BasicEffect" family of shaders has the extra logic in them directly to support instancing.

This older "Effects 11"-based sample might give you some good ideas on how to do it yourself: InstancingFX11. In that sample, uses one float4x4 matrix per instance to place the rendering.

I have on my backlog to do a tutorial on instancing. I'll try to do it sooner than later.

commented

DirectX Tool Kit does include a DrawInstanced method on ModelMeshPart as you noticed, but I leave the rest of instancing up to the user because there are many different strategies for handling instancing that are typically a combination of extra data in the vertex buffer through multi-streaming rendering, and shader logic to append a position and/or full matrix information per-instance.

None of the "BasicEffect" family of shaders has the extra logic in them directly to support instancing.

This older "Effects 11"-based sample might give you some good ideas on how to do it yourself: InstancingFX11. In that sample, uses one float4x4 matrix per instance to place the rendering.

I have on my backlog to do a tutorial on instancing. I'll try to do it sooner than later.

I've spent some time learning a bit more about instancing, and I'm now blocked by learning HLSL to create the instancing logic for BasicEffect.

Member Data:

std::map<int32_t, std::vector<Model3D*>> m_model3d;
std::map<int32_t, Microsoft::WRL::ComPtr<ID3D11Buffer>> m_instanceBuffer;

Initialize Instance Buffers:
image

Instanced Rendering with DirectXTK:
image

I'm assuming where this is going wrong is that the ModelMeshPart InputLayout is the input layout we created with BasicEffect:
image

So what really needs to happen is the input layout needs to use a new BasicEffect shader with instancing support, so when the models are subsequently loaded - the ModelMeshPart will use those shaders instead?

TLDR:
Is it possible you can add a shader that supports position instancing?

I would like something like this:

const D3D11_INPUT_ELEMENT_DESC VertexPositionInstance[] =
{
  { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  { "INSTANCE_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1 }
};

The request here is to add instancing support (at least positional/translation) to the stock effects used in DirectX Tool Kit. Based on the example of the glTF gpu instancing extension, it seems like TRANSLATION, ROTATION, and SCALE are sufficient for most needs.

Adding this to PBREffect is simple as there are not a huge number of Vertex Shader permutations there (it has 4 now, this would add 4 more).

BasicEffect already has 32 vertex shader permutations, so I don't think adding instancing to BasicEffect makes sense. It would be easy to add it to NormalMapEffect since that too has only 4 Vertex Shader Permutation so would be 8. In the end you can always use a 'flat' normal texture map if you need instancing with "BasicEffect" style per-pixel lighting.

DualTexture doesn't make much sense instanced since the whole point of it is lightmapping which requires unique textures for global illumination.

SkinnedEffect already has 18 Vertex Shader permutations, and honestly skinned instances models are a whole other weird scenario.

First steps is adding instancing support to the shaders. See this PR (and did the same for DX12)

I'm pretty confident after getting my tests updated to try it out, but I'm happy to get more feedback as well.

Leaving this issue open so I remember to do the tutorial for it :)

commented

I'm pretty confident after getting my tests updated to try it out, but I'm happy to get more feedback as well.

Leaving this issue open so I remember to do the tutorial for it :)

Thanks for the hard work, I can't wait to try it! This is a really useful addition to DXTK, not sure why others haven't complained before for something like this. I'll comment again when I get around to implementing this upcoming week.

commented

Looks like I'll definitely wait on the documentation, there are just too many questions I have. Below are some of the issues and questions I ran into while trying to integrate instancing. Again, I'll just wait on the documentation because I'm just not knowledgable enough to do without; I'm not looking for answers - but perhaps the following can help shape the documentation.

First is fairly trivial, and not directly related to instancing but I'm still struggling with NormalMapEffect despite copy-pasting your code. NormalEffectInstance is just my own name for s_InputElements, copied directly from here:
https://github.com/microsoft/DirectXTK/wiki/Effects

I'm also unsure what is meant by the value of "(0.5, 0.5, 1, 1)" compared to "(1, 1, 1, 1)". What are those values?
https://github.com/microsoft/DirectXTK/wiki/NormalMapEffect

I tried just with and without instancing, both fail.

image

Second is setting up the Instance Buffer. Prior to the update, I was just attempting to use a Vector3. XMFLOAT3X4 makes more sense to keep the permutations down - but I'm unsure of which rows correlate to which instance data. Right now I only want to instance the translation data. I have a "Model3D class" which contains "m_world" and a DirectX::Model.

image

Finally I'm revisiting the Draw call which started this all. Now the objects do in fact draw with this draw setup/routine; however only the first model because we are explicitly calling it - almost as if the DrawIndexedInstanced is using "1" as an instance count. Other areas of concern include not calling IASetInputLayout on the m_NormalInputLayout I created during the setup. Also it appears that m_NormalMapEffect is only used to create the m_NormalInputLayout (in regards to ONLY drawing the models).

image

Again, please do not feel compelled to answer here on this PR - I'm clearly not knowledgable enough yet to implement instancing. Thanks!

Documented in a tutorial here