Generating mipmaps in Vulkan

2019-07-04 17:19发布

问题:

I'm trying to generate mipmaps runtime (implement glGenerateMipmap() functionality) using vkCmdBlitImage but don't understand the process. Here's my code so far:

VkImageCreateInfo imageCreateInfo = {};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.pNext = nullptr;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.format = format;
imageCreateInfo.mipLevels = mipLevels;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
imageCreateInfo.extent = { static_cast<std::uint32_t>( width ), static_cast<std::uint32_t>( height ), 1 };
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;

err = vkCreateImage( GfxDeviceGlobal::device, &imageCreateInfo, nullptr, &image );

...

    for (int i = 1; i < mipLevels; ++i)
    {
        const std::int32_t mipWidth = width >> i;
        const std::int32_t mipHeight = height >> i;

        VkImageBlit imageBlit = {};
        imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        imageBlit.srcSubresource.baseArrayLayer = 0;
        imageBlit.srcSubresource.layerCount = 1;
        imageBlit.srcSubresource.mipLevel = 0;
        imageBlit.srcOffsets[ 0 ] = { 0, 0, 0 };
        imageBlit.srcOffsets[ 1 ] = { width, height, 1 };

        imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        imageBlit.dstSubresource.baseArrayLayer = 0;
        imageBlit.dstSubresource.layerCount = 1;
        imageBlit.dstSubresource.mipLevel = i;
        imageBlit.dstOffsets[ 0 ] = { 0, 0, 0 };
        imageBlit.dstOffsets[ 1 ] = { mipWidth, mipHeight, 1 };

        vkCmdBlitImage( texCmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
                        image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                        1, &imageBlit, VK_FILTER_LINEAR );
    }

Does each mip level need its own image? How should they be initialized? When should I generate mipmaps when using a staging buffer for texture data?

Edit: I modified the code to only use one image as suggested and it seems to work now (I can see correct mip level images in RenderDoc texture viewer and I don't get any validation errors).

回答1:

Does each mip level need its own image?

No, you don't need a separate image for each mip level. If you create the image for your texture you only need to set mipLevels of the VkImageCreateInfo.

How should they be initialized? When should I generate mipmaps when using a staging buffer for texture data?

This heavily depends on what you want to feed your mips with. Do you just want to load them from a texture file (static mip levels) or do you need to generate them at runtime?

If you only want to load them from a file, don't blit at all. Create a staging buffer (not image), load your texture data into it and do a vkCmdCopyBufferToImage to your image from that buffer.

But can you please add some more detail. I don't see any image layout transitions that may be crucial depending on the implementation you're on, there is no info on the image create info (flags e.g.) and you don't state if your code actually works or not.

One thing that could be wrong (hard to tell without a bit more code) is the mipLevel in your blit source. Do you really have a source that has the complete mip chain and copy mip by mip?