dx12 gpu memory
2. 堆与资源
当须要多个缓冲区资源来渲染场景时,对于这些资源,图形程序员必须决定如何建立这些缓冲区资源。在内存堆(ID3D12Heap)中分配GPU资源的方式有几种:app
- 已提交资源 (Committed Resources)
- 已放置资源 (Placed Resources)
- 预留资源 (Reserved Resources)
2.1 已提交资源 (Committed Resources)
函数接口:ID3D12Device::CreateCommittedResourcesvg
使用该方法建立Committed Resources时,会同时建立一个堆(implicit heap),该堆足够大以容纳Resources,资源也映射到堆。Committed Resources易于管理,由于图形程序员无需关心如何将资源放置在堆中。
函数
Committed Resources很是适合分配大型资源(内存资源使用较多的资源),例如纹理,它也一般用于在上传堆(upload heap)中建立大型资源;也可用于上传动态顶点或索引缓冲区。布局
2.2 已放置资源 (Placed Resources)
函数接口:
(1)ID3D12Device::CreateHeap
(2)ID3D12Device::CreatePlacedResource性能
已放置资源(Placed Resources)显式地放置在堆中,位于堆中的特定偏移处。因此在建立放置的资源以前,首先使用(1)方法建立一个堆,而后使用(2)方法在堆内部建立放置的资源。学习

尽管因为不须要为每一个资源从全局GPU内存分配堆,因此Placed Resources提供了更好的性能,可是正确使用Placed Resources有一些限制条件。
- 限制条件1: 必须预先知道将用于Placed Resource的堆的大小
建立一个过大的堆并非一个好主意,由于回收堆所使用的GPU内存的惟一方法是退出(evict )或彻底销毁(destroy ),这样的代价太大,并且在GPU上执行的命令列表(command list )中不得引用当前放置在堆中的任何资源。
- 限制条件2:不一样的堆,容许分配的Resource的类型不一样
- ALLOW_ONLY_BUFFERS : 缓冲区资源(顶点缓冲区,索引缓冲区,常量缓冲区,结构缓冲区等)
- ALLOW_ONLY_RT_DS_TEXTURES : 渲染目标和深度/模板资源
- ALLOW_ONLY_NON_RT_DS_TEXTURES : 非渲染目标纹理资源
- ALLOW_ALL_BUFFERS_AND_TEXTURES : 缓冲区资源和纹理资源
- 限制条件3:只要多个放置的资源不一样时访问相同的别名堆空间,它们就能够在堆中使用别名。
别名能够帮助减小过多分配的GPU内存使用量,由于能够将堆的大小设置为放置在堆中的最大资源的大小,只要堆中的同一空间未被多个别名同时使用,就可使用别名,可使用资源别名屏障(resource aliasing barrier)来交换(swap)别名资源。

比如创建上传堆,与创建默认堆类似,其代码如下:
D3D12_HEAP_DESC stUploadHeapDesc = { };
//尺寸依然是实际纹理数据大小的2倍并64K边界对齐大小
stUploadHeapDesc.SizeInBytes = GRS_UPPER(2 * n64UploadBufferSize, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT);
//注意上传堆肯定是Buffer类型,可以不指定对齐方式,其默认是64k边界对齐
stUploadHeapDesc.Alignment = 0;
stUploadHeapDesc.Properties.Type = D3D12_HEAP_TYPE_UPLOAD; //上传堆类型
stUploadHeapDesc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
stUploadHeapDesc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
//上传堆就是缓冲,可以摆放任意数据
stUploadHeapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
GRS_THROW_IF_FAILED(pID3DDevice->CreateHeap(&stUploadHeapDesc, IID_PPV_ARGS(&pIUploadHeap)));
这段代码与之前的类似,需要注意的就是Flags标志,这次我们又正面的指定只允许缓冲的类型D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,堆大小我们特意设置为两倍纹理图片数据的大小,并且上边界对齐。再一次领教它的反人类设计!
接着我们就可以像下面这样首先来创建上传堆上的纹理资源缓冲了:
GRS_THROW_IF_FAILED(pID3DDevice->CreatePlacedResource(
pIUploadHeap.Get()
, 0
, &CD3DX12_RESOURCE_DESC::Buffer(n64UploadBufferSize)
, D3D12_RESOURCE_STATE_GENERIC_READ
, nullptr
, IID_PPV_ARGS(&pITextureUpload)));
从上面代码我们可以看到其实只使用了一般不到的区域用来做我们的纹理资源数据缓冲了,那么剩下的部分如果不用的话就浪费了,所以我们可以接着象下面这样,把顶点缓冲也放在上面:
GRS_THROW_IF_FAILED(pID3DDevice->CreatePlacedResource(
pIUploadHeap.Get()
, GRS_UPPER(n64UploadBufferSize, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)
, &CD3DX12_RESOURCE_DESC::Buffer(nVertexBufferSize)
, D3D12_RESOURCE_STATE_GENERIC_READ
, nullptr
, IID_PPV_ARGS(&pIVertexBuffer)));
那么请注意第二个参数,我们设置了偏移量为纹理资源大小上边界对齐的位置,这里的对齐要求是必须的。从内存视图上看的话,其实就是我们将顶点缓冲区放在了需要上传的纹理资源图片数据的后面。这样我们就省去了一次顶点缓冲存储的实际的申请和释放操作,提高了效率和性能。
2.3 预留资源 (Reserved Resources)
函数接口:
(1) ID3D12Device::CreateReservedResource
(2) ID3D12CommandQueue::UpdateTileMappings
建立预留资源(Reserved Resources)时不指定用于放置资源的堆,首先使用(1)方法建立,在使用以前,必须使用(2)方法将其映射到堆。能够建立大于单个堆容量的预留资源,也可使用在GPU内存中的一个或多个堆来映射(和未映射)预留资源。

使用预留资源时,可使用虚拟内存保存大量的纹理数据,可是只映射使用的部分到物理内存,这也提供用于实现使用稀疏体素八叉树(sparse voxel octrees)的渲染技术的机会,并且不会超出GPU内存预算。
浙公网安备 33010602011771号