跨平台shader 編譯

2021-07-30 03:42:49 字數 2798 閱讀 4760

(本文根據我部落格上的文章跨平台shader編譯的過去、現在和未來改寫。知乎編輯器不支援多個級別的標題,以至於我只能用加粗不加粗來區分了。。。)

很多跨平台遊戲引擎都有統一shader的需求。比如klayge從建立伊始,就強調乙份**跨多個平台,shader**也不例外。如果需要對不同平台都分別寫一遍shader,那樣的工作量和可維護性都很糟糕。

既然有這樣的需求,就必然會在技術上遇到乙個問題,如何把乙份**編譯成不同api上的shader。從目前的api上,我們至少需要應對hlsl/glsl/essl,以後還有vulkan加入戰團。這裡就打算**一下跨平台shader編譯的情況,希望對大家有啟發意義。

剛有shader高階語言的時候,cg是幾乎唯一的shader語言。後來才在d3d9時代衍生出了hlsl,再往後有了glsl和essl。所以自然而然一開始都會從cg入手。在klayge發展的過程中,這還分為兩個階段。

因為早期的cg和hlsl幾乎一樣,我的做法是用hlsl寫shader,在d3d上用hlsl編譯器編譯,opengl上用cg編譯器編譯。遇到cg不支援的個別語法,就用#ifdef隔開。一開始這個做法工作得還不錯,得益於cg的跨平台,windows和linux都能用全套cg runtime來跑。不管是編譯還是設定狀態還是渲染,都通過cg來實現。這樣的系統工作流如下。

這個做法的缺點也很明顯。

既然用全套cg runtime有諸多問題,那麼能不能把cg編譯器離線使用呢?在某個版本的cg裡,加了glslv這樣的profile,可以把cg**編譯成glsl。那麼就可以在載入cg**後,立刻編譯成glsl儲存下來。之後執行過程中完全不需要跟cg打交道了。這麼做解決了之前的問題1和2。因為用的都是glsl和opengl,其他廠商的卡也能跑,因為cg runtime造成的效能損失也不存在了。改進後的系統工作流如下。

另外又多了乙個新問題:

正因為有這些問題,大概在2023年的時候,我才會有放棄cg,自己搞一套編譯或轉換系統的想法,從hlsl連到glsl/essl。

碰巧在打算放棄cg的時候,看到了mesa的乙個神奇commit,d3d1x for linux。這個commit是再給mesa增加d3d10/11的原生支援。雖然這個專案最後掛了,但給人們留下了乙個叫d3d1xshader的庫。這是第乙個非官方的完整dx shader位元組碼(dxbc)解析的庫。用它可以很容易做出來乙個反彙編工具。以這個為基礎,我們可以實現我設想的轉換系統。

有了這麼個系統,跨平台shader編譯工作流就變成了這樣。

同時期還有個功能相同的庫,叫hlslcc。也是從d3d1xshader發展而來。

說到底,造成這些問題的原因在於沒有乙個通用並開放的中間格式。否則用hlsl的前端,接到不同的後端生成不同的目標**就解決了。ue4的做法是自己寫了乙個hlsl解析。這麼做可以避免問題5,但因為hlsl經常會變,作為個人專案的klayge如果這麼做會花很多時間在相容性上。

這時候,vulkan來了。不但有新api,還帶來了乙個新的shader中間格式,spir-v。而我認為這正是通往統一的跨平台shader編譯路上最更要的一級台階。

spir-v和dxbc類似,都是乙個shader的二進位制中間語言。但比起dxbc,spir-v的標準是開放的,有相應的生態系統,解析起來容易得多。根據之前的工作流,我們可以設想一下用到了spir-v的話,未來的新工作流會是什麼樣子。

這個圖景就好象乙個巨大的拼圖,但目前為止,我們還缺了幾塊沒介紹。需要把它們湊齊。

hlsl->spir-v編譯器

這件事情,已經有khronos的人在做了complete hlsl -> spir-v translator · issue #362 · khronosgroup/glslang,但終歸不是最好的方法,進度和發展速度都缺乏管理。而因為現在微軟開源的hlsl編譯器microsoft/directxshadercompiler已經發布,我們可以在那基礎上做乙個spir-v的後端,解決hlsl編譯的問題。

spir-v->glsl/essl

這有khronosgroup/spirv-cross,是乙個已經解決的問題。

除了前面提到的工作流,我們其實還有一些可能的選項。

dxbc->spir-v

除了前面提到的從hlsl直接編譯成spir-v,其實還可以從dxbc生成spir-v。這個難度比直接搞編譯器小得多,但很可能做完之後發現hlsl->spir-v已經完善了。

spir-v->dxbc/dxil

這個就更徹底了,連d3d上都用hlsl->spir-v,然後從spir-v轉成d3d的dxbc或未來的dxil。但這麼做好處實在有限,並且損失了dxbc/dxil的高效。換句話說,並沒有什麼原先不能解決的通過這樣解決了,也沒有什麼原先能解決的通過這樣解決得更好。

用glsl/essl

這樣就能都編譯到spir-v,然後轉成dxbc/dxil。但問題和前面的一樣,好處有限。

vulkan取代opengl/opengles

在未來某個時候,vulkan一定會取代opengl和opengles。到那時候,跨平台shader編譯變得更簡單了。

「看都看了」

golang跨平台編譯

golang中跨平台交叉編譯涉及兩個重要的環境變數 goos和goarch,分別代表target host os和target host arch,如果沒有顯式設定這些環境變數,我們通過go env可以看到go編譯器眼中這兩個環境變數的當前值 go env goarch amd64 gobin ho...

Golang 跨平台編譯

1 首先進入go src 原始碼所在目錄,執行如下命令建立目標平台所需的包和工具檔案。cd usr loc al g o sr c cgo enabled 0 goos linux goarch amd64 make.bash 如果是 windows 則修改 goos 即可。cgo enabled ...

gcc跨平台編譯

在osx下編譯報找不到malloc.h,經過查詢是標頭檔案位置不相容,於是搜尋資料,看如何通過巨集來相容不同平台。在osx下可以通過gcc arch i386 dm e dev null sort檢視gcc定義的預編譯巨集。可以新增如下 進行相容 ifdef win32 define somethi...