在 Vulkan 中生成 mipmap
Generating mipmaps in Vulkan
我正在尝试使用 vkCmdBlitImage
生成 mipmap 运行时(实现 glGenerateMipmap()
功能),但不了解该过程。到目前为止,这是我的代码:
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 );
}
是否每个 mip 级别都需要自己的图像?它们应该如何初始化?在为纹理数据使用暂存缓冲区时应该何时生成 mipmap?
编辑:我将代码修改为仅按照建议使用一张图像,现在它似乎可以正常工作(我可以在 RenderDoc 纹理查看器中看到正确的 mip 级别图像,并且我没有收到任何验证错误).
Does each mip level need its own image?
不,您不需要为每个 mip 级别使用单独的图像。如果你为你的纹理创建图像,你只需要设置 VkImageCreateInfo 的 mipLevels。
How should they be initialized? When should I generate mipmaps when using a staging buffer for texture data?
这在很大程度上取决于你想用什么来喂养你的 mips。您只想从纹理文件(静态 mip 级别)加载它们,还是需要在运行时生成它们?
如果您只想从文件中加载它们,则完全不要 blit。创建一个暂存缓冲区(不是图像),将纹理数据加载到其中,然后从该缓冲区对图像执行 vkCmdCopyBufferToImage。
但是你能再补充一些细节吗?我没有看到任何图像布局转换可能很重要,具体取决于您所使用的实现,图像创建信息(例如标志)上没有任何信息,并且您没有说明您的代码是否实际工作。
您的 blit 源代码中的 mipLevel 可能是错误的(如果没有更多代码则很难判断)。你真的有完整的mip链和mip复制mip的来源吗?
我正在尝试使用 vkCmdBlitImage
生成 mipmap 运行时(实现 glGenerateMipmap()
功能),但不了解该过程。到目前为止,这是我的代码:
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 );
}
是否每个 mip 级别都需要自己的图像?它们应该如何初始化?在为纹理数据使用暂存缓冲区时应该何时生成 mipmap?
编辑:我将代码修改为仅按照建议使用一张图像,现在它似乎可以正常工作(我可以在 RenderDoc 纹理查看器中看到正确的 mip 级别图像,并且我没有收到任何验证错误).
Does each mip level need its own image?
不,您不需要为每个 mip 级别使用单独的图像。如果你为你的纹理创建图像,你只需要设置 VkImageCreateInfo 的 mipLevels。
How should they be initialized? When should I generate mipmaps when using a staging buffer for texture data?
这在很大程度上取决于你想用什么来喂养你的 mips。您只想从纹理文件(静态 mip 级别)加载它们,还是需要在运行时生成它们?
如果您只想从文件中加载它们,则完全不要 blit。创建一个暂存缓冲区(不是图像),将纹理数据加载到其中,然后从该缓冲区对图像执行 vkCmdCopyBufferToImage。
但是你能再补充一些细节吗?我没有看到任何图像布局转换可能很重要,具体取决于您所使用的实现,图像创建信息(例如标志)上没有任何信息,并且您没有说明您的代码是否实际工作。
您的 blit 源代码中的 mipLevel 可能是错误的(如果没有更多代码则很难判断)。你真的有完整的mip链和mip复制mip的来源吗?