Vulkan on Android 4 — Model Rendering Part 7

Jin-Long Wu
5 min readDec 1, 2018

--

Photo by Samuel Sianipar on Unsplash

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 VkDescriptorSetLayoutBindingstructure. 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 bindings accord with the binding numbers we’ll use in the shaders and dstBindings of vkWriteDescriptorSets 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

just a relationship not the case of our program

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:

  1. topology: VK_PRIMITIVE_TOPOLOGY_TRIANNGLE_LIST as GL_TRIANGLES.
  2. primitiveRestartEnable1 controls whether a special vertex index value is treated as restarting the assembly of primitives and is the same with OpenGL does.
  3. 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.

--

--