Using array of textures in DX12

Started by
2 comments, last by komilll 3 years, 8 months ago

I managed to create code, working correctly as Texture2DArray in hlsl by using single ID3D12Resource and binding it as D3D12_SRV_DIMENSION_TEXTURE2DARRAY with constant array size.

std::pair<ComPtr<ID3D12Resource>, D3D12_SUBRESOURCE_DATA> ModelClass::GetTextureFromModel(const aiScene* scene, std::string filename, ComPtr<ID3D12Device2> device, ComPtr<ID3D12GraphicsCommandList4> commandList, int index)
{
    D3D12_SUBRESOURCE_DATA textureDataSingle;
    std::unique_ptr<uint8_t[]> decodedData;
    ComPtr<ID3D12Resource> texture;
    m_uploadHeaps.push_back({});

    std::string s = std::regex_replace(filename, std::regex("\\\\"), "/");

    std::wstring ws(s.begin(), s.end());
    ThrowIfFailed(LoadWICTextureFromFileEx(device.Get(), ws.c_str(), 0, D3D12_RESOURCE_FLAG_NONE, WIC_LOADER_FORCE_RGBA32, texture.ReleaseAndGetAddressOf(), decodedData, textureDataSingle));

    const UINT64 uploadBufferSize = GetRequiredIntermediateSize(texture.Get(), 0, 1);

    // uploadHeap must outlive this function - until command list is closed
    ThrowIfFailed(device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize),
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(&m_uploadHeaps[m_uploadHeaps.size() - 1])
    ));

    UpdateSubresources(commandList.Get(), texture.Get(), m_uploadHeaps[m_uploadHeaps.size() - 1].Get(), 0, 0, 1, &textureDataSingle);

    if (texture->GetDesc().Width == 128 && texture->GetDesc().Height == 128)
    {
        commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE));

        D3D12_TEXTURE_COPY_LOCATION dst{};
        dst.pResource = m_diffuseTextures[index].Get();
        dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        dst.SubresourceIndex = index;

        D3D12_TEXTURE_COPY_LOCATION src{};
        src.pResource = texture.Get();

        commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
    }

    return { texture, textureDataSingle };
}

However it uses same resource description for all array slices (i.e. width and height). I replaced last "if" with code below:

{
    commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE));

    D3D12_RESOURCE_DESC textureDesc = {};
    textureDesc.MipLevels = 1;
    textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    textureDesc.Width = texture->GetDesc().Width;
    textureDesc.Height = texture->GetDesc().Height;
    textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
    textureDesc.DepthOrArraySize = 1;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.SampleDesc.Quality = 0;
    textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;

    ThrowIfFailed(device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &textureDesc,
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_PPV_ARGS(&m_diffuseTextures[index])
    ));

    D3D12_TEXTURE_COPY_LOCATION dst{};
    dst.pResource = m_diffuseTextures[index].Get();

    D3D12_TEXTURE_COPY_LOCATION src{};
    src.pResource = texture.Get();

    commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
}

I don't have any errors, so I assume that data is loaded correctly. However, how can I upload std::vector or std::array<...> to GPU and use it with Texture2D[]?

Advertisement

An array of Texture2D objects in HLSL corresponds to multiple SRV descriptors in the descriptor table bound to the root signature. Since the descriptors are completely separate, they can point to completely different texture resources if you want. You don't really need to do anything special in terms of how you load the texture.

However, there are definitely restrictions on how you index into that Texture2D[] array that don't apply if you're using a Texture2DArray. In particular you need to be careful if you're using a non-uniform index into that array.

[Topic can be closed, problem solved]

void RaytracingResources::CreateDxrPipelineAssets(ID3D12Device5* device, ModelClass* model, std::vector<TextureWithDesc> texturesWithDesc, D3D12_SHADER_RESOURCE_VIEW_DESC indexDesc, D3D12_SHADER_RESOURCE_VIEW_DESC vertexDesc, std::vector<ResourceWithSize> buffersWithSize, std::vector<bool> isUAV)
    {
        // Create descriptor heaps
        {
            D3D12_DESCRIPTOR_HEAP_DESC desc = {};
            // Vertex + index + TLAS = 3
            // Buffer count = y // buffersWithSize
            // Number of textures = x // texturesWithDesc
            int textureCount = 0;
            for (const auto&amp;amp; tex : texturesWithDesc) {
                textureCount += tex.resources.size();
            }
            desc.NumDescriptors = 3 + static_cast<UINT>(buffersWithSize.size()) + static_cast<UINT>(textureCount);
            desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
            desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    
            // Create the descriptor heap
            ThrowIfFailed(device->CreateDescriptorHeap(&amp;amp;desc, IID_PPV_ARGS(&amp;amp;m_descriptorHeap)));
    
            // Get the descriptor heap handle and increment size
            D3D12_CPU_DESCRIPTOR_HANDLE handle = m_descriptorHeap->GetCPUDescriptorHandleForHeapStart();
            UINT handleIncrement = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    
            // Create the CBV
            // ...
    
            // Create the DXR output buffer UAV
            // ...
    
            // Create the DXR Top Level Acceleration Structure SRV
            // ...
    
            // Create the index buffer SRV
            // ...
    
            // Create the vertex buffer SRV
            // ...
    
            // Create texture buffer SRV
            for (auto&amp;amp; tex : texturesWithDesc)
            {
                for (auto&amp;amp; singleTextureResource : tex.resources)
                {
                    if (tex.isSRV)
                    {
                        handle.ptr += handleIncrement;
                        device->CreateShaderResourceView(singleTextureResource.Get(), &amp;amp;tex.srvDesc, handle);
                    }
                }
            }
        }
    }

Thank you Matt! I'm posting code for anyone wondering how I've dealt with that problem - Each texture is a separate descriptor in HLSL, so I'm calculating number of textures to bind, based on passed data. I am using single space with assumption that Texture2D[] SRVs are the last element in the texture buffer space in HLSL. For now it works only for single texture array, so I'll need to add more spaces to make it possible to use multi array of textures.

This topic is closed to new replies.

Advertisement