Skip to main content
GameDev.net gamedev.net
Using GameDev.net for your class this semester?
Learn more →
🔒 Locked 🎮 Unity

D3D alternative for OpenGL gl_BaseInstanceARB

Started by _void_ Apr 9, 2016 at 10:39 AM 6 replies 4.3k views
Original Post
_void_
_void_

Hello,

http://on-demand.gputechconf.com/gtc/2014/presentations/S4379-opengl-44-scene-rendering-techniques.pdf

In the article mentioned above (slides 35, 36), they explain how MutiDrawInderect could be use to render multiple meshes with the single CPU call.

In particular, they utilize baseInstance from DrawElementsIndirect struct to encode transform and material indices for the mesh.

Then, this data is exposed in the vertex shader as gl_BaseInstanceARB variable and used to fetch transform for the specific mesh.

It seems that there is no alternative system variable in D3D for gl_BaseInstanceARB. I am thinking how I could work around this.

The first thing that comes to my mind is having a structured buffer, each element of which contains transform and material index for each vertex from the compound mesh vertex buffer. There will be data duplication but it should not be problematic, I gather. After, I could read the data through SV_VertexID in the vertex shader.

Edit: another way would be to add mesh id on vertex object itself.

Do you have any other ideas?

_void_
_void_

No, I am not really talking about the instancing. The instance will be always 1. I am interested in D3D12 ExecuteIndirect.

I am having a massive vertex/index buffer with all meshes merged data.

In the compute shader, I am planning to apply view frustum culling of the geometry and generate IndirectDraw arguments for each submesh that have survived culling.

I have a structured buffer that specifies vertex/index offset, index count, material/transform index for each submesh. Each submesh is distinguished by the material.

Then, I am going to ExecuteIndirect on the list of generated arguments to render GBuffer.

Essentially, I was asking how I could transfer material/transform index for each submesh from the culling pass to the GBuffer pass

Hodgman
Hodgman

In D3D12, the D3D12_DRAW_INDEXED_ARGUMENTS struct does have a StartInstanceLocation field like GL's BaseInstance.

BTW there's a multi-draw extension for D3D11 too :) and the D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS struct also has that field.

However, D3D doesn't pass this value through to SV_InstanceID properly :( So, if you want to access it, you've got to make a buffer containing {0,1,2,3,...} and bind it as per-instance data :lol:

Alternatively if you just want material data, just put it into a buffer, bind it as per-instance data, and then have your culling job fill in the StartInstanceLocation field properly. The indirect draw call's vertex shader will then receive materialBuffer[StartInstanceLocation] from the IA stage.

Matias Goldberg
Matias Goldberg

In the article mentioned above (slides 35, 36), they explain how MutiDrawInderect could be use to render multiple meshes with the single CPU call.
In particular, they utilize baseInstance from DrawElementsIndirect struct to encode transform and material indices for the mesh.
Then, this data is exposed in the vertex shader as gl_BaseInstanceARB variable and used to fetch transform for the specific mesh.

Actually... MDI (MultiDrawIndirect) didn't expose gl_BaseInstanceARB. It was added later. So you may find old drivers having MDI but without gl_BaseInstanceARB.

The best solution/workaround which works everywhere very well is to create a vertex buffer with 4096 entries, and bind it as instanced data with a frequency of 1.
Thus for each instance you get 0, 1, 2, 3, 4, 5, ..., 4095 (same as SV_InstanceID/gl_InstanceID) but with the added bonus that it starts from baseInstance instead of starting from 0.

Obviously you create this buffer once, since it can be reused anywhere.

The only caveat is that you can't use more than 4096 instances. Why 4096? It's quite random, but it's small enough to fit in a cache (whole buffer is just 16kb) and big enough to not matter whether you have two DrawPrimitive calls of 4096 instances each instead of one DP call of 8192 instances.

This is what we do in Ogre 2.1 and it works wonders for us. We do this for OpenGL as well, to avoid having to deal with drivers not having gl_BaseInstanceARB (and also have D3D11 & GL more consistent)

MJP
MJP

ExecuteIndirect supports setting arbitrary 32-bit constants through the D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT argument type. You can use this to specify transform/materialID data per-draw without having to abuse the instance offset. You can also set a root CBV or SRV via a GPU virtual address, which means you can use that to directly specify a pointer to the draw's transform data or material data.

_void_
_void_

I will probably go for D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT as MJP suggested.

Many thanks guys! I am really feeling appreciative for the help.

gwihlidal
gwihlidal

Hi, in my GDC 2016 talk, I discuss using the approach MJP mentioned to pass the draw index through to the indirect args using a root constant:

http://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/

Cheers,

Graham

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.