流輸出 (SO) 階段可以在這些頂點到達光柵器之前將頂點流式傳輸到內存。 流輸出的運行方式類似于管道中的點擊。 即使數據繼續向下流向光柵器,也可以打開此點擊。 通過流輸出發送的數據連接到緩沖區。 這些緩沖區可以在后續傳遞上作為管道輸入進行循環。
流輸出的一個約束是,它與幾何著色器相關聯,因為它們必須一起創建, (兩者可以是“NULL”/“off”) 。 不過,流出到的特定內存緩沖區不會綁定到特定的幾何著色器和流輸出對。 僅描述要饋送給流輸出的頂點數據部分與幾何著色器相關聯。
流輸出可用于保存將重復使用的有序管道數據。 例如,一批頂點可以通過將頂點傳入管道來“皮膚化”,就好像它們是獨立的點 (只是) 訪問所有這些點,對每個頂點應用“皮膚化”操作,并將結果流式傳輸到內存。 保存的“皮膚化”頂點隨后可用作輸入。
由于通過流輸出寫入的輸出量是動態的,因此需要一種新型的 Draw ,DrawAuto 才能允許流輸出緩沖區與輸入匯編程序一起重復使用,而無需 CPU 參與來確定實際寫入的數據量。 此外,需要查詢來緩解流輸出溢出,以及檢索寫入流輸出緩沖區的數據量 (D3D10DDI_QUERY_STREAMOVERFLOWPREDICATE和 D3D10DDI_QUERY_STREAMOUTPUTSTATS D3D10DDI_QUERY 枚舉) 。
Direct3D 運行時調用以下驅動程序函數來創建和設置流輸出:
- CalcPrivateGeometryShaderWithStreamOutput
- CreateGeometryShaderWithStreamOutput
- SoSetTargets
?1. 核心功能與設計理念
流輸出 (SO) 是 Direct3D 10 引入的數據回寫機制,允許在幾何著色器 (GS) 處理后,將頂點數據直接寫入GPU緩沖區,而非僅傳遞到光柵化階段。其核心特性包括:
- 數據持久化:將處理后的頂點保存到緩沖區,供后續渲染循環使用。
- GPU閉環:實現完全在GPU內部的數據復用(如粒子系統更新)。
- 動態數據量:支持可變長度輸出(需配合 DrawAuto 和查詢機制)。
類比:SO 如同在渲染管線中插入一個“T型閥門”,既可流向光柵器,也可分流到內存。
2. 流輸出與幾何著色器的關系
- 強耦合性:SO 必須與 GS 同時創建(兩者可同時為NULL關閉)。
- 分離綁定:
- GS+SO創建時:定義哪些頂點屬性輸出到緩沖區(如僅位置+速度)。
- 運行時綁定:實際緩沖區(D3D10DDI_HRESOURCE)通過 SoSetTargets 動態指定。
示例:創建帶SO的GS:
D3D10DDIARG_STAGE_IO_SIGNATURES soSignatures;
soSignatures.NumEntries = 2;
soSignatures.pOutputSignature = { "POSITION", "VELOCITY" }; // 輸出到緩沖區的屬性pDeviceFuncs->CreateGeometryShaderWithStreamOutput(hDevice, pGSBytecode, &soSignatures, hGS, hRTGS
);
3. 關鍵驅動函數與實現
(1) 函數列表
函數 | 職責 |
---|---|
CalcPrivateGeometryShaderWithStreamOutput | 計算GS+SO私有數據所需內存。 |
CreateGeometryShaderWithStreamOutput | 創建帶SO的GS對象,指定輸出屬性和格式。 |
SoSetTargets | 綁定SO目標緩沖區(支持多緩沖區,需匹配創建時的聲明)。 |
(2) SoSetTargets 實現示例
void APIENTRY SoSetTargets(D3D10DDI_HDEVICE hDevice,UINT NumBuffers,const D3D10DDI_HRESOURCE* phBuffers, // SO緩沖區資源句柄const UINT* pOffsets // 各緩沖區的寫入起始偏移
) {MyDeviceContext* pCtx = (MyDeviceContext*)hDevice.pDrvPrivate;for (UINT i = 0; i < NumBuffers; ++i) {pCtx->soBuffers[i] = phBuffers[i];pCtx->soOffsets[i] = pOffsets ? pOffsets[i] : 0;// 標記SO緩沖區為臟(需GPU同步)pCtx->dirtyFlags |= SO_TARGETS_DIRTY;}
}
4. 數據流控制與高級特性
(1) 流輸出工作流程
- GS處理頂點:輸出到SO緩沖區的屬性由創建時的簽名定義。
數據寫入緩沖區:
- 每個頂點按聲明順序寫入綁定的緩沖區。
- 支持多緩沖區交錯寫入(如位置和速度分開存儲)。
后續渲染循環:
- 綁定SO緩沖區作為輸入裝配器 (IA) 的輸入(需兼容格式)。
- 使用 DrawAuto 自動確定繪制數量。
(2) DrawAuto 機制
- 用途:在不知道SO輸出數據量的情況下,自動繪制所有有效頂點。
- 驅動實現:
需內部記錄SO寫入的頂點數,并在 DrawAuto 時回傳給IA。
void APIENTRY DrawAuto(D3D10DDI_HDEVICE hDevice) {MyDeviceContext* pCtx = (MyDeviceContext*)hDevice.pDrvPrivate;UINT vertexCount = pCtx->soStats.NumPrimitivesWritten * 3; // 假設三角形列表pDeviceFuncs->Draw(hDevice, vertexCount, 0);
}
(3) 溢出查詢與統計
通過查詢對象監測SO狀態:
查詢類型 | 用途 |
---|---|
D3D10DDI_QUERY_STREAMOVERFLOWPREDICATE | 檢測SO緩沖區是否溢出(返回TRUE /FALSE )。 |
D3D10DDI_QUERY_STREAMOUTPUTSTATS | 獲取寫入的圖元/頂點數(用于調試或邏輯控制)。 |
示例:檢查溢出:
BOOL overflow = FALSE;
pDeviceFuncs->QueryGetData(hQuery, &overflow, sizeof(BOOL));
if (overflow) {// 處理緩沖區擴容或數據截斷
}
5. 典型應用場景
(1) GPU粒子系統
- 初始化:創建帶SO的GS,輸出位置+速度。
更新循環:
- 綁定SO緩沖區,用GS計算粒子運動。
- 通過 DrawAuto 繪制更新后的粒子。
- 渲染循環將SO緩沖區作為IA輸入,渲染粒子。
(2) 幾何變形緩存
- 預處理:使用GS+SO將復雜變形(如曲面細分)結果存入緩沖區。
- 復用數據:后續幀直接讀取緩存,跳過重復計算。
6. 性能優化與注意事項
(1) 優化建議
- 緩沖區復用:使用雙緩沖或環形緩沖避免GPU停滯。
- 對齊寫入:確保SO輸出格式符合硬件要求(如4字節對齊)。
- 查詢最小化:僅在必要時檢查 STREAMOVERFLOWPREDICATE。
(2) 限制與兼容性
限制 | 解決方案 |
---|---|
SO緩沖區必須綁定為無序訪問視圖 (UAV) | 創建資源時指定?D3D10_DDI_BIND_STREAM_OUTPUT 。 |
輸出數據量不可預測 | 預分配足夠大的緩沖區,或動態調整。 |
僅支持頂點數據(非結構化) | 需通過GS組織輸出結構。 |
7. 驅動調試與驗證
- 驗證SO簽名匹配:確保 CreateGeometryShaderWithStreamOutput 聲明的輸出格式與 SoSetTargets 綁定的緩沖區兼容。
- 檢查溢出查詢:在復雜場景中強制觸發溢出,驗證驅動正確處理。
- 性能分析:使用GPU Profiler(如PIX)監測SO帶寬占用。
總結
流輸出 (SO) 是 Direct3D 10 實現 GPU數據閉環 的關鍵技術,其核心價值包括:
- 數據持久化:避免CPU-GPU通信開銷。
- 動態處理:支持可變長度輸出與自動繪制(DrawAuto)。
- 高效復用:適用于粒子系統、幾何變形等場景。
開發者需關注:
- GS與SO的協同設計:明確定義輸出屬性。
- 緩沖區管理:防止溢出并優化內存訪問。
- 查詢機制:確保動態數據量可控。