編輯

共用方式為


Direct3D 10 常見問題集

本文包含從將現有應用程式從 Direct3D 9 (D3D) 9 移植到 Direct3D 10 (D3D10) 開發人員的觀點,包含一些關於 Direct3D 10 的常見問題。

常數緩衝區

更新常數緩衝區的最佳方式為何?

UpdateSubresource 和 Map with Discard 應該大約是相同的速度。 視複製最少的記憶體量而定,選擇它們之間。 如果您已經將資料儲存在一個連續區塊的記憶體中,請使用 UpdateSubresource。 如果您需要累積來自其他位置的資料,請使用 Map with Discard。

組織常數緩衝區的最差方式為何?

將特定著色器的所有常數放入一個常數緩衝區,可實現最差的效能。 雖然這通常是從 D3D9 移植到 D3D10 的最簡單方式,但它可能會阻礙效能。 例如,請考慮使用下列常數緩衝區的案例:

cbuffer VSGlobalsCB
{
    matrix  ViewProj;
    matrix  Bones[100];
    matrix  World;
    float   SpecPower;
    float4  BDRFCoefficients;
    float   AppTime;
    uint2   RenderTargetSize;
};

緩衝區是 6560 個位元組。 假設有一個應用程式要轉譯 1000 個物件,其中 100 個是面板網格,其中 900 個是靜態網格。 此外,假設此應用程式使用陰影對應搭配一個光源。 這表示有兩個階段,一個用於從光線轉譯的深度圖,另一個用於正向轉譯階段。 這會導致 2000 個繪製呼叫。 雖然每個繪製呼叫不需要更新常數緩衝區的每個部分,但整個常數緩衝區仍會更新並傳送至卡片。 這會導致每一個畫面格更新 13 MB 的資料, (2000 個繪製呼叫次數 6560 KB) 。

組織常數緩衝區的最佳方式為何?

最佳方法是依更新頻率來組織常數緩衝區。 以類似頻率更新的常數應該位於相同的緩衝區中。 例如,請考慮下列案例:「組織常數緩衝區最差的方式為何?」,但具有較佳的常數配置:

cbuffer VSGlobalPerFrameCB
  { 
    float   AppTime; 
  };
cbuffer VSPerSkinnedCB
  { 
    matrix  Bones[100]; 
  };
cbuffer VSPerStaticCB
  {
    matrix  World;
  };
cbuffer VSPerPassCB
  {
    matrix  ViewProj;
    uint2   RenderTargetSize;
  };
cbuffer VSPerMaterialCB
  {
    float   SpecPower;
    float4  BDRFCoefficients;
  };    

常數緩衝區會根據其更新頻率進行分割,但這只是解決方案的一半。 應用程式必須正確更新常數緩衝區,才能充分利用分割。 我們將假設與上述場景相同:900 個靜態網格、100 個面板網格、一個光線傳遞,一個向前傳遞。 我們也會假設會儲存每個物件的一些常數緩衝區。 這表示每個物件都會包含 VSPerSkinnedCB 或 VSPerStaticCB,視其外觀或靜態而定。 我們會這麼做,以避免將透過管線傳送的矩陣數量加倍。

我們會將框架分割成三個階段。 第一個階段是畫面的開頭,而且不涉及任何轉譯,只是常數更新。

開始畫面

  • 針對應用程式時間更新 VSGlobalPerFrameCB (4 個位元組)
  • 更新 100 個外觀物件的 VSPerSkinnedCB (640000 個位元組)
  • 更新 900 個靜態物件的 VSPerStaticCB (57600 個位元組)

接下來是陰影貼圖傳遞。 請注意,實際更新的唯一常數緩衝區是 VSPerPassCB。 所有其他常數緩衝區都會在開始畫面傳遞期間更新。 雖然我們仍然需要系結這些常數緩衝區,但傳遞至視訊卡的資訊量很小,因為緩衝區已經更新。

