쿼리(Direct3D 9)

리소스의 상태 쿼리하도록 설계된 여러 유형의 쿼리가 있습니다. 지정된 리소스의 상태 GPU(그래픽 처리 장치) 상태, 드라이버 상태 또는 런타임 상태 포함됩니다. 서로 다른 쿼리 형식 간의 차이점을 이해하려면 쿼리 상태를 이해해야 합니다. 다음 상태 전환 다이어그램에서는 각 쿼리 상태를 설명합니다.

쿼리 상태 간의 전환을 보여 주는 다이어그램

다이어그램은 각각 원으로 정의된 세 가지 상태를 보여 줍니다. 각 실선은 상태 전환을 유발하는 애플리케이션 기반 이벤트입니다. 파선은 쿼리를 발급된 상태에서 신호 상태로 전환하는 리소스 기반 이벤트입니다. 이러한 각 상태는 다른 용도로 사용됩니다.

  • 신호 상태는 유휴 상태와 같습니다. 쿼리 개체가 생성되었으며 애플리케이션이 쿼리를 실행하기를 기다리고 있습니다. 쿼리가 완료되고 신호가 지정된 상태로 다시 전환되면 쿼리에 대한 답변을 검색할 수 있습니다.
  • 빌드 상태는 쿼리의 준비 영역과 같습니다. 빌드 상태에서 쿼리가 실행되었지만( D3DISSUE_BEGIN 호출하여) 아직 발급된 상태로 전환되지 않았습니다. 애플리케이션이 D3DISSUE_END 호출하여 쿼리 종료를 실행하면 쿼리가 발급된 상태로 전환됩니다.
  • 발급된 상태는 쿼리되는 리소스가 쿼리를 제어한다는 것을 의미합니다. 리소스가 작업을 완료하면 리소스는 상태 머신을 신호 상태로 전환합니다. 발급된 상태 중에 애플리케이션은 폴링하여 신호된 상태로의 전환을 감지해야 합니다. 신호가 있는 상태로 전환되면 GetData 는 쿼리 결과(인수를 통해)를 애플리케이션에 반환합니다.

다음 표에는 사용 가능한 쿼리 유형이 나열됩니다.

쿼리 유형 문제 이벤트 GetData 버퍼 런타임 쿼리의 암시적 시작
대역폭 D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9BANDWIDTHTIMINGS 소매/디버그 해당 없음
CACHEUTILIZATION D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9CACHEUTILIZATION 소매/디버그 해당 없음
이벤트 D3DISSUE_END BOOL 소매/디버그 CreateDevice
INTERFACETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9INTERFACETIMINGS 소매/디버그 해당 없음
폐색 D3DISSUE_BEGIN, D3DISSUE_END DWORD 소매/디버그 해당 없음
PIPELINETIMINGS D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9PIPELINETIMINGS 소매/디버그 해당 없음
Resourcemanager D3DISSUE_END D3DDEVINFO_ResourceManager 디버그만 표시
timestamp D3DISSUE_END UINT64 소매/디버그 해당 없음
TIMESTAMPDISJOINT D3DISSUE_BEGIN, D3DISSUE_END BOOL 소매/디버그 해당 없음
TIMESTAMPFREQ D3DISSUE_END UINT64 소매/디버그 해당 없음
Vcache D3DISSUE_END D3DDEVINFO_VCACHE 소매/디버그 CreateDevice
꼭짓점 통계 D3DISSUE_END D3DDEVINFO_D3DVERTEXSTATS 디버그만 표시
꼭짓점 D3DISSUE_BEGIN, D3DISSUE_END D3DDEVINFO_D3D9STAGETIMINGS 소매/디버그 해당 없음

 

일부 쿼리에는 시작 및 종료 이벤트가 필요하고 다른 쿼리에는 end 이벤트만 필요합니다. 종료 이벤트만 필요한 쿼리는 테이블에 나열된 다른 암시적 이벤트가 발생할 때 시작됩니다. 응답이 항상 TRUE인 이벤트 쿼리를 제외한 모든 쿼리는 답변을 반환 합니다. 애플리케이션은 쿼리의 상태 또는 GetData의 반환 코드를 사용합니다.

쿼리 만들기

쿼리를 만들기 전에 다음과 같은 NULL 포인터를 사용하여 CreateQuery를 호출하여 런타임이 쿼리를 지원하는지 검사 수 있습니다.

IDirect3DQuery9* pEventQuery;

// Create a device pointer m_pd3dDevice

// Create a query object
HRESULT hr = m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, NULL);

이 메서드는 쿼리를 만들 수 있는 경우 성공 코드를 반환합니다. 그렇지 않으면 오류 코드를 반환합니다. CreateQuery가 성공하면 다음과 같은 쿼리 개체를 만들 수 있습니다.

