Day 6 (Dec 19, 2020) ~~~ Creating Shaders for the Vulkan Graphics Pipeline - kwilson33/learning-vulkan GitHub Wiki
Progress Today
I made it through these pages today of the official Vulkan tutorial. I started the Graphics pipeline basics part of Drawing a triangle after a 1 day break.
- Drawing a triangle --> Graphics pipeline basics --> Introduction
- Drawing a triangle --> Graphics pipeline basics --> Shader modules
Here are some major takeaways
1. The graphics pipeline in Vulkan is almost completely immutable, unlike older APIs like OpenGL and Direct3D,
- This means that you will have to create multiple pipelines to perform different combinations of states you want to use. For example, one pipeline may include the
Fragment shaderwhile another won't. - This takes time, but because all the steps are known in advance the driver can optimize it much better/faster (
ahead of time compilationvsjust in time compilation)
2. The graphics pipeline in Vulkan and fixed-function stages vs programmable stages,
Image from the Vulkan tutorial
- The yellow stages, like the
Fragment shaderareprogrammablewhich means you can use your own code to do the operations exactly as you want. - On the other hand, green stages, like
Rasterizationarefixed-functionstages meaning you can tweak their operations a little using parameters, but the way they work is predefined by the Vulkan API.
3. The geometry shader (a programmable stage) is not used much in today's applications.
- The geometry shader is run on every primitive (like a triangle) and can discard it or output more prims than came in.
- This is similar to the
tessellation shader, except it's more flexible. The performance is not very good, and so it's not used often.
4. Shader code in Vulkan has to be specified in a bytecode format called SPIR-V.
- This is different than human-readable syntax like
GLSL(OpenGL shading language) andHLSL(High Level Shading Language used for DirectX) SPIR-Vis designed to be used with both Vulkan and OpenCL. It is used for bothgraphicsandcomputeshaders.- The benefit is that compilers written by GPU vendors to turn shader code into company specific code are way less complex.
- Also, with a more straighforward format, Vulkan uses
SPIR-Vin hopes that shader code is more shareable and not rejected based on different companies. SPIR-Vdoes NOT need to be written by hand. Khronos (the company that makes Vulkan) has their own vendor-independent compiler than compilesGLSLintoSPIR-V. The compiler verifies that theGLSLcode is compliant with theSPIR-Vstandards. There are other compilers that do this, for exampleglslc.exemade by Google. The benefit of using Google's compiler is that it uses the same parameter format as well-known compilers like GCC and Clang, and has some extra functionality like includes. Both these compilers are included in the Vulkan SDK.
5. How the vertex shader (VS) takes in position and normalizes it.
-
The
VSprocesses each incoming vertex. It takes in attributes like world pos, color, normal, and texture coords. -
The output of the
VSis the final pos inclip coordsand the attributes that need to be passed onto thefragment shader. -
A
clip coordis a 4D vector that is then turned into anormalized device coordinateby dividing the whole vector by its last component. -
These
normalized device coordsare homogeneous coordinates, meaning they map the framebuffer to a [-1,1] by [-1, 1] coord system, like below
Image from the Vulkan tutorial.
- For example, a
clip coordin theVSmay look likevec4(1.0, 2.0, 3.0, 1.0)where the first three params are X,Y,Z coords, and the last parameter is the value to divide by to get thenormalized device coord. You can make this value 1.0 to not change anything.
6. With use of different entrypoints, like main, it's possible to combine multiple fragment shaders into one.
- You specify the
entrypointwhen you are specifying the pipeline stage for a shader throughVkPipelineShaderStageCreateInfostructures.
7. One optimization you can make is using the optional pSpecializationInfo field when creating shaders.
- When specifying the pipelne stage for a shader through
VkPipelineShaderStageCreateInfostructures, you can use this field to specify values for shader constants. - This means you can use a single shader module where its behavior can be configured at pipeline creation, kind of like Verilog parameters.
- This is more efficient than configuring the shader using variables at render time, because the compiler can do optimizations like removing
ifstatements that depend on these values.
See ya!
Now that the tutorial is getting into the graphics pipeline things are starting to make more sense. Today was probably the most fun I had with the tutorial, because it was stuff I had a little experience with from my intro to Computer Graphics class. I wrote a little GLSL in that class for shaders and today I learned how to do that (again) and in-depth what everything did (more so than my class). I also learned how to compile the shader code (GLSL) into shader bytecode (SPIR-V). I feel like I'm really learning a lot by going through each step painstakingly slow and it feels really good to make progress. Until tomorrow!