陰影傳遞

  • 更新 VSPerPassCB (72 個位元組)
  • 繪製 100 個外觀網格 (100 個系結,沒有更新)
  • 繪製 900 個靜態網格 (100 個系結,沒有更新)

同樣地,正向轉譯傳遞只需要更新每個材質的資料,因為它並未儲存每個網格。 如果我們假設場景中有 500 個材質正在使用:

正向傳遞

  • 更新 VSPerPassCB (72 個位元組)
  • 更新 500 VSPerMaterialCBs (10000 個位元組)

這總共會產生 707 KB。 雖然這是非常頻繁的案例,但它說明藉由依更新頻率排序常數可以降低多少常數更新額外負荷。

如果我沒有足夠的空間來儲存網格、材質等個別常數緩衝區,該怎麼辦?

您一律可以使用固定緩衝區的分層系統。 建立可變大小的常數緩衝區 (16 個位元組、32 個位元組、64 個位元組等等,最多) 最多所需的最大常數緩衝區大小。 當將常數緩衝區系結至著色器時,請選取可保存著色器所需資料的最小常數緩衝區。 雖然這種方法較不有效率,但它是不錯的中繼步驟。

我在不同的著色器之間共用常數緩衝區。 一個著色器可能會使用所有常數,但另一個著色器可能會使用一些常數。 更新這些方法的最佳方式為何?

其中一種方法是進一步分割常數緩衝區。 不過,有一點會系結太多常數緩衝區。 在此情況下,將數個著色器可能未使用的常數移至常數緩衝區的結尾。 從著色器取得變數資料時,請使用來自D3D10_SHADER_VARIABLE_DESC的 D3D10_SVF_USED 旗標來判斷是否使用變數。 藉由將未使用的變數放在常數緩衝區的結尾,您可以將較小的緩衝區系結至不使用這些變數的著色器,進而節省更新成本。

如果我只為每個畫面上傳一次字元的骨頭,而不是每一次/繪製一次,我可以改善畫面播放速率多少?

視備援資料量而定,您可以改善介於 8% 到 50% 之間的畫面播放速率。 在最糟的情況下,不會降低效能。

我應該一次系結多少個常數緩衝區?

系結最少的常數緩衝區數目,以將所有資料放入著色器。 在實際案例中,建議使用五個常數緩衝區。 在著色器之間共用常數緩衝區, (將相同的 CB 系結至 VS 和 PS) 也可以改善效能。

系結常數緩衝區的成本是否不需要使用它們?

是,如果您實際上未使用緩衝區,請勿呼叫 VSSetConsantBuffer 或 PSSetConstantBuffer。 這個額外的 API 額外負荷可能會隨著多個繪製呼叫而增加。

狀態

在 D3D10 中管理狀態的最佳方式為何?

最佳解決方案是先知道所有狀態,並預先建立狀態物件。 這表示在轉譯時間,狀態系結是唯一需要執行的作業。 D3D10 也會篩選掉重複專案。

我的遊戲已動態載入或具有使用者產生的內容。 我無法預先載入所有的狀態物件。 我該怎麼辦?

這裡有兩個解決方案。 第一個是只即時建立狀態物件,並讓 D3D10 篩選掉重複專案。 不過,不建議針對每個畫面上有許多狀態物件變更的案例使用。 更好的解決方案是自行雜湊狀態物件,而且只有在雜湊表中找不到符合需求的狀態物件時,才建立狀態物件。 使用自訂雜湊表背後的原因,是應用程式可以根據該應用程式的特定使用案例來選取快速雜湊。 例如,如果應用程式只會變更 BlendState 中的 rendertargetwritemask,並保留所有其他值,則應用程式可以從 rendertargetwritemask 產生雜湊,而不是整個結構。

AlphaTest 狀態已消失。 它在哪裡?

AlphaTest 現在應該是著色器中的效能。 請參閱 FixedFuncEMU 範例。

