diff --git a/impeller/renderer/backend/vulkan/allocator_vk.cc b/impeller/renderer/backend/vulkan/allocator_vk.cc index 2d0caad238ebe..ae862731b780b 100644 --- a/impeller/renderer/backend/vulkan/allocator_vk.cc +++ b/impeller/renderer/backend/vulkan/allocator_vk.cc @@ -116,6 +116,7 @@ std::shared_ptr AllocatorVK::OnCreateTexture( image_create_info.initialLayout = vk::ImageLayout::eUndefined; image_create_info.usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eColorAttachment | + vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst; VmaAllocationCreateInfo alloc_create_info = {}; diff --git a/impeller/renderer/backend/vulkan/blit_command_vk.cc b/impeller/renderer/backend/vulkan/blit_command_vk.cc index 8a9dd4ca9a208..bde1d0338c394 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk.cc @@ -59,10 +59,13 @@ std::string BlitCopyTextureToTextureCommandVK::GetLabel() const { return false; } + uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count; + // transition the source image to transfer source optimal TransitionImageLayoutCommandVK transition_source_cmd = TransitionImageLayoutCommandVK(source_image, vk::ImageLayout::eUndefined, - vk::ImageLayout::eTransferSrcOptimal); + vk::ImageLayout::eTransferSrcOptimal, + mip_count); bool success = transition_source_cmd.Submit(fenced_command_buffer); if (!success) { VALIDATION_LOG << "Failed to transition source image layout"; @@ -72,7 +75,8 @@ std::string BlitCopyTextureToTextureCommandVK::GetLabel() const { // transition the destination image to transfer destination optimal TransitionImageLayoutCommandVK transition_dest_cmd = TransitionImageLayoutCommandVK(dest_image, vk::ImageLayout::eUndefined, - vk::ImageLayout::eTransferDstOptimal); + vk::ImageLayout::eTransferDstOptimal, + mip_count); success = transition_dest_cmd.Submit(fenced_command_buffer); if (!success) { VALIDATION_LOG << "Failed to transition destination image layout"; @@ -127,10 +131,13 @@ std::string BlitCopyTextureToBufferCommandVK::GetLabel() const { image_copy.setImageExtent( vk::Extent3D(source_region.size.width, source_region.size.height, 1)); + uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count; + // transition the source image to transfer source optimal TransitionImageLayoutCommandVK transition_source_cmd = TransitionImageLayoutCommandVK(source_image, vk::ImageLayout::eUndefined, - vk::ImageLayout::eTransferSrcOptimal); + vk::ImageLayout::eTransferSrcOptimal, + mip_count); bool success = transition_source_cmd.Submit(fenced_command_buffer); if (!success) { return false; @@ -171,9 +178,125 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const { [[nodiscard]] bool BlitGenerateMipmapCommandVK::Encode( FencedCommandBufferVK* fenced_command_buffer) const { - // TODO(https://github.com/flutter/flutter/issues/120134): Support generating - // mipmaps on Vulkan. - IMPELLER_UNIMPLEMENTED; + const auto& source_tex_vk = TextureVK::Cast(*texture); + const auto source_image = source_tex_vk.GetImage(); + + const auto size = source_tex_vk.GetTextureDescriptor().size; + uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count; + + uint32_t mip_width = size.width; + uint32_t mip_height = size.height; + + // create the subresource range + vk::ImageSubresourceRange subresource_range{}; + subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor); + subresource_range.setBaseArrayLayer(0); + subresource_range.setLayerCount(1); + subresource_range.setLevelCount(1); + + // create a barrier to transition the image to transfer source optimal + vk::ImageMemoryBarrier barrier{}; + barrier.setImage(source_image); + barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + barrier.setSubresourceRange(subresource_range); + + auto gen_mip_cmd = fenced_command_buffer->Get(); + + vk::CommandBufferBeginInfo begin_info; + begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + auto begin_res = gen_mip_cmd.begin(begin_info); + + if (begin_res != vk::Result::eSuccess) { + VALIDATION_LOG << "Failed to begin command buffer: " + << vk::to_string(begin_res); + return false; + } + + // transition all layers to transfer dst optimal + for (uint32_t i = 0; i < mip_count; i++) { + barrier.subresourceRange.baseMipLevel = i; + barrier.oldLayout = vk::ImageLayout::eUndefined; + barrier.newLayout = vk::ImageLayout::eTransferDstOptimal; + barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; + barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite; + + gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eTransfer, {}, + nullptr, nullptr, barrier); + } + + for (uint32_t i = 1; i < mip_count; i++) { + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal; + barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal; + barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; + barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead; + + gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eTransfer, {}, + nullptr, nullptr, barrier); + + vk::ImageBlit blit{}; + + // src + blit.srcOffsets[0] = vk::Offset3D(0, 0, 0); + blit.srcOffsets[1] = vk::Offset3D(mip_width, mip_height, 1); + blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + + // dst + blit.dstOffsets[0] = vk::Offset3D(0, 0, 0); + blit.dstOffsets[1] = vk::Offset3D(mip_width > 1 ? mip_width / 2 : 1, + mip_height > 1 ? mip_height / 2 : 1, 1); + blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + gen_mip_cmd.blitImage(source_image, vk::ImageLayout::eTransferSrcOptimal, + source_image, vk::ImageLayout::eTransferDstOptimal, + blit, vk::Filter::eLinear); + + // transition the previous mip level to shader read only optimal + barrier.oldLayout = vk::ImageLayout::eUndefined; + barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; + barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead; + barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; + + gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eFragmentShader, {}, + nullptr, nullptr, barrier); + + if (mip_width > 1) { + mip_width /= 2; + } + + if (mip_height > 1) { + mip_height /= 2; + } + } + + // transition the last mip level to shader read only optimal + barrier.subresourceRange.baseMipLevel = mip_count - 1; + barrier.oldLayout = vk::ImageLayout::eUndefined; + barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; + barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead; + barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; + + gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eFragmentShader, {}, + nullptr, nullptr, barrier); + + // submit the command buffer + auto res = gen_mip_cmd.end(); + if (res != vk::Result::eSuccess) { + VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res); + return false; + } + return true; } diff --git a/impeller/renderer/backend/vulkan/commands_vk.cc b/impeller/renderer/backend/vulkan/commands_vk.cc index 6eab7785cf2af..eda6462d76c7b 100644 --- a/impeller/renderer/backend/vulkan/commands_vk.cc +++ b/impeller/renderer/backend/vulkan/commands_vk.cc @@ -9,8 +9,12 @@ namespace impeller { TransitionImageLayoutCommandVK::TransitionImageLayoutCommandVK( vk::Image image, vk::ImageLayout old_layout, - vk::ImageLayout new_layout) - : image_(image), old_layout_(old_layout), new_layout_(new_layout) {} + vk::ImageLayout new_layout, + uint32_t mip_levels) + : image_(image), + old_layout_(old_layout), + new_layout_(new_layout), + mip_levels_(mip_levels) {} TransitionImageLayoutCommandVK::~TransitionImageLayoutCommandVK() = default; @@ -35,7 +39,7 @@ bool TransitionImageLayoutCommandVK::Submit( vk::ImageSubresourceRange() .setAspectMask(vk::ImageAspectFlagBits::eColor) .setBaseMipLevel(0) - .setLevelCount(1) + .setLevelCount(mip_levels_) .setBaseArrayLayer(0) .setLayerCount(1)); diff --git a/impeller/renderer/backend/vulkan/commands_vk.h b/impeller/renderer/backend/vulkan/commands_vk.h index 31c1c12285650..7bbd60ed722ba 100644 --- a/impeller/renderer/backend/vulkan/commands_vk.h +++ b/impeller/renderer/backend/vulkan/commands_vk.h @@ -14,7 +14,8 @@ class TransitionImageLayoutCommandVK { public: TransitionImageLayoutCommandVK(vk::Image image, vk::ImageLayout old_layout, - vk::ImageLayout new_layout); + vk::ImageLayout new_layout, + uint32_t mip_levels); ~TransitionImageLayoutCommandVK(); @@ -24,6 +25,7 @@ class TransitionImageLayoutCommandVK { vk::Image image_; vk::ImageLayout old_layout_; vk::ImageLayout new_layout_; + uint32_t mip_levels_; FML_DISALLOW_COPY_AND_ASSIGN(TransitionImageLayoutCommandVK); }; diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index 720ecf9159fd8..2bd665cfc7f92 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/vulkan/render_pass_vk.h" #include +#include #include #include "fml/logging.h" @@ -71,7 +72,9 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { const auto& stencil0 = render_target.GetStencilAttachment(); auto& texture = TextureVK::Cast(*color0.texture); + const auto& size = texture.GetTextureDescriptor().size; vk::Framebuffer framebuffer = CreateFrameBuffer(texture); + uint32_t mip_count = texture.GetTextureDescriptor().mip_count; command_buffer_->GetDeletionQueue()->Push( [device = device_, fbo = framebuffer]() { @@ -80,7 +83,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { // layout transition. if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined, - vk::ImageLayout::eColorAttachmentOptimal)) { + vk::ImageLayout::eColorAttachmentOptimal, + mip_count)) { return false; } @@ -88,7 +92,6 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { clear_value.color = vk::ClearColorValue(std::array{0.0f, 0.0f, 0.0, 0.0f}); - const auto& size = texture.GetTextureDescriptor().size; vk::Rect2D render_area = vk::Rect2D() .setOffset(vk::Offset2D(0, 0)) @@ -124,7 +127,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { } if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined, - vk::ImageLayout::eColorAttachmentOptimal)) { + vk::ImageLayout::eColorAttachmentOptimal, + mip_count)) { return false; } @@ -315,18 +319,19 @@ bool RenderPassVK::UpdateDescriptorSets(const char* label, const SamplerVK& sampler_vk = SamplerVK::Cast(sampler); const SampledImageSlot& slot = bindings.sampled_images.at(index); + uint32_t mip_levels = texture_vk.GetTextureDescriptor().mip_count; - if (!TransitionImageLayout(texture_vk.GetImage(), - vk::ImageLayout::eUndefined, - vk::ImageLayout::eTransferDstOptimal)) { + if (!TransitionImageLayout( + texture_vk.GetImage(), vk::ImageLayout::eUndefined, + vk::ImageLayout::eTransferDstOptimal, mip_levels)) { return false; } CopyBufferToImage(texture_vk); - if (!TransitionImageLayout(texture_vk.GetImage(), - vk::ImageLayout::eTransferDstOptimal, - vk::ImageLayout::eShaderReadOnlyOptimal)) { + if (!TransitionImageLayout( + texture_vk.GetImage(), vk::ImageLayout::eTransferDstOptimal, + vk::ImageLayout::eShaderReadOnlyOptimal, mip_levels)) { return false; } @@ -394,9 +399,10 @@ vk::Framebuffer RenderPassVK::CreateFrameBuffer( bool RenderPassVK::TransitionImageLayout(vk::Image image, vk::ImageLayout layout_old, - vk::ImageLayout layout_new) const { + vk::ImageLayout layout_new, + uint32_t mip_levels) const { auto transition_cmd = - TransitionImageLayoutCommandVK(image, layout_old, layout_new); + TransitionImageLayoutCommandVK(image, layout_old, layout_new, mip_levels); return transition_cmd.Submit(command_buffer_.get()); } diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.h b/impeller/renderer/backend/vulkan/render_pass_vk.h index 811267e41021a..4955a83ad6307 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.h +++ b/impeller/renderer/backend/vulkan/render_pass_vk.h @@ -65,7 +65,8 @@ class RenderPassVK final : public RenderPass { bool TransitionImageLayout(vk::Image image, vk::ImageLayout layout_old, - vk::ImageLayout layout_new) const; + vk::ImageLayout layout_new, + uint32_t mip_levels) const; bool CopyBufferToImage(const TextureVK& texture_vk) const;