4
\$\begingroup\$

Out of curiosity (and peace of the mind...) I wondered how DirectX decides which attribute from a struct corresponds to the right variable inside an HLSL cbuffer-register(x) (apart from the order/type they are declared with).

My main concern is my structs not only have member variables but 2 constructors each. So how can I be sure the constructors are not at the beginning of my struct definition (memory wise - C++ question I guess) so my variables would overflow after my cbuffer ? Additionnaly, can I only rely on declare-order/type matching ?

Is there a more precise/explicit way than doing :

struct foo {
    int a;
    int b;
    int c;

    foo () {}
    foo (int a, int b, int c) : a{a}, b{b}, c{c} {}
}

D3D11_BUFFER_DESC constantBufferDesc;
...
constantBufferDesc.ByteWidth = sizeof(foo) + (16 - sizeof(foo) % 16);
hr = g_d3dDevice->CreateBuffer(...);
g_d3dDeviceContext->VSSetConstantBuffers(0, 1, &fooBuffer);
g_d3dDeviceContext->UpdateSubresource(&fooBuffer, 0, nullptr, &foo, 0, 0);

With HLSL :

cbuffer foo: register(b0) {
    // constructor here ?
    int a;
    // constructor here ?
    int b;
    int c;
    // constructor here ?
}

I am aware this is more a C++ question - Thanks for your insight.

\$\endgroup\$
  • 2
    \$\begingroup\$ By the way, (addressed somewhat below), all cbuffer structs in DX11 must be 16 byte aligned. In VS you can prefix your struct definition with __declspec(align(16)). (__declspec(align(16)) struct Foo { ... };) \$\endgroup\$ – CobaltHex Aug 13 '15 at 0:41
  • \$\begingroup\$ This is what I intended to do with ByteWidth = sizeof(foo) + (16 - sizeof(foo) % 16);. Should I use __declspec(align(16)) instead ? \$\endgroup\$ – PinkTurtle Aug 13 '15 at 9:02
  • \$\begingroup\$ Yes, this will force the struct to always be aligned which is important, especially if you have data not in even multiples/fractions of 16 \$\endgroup\$ – CobaltHex Aug 13 '15 at 16:45
8
\$\begingroup\$

Out of curiosity (and peace of the mind...) I wondered how DirectX decides which attribute from a struct corresponds to the right variable inside an HLSL cbuffer-register(x) (apart from the order/type they are declared with).

Purely through memory layout. You give D3D a pointer to a chunk of memory which you claim to be organized in a certain fashion (for example, via your cbuffer definition). It's up to you to ensure that organization is actually true.

My main concern is my structs not only have member variables but 2 constructors each. So how can I be sure the constructors are not at the beginning of my struct definition

In C++, member functions (such as constructors) do not take up space in the memory layout of an instance of a structure type. In your example, sizeof(foo) is going to be 3 * sizeof(int), so it will map correctly to the foo structure in HLSL. So you don't need to worry about this.

Most of the time.


One potential issue would be if you have any virtual functions define in your type. Although it is implementation-defined how the virtual methods are handled, the far-and-away most common implementation is to stuff a pointer to the type's virtual table at the start of an instance of a type. This will offset everything by sizeof(void*) and render your structures mismatched.

Another potential issue is structure padding; compilers can and will insert dummy data between structure members (padding) to ensure the data is appropriately aligned. Generally you don't need to worry too much about this, as the types you are likely to use in your structures are those that usually end up tightly-packed once default compiler alignment comes into play. However if you have specialized alignment needs for either your CPU-side or GPU-side structures, and have padding in your type as a result, this is something you need to be aware of.

Thus, as long as you don't have any virtual methods defined in your structure types that you're mapping to GPU buffers, and you don't have any weird padding going on, you have nothing to worry about.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ I'm not at using virtual functions or anything that could create problems as you described. My structs consist only of member variables and 2 constructors (empty & all members initialization). I've had problems at cbuffer members not holding the correct values (it seems) but can't test it more then because windows 10 won't open my VS project =( Could it be that I pad my buffer size to a multiple of 16 ? Please check my OP for clarification: i.e.a 68 sized buffer would be scaled to 80. \$\endgroup\$ – PinkTurtle Aug 12 '15 at 21:01
  • \$\begingroup\$ Padding is all up to the compiler and affected by compiler settings; you'll have to look up the defaults for your compiler (they tend to pad to word size of the CPU though) and verify you don't use any padding-altering pragmas or attributes. You also want to make sure you're obeying the packing rules for constant buffer values. \$\endgroup\$ – user1430 Aug 12 '15 at 22:18
  • \$\begingroup\$ I suggest using static_assert to assert that the size of the struct is equal to what you tell the gpu, if you dont want to explicitly enforce no padding (not sure if theres a good standard way to do that yet). \$\endgroup\$ – Waterlimon Aug 12 '15 at 23:39
  • \$\begingroup\$ @JoshPetrie so __declspec(align(16)) is the way ? \$\endgroup\$ – PinkTurtle Aug 13 '15 at 9:32
1
\$\begingroup\$

My assumption based on my experiences with shader reflection in DX11 is that it's mapped by name. If you check out the DX11 shader reflection API you'll see that there is a D3D11_SHADER_VARIABLE_DESC struct that you can rip from the shader. Notice the LPCSTR for the name of the variable. OpenGL also handles variable mapping by name.

Of course the best way to find out is to try it yourself! Try repositioning your constructors and see if it affects your cbuffer buffering.

https://msdn.microsoft.com/en-us/library/windows/desktop/ff476213%28v=vs.85%29.aspx

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ Constructors don't occupy space in instances, and the C++ structure is mapped onto the HLSL structure by memory arrangement only (the name of the C++ members is irrelevant). \$\endgroup\$ – user1430 Aug 12 '15 at 17:50
  • \$\begingroup\$ I can't test it now (as Windows 10 is causing problems) but got erronous results (it seems, if I tested correctly) for my cbuffer variables : i.e. not holding the value they should from C++ side. Some variables seemed OK while some I seemed not. I only just started testing this afternoon will do more as soon as I can ;p \$\endgroup\$ – PinkTurtle Aug 12 '15 at 21:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.