使用者裁剪平面會發生什麼事?

使用者裁剪平面已移至著色器。 有兩種方式可以處理此問題。 第一個是從頂點著色器或幾何著色器輸出SV_ClipDistance。 另一個選項是根據頂點著色器或幾何著色器所傳遞的一些值,在圖元著色器中使用捨棄。 試驗這兩者,以查看特定案例的速度更快。 使用 SV_ClipDistance可能會導致硬體使用以幾何為基礎的裁剪常式,這可能會造成幾何系結繪製呼叫的執行速度較慢。 同樣地,使用捨棄會將工作移轉至圖元著色器,這可能會導致圖元系結的繪製呼叫執行速度較慢。

清除不會遵守任何狀態設定,例如我點陣化狀態中的剪刀或重新設定。

清除已與管線狀態分開。 若要取得 D3D9 樣式的行為,請繪製全螢幕四邊形來模擬清除。

我將狀態設定回預設,以嘗試並診斷轉譯錯誤。 現在我的畫面只會顯示黑色,但我知道我正在將物件繪製到畫面上。

將狀態設定回預設值 (Null) 時,請確定 OMSetBlendState 呼叫中的 SampleMask 永遠不會是零。 如果 SampleMask 設定為零,則所有樣本都會以邏輯方式為 AND,且具有零。 在此案例中,沒有範例會通過混合測試。

D3DSAMP\SREXTURE 狀態在哪裡?

SRGB 已移除為取樣器狀態的一部分,現在會系結至紋理格式。 系結 SRGB 紋理會導致您在 Direct3D 9 中指定D3DSAMP_SRGBTEXTURE取得的相同取樣。

格式

哪個 D3D9 格式對應至哪一個 D3D10 格式?

如需詳細資訊,請參閱 Direct3D 9 至 Direct3D 10 考慮

A8R8G8B8紋理格式會發生什麼事?

它們在 D3D10 中已被取代。 您可以將紋理重新建立為R8G8B8A8,或者可以在載入時撥動,或者您可以在著色器中撥動。

如何?使用分色紋理嗎?

將您的調色盤放在紋理或常數緩衝區中,並將它系結至管線。 在圖元著色器中,會使用您 Palettized 紋理中的索引進行間接查閱。

這些新的 SRGB 格式為何?

SRGB 已移除為取樣器狀態的一部分,現在會系結至紋理格式。 系結 SRGB 紋理會導致您在 Direct3D 9 中指定D3DSAMP_SRGBTEXTURE取得的相同取樣。

三角形風扇在哪裡?

三角形風扇已在 D3D10 中淘汰。 三角形風扇必須在內容管線或負載時轉換。

著色器連結

我的 Direct3D 9 著色器會編譯為著色器模型 4.0,但當我將它們系結至管線時,我會收到偵錯輸出中顯示偵錯執行時間的連結錯誤。

著色器連結在 D3D10 中更為嚴格。 後續階段中的元素必須依前一個階段輸出的順序讀取。 例如:

頂點著色器輸出:

    float4 Pos  : SV_POSITION;
    float3 Norm : NORMAL;
    float2 Tex  : TEXCOORD0;

圖元著色器會讀取下列專案:

        float3 Norm : NORMAL;
        float2 Tex  : TEXCOORD0;

雖然圖元著色器中不需要位置,但這會導致連結錯誤,因為位置是從頂點著色器輸出,但圖元著色器不會讀取。 較正確的版本看起來會像這樣:

頂點著色器輸出:

        float3 Norm : NORMAL;
        float2 Tex  : TEXCOORD0;
        float4 Pos  : SV_POSITION;

圖元著色器會讀取下列專案:

        float3 Norm : NORMAL;
        float2 Tex  : TEXCOORD0;

在此情況下,頂點著色器會輸出相同的資訊,但現在圖元著色器會依順序輸出讀取專案。 因為圖元著色器不會在 Tex 之後讀取任何內容,所以我們不需要擔心 VS 輸出比 PS 正在讀取更多的資訊。

