-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] generate mip level N from N-1 in Vulkan backend. #51749
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,11 +6,46 @@ | |
|
|
||
| #include <cstdint> | ||
|
|
||
| #include "impeller/renderer/backend/vulkan/barrier_vk.h" | ||
| #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" | ||
| #include "impeller/renderer/backend/vulkan/texture_vk.h" | ||
| #include "vulkan/vulkan_core.h" | ||
| #include "vulkan/vulkan_enums.hpp" | ||
| #include "vulkan/vulkan_structs.hpp" | ||
|
|
||
| namespace impeller { | ||
|
|
||
| static void InsertImageMemoryBarrier(const vk::CommandBuffer& cmd, | ||
| const vk::Image& image, | ||
| vk::AccessFlags src_access_mask, | ||
| vk::AccessFlags dst_access_mask, | ||
| vk::ImageLayout old_layout, | ||
| vk::ImageLayout new_layout, | ||
| vk::PipelineStageFlags src_stage, | ||
| vk::PipelineStageFlags dst_stage, | ||
| uint32_t base_mip_level, | ||
| uint32_t mip_level_count = 1u) { | ||
| if (old_layout == new_layout) { | ||
| return; | ||
| } | ||
|
|
||
| vk::ImageMemoryBarrier barrier; | ||
| barrier.srcAccessMask = src_access_mask; | ||
| barrier.dstAccessMask = dst_access_mask; | ||
| barrier.oldLayout = old_layout; | ||
| barrier.newLayout = new_layout; | ||
| barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
| barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
| barrier.image = image; | ||
| barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; | ||
| barrier.subresourceRange.baseMipLevel = base_mip_level; | ||
| barrier.subresourceRange.levelCount = mip_level_count; | ||
| barrier.subresourceRange.baseArrayLayer = 0u; | ||
| barrier.subresourceRange.layerCount = 1u; | ||
|
|
||
| cmd.pipelineBarrier(src_stage, dst_stage, {}, nullptr, nullptr, barrier); | ||
| } | ||
|
|
||
| BlitEncodeVK::~BlitEncodeVK() = default; | ||
|
|
||
| //------------------------------------------------------------------------------ | ||
|
|
@@ -240,37 +275,6 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const { | |
| return label; | ||
| } | ||
|
|
||
| static void InsertImageMemoryBarrier(const vk::CommandBuffer& cmd, | ||
| const vk::Image& image, | ||
| vk::AccessFlags src_access_mask, | ||
| vk::AccessFlags dst_access_mask, | ||
| vk::ImageLayout old_layout, | ||
| vk::ImageLayout new_layout, | ||
| vk::PipelineStageFlags src_stage, | ||
| vk::PipelineStageFlags dst_stage, | ||
| uint32_t base_mip_level, | ||
| uint32_t mip_level_count = 1u) { | ||
| if (old_layout == new_layout) { | ||
| return; | ||
| } | ||
|
|
||
| vk::ImageMemoryBarrier barrier; | ||
| barrier.srcAccessMask = src_access_mask; | ||
| barrier.dstAccessMask = dst_access_mask; | ||
| barrier.oldLayout = old_layout; | ||
| barrier.newLayout = new_layout; | ||
| barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
| barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
| barrier.image = image; | ||
| barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; | ||
| barrier.subresourceRange.baseMipLevel = base_mip_level; | ||
| barrier.subresourceRange.levelCount = mip_level_count; | ||
| barrier.subresourceRange.baseArrayLayer = 0u; | ||
| barrier.subresourceRange.layerCount = 1u; | ||
|
|
||
| cmd.pipelineBarrier(src_stage, dst_stage, {}, nullptr, nullptr, barrier); | ||
| } | ||
|
|
||
| bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const { | ||
| auto& src = TextureVK::Cast(*texture); | ||
|
|
||
|
|
@@ -288,55 +292,73 @@ bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const { | |
| return false; | ||
| } | ||
|
|
||
| // Transition the base mip level to transfer-src layout so we can read from | ||
| // it and transition the rest to dst-optimal since they are going to be | ||
| // written to. | ||
| // Initialize all mip levels to be in TransferDst mode. Later, in a loop, | ||
| // after writing to that mip level, we'll first switch its layout to | ||
| // TransferSrc to prepare the mip level after it, use the image as the source | ||
| // of the blit, before finally switching it to ShaderReadOnly so its available | ||
| // for sampling in a shader. | ||
| InsertImageMemoryBarrier( | ||
| cmd, // command buffer | ||
| image, // image | ||
| vk::AccessFlagBits::eTransferWrite, // src access mask | ||
| vk::AccessFlagBits::eTransferRead, // dst access mask | ||
| src.GetLayout(), // old layout | ||
| vk::ImageLayout::eTransferSrcOptimal, // new layout | ||
| vk::PipelineStageFlagBits::eTransfer, // src stage | ||
| vk::PipelineStageFlagBits::eTransfer, // dst stage | ||
| 0u // mip level | ||
| ); | ||
| InsertImageMemoryBarrier( | ||
| cmd, // command buffer | ||
| image, // image | ||
| {}, // src access mask | ||
| vk::AccessFlagBits::eTransferWrite, // dst access mask | ||
| vk::ImageLayout::eUndefined, // old layout | ||
| vk::ImageLayout::eTransferDstOptimal, // new layout | ||
| vk::PipelineStageFlagBits::eTransfer, // src stage | ||
| vk::PipelineStageFlagBits::eTransfer, // dst stage | ||
| 1u, // mip level | ||
| mip_count - 1 // mip level count | ||
| 0u, // mip level | ||
| mip_count // mip level count | ||
| ); | ||
|
|
||
| // Blit from the base mip level to all other levels. | ||
| vk::ImageMemoryBarrier barrier; | ||
| barrier.image = image; | ||
| barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
| barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | ||
| barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; | ||
| barrier.subresourceRange.baseArrayLayer = 0; | ||
| barrier.subresourceRange.layerCount = 1; | ||
| barrier.subresourceRange.levelCount = 1; | ||
|
|
||
| // Blit from the mip level N - 1 to mip level N. | ||
| size_t width = size.width; | ||
| size_t height = size.height; | ||
| for (size_t mip_level = 1u; mip_level < mip_count; mip_level++) { | ||
| vk::ImageBlit blit; | ||
| barrier.subresourceRange.baseMipLevel = mip_level - 1; | ||
| barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal; | ||
| barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal; | ||
| barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; | ||
| barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead; | ||
|
|
||
| // We just finished writing to the previous (N-1) mip level or it was the | ||
| // base mip level. These were initialized to TransferDst earler. We are now | ||
| // going to read from it to write to the current level (N) . So it must be | ||
| // converted to TransferSrc. | ||
| cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to make this easier to read, add a comment about why the barrier is necessary. I'll attempt to jot down how I read it but please proofread.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment Suggestion:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, | ||
| {barrier}); | ||
|
|
||
| vk::ImageBlit blit; | ||
| blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; | ||
| blit.srcSubresource.baseArrayLayer = 0u; | ||
| blit.srcSubresource.layerCount = 1u; | ||
| blit.srcSubresource.mipLevel = 0u; | ||
| blit.srcSubresource.mipLevel = mip_level - 1; | ||
|
|
||
| blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; | ||
| blit.dstSubresource.baseArrayLayer = 0u; | ||
| blit.dstSubresource.layerCount = 1u; | ||
| blit.dstSubresource.mipLevel = mip_level; | ||
|
|
||
| // offsets[0] is origin. | ||
| blit.srcOffsets[1].x = size.width; | ||
| blit.srcOffsets[1].y = size.height; | ||
| blit.srcOffsets[1].x = std::max<int32_t>(width, 1u); | ||
| blit.srcOffsets[1].y = std::max<int32_t>(height, 1u); | ||
| blit.srcOffsets[1].z = 1u; | ||
|
|
||
| width = width / 2; | ||
| height = height / 2; | ||
|
|
||
| // offsets[0] is origin. | ||
| blit.dstOffsets[1].x = std::max<int32_t>(size.width >> mip_level, 1u); | ||
| blit.dstOffsets[1].y = std::max<int32_t>(size.height >> mip_level, 1u); | ||
| blit.dstOffsets[1].x = std::max<int32_t>(width, 1u); | ||
| blit.dstOffsets[1].y = std::max<int32_t>(height, 1u); | ||
| blit.dstOffsets[1].z = 1u; | ||
|
|
||
| cmd.blitImage(image, // src image | ||
|
|
@@ -347,33 +369,29 @@ bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const { | |
| &blit, // regions | ||
| vk::Filter::eLinear // filter | ||
| ); | ||
|
|
||
| barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal; | ||
| barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; | ||
| barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead; | ||
| barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; | ||
|
|
||
| // Now that the blit is done, the image at the previous level (N-1) | ||
| // is done reading from (TransferSrc)/ Now we must prepare it to be read | ||
| // from a shader (ShaderReadOnly). | ||
| cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment Suggestion:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, | ||
| {barrier}); | ||
| } | ||
|
|
||
| // Transition all mip levels to shader read. The base mip level has a | ||
| // different "old" layout than the rest now. | ||
| InsertImageMemoryBarrier( | ||
| cmd, // command buffer | ||
| image, // image | ||
| vk::AccessFlagBits::eTransferWrite, // src access mask | ||
| vk::AccessFlagBits::eShaderRead, // dst access mask | ||
| vk::ImageLayout::eTransferSrcOptimal, // old layout | ||
| vk::ImageLayout::eShaderReadOnlyOptimal, // new layout | ||
| vk::PipelineStageFlagBits::eTransfer, // src stage | ||
| vk::PipelineStageFlagBits::eFragmentShader, // dst stage | ||
| 0u // mip level | ||
| ); | ||
| InsertImageMemoryBarrier( | ||
| cmd, // command buffer | ||
| image, // image | ||
| vk::AccessFlagBits::eTransferWrite, // src access mask | ||
| vk::AccessFlagBits::eShaderRead, // dst access mask | ||
| vk::ImageLayout::eTransferDstOptimal, // old layout | ||
| vk::ImageLayout::eShaderReadOnlyOptimal, // new layout | ||
| vk::PipelineStageFlagBits::eTransfer, // src stage | ||
| vk::PipelineStageFlagBits::eFragmentShader, // dst stage | ||
| 1u, // mip level | ||
| mip_count - 1 // mip level count | ||
| ); | ||
| barrier.subresourceRange.baseMipLevel = mip_count - 1; | ||
| barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal; | ||
| barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; | ||
| barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; | ||
| barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; | ||
|
|
||
| cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment SUggestion:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, | ||
| {barrier}); | ||
|
|
||
| // We modified the layouts of this image from underneath it. Tell it its new | ||
| // state so it doesn't try to perform redundant transitions under the hood. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment Suggestion:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done