Utilizzando array di trame in DX12

Sep 09 2020

Sono riuscito a creare codice, funzionando correttamente come Texture2DArray in hlsl utilizzando ID3D12Resource singolo e associandolo come D3D12_SRV_DIMENSION_TEXTURE2DARRAY con dimensione array costante.

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 };
}

Tuttavia utilizza la stessa descrizione delle risorse per tutte le sezioni di array (cioè larghezza e altezza). Ho sostituito l'ultimo "se" con il codice seguente:

{
    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);
}

Non ho errori, quindi presumo che i dati siano caricati correttamente. Tuttavia, come posso caricare std :: vector o std :: array <...> sulla GPU e usarlo con Texture2D []?


Modifica: @Nathan Reed sto cercando di caricare più trame sulla GPU, da utilizzare nel codice shader (hlsl). Attualmente sto usando ID3D12Resource singolo con il valore scelto di "DepthOrArraySize" e larghezza e altezza fisse per tutte le sottorisorse. Quindi lo sto trattando come Texture2DArray durante la creazione di SRV. Di conseguenza, ho Texture2DArray nel mio HLSL che funziona bene, ma requisito di aver fissato larghezza / altezza di tutti gli elementi non è conveniente. Voglio sostituirlo con del codice, che mi permette di usare Texture2D [] in hlsl, dove ogni texture può avere dimensioni diverse. L'indicizzazione dinamica non è necessaria.

Risposte

1 NathanReed Sep 12 2020 at 01:31

Penso che quello che vuoi per questo sia creare una tabella descrittiva che elenchi le tue trame. Le singole texture verrebbero create e caricate come normali Texture2D. Dovresti impostare la firma radice del tuo shader per legare il tuo Texture2D[]in HLSL a un intervallo contiguo di descrittori SRV da un heap di descrittori. Quindi, quando crei gli SRV per le tue trame, inserisci i loro descrittori negli slot corrispondenti nell'heap.

Per i dettagli, puoi guardare l' esempio D3D12DynamicIndexing nel repository DirectX Graphics Samples di MS. So che hai detto che non hai bisogno dell'indicizzazione dinamica, ma la strategia qui sarebbe la stessa.

DirectX_Programmer Sep 12 2020 at 15:27

Grazie Nathan! Stavo basando la mia soluzione Texture2DArray sul repository di esempio di MS che hai inviato, ma dopo la tua spiegazione è stato molto più semplice convertire il codice in Texture2D []. Per chiunque si chieda, ecco il codice:

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);
                }
            }
        }
    }
}

Come ha detto Nathan. Ogni trama è un descrittore separato in HLSL, quindi sto calcolando il numero di trame da legare, in base ai dati passati. Sto usando lo spazio singolo supponendo che gli SRV di Texture2D [] siano l'ultimo elemento nello spazio del buffer di texture in HLSL. Per ora funziona solo per un singolo array di texture, quindi dovrò aggiungere più spazi per rendere possibile l'utilizzo di più array di trame.