我需要著色器簽章才能建立輸入配置,但我在建立著色器之前載入網格並建立版面配置。 我該怎麼辦?

其中一個解決方案是在載入網格之前切換順序和載入著色器。 不過,這比完成更容易。 您一律可以在應用程式需要時視需要建立輸入配置。 您必須保留著色器簽章的版本。 您應該根據著色器和緩衝區配置建立雜湊,而且只有在符合的雜湊不存在時,才建立輸入配置。

繪製呼叫

D3D10 的繪製呼叫達到 60 Hz 的限制為何? 30 Hz?

Direct3D 9 因為每個繪製呼叫的 CPU 成本,所以繪製呼叫的數目有所限制。 在 Direct3D 10 上,每個繪製呼叫的成本已降低。 不過,繪製呼叫和畫面播放速率之間不再有明確的相互關聯。 因為繪製呼叫通常需要許多支援呼叫, ( 常數緩衝區更新、紋理系結、狀態設定等等,) API 的畫面播放速率影響現在更依賴整體 API 使用量,而不是只繪製呼叫計數。

資源

我應該針對哪些作業使用哪些資源使用量類型?

使用下列速查表:

  • CPU 會針對每個畫面更新一次以上的資源:D3D10_USAGE_DYNAMIC
  • CPU 會更新每個畫面少於一次的資源:D3D10_USAGE_DEFAULT
  • CPU 不會更新資源:D3D10_USAGE_IMMUTABLE
  • CPU 需要讀取資源:D3D10_USAGE_STAGING

因為常數緩衝區一律要經常更新,所以它們不符合「速查表」。如需要用於常數緩衝區的資源類型,請參閱 常數緩衝區一 節。

DrawPrimitiveUP 和 DrawIndexedPrimitiveUP 會發生什麼事?

它們會在 D3D10 中消失。 針對動態幾何,請使用大型D3D10_USAGE_DYNAMIC緩衝區。 在框架的開頭,將它與D3D10_MAP_WRITE_DISCARD對應。 針對每個後續繪製呼叫,將寫入指標往前移動先前繪製頂點的位置,並將緩衝區與D3D10_MAP_WRITE_NO_OVERWRITE對應。 如果您在框架結尾之前接近緩衝區的結尾,請將寫入指標包裝在開頭,並以D3D10_MAP_WRITE_DISCARD對應。

我可以將 16 位索引和 32 位索引寫入相同的動態幾何緩衝區嗎?

是,您可以,但這可能會對某些硬體造成效能負面影響。 建立動態 16 位索引資料和 32 位索引資料的個別緩衝區是更安全的。

如何?從 GPU 將資料讀回 CPU?

您必須使用預備資源。 使用 CopyResource 將資料從 GPU 資源複製到預備資源。 對應暫存資源以讀取資料。

我的應用程式相依于 StretchRect 功能。

由於這基本上是基本 Direct3D 功能的包裝函式,因此已從 API 中移除。 部分 StretchRect 功能已移至 D3DX10LoadTextureFromTexture。 針對格式轉換和複製紋理,D3DX10LoadTextureFromTexture 可能會執行此作業。 不過,從一個大小轉換成另一個大小的作業可能需要在應用程式中轉譯為紋理作業。

對應呼叫資源時沒有位移或大小。 這些是在 Direct3D 9 上的鎖定呼叫上;為什麼他們變更了?

Direct3D 9 上鎖定呼叫的位移和大小基本上是 API 雜亂,而且驅動程式通常會忽略。 應該改為由應用程式從 Map 呼叫中傳回的指標計算位移。

深度為紋理

哪一個較快? 使用深度做為紋理,或將深度寫入 Alpha 並讀取?

