如何填充常量着色器以避免 E_INVALIDARG?
Posted
技术标签:
【中文标题】如何填充常量着色器以避免 E_INVALIDARG?【英文标题】:How do constant shaders need to be padded in order to avoid a E_INVALIDARG? 【发布时间】:2013-06-11 03:02:19 【问题描述】:我正在调查一个 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
)
);
此时,传递给 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;
;
因此在顶点着色器 (.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;
为了找出造成这种情况的原因,我在 MSDN HLSL 着色器文档 (http://msdn.microsoft.com/en-us/library/windows/desktop/ff476898(v=vs.85).aspx) 中偶然发现了这一行:
每个元素存储一个 1 到 4 的分量常数,由存储的数据格式决定。
这是什么意思,是这个例外的原因吗?我注意到在 Visual Studio 3D Starter Kit (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
所以我只是没有正确填充这些东西吗?如果是这样,我应该如何填充它们?或者我错过了什么完全不同的东西?
非常感谢您阅读本文并提供帮助。
【问题讨论】:
【参考方案1】:由于缺少重要信息,您的问题很难解决,但让我们尝试一下。
显然,“E_INVALIDARG”表示传递给函数的参数无效。现在我们必须弄清楚哪个参数是错误的。 ID3D11Device::CreateBuffer 方法接受 3 个参数:D3D11_BUFFER_DESC、D3D11_SUBRESOURCE_DATA 和 ID3D11Buffer** 本身。
然后您向它提供 &constantLightBufferDesc、nullptr、&m_constantLightBuffer。 现在您必须仔细阅读所有 4 篇 MSDN 文章以找出问题所在。
constantLightBuffer
没问题,检查是否有ID3D11Buffer指针类型即可。
nullptr
这不太可能是个问题,constantLightBufferDesc
定义,这可能会成为问题:
正如您所说,可能存在缓冲区对齐错误:如果您的 constantLightBufferDesc.BindFlags
具有 D3D11_BIND_CONSTANT_BUFFER
标志和 constantLightBufferDesc.ByteWidth
is not a multiple of 16,则缓冲区创建失败。但这只是一个猜测。您可以在这里有任何其他不匹配,因此您可以无限猜测。
幸运的是,还有另一种诊断方法:如果您使用 D3D11_CREATE_DEVICE_DEBUG 标志创建 ID3D11Device,在 Visual Studio 输出窗口中,您将根据 D3D11 看到所有警告和错误。例如,在未对齐的情况下,您将看到:
D3D11 错误:ID3D11Device::CreateBuffer:尺寸无效。 对于 ConstantBuffers,用 D3D11_BIND_CONSTANT_BUFFER 标记 BindFlag,ByteWidth(值 = 10)必须是 16 的倍数。 当前的 ByteWidth 也必须小于或等于 65536 司机。 [STATE_CREATION 错误 #66:CREATEBUFFER_INVALIDDIMENSIONS]
所以,如果CreateBuffer()
因缓冲区大小错误而失败,有几种方法可以处理:
-
调整结构的大小:添加填充成员,使
sizeof()
的总数变为 16 的倍数。
将结构声明为 16 位对齐。 AFAIK 只有编译器特定的方法可以做到这一点:例如 #pragma pack
用于 msvc。
分配给ByteWidth
的不是实际结构大小,而是四舍五入到16 的下一个倍数:link
调试愉快! =)
【讨论】:
谢谢先生!问题出在您的直觉上,我通过添加填充成员使 sizeof 成为 16 的倍数来解决它。非常感谢您的帮助。以上是关于如何填充常量着色器以避免 E_INVALIDARG?的主要内容,如果未能解决你的问题,请参考以下文章
E_INVALIDARG 而 IMFTransform::ProcessOutput