Vulkan on Android 4 — Model Rendering Part 4

Jin-Long Wu
4 min readNov 7, 2018

--

mipmap level 0 ~ 3

Today we’ll complement the missing part from the last article, mipmap.

Mipmap

A mipmap is pre-calculated sequences of images, each of which in the mipmap is a power of two smaller than the previous level. For example, for a 32 x 8 image it could generate mipmaps for 16 x 4, 8 x 2, 4 x 1, 2 x 1, 1 x 1. Note it is not necessary that mipmaps keep the aspect ratio of the original image.

If we can recall we set up magFilter and minFilter for sampler creation, which specifies how Vulkan applies filtering when a texture is greater or smaller in size than geometry. The best result of texture mapping comes out of the size of the texture being exactly matched with the size of geometry since we could almost map 1 texel to 1 pixel, no filtering is needed.

Workflow

The method I found is from this great tutorial.

After we have prepared the image at the end of BuildTexture2D, call GenerateMipmap to generates a mipmap for the image. First, it queries VkFormatProperties with vkGetPhysicalDeviceFormatProperties of the specific format. Based on if optimalTilingFeatures contains VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR to set samplerMipmapFormat to VK_SAMPLER_MIPMAP_MODE_LINEAR, or VK_SAMPLER_MIPMAP_MODE_NEAREST, otherwise.

And for each mipmap level other than 0, we blit down-scaled level 0 image to level 1 image, down-scaled level 1 image to level 2 image, and on and on till the last level. Mipmap level i is the source of mipmap level i+1 so we include VK_IMAGE_USAGE_TRANSFER_SRC_BIT additionally in CreateTexture2D.

To go into detail, for example, if we have a 16 x 16 image, CreateTexure2D uses information of the image loaded to create a VkImage and AllocateTexture allocates space for it, including the space the mipmap requires.

Next, we transition the image layout from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL in order for the buffer to copy pixel data to the image.

Now we copy the pixel data from the buffer to the image(mipmap level 0).

In the first round of the loop, we transition the layout of the mipmap level 0 image from VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL to VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL with vkCmdPipelineBarrier, because it is the source of the bliting operation, which is also a transfer operation.

Second, blit the pixel data of mipmap level 0 image to level 1 image with vkCmdBlitImage.

Third, transition the layout of the mipmap level 0 image from VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL. And now, the image of mipmap level 0 is ready for a fragment shader to access.

And proceeds the subsequent mipmap level images in the same way.

And the last vkCmdPipelineBarrier outside the loop is for the image of mipmap level 5 to transition its layout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL. It doesn’t need to transition to VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL first as it is not going to blit its content to another image.

Test

We can tackle the field minLod and maxLod of VkSamplerCreateInfo to see different mipmap levels on the model. For instance, the title picture is the animation of mipmap levels applied from 0 ~ 3.

--

--