Alright, I'm currently making the transition from DirectX 10 to DirectX 11 (with very little help from the MSDN documention) and recently ran into a snag with updating a shader's constant buffer.
My application at the moment simply draws a rectangle in screen space and I've attached a float to the pixel shader so that I can quickly determine if the constant buffer is being updated by looking at the rectangle color.
I've used two methods presented in the MSDN tutorial (pDeviceContext->UpdateSubresource()) and the D3D11 sample code (pDeviceContext->Map()), both yielded the same results which leads me to wonder if maybe I missed an important flag earlier in the engine while initializing the D3D device or compiling the shaders. Also is anyone aware of any important trade offs between the two techniques?
Here is the relevant code...
struct UIShaderBuffer {
XMMATRIX scale,
rotation,
translation;
float temp; // Used to change the rectangle's color.
};
// Initialize the shader buffer and interface
bool UIRectangle::StartUpShaderBuffer() {
UIShaderBuffer _shaderBuffer;
ZeroMemory(&_shaderBuffer, sizeof(UIShaderBuffer));
D3D11_BUFFER_DESC _bd;
ZeroMemory(&_bd, sizeof(D3D11_BUFFER_DESC) );
_bd.Usage = D3D11_USAGE_DYNAMIC;
_bd.ByteWidth = sizeof(UIShaderBuffer);
_bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
_bd.CPUAccessFlags = 0;
_bd.MiscFlags = 0;
_bd.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA _subResData;
ZeroMemory(&_subResData, sizeof(D3D11_SUBRESOURCE_DATA));
_subResData.pSysMem = &_shaderBuffer;
_subResData.SysMemPitch = 0;
_subResData.SysMemSlicePitch = 0;
if(FAILED(pDevice->CreateBuffer(&_bd, &_subResData, &pShaderBuffer))) {
MessageBox(0, TEXT("UIRectangle: Failed to bind shader constant buffer.\0"), 0, 0);
return false;
}
return true;
}
// Update the buffer. Call before rendering.
void UIRectangle::UpdateShaderBuffer(XMMATRIX &_rScale, XMMATRIX &_rRotation, XMMATRIX &_rTranslation) {
static UIShaderBuffer _updatedBuffer;
_updatedBuffer.scale = _rScale;
_updatedBuffer.rotation = _rRotation;
_updatedBuffer.translation = _rTranslation;
_updatedBuffer.temp = 1.0f; // Used to affect rectangle color in order to see if buffer is getting updated.
pDeviceContext->UpdateSubresource(pShaderBuffer, 0 , 0, &_updatedBuffer, 0, 0);
pDeviceContext->VSSetConstantBuffers(0, 1, &pShaderBuffer);
pDeviceContext->PSSetConstantBuffers(0, 1, &pShaderBuffer);
/* Alternative method used in D3D11 sample code.
D3D11_MAPPED_SUBRESOURCE mappedResource;
UIShaderBuffer* bufPtr = 0;
if (FAILED(pDeviceContext->Map(pShaderBufferAddr, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
MessageBox(0, TEXT("UIRectangle: Failed to map shader buffer.\0"), 0, 0);
bufPtr = (UIShaderBuffer*) mappedResource.pData;
bufPtr->scale = _rScale;
bufPtr->rotation = _rRotation;
bufPtr->translation = _rTranslation;
bufPtr->temp = pos;
pDeviceContext->Unmap(pShaderBufferAddr, 0);
pDeviceContext->VSSetConstantBuffers(0, 1, &pShaderBufferAddr);
pDeviceContext->PSSetConstantBuffers(0, 1, &pShaderBufferAddr);
*/
}
// Render
bool UIRectangle::Render() {
static XMMATRIX _scale = XMMatrixTranspose(XMMatrixScaling(1.0f, 1.0f, 1.0f)),
_rotation = XMMatrixTranspose(XMMatrixIdentity()),
_translation = XMMatrixTranspose(XMMatrixTranslation(-0.5f, 0.0f, 0.0f));
UpdateShaderBuffer(_scale, _rotation, _translation);
return Mesh<V_Pos>::Render();
}
template <class V>
bool Mesh<V>::Render() {
pDeviceContext->IASetInputLayout(pVertexLayout);
pDeviceContext->IASetPrimitiveTopology(topologyType);
pDeviceContext->VSSetShader(pVShader, 0, 0);
pDeviceContext->PSSetShader(pPShader, 0, 0);
pDeviceContext->Draw(bufSize, 0);
return true;
}
Shader code....
cbuffer ConstantBuffer : register( b0 ) {
matrix scale;
matrix rotation;
matrix translation;
float temp;
};
float4 VS( float4 _pos : POSITION ) : SV_POSITION {
//return mul(translation, _pos);
return _pos;
}
float4 PS( float4 Pos : SV_POSITION ) : SV_Target {
return float4( temp, temp, temp, 1.0f );
}
Forgot to mention that I noticed this in the D3D11 debug output.
D3D11: INFO: ID3D11DeviceContext::Draw: Constant Buffer expected at slot 0 of the Pixel Shader unit (with size at least 208 bytes) but none is bound. This is OK, as reads of an unbound Constant Buffer are defined to return 0. It is also posible the developer knows the data will not be used anyway. This is only a problem if the developer actually intended to bind a Constant Buffer here. [ EXECUTION INFO #350: DEVICE_DRAW_CONSTANT_BUFFER_NOT_SET ]
I'm assuming that this is the problem however I don't understand it! Playing with the slot parameter of the SetConstantBuffers() call doesn't change anything, and I have no idea where else I need to bind the buffer. Is there some kind of resource dispute between the pixel and vertex shaders over the constant buffer? Or is this a data packing and alignment issue?
Other questions...
Are there any advantages to using XMMatrices over D3DMatrices? (Other than having to transpose one)
"XMMATRIX is SSE2 optimized and the code inlined, which as far as I know, isn't true of D3DXMATRIX. So it's definitely faster than D3DX, and in addition, it's more portable (has an Xbox 360 port)." - DeadMG
I've mirrored this question here http://www.gamedev.net/topic/605347-updating-shader-buffers/
Edit 1
Jumped the gun, thought I found the solution. Same issue and warning remains.
Here is the shader initialization code
Vertex Shader
bool VShader::StartUp(const character *_id, const void *_pDevice, LPCWSTR _pFileName, const char *_pFuncName, const char *_pShadModel) {
// Id used to reference the shader from the shader manager subsystem's hash table.
// Replace with Int hash value!!!!
if ( !id.StartUp(GAME_OBJECT_STRLEN) ) {
MessageBox(0, TEXT("VShader: Failed to start up shader id.\0"), 0, 0);
return false;
}
if (!id.SetString(_id)) {
MessageBox(0, TEXT("VShader: Failed to set shader id.\0"), 0, 0);
return false;
}
// I'm under the impression that these are the correct flags since MSDN documentation referenced typedefs and enums that didn't exist.
unsigned int _shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
// Debug flag
#if defined(_DEBUG)
_shaderFlags |= D3D10_SHADER_DEBUG | D3D10_SHADER_SKIP_OPTIMIZATION;
#endif
ID3D10Blob* _pErrorBlob = 0;
bool _hasFailed = false;
// Create the vertex shader
if(FAILED(D3DX11CompileFromFile(_pFileName, 0, 0, _pFuncName, _pShadModel, _shaderFlags, 0, 0, &pVSBlob, &_pErrorBlob, 0)))
_hasFailed = true;
if (_pErrorBlob || _hasFailed) {
if (_pErrorBlob != 0)
MessageBoxA(0, (LPCSTR)_pErrorBlob->GetBufferPointer(), 0, 0);
else {
SString _error(ERROR_STRING_SIZE);
_error.SetString(TEXT("VShader: Unable to find \"\0"));
_error.Append(_pFileName);
_error.Append(TEXT("\". (\0"));
_error.Append(id.GetStringPtr());
_error.Append(TEXT(")\0"));
MessageBox(0, _error.GetStringPtr(), 0, 0);
_error.ShutDown();
}
return false;
}
if (FAILED(((ID3D11Device*)_pDevice)->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), 0, &pVShader))) {
SString _error(ERROR_STRING_SIZE);
_error.SetString(TEXT("VShader: Failed to create pixel shader.\0"));
_error.Append(_pFileName);
_error.Append(TEXT(")\0"));
MessageBox(0, _error.GetStringPtr(), 0, 0);
_error.ShutDown();
return false;
} else return true;
}
Pixel Shader.
bool PShader::StartUp(const character *_id, const void *_pDevice, LPCWSTR _pFileName, const char *_pFuncName, const char *_pShadModel) {
if ( !id.StartUp(GAME_OBJECT_STRLEN) ) {
MessageBox(0, TEXT("PShader: Failed to start up shader id.\0"), 0, 0);
return false;
}
if (!id.SetString(_id)) {
MessageBox(0, TEXT("PShader: Failed to set shader id.\0"), 0, 0);
return false;
}
unsigned int _shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
// Debug flag
#if defined(_DEBUG)
_shaderFlags |= D3D10_SHADER_DEBUG | D3D10_SHADER_SKIP_OPTIMIZATION;
#endif
ID3D10Blob* _pErrorBlob = 0;
bool _hasFailed = false;
// Create the pixel shader.
if(FAILED(D3DX11CompileFromFile(_pFileName, 0, 0, _pFuncName, _pShadModel, _shaderFlags, 0, 0, &pPSBlob, &_pErrorBlob, 0)))
_hasFailed = true;
if (_pErrorBlob || _hasFailed) {
if (_pErrorBlob != 0)
MessageBoxA(0, (LPCSTR)_pErrorBlob->GetBufferPointer(), 0, 0);
else {
SString _error(ERROR_STRING_SIZE);
_error.SetString(TEXT("PShader: Unable to find \"\0"));
_error.Append(_pFileName);
_error.Append(TEXT("\". (\0"));
_error.Append(id.GetStringPtr());
_error.Append(TEXT(")\0"));
MessageBox(0, _error.GetStringPtr(), 0, 0);
_error.ShutDown();
}
return false;
}
if (FAILED(((ID3D11Device*)_pDevice)->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), 0, &pPShader))) {
SString _error(ERROR_STRING_SIZE);
_error.SetString(TEXT("PShader: Failed to create pixel shader. (\0"));
_error.Append(id.GetStringPtr());
_error.Append(TEXT(")\0"));
MessageBox(0, (LPCWSTR)_error.GetStringPtr(), 0, 0);
_error.ShutDown();
return false;
} else return true;
}
Edit 2
I'm starting to think it's an issue with my draw call sequence and by extension the design of my Mesh superclass and UIRectangle subclass. Didn't originally include the Mesh.Render() function with the code I posted but I added it to the bottom of the first code block. Could someone validate it?
Voting to delete. I solved the problem while rearranging my architecture and it was an inheritance issue! (kicks self) Don't believe this question can help anyone.