Jump to content

  • Log In with Google      Sign In   
  • Create Account

Shader reflection unused variables in cbuffer


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
5 replies to this topic

#1 Juliean   GDNet+   -  Reputation: 2618

Like
0Likes
Like

Posted 08 February 2014 - 07:34 AM

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?



Sponsor:

#2 phantom   Moderators   -  Reputation: 7278

Like
4Likes
Like

Posted 08 February 2014 - 10:10 AM

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.)

#3 unbird   Crossbones+   -  Reputation: 4977

Like
3Likes
Like

Posted 08 February 2014 - 11:00 AM

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.



#4 Juliean   GDNet+   -  Reputation: 2618

Like
0Likes
Like

Posted 09 February 2014 - 09:40 AM

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).



#5 MJP   Moderators   -  Reputation: 11368

Like
4Likes
Like

Posted 09 February 2014 - 02:53 PM

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, 09 February 2014 - 02:56 PM.


#6 Juliean   GDNet+   -  Reputation: 2618

Like
0Likes
Like

Posted 09 February 2014 - 06:48 PM

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, 09 February 2014 - 06:49 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS