共用方式為


查詢 (Direct3D 9)

有數種類型的查詢是設計來查詢資源狀態。 指定資源的狀態包括圖形處理器 (GPU) 狀態、驅動程式狀態或運行時間狀態。 若要瞭解不同查詢類型之間的差異,您需要了解查詢狀態。 下列狀態轉換圖表說明每個查詢狀態。

顯示查詢狀態之間轉換的圖表 圖表

此圖顯示三種狀態,每個狀態都是由圓形所定義。 每個實線都是導致狀態轉換的應用程式驅動事件。 虛線是資源驅動事件,會將查詢從發出的狀態切換至訊號狀態。 每個狀態都有不同的用途:

  • 訊號狀態就像是閑置狀態。 已產生查詢物件,並正在等候應用程式發出查詢。 一旦查詢完成並轉換回訊號狀態,就可以擷取查詢的答案。
  • 建置狀態就像查詢的暫存區域。 從建置狀態開始,已發出查詢(藉由呼叫 D3DISSUE_BEGIN),但尚未轉換為已發行的狀態。 當應用程式發出查詢結束時(藉由呼叫 D3DISSUE_END),查詢會轉換成已發出的狀態。
  • 發出的狀態表示所查詢的資源可以控制查詢。 一旦資源完成其工作,資源會將狀態機器轉換為訊號狀態。 在發出狀態期間,應用程式必須輪詢以偵測轉換至訊號狀態。 轉換至訊號狀態之後,GetData 會將查詢結果(透過自變數)傳回至應用程式。

下表列出可用的查詢類型。

查詢類型 問題事件 GetData 緩衝區 運行 隱含的查詢開頭
BANDWIDTHTIMINGS D3DISSUE_BEGIND3DISSUE_END D3DDEVINFO_D3D9BANDWIDTHTIMINGS 零售/偵錯 N/A
CACHEUTILIZATION D3DISSUE_BEGIND3DISSUE_END D3DDEVINFO_D3D9CACHEUTILIZATION 零售/偵錯 N/A
事件 D3DISSUE_END BOOL 零售/偵錯 CreateDevice
INTERFACETIMINGS D3DISSUE_BEGIND3DISSUE_END D3DDEVINFO_D3D9INTERFACETIMINGS 零售/偵錯 N/A
閉塞 D3DISSUE_BEGIND3DISSUE_END DWORD 零售/偵錯 N/A
PIPELINETIMINGS D3DISSUE_BEGIND3DISSUE_END D3DDEVINFO_D3D9PIPELINETIMINGS 零售/偵錯 N/A
RESOURCEMANAGER D3DISSUE_END D3DDEVINFO_ResourceManager 僅偵錯 簡報
時間戳 D3DISSUE_END UINT64 零售/偵錯 N/A
TIMESTAMPDISJOINT D3DISSUE_BEGIND3DISSUE_END BOOL 零售/偵錯 N/A
TIMESTAMPFREQ D3DISSUE_END UINT64 零售/偵錯 N/A
VCACHE D3DISSUE_END D3DDEVINFO_VCACHE 零售/偵錯 CreateDevice
VERTEXSTATS D3DISSUE_END D3DDEVINFO_D3DVERTEXSTATS 僅偵錯 簡報
VERTEXTIMINGS D3DISSUE_BEGIND3DISSUE_END D3DDEVINFO_D3D9STAGETIMINGS 零售/偵錯 N/A

 

某些查詢需要開始和結束事件,而其他查詢則只需要結束事件。 只有在發生另一個隱含事件時,才需要結束事件的查詢(這會列在數據表中)。 所有查詢都會傳回答案,但回應一律 TRUE 的事件查詢除外。 應用程式會使用查詢的狀態或 getData傳回碼。

建立查詢

在建立查詢之前,您可以呼叫 CreateQuery,並搭配 NULL 指標來檢查運行時間是否支持查詢:

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 中傳回。 例如,以下是傳回在轉譯序列中繪製的像素數(或啟用多重取樣時樣本)的事件序列:

  • 建立查詢。
  • 發出開始事件。
  • 繪製一些東西。
  • 發出結束事件。

以下是對應的程式代碼序列:

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,這會提供更多 up-to日期資訊,如果查詢處於發行狀態,您可以提供更輕量檢查的零。 提供零會導致 GetData 不會排清命令緩衝區。 基於這個理由,必須小心避免無限迴圈(如需詳細資訊,請參閱 GetData)。 由於運行時間在命令緩衝區中排入工作佇列,D3DGETDATA_FLUSH 是將命令緩衝區排清至驅動程序的機制(因此是 GPU;請參閱 正確分析 Direct3D API 呼叫 (Direct3D 9))。 在命令緩衝區排清期間,查詢可能會轉換成訊號狀態。

範例:事件查詢

事件查詢不支援 begin 事件。

  • 建立查詢。
  • 發出結束事件。
  • 輪詢直到 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 週期。

查詢是轉譯期間要用來提升效能的優化。 因此,花點時間等候查詢完成並不有説明。 如果發出查詢,且應用程式檢查結果時尚未就緒,則優化嘗試不會成功,而且轉譯應該會如常一樣繼續。

此範例的經典範例是在遮蔽 Culling 期間。 除了上述 循環時,使用查詢的應用程式可以實作遮蔽功能來檢查查詢是否在需要結果時完成。 如果查詢尚未完成,請繼續 (作為最壞的情況),就好像所測試的對象沒有遮蔽(亦即可見),並轉譯它。 程式代碼看起來會像下面這樣。

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();
}

進階主題