Vulkan on Android 4 — Model Rendering Part 7
We have not checked all parts of our new scene renderer initialization in the previous article and in this post, we are going to talk about descriptor and graphics pipeline.
Descriptor
A descriptor contains information about a resource or resource view that is accessed by a shader.
Descriptor Set Layout
A descriptor set layout is used when allocating descriptor sets and when creating pipeline layouts(pSetLayouts
in VkPipelineLayoutCreateInfo
) and it defines the set of resources and their relative arrangement through descriptor bindings within a descriptor set. Descriptor set layouts are represented by VkDescriptorSetLayout
handles.
Descriptor Binding
A descriptor binding corresponds to zero or more descriptors of a single descriptor type in a set and is defined by a VkDescriptorSetLayoutBinding
structure. It is specified by a descriptor type, a count (array size) of the number of descriptors in the binding, a set of shader stages that can access the binding, and (if using immutable samplers) an array of sampler descriptors.
The first binding is the model, view, and projection matrices, the second is for the model texture, the third is the normal map texture, and the last one is for lighting position is world space. The binding
s accord with the binding numbers we’ll use in the shaders and dstBinding
s of vkWriteDescriptorSet
s later when we initialize the descriptor set allocated. Here _pipelineLayout
will be used to create a graphics pipeline.
A descriptor set layout example on “Descriptor Set Layout” section on spec is worth a look.
Descriptor Pool
A descriptor pool maintains a pool of descriptors of various kinds and is where descriptor sets are allocated from. Descriptor pools are represented by VkDescriptorPool
handles.
We are intended to allocate one descriptor set so maxSets
is 1, and the descriptor set layout we created before demands two uniform buffers and two image samplers so poolSizes
array must contain sufficient descriptors to allocate one descriptor set of that type of descriptor set layout.
Descriptor Set
A descriptor set object contains storage for a set of descriptors and is where descriptors are written into. It can be bound to a command buffer for shaders to access the descriptors contained within it. Descriptor sets are represented by VkDescriptorSet
handles.
We allocate one descriptor set from the _descriptorPool based on _descriptorSetLayout with vkAllocateDescriptorSets. After allocation, we should initialize the content of the descriptor set so we specify VkBuffers and VkImageViews with VkSamplers. Because we do not have descriptor arrays, dstArrayElement
is simply 0. Finally, stuff all the VkDescriptorBufferInfos and VkDescriptorImageInfos to the VkWriteDescriptorSet, we call vkUpdateDescriptorSets to update the descriptor set.
Relationship Between Them
Graphics Pipeline
Shader Loading
VkGraphicsPipelineCreateInfo
is a structure that takes tons of arguments about pipeline creation, the first one we are talking about is shader.
Our shaders phong_shading.vert
/frag
are located at app/src/main/shaders/
based on the document. And notice the .spv
extension that indicates shaders we load have already compiled to SPIR-V format. VkShaderModule
contains shader code and one or more entry points and VkPipelineShaderStageCreateInfo
has a pName
field which is “main”
in our case to indicate the entry name of the shader modules.
It is just a normal pair of vertex/fragment shader code about Phong shading. What I want to say is binding 0. We can see the data layout of uniform MVP
is identical to that of Model::MVP
in this and we might think the data are laid out exactly as it seems to be. In this case, it does. But it is not always to be and this tutorial gives a clear example and the spec tells what all the rules are. So, the data layout of uniform MVP
is not always matched with Model::MVP
if we add some other fields or become a nested structure that violates the alignment requirement. And, note the binding numbers match the ones in the descriptor set layout.
I want to say something again about shader compiling. Though Android Studio compiles shaders for us, it does not tell what is going on if we have some errors in shader code. It just fails to create a VkShaderModule
. So I recommend we compile shaders before we run the app with glslc
shipped with our NDK installation. In my case, it is located at \Sdk\ndk-bundle\shader-tools\windows-x86_64
under my Android SDK installation directory. The usage is simple:
glslc -c foo.vert
glslc -c foo.frag
The commands create shaders in SPIR-V format, and the Github page covers comprehensive usage about it.
Vertex Input Binding
vertexInputBinding.binding
corresponds to arguments of vkCmdBindVertexBuffers
. The relationship will be more clear when we check the function later. vertexInputBinding.stride
is the distance in bytes between two consecutive elements within the buffer. In our case, it is the summary of the size of bytes of position, normal, uv coordinates, etc.
VkVertexInputAttributeDescription
specifies the attributes of a vertex, that is, their location(should match the location number in shader), binding(0 because we are going to bind the only one vertex buffer), format(we should ignore the letters R, G, and B in VK_FORMAT_R32G32B32_SFLOAT
and VK_FORMAT_R32G32_SFLOAT
. It’s the number, size, and type of components that matter.), and offset in the vertex.
Others
We are about to browse the rest part of the pipeline creation since they are concepts in OpenGL that we are already familiar with. For example:
topology
:VK_PRIMITIVE_TOPOLOGY_TRIANNGLE_LIST
asGL_TRIANGLES
.primitiveRestartEnable1
controls whether a special vertex index value is treated as restarting the assembly of primitives and is the same with OpenGL does.- Viewport, scissor, culling mode, the front-face definition, multi-sampling, and depth test are all the same with what we already know.
Finally, we should destroy the shader modules once we have created the graphics pipelines.
Reference
- https://developer.android.com/ndk/guides/graphics/shader-compilers#asi
- https://vulkan-tutorial.com/Uniform_buffers/Descriptor_pool_and_sets#page_Alignment_requirements
- https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#interfaces-resources-layout
- https://github.com/google/shaderc/tree/master/glslc
- https://www.khronos.org/opengl/wiki/Vertex_Rendering#Primitive_Restart