作為光影包與紋理包開發(fā)者,我為什么留在Optifine

觀前提示:本文帶有強烈的主觀色彩和對Optifine的強烈偏袒,并且由于撰寫本文時筆者血壓過高,邏輯可能比較差。如果您認為Optifine罪該萬死應(yīng)該立刻消失,讓所有人投奔Sodium和Iris的懷抱的話,請立即關(guān)閉本篇專欄。
熟悉筆者的人都應(yīng)該知道,筆者經(jīng)常在各種地方說Sodium和Iris兩個模組的“壞話”,而對于不熟悉筆者的人來說,看一看筆者的簽名也大概能知道筆者對這兩個模組的態(tài)度了。不過由于Sodium和Iris的用戶越來越多,不少地方都有人對筆者說Optifine怎么怎么爛,Iris和Sodium怎么怎么好,應(yīng)該去用那兩個模組并且做它們的支持之類的話。筆者不想就同一個話題在不同的地方重復(fù)回答,同時也對這倆模組實在有許多不滿,今天就在這里發(fā)一個專欄,詳細地作為一個資源包與光影包雙包開發(fā)者講述一下筆者為什么不喜歡Sodium與Iris,以及為什么堅持停留在Optifine。以及,在以后還有人問我這方面問題的時候,有一個可以直接發(fā)送的鏈接。
前言:為什么停留在Optifine

筆者自然知道為什么Sodium與Iris會這么受歡迎。傾向于Sodium與Iris的人里面,除去跟風(fēng),無非是出于三個原因:優(yōu)化好,兼容性好和開源。第一個是否在絕大部分情況下成立還有待商榷,不過第二個和第三個是實打?qū)嵉?。由于Optifine的工作方式是暴力修改與注入原版代碼,受限于Minecraft的EULA注定無法開源;而對原版代碼的暴力修改也使得模組兼容性差是理所當(dāng)然的事情。不過由于筆者的情況比較特殊,因為筆者本身幾乎沒有mod需求,不管是玩Minecraft本身還是做資源包與光影包開發(fā),都只需要裝載一個Optifine,甚至不需要通過Forge安裝,導(dǎo)致筆者幾乎不會受到Optifine的兼容性帶來的負面影響,自然對于Optifine這方面的容忍度也相當(dāng)高。
Optifine也并非沒有給筆者帶來困擾,筆者在Github也給Optifine提過不少issue。但是筆者所碰見的Optifine的問題,除去適配新MC版本時產(chǎn)生的問題之外,對筆者來說影響都相對很小,甚至可以忽略不計。而Sodium與Iris相較于Optifine,對于筆者的開發(fā)體驗來說都是無比的糟糕,同時也給不少筆者熟悉的資源包/光影包開發(fā)者帶來了很多的困擾。筆者會在下面詳細的講述這些非常不愉快的開發(fā)體驗。
Sodium:優(yōu)化?還是破壞?

由于筆者的資源包(3D Default)大量使用了Optifine的資源包特性,包括但不限于自定義實體模型與發(fā)光紋理等,在Sodium出來的一段時間內(nèi)筆者完全沒有嘗試過Sodium+Iris的組合。直到有人來反饋資源包的問題,筆者無法復(fù)現(xiàn)并讓他排除mod的影響,最終測出來是Sodium的問題,筆者才意識到Sodium可能破壞了甚至是對原版資源的兼容性。
上面說的問題就是 https://github.com/CaffeineMC/sodium-fabric/issues/226 ,表現(xiàn)為完整方塊的模型,只要關(guān)閉了模型的ambientOcclusion(環(huán)境光遮蔽),就會導(dǎo)致模型位于本方塊所處空間內(nèi)的部分全部變黑,也同樣影響了游戲內(nèi)硬性關(guān)閉環(huán)境光遮蔽的方塊,例如各種光源方塊。根據(jù)筆者個人的推測,這是由于Sodium對于原版的AO系統(tǒng)進行了更改導(dǎo)致的(當(dāng)然如果有了解這一塊的人歡迎指正)。這個問題經(jīng)歷了長達兩年的時間才得到解決,筆者不清楚是因為大伙都不把這當(dāng)回事,還是Sodium開發(fā)者+有意向的社區(qū)貢獻者都一直沒法解決,又或者這個bug本身就是Sodium的刻意為之的優(yōu)化產(chǎn)生的,總之這個bug導(dǎo)致筆者對Sodium的第一印象就非常之差。


