翻譯|使用教程|編輯:吳園園|2020-05-27 09:55:21.820|閱讀 572 次
概述:在Qt圖形系列博客的第三部分(第一部分、第二部分),我們會了解在Qt 5.14中,將Qt Quick的Scene Graph切換到通過QRhi (Qt渲染硬件接口)渲染時,著色器是如何工作的。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關(guān)鏈接:
Qt是目前最先進、最完整的跨平臺C++開發(fā)工具。它不僅完全實現(xiàn)了一次編寫,所有平臺無差別運行,更提供了幾乎所有開發(fā)過程中需要用到的工具。如今,Qt已被運用于超過70個行業(yè)、數(shù)千家企業(yè),支持數(shù)百萬設備及應用。
在Qt圖形系列文章的第三部分(第一部分、第二部分),我們會了解在Qt 5.14中,將Qt Quick的Scene Graph切換到通過QRhi (Qt渲染硬件接口)渲染時,著色器是如何工作的。我們先研究著色器的處理方式,然后再深入研究RHI,因為在Qt Quick中當需要使用ShaderEffect Item或自定義材質(zhì)時,必須自己編寫片段和/或頂點著色器代碼,因此必需要了解新的著色器處理方法(到Qt 6時才能升級)。
說到Qt 6:雖然我們在這里描述的內(nèi)容只會應用到Qt 5.14,后面的版本還會有許多修改,但是我們現(xiàn)在闡述的內(nèi)容極有可能是Qt 6中處理圖形和通用計算著色器的基礎,當然到時細節(jié)內(nèi)容會打磨得更加精致
為什么加入新東西?
問題一
查看qtdeclarative源代碼樹(即包含QtQml 、QtQuick和相關(guān)模塊的git代碼倉庫),然后進入著色器目錄,其中包含了Qt Quick Scene Graph內(nèi)建材質(zhì)的頂點著色器和片段著色器代碼,你會發(fā)現(xiàn)Qt Quick已為每個GLSL頂點和片段著色器準備了兩個版本:
為什么這樣處理呢?這是為了兼容支持使用了核心配置(core profile)的OpenGL(對應OpenGL 3.2及以上)。由于OpenGL標準并沒有要求新版本的OpenGL實現(xiàn)必須支持GLSL 100/110/120的編譯(即老的GLSL版本),因此Qt不得不準備了兩個版本的GLSL:一個適配OpenGL ES 2.0、OpenGL 2.1和兼容性配置(compatibility profile),另一個(GLSL版本號為150)專門用于適配核心配置。如本系列博客第一部分所述,在需要把自定義OpenGL渲染和基礎的Qt Quick UI結(jié)合的使用場景中,提供兩個版本的著色器代碼才能使開發(fā)者可以自由選擇使用哪個版本的OpenGL。因為不論選擇兼容性配置還是核心配置,Qt Quick都可以正常渲染。
當著色器版本的數(shù)量是2時,這種實現(xiàn)方式還是可以正常實施的。但如果現(xiàn)在我們還需要添加Vulkan風格的GLSL、HLSL和MSL呢?遺憾的是,這種方式無法規(guī)模化擴展。
問題二
與OpenGL不同,一些較新的圖形API不再支持內(nèi)置著色器編譯。(再見了,glCompileShader)。而即使最終還是支持著色器編譯,這部分功能可能變成了一個分離的庫,但它們可能不提供運行時反射機制,這意味著沒有辦法動態(tài)定位輸入頂點以及其他頂點、片段或通用計算著色器所需要的材質(zhì),以及這些材質(zhì)的布局。(例如,一個uniform變量的名稱和偏移量)
問題三
一個內(nèi)部細節(jié):Qt Quick Scene Graph的批次處理系統(tǒng)需要對頂點著色器進行一些調(diào)整,在一個稱為合并批次中調(diào)整材質(zhì)(就是當多個幾何節(jié)點最終合并到一個draw調(diào)用后得到的結(jié)果)。把著色器傳送到glCompileShader之前動態(tài)修改,這種方式適用于只有一種著色語言在使用的情況,不能簡單擴展到必須為多種不同語言實現(xiàn)相同邏輯的情況。
如何改變呢?
看看Khronos的SPIR頁面,里面有一張很好的關(guān)于SPIR-V開源生態(tài)系統(tǒng)的信息圖片。為什么不嘗試在此基礎上進行開發(fā)呢?
我們感興趣的關(guān)鍵組件如下:
因此,如果我們“標準化”一種語言,比如Vulkan風格的GLSL,把它編譯成SPIR-V,我們就可以適配Vulkan了。然后,如果我們通過SPIRV-Cross運行SPIR-V的二進制文件,就可以獲得所需的反射信息,并可以為各種版本的GLSL、HLSL和Metal著色器語言生成源代碼。
(是的,GLSL仍然至關(guān)重要,因為雖然有讓OpenGL可以直接使用SPIR-V的擴展,但是指望這套方式在實際中應用并不現(xiàn)實,因為這樣的擴展在90%的Qt目標平臺和設備上不存在——例如,OpenGL ES 2.0在2019年仍然常見。)
最后,將所有這些(包括元數(shù)據(jù)反射特性)打包到一個可以方便(反)序列化的包中,這樣就得到了我們的解決方案。
因此,設置QSG_RHI=1然后運行Qt Quick應用程序,其后端渲染管道是這樣的:
Vulkan-flavor GLSL [ -> generate batching-friendly variant for vertex shaders] -> glslang : SPIR-V bytecode -> SPIRV-Cross : reflection metadata + GLSL/HLSL/MSL source -> pack it all together and serialize to a .qsb file
.qsb擴展名來自于執(zhí)行上述步驟的命令行工具的名稱——qsb,Qt Shader Baker的縮寫。(不要與qbs混淆)
在運行時,.qsb文件被反序列化成QShader實例。它是一個相當簡單的容器,遵循標準的Qt模式,如隱式共享,并為一個著色器托管多個版本的源碼和字節(jié)碼以及包含反射數(shù)據(jù)的QShaderDescription。與RHI的其他部分一樣,這些類目前都是私有的API。
圖形層直接使用QShader實例。圖形流水線的狀態(tài)對象為每個激活的著色步驟分配一個QShader。然后QRhi后端從QShader容器中選擇適當?shù)闹靼姹尽?
在Qt 5.14中,具體選擇規(guī)則如下:
上表中的HLSL和MSL條目初看可能會有些奇怪。這是因為我們即可以在運行時源碼編譯HLSL和MSL(我們的默認方法),同時也做了一些實驗,允許在.qsb包中包含預編譯的中間格式。在實踐中,這意味著調(diào)用fxc(目前還不支持dxc——它也在計劃中,但只有在我們開始研究D3D12時才真正相關(guān))或Metal命令行工具,然后再在管道中執(zhí)行上面所示的“打包”步驟。這里的挑戰(zhàn)當然是這些工具與它們的平臺(分別是Windows和macOS)綁定在一起,因此qsb只有當在該平臺上運行時才能被啟用。例如,在Linux上手動生成.qsb文件不可行。從長遠來看,這可能不是什么大問題,因為在Qt 6的規(guī)劃中,我們會研究更好地與構(gòu)建系統(tǒng)集成,所以像qsb這樣的手動運行工具就不那么常見了。
等等,qsb是怎么來的?
來自Qt Shader Tools模塊。它提供了一個稱為QShaderBaker的API以及一個稱為qsb的命令行工具來執(zhí)行上面描述的編譯、轉(zhuǎn)換和打包步驟。
這里有一點需要注意:這是一個Qt-labs模塊,所以它不會隨Qt 5.14一起發(fā)布。
為什么呢?主要是因為第三方依賴,例如glslang和SPIRV-Cross。涉及到需要在我們所有的目標平臺上編譯和運行的情況時,就會有許多事情需要調(diào)查和確認,有些與許可證相關(guān)。如果所有這些聽起來都很熟悉,那是因為在本博客系列的第一部分討論API轉(zhuǎn)換解決方案時提到了其中的一些問題。因此,現(xiàn)在生成.qsb包就牽涉到了該模塊的檢查和構(gòu)建,然后才能手動運行.qsb工具。
盡管我們還是需要一個新的集成打包在Qt中的解決方案,目前依賴一個離線著色處理并不是件壞事。不管發(fā)生什么,它都是Qt 6的目標之一。我們的愿景是擁有一些與Qt構(gòu)建系統(tǒng)集成的東西,這樣上述的著色器處理步驟就可以在應用程序(或庫)構(gòu)建時完成。但這推遲到成為一個未來的目標,主要是因為即將到來的qmake -> cmake切換。一旦情況穩(wěn)定下來,我們就可以開始在新系統(tǒng)上構(gòu)建解決方案了。
那么Qt Quick在Qt 5.14中表現(xiàn)怎么樣?
看看qt/ src/quick/scenegraph/shaders_ng,答案很明顯:通過手動運行qsb(注意名稱很貼切的compile.bat),并通過Qt資源系統(tǒng)在Qt quick庫中引入生成的.qsb文件。正如上面所概述的,稍后應該會變得更加精巧一些,但是現(xiàn)在已經(jīng)完成了任務。
.vert和.frag文件包含了與Vulkan兼容的GLSL代碼,并且沒有包含在Qt Quick 構(gòu)建中。scenegraph.qrc中只有.qsb文件。
每個材質(zhì)只有一對頂點和片段著色器,總是以與Vulkan兼容的GLSL的形式編寫,遵循一些簡單的約定(比如只使用一個uniform緩沖區(qū),位于binding 0處)。
所有這些文件都通過著色器機制運行,產(chǎn)生一個QShader包。在這個例子中,結(jié)果是相同著色器的六個版本,加上反射數(shù)據(jù)(qsb可以打印為JSON文本;然而.qsb文件本身是壓縮的不可讀的二進制文件)。這樣就解決了上面的問題一和二。
注意著色器列表中的[Standard]標簽。如果這是一個頂點著色器,并且指定了-b參數(shù),輸出著色器的數(shù)量將是12個,而不是6個。另外的6個將被標記為[Batchable],這表明它們是非常友好的批處理,對Qt Quick scenegraph的渲染器做了輕微的修改。這解決了問題3,代價是存儲會有所提高。(由于減少了運行時工作,所以最終是值得的)
本文涵蓋了新著色器管道背后的核心概念。我們將會在另一篇文章中討論ShaderEffect和QSGMaterial。基本思想(Qt 5.14中的)是傳遞.qsb文件的名稱,而不是著色器源碼字符串,但對于材質(zhì)需要特別注意幾個問題(主要是由于使用uniform緩沖區(qū)代替了單一的緩沖區(qū),而且由于沒有了線程上下文的概念,因此任何人都可以隨意改變狀態(tài))。下次再詳細講。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自: