转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=3406
经过前面的练习,实现query已经没什么难的了。
Query heap
在D3D12里,query也是放在heap中。但和其他heap不同的是,query heap与其说是heap,不如说是array。它并不需要设置给设备,而是相当于一次可以提交多个query。Query heap的数量不限,所以为了简单实现,可以给每个query建立一个query heap。
D3D12_QUERY_HEAP_DESC query_heap_desc; query_heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION; query_heap_desc.Count = 1; query_heap_desc.NodeMask = 0; ID3D12QueryHeap* query_heap; TIF(device->CreateQueryHeap(&query_heap_desc, IID_ID3D12QueryHeap, reinterpret_cast<void**>(&query_heap))); query_heap_ = MakeCOMPtr(query_heap);
这就建立出一个occlusion query来了。使用的时候只要
cmd_list->BeginQuery(query_heap_.get(), D3D12_QUERY_TYPE_OCCLUSION, 0); ... cmd_list->EndQuery(query_heap_.get(), D3D12_QUERY_TYPE_OCCLUSION, 0);
读回结果
在D3D12里,query的结果是放在buffer里,而不是直接存入某个变量。所以,在建立出query heap之后,需要接着建立两个buffer。
D3D12_HEAP_PROPERTIES heap_prop; heap_prop.Type = D3D12_HEAP_TYPE_DEFAULT; heap_prop.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; heap_prop.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; heap_prop.CreationNodeMask = 0; heap_prop.VisibleNodeMask = 0; D3D12_RESOURCE_DESC res_desc; res_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; res_desc.Alignment = 0; res_desc.Width = sizeof(uint64_t); res_desc.Height = 1; res_desc.DepthOrArraySize = 1; res_desc.MipLevels = 1; res_desc.Format = DXGI_FORMAT_UNKNOWN; res_desc.SampleDesc.Count = 1; res_desc.SampleDesc.Quality = 0; res_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; res_desc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource* query_result; TIF(device->CreateCommittedResource(&heap_prop, D3D12_HEAP_FLAG_NONE, &res_desc, D3D12_RESOURCE_STATE_PREDICATION, nullptr, IID_ID3D12Resource, reinterpret_cast<void**>(&query_result))); query_result_ = MakeCOMPtr(query_result); heap_prop.Type = D3D12_HEAP_TYPE_READBACK; ID3D12Resource* query_result_readback; TIF(device->CreateCommittedResource(&heap_prop, D3D12_HEAP_FLAG_NONE, &res_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_ID3D12Resource, reinterpret_cast<void**>(&query_result_readback))); query_result_readback_ = MakeCOMPtr(query_result_readback);
query_result_这个buffer用来从query取回数据,也就是在EndQuery之后
D3D12_RESOURCE_BARRIER barrier; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = query_result_.get(); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PREDICATION; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; barrier.Transition.Subresource = 0; cmd_list->ResourceBarrier(1, &barrier); cmd_list->ResolveQueryData(query_heap_.get(), D3D12_QUERY_TYPE_OCCLUSION, 0, 1, query_result_.get(), 0); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; cmd_list->ResourceBarrier(1, &barrier);
这个buffer的heap type是default,所以不能被CPU读取。需要在此拷贝到那个query_result_readback_。
cmd_list->CopyResource(query_result_readback_.get(), query_result_.get()); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PREDICATION; cmd_list->ResourceBarrier(1, &barrier);
在从query_result_readback_取出数据之前,需要用fence进行一次同步。D3D11里是用while轮询的方式等query完成,D3D12则变成了fence等待,占用CPU少得多。
总结
D3D12的Query并不难用。在KlayGE里实现了occlusion query、conditional render(也叫binary occlusion query)、以及timer query。由于变成query heap的形式,API调用可以更少。
Comments