Usando array de texturas no DX12
Consegui criar código, funcionando corretamente como Texture2DArray em hlsl usando um único ID3D12Resource e ligando-o como D3D12_SRV_DIMENSION_TEXTURE2DARRAY com tamanho de array constante.
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 };
}
No entanto, ele usa a mesma descrição de recurso para todas as fatias da matriz (ou seja, largura e altura). Substituí o último "if" pelo código abaixo:
{
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);
}
Eu não tenho nenhum erro, então presumo que os dados sejam carregados corretamente. No entanto, como posso fazer upload de std::vector ou std::array<...> para GPU e usá-lo com Texture2D[]?
Edit: @Nathan Reed Estou tentando fazer upload de várias texturas para a GPU, para usar no código do sombreador (hlsl). Atualmente estou usando um único ID3D12Resource com o valor escolhido de "DepthOrArraySize" e largura e altura fixas para todos os sub-recursos. Então estou tratando como Texture2DArray ao criar SRV. Como resultado, eu tenho Texture2DArray no meu hlsl que funciona bem, mas o requisito de ter largura/altura fixa para todos os elementos não é conveniente. Eu quero substituí-lo por código, o que me permite usar Texture2D[] em hlsl, onde cada textura pode ter tamanho diferente. A indexação dinâmica não é necessária.
Respostas
Eu acho que o que você quer para isso é criar uma tabela de descritores que liste suas texturas. As texturas individuais seriam criadas e carregadas como Texture2Ds comuns. Você configuraria a assinatura raiz de seu sombreador para vincular seu Texture2D[]HLSL a um intervalo contíguo de descritores SRV de um heap de descritor. Então, quando você criar os SRVs para suas texturas, coloque seus descritores nos slots correspondentes no heap.
Para obter detalhes, você pode consultar o exemplo D3D12DynamicIndexing no repositório de exemplos de gráficos DirectX da MS. Eu sei que você disse que não precisa de indexação dinâmica, mas a estratégia aqui seria a mesma.
Obrigado Natan! Eu estava baseando minha solução Texture2DArray no repositório de amostra do MS que você enviou, mas após sua explicação ficou muito mais fácil converter o código para Texture2D[]. Para quem está se perguntando, aqui está o código:
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);
}
}
}
}
}
Como Natan disse. Cada textura é um descritor separado em HLSL, então estou calculando o número de texturas para vincular, com base nos dados passados. Estou usando espaço único com a suposição de que Texture2D[] SRVs são o último elemento no espaço de buffer de textura em HLSL. Por enquanto ele funciona apenas para um único array de texturas, então precisarei adicionar mais espaços para possibilitar o uso de vários arrays de texturas.