Sodium對筆者的影響主要就是這一個問題,但是筆者從其他資源包的作者那也聽到過數(shù)個Sodium的問題。一個是Sodium與部分mod一起加載的時候會導(dǎo)致哞菇背上的蘑菇使用的模型異常,如果資源包作者在對應(yīng)蘑菇的方塊狀態(tài)文件內(nèi)配置了多個隨機模型,那么哞菇背上的蘑菇模型就會迅速的在這幾個被配置的模型中切換,產(chǎn)生嚴重的閃爍。這個問題在停用Sodium后會恢復(fù)正常。
另一個問題的影響對于使用3D模型的資源包作者來說影響則廣泛得多:Sodium會降低模型頂點位置的精度。由于浮點數(shù)的精度是有限的,頂點著色器最終輸出的位置在幾乎所有情況下都不是實際準確的,導(dǎo)致了頂點位置會隨著視角轉(zhuǎn)動和玩家移動產(chǎn)生微小的變化;而如果模型的幾個面有一部分重合,就會在這種變化中遮蔽關(guān)系不斷變化,產(chǎn)生非常迅速的閃爍。而即使是靜止不動,也能看見兩個面在間斷的互相遮蔽。我們把這種現(xiàn)象叫做深度沖突(z-fighting)。

有些時候,出于優(yōu)化或者其他視覺效果上的目的,部分面的重合無法避免。于是,為了避免深度沖突,大部分模型作者就會把一個面前移一些,或者把一個面后移一些。移動的幅度一般不會很大,以避免破壞視覺上的效果。但是Sodium降低頂點精度之后導(dǎo)致了一個問題:如果移動幅度不夠大,Sodium降低頂點位置精度后還是重合在一起,照樣深度沖突;如果移動幅度太大,又會影響整體的視覺效果。筆者對Sodium產(chǎn)生這個問題的原因有較為詳細的了解,并且筆者認為這就是他們優(yōu)化帶來的無可避免的副作用,幾乎不存在修復(fù)的可能。
Iris:最讓人高血壓的一集

筆者不怎么喜歡Sodium/Iris這套組合,絕大部分得拜Iris所賜。在Iris 1.5發(fā)布之前,筆者給GFME做了Iris的兼容,已經(jīng)算是給夠面子了。毫不客氣的說,每一次使用Iris,都能給筆者帶來全新的極為負面的體驗,導(dǎo)致筆者出于測試的目的使用了幾次Iris之后,已經(jīng)再也不想碰它了。
首先來聊點震撼的。做著色器開發(fā)時碰到問題時,我們一般會使用NVIDIA Nsight Graphics或者Renderdoc等工具進行詳細的debug,同時也能提供性能參數(shù)作為優(yōu)化時的參考。但是Iris使用Nsight Graphics的時候有一個非常嚴重的問題:它會在日志中打印大量無用的OpenGL debug message,多的時候一秒鐘甚至能打印上萬條,不說在這種密度的無用信息中找有效信息的難度,海量的日志還會大量拖慢不少啟動器的日志界面的性能,同時占用相當(dāng)大的磁盤空間和內(nèi)存空間。如果對Nsight Graphics啟動的Iris的日志大小沒有什么概念的話,筆者這里有一個現(xiàn)成的例子:接近5分鐘的時間,Iris產(chǎn)生了一個600MB大小,156萬行的日志。筆者曾經(jīng)在ShaderLABS Discord頻道吐槽過用Nsight Graphic開啟Iris時輸出過多的日志信息,Iris主開發(fā)之一的IMS的回答是“Optifine直接把OpenGL的調(diào)試信息徹底關(guān)了,這樣做不好”??上н@個五分鐘產(chǎn)生的600M日志是在這件事之后產(chǎn)生的,不然筆者一定要把這個log拍到IMS臉上,相較于產(chǎn)生一個這樣的玩意,筆者寧愿不輸出那一大堆沒有用的GL調(diào)試信息。

