当前位置:网站首页>SPIR-V初窺
SPIR-V初窺
2022-07-06 01:14:00 【zenny_chen】
SPIR-V的概述
SPIR-V著色器被嵌入在 模塊 之中。每個模塊可以包含一個或多個著色器。每個著色器具有一個入口點,該入口點具有一個名字和一個著色器類型,著色器類型用於定義當前著色器跑在哪個著色 階段 。入口點則是當前著色器開始執行的比特置。一個SPIR-V模塊伴隨著創建信息被傳遞給Vulkan,然後Vulkan返回錶示該模塊的一個對象。該模塊對象隨後可以用於構造一條 流水線。這就是一單個著色器完整編譯的版本,伴隨著在當前設備上要運行它所需要的信息。
SPIR-V的錶示
SPIR-V對於Vulkan而言是僅有的官方支持的著色語言。它在API層被接受並且最終用於構造流水線,這些流水線是配置一個Vulkan設備的對象,為你的應用完成工作。
SPIR-V被設計為對一些工具和驅動而言非常容易處理的錶示。這通過不同實現之間的多樣性來提昇可移植性。一個SPIR-V模塊的內部錶示是一條32比特字的流,存放在存儲器中。除非你是一比特工具寫手或計劃自己生成SPIR-V,否則的話你不太需要直接處理SPIR-V的二進制編碼。而是說,你要麼可以看SPIR-V的可讀的文本錶示,或是使用諸如 glslangvalidator 這樣的官方Khronos GLSL編譯工具來生成SPIR-V。
下面我們可以寫一個計算著色器源文件,命名為 simpleKernel.comp
,然後拿給 glslangvalidator 去編譯。其中 .comp
後綴名能告訴 glslangvalidator 該著色器將作為一個計算著色器進行編譯。
#version 460 core
void main(void)
{
// Do Nothing...
}
然後我們使用以下命令行(筆者用的是Windows環境):
%VK_SDK_PATH%/Bin/glslangValidator -o simpleKernel.spv -V100 simpleKernel.comp
隨後我們就能看到生成了一個名為 simpleKernel.spv 的SPIR-V二進制文件。我們可以使用SPIR-V反匯編器對該二進制文件再進行反匯編。我們使用 spirv-dis 這一官方反匯編工具。
%VK_SDK_PATH%/Bin/spirv-dis -o simpleKernel.spvasm simpleKernel.spv
以上命令將會輸出人可讀的匯編文件 simpleKernel.spvasm。其內容如下:
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 10
; Bound: 6
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 460
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
我們可以看到SPIR-V的文本形式看上去像一種古怪的匯編語言的變種。我們可以逐行過一下上述反匯編內容,然後看看它是如何與原始的GLSL輸入相關聯的。上面所輸出匯編的每一行錶示一單條SPIR-V指令,一條指令可能會由多個符號(token)構成。此外,分號(;
)開頭的語句為一條注釋。
流中的第一條指令是 OpCapability Shader
,它請求開啟著色器能力。SPIR-V功能被粗略地劃分為 指令 和 特征。在你的著色器能使用任一這些特征之前,必須聲明它將使用哪種特征作為其一部分。上述代碼中的著色器是一個圖形著色器並從而使用 Shader 這一能力。隨著我們介紹更多SPIR-V以及Vulkan功能,我們將會介紹每種特征所依賴的各種不同的能力。
接著,我們看到 %1 = OpExtInstImport "GLSL.std.450"
。這本質上是導入額外的一組相應於GLSL版本450所包含的功能,而這往往也是原始著色器所寫入的。注意,這條指令最前面寫有 %1 =
。這是對當前指令的結果賦上一個ID。OpExtInstImport
的結果在效果上是一個庫。當我們想調用此庫中的函數時,我們就使用 OpExtInst
這條指令來實現。它具有兩個操作數,分別是一個庫(OpExtInstImport
指令的結果)和一個指令索引。這允許SPIR-V指令集可被任意擴展。
接著,我們看到了某些額外的聲明。OpMemoryModel
為此模塊指定了工作存儲器模型,在上述代碼例子中是對應於GLSL版本450的邏輯存儲器模型。這意味著所有存儲器訪問所有存儲器訪問通過資源執行而不是通過一個物理存儲器模型。而物理存儲器模型訪問存儲器時是直接通過指針。
接著是對當前模塊的入口點的聲明。OpEntryPoint GLCompute %main "main"
指令意味著有一個入口點對應於OpenGL計算著色器,所導出的函數名為 main,而這裏所指定的ID為 %main
,該符號將會被 spirv-as 匯編器根據上下文來分配一個具體的ID號。從上述代碼中我們看到已經分配了 %1
、%3
和 %5
,而唯獨沒有 %2
和 %4
。而這兩個ID號很有可能最終在被編譯的時候會被 %main
和 下面一個符號 %void
所分配。這裏的函數名 "main"
用於引用入口點,當我們將所產生的著色器模塊遞回給Vulkan時需要此模塊的入口點。
然後我們會在後一條指令中使用上面的 %main
這個符號—— OpExecutionMode %main LocalSize 1 1 1
定義了此著色器的執行組大小為 1×1×1 個工作項。如果局部大小 layout
修飾符缺省的話,那麼GLSL會隱式指定local size為 1×1×1。
下面的兩條指令只是簡單地提供一些信息。OpSource GLSL 460
指明當前模塊是用GLSL版本460進行編譯的,而 OpName %main "main"
為具有ID為 %main
的符號提供了一個名字。
現在我們就可以來看看這個 main 函數真正有料的部分。首先,%void = OpTypeVoid
聲明了我們想用 %void
這一符號作為類型 void
。正如前面所述,它最終可能會被分配的ID為2。在SPIR-V中所有一切都具有一個ID,甚至是類型定義。較大的聚合類型可以通過順序地引用更小的、更簡單的類型進行構造。然而,我們需要從某個地方開始,而這裏將一個類型分配給 void
正是我們開始的地方。
%3 = OpTypeFunction %void
意味著我們定義一個為3的ID作為一個函數類型,該函數類型取了 void
作為其返回類型(先前聲明為 void
)並且沒有任何參數。我們在下面一行使用了該類型。%main = OpFunction %void None %3
這意味著我們聲明了一個符號 %main
(最終被分配的ID可能為4,而我們之前用它命名了 "main"
)作為函數3(上面那條語句)的一個實例,它返回類型為 void
,並且沒有其他特別的聲明。這是通過指令中的 None
進行指示,而該比特置上其他可用的參數包括是否內聯、或是該變量是否為常量等等。
最後,我們看到對一個標簽(標簽沒有被使用,而只是編譯器操作所留下的副作用)、隱式的返回語句、以及最終函數結束的聲明。這就是我們SPIR-V的末尾。
設計原則
- 規律性:所有指令均以指示當前指令有多長的一個字的個數開頭。這允許SPIR-V模塊不需要去解碼每個操作碼。所有指令都具有一個操作碼來支配所有的操作數,確定它們屬於哪種操作數。對於具有可變個數操作數的指令,可變操作數的個數通過將該指令的字的總個數减去非可變的字的個數得到。
- 非組合性:沒有組合類型暴露,也不需要對類型的大型編碼/譯碼錶。取而代之的是,類型是參數化的。圖像類型聲明了它們的維數、陣列等等。一切都是正交的,這極大地簡化了代碼。這對於其他類型也是類似的。這也應用於操作碼。操作對於標量/向量大小都是正交的,但對於整數與浮點數之間的區別並不是正交的。
- 無模型:當指定了一個給定的執行模型(比如流水線階段)之後,內部操作本質上是無模型的:一般來說,它遵循這個規則:“同一個拼寫具有相同的語義”,並從而不會具有修改語義的模式比特比特。如果對SPIR-V的改變修改了語義,那麼它應該是一種不同的拼寫。這使得SPIR-V的消費者更為健壯。確實存在聲明的執行模式,但這些通常影響了模塊與其執行環境交互的方式,而不是其內部語義。也有能力被聲明,但這是要聲明要被使用的功能子集,而不是去改變要被使用的任何語義。
- 聲明式的:SPIR-V聲明了外部可見的模式,像“寫深度”,而不是具有要求從完整著色器觀察進行推導的規則。它也顯式地聲明了將要使用什麼尋址模式、執行模型、擴展指令集等等。
- SSA:中間操作的所有結果都是嚴格的SSA。然而,在存儲器中駐留的所聲明的變量、以及用於訪問的加載/存儲,並且這樣的變量可以被存儲多次。
- IO:某些存儲類是用於輸入/輸出(IO)的,並且從根本上,IO是通過在這些存儲類中所聲明的變量的加載/存儲來完成的。
靜態單一賦值(SSA)
SPIR-V包含了一條phi指令以允許將中間結果從分裂的控制流合並在一起。這允許控制流不需要加載/存儲到存儲器。SPIR-V在加載/存儲的使用程度上是很靈活的;不用phi指令來使用控制流也是可能的,而通過用存儲器的加載/存儲仍然遵循著SSA形式。
某些存儲類是用於IO的,而且在根本上IO是通過存儲/加載實現的,而初始的加載和最終的存儲不會被消除。其他存儲類是著色器本地的,這可以將它們的加載/存儲進行消除。我們可以認為對這種加載/存儲通過將它們搬移到SSA形式的中間結果進行巨大的消除是一種優化。
內建變量
SPIR-V以一個枚舉值裝飾從一個高級語言來標識內建變量。這將任一不尋常的語義分配給該變量。內建變量除此以外以它們正確的SPIR-V類型進行聲明,並且跟其他變量一樣對待。
特化
特化允許基於常量值的一個可移植的SPIR-V模塊進行離線創建,而此常量一開始是未知的,直到後面某個時間點。比如,在創建一個模塊期間,具有一個常量固定大小數組,其常量值是未知的,從而該數組的具體大小未知。但當該模塊被下放到目標架構時,該常量值就已知了。
術語
指令
一個SPIR-V模塊與指令的物理布局
一個SPIR-V模塊是一單串線性字(word)流。開頭的一些字如下錶所示:
錶1:物理布局的起始字
字序號 | 內容 |
---|---|
0 | 魔術數——0x07230203 |
1 | 版本號。這些字節從高比特序到低比特序: 0 | 主版本號 | 小版本號 | 0 從而,版本 1.3 的值即為: 0x00'01'03'00 |
2 | 生成器的魔術數。它與生成該模塊的工具相關聯。其值並不影響任何語義,並且允許是0。使用一個非0值是值得鼓勵的,並且可以在Khronos中注册。 |
3 | 邊界;這裏在本模塊中的所有 <id> 確保滿足:0 < id < Bound 邊界應當盡量小,越小越好。一個模塊中的所有 <id> 應當密集地打包並從而靠近0。 |
4 | 0(為指令模式而保留,如果需要的話) |
5 | 指令流的第一個字,見下錶 |
所有剩下的字是一個線性的指令序列。每條指令的字流如下:
錶2:指令物理布局
指令字序號 | 內容 |
---|---|
0 | 操作碼:高16比特是當前指令的字個數。低16比特是操作碼枚舉值。 |
1 | 可選的指令類型 type <id> (是否存在以操作碼來决定) |
- | 可選的指令結果 Result <id> (是否存在以操作碼來决定) |
- | 操作數1(如果需要) |
- | 操作數2(如果需要) |
… | … |
字個數 - 1 | 操作數 N(N 由字個數减去 1到3個字來確定,這1到3個字用於操作碼、指令類型 type <id> 、以及指令結果 Result <id> )。 |
指令是可變長度的,由於具有可選的指令類型 type <id>
和 Result <id>
字,以及可變個數的操作數。
边栏推荐
- Getting started with devkit
- Cannot resolve symbol error
- BiShe - College Student Association Management System Based on SSM
- Gartner released the prediction of eight major network security trends from 2022 to 2023. Zero trust is the starting point and regulations cover a wider range
- Use of crawler manual 02 requests
- cf:C. The Third Problem【关于排列这件事】
- 程序员成长第九篇:真实项目中的注意事项
- Mobilenet series (5): use pytorch to build mobilenetv3 and learn and train based on migration
- Who knows how to modify the data type accuracy of the columns in the database table of Damon
- Recommended areas - ways to explore users' future interests
猜你喜欢
基于DVWA的文件上传漏洞测试
Fibonacci number
3D模型格式汇总
Study diary: February 13, 2022
Convert binary search tree into cumulative tree (reverse middle order traversal)
How to see the K-line chart of gold price trend?
Vulhub vulnerability recurrence 75_ XStream
Unity | two ways to realize facial drive
Cannot resolve symbol error
cf:C. The Third Problem【关于排列这件事】
随机推荐
记一个 @nestjs/typeorm^8.1.4 版本不能获取.env选项问题
Fibonacci number
cf:C. The Third Problem【关于排列这件事】
现货白银的一般操作方法
Paging of a scratch (page turning processing)
Promise
Leetcode study - day 35
Leetcode1961. 检查字符串是否为数组前缀
cf:D. Insert a Progression【关于数组中的插入 + 绝对值的性质 + 贪心一头一尾最值】
I'm interested in watching Tiktok live beyond concert
VMware Tools安装报错:无法自动安装VSock驱动程序
Logstash clear sincedb_ Path upload records and retransmit log data
In the era of industrial Internet, we will achieve enough development by relying on large industrial categories
Three methods of script about login and cookies
Recursive method converts ordered array into binary search tree
Recommended areas - ways to explore users' future interests
282. Stone consolidation (interval DP)
VMware Tools installation error: unable to automatically install vsock driver
Who knows how to modify the data type accuracy of the columns in the database table of Damon
直播系统代码,自定义软键盘样式:字母、数字、标点三种切换