3
$\begingroup$

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[]?


Edit: @Nathan Reed I am trying to upload multiple textures to GPU, to use in shader code (hlsl). Currently I am using single ID3D12Resource with chosen value of "DepthOrArraySize" and fixed width and height for all subresources. Then I am treating it as Texture2DArray when creating SRV. As a result, I have Texture2DArray in my hlsl which works fine, but requirement of having fixed width/height for all elements is not convenient. I want to replace it with code, which enable me to use Texture2D[] in hlsl, where each texture may have different size. Dynamic indexing is not necessary.

$\endgroup$
2
  • 1
    $\begingroup$ Sorry, this is really unclear - what do you mean "upload std::vector or std::array<...> to GPU and use it with Texture2D[]"? Can you add more details about what you're trying to achieve? $\endgroup$ Commented Sep 9, 2020 at 17:20
  • 1
    $\begingroup$ @NathanReed I've added more detailed explanation at the end of question. $\endgroup$ Commented Sep 9, 2020 at 18:36

2 Answers 2

2
$\begingroup$

I think what you want for this is to create a descriptor table which lists your textures. The individual textures would be created and uploaded as ordinary Texture2Ds. You'd set up the root signature of your shader to bind your Texture2D[] in HLSL to a contiguous range of SRV descriptors from a descriptor heap. Then, when you create the SRVs for your textures, put their descriptors into the corresponding slots in the heap.

For the details, you can look at the D3D12DynamicIndexing sample in MS's DirectX Graphics Samples repository. I know you said you don't need dynamic indexing, but the strategy here would be the same.

$\endgroup$
0
$\begingroup$

Thank you Nathan! I was basing my Texture2DArray solution on MS's sample repo that you've sent, but after your explanation it was much easier to convert code to Texture2D[]. For anyone wondering, here's code:

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& 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(&desc, IID_PPV_ARGS(&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& tex : texturesWithDesc)
        {
            for (auto& singleTextureResource : tex.resources)
            {
                if (tex.isSRV)
                {
                    handle.ptr += handleIncrement;
                    device->CreateShaderResourceView(singleTextureResource.Get(), &tex.srvDesc, handle);
                }
            }
        }
    }
}

As Nathan said. 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 multiple array of textures.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.