使用方法
事件查询步骤:
- pDeviceContext->Begin(pQuery)
- pDeviceContext->End(pQuery)
- pDeviceContext->GetData(pQuery , &queryData , sizeof(UINT64) , 0)
注意事项
Begin()
并非是必须的,但是End()
是必须的
End()
调用之后,必须在GetData()
成功之后才能继续调用End()
,否则将会报D3D Warning
常见用法
1 | D3D11_QUERY_DESC queryDesc; |
实际需求
公司残影实现是通过记录下角色的多帧的动画数据(蒙皮矩阵),然后将这些蒙皮数据写到Palette
上,然后在VS阶段用于蒙皮计算
现在我将所有的蒙皮计算单独挪到一个阶段
因此,残影的渲染需要先走一遍CS蒙皮,将蒙皮结果(Model空间)写到一个Buffer中,再将该Buffer作为渲染的输入流
1 | pDeviceContext->CSSetUnorderedAccessView(iStartSlot , 1 , &pGhostBuffer , nullptr); |
由于残影的存在时间很短,存在大量的创建与销毁,因此每个残影一个Buffer的话,将导致资源的重复创建与销毁
所以,我决定使用一个大的Buffer让所有的残影共享这段Buffer,并且可以通过限制残影的个数
1 | ID3D11Buffer* pGhostBuffer = nullptr; |
既然多个残影共享同一段Buffer,那么就可能出现即写又读的情况,造成GPU与GPU之间的同步等待,这个消耗非常巨大
因此,对于每个残影,都记录一个偏移以及数量,用于表示占用Buffer中的哪一段
1 | struct GhostSubBuffer |
每个残影创建之后,第一次运行CS时,尝试从Buffer中分配一段可用的区间,若分配失败,则该残影不渲,下一帧再继续检测
若分配成功,则走一次蒙皮,将数据缓存在Buffer中,之后渲染不需要再重复计算
1 | int bufferOffset = skincache_ghost_get_buffer(cloneEntity , pPartMesh); |
那么必然会涉及到一个何止回收这一段区间的问题,区间被回收之后,就意味着这段区间可被再次使用
因此,我们需要保证回收时这段区间已不再被GPU使用,从而避免同一段Buffer既作为SRV,又作为CS的输出UAV的情况
因为,可以为每一段分配的Buffer区间创建一个ID3D11Query* query
,在渲染命令之后调用pDeviceContext->End(pQuery)
1 | struct GhostSubBuffer |
然后每帧进行查询,若该残影已被销毁且GPU不再使用这段Buffer,则将其回收
因为我们查询GPU是否在使用这段Buffer并不希望一直等待,因此,可能要在多帧之后GetData()
才能返回TRUE
.
但在GetData()
返回TRUE
之前不能多次调用End()
,因此需要记录下相应的状态
1 | <Query Ghost Buffer GPU State> = |
ID3D11Query
可以将其放到一个池中,以便重复利用
Comments