当前位置:网站首页>A glimpse of spir-v

A glimpse of spir-v

2022-07-06 01:15:00 zenny_ chen

SPIR-V Overview

SPIR-V Shaders are embedded in modular In . Each module can contain one or more shaders . Each shader has an entry point , The entry point has a name and a shader type , The shader type is used to define which shader the current shader runs in Stage . The entry point is where the current shader starts executing . One SPIR-V The module is passed to... Along with the creation information Vulkan, then Vulkan Return an object representing the module . The module object can then be used to construct a Assembly line . This is a fully compiled version of a single shader , Along with the information needed to run it on the current device .


SPIR-V It means

SPIR-V about Vulkan Is the only officially supported coloring language . It's in API Layers are accepted and eventually used to construct pipelines , These pipelines are configured with one Vulkan The object of the device , Finish the work for your application .

SPIR-V Designed to be very easy to handle for some tools and drivers . This improves portability through diversity between different implementations . One SPIR-V The internal representation of the module is a 32 Bitword stream , Store in memory . Unless you are a tool writer or plan to build it yourself SPIR-V, Otherwise, you don't need to deal with it directly SPIR-V Binary code of . But rather , You can either watch SPIR-V Readable text representation of , Or use something like glslangvalidator Such an official Khronos GLSL Compile tools to generate SPIR-V.

Next we can write a calculation shader source file , Name it simpleKernel.comp, Then give it to me glslangvalidator To compile . among .comp The suffix can tell glslangvalidator This shader will be compiled as a computational shader .

#version 460 core

void main(void)
{
    
    // Do Nothing...
}

Then we use the following command line ( I use Windows Environmental Science ):

%VK_SDK_PATH%/Bin/glslangValidator  -o simpleKernel.spv  -V100  simpleKernel.comp

Then we can see that a file named simpleKernel.spv Of SPIR-V Binary . We can use SPIR-V The disassembler disassembles the binary file . We use spirv-dis This official disassembly tool .

%VK_SDK_PATH%/Bin/spirv-dis  -o simpleKernel.spvasm  simpleKernel.spv

The above command will output a human readable assembly file simpleKernel.spvasm. It reads as follows :

; 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

We can see SPIR-V The text form of looks like a strange variant of assembly language . We can go through the above disassembly content line by line , Then see how it works with the original GLSL Enter the associated . Each line of the output assembly above represents a single SPIR-V Instructions , An instruction may consist of multiple symbols (token) constitute . Besides , A semicolon (;) The first statement is a comment .

The first instruction in the stream is OpCapability Shader, It requests to turn on shader capabilities .SPIR-V Functions are roughly divided into Instructions and features . Before your shader can use any of these features , You must state which features it will use as part of it . The shader in the above code is a graphics shader and thus uses Shader This ability . As we introduce more SPIR-V as well as Vulkan function , We will introduce the different capabilities that each feature depends on .

next , We see %1 = OpExtInstImport "GLSL.std.450". This is essentially importing an additional set corresponding to GLSL edition 450 Functions included , And this is often written by the original shader . Be careful , This instruction begins with %1 = . This is an assignment to the result of the current instruction ID.OpExtInstImport The result is a library in effect . When we want to call functions in this library , We just use OpExtInst This instruction is used to realize . It has two operands , They are one library (OpExtInstImport The result of the instruction ) And an instruction index . This allows SPIR-V The instruction set can be arbitrarily extended .

next , We see some additional statements .OpMemoryModel A working memory model is specified for this module , In the above code example, it corresponds to GLSL edition 450 Logical memory model . This means that all memory accesses all memory accesses are performed through resources rather than through a physical memory model . The physical memory model accesses the memory directly through the pointer .

Next is the declaration of the entry point of the current module .OpEntryPoint GLCompute %main "main" Instruction means that there is an entry point corresponding to OpenGL Compute Shader , The exported function is named main, And what is specified here ID by %main, The symbol will be spirv-as The assembler assigns a specific ID Number . From the above code, we can see that %1%3 and %5, But there is no %2 and %4. And these two. ID It is very likely that number will eventually be compiled %main and The next symbol %void Allocated . The function name here "main" Used to reference entry points , When we pass the generated shader module back to Vulkan The entry point of this module is required .

Then we will use the above in the latter instruction %main This symbol —— OpExecutionMode %main LocalSize 1 1 1 The execution group size that defines this shader is 1×1×1 Work items . If the local size layout Modifier defaults , that GLSL Will implicitly specify local size by 1×1×1.

The following two instructions simply provide some information .OpSource GLSL 460 Indicates that the current module uses GLSL edition 460 For compiling , and OpName %main "main" For having ID by %main The symbol of provides a name .

Now we can take a look at this main The real part of the function . First ,%void = OpTypeVoid It states that we want to use %void This symbol is used as a type void. As mentioned earlier , It may eventually be allocated ID by 2. stay SPIR-V Everything in has a ID, Even type definitions . Larger aggregate types can be referenced sequentially by smaller 、 Simpler types to construct . However , We need to start somewhere , Here, a type is assigned to void That's where we started .