IDirect3DQuery9* pEventQuery;
m_pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

이 호출이 성공하면 쿼리 개체가 만들어집니다. 쿼리는 기본적으로 발급되기를 기다리는 신호 상태(초기화되지 않은 답변 포함)에서 유휴 상태입니다. 쿼리를 마치면 다른 인터페이스처럼 해제합니다.

쿼리 실행

애플리케이션은 쿼리를 실행하여 쿼리 상태를 변경합니다. 쿼리를 실행하는 예제는 다음과 같습니다.

IDirect3DQuery9* pEventQuery;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

// Issue a Begin event
pEventQuery->Issue(D3DISSUE_BEGIN);

or

// Issue an End event
pEventQuery->Issue(D3DISSUE_END);

신호를 받은 상태의 쿼리는 다음과 같이 전환됩니다.

문제 유형 쿼리 전환이 로 전환됩니다. . .
D3DISSUE_BEGIN 빌드 상태입니다.
D3DISSUE_END 발급된 상태입니다.

 

다음과 같이 빌드 상태의 쿼리가 다음과 같이 전환됩니다.

문제 유형 쿼리 전환이 로 전환됩니다. . .
D3DISSUE_BEGIN (전환 없음, 건물 상태에 유지됩니다. 쿼리 대괄호를 다시 시작합니다.)
D3DISSUE_END 발급된 상태입니다.

 

발급된 상태의 쿼리는 다음과 같이 전환됩니다.

문제 유형 쿼리 전환이 로 전환됩니다. . .
D3DISSUE_BEGIN 상태를 빌드하고 쿼리 대괄호를 다시 시작합니다.
D3DISSUE_END 기존 쿼리를 중단한 후 발급된 상태입니다.

 

쿼리 상태 확인 및 쿼리에 대한 답변 가져오기

GetData 는 다음 두 가지 작업을 수행합니다.

  1. 반환 코드에서 쿼리 상태를 반환합니다.
  2. pData의 쿼리에 대한 답변을 반환합니다.

세 가지 쿼리 상태 각각에서 GetData 반환 코드는 다음과 같습니다.

상태 쿼리 GetData 반환 코드
신호 S_OK
빌딩 오류 코드
발급됨 S_FALSE

 

예를 들어 쿼리가 발급된 상태이고 쿼리에 대한 답변을 사용할 수 없는 경우 GetData 는 S_FALSE 반환합니다. 리소스가 작업을 완료하고 애플리케이션이 쿼리 종료를 실행하면 리소스는 쿼리를 신호 상태로 전환합니다. 신호가 지정된 상태에서 GetData 는 S_OK 반환합니다. 즉, 쿼리에 대한 답변도 pData로 반환됩니다. instance 경우 렌더링 시퀀스에서 그린 픽셀 수(또는 다중 샘플링을 사용하는 경우 샘플)를 반환하는 이벤트 시퀀스는 다음과 같습니다.

  • 쿼리 만들기.
  • 시작 이벤트를 실행합니다.
  • 무언가를 그립니다.
  • 종료 이벤트를 실행합니다.

다음은 해당 코드 시퀀스입니다.

IDirect3DQuery9* pOcclusionQuery;
DWORD numberOfSamplesDrawn;

m_pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery);

// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_BEGIN);

// API render loop
...
Draw(...)
...

// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue(D3DISSUE_END);

// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pOcclusionQuery->GetData( &numberOfSamplesDrawn, 
                                  sizeof(DWORD), D3DGETDATA_FLUSH ))
    ;

// To get the number of pixels drawn when multisampling is enabled,
// divide numberOfSamplesDrawn by the sample count of the render target.

이러한 코드 줄은 다음과 같은 몇 가지 작업을 수행합니다.

  • GetData를 호출하여 그린 픽셀/샘플 수를 반환합니다.
  • 리소스가 쿼리를 신호 상태로 전환할 수 있도록 D3DGETDATA_FLUSH 지정합니다.
  • 루프에서 GetData를 호출하여 쿼리 리소스를 폴링합니다. GetData가 S_FALSE 반환하는 한 리소스가 아직 답변을 반환하지 않았다는 의미입니다.