這是應用程式與硬體專屬。 使用哪一個可節省最多頻寬。 如果您已經使用多個轉譯目標並具有額外的通道,則從著色器寫入深度可能是更好的解決方案。 此外,將深度寫入 Alpha 或其他轉譯目標,可讓您撰寫線性深度值,以加速需要存取深度緩衝區的計算。

我是否可以將紋理系結為輸入和系結為深度樣板紋理,只要我停用深度寫入?

不在 D3D10 中。

MSAA

我可以解析 MSAA 深度樣板紋理嗎?

不在 D3D10 中。 不過,您可以從 MSAA 紋理取樣個別樣本。 如需詳細資訊 ,請參閱 HLSL 一節。

為什麼我的應用程式會在啟用 MSAA 時立即當機?

請確定您啟用驅動程式實際列舉的 MSAA 範例計數和品質號碼。

損毀

我的應用程式在 D3D10 或驅動程式中當機,我不知道原因。

第一個步驟是啟用傳遞至D3D10CreateDevice) 的偵錯運行時間 (D3D10_CREATE_DEVICE_DEBUG旗標。 這會將最常見的錯誤公開為偵錯輸出。

當我嘗試搭配使用我的應用程式時,PIX 會當機。

第一個步驟是啟用傳遞至D3D10CreateDevice) 的偵錯運行時間 (D3D10_CREATE_DEVICE_DEBUG旗標。 如果偵錯輸出未清除,PIX 的機率會更高。

我的遊戲在 D3D10 下的 32 位 Vista 上用盡虛擬位址空間。 D3D9 上沒有問題。

D3D10 和虛擬位址空間有一些問題。 KB940105中已修正此問題。 如果這無法修正您的問題,請確定您不會建立比在 D3D9 中建立更多的資源,這些資源可以在 D3D10 中 (鎖定) 進行對應。 也請考慮移植到 64 位,因為未來會更普遍。

述詞轉譯

我根據遮蔽查詢結果) 使用述詞轉譯 (。 為什麼我的應用程式速度仍然相同?

首先,請確定您想要略過的轉譯實際上是應用程式瓶頸。 如果不是瓶頸,則略過轉譯將無法協助畫面播放速率。

其次,請確定在查詢問題與您想要述詞的轉譯之間經過足夠的時間。 如果查詢在轉譯呼叫達到 GPU 時尚未完成,則仍然會發生轉譯。

第三,預先部署只會略過特定呼叫。 略過的呼叫包括 Draw、Clear、Copy、Update、ResolveSubresource 和 GenerateMips。 狀態設定、IA 設定、對應和建立呼叫不會遵守預先設定。 如果繪製呼叫周圍有許多狀態設定呼叫要述詞,這些狀態仍會設定。

幾何著色器

我應該使用幾何著色器來鑲嵌我的 (在此插入任何專案) ?

不是 幾何著色器不應該用於鑲嵌。

我可以使用幾何著色器來建立幾何嗎?

是,在非常有限的案例中。 目前 D3D10 中的幾何著色器 (2008) 元件無法處理大量擴充。 這可能會在未來變更。 視訊卡廠商可能會有一到四個擴充的特殊路徑,因為現有的點 Sprite 硬體。 任何其他擴充都必須非常有限。 ParticlesGS 和 PipesGS 範例只會執行有限的擴充,以達到高畫面播放速率。 每個畫面只會展開幾個點。

我應該使用幾何著色器?

需要整個基本類型上作業的任何專案,例如剪除偵測、直心座標等等。 也使用它來選取要傳送基本類型之轉譯目標陣列的配量。

我可以從幾何著色器輸出可變的幾何數量嗎?

是,但這可能會導致效能問題。 採用一個叫用的 1 點,另一個叫用 4 點的範例。 在擴充指導方針內調整時,這可能會導致幾何著色器執行緒以序列方式執行。

D3D10 如何知道如何為網格產生相鄰索引? 或者,當我指定幾何著色器需要相鄰資訊時,為什麼 D3D10 無法正確轉譯。

相鄰資訊不是由 D3D10 所建立,而是由應用程式所建立。 相鄰索引是由應用程式產生,而且每個基本類型必須包含六個索引;在六個中,奇數索引是邊緣相鄰頂點。 ID3DX10Mesh::GenerateAdjacencyAndPointsReps 可用來產生此資料。

HLSL

整數和位指令是否緩慢?

它們可以是 。 各種 D3D10 卡片只能對可用 ALU 單位的子集發出整數運算。 這高度相依于硬體。 如需如何解決該特定硬體上的整數作業的建議,請參閱您的個別硬體廠商。 此外,請非常小心在類型之間轉換。

VPOS 發生什麼事?

如果您將圖元著色器的輸入宣告為SV_POSITION,您會收到與宣告為 VPOS 相同的行為。

如何? MSAA 紋理取樣?

在您的著色器中,將您的紋理宣告為 Texture2DMS。 然後,您可以使用 Texture2DMS 物件的 Sample 方法擷取個別樣本。

如何?判斷常數緩衝區中的著色器變數是否實際使用?

查看該變數的反映D3D10_SHADER_VARIABLE_DESC結構。 uFlags 應該已設定D3D10_SVF_USED旗標。

如何?判斷常數緩衝區中的著色器變數是否實際使用 FX10?

目前無法使用 FX10。

我無法控制 FX10 所建立的常數緩衝區。 它們如何建立和更新?

所有 FX10 管理的常數緩衝區都會建立為D3D10_USAGE_DEFAULT資源,並使用 UpdateSubresource 更新。 由於 FX10 會保留所有常數資料的備份存放區,因此 UpdateSubresource 是更新這些資料的最佳方法。

如何?使用著色器模擬固定函式管線?

請參閱 FixedFuncEMU 範例。

我應該使用新的 \[unroll\]、\[loop\]、\[branch\],依此類推,編譯器提示?

一般而言不行。 編譯器通常會嘗試這兩種方式,並選擇最快的方法。 在某些情況下,可能需要使用 [unroll],例如,當迴圈內的紋理擷取需要存取漸層時。

部分精確度在 D3D10 上是否有任何差異? 我可以在 D3D9 HLSL 中指定部分精確度,但不能在 D3D10 HLSL 中指定。

所有 D3D10 作業都會指定為以 32 位浮點精確度執行。 因此,部分精確度不應該在 D3D10 中有任何差異。

在 D3D9 中,我可以藉由將深度緩衝區系結為紋理並使用一般 tex2d hlsl 指令來執行 HW PCF 陰影篩選。 如何? D3D10 上執行此動作嗎?

您必須使用比較取樣器狀態,並使用 SampleCmp 指示。

此 register 關鍵字如何在 D3D10 中運作?

D3D10 中的 register 關鍵字現在適用于特定資源所系結的位置。 在此情況下,資源可以是緩衝區 (常數,或) 、紋理或取樣器。

  • 針對常數緩衝區,請使用語法:register (bN) ,其中 N 是輸入位置 (0-15)
  • 針對紋理,請使用語法:註冊 (tN) ,其中 N 是輸入位置 (0-127)
  • 針對取樣器,請使用語法:註冊 (sN) ,其中 N 是輸入位置 (0-127)

如果暫存器只是用來指定系結整個緩衝區的位置,如何?將變數放在常數緩衝區內?

使用 packoffset 關鍵字。 packoffset 的引數格式為 c[0-4095]。[x,y,z,w]。 例如:

        cbuffer cbLotsOfEmptySpace
        {
        float   IWaste2Floats   : packoffset(c0.z);
        float4  IWasteMore  : packoffset(c13);
        };

在這個常數緩衝區中,IWaste2Floats 會放在常數緩衝區的第三個浮點數 (12 位元組) 。 IWasteMore 會放在常數緩衝區的第 13 個 float4 或 52nd float。