├── .gitignore ├── .gitmodules ├── 01 - Introduction to SPIR-V ├── README.md ├── exercise.spvasm └── validate ├── 02 - My First Change ├── README.md ├── exercise.spvasm └── validate ├── 03 - My First Variable ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 04 - An Interface Variable ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 05 - A Useful Shader ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 06 - A Vector ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 07 - A Uniform Buffer - Part 1 ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 08 - A Uniform Buffer - Part 2 ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 09 - Composite Extraction and Construction ├── README.md ├── exercise.spvasm ├── solution1.spvasm ├── solution2.spvasm └── validate ├── 10 - Arithmetics ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 11 - If Else ├── README.md ├── exercise.spvasm ├── solution.spvasm └── validate ├── 12 - The Phi Function ├── README.md ├── exercise.spvasm ├── solution1.spvasm ├── solution2.spvasm ├── solution3.spvasm └── validate ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md └── scripts ├── id_to_names ├── names_to_id ├── setup ├── test_spirv └── validate /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .*.sw* 3 | .sw* 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/spirv-cross"] 2 | path = third_party/spirv-cross 3 | url = https://github.com/KhronosGroup/SPIRV-Cross 4 | -------------------------------------------------------------------------------- /01 - Introduction to SPIR-V/README.md: -------------------------------------------------------------------------------- 1 | # Introduction to SPIR-V 2 | 3 | SPIR-V is the language to describe shaders used with Vulkan. Normally, these shaders are written in 4 | a high-level language such as GLSL, but it may occasionally be necessary to look at, debug or modify 5 | the corresponding SPIR-V. These tutorials are aimed at introducing this language bit by bit and 6 | making it easier to read. 7 | 8 | The SPIR-V language is in SSA form, and is very much an Abstract Syntax Tree (with a heading). This 9 | means that every intermediate value is written to only once. Think of it as a language where every 10 | variable is `const`, much like how functional languages are. For the first few tutorials, we will 11 | be focusing on the heading. 12 | 13 | During all these tutorials, please have [the SPIR-V specification][SPIRV-spec] handy. 14 | 15 | [SPIRV-spec]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html 16 | 17 | ## A Minimal SPIR-V 18 | 19 | Let's take the boilerplate out of the way. Here's the minimum required to have a valid shader (that 20 | does nothing): 21 | 22 | ```swift 23 | OpCapability Shader 24 | OpMemoryModel Logical GLSL450 25 | OpEntryPoint Vertex %3 "main" 26 | %1 = OpTypeVoid 27 | %2 = OpTypeFunction %1 28 | %3 = OpFunction %1 None %2 29 | %4 = OpLabel 30 | OpReturn 31 | OpFunctionEnd 32 | ``` 33 | 34 | Let's go through this line by line: 35 | 36 | ```swift 37 | OpCapability Shader 38 | ``` 39 | 40 | It's a shader! There are other capabilities, that the SPIR-V could declare up front, like the fact 41 | that it may use 16-bit float instructions, or that it uses transform feedback. 42 | 43 | ```swift 44 | OpMemoryModel Logical GLSL450 45 | ``` 46 | 47 | Consider this boilerplate. It declares that the shader uses logical addresses (as opposed to 48 | physical) and that it uses the GLSL memory model. 49 | 50 | ```swift 51 | OpEntryPoint Vertex %3 "main" 52 | ``` 53 | 54 | This instruction (`OpEntryPoint`) declares an "entry point" in the shader module. In this case, the 55 | entry point is for a Vertex Shader, the function is identified by `%3` (more on this below), and the 56 | name is `"main"`. The name is used with `VkPipelineShaderStageCreateInfo` to identify this entry 57 | point. 58 | 59 | The SPIR-V may very well contain code for multiple shaders, possibly sharing some functions, and it 60 | can have multiple entry points. 61 | 62 | ```swift 63 | %1 = OpTypeVoid 64 | ``` 65 | 66 | SPIR-V uses "ids" to refer to everything that is declared, be it types, functions, variables, 67 | intermediate values etc. These ids are written as `%id` where `id` can be a number or a c-style 68 | variable name. 69 | 70 | This instruction is declaring the `void` type and giving it id `%1`. As you can observe, SPIR-V 71 | itself doesn't predefine any types. 72 | 73 | As an exercise, look up `OpTypeVoid` in the SPIR-V spec. Simply search for `OpTypeVoid` in the spec 74 | until you find a link to the instruction, or the table that defines the instruction itself. No need 75 | to bother with the binary representation of the instruction. 76 | 77 | ```swift 78 | %2 = OpTypeFunction %1 79 | ``` 80 | 81 | This instruction declares the function type `void (*)()` in C parlance. `%1` was just declared 82 | above as the `void` type, and that's the return type. The function type itself is stored in a new 83 | id `%2`. 84 | 85 | What if a function type needed parameters? Look up `OpTypeFunction` in the SPIR-V spec. 86 | 87 | ```swift 88 | %3 = OpFunction %1 None %2 89 | ``` 90 | 91 | Finally, declare the function itself. Observe the function type (`%2`). The return type (`%1`) is 92 | redundantly specified here. This is a common pattern in SPIR-V where the "result type" of every 93 | instruction is redundantly specified. 94 | 95 | What is `None`? Look up `OpFunction` in the spec, then click on "Function Control". 96 | 97 | 98 | ```swift 99 | %4 = OpLabel 100 | ``` 101 | 102 | A label, marking the beginning of a code block. Used for "jump" instructions. You can ignore this 103 | for now. 104 | 105 | ```swift 106 | OpReturn 107 | OpFunctionEnd 108 | ``` 109 | 110 | Self-explanatory instructions! 111 | 112 | ## Try it out 113 | 114 | The above SPIR-V can be found in `exercise.spvasm`. Try validating it (and generating the 115 | corresponding GLSL) by running: 116 | 117 | $ ./validate 118 | 119 | Neat! If you want to see the output colored by `spirv-dis`, try: 120 | 121 | $ ../scripts/names_to_id exercise.spvasm 122 | 123 | If you want to see (some of) the ids use "friendly names" instead of numbers, try: 124 | 125 | $ ../scripts/id_to_names exercise.spvasm 126 | -------------------------------------------------------------------------------- /01 - Introduction to SPIR-V/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | OpEntryPoint Vertex %3 "main" 4 | %1 = OpTypeVoid 5 | %2 = OpTypeFunction %1 6 | %3 = OpFunction %1 None %2 7 | %4 = OpLabel 8 | OpReturn 9 | OpFunctionEnd 10 | -------------------------------------------------------------------------------- /01 - Introduction to SPIR-V/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /02 - My First Change/README.md: -------------------------------------------------------------------------------- 1 | # My First Change 2 | 3 | In this exercise, we'll make superfluous modifications to the SPIR-V provided in the previous 4 | tutorial as warm up. 5 | 6 | ## Comments 7 | 8 | In SPIR-V, comments start with `;`. Try adding comments to `exercise.spvasm`. Validate the changes 9 | by calling: 10 | 11 | $ ./validate 12 | 13 | Can you comment on the same line as an instruction? 14 | 15 | ## Naming Ids 16 | 17 | In the previous tutorial, it was mentioned that `%` ids can be either numerical or c-style variable 18 | names. Try changing `exercise.spvasm` by replacing numerical ids with names. For example, a good 19 | substitute for `%3` can be `%main`, and a good substitute for `%1` can be `%void`. 20 | 21 | Now try introducing an intentional mistake. For example, change the definition of `%1` to: 22 | 23 | ```swift 24 | %void = OpTypeVoid 25 | ``` 26 | 27 | but keep `%1` elsewhere intact. What does `./validate` say? Now try changing `%2` as well: 28 | 29 | ```swift 30 | OpCapability Shader 31 | OpMemoryModel Logical GLSL450 32 | OpEntryPoint Vertex %3 "main" 33 | %void = OpTypeVoid 34 | %main_type = OpTypeFunction %1 35 | %3 = OpFunction %1 None %2 36 | %4 = OpLabel 37 | OpReturn 38 | OpFunctionEnd 39 | ``` 40 | 41 | What does `validate` say? When it outputs the `OpTypeFunction` line, it uses `%6` instead of 42 | `%main_type`. This is because once assembled, the name of the ids are dropped, and only their 43 | numerical value remains. 44 | 45 | To see the SPIR-V after named ids are resolved, you can assemble and disassemble the SPIR-V. The 46 | `scripts/names_to_id` script does this: 47 | 48 | $ ../scripts/names_to_id exercise.spvasm 49 | 50 | ## Renaming "main" 51 | 52 | Revert `exercise.spvasm` back to its original state. Here's how it looked like, in case this is not 53 | a `git` checkout: 54 | 55 | ```swift 56 | OpCapability Shader 57 | OpMemoryModel Logical GLSL450 58 | OpEntryPoint Vertex %3 "main" 59 | %1 = OpTypeVoid 60 | %2 = OpTypeFunction %1 61 | %3 = OpFunction %1 None %2 62 | %4 = OpLabel 63 | OpReturn 64 | OpFunctionEnd 65 | ``` 66 | 67 | Now try renaming "main" to something else. That would be by changing the string in the line that 68 | starts with `OpEntryPoint`. What does `./validate` generate as GLSL after modification? What if 69 | you changed the string to something like "1 2 3 4"? 70 | 71 | As mentioned in the previous tutorial, the name given to `OpEntryPoint` is used with 72 | `VkPipelineShaderStageCreateInfo` for the sole purpose of referencing the function identified by 73 | `%3`, which is why any string (even those not valid as a GLSL variable name) are acceptable. 74 | 75 | How can `main` be renamed then? SPIR-V can include debug instructions, one of which assigns a 76 | "name" to an id. Look up `OpName` in the SPIR-V spec. Try adding the following instruction right 77 | after the `OpEntryPoint` line: 78 | 79 | ```swift 80 | OpName %3 "new_name_for_main" 81 | ``` 82 | 83 | Behold the GLSL that is generated by `./validate`, and voila, it's still `main`. We did technically 84 | achieve the goal of renaming `main`, but why does SPIR-V Cross (the cross-compiler that turns SPIR-V 85 | to GLSL, used by `./validate`) still output `main`? Because the goal is to have the generated GLSL 86 | be usable, and if the entry point is not called `main`, that GLSL is not usable! In other words, 87 | SPIR-V is more expressive than GLSL (at least in this aspect). 88 | 89 | In the next exercise, we will make modifications to SPIR-V that would actually change the generated 90 | GLSL. 91 | -------------------------------------------------------------------------------- /02 - My First Change/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | OpEntryPoint Vertex %3 "main" 4 | %1 = OpTypeVoid 5 | %2 = OpTypeFunction %1 6 | %3 = OpFunction %1 None %2 7 | %4 = OpLabel 8 | OpReturn 9 | OpFunctionEnd 10 | -------------------------------------------------------------------------------- /02 - My First Change/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /03 - My First Variable/README.md: -------------------------------------------------------------------------------- 1 | # My First Variable 2 | 3 | In this exercise, we will modify the SPIR-V by adding "variables". This will have a visible effect 4 | on the generated GLSL. 5 | 6 | As a reminder, please have [the SPIR-V specification][SPIRV-spec] handy. 7 | 8 | [SPIRV-spec]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html 9 | 10 | ## A Global Variable 11 | 12 | First, let's start with a simple global variable. If you recall from the first exercise, SPIR-V 13 | doesn't predefine any types, so we need to also define a type for this global variable. Let's say 14 | we want to have the following GLSL generated: 15 | 16 | ```c 17 | float var; 18 | ``` 19 | 20 | We first need to define the float type itself. 21 | 22 | ### The Float Type 23 | 24 | Find the definition of `OpTypeFloat` in the SPIR-V spec. 25 | 26 | > Waiting for you to consult the spec ... 27 | 28 | The table for `OpTypeFloat` says: `3 | 22 | Result | Literal Width`. 29 | 30 | - What are `3` and `22`? Those are the length and opcode of the instruction. See section _2.3. 31 | Physical Layout of a SPIR-V Module and Instruction_, _Table 2. Instruction Physical Layout_ if you 32 | are interested, but you can generally ignore those during these tutorials. 33 | - When a field mentions ``, it means that an id is expected (rather than some literal). 34 | Likewise, literals are marked with `Literal`. You can click on these for their definitions! 35 | - `Result ` is what gets written before `=` in the human-readable SPIR-V. Note that this may 36 | not always be the first parameter. If there is a `Result Type `, that always comes first in 37 | the instruction layout. 38 | 39 | Throughout these tutorials, you will encounter many instructions. Translating from the SPIR-V spec 40 | to human-readable SPIR-V is as such: 41 | 42 | #### No Result 43 | 44 | ``` 45 | length | opcode | Param1 | ... -> OpX Param1 ... 46 | ``` 47 | 48 | #### Result, No Type 49 | 50 | ``` 51 | length | opcode | Result | Param1 | ... -> %id = OpX Param1 ... 52 | ``` 53 | 54 | #### Result and Type 55 | 56 | ``` 57 | length | opcode | Result Type | Result | Param1 | ... -> %id = OpX %tid Param1 ... 58 | ``` 59 | 60 | Back to `OpTypeFloat`, the human-readable form of the instruction is: 61 | 62 | ```swift 63 | %float = OpTypeFloat 32 ; or %5 instead of %float, 5 being a new id 64 | ``` 65 | 66 | Place this right after (or right before) the `OpTypeVoid` line in `exercise.spvasm`. Notes: 67 | 68 | - You can consult `solution.spvasm` if you get stuck, though it's recommended that you learn through 69 | experimentation. 70 | - The placement of the instruction matters. SPIR-V requires an ordering of instructions, grouped in 71 | sections. You can read about this in section _2.3. Physical Layout of a SPIR-V Module and 72 | Instruction_. 73 | - You can always use `./validate` to verify your code. 74 | 75 | Note: Basic types in SPIR-V must have unique ids, as different ids indicate different types. There 76 | cannot be two `OpTypeFloat 32` instructions for example. 77 | 78 | ### The Type Pointer 79 | 80 | Basic types are useful in SPIR-V to indicate the type of intermediate results. Variables are not of 81 | a type, but rather are a "pointer" to a type. A type pointer carries an extra piece of information 82 | aside from the type it is pointing to; a _Storage Class_. Look up `OpTypePointer` in the SPIR-V 83 | spec. 84 | 85 | > Waiting for you to consult the spec ... 86 | 87 | Click on _Storage Class_ to see what values it can take. For a global variable, we want the 88 | `Private` storage class. Hopefully, by now you should be able to figure out how to write the 89 | instruction in human-readable SPIR-V. Place this after the `OpTypeFloat` instruction: 90 | 91 | ```swift 92 | %floatptr = OpTypePointer Private %float ; again, feel free to use numerical ids 93 | ``` 94 | 95 | We will see more of type pointers in future tutorials. 96 | 97 | ### The Variable 98 | 99 | We are now ready to define a variable. Look for `OpVariable` in the SPIR-V spec. 100 | 101 | > Waiting for you to consult the spec ... 102 | 103 | This instruction has an optional argument (and thus a variable length). We won't specify this 104 | argument in this tutorial. You should be able to write the instruction on your own now. Place it 105 | right after the `OpTypePointer` instruction. 106 | 107 | Note: Consult `solution.spvasm` if you get stuck, but do so as a last resort. You will learn much 108 | better by try and error. 109 | 110 | ### Better GLSL 111 | 112 | Did you remember to use `./validate` along the way to verify your code? If not, now is the time to 113 | do so. Nice work declaring a variable. Is the generated GLSL not fantastic? No, the variable name 114 | is basically the id, like `_7`, that's not nice. Let's use `OpName` as seen in the previous 115 | tutorial to give this variable a readable name. 116 | 117 | Debug instructions are placed right after the "boilerplate" header, in this case `OpEntryPoint`. 118 | 119 | ```swift 120 | OpName %variable "fancy_variable" 121 | ``` 122 | 123 | Try `./validate` again and enjoy this little victory. 124 | -------------------------------------------------------------------------------- /03 - My First Variable/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | OpEntryPoint Vertex %3 "main" 4 | %1 = OpTypeVoid 5 | %2 = OpTypeFunction %1 6 | %3 = OpFunction %1 None %2 7 | %4 = OpLabel 8 | OpReturn 9 | OpFunctionEnd 10 | -------------------------------------------------------------------------------- /03 - My First Variable/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | OpEntryPoint Vertex %3 "main" 4 | 5 | ; Give the variable a name for more readable GLSL, better debuggability 6 | OpName %7 "fancy_variable" 7 | 8 | ; Declare the float type 9 | %5 = OpTypeFloat 32 10 | 11 | ; Declare a global variable of float type 12 | %6 = OpTypePointer Private %5 13 | %7 = OpVariable %6 Private 14 | 15 | %1 = OpTypeVoid 16 | %2 = OpTypeFunction %1 17 | %3 = OpFunction %1 None %2 18 | %4 = OpLabel 19 | OpReturn 20 | OpFunctionEnd 21 | -------------------------------------------------------------------------------- /03 - My First Variable/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /04 - An Interface Variable/README.md: -------------------------------------------------------------------------------- 1 | # An Interface Variable 2 | 3 | Building on the previous exercise, we will now try to change the variable type from global to input. 4 | `exercise.spvasm` contains a vertex shader, so this variable would be a vertex attribute. 5 | 6 | As a reminder, please have [the SPIR-V specification][SPIRV-spec] handy. 7 | 8 | [SPIRV-spec]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html 9 | 10 | ## The Storage Class 11 | 12 | Assuming the variable still has the `float` type, we can keep the `OpTypeFloat` instruction as is. 13 | We used the `Private` storage class to declare a global variable. We need to change that in both 14 | the `OpTypePointer` and `OpVariable` instructions. 15 | 16 | Take a look at `OpTypePointer` in the spec and click on _Storage Class_ (or just search for "storage 17 | class"). Can you spot the storage class necessary to declare an input variable? Great, let's use 18 | that instead of `Private`. 19 | 20 | Add this to the SPIR-V too, right after `OpName`: 21 | 22 | ```elisp 23 | OpDecorate %7 Location 0 24 | ``` 25 | 26 | We'll get to `OpDecorate` in the next exercise. 27 | 28 | How is the GLSL (through `./validate`) looking now? 29 | 30 | ## The Entry Point 31 | 32 | The GLSL generated from the SPIR-V looks good now actually. However, there's one modification 33 | missing from the SPIR-V to actually make this input variable usable. Recall that a SPIR-V file may 34 | include multiple entry points (because multiple shaders could be bundled in one SPIR-V, possibly 35 | sharing code). Because of that, an input variable is not usable in the shader just by the virtue of 36 | it being declared. 37 | 38 | You can verify this by placing this instruction inside the function (right after the `OpLabel` 39 | instruction): 40 | 41 | ```swift 42 | %8 = OpLoad %5 %7 43 | ``` 44 | 45 | You can probably guess what this instruction does; it's reading the attribute. Is `./validate` 46 | happy now? 47 | 48 | Note: `spirv-val` (the program used by `./validate` to validate the generated SPIR-V) is outputting 49 | the error message partially specifying ids numerically, and partially by their autogenerated 50 | "friendly names". Since your source code is definitely using either one or the other, this is bound 51 | to be confusing. Try the following scripts to see your SPIR-V with numerical ids and friendly 52 | names: 53 | 54 | $ ../scripts/id_to_names exercise.spvasm 55 | $ ../scripts/names_to_id exercise.spvasm 56 | 57 | Back to the validation error, it's saying that the interface variable is used by the entry point (we 58 | loaded from it in `main`), but it's not listed as an interface. Look up `OpEntryPoint` in the 59 | SPIR-V spec. What do you see about interfaces? 60 | 61 | > Waiting for you to consult the spec ... 62 | 63 | This instruction takes a list of interface variables that this entry point accesses. Add the 64 | id of the variable to this instruction and `./validate` should work again: 65 | 66 | ```swift 67 | OpEntryPoint Vertex %3 "main" %7 68 | ``` 69 | 70 | Why does the load from this variable not show in the generated GLSL? SPIR-V Cross is helpfully 71 | dropping this as the load is pointless; the results are not used. 72 | -------------------------------------------------------------------------------- /04 - An Interface Variable/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | OpEntryPoint Vertex %3 "main" 4 | 5 | OpName %7 "fancy_variable" 6 | 7 | ; float fancy_variable; 8 | %5 = OpTypeFloat 32 9 | %6 = OpTypePointer Private %5 10 | %7 = OpVariable %6 Private 11 | 12 | %1 = OpTypeVoid 13 | %2 = OpTypeFunction %1 14 | %3 = OpFunction %1 None %2 15 | %4 = OpLabel 16 | OpReturn 17 | OpFunctionEnd 18 | -------------------------------------------------------------------------------- /04 - An Interface Variable/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | ; The shader uses fancy_attribute 5 | OpEntryPoint Vertex %3 "main" %7 6 | 7 | OpName %7 "fancy_attribute" 8 | 9 | OpDecorate %7 Location 0 10 | 11 | ; in float fancy_attribute; 12 | %5 = OpTypeFloat 32 13 | %6 = OpTypePointer Input %5 14 | %7 = OpVariable %6 Input 15 | 16 | %1 = OpTypeVoid 17 | %2 = OpTypeFunction %1 18 | %3 = OpFunction %1 None %2 19 | %4 = OpLabel 20 | 21 | ; Test that loading from this attribute works 22 | %8 = OpLoad %5 %7 23 | 24 | OpReturn 25 | OpFunctionEnd 26 | -------------------------------------------------------------------------------- /04 - An Interface Variable/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /05 - A Useful Shader/README.md: -------------------------------------------------------------------------------- 1 | # A Useful Shader 2 | 3 | Let's take the exercise in the previous tutorial one step further and create a shader that does 4 | something useful (albeit mundane). 5 | 6 | ## An Output Variable 7 | 8 | In the previous exercise, we added an attribute to the vertex shader in `exercise.spvasm`. In this 9 | exercise, we will add an output of the same type: 10 | 11 | ```c 12 | in float fancy_attribute; // What we had 13 | out float useful_output; // What we are adding 14 | ``` 15 | 16 | Recall that basic types must be unique in SPIR-V, so the `OpTypeFloat 32` instruction shouldn't be 17 | duplicated. However, we need a new `OpTypePointer` and `OpVariable` for this variable. We need a 18 | new `OpTypePointer` because the storage class is different. We need a new `OpVariable` because well 19 | of course we are adding a new variable! 20 | 21 | > Waiting for you to complete this part of the exercise ... 22 | 23 | Is the output of `./validate` satisfactory? Did you forget something? 24 | 25 | ## Load and Store 26 | 27 | We briefly saw `OpLoad` in the previous exercise. Now is the time to look at it, along with 28 | `OpStore` in more depth. First, look these two up in the spec. 29 | 30 | > Waiting for you to consult the spec ... 31 | 32 | Let's ignore the optional _Memory Operands_ parameter, though you could click on it if you are 33 | curious. 34 | 35 | `OpLoad` is one of many instructions you will encounter that redundantly specify the _Result Type_. 36 | In this case, it must match the type pointed to by the _Pointer_ being loaded. In fact, you have 37 | already seen such an instruction before; `OpFunction`. Maybe take a look at `OpFunction` in the 38 | spec again, now that you know SPIR-V better. 39 | 40 | So we have two variables in our SPIR-V at this point (defined with `OpVariable`), and they are both 41 | pointers to the `float` type (one `Input`, one `Output`). You should now be able to implement the 42 | following by adding an `OpLoad` instruction (using a new id as result) followed by an `OpStore` 43 | instruction, inserted right after the `OpLabel` instruction (i.e. inside our `main` function): 44 | 45 | ```c 46 | useful_output = fancy_attribute; 47 | ``` 48 | 49 | Is `./validate` happy? If you forgot something in the previous section (but there were no errors), 50 | you should see an error message now. Where did we see a similar one before? 51 | 52 | > (If error:) Waiting for you to figure it out ... 53 | 54 | See `solution.spvasm` if you are not successful. 55 | 56 | ## Locations 57 | 58 | If you have ever written shader code for Vulkan, you might have noticed something missing in the 59 | GLSL output from `./validate`. Unlike OpenGL, where the driver assigns _locations_ to attributes 60 | and varyings (shader interface variables), Vulkan requires that they be "qualified" in the shader 61 | source (well, SPIR-V in the end). In GLSL, this is written as: 62 | 63 | ```c 64 | layout(location=4) in float fancy_attribute; 65 | layout(location=2) out float useful_output; 66 | ``` 67 | 68 | In SPIR-V, the location "decoration" (SPIR-V parlance for GLSL's "qualification") is specified with 69 | `OpDecorate`. 70 | 71 | > Waiting for you to consult the spec ... 72 | 73 | Click on _Decoration_ to see what decorations there are. Unsurprisingly, the location decoration is 74 | called `Location`. As `OpDecorate` doesn't produce a result, it looks like this in human-readable 75 | SPIR-V: 76 | 77 | ```swift 78 | OpDecorate %variable Location Value 79 | ``` 80 | 81 | Try adding a `Location` decoration for each variable in the shader (`fancy_attribute` and 82 | `useful_output`). These instructions must go after the debug instructions (e.g. `OpName`) but 83 | before type/variable declarations (e.g. `OpFloatType`, `OpVariable` etc). 84 | 85 | How does the generated GLSL (again, through `./validate`) look like? 86 | -------------------------------------------------------------------------------- /05 - A Useful Shader/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | OpEntryPoint Vertex %3 "main" %7 4 | 5 | OpName %7 "fancy_attribute" 6 | 7 | ; the `float` type 8 | %5 = OpTypeFloat 32 9 | 10 | ; in float fancy_attribute; 11 | %6 = OpTypePointer Input %5 12 | %7 = OpVariable %6 Input 13 | 14 | %1 = OpTypeVoid 15 | %2 = OpTypeFunction %1 16 | %3 = OpFunction %1 None %2 17 | %4 = OpLabel 18 | 19 | OpReturn 20 | OpFunctionEnd 21 | -------------------------------------------------------------------------------- /05 - A Useful Shader/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | ; The shader uses both fancy_attribute and useful_output 5 | OpEntryPoint Vertex %3 "main" %7 %9 6 | 7 | OpName %7 "fancy_attribute" 8 | OpName %9 "useful_output" 9 | 10 | ; Assign locations to the interface variables 11 | OpDecorate %7 Location 4 12 | OpDecorate %9 Location 2 13 | 14 | ; the `float` type 15 | %5 = OpTypeFloat 32 16 | 17 | ; in float fancy_attribute; 18 | %6 = OpTypePointer Input %5 19 | %7 = OpVariable %6 Input 20 | 21 | ; out float useful_output; 22 | %8 = OpTypePointer Output %5 23 | %9 = OpVariable %8 Output 24 | 25 | %1 = OpTypeVoid 26 | %2 = OpTypeFunction %1 27 | %3 = OpFunction %1 None %2 28 | %4 = OpLabel 29 | 30 | ; useful_output = fancy_attribute; 31 | %10 = OpLoad %5 %7 32 | OpStore %9 %10 33 | 34 | OpReturn 35 | OpFunctionEnd 36 | -------------------------------------------------------------------------------- /05 - A Useful Shader/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /06 - A Vector/README.md: -------------------------------------------------------------------------------- 1 | # A Vector 2 | 3 | In this exercise, we will create a new type (`vec4`) out of an existing type (`float`) and learn 4 | about constants. 5 | 6 | ## The Vector Operator 7 | 8 | First, we are going to add a new output variable to our shader. 9 | 10 | ```c 11 | layout(location = 1) out vec4 crucial_data; 12 | ``` 13 | 14 | We already know how to do this, except for the `vec4` type. That is where `OpTypeVector` comes in. 15 | This instruction takes a basic type, for example one defined with `OpTypeFloat 32` and turns it into 16 | a vector. Look it up in the SPIR-V specification. 17 | 18 | > Waiting for you to consult the spec ... 19 | 20 | I suggest placing this instruction (giving a new id as its result) right after the `OpTypeFloat` 21 | instruction. Once done, please go ahead and add the above declaration in SPIR-V. This is going to 22 | be very similar to how `useful_output` was declared. 23 | 24 | > Waiting for you to complete this part of the exercise ... 25 | 26 | Need a hint? Recall from the previous exercise that declaring a new variable requires: 27 | 28 | - Declaring a type, if not already available 29 | - Declaring a type pointer (`OpTypePointer`), if not already available 30 | - Declaring a variable (`OpVariable`) 31 | - Specifying the variable id in `OpEntryPoint` 32 | - Adding a `Location` decoration to the variable (`OpDecorate`) 33 | 34 | You should be able to see the above declaration in the output of `./validate` now. 35 | 36 | ## Constants 37 | 38 | Next, let's give this `crucial_data` some crucial data! How about a dash of blue? Let's add the 39 | (equivalent of the) following line to `main`: 40 | 41 | ```c 42 | crucial_data = vec4(0.25, 0.25, 0.75, 1.0); 43 | ``` 44 | 45 | The assignment part is familiar, `OpStore` will take care of that. But `OpStore` takes an id to 46 | store into `crucial_data`, so we must declare that constant vector somewhere and give it some id. 47 | There are two pieces to this puzzle. First, each element of the vector is a constant itself; 48 | `0.25`, `0.75` and `1.0`. Then the vector is a composite of those constants. 49 | 50 | Use `OpConstant` to declare the elements of the above vector. This should be placed in the 51 | declarations section of the SPIR-V, i.e. after the decorations (with `OpDecorate`) and before the 52 | function declarations (with `OpFunction`). Naturally, they should necessarily be placed after the 53 | `float` type is declared! 54 | 55 | Next, use `OpConstantComposite` to use the above constants and create a composite `vec4` constant 56 | out of them. Same placement restriction applies as `OpConstant`. 57 | 58 | Finally, add an `OpStore` instruction inside `main` (right after the other `OpStore` for example) to 59 | assign the new constant to the new variable we introduced in this exercise. 60 | 61 | As always, validate your code with `./validate`. 62 | 63 | > Waiting for you to complete this part of the exercise ... 64 | -------------------------------------------------------------------------------- /06 - A Vector/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %7 %9 5 | 6 | OpName %7 "fancy_attribute" 7 | OpName %9 "useful_output" 8 | 9 | OpDecorate %7 Location 0 10 | OpDecorate %9 Location 0 11 | 12 | ; the `float` type 13 | %5 = OpTypeFloat 32 14 | 15 | ; in float fancy_attribute; 16 | %6 = OpTypePointer Input %5 17 | %7 = OpVariable %6 Input 18 | 19 | ; out float useful_output; 20 | %8 = OpTypePointer Output %5 21 | %9 = OpVariable %8 Output 22 | 23 | %1 = OpTypeVoid 24 | %2 = OpTypeFunction %1 25 | %3 = OpFunction %1 None %2 26 | %4 = OpLabel 27 | 28 | ; useful_output = fancy_attribute; 29 | %10 = OpLoad %5 %7 30 | OpStore %9 %10 31 | 32 | OpReturn 33 | OpFunctionEnd 34 | -------------------------------------------------------------------------------- /06 - A Vector/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | ; The shader uses fancy_attribute, useful_output and crucial_data 5 | OpEntryPoint Vertex %3 "main" %7 %9 %13 6 | 7 | OpName %7 "fancy_attribute" 8 | OpName %9 "useful_output" 9 | OpName %13 "crucial_data" 10 | 11 | OpDecorate %7 Location 0 12 | OpDecorate %9 Location 0 13 | OpDecorate %13 Location 1 14 | 15 | ; the `float` type 16 | %5 = OpTypeFloat 32 17 | 18 | ; the `vec4` type 19 | %11 = OpTypeVector %5 4 20 | 21 | ; in float fancy_attribute; 22 | %6 = OpTypePointer Input %5 23 | %7 = OpVariable %6 Input 24 | 25 | ; out float useful_output; 26 | %8 = OpTypePointer Output %5 27 | %9 = OpVariable %8 Output 28 | 29 | ; out vec4 crucial_data; 30 | %12 = OpTypePointer Output %11 31 | %13 = OpVariable %12 Output 32 | 33 | ; Constants 0.25, 0.75 and 1.0 34 | %14 = OpConstant %5 0.25 35 | %15 = OpConstant %5 0.75 36 | %16 = OpConstant %5 1 37 | 38 | ; Constant vec4(0.25, 0.25, 0.75, 1.0) 39 | %17 = OpConstantComposite %11 %14 %14 %15 %16 40 | 41 | %1 = OpTypeVoid 42 | %2 = OpTypeFunction %1 43 | %3 = OpFunction %1 None %2 44 | %4 = OpLabel 45 | 46 | ; useful_output = fancy_attribute; 47 | %10 = OpLoad %5 %7 48 | OpStore %9 %10 49 | 50 | ; crucial_data = vec4(0.25, 0.25, 0.75, 1.0); 51 | OpStore %13 %17 52 | 53 | OpReturn 54 | OpFunctionEnd 55 | -------------------------------------------------------------------------------- /06 - A Vector/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /07 - A Uniform Buffer - Part 1/README.md: -------------------------------------------------------------------------------- 1 | # A Uniform Buffer (Part 1) 2 | 3 | Let's be honest, `vec4(0.25, 0.25, 0.75, 1.0)` in the previous exercise did not look like crucial 4 | data to pass to the fragment shader. Let's ditch that in this exercise, and build towards a uniform 5 | buffer. We will dedicate this exercise to just declaring one, and have the next exercise use it. 6 | 7 | ## Declaring a Structure 8 | 9 | In SPIR-V, uniform buffers, storage buffers and I/O blocks are just structures that are decorated 10 | especially. 11 | 12 | Let's first then just declare a structure: 13 | 14 | ```c 15 | struct object_properties 16 | { 17 | vec4 shininess; 18 | vec4 smoothness; 19 | }; 20 | ``` 21 | 22 | We already have the `vec4` type handy, marvelous. Now we just need to create a new struct type that 23 | holds two `vec4`s. Uninspiringly (and that's a good thing), the necessary instruction is called 24 | `OpTypeStruct`. Look it up in the spec, and add the type declaration to the SPIR-V. I recommend 25 | placing it right after the `vec4` type declaration. 26 | 27 | As always, verify your code with `./validate`. 28 | 29 | > Waiting for you to complete this part of the exercise ... 30 | 31 | You can probably guess how to set the debug name `object_properties` for the struct. But, how would 32 | you set debug names to its members? That's `OpMemberName`, do give it a brief look in the spec. 33 | For example: 34 | 35 | ```swift 36 | OpMemberName %struct_type 1 "smoothness" 37 | ``` 38 | 39 | ## Turning the Struct Into a Uniform Buffer 40 | 41 | Declaring the struct was easy, but we really want a uniform buffer. What's importantly different 42 | between the two is that the uniform buffer has a memory backing, so the SPIR-V should know exactly 43 | where to find each member of the uniform buffer. If you have heard of `std140` and `std430`, you 44 | know what I'm talking about. If not, you really should look that up! 45 | 46 | So we need a host of new decorations: 47 | 48 | - Use `OpDecorate` with the `Block` decoration to indicate that the struct type is special 49 | - Use `OpMemberDecorate` with the `Offset` decoration to lay each member out in memory by giving 50 | them byte offsets. I suggest offset 0 for the first member and offset 16 for the second. In case 51 | it's not obvious, you should place `OpMemberDecorate` instructions near where `OpDecorate` 52 | instructions are. 53 | 54 | We also need a new variable of this type, otherwise the declaration is pointless. Declare a new 55 | variable, much like `useful_output` and `crucial_data`, except they are of this struct type **and** 56 | their _Storage Class_ is `Uniform` (instead of `Output` or `Input` as we previously used). 57 | 58 | Give that a whirl with `./validate`. 59 | 60 | > Waiting for you to complete this part of the exercise ... 61 | 62 | If you are stuck, you can take a peek at `solution.spvasm`, but don't cheat or you think you are 63 | learning but you are not. 64 | 65 | > Waiting for you to really complete this part of the exercise ... 66 | 67 | Let's experiment a bit too. What happens if you set both offsets (in `OpMemberDecorate` 68 | instructions) to 0? What error does `spirv-val` (the command used to validate the SPIR-V in 69 | `exercise.spvasm`, called from `./validate`) generate? What if you set the first offset to 0 and 70 | the second to 10? What about 0 and 32? 71 | 72 | ## Set and Binding Qualifications 73 | 74 | Just like interface variables require a `location` qualification (GLSL's term for SPIR-V's 75 | decorations) for the shader to be usable with Vulkan, so do uniform buffers require `set` and 76 | `binding` qualifications. If you don't know what descriptor sets and bindings are, look them up in 77 | the Vulkan spec. 78 | 79 | How about we make the uniform buffer look like this? 80 | 81 | ```c 82 | layout(set = 2, binding = 3) uniform object_properties 83 | { 84 | vec4 shininess; 85 | vec4 smoothness; 86 | } props; 87 | ``` 88 | 89 | As you might have guessed since "decorations" were mentioned, these are set with an `OpDecorate` 90 | instruction. For GLSL's `set`, use the `DescriptorSet` decoration and for `binding`, use `Binding`. 91 | Otherwise the instructions look pretty similar to the ones we already have for `Location`. Also, 92 | remember that it's the variable that's being decorated here not the type! 93 | 94 | Congratulations, `./validate` should now show this decoration. 95 | 96 | Did you happen to encounter a validation error with `OpEntryPoint`? Before SPIR-V 1.4 (we are using 97 | SPIR-V 1.0 here) only the `Input` and `Output` variables need to be declared in `OpEntryPoint`, so 98 | the uniform buffer variable shouldn't be placed there! 99 | 100 | > Waiting for you to complete this part of the exercise ... 101 | -------------------------------------------------------------------------------- /07 - A Uniform Buffer - Part 1/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %8 %10 %12 5 | 6 | OpName %8 "fancy_attribute" 7 | OpName %10 "useful_output" 8 | OpName %12 "crucial_data" 9 | 10 | OpDecorate %8 Location 0 11 | OpDecorate %10 Location 0 12 | OpDecorate %12 Location 1 13 | 14 | ; the `float` type 15 | %5 = OpTypeFloat 32 16 | 17 | ; the `vec4` type 18 | %6 = OpTypeVector %5 4 19 | 20 | ; in float fancy_attribute; 21 | %7 = OpTypePointer Input %5 22 | %8 = OpVariable %7 Input 23 | 24 | ; out float useful_output; 25 | %9 = OpTypePointer Output %5 26 | %10 = OpVariable %9 Output 27 | 28 | ; out vec4 crucial_data; 29 | %11 = OpTypePointer Output %6 30 | %12 = OpVariable %11 Output 31 | 32 | %1 = OpTypeVoid 33 | %2 = OpTypeFunction %1 34 | %3 = OpFunction %1 None %2 35 | %4 = OpLabel 36 | 37 | ; useful_output = fancy_attribute; 38 | %13 = OpLoad %5 %8 39 | OpStore %10 %13 40 | 41 | OpReturn 42 | OpFunctionEnd 43 | -------------------------------------------------------------------------------- /07 - A Uniform Buffer - Part 1/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %8 %10 %12 5 | 6 | OpName %8 "fancy_attribute" 7 | OpName %10 "useful_output" 8 | OpName %12 "crucial_data" 9 | OpName %14 "object_properties" 10 | OpMemberName %14 0 "shininess" 11 | OpMemberName %14 1 "smoothness" 12 | OpName %16 "props" 13 | 14 | OpDecorate %8 Location 0 15 | OpDecorate %10 Location 0 16 | OpDecorate %12 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %14 Block 20 | OpMemberDecorate %14 0 Offset 0 21 | OpMemberDecorate %14 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %16 DescriptorSet 2 25 | OpDecorate %16 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{vec4, vec4}` struct type 34 | %14 = OpTypeStruct %6 %6 35 | 36 | ; in float fancy_attribute; 37 | %7 = OpTypePointer Input %5 38 | %8 = OpVariable %7 Input 39 | 40 | ; out float useful_output; 41 | %9 = OpTypePointer Output %5 42 | %10 = OpVariable %9 Output 43 | 44 | ; out vec4 crucial_data; 45 | %11 = OpTypePointer Output %6 46 | %12 = OpVariable %11 Output 47 | 48 | ; uniform object_properties { ... } props; 49 | %15 = OpTypePointer Uniform %14 50 | %16 = OpVariable %15 Uniform 51 | 52 | %1 = OpTypeVoid 53 | %2 = OpTypeFunction %1 54 | %3 = OpFunction %1 None %2 55 | %4 = OpLabel 56 | 57 | ; useful_output = fancy_attribute; 58 | %13 = OpLoad %5 %8 59 | OpStore %10 %13 60 | 61 | OpReturn 62 | OpFunctionEnd 63 | -------------------------------------------------------------------------------- /07 - A Uniform Buffer - Part 1/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /08 - A Uniform Buffer - Part 2/README.md: -------------------------------------------------------------------------------- 1 | # A Uniform Buffer (Part 2) 2 | 3 | In the previous exercise, we declared a uniform buffer. That was rather useless in and of itself, 4 | so let's fix that. In this exercise, we will read from that uniform buffer and finally initialize 5 | `crucial_data` with data worthy of the name. 6 | 7 | If you have been impatient and going to `solution.spvasm` prematurely before, try to really do this 8 | one on your own. 9 | 10 | ## Type Pointers and Access Chains 11 | 12 | This is where we can finally understand the point of "type pointers" (no pun intended, though if you 13 | enjoyed it I'll let it slide this time). Recall that variables are declared (with `OpVariable`) 14 | as a type pointer (declared with `OpTypePointer`). Later, load and store (`OpLoad` and `OpStore` 15 | respectively) operate on these variables. Truth is, load and store operate on any pointers (of 16 | which variables are one example). 17 | 18 | But what other pointers are there? 19 | 20 | Let's take another point of view. Say you have the following: 21 | 22 | ```c 23 | struct points 24 | { 25 | vec4 point[10]; 26 | }; 27 | 28 | uniform properties 29 | { 30 | points geometry[3]; 31 | } props; 32 | ``` 33 | 34 | And you want to access: 35 | 36 | ```c 37 | props.geometry[2].point[5].y 38 | ``` 39 | 40 | One way of reading this would be to `OpLoad` the whole buffer, then use various instructions to get 41 | to the data we are looking for. If this was writable (because it's not a uniform buffer, but a 42 | storage buffer), that would get more tricky (read the whole thing, modify one part, then write it 43 | all back), or impossible (with variable sized arrays). 44 | 45 | SPIR-V instead has a nifty instruction called `OpAccessChain`. `OpAccessChain` takes a pointer, 46 | applies a number of indices in sequence and creates a new pointer. The extra nice part is that this 47 | "indexing" applies to all of: 48 | 49 | - Selecting a member in a struct, 50 | - Selecting an element in an array, 51 | - Selecting a component of a vector, and 52 | - Selecting a column of a matrix. 53 | 54 | In other words, the whole expression above requires a single `OpAccessChain` which takes `props` and 55 | produces `props.geometry[2].point[5].y`. In this case, the indices used are `0 2 0 5 1`, which in 56 | order are: 57 | 58 | - Member 0 of `props` (i.e. `.geometry`), then 59 | - Element 2 of that (i.e. `[2]`), then 60 | - Member 0 of that (i.e. `.point`), then 61 | - Element 5 of that (i.e. `[5]`), then 62 | - Component 1 of that (i.e. `.y`). 63 | 64 | > Waiting for you to review the above and make sure you completely understand it ... 65 | 66 | ## Accessing the Uniform Buffer 67 | 68 | Let's get to work then. We want to add the following line to `main`: 69 | 70 | ```c 71 | crucial_data = props.smoothness; 72 | ``` 73 | 74 | First, we need to declare a constant for the index (which selects the member `smoothness`). This is 75 | not a `float` constants as we have so far seen, but rather `int`. Zeroth, then, we should declare 76 | the `int` type. That is done with the `OpTypeInt` instruction. 77 | 78 | `OpTypeInt` takes a _Signedness_ parameter. Do we want to declare these constants as signed or 79 | unsigned integer? The truth is, it doesn't matter. _Signedness_ is largely unused (helps mostly 80 | with validation and checking), and the instructions themselves dictate whether an integer is treated 81 | as signed or unsigned. Nevertheless, let's go with signed; `OpAccessChain` says it treats these 82 | indices as signed. 83 | 84 | You have already learned how to declare constants, so declare one with a value of 1 (because 85 | `smoothness` is member 1 of the uniform buffer struct type). As a reminder, `OpConstant` is the 86 | instruction to use here. 87 | 88 | Next, we need to use the `OpAccessChain` instruction. This instruction goes in `main`, for example 89 | right after the `OpStore` instruction. Look it up now and see if we have everything we need for it. 90 | 91 | > Waiting for you to consult the spec ... 92 | 93 | What should we use as the _Result Type_? What is the type of `props.smoothness`? If you don't see 94 | a type pointer declaration (an `OpTypePointer` instruction that is) for this type, go ahead and add 95 | one! 96 | 97 | > Waiting for you to come up with the `OpAccessChain` instruction ... 98 | 99 | Now that we have a pointer to what we need to load, it's business as usual. Let's load (`OpLoad`) 100 | from this shiny new pointer and store (`OpStore`) it in the pointer for `crucial_data`. Again, 101 | these go in `main`, right after the `OpAccessChain` instruction. 102 | 103 | As always, use `./validate` to validate and see what GLSL you end up with. 104 | 105 | > Waiting for you to complete this part of the exercise ... 106 | 107 | Stuck? Don't give up! Why not reread this whole exercise? 108 | 109 | > Waiting for you to really try to complete this part of the exercise without looking at 110 | > `solution.spvasm` ... 111 | 112 | Good job! This was probably the second most exciting part of SPIR-V. By the way, now that our 113 | little SPIR-V is not so little anymore, you could see a little summary of what each exercise does by 114 | taking the `diff` of the two files: 115 | 116 | ```bash 117 | $ diff exercise.spvasm solution.spvasm 118 | ``` 119 | 120 | ## (Optional) The Extra 1.6Km 121 | 122 | If you like to experiment more with `OpAccessChain` and type declarations, feel free to experiment 123 | and construct more complicated types, for example with arrays (`OpTypeArray`), matrices 124 | (`OpTypeMatrix`) and nested structs (`OpTypeStruct`). 125 | -------------------------------------------------------------------------------- /08 - A Uniform Buffer - Part 2/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %9 %11 %13 5 | 6 | OpName %9 "fancy_attribute" 7 | OpName %11 "useful_output" 8 | OpName %13 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "shininess" 11 | OpMemberName %7 1 "smoothness" 12 | OpName %15 "props" 13 | 14 | OpDecorate %9 Location 0 15 | OpDecorate %11 Location 0 16 | OpDecorate %13 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %15 DescriptorSet 2 25 | OpDecorate %15 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{vec4, vec4}` struct type 34 | %7 = OpTypeStruct %6 %6 35 | 36 | ; in float fancy_attribute; 37 | %8 = OpTypePointer Input %5 38 | %9 = OpVariable %8 Input 39 | 40 | ; out float useful_output; 41 | %10 = OpTypePointer Output %5 42 | %11 = OpVariable %10 Output 43 | 44 | ; out vec4 crucial_data; 45 | %12 = OpTypePointer Output %6 46 | %13 = OpVariable %12 Output 47 | 48 | ; uniform object_properties { ... } props; 49 | %14 = OpTypePointer Uniform %7 50 | %15 = OpVariable %14 Uniform 51 | 52 | %1 = OpTypeVoid 53 | %2 = OpTypeFunction %1 54 | %3 = OpFunction %1 None %2 55 | %4 = OpLabel 56 | 57 | ; useful_output = fancy_attribute; 58 | %16 = OpLoad %5 %9 59 | OpStore %11 %16 60 | 61 | OpReturn 62 | OpFunctionEnd 63 | -------------------------------------------------------------------------------- /08 - A Uniform Buffer - Part 2/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %9 %11 %13 5 | 6 | OpName %9 "fancy_attribute" 7 | OpName %11 "useful_output" 8 | OpName %13 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "shininess" 11 | OpMemberName %7 1 "smoothness" 12 | OpName %15 "props" 13 | 14 | OpDecorate %9 Location 0 15 | OpDecorate %11 Location 0 16 | OpDecorate %13 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %15 DescriptorSet 2 25 | OpDecorate %15 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{vec4, vec4}` struct type 34 | %7 = OpTypeStruct %6 %6 35 | 36 | ; the `int` type 37 | %17 = OpTypeInt 32 1 38 | 39 | ; in float fancy_attribute; 40 | %8 = OpTypePointer Input %5 41 | %9 = OpVariable %8 Input 42 | 43 | ; out float useful_output; 44 | %10 = OpTypePointer Output %5 45 | %11 = OpVariable %10 Output 46 | 47 | ; out vec4 crucial_data; 48 | %12 = OpTypePointer Output %6 49 | %13 = OpVariable %12 Output 50 | 51 | ; uniform object_properties { ... } props; 52 | %14 = OpTypePointer Uniform %7 53 | %15 = OpVariable %14 Uniform 54 | 55 | ; Uniform pointer to `vec4` 56 | %18 = OpTypePointer Uniform %6 57 | 58 | ; The integer constant 1 59 | %19 = OpConstant %17 1 60 | 61 | %1 = OpTypeVoid 62 | %2 = OpTypeFunction %1 63 | %3 = OpFunction %1 None %2 64 | %4 = OpLabel 65 | 66 | ; useful_output = fancy_attribute; 67 | %16 = OpLoad %5 %9 68 | OpStore %11 %16 69 | 70 | ; crucial_data = props.smoothness; 71 | %20 = OpAccessChain %18 %15 %19 72 | %21 = OpLoad %6 %20 73 | OpStore %13 %21 74 | 75 | OpReturn 76 | OpFunctionEnd 77 | -------------------------------------------------------------------------------- /08 - A Uniform Buffer - Part 2/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /09 - Composite Extraction and Construction/README.md: -------------------------------------------------------------------------------- 1 | # Composite Extraction and Construction 2 | 3 | In this exercise, we'll learn how to extract components of a composite and stitch them together to 4 | make new ones. What is a composite? It's a piece of data that's more complex than a basic type 5 | (`int`, `float`); so structs, arrays, vectors and matrices and any combination of them are 6 | composites. 7 | 8 | In the previous exercise, we generated the following code: 9 | 10 | ```c 11 | crucial_data = props.smoothness; 12 | ``` 13 | 14 | We're going to change that here, and generate: 15 | 16 | ```c 17 | crucial_data = vec4(props.shininess.xy, props.smoothness.xy); 18 | ``` 19 | 20 | Which is really: 21 | 22 | ```c 23 | crucial_data = vec4(props.shininess.x, 24 | props.shininess.y, 25 | props.smoothness.x); 26 | props.smoothness.y); 27 | ``` 28 | 29 | There are two parts to this; extracting the `x` and `y` components out of the uniform values, and 30 | compositing them to make a new `vec4`. 31 | 32 | ## Extraction 33 | 34 | To extract a component out of a composite, you can use `OpCompositeExtract`. This instruction is 35 | somewhat similar to `OpAccessChain`, in that it takes a list of indices to drill down the composite 36 | and reach the component of interest. 37 | 38 | > Waiting for you to consult the spec ... 39 | 40 | Did you notice the difference between the _Indexes_ of `OpCompositeExtract` and `OpAccessChain`? 41 | 42 | > Waiting for you to check again ... 43 | 44 | That's right, `OpCompositeExtract` takes literals as indices instead of ids. That may seem 45 | insignificant at first, because so far we have only used constants (`OpConstant`) as indices. 46 | However, as you might have guessed by now, this means that you can use variables for example to 47 | index an array with `OpAccessChain`, but not with `OpCompositeExtract`. Take a look at the 48 | description of _Indexes_ in `OpAccessChain` to see what restrictions apply! If you are curious to 49 | know how `someVec4Var[i]` can be implemented, take a look at `OpVectorExtractDynamic`. 50 | 51 | Alright, we know the instruction now. Let's get to coding. First, modify `exercise.spvasm` to load 52 | (`OpLoad`) both `props.shininess` and `props.smoothness`. `props.smoothness` is already loaded, so 53 | loading the other is a matter of copy paste. Next, use `OpCompositeExtract` four times to extract 54 | the `x` and `y` components of both `props.shininess` and `props.smoothness`. In case it's not 55 | obvious, `x` is component 0 and `y` is component 1! You should have four ids now for these 56 | extracted components. 57 | 58 | > Waiting for you to complete this part of the exercise ... 59 | 60 | ## Construction 61 | 62 | We have four `float` values now, extracted from the uniform buffer members. We can construct a 63 | `vec4` out of these using the `OpCompositeConstruct` instruction. You should be pretty familiar by 64 | now with the syntax, so this should be a breeze once you consult the spec. 65 | 66 | Once the `vec4` is constructed, store (`OpStore`) it in `crucial_data` as usual. 67 | 68 | > Waiting for you to complete this part of the exercise ... 69 | 70 | The solution can be found in `solution1.spvasm`. There's an alternative instruction that can be 71 | used here, presented in `solution2.spvasm`. Read the next section if you are interested. 72 | 73 | ## (Optional) An Alternative; Vector Shuffling 74 | 75 | In the special case of a vector swizzle, or constructing one vector out of two other vectors where 76 | components of one come before the components of another (as in this exercise), there's another 77 | instruction that can be used to achieve the same results as all the instructions above. That 78 | instruction is `OpVectorShuffle`. This instruction takes two vectors, logically concatenates them 79 | to be a larger vector (of up to 8 components), then takes a list of literals as components to choose 80 | from this logical vector. This is _the_ way to implement swizzle (where both vectors are given the 81 | same), but can come in handy in situations like in this exercise. 82 | 83 | Try removing all the `OpCompositeExtract` and `OpCompositeConstruct` instructions and replacing them 84 | all with a single `OpVectorShuffle` instruction. `solution2.spvasm` has the answer. 85 | -------------------------------------------------------------------------------- /09 - Composite Extraction and Construction/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %10 %12 %14 5 | 6 | OpName %10 "fancy_attribute" 7 | OpName %12 "useful_output" 8 | OpName %14 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "shininess" 11 | OpMemberName %7 1 "smoothness" 12 | OpName %16 "props" 13 | 14 | OpDecorate %10 Location 0 15 | OpDecorate %12 Location 0 16 | OpDecorate %14 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %16 DescriptorSet 2 25 | OpDecorate %16 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{vec4, vec4}` struct type 34 | %7 = OpTypeStruct %6 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; in float fancy_attribute; 40 | %9 = OpTypePointer Input %5 41 | %10 = OpVariable %9 Input 42 | 43 | ; out float useful_output; 44 | %11 = OpTypePointer Output %5 45 | %12 = OpVariable %11 Output 46 | 47 | ; out vec4 crucial_data; 48 | %13 = OpTypePointer Output %6 49 | %14 = OpVariable %13 Output 50 | 51 | ; uniform object_properties { ... } props; 52 | %15 = OpTypePointer Uniform %7 53 | %16 = OpVariable %15 Uniform 54 | 55 | ; Uniform pointer to `vec4` 56 | %17 = OpTypePointer Uniform %6 57 | 58 | ; The integer constant 1 59 | %18 = OpConstant %8 1 60 | 61 | %1 = OpTypeVoid 62 | %2 = OpTypeFunction %1 63 | %3 = OpFunction %1 None %2 64 | %4 = OpLabel 65 | 66 | ; useful_output = fancy_attribute; 67 | %19 = OpLoad %5 %10 68 | OpStore %12 %19 69 | 70 | ; crucial_data = props.smoothness; 71 | %20 = OpAccessChain %17 %16 %18 72 | %21 = OpLoad %6 %20 73 | OpStore %14 %21 74 | 75 | OpReturn 76 | OpFunctionEnd 77 | -------------------------------------------------------------------------------- /09 - Composite Extraction and Construction/solution1.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %10 %12 %14 5 | 6 | OpName %10 "fancy_attribute" 7 | OpName %12 "useful_output" 8 | OpName %14 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "shininess" 11 | OpMemberName %7 1 "smoothness" 12 | OpName %16 "props" 13 | 14 | OpDecorate %10 Location 0 15 | OpDecorate %12 Location 0 16 | OpDecorate %14 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %16 DescriptorSet 2 25 | OpDecorate %16 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{vec4, vec4}` struct type 34 | %7 = OpTypeStruct %6 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; in float fancy_attribute; 40 | %9 = OpTypePointer Input %5 41 | %10 = OpVariable %9 Input 42 | 43 | ; out float useful_output; 44 | %11 = OpTypePointer Output %5 45 | %12 = OpVariable %11 Output 46 | 47 | ; out vec4 crucial_data; 48 | %13 = OpTypePointer Output %6 49 | %14 = OpVariable %13 Output 50 | 51 | ; uniform object_properties { ... } props; 52 | %15 = OpTypePointer Uniform %7 53 | %16 = OpVariable %15 Uniform 54 | 55 | ; Uniform pointer to `vec4` 56 | %17 = OpTypePointer Uniform %6 57 | 58 | ; The integer constants 0 and 1 59 | %22 = OpConstant %8 0 60 | %18 = OpConstant %8 1 61 | 62 | %1 = OpTypeVoid 63 | %2 = OpTypeFunction %1 64 | %3 = OpFunction %1 None %2 65 | %4 = OpLabel 66 | 67 | ; useful_output = fancy_attribute; 68 | %19 = OpLoad %5 %10 69 | OpStore %12 %19 70 | 71 | ; %21 = props.shininess 72 | %20 = OpAccessChain %17 %16 %22 73 | %21 = OpLoad %6 %20 74 | 75 | ; %24 = props.smoothness 76 | %23 = OpAccessChain %17 %16 %18 77 | %24 = OpLoad %6 %23 78 | 79 | ; crucial_data = vec4(props.shininess.x, 80 | ; props.shininess.y, 81 | ; props.smoothness.x, 82 | ; props.smoothness.y); 83 | %25 = OpCompositeExtract %5 %21 0 84 | %26 = OpCompositeExtract %5 %21 1 85 | %27 = OpCompositeExtract %5 %24 0 86 | %28 = OpCompositeExtract %5 %24 1 87 | %29 = OpCompositeConstruct %6 %25 %26 %27 %28 88 | OpStore %14 %29 89 | 90 | OpReturn 91 | OpFunctionEnd 92 | -------------------------------------------------------------------------------- /09 - Composite Extraction and Construction/solution2.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %10 %12 %14 5 | 6 | OpName %10 "fancy_attribute" 7 | OpName %12 "useful_output" 8 | OpName %14 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "shininess" 11 | OpMemberName %7 1 "smoothness" 12 | OpName %16 "props" 13 | 14 | OpDecorate %10 Location 0 15 | OpDecorate %12 Location 0 16 | OpDecorate %14 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %16 DescriptorSet 2 25 | OpDecorate %16 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{vec4, vec4}` struct type 34 | %7 = OpTypeStruct %6 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; in float fancy_attribute; 40 | %9 = OpTypePointer Input %5 41 | %10 = OpVariable %9 Input 42 | 43 | ; out float useful_output; 44 | %11 = OpTypePointer Output %5 45 | %12 = OpVariable %11 Output 46 | 47 | ; out vec4 crucial_data; 48 | %13 = OpTypePointer Output %6 49 | %14 = OpVariable %13 Output 50 | 51 | ; uniform object_properties { ... } props; 52 | %15 = OpTypePointer Uniform %7 53 | %16 = OpVariable %15 Uniform 54 | 55 | ; Uniform pointer to `vec4` 56 | %17 = OpTypePointer Uniform %6 57 | 58 | ; The integer constants 0 and 1 59 | %22 = OpConstant %8 0 60 | %18 = OpConstant %8 1 61 | 62 | %1 = OpTypeVoid 63 | %2 = OpTypeFunction %1 64 | %3 = OpFunction %1 None %2 65 | %4 = OpLabel 66 | 67 | ; useful_output = fancy_attribute; 68 | %19 = OpLoad %5 %10 69 | OpStore %12 %19 70 | 71 | ; %21 = props.shininess 72 | %20 = OpAccessChain %17 %16 %22 73 | %21 = OpLoad %6 %20 74 | 75 | ; %24 = props.smoothness 76 | %23 = OpAccessChain %17 %16 %18 77 | %24 = OpLoad %6 %23 78 | 79 | ; crucial_data = vec4(props.shininess.x, 80 | ; props.shininess.y, 81 | ; props.smoothness.x, 82 | ; props.smoothness.y); 83 | %25 = OpVectorShuffle %6 %21 %24 0 1 4 5 84 | OpStore %14 %25 85 | 86 | OpReturn 87 | OpFunctionEnd 88 | -------------------------------------------------------------------------------- /09 - Composite Extraction and Construction/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /10 - Arithmetics/README.md: -------------------------------------------------------------------------------- 1 | # Arithmetics 2 | 3 | Let's take a break and do something simpler in this exercise: math. So far we learned how to 4 | declare stuff and move data around. But what if we wanted to actually do something? Luckily doing 5 | things is simpler than setting things up! 6 | 7 | In this exercise, you are pretty much free to do whatever you want. In `exercise.spvasm` you can 8 | find a fragment shader to work with that produces the following code inside `main()`: 9 | 10 | ```c 11 | beautiful_color = fancy_varying; 12 | physicsy_properties = props.smoothness; 13 | ``` 14 | 15 | I suggest modifying the SPIR-V to produce the following instead: 16 | 17 | ```c 18 | beautiful_color = fancy_varying * (vec4(1) - props.shininess); 19 | physicsy_properties = -props.smoothness / props.shininess; 20 | ``` 21 | 22 | Here are a few pointers: 23 | 24 | - `OpConstant` is used to create constants, 25 | - `OpFNegate` can be used to negate floating point scalars and vectors 26 | - `OpFSub` subtracts floating point scalars and vectors, 27 | - `OpFMul` multiplies floating point scalars and vectors (per component), 28 | - `OpFDiv` divides floating point scalars and vectors (per component), 29 | 30 | Fancy doing something more complicated? By all means go for it. Here are a couple more pointers 31 | to create something even more interesting: 32 | 33 | - `OpTypeMatrix` is used to create matrix types; why not add a matrix field to our buffer object? 34 | - `OpMatrixTimesVector` and `OpVectorTimesMatrix` multiply a floating point matrix and a vector, 35 | - `OpDot` produces the dot product of two floating point vectors, 36 | -------------------------------------------------------------------------------- /10 - Arithmetics/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Fragment %3 "main" %10 %12 %14 5 | OpExecutionMode %3 OriginUpperLeft 6 | 7 | OpName %10 "fancy_varying" 8 | OpName %12 "beautiful_color" 9 | OpName %14 "physicsy_properties" 10 | OpName %7 "object_properties" 11 | OpMemberName %7 0 "shininess" 12 | OpMemberName %7 1 "smoothness" 13 | OpName %16 "props" 14 | 15 | OpDecorate %10 Location 0 16 | OpDecorate %12 Location 0 17 | OpDecorate %14 Location 1 18 | 19 | ; Make the struct definition a uniform buffer, and specify its memory layout 20 | OpDecorate %7 Block 21 | OpMemberDecorate %7 0 Offset 0 22 | OpMemberDecorate %7 1 Offset 16 23 | 24 | ; Assign set/binding decorations to the uniform buffer variable 25 | OpDecorate %16 DescriptorSet 2 26 | OpDecorate %16 Binding 3 27 | 28 | ; the `float` type 29 | %5 = OpTypeFloat 32 30 | 31 | ; the `vec4` type 32 | %6 = OpTypeVector %5 4 33 | 34 | ; the `{vec4, vec4}` struct type 35 | %7 = OpTypeStruct %6 %6 36 | 37 | ; the `int` type 38 | %8 = OpTypeInt 32 1 39 | 40 | ; in float fancy_varying; 41 | %9 = OpTypePointer Input %6 42 | %10 = OpVariable %9 Input 43 | 44 | ; out float beautiful_color; 45 | %11 = OpTypePointer Output %6 46 | %12 = OpVariable %11 Output 47 | 48 | ; out vec4 physicsy_properties; 49 | %13 = OpTypePointer Output %6 50 | %14 = OpVariable %13 Output 51 | 52 | ; uniform object_properties { ... } props; 53 | %15 = OpTypePointer Uniform %7 54 | %16 = OpVariable %15 Uniform 55 | 56 | ; Uniform pointer to `vec4` 57 | %17 = OpTypePointer Uniform %6 58 | 59 | ; The integer constant 1 60 | %18 = OpConstant %8 1 61 | 62 | %1 = OpTypeVoid 63 | %2 = OpTypeFunction %1 64 | %3 = OpFunction %1 None %2 65 | %4 = OpLabel 66 | 67 | ; beautiful_color = fancy_varying; 68 | %19 = OpLoad %6 %10 69 | OpStore %12 %19 70 | 71 | ; physicsy_properties = props.smoothness; 72 | %20 = OpAccessChain %17 %16 %18 73 | %21 = OpLoad %6 %20 74 | OpStore %14 %21 75 | 76 | OpReturn 77 | OpFunctionEnd 78 | -------------------------------------------------------------------------------- /10 - Arithmetics/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Fragment %3 "main" %10 %12 %14 5 | OpExecutionMode %3 OriginUpperLeft 6 | 7 | OpName %10 "fancy_varying" 8 | OpName %12 "beautiful_color" 9 | OpName %14 "physicsy_properties" 10 | OpName %7 "object_properties" 11 | OpMemberName %7 0 "shininess" 12 | OpMemberName %7 1 "smoothness" 13 | OpName %16 "props" 14 | 15 | OpDecorate %10 Location 0 16 | OpDecorate %12 Location 0 17 | OpDecorate %14 Location 1 18 | 19 | ; Make the struct definition a uniform buffer, and specify its memory layout 20 | OpDecorate %7 Block 21 | OpMemberDecorate %7 0 Offset 0 22 | OpMemberDecorate %7 1 Offset 16 23 | 24 | ; Assign set/binding decorations to the uniform buffer variable 25 | OpDecorate %16 DescriptorSet 2 26 | OpDecorate %16 Binding 3 27 | 28 | ; the `float` type 29 | %5 = OpTypeFloat 32 30 | 31 | ; the `vec4` type 32 | %6 = OpTypeVector %5 4 33 | 34 | ; the `{vec4, vec4}` struct type 35 | %7 = OpTypeStruct %6 %6 36 | 37 | ; the `int` type 38 | %8 = OpTypeInt 32 1 39 | 40 | ; in float fancy_varying; 41 | %9 = OpTypePointer Input %6 42 | %10 = OpVariable %9 Input 43 | 44 | ; out float beautiful_color; 45 | %11 = OpTypePointer Output %6 46 | %12 = OpVariable %11 Output 47 | 48 | ; out vec4 physicsy_properties; 49 | %13 = OpTypePointer Output %6 50 | %14 = OpVariable %13 Output 51 | 52 | ; uniform object_properties { ... } props; 53 | %15 = OpTypePointer Uniform %7 54 | %16 = OpVariable %15 Uniform 55 | 56 | ; Uniform pointer to `vec4` 57 | %17 = OpTypePointer Uniform %6 58 | 59 | ; The integer constants 0 and 1 60 | %18 = OpConstant %8 0 61 | %19 = OpConstant %8 1 62 | 63 | ; The constants 1.0 and vec4(1.0) 64 | %20 = OpConstant %5 1.0 65 | %21 = OpConstantComposite %6 %20 %20 %20 %20 66 | 67 | %1 = OpTypeVoid 68 | %2 = OpTypeFunction %1 69 | %3 = OpFunction %1 None %2 70 | %4 = OpLabel 71 | 72 | ; load fancy_varying 73 | %22 = OpLoad %6 %10 74 | 75 | ; load props.shininess 76 | %23 = OpAccessChain %17 %16 %18 77 | %24 = OpLoad %6 %23 78 | 79 | ; load props.smoothness 80 | %25 = OpAccessChain %17 %16 %19 81 | %26 = OpLoad %6 %25 82 | 83 | ; vec4(1) - props.shininess 84 | %27 = OpFSub %6 %21 %24 85 | 86 | ; fancy_varying * (vec4(1) - props.shininess) 87 | %28 = OpFMul %6 %22 %27 88 | 89 | ; beautiful_color = fancy_varying * (vec4(1) - props.shininess); 90 | OpStore %12 %28 91 | 92 | ; -props.smoothness 93 | %29 = OpFNegate %6 %26 94 | 95 | ; -props.smoothness / props.shininess 96 | %30 = OpFDiv %6 %29 %24 97 | 98 | ; physicsy_properties = -props.smoothness / props.shininess; 99 | OpStore %14 %30 100 | 101 | OpReturn 102 | OpFunctionEnd 103 | -------------------------------------------------------------------------------- /10 - Arithmetics/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /11 - If Else/README.md: -------------------------------------------------------------------------------- 1 | # If Else 2 | 3 | In this exercise, we'll learn all about code blocks and see how to write conditional code, sticking 4 | to if-else for the time being. 5 | 6 | In `exercise.spvasm`, we have the usual setup with `fancy_attribute`, `useful_output` and 7 | `crucial_data`: 8 | 9 | ```c 10 | useful_output = fancy_attribute; 11 | crucial_data = props.highly_exclusive_bits; 12 | ``` 13 | 14 | We're going to turn that into the following: 15 | 16 | ```c 17 | if (fancy_attribute < props.threshold) 18 | { 19 | useful_output = 0; 20 | crucial_data = vec4(0); 21 | } 22 | else 23 | { 24 | useful_output = 1.0; 25 | crucial_data = props.highly_exclusive_bits; 26 | } 27 | ``` 28 | 29 | ## Blocks 30 | 31 | You have already seen the `OpLabel` instruction after which we have so far been writing code that 32 | appears inside `main()`. You have surely also noticed the `OpReturn` instruction at the end of the 33 | function. Everything from the `OpLabel` instruction to the `OpReturn` one has so far been one 34 | "block". A block is defined as a set of instructions starting with `OpLabel` and some form of 35 | "jump" instruction like return, branch, switch, etc. 36 | 37 | So first of all, `OpLabel` is the first instruction of every block. This instruction is not just an 38 | identifier for the block, but is also used as the jump target of other blocks. 39 | 40 | Thinking of the blocks as nodes and the jumps as (directed) connections between them, a graph is 41 | formed called the Control-Flow Graph. SPIR-V also defines a "dominates" relationship between the 42 | blocks which basically tells whether a block needs to execute before another. This is nothing too 43 | important for us to know at this point (or maybe ever), other than that SPIR-V requires the blocks 44 | to be specified in order such that a block that dominates another (i.e. is executed before) is 45 | written before it. In simple terms, no backward jumps (other than for loops obviously). 46 | 47 | In this exercise, we will have four blocks. If you thought blocks are just going to be like in C, 48 | here's where you know that's not true. Recall that every block must start with an `OpLabel` 49 | instruction and that's where other blocks can jump to. Our four blocks will look like the 50 | following (written in made-up pseudo-code): 51 | 52 | ```c 53 | // Block 1: everything leading up to the branch, ending in branch. 54 | block1: 55 | condition = fancy_attribute < props.threshold; 56 | if (condition) jump block2; else jump block3; 57 | 58 | // Block 2: contents of the `if` case, ending in jump. 59 | block2: 60 | useful_output = 0; 61 | crucial_data = vec4(0); 62 | jump block4; 63 | 64 | // Block 3: contents of the `else` case, ending in jump. 65 | block3: 66 | useful_output = 1.0; 67 | crucial_data = props.highly_exclusive_bits; 68 | jump block4; 69 | 70 | // Block 4: everything after the if-else blocks, in this case just containing return. 71 | block4: 72 | return; 73 | ``` 74 | 75 | ## An If-Else Construct 76 | 77 | Now let's see. We know how to write an `OpLabel` instruction (if not, look it up right now). We 78 | also know how to write the actual contents of the blocks (you _did_ do the previous exercises, 79 | didn't you?). We actually need only a handful of instructions to arrive at the full solution. 80 | 81 | First off, the comparison operation used in the condition of the branch (floating point less than) 82 | is implemented with the `OpFOrdLessThan` instruction. 83 | 84 | > Waiting for you to consult the spec ... 85 | 86 | What's the return type of that instruction, and how do we declare it? 87 | 88 | > Waiting for you to check again ... 89 | 90 | And what's the meaning of "Ord" in the instruction? You might have noticed there's an "Unord" 91 | version of the same instruction (and other similar ones). The difference between the Ord and Unord 92 | instructions is in the handling of NaN. Ordered floating points can't be NaN, and we're satisfied 93 | with that. 94 | 95 | You can go ahead and implement the four blocks in order in `exercise.spvasm`, leaving the `if` and 96 | `jump` instructions out. You will end up with four blocks each starting with one `OpLabel` 97 | instruction, but missing their terminating jump (except maybe the last block that already has 98 | `OpReturn`) 99 | 100 | > Waiting for you to complete this part of the exercise ... 101 | 102 | Before implementing the if-else construct, let's look at the control flow again: 103 | 104 | ``` 105 | block1 106 | / \ 107 | | | 108 | true false 109 | | | 110 | V V 111 | block2 block3 112 | \ / 113 | \ / 114 | | 115 | V 116 | block4 117 | ``` 118 | 119 | To implement the if-else branch, SPIR-V provides the above graph through two instructions: 120 | 121 | - One instruction specifies what block is reached after the if-else flow is finished; this block is 122 | referred to as the "merge" block, 123 | - Another instruction specifies the if condition and the true and false blocks. 124 | 125 | Note again that the blocks are identified by the id of their `OpLabel` instruction. The first 126 | instruction above is `OpSelectionMerge` and the second instruction is `OpBranchConditional`. Go 127 | ahead and look these up in the spec before implementing them in the exercise. 128 | 129 | > Waiting for you to complete this part of the exercise ... 130 | 131 | Finally, all that's left is to tell blocks 2 and 3 to jump to block 4 at the end. This is done with 132 | the `OpBranch` instruction. You can now finish the exercise. 133 | 134 | As always, remember to use `./validate` to validate your hand-written SPIR-V and see the 135 | corresponding GLSL. You can also see a summary of what each exercise does with: 136 | 137 | ```bash 138 | $ diff exercise.spvasm solution.spvasm 139 | ``` 140 | -------------------------------------------------------------------------------- /11 - If Else/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %10 %12 %14 5 | 6 | OpName %10 "fancy_attribute" 7 | OpName %12 "useful_output" 8 | OpName %14 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "threshold" 11 | OpMemberName %7 1 "highly_exclusive_bits" 12 | OpName %16 "props" 13 | 14 | OpDecorate %10 Location 0 15 | OpDecorate %12 Location 0 16 | OpDecorate %14 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %16 DescriptorSet 2 25 | OpDecorate %16 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{float, vec4}` struct type 34 | %7 = OpTypeStruct %5 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; in float fancy_attribute; 40 | %9 = OpTypePointer Input %5 41 | %10 = OpVariable %9 Input 42 | 43 | ; out float useful_output; 44 | %11 = OpTypePointer Output %5 45 | %12 = OpVariable %11 Output 46 | 47 | ; out vec4 crucial_data; 48 | %13 = OpTypePointer Output %6 49 | %14 = OpVariable %13 Output 50 | 51 | ; uniform object_properties { ... } props; 52 | %15 = OpTypePointer Uniform %7 53 | %16 = OpVariable %15 Uniform 54 | 55 | ; Uniform pointer to `vec4` 56 | %17 = OpTypePointer Uniform %6 57 | 58 | ; The integer constant 1 59 | %18 = OpConstant %8 1 60 | 61 | %1 = OpTypeVoid 62 | %2 = OpTypeFunction %1 63 | %3 = OpFunction %1 None %2 64 | %4 = OpLabel 65 | 66 | ; useful_output = fancy_attribute; 67 | %19 = OpLoad %5 %10 68 | OpStore %12 %19 69 | 70 | ; crucial_data = props.highly_exclusive_bits; 71 | %20 = OpAccessChain %17 %16 %18 72 | %21 = OpLoad %6 %20 73 | OpStore %14 %21 74 | 75 | OpReturn 76 | OpFunctionEnd 77 | -------------------------------------------------------------------------------- /11 - If Else/solution.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %10 %12 %14 5 | 6 | OpName %10 "fancy_attribute" 7 | OpName %12 "useful_output" 8 | OpName %14 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "threshold" 11 | OpMemberName %7 1 "highly_exclusive_bits" 12 | OpName %16 "props" 13 | 14 | OpDecorate %10 Location 0 15 | OpDecorate %12 Location 0 16 | OpDecorate %14 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %16 DescriptorSet 2 25 | OpDecorate %16 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{float, vec4}` struct type 34 | %7 = OpTypeStruct %5 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; the `bool` type 40 | %24 = OpTypeBool 41 | 42 | ; in float fancy_attribute; 43 | %9 = OpTypePointer Input %5 44 | %10 = OpVariable %9 Input 45 | 46 | ; out float useful_output; 47 | %11 = OpTypePointer Output %5 48 | %12 = OpVariable %11 Output 49 | 50 | ; out vec4 crucial_data; 51 | %13 = OpTypePointer Output %6 52 | %14 = OpVariable %13 Output 53 | 54 | ; uniform object_properties { ... } props; 55 | %15 = OpTypePointer Uniform %7 56 | %16 = OpVariable %15 Uniform 57 | 58 | ; Uniform pointer to `float` and `vec4` 59 | %20 = OpTypePointer Uniform %5 60 | %17 = OpTypePointer Uniform %6 61 | 62 | ; The integer constants 0 and 1 63 | %18 = OpConstant %8 0 64 | %19 = OpConstant %8 1 65 | 66 | ; The float constants 0, 1.0 and vec4(0) 67 | %21 = OpConstantNull %5 68 | %22 = OpConstant %5 1.0 69 | %23 = OpConstantNull %6 70 | 71 | %1 = OpTypeVoid 72 | %2 = OpTypeFunction %1 73 | %3 = OpFunction %1 None %2 74 | %4 = OpLabel 75 | 76 | ; load fancy_attribute 77 | %25 = OpLoad %5 %10 78 | 79 | ; load props.threshold 80 | %26 = OpAccessChain %20 %16 %18 81 | %27 = OpLoad %5 %26 82 | 83 | ; fancy_attribute < props.threshold 84 | %28 = OpFOrdLessThan %24 %25 %27 85 | 86 | ; if (fancy_attribute < props.threshold) jump %29; else jump %30; 87 | OpSelectionMerge %33 None 88 | OpBranchConditional %28 %29 %30 89 | 90 | 91 | ; the true block 92 | %29 = OpLabel 93 | 94 | ; useful_output = 0; 95 | OpStore %12 %21 96 | 97 | ; crucial_data = vec4(0); 98 | OpStore %14 %23 99 | 100 | ; End the block 101 | OpBranch %33 102 | 103 | 104 | ; the false block 105 | %30 = OpLabel 106 | 107 | ; useful_output = 1.0; 108 | OpStore %12 %22 109 | 110 | ; crucial_data = props.highly_exclusive_bits; 111 | %31 = OpAccessChain %17 %16 %19 112 | %32 = OpLoad %6 %31 113 | OpStore %14 %32 114 | 115 | ; End the block 116 | OpBranch %33 117 | 118 | 119 | ; the merge block 120 | %33 = OpLabel 121 | 122 | OpReturn 123 | OpFunctionEnd 124 | -------------------------------------------------------------------------------- /11 - If Else/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /12 - The Phi Function/README.md: -------------------------------------------------------------------------------- 1 | # The Phi Function 2 | 3 | You may recall (as if!) from exercise 01 that SPIR-V is in SSA form. What does that mean? TL;DR, 4 | it means that every id is written to in exactly one place. In C, you can modify a variable like 5 | `x = x + 1`, which is not allowed in SSA form. In SSA form instead, you can think of the `x` being 6 | written to as a new variable; `x2 = x1 + 1`. 7 | 8 | SSA form makes a lot of things easier for the optimizer, but we aren't going to get bogged down on 9 | the details. If you want to, you can read a bit about it [on Wikipedia][SSA]. Funnily enough, 10 | SPIR-V isn't entirely in SSA form. You might recall from the previous exercise that we wrote to the 11 | same output variable in both the true and false blocks of an `if`! 12 | 13 | So how is SPIR-V in SSA form? It's true that every "intermediate" result is written to in exactly 14 | one place; that would be the ids. Writes to memory however (through `OpVariable`s) are allowed to 15 | be done in multiple places. This could be largely avoided using a special instruction called 16 | `OpPhi` which is what this exercise is all about. That said, you can write SPIR-V without ever 17 | using `OpPhi`, so feel free to skip learning about the most interesting part of SPIR-V. 18 | 19 | [SSA]: https://en.wikipedia.org/wiki/Static_single_assignment_form 20 | 21 | ## Local Variables 22 | 23 | Before we get to `OpPhi`, let's actually see how we can avoid it. Maybe then you could appreciate 24 | its value. Admittedly this will be a simple exercise, so I suggest an `x20` multiplier on the 25 | appreciation. 26 | 27 | In `exercise.spvasm`, you can find the solution to the previous exercise, which basically generates 28 | the following code in `main()`: 29 | 30 | ``` 31 | if (fancy_attribute < props.threshold) 32 | { 33 | useful_output = 0.0; 34 | crucial_data = vec4(0.0); 35 | } 36 | else 37 | { 38 | useful_output = 1.0; 39 | crucial_data = props.highly_exclusive_bits; 40 | } 41 | ``` 42 | 43 | As you can see, write to `crucial_data` is being done in both the true and false blocks of the `if`. 44 | Now that's a simple `OpStore` instruction, but for a moment let's pretend something complicated was 45 | supposed to happen to the data before it gets written to `crucial_data`. Obviously we don't want to 46 | repeat that in both blocks. So let's try to assign to `crucial_data` only once outside the if-else 47 | blocks (and pretend it was complicated). In GLSL, there's really only one way to do that; use a 48 | temporary variable: 49 | 50 | ``` 51 | vec4 temp; 52 | if (fancy_attribute < props.threshold) 53 | { 54 | useful_output = 0.0; 55 | temp = vec4(0.0); 56 | } 57 | else 58 | { 59 | useful_output = 1.0; 60 | temp = props.highly_exclusive_bits; 61 | } 62 | crucial_data = temp; 63 | ``` 64 | 65 | Let's get that implemented. We need to declare a new variable, which as you already know is done 66 | with `OpVariable`. Let's be civil and not declare a global variable here. To declare a 67 | function-local variable, you need to know the following: 68 | 69 | - The storage class of a function-local variable is `Function` 70 | - Function-local variables must be declared right after `OpLabel` at the beginning of the function. 71 | Note that it's not the beginning of the block that uses it, but the beginning of the function! In 72 | terms of scope, thus, there are only two scopes in SPIR-V, the global scope and the function 73 | scope (unlike C or GLSL where every block introduces a new scope). 74 | 75 | To summarize: 76 | 77 | - Declare a function local variable `temp` (where does the corresponding `OpTypePointer` go?), 78 | - Write the values that were previously written to `crucial_data` to `temp` instead, 79 | - In the "merge" block of the `if`, load from `temp` and write to `crucial_data`. 80 | 81 | > Waiting for you to complete this part of the exercise ... 82 | 83 | If you have trouble completing this part, I suggest revisiting previous exercises. If not, good job 84 | champion! You can find the solution to this part in `solution1.spvasm` for reference (and not 85 | cheating). 86 | 87 | ## `OpPhi` 88 | 89 | Now let's make things interesting. We are going to remove the `temp` variable now and use the 90 | SSA `OpPhi` instruction to do something you can't do in GLSL. 91 | 92 | > Waiting for you to consult the spec even though I'm sure you already did out of habit ... 93 | 94 | So `OpPhi` says "select a value based on which of the parent blocks was taken to reach this 95 | instruction". Mind = blown? 96 | 97 | Attempting to put this in GLSL-ish, here's how we can make the code look like with `OpPhi`: 98 | 99 | ``` 100 | if (fancy_attribute < props.threshold) 101 | { 102 | useful_output = 0.0; 103 | } 104 | else 105 | { 106 | useful_output = 1.0; 107 | temp = props.highly_exclusive_bits 108 | } 109 | crucial_data = true_block_taken ? vec4(0) 110 | false_block_taken ? temp; 111 | ``` 112 | 113 | That last made-up syntax with question marks is what `OpPhi` does. Let's do that now: 114 | 115 | - Remove the `temp` variable you added in the previous section (and the `OpStore`s to it), 116 | - In the "merge" block, instead of `OpLoad` from `temp`, use `OpPhi` to select one value or another 117 | based on which block was taken. In this case, one of the values is a constant (which is declared 118 | globally, and not calculated in the if-else blocks, so yeah, that works) 119 | 120 | It doesn't help that the spec says `OpPhi` selects a variable. It's *not* selecting a variable (as 121 | in something defined by `OpVariable`), but rather a value. 122 | 123 | > Waiting for you to complete this part of the exercise ... 124 | 125 | Now do the same for `useful_output`: 126 | 127 | ``` 128 | if (fancy_attribute < props.threshold) 129 | { 130 | } 131 | else 132 | { 133 | temp = props.highly_exclusive_bits 134 | } 135 | useful_output = true_block_taken ? 0.0 136 | false_block_taken ? 1.0; 137 | crucial_data = true_block_taken ? vec4(0) 138 | false_block_taken ? temp; 139 | ``` 140 | 141 | > Waiting for you to complete this part of the exercise ... 142 | 143 | Good job! We saved a number of instructions this way, avoiding `OpStore` and `OpLoad` instructions. 144 | Now imagine how many `OpStore` instructions you could save by using that on a huge switch case! 145 | 146 | Most importantly though, we delivered on the SSA promise that everything is written to in exactly 147 | one place. The optimizer shall rejoice in this knowledge. 148 | 149 | You can find the solution to this part in `solution2.spvasm` (cheating allowed, but only this time). 150 | 151 | ## (Optional) Eliminating The Empty Block 152 | 153 | It's pretty annoying that we now have an empty block. Can we eliminate that? Why would I ask if 154 | the answer was "no"? 155 | 156 | Here are a couple of hints: 157 | 158 | - `OpBranchConditional` takes two blocks to jump to in case of the condition being true or false. 159 | Either of those can be the merge block! In this example, you can have the true case select the 160 | merge block as the jump target and delete the empty true block altogether. Notice how there's 161 | only an "else" block now (which is impossible in GLSL)? That's a clever way of avoiding one 162 | `OpLogicalNot` on the condition! 163 | - `OpPhi` considers the block before `OpBranchConditional` (which is called the "header" block) as a 164 | parent too. Using the id of that block effectively means "if none of the other parent blocks were 165 | taken". 166 | 167 | > Waiting for you to complete this part of the exercise ... 168 | 169 | Nice! The solution to this part can be found in `solution3.spvasm`. Did you notice how no matter 170 | what we do here, `./validate` ends up producing the same GLSL? This stuff is just something GLSL 171 | can't do. 172 | -------------------------------------------------------------------------------- /12 - The Phi Function/exercise.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %11 %13 %15 5 | 6 | OpName %11 "fancy_attribute" 7 | OpName %13 "useful_output" 8 | OpName %15 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "threshold" 11 | OpMemberName %7 1 "highly_exclusive_bits" 12 | OpName %17 "props" 13 | 14 | OpDecorate %11 Location 0 15 | OpDecorate %13 Location 0 16 | OpDecorate %15 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %17 DescriptorSet 2 25 | OpDecorate %17 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{float, vec4}` struct type 34 | %7 = OpTypeStruct %5 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; the `bool` type 40 | %9 = OpTypeBool 41 | 42 | ; in float fancy_attribute; 43 | %10 = OpTypePointer Input %5 44 | %11 = OpVariable %10 Input 45 | 46 | ; out float useful_output; 47 | %12 = OpTypePointer Output %5 48 | %13 = OpVariable %12 Output 49 | 50 | ; out vec4 crucial_data; 51 | %14 = OpTypePointer Output %6 52 | %15 = OpVariable %14 Output 53 | 54 | ; uniform object_properties { ... } props; 55 | %16 = OpTypePointer Uniform %7 56 | %17 = OpVariable %16 Uniform 57 | 58 | ; Uniform pointer to `float` and `vec4` 59 | %18 = OpTypePointer Uniform %5 60 | %19 = OpTypePointer Uniform %6 61 | 62 | ; The integer constants 0 and 1 63 | %20 = OpConstant %8 0 64 | %21 = OpConstant %8 1 65 | 66 | ; The float constants 0, 1.0 and vec4(0) 67 | %22 = OpConstantNull %5 68 | %23 = OpConstant %5 1.0 69 | %24 = OpConstantNull %6 70 | 71 | %1 = OpTypeVoid 72 | %2 = OpTypeFunction %1 73 | %3 = OpFunction %1 None %2 74 | %4 = OpLabel 75 | 76 | ; load fancy_attribute 77 | %25 = OpLoad %5 %11 78 | 79 | ; load props.threshold 80 | %26 = OpAccessChain %18 %17 %20 81 | %27 = OpLoad %5 %26 82 | 83 | ; fancy_attribute < props.threshold 84 | %28 = OpFOrdLessThan %9 %25 %27 85 | 86 | ; if (fancy_attribute < props.threshold) jump %29; else jump %30; 87 | OpSelectionMerge %33 None 88 | OpBranchConditional %28 %29 %30 89 | 90 | 91 | ; the true block 92 | %29 = OpLabel 93 | 94 | ; useful_output = 0; 95 | OpStore %13 %22 96 | 97 | ; crucial_data = vec4(0); 98 | OpStore %15 %24 99 | 100 | ; End the block 101 | OpBranch %33 102 | 103 | 104 | ; the false block 105 | %30 = OpLabel 106 | 107 | ; useful_output = 1.0; 108 | OpStore %13 %23 109 | 110 | ; crucial_data = props.highly_exclusive_bits; 111 | %31 = OpAccessChain %19 %17 %21 112 | %32 = OpLoad %6 %31 113 | OpStore %15 %32 114 | 115 | ; End the block 116 | OpBranch %33 117 | 118 | 119 | ; the merge block 120 | %33 = OpLabel 121 | 122 | OpReturn 123 | OpFunctionEnd 124 | -------------------------------------------------------------------------------- /12 - The Phi Function/solution1.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %11 %13 %15 5 | 6 | OpName %11 "fancy_attribute" 7 | OpName %13 "useful_output" 8 | OpName %15 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "threshold" 11 | OpMemberName %7 1 "highly_exclusive_bits" 12 | OpName %17 "props" 13 | OpName %35 "temp" 14 | 15 | OpDecorate %11 Location 0 16 | OpDecorate %13 Location 0 17 | OpDecorate %15 Location 1 18 | 19 | ; Make the struct definition a uniform buffer, and specify its memory layout 20 | OpDecorate %7 Block 21 | OpMemberDecorate %7 0 Offset 0 22 | OpMemberDecorate %7 1 Offset 16 23 | 24 | ; Assign set/binding decorations to the uniform buffer variable 25 | OpDecorate %17 DescriptorSet 2 26 | OpDecorate %17 Binding 3 27 | 28 | ; the `float` type 29 | %5 = OpTypeFloat 32 30 | 31 | ; the `vec4` type 32 | %6 = OpTypeVector %5 4 33 | 34 | ; the `{float, vec4}` struct type 35 | %7 = OpTypeStruct %5 %6 36 | 37 | ; the `int` type 38 | %8 = OpTypeInt 32 1 39 | 40 | ; the `bool` type 41 | %9 = OpTypeBool 42 | 43 | ; in float fancy_attribute; 44 | %10 = OpTypePointer Input %5 45 | %11 = OpVariable %10 Input 46 | 47 | ; out float useful_output; 48 | %12 = OpTypePointer Output %5 49 | %13 = OpVariable %12 Output 50 | 51 | ; out vec4 crucial_data; 52 | %14 = OpTypePointer Output %6 53 | %15 = OpVariable %14 Output 54 | 55 | ; uniform object_properties { ... } props; 56 | %16 = OpTypePointer Uniform %7 57 | %17 = OpVariable %16 Uniform 58 | 59 | ; Uniform pointer to `float` and `vec4` 60 | %18 = OpTypePointer Uniform %5 61 | %19 = OpTypePointer Uniform %6 62 | 63 | ; The integer constants 0 and 1 64 | %20 = OpConstant %8 0 65 | %21 = OpConstant %8 1 66 | 67 | ; The float constants 0, 1.0 and vec4(0) 68 | %22 = OpConstantNull %5 69 | %23 = OpConstant %5 1.0 70 | %24 = OpConstantNull %6 71 | 72 | ; The function-local `vec4` type pointer 73 | %34 = OpTypePointer Function %6 74 | 75 | %1 = OpTypeVoid 76 | %2 = OpTypeFunction %1 77 | %3 = OpFunction %1 None %2 78 | %4 = OpLabel 79 | 80 | ; vec4 temp; 81 | %35 = OpVariable %34 Function 82 | 83 | ; load fancy_attribute 84 | %25 = OpLoad %5 %11 85 | 86 | ; load props.threshold 87 | %26 = OpAccessChain %18 %17 %20 88 | %27 = OpLoad %5 %26 89 | 90 | ; fancy_attribute < props.threshold 91 | %28 = OpFOrdLessThan %9 %25 %27 92 | 93 | ; if (fancy_attribute < props.threshold) jump %29; else jump %30; 94 | OpSelectionMerge %33 None 95 | OpBranchConditional %28 %29 %30 96 | 97 | 98 | ; the true block 99 | %29 = OpLabel 100 | 101 | ; useful_output = 0; 102 | OpStore %13 %22 103 | 104 | ; temp = vec4(0); 105 | OpStore %35 %24 106 | 107 | ; End the block 108 | OpBranch %33 109 | 110 | 111 | ; the false block 112 | %30 = OpLabel 113 | 114 | ; useful_output = 1.0; 115 | OpStore %13 %23 116 | 117 | ; temp = props.highly_exclusive_bits; 118 | %31 = OpAccessChain %19 %17 %21 119 | %32 = OpLoad %6 %31 120 | OpStore %35 %32 121 | 122 | ; End the block 123 | OpBranch %33 124 | 125 | 126 | ; the merge block 127 | %33 = OpLabel 128 | 129 | ; crucial_data = temp 130 | %36 = OpLoad %6 %35 131 | OpStore %15 %36 132 | 133 | OpReturn 134 | OpFunctionEnd 135 | -------------------------------------------------------------------------------- /12 - The Phi Function/solution2.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %11 %13 %15 5 | 6 | OpName %11 "fancy_attribute" 7 | OpName %13 "useful_output" 8 | OpName %15 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "threshold" 11 | OpMemberName %7 1 "highly_exclusive_bits" 12 | OpName %17 "props" 13 | 14 | OpDecorate %11 Location 0 15 | OpDecorate %13 Location 0 16 | OpDecorate %15 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %17 DescriptorSet 2 25 | OpDecorate %17 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{float, vec4}` struct type 34 | %7 = OpTypeStruct %5 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; the `bool` type 40 | %9 = OpTypeBool 41 | 42 | ; in float fancy_attribute; 43 | %10 = OpTypePointer Input %5 44 | %11 = OpVariable %10 Input 45 | 46 | ; out float useful_output; 47 | %12 = OpTypePointer Output %5 48 | %13 = OpVariable %12 Output 49 | 50 | ; out vec4 crucial_data; 51 | %14 = OpTypePointer Output %6 52 | %15 = OpVariable %14 Output 53 | 54 | ; uniform object_properties { ... } props; 55 | %16 = OpTypePointer Uniform %7 56 | %17 = OpVariable %16 Uniform 57 | 58 | ; Uniform pointer to `float` and `vec4` 59 | %18 = OpTypePointer Uniform %5 60 | %19 = OpTypePointer Uniform %6 61 | 62 | ; The integer constants 0 and 1 63 | %20 = OpConstant %8 0 64 | %21 = OpConstant %8 1 65 | 66 | ; The float constants 0, 1.0 and vec4(0) 67 | %22 = OpConstantNull %5 68 | %23 = OpConstant %5 1.0 69 | %24 = OpConstantNull %6 70 | 71 | %1 = OpTypeVoid 72 | %2 = OpTypeFunction %1 73 | %3 = OpFunction %1 None %2 74 | %4 = OpLabel 75 | 76 | ; load fancy_attribute 77 | %25 = OpLoad %5 %11 78 | 79 | ; load props.threshold 80 | %26 = OpAccessChain %18 %17 %20 81 | %27 = OpLoad %5 %26 82 | 83 | ; fancy_attribute < props.threshold 84 | %28 = OpFOrdLessThan %9 %25 %27 85 | 86 | ; if (fancy_attribute < props.threshold) jump %29; else jump %30; 87 | OpSelectionMerge %33 None 88 | OpBranchConditional %28 %29 %30 89 | 90 | 91 | ; the true block 92 | %29 = OpLabel 93 | 94 | ; End the block 95 | OpBranch %33 96 | 97 | 98 | ; the false block 99 | %30 = OpLabel 100 | 101 | ; load props.highly_exclusive_bits; 102 | %31 = OpAccessChain %19 %17 %21 103 | %32 = OpLoad %6 %31 104 | 105 | ; End the block 106 | OpBranch %33 107 | 108 | 109 | ; the merge block 110 | %33 = OpLabel 111 | 112 | ; useful_output = 113 | ; 114 | ; If the true path was taken: %22 which is 0 115 | ; If the false path was taken: %23 which is 1 116 | ; 117 | %34 = OpPhi %5 %22 %29 %23 %30 118 | 119 | ; crucial_data = 120 | ; 121 | ; If the true path was taken: %24 which is vec4(0) 122 | ; If the false path was taken: %32 which is props.highly_exclusive_bits 123 | ; 124 | %35 = OpPhi %6 %24 %29 %32 %30 125 | 126 | OpStore %13 %34 127 | OpStore %15 %35 128 | 129 | OpReturn 130 | OpFunctionEnd 131 | -------------------------------------------------------------------------------- /12 - The Phi Function/solution3.spvasm: -------------------------------------------------------------------------------- 1 | OpCapability Shader 2 | OpMemoryModel Logical GLSL450 3 | 4 | OpEntryPoint Vertex %3 "main" %11 %13 %15 5 | 6 | OpName %11 "fancy_attribute" 7 | OpName %13 "useful_output" 8 | OpName %15 "crucial_data" 9 | OpName %7 "object_properties" 10 | OpMemberName %7 0 "threshold" 11 | OpMemberName %7 1 "highly_exclusive_bits" 12 | OpName %17 "props" 13 | 14 | OpDecorate %11 Location 0 15 | OpDecorate %13 Location 0 16 | OpDecorate %15 Location 1 17 | 18 | ; Make the struct definition a uniform buffer, and specify its memory layout 19 | OpDecorate %7 Block 20 | OpMemberDecorate %7 0 Offset 0 21 | OpMemberDecorate %7 1 Offset 16 22 | 23 | ; Assign set/binding decorations to the uniform buffer variable 24 | OpDecorate %17 DescriptorSet 2 25 | OpDecorate %17 Binding 3 26 | 27 | ; the `float` type 28 | %5 = OpTypeFloat 32 29 | 30 | ; the `vec4` type 31 | %6 = OpTypeVector %5 4 32 | 33 | ; the `{float, vec4}` struct type 34 | %7 = OpTypeStruct %5 %6 35 | 36 | ; the `int` type 37 | %8 = OpTypeInt 32 1 38 | 39 | ; the `bool` type 40 | %9 = OpTypeBool 41 | 42 | ; in float fancy_attribute; 43 | %10 = OpTypePointer Input %5 44 | %11 = OpVariable %10 Input 45 | 46 | ; out float useful_output; 47 | %12 = OpTypePointer Output %5 48 | %13 = OpVariable %12 Output 49 | 50 | ; out vec4 crucial_data; 51 | %14 = OpTypePointer Output %6 52 | %15 = OpVariable %14 Output 53 | 54 | ; uniform object_properties { ... } props; 55 | %16 = OpTypePointer Uniform %7 56 | %17 = OpVariable %16 Uniform 57 | 58 | ; Uniform pointer to `float` and `vec4` 59 | %18 = OpTypePointer Uniform %5 60 | %19 = OpTypePointer Uniform %6 61 | 62 | ; The integer constants 0 and 1 63 | %20 = OpConstant %8 0 64 | %21 = OpConstant %8 1 65 | 66 | ; The float constants 0, 1.0 and vec4(0) 67 | %22 = OpConstantNull %5 68 | %23 = OpConstant %5 1.0 69 | %24 = OpConstantNull %6 70 | 71 | %1 = OpTypeVoid 72 | %2 = OpTypeFunction %1 73 | %3 = OpFunction %1 None %2 74 | %4 = OpLabel 75 | 76 | ; load fancy_attribute 77 | %25 = OpLoad %5 %11 78 | 79 | ; load props.threshold 80 | %26 = OpAccessChain %18 %17 %20 81 | %27 = OpLoad %5 %26 82 | 83 | ; fancy_attribute < props.threshold 84 | %28 = OpFOrdLessThan %9 %25 %27 85 | 86 | ; if (fancy_attribute < props.threshold) jump %33; else jump %30; 87 | OpSelectionMerge %33 None 88 | OpBranchConditional %28 %33 %30 89 | 90 | 91 | ; the false block 92 | %30 = OpLabel 93 | 94 | ; load props.highly_exclusive_bits; 95 | %31 = OpAccessChain %19 %17 %21 96 | %32 = OpLoad %6 %31 97 | 98 | ; End the block 99 | OpBranch %33 100 | 101 | 102 | ; the merge block 103 | %33 = OpLabel 104 | 105 | ; useful_output = 106 | ; 107 | ; If the true path was taken: %22 which is 0 108 | ; If the false path was taken: %23 which is 1 109 | ; 110 | %34 = OpPhi %5 %22 %4 %23 %30 111 | 112 | ; crucial_data = 113 | ; 114 | ; If the true path was taken: %24 which is vec4(0) 115 | ; If the false path was taken: %32 which is props.highly_exclusive_bits 116 | ; 117 | %35 = OpPhi %6 %24 %4 %32 %30 118 | 119 | OpStore %13 %34 120 | OpStore %15 %35 121 | 122 | OpReturn 123 | OpFunctionEnd 124 | -------------------------------------------------------------------------------- /12 - The Phi Function/validate: -------------------------------------------------------------------------------- 1 | ../scripts/validate -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to Shahbaz Youssefi , the 73 | Project Steward(s) for spirv-tutor. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 94 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPIR-V Tutorials 2 | 3 | This is a repository of SPIR-V tutorials starting from the very basics. It is expected that the 4 | reader is familiar with basics of Vulkan GLSL. 5 | 6 | This is not an officially supported Google product. 7 | 8 | ## Initial Setup 9 | 10 | These tutorials are currently only supported on Linux. Please run `./scripts/setup` for initial 11 | setup. It is expected that `spirv-as` and `spirv-val` are installed on the system (usually in a 12 | `spirv-tools` package). Additionally, it is recommended to have the `highlight` command installed 13 | (from a package with the same name). 14 | 15 | ## How to Follow the Tutorials 16 | 17 | Navigate to the directory of each tutorial and follow the instructions in the `README.md` there. 18 | The tutorial directories are tagged with a number to indicate the intended order, for example: 19 | 20 | 01 - Introduction to SPIR-V/ 21 | 02 - My First Change/ 22 | 03 - My First Variable/ 23 | 24 | In the tutorials, `README.md` will generally introduce a new set of SPIR-V instructions and include 25 | an exercise to modify a provided SPIR-V by hand and verify the results. Solutions are provided, but 26 | it's strongly recommended not to be consulted until the exercise is complete. 27 | 28 | **Warning:** passively reading the tutorials will quickly become boring. Do get your hands dirty. 29 | 30 | When opening the SPIR-V files, it is recommended to enable some syntax highlighting for readability. 31 | With vim for example, you can install [this excellent plugin][1], otherwise `set ft=lisp` is an 32 | improvement over no highlighting. 33 | 34 | [1]: https://github.com/kbenzie/vim-spirv 35 | 36 | ## Troubleshooting 37 | 38 | If issues with `highlight` are encountered, edit `./scripts/test_spirv`: 39 | 40 | - Try changing `xterm256` to `ansi` if your terminal is not an XTERM. 41 | - Get rid of the `highlight` path and live with monochrome GLSL. 42 | -------------------------------------------------------------------------------- /scripts/id_to_names: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # This script can be used to turn a SPIR-V that uses numerical ids, to one 4 | # where those ids are resolved to friendly names. This can be useful to 5 | # decipher messages from spirv-val when they specify the friendly name only. 6 | 7 | if [ $# -ne 1 ]; then 8 | echo "Usage: $0 " 9 | exit 1 10 | fi 11 | 12 | set -e 13 | 14 | target_env=vulkan1.0 15 | 16 | # Assemble into a temp file 17 | echo "Assembling $1 ..." 18 | as_out="$(mktemp)" 19 | spirv-as "$1" --target-env "$target_env" --preserve-numeric-ids -o "$as_out" 20 | 21 | # Disassmble with friendly names 22 | echo "Disassembling ..." 23 | spirv-dis "$as_out" 24 | -------------------------------------------------------------------------------- /scripts/names_to_id: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # This script can be used to turn a SPIR-V that uses non-numerical ids, to one 4 | # where those ids are resolved to numbers. This can be useful to decipher 5 | # messages from spirv-val when they specify the numeric id only. 6 | 7 | if [ $# -ne 1 ]; then 8 | echo "Usage: $0 " 9 | exit 1 10 | fi 11 | 12 | set -e 13 | 14 | target_env=vulkan1.0 15 | 16 | # Assemble into a temp file 17 | echo "Assembling $1 ..." 18 | as_out="$(mktemp)" 19 | spirv-as "$1" --target-env "$target_env" --preserve-numeric-ids -o "$as_out" 20 | 21 | # Disassmble with raw ids 22 | echo "Disassembling ..." 23 | spirv-dis --raw-id "$as_out" 24 | -------------------------------------------------------------------------------- /scripts/setup: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | root_dir="$(dirname "${BASH_SOURCE[0]}")/.." 4 | build_dir="$root_dir/build/" 5 | spirv_cross_relative_to_build_dir="../third_party/spirv-cross/" 6 | 7 | echo "Initializing submodules ..." 8 | git submodule init 9 | git submodule update 10 | 11 | echo "Building SPIR-V Cross ..." 12 | rm -rf "$build_dir" 13 | mkdir "$build_dir" 14 | pushd "$build_dir" > /dev/null 15 | cmake "$spirv_cross_relative_to_build_dir" 16 | make -j 17 | popd > /dev/null 18 | 19 | echo "" 20 | echo "NOTE:" 21 | echo "Make sure to have spirv-as(1) and spirv-val(1) installed." 22 | echo "Additionally, highlight(1) is suggested." 23 | echo "" 24 | echo " - On a debian-based system:" 25 | echo " \$ sudo apt install spirv-tools highlight" 26 | echo "" 27 | -------------------------------------------------------------------------------- /scripts/test_spirv: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # 3 | # This script is used by the tutorials to assemble a hand-crafted SPIR-V, 4 | # validate it and generate the corresponding GLSL code. 5 | 6 | if [ $# -ne 1 ]; then 7 | echo "Usage: $0 " 8 | exit 1 9 | fi 10 | 11 | set -e 12 | 13 | target_env=vulkan1.0 14 | build_dir="$(dirname "${BASH_SOURCE[0]}")/../build" 15 | 16 | # Assemble into a temp file 17 | echo "Assembling $1 ..." 18 | as_out="$(mktemp)" 19 | spirv-as "$1" --target-env "$target_env" --preserve-numeric-ids -o "$as_out" 20 | 21 | # Validate it 22 | echo "Validating ..." 23 | spirv-val "$as_out" --target-env "$target_env" 24 | 25 | # Generate glsl into a temp file. If highlight is available, print it with 26 | # pretty colors. 27 | # 28 | # NOTE: having trouble with color? Try changing xterm256 to ansi. 29 | echo "Generating GLSL ..." 30 | glsl_out="$(mktemp)" 31 | "$build_dir"/spirv-cross --vulkan-semantics "$as_out" --output "$glsl_out" 32 | if which highlight > /dev/null; then 33 | cat "$glsl_out" | highlight --syntax c -O xterm256 --style darkness 34 | else 35 | cat "$glsl_out" 36 | fi 37 | -------------------------------------------------------------------------------- /scripts/validate: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | exercise_dir="$(dirname "${BASH_SOURCE[0]}")" 4 | 5 | "$exercise_dir"/../scripts/test_spirv "$exercise_dir"/exercise.spvasm 6 | --------------------------------------------------------------------------------