GetData의 반환 값은 기본적으로 쿼리의 상태를 알려줍니다. 가능한 값은 S_OK, S_FALSE 및 오류입니다. 빌드 상태에 있는 쿼리에서 GetData 를 호출하지 마세요.

  • S_OK 리소스(GPU 또는 드라이버 또는 런타임)가 완료됨을 의미합니다. 쿼리가 신호를 받은 상태로 돌아갑니다. GetData에서 반환되는 답변(있는 경우)입니다.
  • S_FALSE 리소스(GPU 또는 드라이버 또는 런타임)가 아직 답변을 반환할 수 없음을 의미합니다. GPU가 완료되지 않았거나 아직 작업을 보지 못했기 때문일 수 있습니다.
  • 오류는 쿼리가 복구할 수 없는 오류를 생성했음을 의미합니다. 쿼리 중에 디바이스가 손실된 경우일 수 있습니다. 쿼리가 S_FALSE 이외의 오류를 생성한 후에는 쿼리를 다시 만들어야 합니다. 그러면 쿼리 시퀀스가 신호 상태에서 다시 시작됩니다.

최신 정보를 제공하는 D3DGETDATA_FLUSH 지정하는 대신 쿼리가 발급된 상태인 경우 더 가벼운 검사 0을 제공할 수 있습니다. 0을 제공하면 GetData 가 명령 버퍼를 플러시하지 않습니다. 이러한 이유로 무한 루프를 방지하려면 주의해야 합니다(자세한 내용은 GetData 참조). 런타임 큐가 명령 버퍼에서 작동하므로 D3DGETDATA_FLUSH 명령 버퍼를 드라이버로 플러시하는 메커니즘입니다(따라서 GPU; Direct3D API 호출 정확도 프로파일링(Direct3D 9)참조). 명령 버퍼 플러시 중에 쿼리가 신호 상태로 전환될 수 있습니다.

예: 이벤트 쿼리

이벤트 쿼리는 시작 이벤트를 지원하지 않습니다.

  • 쿼리 만들기.
  • 종료 이벤트를 실행합니다.
  • GPU가 유휴 상태가 될 때까지 폴링합니다.
  • 종료 이벤트를 실행합니다.
IDirect3DQuery9* pEventQuery = NULL;
m_pD3DDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pEventQuery);

// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);

// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
    ;

... // API calls

// Add an end marker to the command buffer queue.
pEventQuery->Issue(D3DISSUE_END);

// Force the driver to execute the commands from the command buffer.
// Empty the command buffer and wait until the GPU is idle.
while(S_FALSE == pEventQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ))
    ;

이는 이벤트 쿼리가 API(애플리케이션 프로그래밍 인터페이스) 호출을 프로파일링하는 데 사용하는 명령 시퀀스입니다( Direct3D API 호출 정확도 프로파일링(Direct3D 9)참조). 이 시퀀스는 마커를 사용하여 명령 버퍼의 작업 양을 제어합니다.

따라서 운영 체제가 커널 모드로 전환되어 상당한 성능 저하가 발생하기 때문에 애플리케이션은 명령 버퍼 플러시와 관련된 큰 비용에 특히 주의해야 합니다. 또한 애플리케이션은 쿼리가 완료될 때까지 대기하여 CPU 주기를 낭비하는 것을 알고 있어야 합니다.

쿼리는 렌더링 중에 성능을 향상시키는 데 사용할 최적화입니다. 따라서 쿼리가 완료될 때까지 기다리는 데 시간을 할애하는 것은 도움이 되지 않습니다. 쿼리가 실행되고 애플리케이션이 쿼리를 검사할 때까지 결과가 아직 준비되지 않은 경우 최적화 시도가 성공하지 못하고 렌더링이 정상적으로 계속되어야 합니다.

이 것의 고전적인 예는 폐색 컬링 중입니다. 위의 while 루프 대신 쿼리를 사용하는 애플리케이션은 결과를 필요로 할 때까지 쿼리가 완료되었는지 확인하기 위해 검사 폐색 컬링을 구현할 수 있습니다. 쿼리가 완료되지 않은 경우 테스트 중인 개체가 가려지지 않은 것처럼(예: 표시되는 경우) 계속하여 렌더링합니다. 코드는 다음과 유사합니다.

IDirect3DQuery9* pOcclusionQuery = NULL;
m_pD3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, &pOcclusionQuery );

// Add a begin marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_BEGIN );

... // API calls

// Add an end marker to the command buffer queue.
pOcclusionQuery->Issue( D3DISSUE_END );

// Avoid flushing and letting the CPU go idle by not using a while loop.
// Check if queries are finished:
DWORD dwOccluded = 0;
if( S_FALSE == pOcclusionQuery->GetData( &dwOccluded, sizeof(DWORD), 0 ) )
{
    // Query is not done yet or object not occluded; avoid flushing/wait by continuing with worst-case scenario
    pSomeComplexMesh->Render();
}
else if( dwOccluded != 0 )
{
    // Query is done and object is not occluded.
    pSomeComplexMesh->Render();
}

고급 항목