Iris本身在合并著色器文件的時候也根本沒有考慮過著色器開發(fā)者。經(jīng)常看日志的玩家應(yīng)該能看見Iris在編譯著色器之前會刪除未使用的函數(shù)————出發(fā)點是好的,每次切換光影設(shè)置都預(yù)先把宏應(yīng)用了然后刪除未使用函數(shù)可以盡可能的減少對著色器的影響,減少需要重新編譯的著色器的數(shù)量,以提升修改光影設(shè)置之后的重載光影的性能。但是Iris有一個大問題:合并著色器,應(yīng)用宏和刪除未使用函數(shù)的時候,都沒有使用#line宏規(guī)范文件序號和行數(shù),導(dǎo)致如果著色器在Iris上編譯報錯,報錯的內(nèi)容基本上沒有用————文件序號恒定為0,而行數(shù)是執(zhí)行合并后作為一個整體的文件的,只能通過后面的報錯內(nèi)容猜測具體是什么位置,或者在開啟Iris的開發(fā)者模式后,在.minecraft/patched_shaders文件夾找到出問題的著色器,根據(jù)Iris日志里提供的報錯行數(shù)找到錯誤位置,通過上下文猜測這一段是在哪個文件的哪一行。同時,大量的刪除未使用函數(shù)也會影響到日志的閱讀,導(dǎo)致有效信息獲取變得更加困難(雖然說Iris的日志本身就沒什么有效信息)。而在Optifine內(nèi),幾乎所有的文件合并,以及Optifine自己加的宏,都通過#line進行了規(guī)范,如果著色器編譯錯誤,在絕大部分情況下都能在日志中給出準確的文件序號和行號,同時會給出所有的文件序號對應(yīng)的文件。



目前版本的Sundial在Iris上運行時有不少問題,這得歸功于Iris的Optifine功能兼容工作:同一個功能做了一部分,但是沒有做完。Iris在1.5聲稱支持了自定義uniform,而后來在1.6聲稱支持了Optifine在1.17+上的core profile寫法。有自定義uniform的光影在1.5+的Iris上確實能跑了,但是不少光影的自定義uniform仍然存在問題————Iris沒有實現(xiàn)自定義uniform的所有功能。Sundial沒有受到Iris的自定義uniform不完善的影響,不過不少國內(nèi)外光影都受到了影響,比較明顯的是群系霧:Iris沒有做自定義uniform里群系ID的支持,只有群系的大體分組的支持。這導(dǎo)致了Bliss與itt等部分光影依賴于群系ID的群系霧在Iris上完全無法正常工作。Sundial受到影響的地方是1.17+的Core profile支持。Sundial使用“區(qū)塊位置”(在1.16以下是gl_Vertex.xyz,在1.17以上是vaPosition)進行完整方塊的判斷。由于Iris聲稱支持了Optifine的core profile但是沒有完全支持,導(dǎo)致Sundial的完整方塊判斷幾乎所有方塊都能通過,使得竹子、花盆等本來在體素化時會被忽略的方塊參與了體素化,甚至連手持物品都能寫入體素,產(chǎn)生了大量異常的黑影。

如果說上面的問題還只是讓人覺得他們寫代碼不注意細節(jié),筆者在幫助國內(nèi)其他光影測試Iris兼容性的時候發(fā)現(xiàn)的問題則更讓人擔(dān)心Iris的代碼質(zhì)量:如果在gbuffer內(nèi)對特定緩沖指定特殊的混合方式,這種特殊的混合方式可能會泄漏到后處理部分。IterationT 3是被這個bug影響的最嚴重的光影,產(chǎn)生的現(xiàn)象包括但不限于撿起部分物品時白屏(這一段時間整個colortex1的混合方式都變成了根據(jù)alpha混合,而Tahnass有沒有特殊數(shù)據(jù)要寫入時給alpha通道輸出0的習(xí)慣)、手持部分物品時反射錯誤(IterationT在某個地方使用輸出的alpha通道存儲反射相關(guān)數(shù)據(jù))等。

