DX12中常用的资源有:顶点数据、纹理和一些常量等。本文将总结一下这些资源在实际中如何使用和一些细节。
d3d12中所有的资源使用ID3D12Resource来表示,下面以龙书的demo作为例子:
struct Texture
{// Unique material name for lookup.std::string Name;std::wstring Filename;Microsoft::WRL::ComPtr<ID3D12Resource> Resource = nullptr;Microsoft::WRL::ComPtr<ID3D12Resource> UploadHeap = nullptr;
};void CrateApp::LoadTextures()
{auto woodCrateTex = std::make_unique<Texture>();woodCrateTex->Name = "woodCrateTex";woodCrateTex->Filename = L"../../Textures/WoodCrate01.dds";ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.Get(),mCommandList.Get(), woodCrateTex->Filename.c_str(),woodCrateTex->Resource, woodCrateTex->UploadHeap));mTextures[woodCrateTex->Name] = std::move(woodCrateTex);
}
以上LoadTextures方法通过CreateDDSTextureFromFile12方法创建了一个texture对象。
对于初学DX12的人可能会觉得奇怪,为什么这里的texture类有两个Resource。让我们把这里的CreateDDSTextureFromFile12方法展开:
std::unique_ptr<uint8_t[]> ddsData;
std::vector<D3D12_SUBRESOURCE_DATA> subresources;
DX::ThrowIfFailed(LoadDDSTextureFromFile(device, L"texture.dds", tex.ReleaseAndGetAddressOf(),ddsData, subresources));const UINT64 uploadBufferSize = GetRequiredIntermediateSize(tex.Get(), 0,static_cast<UINT>(subresources.size()));// Create the GPU upload buffer.
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);auto desc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);ComPtr<ID3D12Resource> uploadRes;
DX::ThrowIfFailed(device->CreateCommittedResource(&heapProps,D3D12_HEAP_FLAG_NONE,&desc,D3D12_RESOURCE_STATE_GENERIC_READ,nullptr,IID_PPV_ARGS(uploadRes.GetAddressOf())));UpdateSubresources(commandList, tex.Get(), uploadRes.Get(),0, 0, static_cast<UINT>(subresources.size()), subresources.data());
这里面最重要的是CreateCommittedResource和UpdateSubresources这两个方法。
CreateCommittedResource通过CPU上的线程把资源加载到UploadHeap上,然后UpdateSubresources通过GPU上的复制线程把UploadHeap上的资源复制到Resource上。
UploadHeap称为上传堆,Resource称为默认堆。默认堆是保存在GPU上的,CPU无法访问;而上传堆既可以被CPU访问,又可以被GPU访问。使用这种方法是因为D3D12引入了显示的显存管理,具体可以参考这篇文章:D3D12内存管理。
因此对于vertex buffer和constant buffer,也需要要用同样的方法来创建和上传数据。但是对于资源的上传,除了上述的UpdateSubresource()方法之外,还有Map()方法。
一般对于constant buffer通常使用Map,纹理和vertex buffer使用UpdateSubresource方法。两个方法的主要区别是资源是否需要频繁更新。constant buffer需要频繁修改(比如每帧的view matrix,需要每次渲染前上传更新),而vertex buffer和texture是并不会频繁更新。具体的资料详见how-to-use-updatesubresource-and-map-unmap