Advertisement

Passing data to instancing shader

Started by June 08, 2020 03:13 PM
3 comments, last by MJP 4 years, 7 months ago

I have a question about passing per-instance data to my shaders. Currently I am using two vertex buffers to do instancing. The first one is the “true” vertex data and the second one contains one float4x4 per instance, ie. the world matrix. I have been told this is the “old way" of doing things and I should be using a structured buffer or something for the per-instance data instead.

I am soon going to have to send more data than just a world matrix to my instances, so I figured I should give the structured buffer approach a try. I should note that my main graphics API is D3D11. I ran into the problem where the StartInstanceLocation parameter of DrawIndexedInstanced() is ignored unless I use two vertex buffers. The issue is discussed here, for example:

https://www.gamedev.net/forums/topic/662594-startinstancelocation-and-sv-instanceid/​

Basically I have a large structured buffer which is created when the app starts up and doesn't change after that. I would like to draw a subset of the instances in that buffer using an offset and count. However, I cant do that since SV_InstanceID always starts at zero.

Now, I see a few different solutions I could try out. I could add a second VB containing anything really, bind it as the second VB, add the data as per-instance data in my vertex layout, and then not use that data in the shader. That would allow me to use the StartInstanceLocation parameter to specify the value at which I want SV_InstanceID to start. The bogus VB could be created at startup and would not have to change as long as it would contain as many instances as I am ever going to want to draw. However, it seems like a pretty stupid solution, so perhaps not that?

Another solution would be to add a small constant buffer containing the instance offset, to be added to the value of SV_InstanceID in the shader. This could then be used to index into the structured buffer containing the per-instance data. This could potentially be a good solution but it would mean adding a second buffer just for this one thing. If I go down this route, perhaps it would be better to simply add the instance offset to the structured buffer in the first place? However, that would require me to update the buffer for every batch I draw.

Thoughts on what would be the best approach?

I don't know much about structured buffers.. so I cant help with that…but what I am doing is simply passing the data into a constant buffer, I just make a constant buff with an array of matrices and so on, and just use the instanceID as the array index.. works fine for my purposes.. though I don't know how well it scales for very large numbers of instances…in that case you can just draw in batches

Advertisement

Yeah, I ended up adding a per-batch constant buffer. I don't want to add the index of the first instance to the structured buffer since the whole point is that those can be constant data. A might need to add something else to the per-batch data anyway, so this kinda worked out.

Using a constant buffer or structured buffer where you vertex shader manually fetches the per-instance can definitely give you more flexibility in the long run. You're quite limited what you can do with the input assembler, since it's fixed-function. As an example, one thing you can do is produce a per-instance “index” buffer that you use in the vertex shader to fetch the appropriate instance data. This lets you keep your constant/structured buffer completely static with the data for all instances, but then you can still do things like frustum culling which naturally produces a sparse set of instances to draw. So of instance if you have 5 instances but only instances 1, 2, and 4 are visible, you stick those in a dynamic buffer and bind it to the vertex shader. Then in the VS you can do something like this:

uint instanceID = input.InstanceID; // SV_InstanceID
uint instanceBufferIdx = InstanceIndexBuffer[instanceID];
PerInstanceData instanceData = InstanceDataBuffer[instanceBufferIdx];

This topic is closed to new replies.

Advertisement