此外,Iris還有著獨特的著色器讀取機制:如果一組計算著色器組(例如composite.csh至composite_z.csh為一組)中某個后綴沒有出現(xiàn),則剩余的計算著色器都不會讀?。ň唧w描述參考 https://github.com/IrisShaders/Iris/issues/1840)。這也是筆者在Iris的github反饋的唯一一個issue————之后越用越糟心,干脆不用了,自然也不去反饋了。IMS在這個issue下的回答是這么做是為了改善光影加載時間。好吧,從這個回答我們就能看出來Iris是怎么讀著色器的:它會按照著色器正常的加載順序一個個查著色器在不在,在的話就讀取,不在的話就不讀?。欢?.18開始的Optifine最高可以有一萬多個著色器,其中絕大多數(shù)都是計算著色器,所以這個“優(yōu)化”看起來還算合理。但是實際上這種讀取方式本身優(yōu)化就非常差。筆者并沒有去仔細讀過Iris的代碼,不過從筆者的個人經(jīng)歷外推的話,如果他們代碼寫的差一點,每個著色器文件都去查詢文件在不在,那性能確實稀爛;如果他們代碼寫的好一點,先把所有存在的文件存一個哈希表或者是別的表,然后再在表里面查文件名存不存在,那還相對好不少。但是實際上存在著更優(yōu)的解決方案:直接讀取目標目錄(比如shaders,或者shaders/worldxx內(nèi)的維度專屬著色器)下的所有文件,對這些文件進行分類和排序,然后按照順序直接做接下來的緩沖綁定與著色器編譯工作。使用這種讀取方式不僅在效率上快得多,而且可以做到著色器的完全覆蓋,不會像現(xiàn)在一樣為了優(yōu)化被迫在計算著色器的讀取上做出妥協(xié)。當(dāng)然,這個bug的受害者也是IterationT 3,Tahnass使用計算著色器寫泛光效果用的是Dual Blur,降采樣和升采樣是成對出現(xiàn)的,而Tahnass大概是覺得效果已經(jīng)夠了沒必要,刪除了一組升降采樣,導(dǎo)致計算著色器組的后綴出現(xiàn)了一個空缺,這導(dǎo)致了Iris直接放棄了讀取后方的升采樣著色器,最終使IterationT 3在讀取泛光時,讀取到的是場景的法線數(shù)據(jù),從而產(chǎn)生了過亮的現(xiàn)象。

在筆者撰寫這篇專欄的時候,IterationT 3在最新的1.6.5的Iris上又出現(xiàn)了一個bug:gbuffer的alphaTest失效了,草、火把等本應(yīng)忽略透明像素的方塊因為未知原因沒有忽略透明像素,讓許多方塊的模型渲染出來與原版有較大差異。如上文所說,因為每次使用Iris都能給我?guī)砣碌呢撁骟w驗,筆者這次完全沒有興趣去測試這個新bug是怎么回事,Tahnass會不會修復(fù)就不知道了。
結(jié)語
我知道Optifine的模組兼容性差,我也知道Sodium/Iris因為開源和使用mixin前景更廣闊,但是從我個人的開發(fā)體驗來看,Sodium+Iris除了提供了一些諸如SSBO或者一些額外image之類的新特性外,對開發(fā)帶來的負面體驗遠大于新特性帶來的正面感受。而在與Iris的接觸中看到的各種細節(jié)問題更是讓我擔(dān)心他們的代碼質(zhì)量與開發(fā)態(tài)度。因此,我在現(xiàn)在,與至少未來的一段時間內(nèi),不會有使用Iris,或者解決Iris獨有bug的想法。謹在此撰寫這篇專欄,以方便未來有人再次詢問相關(guān)問題時直接粘貼鏈接。