%3 = OpTypeFunction %void It means that we define one as 3 Of ID As a function type , The function type is void As its return type ( Previously stated as void) And there are no parameters . We use this type in the following line .%main = OpFunction %void None %3 This means that we declare a symbol %main( Finally assigned ID May be 4, And we named it before "main") As function 3( The sentence above ) An example of , Its return type is void, And there is no other special statement . This is through None Give instructions , Other parameters available at this location include whether to inline 、 Or whether the variable is a constant .

Last , We see a label ( The label is not used , It's just a side effect of compiler operation )、 Implicit return statement 、 And the declaration of the end of the final function . This is us SPIR-V At the end of .


Design principles

  • Regularity : All instructions begin with a number of words indicating how long the current instruction is . This allows SPIR-V The module does not need to decode every opcode . All instructions have an opcode to govern all operands , Determine which operands they belong to . For instructions with variable operand , The number of variable operands is obtained by subtracting the total number of words of the instruction from the number of non variable words .
  • Non combinatorial : No combination type exposure , There is no need for large-scale coding of types / Decoding table . In its place , Types are parameterized . Image types declare their dimensions 、 Arrays, etc . Everything is orthogonal , This greatly simplifies the code . This is similar to other types . This also applies to opcodes . Operation for scalar / Vector sizes are orthogonal , But the difference between integer and floating-point number is not orthogonal .
  • No model : When a given execution model is specified ( For example, the assembly line stage ) after , Internal operations are essentially modelless : Generally speaking , It follows this rule :“ The same spelling has the same semantics ”, And thus does not have schema bits with modified semantics . If the SPIR-V Changes to the semantics , Then it should be a different spelling . This makes SPIR-V Our consumers are more robust . There are indeed declared execution patterns , But these usually affect the way the module interacts with its execution environment , Rather than its internal semantics . Also have the ability to be declared , But this is to declare the subset of functions to be used , Instead of changing any semantics to be used .
  • declarative :SPIR-V Declare externally visible patterns , image “ Write depth ”, Instead of having rules that require derivation from full shader observations . It also explicitly declares what addressing mode will be used 、 Execution model 、 Extended instruction set, etc .
  • SSA: All results of intermediate operations are strict SSA. However , Declared variables residing in memory 、 And loading for access / Storage , And such variables can be stored many times .
  • IO: Some storage classes are used for input / Output (IO) Of , And fundamentally ,IO It is through the loading of variables declared in these storage classes / Storage to complete .

Static single assignment (SSA)

SPIR-V Contains a phi Instructions to allow intermediate results to be merged from the split control flow . This allows the control flow to be unloaded / Store in memory .SPIR-V In the load / The use of storage is flexible ; no need phi Instructions to use control flow is also possible , And by loading with memory / Storage still follows SSA form .

Some storage classes are used for IO Of , And basically IO It's through storage / Load the implemented , The initial loading and final storage will not be eliminated . Other storage classes are shader native , This can load them / Storage for elimination . We can think of this loading / Storage by moving them to SSA The great elimination of intermediate results of form is an optimization .


Built in variables

SPIR-V Use an enumeration value decoration to identify built-in variables from a high-level language . This assigns any unusual semantics to the variable . In addition to these, the built-in variables are correct SPIR-V Type to declare , And treat it like other variables .


specialized

Specialization allows a portable based on constant values SPIR-V Modules are created offline , This constant was unknown at first , Until some later time . such as , During the creation of a module , Has a constant fixed size array , Its constant value is unknown , Therefore, the specific size of the array is unknown . But when the module is decentralized to the target architecture , The constant value is known .


The term


Instructions


One SPIR-V Physical layout of modules and instructions

One SPIR-V The module is a single string of linear words (word) flow . Some words at the beginning are shown in the following table :

surface 1: The starting word of physical layout

Word serial number Content
0 Magic number ——0x07230203
1 Version number . These bytes are in high order to low order :
0 | The major version number | Minor version number | 0
thus , edition 1.3 The value is :0x00'01'03'00
2 Magic number of generator . It is associated with the tool that generates the module . Its value does not affect any semantics , And it is allowed to be 0. Use a non 0 Value is worth encouraging , And you can Khronos Register in .
3 The border ; Here are all in this module <id> Make sure that :
0 < id < Bound
The boundary should be as small as possible , The smaller the better. . All in a module <id> It should be packed densely and thus close to 0.
40( Reserved for instruction mode , If necessary )
5 The first word of the instruction stream , See the table below

All the remaining words are a linear sequence of instructions . The word stream of each instruction is as follows :

surface 2: Instruction physical layout

Instruction word serial number Content
0 opcode : high 16 Bit is the number of words in the current instruction . low 16 Bit is the opcode enumeration value .
1 Optional instruction types type <id> ( The existence depends on the opcode )
- Optional instruction results Result <id> ( The existence depends on the opcode )
- Operands 1( if necessary )
- Operands 2( if necessary )
The number of words - 1 Operands NN Subtract... From the number of words 1 To 3 Words to determine , this 1 To 3 Words for opcode 、 Instruction type type <id>、 And instruction results Result <id> ).

Instructions are of variable length , Due to the optional instruction type type <id> and Result <id> word , And variable operands .

原网站

版权声明
本文为[zenny_ chen]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060113440173.html