Search code examples
c++directxdirectx-11hlsl

Unable to figure out StructuredBuffers in DirectX11


I'm trying to create a StructuredBuffer in an attempt to do some skeletal animations in D3D11. I'm doing solid progress on the skeleton part, but creating a StructuredBuffer has me stumped, and I don't seem to be able to find any solid advice when searching the web.

As it stands I've gotten only a guess of how to use it in the shader, and no idea how to send the data to the GPU. I tried modifying a template function I used for ConstantBuffers (I'll show code blocks below), but that hit a dead end quickly.

The project is being made mostly in C++, with some external C libraries.

// Template function to create a constant buffer (This works as intended)
template<class T>
bool createBuffer(ID3D11Device* device, ID3D11Buffer*& buffer)
{
    T emptyValues;
    D3D11_BUFFER_DESC desc;
    desc.ByteWidth = sizeof(T) + (16 - sizeof(T) % 16);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0;
    desc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA data;
    data.pSysMem = &emptyValues;
    data.SysMemPitch = 0;
    data.SysMemSlicePitch = 0;

    HRESULT hr = device->CreateBuffer(&desc, &data, &buffer);
    return !FAILED(hr);
};
// Modified template function to create a structured buffer, this always fails
template<class T>
bool createStructuredBuffer(ID3D11Device* device, ID3D11Buffer*& buffer, const T* dataAdress, unsigned int num)
{
    unsigned int stride = sizeof(T);
    unsigned int byteWidth = stride * num + (16 - sizeof(T) % 16);

    D3D11_BUFFER_DESC desc;
    desc.ByteWidth = byteWidth;
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; // D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS ?
    desc.StructureByteStride = stride;

    D3D11_SUBRESOURCE_DATA data;
    data.pSysMem = dataAdress;
    data.SysMemPitch = 0;
    data.SysMemSlicePitch = 0;

    HRESULT hr = device->CreateBuffer(&desc, &data, &buffer);
    return !FAILED(hr);
}
// This is what I've gathered on how to use the StructuredBuffer in a shader, but this could also be completely wrong since I really have no idea what I'm doing with them.
StructuredBuffer<matrix> boneTransformation;

cbuffer constantBuffer : register(bX)
{
    int StructuredBufferSize;
}

type_a main(type_b input)
{
    int someInt = /*A number between 0 and StructuredBufferSize*/;
    doSomethingWith(boneTransformation[someInt]);
}

Solution

  • You cannot have both D3D11_BIND_UNORDERED_ACCESS bind flag and D3D11_USAGE_DYNAMIC at the same time.

    If you want to write to your buffer from CPU side by mapping your buffer, you leave D3D11_USAGE_DYNAMIC and D3D11_CPU_ACCESS_WRITE and remove D3D11_BIND_UNORDERED_ACCESS.

    If you want to write to your buffer from GPU by declaring it RWStructuredBuffer in your HLSL shader, then you leave D3D11_BIND_UNORDERED_ACCESS but you remove D3D11_CPU_ACCESS_WRITE and change D3D11_USAGE_DYNAMIC to D3D11_USAGE_DEFAULT. Of course, you have to create unordered access view before binding it.

    And one more thing, to use it in shader like you do, you need to create your shader resource view:

    device->CreateShaderResourceView(buffer.Get(), nullptr, &srv);
    

    where srv is Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> or ID3D11ShaderResourceView*. Then you bind it using *SetShaderResources. Maybe you already did that, but doesn't hurt to mention it.

    You also don't need ByteWidth to be multiple of 16 like you do for constant buffers. You can just write

    desc.ByteWidth = sizeof(T) * num;
    

    P.S. If you haven't already, you should enable debug layer since it's very helpful in debugging these kind of problems.