來更新一下進度。

目前Metal2的進度是基本管線已經可以運作了,正在寫Multi-Pass Rendering,也就是陰影貼圖、BRDF查找表等的生成部分。下面是Metal2目前的渲染效果(Phong光照模型、非PBR)。在我的Mac Pro(2015年型號,intel集顯)筆記本上可以跑到60fps(實際渲染幀長10ms,也就是不垂直同步的話可以達到100fps左右)

視頻封面

使用Metal2的基本渲染(Phong)

到目前為止碰到的幾個問題:

  1. Objective C++似乎還是有些奇怪的問題,當然也可能是我對Objective C的理解還很有限。比如id<XXXX>和(ObjC當中的)指針的使用區分,對象的生命周期等。將ObjC的對象通過typecast成為C++類型(如void*、intptr_t)然後傳遞到純C++代碼的C++容器(諸如std::vector)當中進行保存,在使用的時候再typecast回來傳遞給ObjC貌似會引起隨機的內存問題。所以目前我只能將所有的ObjC對象保留在Objective C++層面、將其在容器當中的索引傳遞給純C++;(11/27更新:這個問題的起因應該是在使用ARC的情況下不能在C/C++的結構體當中使用Object C的指針,在typecast的時候也有特別的要求。具體參照:Transitioning to ARC Release Notes)
  2. Metal的Shading Language不支持Tessellation Shader、也不支持Geometry Shader。但是Metal的管線支持Tessellation、只不過是通過一種類似設置固定管線的方式去設置(通過在CPU端調用API)。不過仔細想想Tessellation的確是一種可配置的固定管道功能,所以這麼歸類也不能說錯,甚至是更準確。只不過怎麼和其它圖形API的Tessellation進行統一,這個我還要想一想。至於Geometry Shader,目前我這裡是用來加速點光源的Shadow Map生成,主要使用的是Multi-Layered Rendering,這個其實Metal也是支持的,只不過不是以Geometry Shader的方式,而是普通的Vertex Shader就可以。同樣,這個怎麼在不同平台之間統一,我還要想一想;
  3. Metal當中對於Render Target的Clear以及對於MSAA Render Target的Resolve等都是作為Render Target的屬性被封裝掉的。也就是說,給Render Target設置一個清除色並打開清除開關之後,每次使用Render Target的時候它就會自動清除。而MSAA的Resolve更加傻瓜,在設置了Render Target的Sample數>1個之後,Render Target就會自動提供兩個Texture,一個是Resolve之前的一個是Resolve之後的。這很容易使用,但是同樣出現了與其它API(DX12/Vulkan)在API水準上對不齊的問題;
  4. Metal的Shader(.metal)可以和OpenGL一樣在執行期進行編譯,也可以用離線工具將其先編譯為中間形式(.air,類似.obj),然後將其打包(.metalar。這步可選,類似將.obj打包成.lib),最後生成metallib(.metallib)。不過這裡有個問題是,如果將兩個分別編譯的fragment shader打包在一起,如果它們都引用了類似的自定義公共函數,比如shadow_test,生成metalib的時候會抱怨重定義。解決的辦法是在製作metallib的命令行當中加入參數-split-module-without-linking,就是讓它在這個階段不要進行link操作,而在執行的時候進行link。因為執行的時候我們不可能在同一個管線當中使用兩個fragment shader、所以link就不會出問題。會出現這個問題的根本原因是通過spirv轉出來的metal shader代碼會將所有需要的函數,包括共通函數都展開在源代碼裡面(也就是生成的代碼不再有include語句),從而導致共通的部分在各個代碼當中都有一套拷貝。。。看來距離工業實用spirv-cross還有很多要改進的地方。

總的來說Metal目前用下來的感覺是一種比OpenGL低級但是比DX12/Vulkan高級的API水平。推測可能是為了兼顧iPhone這種移動平台吧。

在對應Metal的過程當中對GraphicsManager的結構也作了很多修改,導致現在OpenGL和DX12編譯都出問題,正在慢慢將它們跟上。。。

推薦閱讀:

相关文章