圖形流水線之旅 part3 3D流水線概覽 頂點處理

來源:互聯網
上載者:User
        現在,我們的APP發出的繪製調用已經走過了各種驅動層和指令處理器。如今要做的就是進行真正的圖形處理了。我會介紹頂點流水線。但開始之前,我們先認識認識一些 字母縮寫        在3D流水線中存在著多個階段,每個階段完成特定工作。下面給出的名字是我以後會提到的,當然,多數和D3D10/11官方命名保持一致,只是加了一點縮寫。在今後的很多章節裡,我會一一講解,知道這個系列的結束。OK,先給出這些縮寫和它們簡略的說明。
  • IA——輸入彙編器(Input Assembler):讀入索引和頂點資料
  • VS——頂點著色器(Vertex Shader):擷取輸入的頂點資料,為下一階段輸出處理後的頂點資料。
  • PA——圖元裝配(Primitive Assembly):讀入頂點並組裝成圖元,然後傳遞出去。
  • HS——殼著色器(Hull Shader):接收補丁圖元,輸出變換後(沒變換)的補丁控制點,作為域著色器(domain shader)的輸入,加上一些額外資料來驅使曲面細分。
  • TS——曲面細分階段(Tessellator Stage):為細分後的線段與三角形創造新的頂點和串連。
  • DS——域著色器(Domain Shader):將從HS來的已著色控制點,額外資料和從TS來的已細分位置融合轉化,再次變為一系列頂點。
  • GS——幾何著色器(Geometry Shader):輸入為圖元和(可選)串連資訊,輸出萬為不同的圖元。主要起集線器的作用。
  • SO——流輸出(Stream-out):將GS的輸出(比如轉換後的圖元)寫入到記憶體中的某個緩衝中。
  • RS——光柵化(Rasterizer):光柵化各種圖元。
  • PS——像素著色器(Pixel Shader):擷取插值頂點資料,輸出像素顏色。也可寫入UAVs(unorder access views)。
  • OM——輸出合并(Output Merger):擷取來自PS的像素,做alpha混合,然後將它們回寫到後緩衝。
  • CS——計算著色器(Compute Shader):獨立流水線。唯一輸入為常量緩衝器、線程的ID,可以寫入緩衝和UAVs。
    下面列出各種各樣的資料流路徑,我會按順序介紹。
  1. VS -> PS 早期可程式化流水線。在D3D9時期,這就是你能控制的全部了。迄今為止,對於常規渲染,這仍然是最重要的路徑。我會先從頭到尾講一遍這條路徑,然後再介紹更先進的路徑。
  2. VS -> GS -> PS 添加了幾何著色(D3D10的新特性)
  3. VS -> HS -> TS -> DS -> PS, VS -> HS >TS -> DS -> GS -> PS:添加了細分曲面(D3D11的新特性)
  4. VS -> SO, VS -> GS -> SO, VS -> HS -> TS -> DS -> GS -> SO:添加了流輸出(可選細分曲面)
  5. CS:添加計算著色器(D3D11的新特性)
    現在你知道接下來是什麼了吧,我們先從頂點著色器開始。 輸入彙編器階段        最先發生的肯定是從索引緩衝裡載入索引——當然,前提是這個緩衝是個已索引的批處理。如果不是,就假裝它是一個ID索引緩衝(0 1 2 3 4….),把ID當作索引。索引緩衝裡的內容一般不是直接從記憶體裡讀取的,IA通常利用一個資料緩衝來訪問索引/頂點緩衝。要注意索引緩衝讀取內容(D3D10+所有可訪問資源)時會進行邊界檢測。如果你引用的元素在原始索引緩衝之外(比如,在只5個索引的緩衝中設定IndexCount
== 6調用DrawIndexed),所有越界訪問的傳回值都為0。同樣,你可以使設定一個NULL索引緩衝來調用DrawIndexed,相當於你擁有一個大小為0的索引緩衝,這樣,以後所有讀取都是越界訪問,而且返回0。        擁有了索引,我們也就從輸入頂點流中擷取了所有需要的頂點、執行個體資料(在這個階段,當前執行個體ID就是一種計數器,很簡單直接吧)。真的非常直接——我們有資料格式的聲明,只需從快取/記憶體讀取然後解壓成著色器核心需要的浮點格式。只是這種讀取不是立即模式,硬體此時正在運行已著色頂點的快取。因此,如果一個頂點被多個三角形引用了,它就不需要每次都被著色——我們只需引用已存在的著色資料就行。 頂點的緩衝與著色        注意:本部分內容在某種程度上講只是“猜測”。        小標題中的兩個東西基於“知情人士”對當前GPUs的公開評論。但那帶給我的只有“WHAT”,而沒有“WHY”,所以這兒會有一些個人推論。同樣,我只是簡單猜測一些細節。意思就是我在這兒說的在我的瞭解之內——但我有自信這些是可信可靠的。        長期以來(直到包含shader model 3.0的GPUs出現),頂點著色器與像素著色器被用於不同單元,擁有不同的效能權衡,而且頂點緩衝是個相當簡單的東西。通常對於少量(一打或是兩打)頂點就一個FIFO,對於輸出屬性在最糟糕情況下的數量也有足夠的空間,使用頂點索引作為標記。看吧,相當簡單直接。        然後,出現了統一著色器。如果你要統一兩種不同作用的著色程式,這種設計就變得非常有必要了。想想看,一方面,你的頂點著色單元需要每幀同時關聯大約100萬個頂點做一般應用。另一方面,你的像素著色單元每幀需要至少230萬個像素來將1920×1200的螢幕做一次全屏填充——如果你想渲染更多,那可能需要更多的像素。你猜哪個單元會拉慢速度?        OK,解決辦法就在這兒:扔掉一次處理一個頂點的過時頂點著色單元,換上強悍的統一著色單元,它有著最大的輸送量,沒有延遲,從此開始大批量工作(多大?現今這個數字是每批次16到64個頂點)。        如果不想著色效率低下,你需要16到64個頂點緩衝缺失直到可以分配一個頂點著色器進行載入,但整個FIFO是不會這樣一次性著色的。問題來了:如果一次性著色整批頂點,意味著實際上你得等所有頂點著色完畢後才能開始裝配成三角形。屆時,你已在FIFO末端添加了整批(統一成32個)頂點,意味著前面有32個頂點被擠掉了——而其中每一個頂點都有可能已經被當前正在裝配的三角形頂點快取命中。看吧,這明顯不能工作。我們正在引用前面快被擠掉的的32箇舊頂點,沒法在FIFO中把它們看成頂點快取命中。還有,我們要讓這個FIFO多大呢?如果一批著色32個頂點,它至少需要32個入口吧,而我們又不能使用舊的32個入口(因為正在使用/移除它們),這意味著每批次都會遇到空空的FIFO。因此,讓它更大,64個入口怎麼樣?看起來足夠大了。每次頂點緩衝尋找都會在FIFO內將該標籤(頂點索引)和所有標籤比較一次——這個操作高度並行化,但還是耗時,我們可以在這兒高效地實現一個全關聯快取。還有,在分配一個32頂點著色載入器和接收結果這段時間內,我們做什麼呢?——只能等著?著色會花上上百個周期,乾等是很SB的。交替使用兩個著色載入器來做並行化處理?但現在我們的FIFO至少需要64個入口的長度,沒法把最後64個入口看作頂點快取命中,因為我們接受結果的同時它們會被移除。而且,要一個FIFO對抗很多著色核心?不要忘了Amdahl定律(阿姆達爾定律)在這兒是有效。        這種一體FIFO不是很適應這種環境,所以,扔掉它,我們重新開始吧。我們真正想做的是什麼呢?擷取一個合適大小的頂點批次用來著色,不需要著色超過必需的頂點數。所以呢,簡單點,為32個頂點(一批次)預留足夠的緩衝空間,同樣為32個入口預留快取標籤空間。然後從一個空的“快取”開始,比如所有入口無效。為索引緩衝內的每一個圖元在全部索引上做一次查詢。如果命中一個快取,OK了。如果沒有,就在當前批次內分配一個追蹤,為快取標籤數組添加新的索引。一旦剩下的空間不夠再添加新的圖元,就將整批次分配給頂點著色器,然後儲存快取標籤數組(例如,剛剛著色了的頂點的32條索引),開始設定下一批次,又從一個空的快取開始——確保批次間的完全獨立性。        每一批都會讓一個著色器單元忙一會兒(至少上百個周期)。但沒關係,因為我們有足夠的著色器單元——只需另找一個單元執行另一批。高度並行化。最終得到結果返回 。屆時,我們使用儲存的快取標籤和原始索引快取資料來裝配圖元,然後又送進流水線——這就是“圖元裝配”,我稍後會講到。        順便說一下,我說的“得到結果返回”是什麼意思。我們在哪結束的?有兩種選擇:1、專用緩衝 2、一些通用快取/暫存器,以前一般是1,圍繞固定結構設計的頂點資料(每個頂點有16個float4 向量屬性空間),最近的GPU更偏向2了。2更靈活,擁有明顯的優勢——你可以把這個儲存空間用於其他著色階段,反之,專用頂點快取對像素著色或計算流水線根本沒用。        是目前為止所描述的頂點著色資料流
著色器單元內部構件        簡介:這差不多就是你期望拆開HLSL編譯器能看到的。它只是個擅長運行某類代碼的處理器,在硬體上做的就是將著色程式位元組碼編譯成某些東西。不像我前面講的那些,這個東西文檔非常完善——如果有興趣,可以看看AMD/NVidia的會議示範或閱讀CUDA/Stream SDK中的文檔。        執行概要:快速ALU主要位於一個FMAC(Floating Multiply-ACcumulate)單元周圍,一些HW支援倒數,平方根倒數,log2, exp2,sin,cos,最佳化高輸送量高密度沒有低延遲,執行高線程量來掩飾延遲,每個線程只有很少量寄存器(因為線程確實太多),非常擅長執行線性代碼,不擅長分支結構。上述差不多是全部的共有執行。當然也存在一些差異。AMD的硬體習慣直接使用HLSL/GLSL和著色程式位元組碼中預設的4位寬SIMD(最近看起來要改動了),而前陣子NVidia趨向於將4位寬SIMD轉化為標量指令。反正,互有交集吧。        有意義的是各種著色階段的不同之處。簡介實在很簡略,例如,所有的演算法和邏輯指令在各個階段都是完全相同的。一些設計(比如像素著色器中的派生指令和插值特性)只存於某些階段,但主要的區別還是輸入輸出傳遞的資料類型的不同。        當然,還有一個與著色器相關的特別之物,就是紋理採樣(紋理單元),這個主題有點大,所以我把它單列一章,下一章來講述它。 結束語
        再說一次,“頂點的緩衝與著色”部分有些是我的猜測,所以不可全信,要有點保留意見。我也沒有闡述快取/暫存器儲存是如何管理的細節。緩衝的大小(主要)依賴於你處理的批次的大小和你期望頂點輸出屬性的多少。緩衝大小與管理對效能來說非常重要,但我在這兒沒講也不想講。因為這個東西對任意硬體都很特殊,但沒啥深度。下節再見。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.