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 僅偵錯 目前
timestamp 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的傳回碼。

建立查詢

在建立查詢之前,您可以藉由呼叫具有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中傳回。 例如,以下是在轉譯序列中繪製多重) 取樣時傳回圖元數 (或樣本的事件序列:

  • 建立查詢。
  • 發出開始事件。
  • 繪製某個專案。
  • 發出結束事件。

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

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,以提供更最新的資訊,如果查詢處於已發出狀態,您可以提供更輕量檢查的零。 提供零會導致 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 週期。

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

這的傳統範例是在遮蔽 Culling 期間。 除了上述 的 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();
}

進階主題