2021년 3월 5일 금요일

DirectX11 - Occlusion Predicate, Query

Query

 여러 종류 ( Query 종류 ) 가 있으며 대부분은 디버깅 및 성능측정용이다. 

 값을 gpu 에서 cpu 로 들고오기 때문에 비용이 커보이지만 매 프레임마다 동기화되어 실행되지 않으므로 성능에 생각만큼 큰 영향을 주지 않는다. 

 사용법은 GetData 문서에 적혀있는데로 d3d_context->Begin() 를 Query 를 인자로 넣어서 실행시켜 그 사이에 실행한 데이터를 모으고 d3d_context->End() 를 마찬가지로 인자로 넣어서 종료시킨후 d3d_context->GetData() 를 통해 데이터를 읽는 방식이다. ( 종류에 따라 Begin, End 가 필요없을 수 있다.)


Occlusion Query

flare effect 같은 간단한 기법부터 ray-tracing 같은 고급 기법까지 여러 용도로 사용된다.

 렌더링한 데이터가 Depth/Stencil Test 에 얼마나 통과했는지 픽셀 갯수를 가져다주는데 이 데이터를 이용한다. 만약 DepthStencil 을 꺼놨으면 통과한걸로 친다.

 이때 Occlusion Prediction 과 같이 사용되는데 (cf. 다른 Predicate 종류는 stream-output-overflow predicates 뿐이다.) 이걸 켜놓은 상태에선 다 통과되지 않았거나(TRUE) 혹은 하나라도 통과되었거나(FALSE) 둘중의 한 경우에 Resource Manipulation Command ( Draw, Dispatch 같은거 ) 를 무효화 할 수 있어 성능향상을 꾀할 수 있다. ( Query 종류 참고). 켜는 함수는 SetPrediction 으로 끌때는 nullptr 을 넣으면 된다. Predicate

 Occlusion Query 는 한번 Begin() 를 한 후에 그 값이 cpu 에 올때까지 시간이 걸린다. 값이 업데이트 되기 전에는 GetData() 의 리턴값이 거짓으로 나오며 이때 다시 Begin() 을 호출 시 기존의 Query 는 무효화되고 다시 질의하게 된다. 그러므로 GetData() 의 리턴값이 참이 된 후에 다시 Begin() 을 호출해야한다. 반대로 Prediction 은 이런거 상관없다.


예제 

context->Begin(predict->GetPredictate());
if (query->IsQueried() == false)
{
    context->Begin(query->GetQuery());
    ...
    context->Draw(2, 0);
    context->End(query->GetQuery());
    query->SetIsQueried(true);
}
else context->Draw(2, 0);
context->End(predict->GetPredictate());

UINT64 visibility;
if (context->GetData(query->GetQuery(), &visibility, sizeof(visibility), 0) == S_OK)
{
    query->SetQueried(false);
    query->SetValue(visibility / 3000.f);
}

context->SetPredicate(predict, FALSE);
{
    context->Draw(2, 0);

    ... 

    Update_Constant(query->GetValue());
    context->Draw(6, 0);
}
context->SetPredicate(nullptr, FALSE);

 query 는 임의의 클래스로 안에  ID3D11Query 가 있고 Query 여부를 위한 bool 값과 편의를 위해 float 결과값을 저장한다. predicate 도 비슷하며 ID3D11Predicate 가 저장되어 있다.  

 처음에 query 여부 ( Begin 여부 ) 를 살펴서 Begin 을 호출하는 것을 할 수 있다. Begin 과 Eng 사이에는 Depth/Stencil Test 여부를 살피기위한 Resource Manipulation Command 를 포함한 코드가 들어가 있다. 

 얼마나 픽셀이 통과했는지는 GetData 의 함수를 통해 얻을 수 있다. CPU 에 Query 값이 도달하면 S_OK 를 리턴한다. 이때 데이터는 UINT64 형식으로 가져올 수 있다. 결과는 픽셀 수이므로 렌더링될 최대 픽셀의 기댓값을 나누어주면 비율을 구할 수 있다.

 SetPredicate 는 False 로 지정되어 있는데 위에서의 Begin~End 사이에서 하나라도 Test 를 통과하지 않았으면 이후의 Resource Manipulation Command 을 무효화 한다는 말이다. 필요한 만큼 렌더링 후 null 로 꺼야한다.





List