D3D12엔진개발 – Hierachical z Map Occlusion Culling

D3D11버전에서 구현했던 Compute Shader를 이용한 Hi-Z Occlusion Culling을 D3D12엔진에서 구현했다.

기본 개념과 구현에 대해서는 자세히 적지 않겠다.
예전에 KASA에서 밝표했던 내용이 있으므로 발표자료를 첨부한다.

D3D12에서 Compute Shader를 사용하려면 어떤 API들을 사용해야하는지 찾느라 좀 애먹었다.

Compute Shader를 위한 ID3DPipelineState를 만들려면D3D12_GRAPHICS_PIPELINE_STATE_DESC구조체 대신 D3D12_COMPUTE_PIPELINE_STATE_DESC를 사용해야한다. 또한 CreateGraphicsPipelineState()대신 ID3D12Device::CreateComputePipelineState()를 사용한다. 이거 몰라서 한참 헤맸다.
다음과 같이 Compute Shader에서 사용할 ID3DPipelineState를 만든다.

D3D12_COMPUTE_PIPELINE_STATE_DESC computePsoDesc = {};
	computePsoDesc.pRootSignature = m_pRootSignature_Cull;
	computePsoDesc.CS = CD3DX12_SHADER_BYTECODE(m_pCS_Cull->pCodeBuffer,m_pCS_Cull->dwCodeSize);

	if (FAILED(pResourceManager->CreateComputePipelineState(&computePsoDesc,&m_pPipelineState_Cull)))
		__debugbreak();

그리고 Compute Shader를 호출하는 코드는 다음과 같다.
대충 이런 스타일이다. 참고삼아 함수 하나를 다 적었다.

void CHIZQueryManager::Cull(ID3D12GraphicsCommandList*	pCommandList,PLANE* pPlaneList,DWORD dwPlaneNum,HIZ_OCC_TEST* pOccludeeList,UINT uiOccludeeNum)
{
	ID3D12Device*				pDevice = m_pRenderer->INL_GetD3DDevice();
	
	CAMERA_DESC_COMMON*		pCamDesc = m_pRenderer->INL_GetCameraDesc();

#ifdef _DEBUG
	if (dwPlaneNum != 6)
		__debugbreak();
#endif
	for (DWORD i=0; i<dwPlaneNum; i++) 	{ 		*(PLANE*)&m_pConstBufferZCull->FrustumPlanes[i].x = *(PLANE*)(pPlaneList+i);
	}
	MATRIX4*	pMatView = m_pRenderer->INL_GetViewMatrix();
	MATRIX4*	pMatProj = m_pRenderer->INL_GetProjMatrix();
	
	MATRIX4	matViewProj;
	MatrixMultiply2(&matViewProj,pMatView,pMatProj);

	*(VECTOR3*)&m_pConstBufferZCull->EyePos = pCamDesc->v3From;

	*(VECTOR3*)&m_pConstBufferZCull->Up = pCamDesc->v3Up;
	m_pConstBufferZCull->EyePos.w = 1.0F;
	m_pConstBufferZCull->Up.w = 1.0f;
	m_pConstBufferZCull->matViewProj = matViewProj;
	m_pConstBufferZCull->ViewportSize.x = (float)m_Width;
	m_pConstBufferZCull->ViewportSize.y = (float)m_Height;
	float	Far = m_pRenderer->INL_GetFar();
	m_pConstBufferZCull->FarRcp = 1.0f / Far;
	m_pConstBufferZCull->OccludeeNum = uiOccludeeNum;
	TransposeMatrix(&m_pConstBufferZCull->matViewProj);

	if (uiOccludeeNum > m_uiMaxOccludeeNum)
	{
		CleanupBuffersForCull();
		CreateBuffersForCull(uiOccludeeNum);
	}
	
	VECTOR4*	pDest = (VECTOR4*)m_pBoundsInputBuffer;
	for (DWORD i=0; i<uiOccludeeNum; i++) 	{ 		*(VECTOR3*)&pDest->x = pOccludeeList[i].bs.v3Point;
		pDest->w = pOccludeeList[i].bs.fRs;
		pDest++;
	}

	pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pBoundsBuffer, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COPY_DEST));
	pCommandList->CopyResource(m_pBoundsBuffer,m_pBoundsBufferWrite);
	pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pBoundsBuffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE));
	
	(m_pCullResultList+i,&Bounds,pSysBufferZCull->EyePos,pSysBufferZCull->Up,matViewProj,pSysBufferZCull->FrustumPlanes,pSysBufferZCull->ViewportSize,pSysBufferZCull->FarRcp);
	//}
	//m_uiCullResultNum = uiOccludeeNum;

	pCommandList->SetPipelineState(m_pPipelineState_Cull);
	pCommandList->SetComputeRootSignature(m_pRootSignature_Cull);

	ID3D12DescriptorHeap* ppHeaps[] = { m_pCommonHeap_Cull,m_pSamplerHeap_Cull };
	pCommandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
	pCommandList->SetComputeRootDescriptorTable(0,m_pCommonHeap_Cull->GetGPUDescriptorHandleForHeapStart());
	pCommandList->SetComputeRootDescriptorTable(1,m_pSamplerHeap_Cull->GetGPUDescriptorHandleForHeapStart());

	UINT	GroupNum = (uiOccludeeNum / MAX_CULL_THREAD_NUM) + ((uiOccludeeNum % MAX_CULL_THREAD_NUM) != 0);
    pCommandList->Dispatch(GroupNum,1,1);	

	pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pCulledResultBuffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE));
	pCommandList->CopyResource(m_pCulledResultBufferSystem,m_pCulledResultBuffer);
	pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pCulledResultBuffer, D3D12_RESOURCE_STATE_COPY_SOURCE,D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
    
	pCommandList->Close();
}

일단 잘 작동하는데 최적화가 필요하다.
CommandList를 미리 만들어두고 bundle로 사용할 수 있으므로 그렇게 하도록 수정해야한다. 일단 불필요하게 wait하는 부분도 많다.

그리고 예전에 무시했던 D3DQuery를 이용한 Occlusion Culling이 제법 효과가 있었다는걸 깨달았다. D3D12버전에선 D3DQuery를 뺄 생각이었는데 넣어야겠다.

스샷을 보면 윗쪽에 Down-sampleing된 depth맵들이 보인다.
오브젝트들 위로 표시된 숫자들은 Compute Shader에서 어떤 mipmpa을 선택할지를 보여준다. 오브젝트가 크면 클수록(화면에서 차지하는 면적이 클 수록) 해상도가 낮은 mipmap을 선택하게 되고 그만큼더 빠르게 처리된다.

d3d12_hi-z-occ.png


D3D12엔진개발 – Hierachical z Map Occlusion Culling”에 대한 답글 2개

답글 남기기

댓글을 게시하려면 다음의 방법 중 하나를 사용하여 로그인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중