为了避免使用E_INVALIDARG,如何填充常量着色器?

Nic*_*ico 5 c++ directx graphics hlsl windows-phone-8

我正在研究当我尝试创建存储灯光信息的第二个常量缓冲区时引发的E_INVALIDARG异常:

    // 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
        )
        );
Run Code Online (Sandbox Code Playgroud)

此时,传递给Light的CreateBuffer调用的参数似乎与Matrix的状态相同!问题似乎与缓冲区描述中存储的字节数有关.

缓冲区在模块中定义如下:

// 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;
};
Run Code Online (Sandbox Code Playgroud)

就像在顶点着色器(.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;
}
Run Code Online (Sandbox Code Playgroud)

为了弄清楚导致这种情况的原因,我在MSDN HLSL Shader文档中偶然发现了这一行(http://msdn.microsoft.com/en-us/library/windows/desktop/ff476898(v=vs. 85).aspx):

每个元素存储1到4的组件常量,由存储的数据格式决定.

这是什么意思,是这个例外的原因吗?我注意到在Visual Studio 3D入门套件(http://code.msdn.microsoft.com/wpapps/Visual-Studio-3D-Starter-455a15f1)中,缓冲区有额外的浮点数填充它们:

///////////////////////////////////////////////////////////////////////////////////////////
    //
    // 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
Run Code Online (Sandbox Code Playgroud)

所以我只是没有正确填充这些东西?如果是这样,我应该如何填充它们?还是我错过了一些完全不同的东西?

我非常感谢您阅读本文并尝试提供帮助.

Iva*_*rop 8

由于缺乏重要信息,很难解决您的问题,但让我们试一试.

显然,'E_INVALIDARG'表示无效参数传递给函数.现在我们必须弄清楚哪个参数是错误的. ID3D11Device :: CreateBuffer方法接受3个参数:D3D11_BUFFER_DESC ,D3D11_SUBRESOURCE_DATAID3D11Buffer**本身.

你可以喂到它与constantLightBufferDesc,nullptr,与m_constantLightBuffer.现在你必须仔细阅读所有4篇MSDN文章,找出问题所在.

  1. constantLightBuffer 这不是问题,只需检查它是否具有ID3D11Buffer指针类型.
  2. nullptr它不太可能是一个问题,但AFAIK它不是C++标准关键字,所以可能简单的'0'在这里会更好. 实际上,它是自C++ 11以来的标准
  3. 不幸的是,你没有提供你的constantLightBufferDesc定义,这是一个问题的候选者:正如你所说的,可能存在缓冲区对齐错误:如果你constantLightBufferDesc.BindFlagsD3D11_BIND_CONSTANT_BUFFER标志并且constantLightBufferDesc.ByteWidth 不是16的倍数,则缓冲区创建失败.但这只是猜测.你可以在这里遇到任何其他的不匹配,所以,你可以在网上猜测.

幸运的是,还有另一种诊断方法:如果使用D3D11_CREATE_DEVICE_DEBUG标志创建ID3D11Device,则在Visual Studio输出窗口中,您将看到根据D3D11的所有警告和错误.例如,如果未对齐,您将看到:

D3D11错误:ID3D11Device :: CreateBuffer:维度无效.对于使用D3D11_BIND_CONSTANT_BUFFER BindFlag标记的ConstantBuffers,ByteWidth(值= 10)必须是16的倍数.当前驱动程序上的ByteWidth也必须小于或等于65536.[STATE_CREATION错误#66:CREATEBUFFER_INVALIDDIMENSIONS]

因此,如果CreateBuffer()由于错误的缓冲区大小而失败,有几种方法可以处理:

  1. 调整结构大小:添加填充成员,使总数sizeof()达到16的倍数.
  2. 将结构声明为16位对齐.AFAIK只有编译器特定的方法来执行此操作:例如,#pragma pack对于msvc.
  3. 分配给ByteWidth不是真正的结构大小,但向上舍入到16的下一个倍数:链接

快乐的调试!=)