Advertisement Jump to content
Sign in to follow this  
Juliean

Shader reflection unused variables in cbuffer

This topic is 1808 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

while implementing skinnig to my engine, I came to notice that the shader-reflection API does not seem to know which variables are unused from a cbuffer and therefore optimized away by the program. For example:

    cbuffer instance
    {
        matrix mWorld;
        matrix mPalette[16];
    }

mPalette is only used in the skinning permutation of this shader, but the reflection still returns 1068 for this cbuffer

void Effect::CreateVCBufferDummies(ID3D11ShaderReflection& reflection)
{
    // Get shader info
    D3D11_SHADER_DESC shaderDesc;
    reflection.GetDesc( &shaderDesc );

    for(size_t i = 0; i < shaderDesc.ConstantBuffers; i++)
    {
        ID3D11ShaderReflectionConstantBuffer* pConstantReflection = reflection.GetConstantBufferByIndex(i);
                    
        D3D11_SHADER_BUFFER_DESC desc;
        pConstantReflection->GetDesc(&desc);

        D3D11_SHADER_INPUT_BIND_DESC bindDesc;
        reflection.GetResourceBindingDescByName(desc.Name, &bindDesc);

// desc.size == 1068 for the shader
        m_cVBuffer[bindDesc.BindPoint] = new d3d::ConstantBuffer(*m_pDevice, desc.Size);
    }
}

The problem with this is that even though the reflection API doesn't notice it, the matrix-array is indeed being optimized away in the shader, and therefor when I try to move the data to the cbuffer via mapping, it crashes in this occasion:

void ConstantBuffer::Overwrite(void* pData) const
{
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	m_pDevice->Map(*m_pBuffer, D3D11_MAP_WRITE_DISCARD, mappedResource );

	// m_size == 1068 for this shader, but the mappedResource.pData only contains space for 64 byte
	memcpy(mappedResource.pData, pData, m_size);

	m_pDevice->Unmap(*m_pBuffer);
}

This also happens with other variable types, and even if the cbuffer-variable isn't used anywhere at all.

 

Does anybody know if there is a way to have the shader-reflection return the size of the cbuffer AFTER the optimizations? Is there any kind of flag for this, or is this maybe a bug, and there possibly exists another solution?

Share this post


Link to post
Share on other sites
Advertisement
You can query the variable to see if it is used or not.

However, unused variables are NOT always stripped out of cbuffers - it might be in this case because it is the last parameter however in larger cbuffers variables are left in place. Checking the assembly listing generated when compiling (assuming you compile offline) should show you what is going on.

(In fact I'm not sure I've seen a case where a cbuffer definition and a queried size HAS mismatched on size due to optimisation - but I wouldn't swear to it as it's been some time since I last had to deal with it.)

Share this post


Link to post
Share on other sites

I haven't seen this either. Your reported size is weird though, it should be 1088 (that's what I get through reflection, June 2010 SDK, compiler version 9.29.952.3111).

1068 isn't even a multiple of 16, so if your boilerplate doesn't check that you couldn't even create that buffer.

Share this post


Link to post
Share on other sites

Apologies, the size is indeed 1088, it mistyped it. The issue is there nevertheless, however I've found a quick workaround by only memcpy'ing the range of the CPU-side buffer that has been written to (so for a non-animated mesh only the first 64 bytes, only when I start to push all the bone-matrices to the buffer the rest is copied). I'll also have a look at querying the variables themselfs, hope this works, because its at least a giant waste of memory (1000+ byte instead of 64 for non-skinned meshes).

Share this post


Link to post
Share on other sites

The reflection API always returns the size of the constant buffer as it's declared in the shader, it never strips out unused variables. This is because constant buffers are explicit data  structures, just like structs or classes in C++. Doing it this way allows you to re-use the same constant buffer for different shaders, even if the shaders don't all access the same variables. The common case would be if you had a large constant buffer containing values that only change once a frame, like the view and projection matrices. Say you had a constant buffer like this:

 

cbuffer FrameConstants
{
    float ValueA;
    float ValueB;
}

 

Both these values only change once a frame, so they're put in a constant buffer of per-frame constants that's only updated once. Now say you had one shader that only uses ValueA and one that ValueB. For the second shader if the compiler stripped out ValueA, then it would think that ValueB is at an offset of 0 bytes instead of at 4 bytes. This means that if you re-used the same constant buffer for both, the shader would grab the wrong value.

 

If you don't want variables to included in your constant buffer, then you need to make sure that they're not defined in there at all using conditional compliation macros.

Edited by MJP

Share this post


Link to post
Share on other sites

Thanks MJP, I didn't think of this use case before, now it does make sense that it doesn't do that. This also made me think about how this all doesn't make any sense. I mean, I did create the cbuffer from the same value that the shader reflection returned, so they must actually match - I didn't even think about it, my gfx-wrapper takes so much work away that I pretty much asumed that the cbuffers are generated along with the shaders from the DirectX api, lol. The actual cause of the issue was in my material-system. When a material is set to a model, the model takes its effect and resizes its cbuffer to the effects model cbuffers size. Each model can have multiple materials, and the shadow-material (which only had 64 bytes of cbuffer) overwrote the larger cbuffer, since it was added last. I'm still going to need a good solution for when materials are completely replaced, but for now each additional material added can only enlargen the cbuffer but not shrink it, which fixes the crash.

Edited by Juliean

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!