Constant buffer padding

Started by
5 comments, last by ongamex92 10 years, 3 months ago

I've been thinking about how to go about doing this for a while now. I've got a shader parameter system that basically creates a chunk of data from a parameter list and allows them to be set using a map of names and offsets. The thing I can't figure out though, is how I should calculate padding for the parameters. Basically I allocate the data and store offsets like this:


bool IShaderParameterBuffer::init(const std::vector<SShaderParameterDesc>& paramDesc)
{
	size_t offset = 0;

	for ( unsigned int i=0; i<paramDesc.size(); ++i )
	{
		size_t paramSz = IInputLayout::getElementSize(paramDesc[i].Format);

		m_offsetMap[paramDesc[i].Name] = offset;
		offset += paramSz;
	}

	m_paramBuffer = (char*)malloc(offset);
	m_bufferSz = offset;

	return true;
}

And I take this information to create a constant buffer, but const buffer size has to be a multiple of 16. What would be the best way to go about making sure that the data size is a multiple of 16?

Advertisement

m_bufferSz = (offset + 15) & 0xfffffff0;

m_bufferSz = ((offset + 15) / 16) * 16. //same thing without hex and bitwise-and if you don't like those

It sounded like that's all you were asking (pad for 16B boundary). If I misinterpreted, please clarify what you're looking for.

Edit: originally had 0x100, which is obviously wrong. (2nd edit: more fail)

I've deleted a lot of data just to make the post smaller.

My formula for a variable size is:


return (arraySize - 1) * 16 + VariableType_GetSizeByes(type);
where array size for non arrays is 1

Where array size for non-arrays is 1. VariableType_GetSizeByes returns the size in bytes for(float, int, float2, float3.. ect)

U32 GetTotalSizeBytes() const
{
const int numVariables = GetNumVariables();
U32 totalSize = 0;
bool bForceNewVectorPadding = false;
for(int t = 0; t < numVariables; ++t)
{
const VariableReflection& var = GetVariable(t);
const U32 varSize = var.GetSizeBytes();
if (var.arraySize > 1 || bForceNewVectorPadding)//array or forced variable
{
totalSize += totalSize % 16; // add padding
totalSize += varSize;
//bForceNewVectorPadding = var.arraySize > 1; //next variable starts new vector
}
else //non forced variable
{
U32 freeSpaceUntilNextVector = 16 - totalSize % 16;
if (varSize <= freeSpaceUntilNextVector) totalSize += varSize;
else totalSize += varSize + freeSpaceUntilNextVector;
}
}
return totalSize + 16 - totalSize % 16;
}

To check things fast use install the "hello triangle" demo form the DXSDK June2010 and add that code under the vertex shader compilation:

ID3D11ShaderReflection* pReflection = nullptr;
D3DReflect(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&pReflection);
D3D11_SHADER_DESC shaderDesc;
pReflection->GetDesc(&shaderDesc);
for (UINT i = 0; i < shaderDesc.BoundResources; i++)
{
D3D11_SHADER_INPUT_BIND_DESC resourceDesc;
pReflection->GetResourceBindingDesc(i, &resourceDesc);
switch (resourceDesc.Type)
{
//CBuffer
case D3D_SIT_CBUFFER:
{
const char* pCBufferName = resourceDesc.Name;
ID3D11ShaderReflectionConstantBuffer* pCBufferReflection = (pReflection->GetConstantBufferByName(pCBufferName));
D3D11_SHADER_BUFFER_DESC desc;
pCBufferReflection->GetDesc(&desc);
for (unsigned int t = 0; t < desc.Variables; ++t)
{
ID3D11ShaderReflectionVariable* pVariable = (pCBufferReflection->GetVariableByIndex(t));
D3D11_SHADER_VARIABLE_DESC varDesc;
pVariable->GetDesc(&varDesc);
int x = 10;//bla bla
};
}
}
};

U32 GetTotalSizeBytes() const
{
const int numVariables = GetNumVariables();
U32 totalSize = 0;
bool bForceNewVectorPadding = false;
for(int t = 0; t < numVariables; ++t)
{
const VariableReflection& var = GetVariable(t);
const U32 varSize = var.GetSizeBytes();
if (var.arraySize > 1 || bForceNewVectorPadding)//array or forced variable
{
totalSize += totalSize % 16; // add padding
totalSize += varSize;
//bForceNewVectorPadding = var.arraySize > 1; //next variable starts new vector
}
else //non forced variable
{
U32 freeSpaceUntilNextVector = 16 - totalSize % 16;
if (varSize <= freeSpaceUntilNextVector) totalSize += varSize;
else totalSize += varSize + freeSpaceUntilNextVector;
}
}
return totalSize + 16 - totalSize % 16;
}

I know you said you deleted a lot of your post to make it smaller, so I worry you might have deleted something important that makes it seem weird right now.

I can't figure out what the code intends to do with "totalSize += totalSize %16;". As listed, if you pass in char[2] elem1, char[2] elem2, char[2] elem3, you get elem1, 2 bytes of padding, elem2, 6 bytes of padding, elem3, 14 bytes of padding, elem4. Maybe that is the intent, but it seems really weird.

For OP, it's worth noting "- totalSize % 16" is mathematically the same as "& 0xfffffff0", ">>4)<<4", "/16) * 16". I suspect the only reason %16 isn't common in code is that it requires using 2 lines of code instead of one.

Edit: fixed f00 to f0 per L. Spiro's correction.

It’s 0xFFFFFFF0, not 0xFFFFFF00.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

It’s 0xFFFFFFF0, not 0xFFFFFF00.

L. Spiro

Thank you for the correction.

Yes i've deleted the wrong thing :


if (var.arraySize > 1 || bForceNewVectorPadding)//array or forced variable
{
totalSize += 16 - totalSize % 16; // add padding
...

totalSize += 16 - totalSize % 16; // add padding that line starts a new vector(16 byte aligned block). Imagine cbuffer like that:
float2 var;
float array[3];

var -> 8 bytes (total size of the buffer until now is 8 bytes)

array -> according to MSDN arrays are forced to start in a new 16byte aligned block

//lets force it by adding the needed padding

totalSize += 16 - totalSize % 16; or totalSize += 16 - 8 % 16

also bForceNewVectorPadding is not needed.

This topic is closed to new replies.

Advertisement