Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do constant shaders need to be padded in order to avoid a E_INVALIDARG?

I am investigating a E_INVALIDARG exception that is thrown when I attempt to create a second constant buffer that stores the information for my lights:

    // create matrix stack early
    CD3D11_BUFFER_DESC constantMatrixBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &constantMatrixBufferDesc,
        nullptr,
        &m_constantMatrixBuffer
        )
        );

    DX::ThrowIfFailed(
        m_matrixStack.Initialize(m_d3dContext, m_constantMatrixBuffer, &m_constantMatrixBufferData)
        );

    // also create the light buffer early, we must create it now but we will later
    // update it with the light information that we parsed from the model
    CD3D11_BUFFER_DESC constantLightBufferDesc(sizeof(LightConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);

/* !!!!---- AN E_INVALIDARG IS THROWN BY THE FOLLOWING LINE ----!!!! */
    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &constantLightBufferDesc,
        nullptr,
        &m_constantLightBuffer
        )
        );

At this point, it appears that the parameters being passed into the Light's CreateBuffer call are in the same state that the Matrix's are! The problem seems to have to do with the number of bytes being stored in the buffer description.

The buffer is defined as such in the module:

// a constant buffer that contains the 3 matrices needed to
// transform points so that they're rendered correctly
struct ModelViewProjectionConstantBuffer
{
    DirectX::XMFLOAT4X4 model;
    DirectX::XMFLOAT4X4 view; 
    DirectX::XMFLOAT4X4 projection;
};

// a constant buffer that contains up to 4 directional or point lights
struct LightConstantBuffer
{
    DirectX::XMFLOAT3 ambient[4];
    DirectX::XMFLOAT3 diffuse[4];
    DirectX::XMFLOAT3 specular[4];

    // the first spot in the array is the constant attenuation term,
    // the second is the linear term, and the third is quadradic
    DirectX::XMFLOAT3 attenuation[4];

    // the position and direction of the light
    DirectX::XMFLOAT3 position[4];
    DirectX::XMFLOAT3 direction[4];

    // the type of light that we're working with, defined in lights.h
    UINT type[4];

    // a number from 0 to 4 that tells us how many lights there are
    UINT num;
};

And as such in the vertex shader (.hlsl):

cbuffer ModelViewProjectionConstantBuffer : register (b0)
{
    matrix model;
    matrix view;
    matrix projection;
};

cbuffer LightConstantBuffer : register (b1)
{
    float3 ambient[4];
    float3 diffuse[4];
    float3 specular[4];

    // the first spot in the array is the constant attenuation term,
    // the second is the linear term, and the third is quadradic
    float3 attenuation[4];

    // the position and direction of the light
    float3 position[4];
    float3 direction[4];

    // the type of light that we're working with, defined in lights.h
    uint type[4];

    // a number from 0 to 4 that tells us how many lights there are
    uint num;
}

In an attempt to figure out what is causing this, I have stumbled across this line in the MSDN HLSL Shader documentation (http://msdn.microsoft.com/en-us/library/windows/desktop/ff476898(v=vs.85).aspx):

Each element stores a 1-to-4 component constant, determined by the format of the data stored.

What does this mean and is it the reason for this exception? I have noticed that in the Visual Studio 3D Starter Kit (http://code.msdn.microsoft.com/wpapps/Visual-Studio-3D-Starter-455a15f1), the buffers have extra floats padding them:

///////////////////////////////////////////////////////////////////////////////////////////
    //
    // Constant buffer structures
    //
    // These structs use padding and different data types in places to adhere
    // to the shader constant's alignment.
    //
    struct MaterialConstants
    {
        MaterialConstants()
        {
            Ambient = DirectX::XMFLOAT4(0.0f,0.0f,0.0f,1.0f);
            Diffuse = DirectX::XMFLOAT4(1.0f,1.0f,1.0f,1.0f);
            Specular = DirectX::XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
            Emissive = DirectX::XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
            SpecularPower = 1.0f;
            Padding0 = 0.0f;
            Padding1 = 0.0f;
            Padding2 = 0.0f;
        }

        DirectX::XMFLOAT4   Ambient;
        DirectX::XMFLOAT4   Diffuse;
        DirectX::XMFLOAT4   Specular;
        DirectX::XMFLOAT4   Emissive;
        float               SpecularPower;
        float               Padding0;
        float               Padding1;
        float               Padding2;
    };

    struct LightConstants
    {
        LightConstants()
        {
            ZeroMemory(this, sizeof(LightConstants));
            Ambient = DirectX::XMFLOAT4(1.0f,1.0f,1.0f,1.0f);
        }

        DirectX::XMFLOAT4   Ambient;
        DirectX::XMFLOAT4   LightColor[4];
        DirectX::XMFLOAT4   LightAttenuation[4];
        DirectX::XMFLOAT4   LightDirection[4];
        DirectX::XMFLOAT4   LightSpecularIntensity[4];
        UINT                IsPointLight[4*4];
        UINT                ActiveLights;
        float               Padding0;
        float               Padding1;
        float               Padding2;
    };

    ... // and there's even more where that came from

So am I just not padding these things correctly? And if so, how should I pad them? Or is it something completely different that I'm missing?

I greatly appreciate you reading this and trying to help.

like image 751
Nico Avatar asked Jun 11 '13 03:06

Nico


1 Answers

It is hard to fix your problem because lack of important info, but let's make a try.

Obviously, 'E_INVALIDARG' says that invalid argument passed to function. Now we must figure out what parameter is wrong. ID3D11Device::CreateBuffer method accepts 3 parameters: D3D11_BUFFER_DESC, D3D11_SUBRESOURCE_DATA, and ID3D11Buffer** itself.

And you feed to it &constantLightBufferDesc, nullptr, &m_constantLightBuffer. Now you must carefully read all 4 MSDN articles to find out what is wrong.

  1. constantLightBuffer it is not a problem, just check that it has ID3D11Buffer pointer type.
  2. nullptr it is unlikely a problem, but AFAIK it is not C++ standard keyword, so probably simple '0' will be better here. Actually, it is a standard since C++11
  3. Unfortunately you don't provide your constantLightBufferDesc definition, which is a candidate to be a problem: as you've stated there can be buffer alignment mistake: if your constantLightBufferDesc.BindFlags has D3D11_BIND_CONSTANT_BUFFER flag and constantLightBufferDesc.ByteWidth is not a multiple of 16, buffer creation fails. But that's just a guess. You can have any other mismatch here, so, you can make a guesses infinetely.

Fortunalely, there are another way of diagnostic: if you creating your ID3D11Device with D3D11_CREATE_DEVICE_DEBUG flag, in Visual Studio output window you will see all the warnings and errors according to D3D11. For example, in case of misalignment you will see:

D3D11 ERROR: ID3D11Device::CreateBuffer: The Dimensions are invalid. For ConstantBuffers, marked with the D3D11_BIND_CONSTANT_BUFFER BindFlag, the ByteWidth (value = 10) must be a multiple of 16. ByteWidth must also be less than or equal to 65536 on the current driver. [ STATE_CREATION ERROR #66: CREATEBUFFER_INVALIDDIMENSIONS]

So, if CreateBuffer() failing because of wrong buffer size, there are several ways to handle this:

  1. Resize your structures: add padding members so total sizeof() will become multiple of 16.
  2. Declare your structures as 16-bit aligned. AFAIK there are only compiler-specific ways to do this: for example #pragma pack for msvc.
  3. Assign to ByteWidth not a real structure size, but rounded up to next multiple of 16: link

Happy debugging! =)

like image 164
Ivan Aksamentov - Drop Avatar answered Oct 09 '22 13:10

Ivan Aksamentov - Drop