From 59bc05774b85c0fb355c5c33780f2b4fa0fd2eee Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Thu, 16 Feb 2023 14:52:43 -0500 Subject: [PATCH 1/3] [impeller] support generating mip-maps on Vulkan fix: https://github.com/flutter/flutter/issues/120134 --- .../backend/vulkan/blit_command_vk.cc | 112 +++++++++++++++++- .../renderer/backend/vulkan/commands_vk.cc | 10 +- .../renderer/backend/vulkan/commands_vk.h | 4 +- .../renderer/backend/vulkan/render_pass_vk.cc | 27 +++-- .../renderer/backend/vulkan/render_pass_vk.h | 3 +- 5 files changed, 134 insertions(+), 22 deletions(-) diff --git a/impeller/renderer/backend/vulkan/blit_command_vk.cc b/impeller/renderer/backend/vulkan/blit_command_vk.cc index 8a9dd4ca9a208..e62ccf3309f5d 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,102 @@ 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->GetSingleUseChild(); + + for (uint32_t i = 1; i < mip_count; i++) { + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = vk::ImageLayout::eUndefined; + 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::eTransferSrcOptimal; + 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::eTransferSrcOptimal; + 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..5b561cfea384c 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,6 +72,7 @@ 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); command_buffer_->GetDeletionQueue()->Push( @@ -80,7 +82,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { // layout transition. if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined, - vk::ImageLayout::eColorAttachmentOptimal)) { + vk::ImageLayout::eColorAttachmentOptimal, + size.MipCount())) { return false; } @@ -88,7 +91,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 +126,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { } if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined, - vk::ImageLayout::eColorAttachmentOptimal)) { + vk::ImageLayout::eColorAttachmentOptimal, + size.MipCount())) { return false; } @@ -315,18 +318,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 +398,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; From 8224dfa8e359a08cbea3b306394f43e80bd5f5f5 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Thu, 16 Feb 2023 15:51:09 -0500 Subject: [PATCH 2/3] various bug fixes --- .../renderer/backend/vulkan/allocator_vk.cc | 1 + .../backend/vulkan/blit_command_vk.cc | 31 ++++++++++++++++--- .../renderer/backend/vulkan/render_pass_vk.cc | 5 +-- .../platform/android/platform_view_android.cc | 2 +- 4 files changed, 32 insertions(+), 7 deletions(-) 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 e62ccf3309f5d..bde1d0338c394 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk.cc @@ -201,11 +201,34 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const { barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); barrier.setSubresourceRange(subresource_range); - auto gen_mip_cmd = fenced_command_buffer->GetSingleUseChild(); + 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::eUndefined; + barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal; barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal; barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead; @@ -238,7 +261,7 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const { blit, vk::Filter::eLinear); // transition the previous mip level to shader read only optimal - barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal; + barrier.oldLayout = vk::ImageLayout::eUndefined; barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead; barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; @@ -258,7 +281,7 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const { // transition the last mip level to shader read only optimal barrier.subresourceRange.baseMipLevel = mip_count - 1; - barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal; + barrier.oldLayout = vk::ImageLayout::eUndefined; barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal; barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead; barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead; diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index 5b561cfea384c..2bd665cfc7f92 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -74,6 +74,7 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { 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]() { @@ -83,7 +84,7 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { // layout transition. if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal, - size.MipCount())) { + mip_count)) { return false; } @@ -127,7 +128,7 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const { if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal, - size.MipCount())) { + mip_count)) { return false; } diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 1b9d440a2d4bd..d70c79211ed8f 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -47,7 +47,7 @@ std::unique_ptr AndroidSurfaceFactoryImpl::CreateSurface() { case AndroidRenderingAPI::kOpenGLES: if (enable_impeller_) { // TODO(kaushikiska@): Enable this after wiring a preference for Vulkan backend. -#if false +#if true return std::make_unique(android_context_, jni_facade_); From 97bcbf9a9e331f871c0d5b58339a1d7fd38d6d81 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Fri, 17 Feb 2023 13:02:53 -0500 Subject: [PATCH 3/3] revert to gl surface --- shell/platform/android/platform_view_android.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index d70c79211ed8f..1b9d440a2d4bd 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -47,7 +47,7 @@ std::unique_ptr AndroidSurfaceFactoryImpl::CreateSurface() { case AndroidRenderingAPI::kOpenGLES: if (enable_impeller_) { // TODO(kaushikiska@): Enable this after wiring a preference for Vulkan backend. -#if true +#if false return std::make_unique(android_context_, jni_facade_);