| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/shell/gpu/gpu_surface_vulkan_impeller.h" | |
| #include <memory> | |
| #include "flow/surface_frame.h" | |
| #include "flutter/fml/make_copyable.h" | |
| #include "fml/trace_event.h" | |
| #include "impeller/core/formats.h" | |
| #include "impeller/core/texture_descriptor.h" | |
| #include "impeller/display_list/dl_dispatcher.h" | |
| #include "impeller/renderer/backend/vulkan/command_buffer_vk.h" | |
| #include "impeller/renderer/backend/vulkan/context_vk.h" | |
| #include "impeller/renderer/backend/vulkan/surface_context_vk.h" | |
| #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h" | |
| #include "impeller/renderer/render_target.h" | |
| #include "impeller/renderer/surface.h" | |
| #include "impeller/typographer/backends/skia/typographer_context_skia.h" | |
| namespace flutter { | |
| class WrappedTextureSourceVK : public impeller::TextureSourceVK { | |
| public: | |
| explicit WrappedTextureSourceVK(impeller::vk::Image image, | |
| impeller::vk::ImageView image_view, | |
| impeller::TextureDescriptor desc) | |
| : TextureSourceVK(desc), image_(image), image_view_(image_view) {} | |
| ~WrappedTextureSourceVK() {} | |
| private: | |
| impeller::vk::Image GetImage() const override { return image_; } | |
| impeller::vk::ImageView GetImageView() const override { return image_view_; } | |
| impeller::vk::ImageView GetRenderTargetView() const override { | |
| return image_view_; | |
| } | |
| bool IsSwapchainImage() const override { return true; } | |
| impeller::vk::Image image_; | |
| impeller::vk::ImageView image_view_; | |
| }; | |
| GPUSurfaceVulkanImpeller::GPUSurfaceVulkanImpeller( | |
| GPUSurfaceVulkanDelegate* delegate, | |
| std::shared_ptr<impeller::Context> context) | |
| : delegate_(delegate) { | |
| if (!context || !context->IsValid()) { | |
| return; | |
| } | |
| auto aiks_context = std::make_shared<impeller::AiksContext>( | |
| context, impeller::TypographerContextSkia::Make()); | |
| if (!aiks_context->IsValid()) { | |
| return; | |
| } | |
| impeller_context_ = std::move(context); | |
| aiks_context_ = std::move(aiks_context); | |
| is_valid_ = !!aiks_context_; | |
| } | |
| // |Surface| | |
| GPUSurfaceVulkanImpeller::~GPUSurfaceVulkanImpeller() = default; | |
| // |Surface| | |
| bool GPUSurfaceVulkanImpeller::IsValid() { | |
| return is_valid_; | |
| } | |
| // |Surface| | |
| std::unique_ptr<SurfaceFrame> GPUSurfaceVulkanImpeller::AcquireFrame( | |
| const SkISize& size) { | |
| if (!IsValid()) { | |
| FML_LOG(ERROR) << "Vulkan surface was invalid."; | |
| return nullptr; | |
| } | |
| if (size.isEmpty()) { | |
| FML_LOG(ERROR) << "Vulkan surface was asked for an empty frame."; | |
| return nullptr; | |
| } | |
| if (delegate_ == nullptr) { | |
| auto& context_vk = impeller::SurfaceContextVK::Cast(*impeller_context_); | |
| std::unique_ptr<impeller::Surface> surface = | |
| context_vk.AcquireNextSurface(); | |
| if (!surface) { | |
| FML_LOG(ERROR) << "No surface available."; | |
| return nullptr; | |
| } | |
| impeller::RenderTarget render_target = surface->GetRenderTarget(); | |
| auto cull_rect = render_target.GetRenderTargetSize(); | |
| SurfaceFrame::EncodeCallback encode_callback = [aiks_context = | |
| aiks_context_, // | |
| render_target, | |
| cull_rect // | |
| ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool { | |
| if (!aiks_context) { | |
| return false; | |
| } | |
| auto display_list = surface_frame.BuildDisplayList(); | |
| if (!display_list) { | |
| FML_LOG(ERROR) << "Could not build display list for surface frame."; | |
| return false; | |
| } | |
| SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); | |
| return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // | |
| render_target, // | |
| display_list, // | |
| sk_cull_rect, // | |
| /*reset_host_buffer=*/true // | |
| ); | |
| }; | |
| return std::make_unique<SurfaceFrame>( | |
| nullptr, // surface | |
| SurfaceFrame::FramebufferInfo{}, // framebuffer info | |
| encode_callback, // encode callback | |
| fml::MakeCopyable([surface = std::move(surface)](const SurfaceFrame&) { | |
| return surface->Present(); | |
| }), // submit callback | |
| size, // frame size | |
| nullptr, // context result | |
| true // display list fallback | |
| ); | |
| } else { | |
| FlutterVulkanImage flutter_image = delegate_->AcquireImage(size); | |
| if (!flutter_image.image) { | |
| FML_LOG(ERROR) << "Invalid VkImage given by the embedder."; | |
| return nullptr; | |
| } | |
| impeller::vk::Format vk_format = | |
| static_cast<impeller::vk::Format>(flutter_image.format); | |
| std::optional<impeller::PixelFormat> format = | |
| impeller::VkFormatToImpellerFormat(vk_format); | |
| if (!format.has_value()) { | |
| FML_LOG(ERROR) << "Unsupported pixel format: " | |
| << impeller::vk::to_string(vk_format); | |
| return nullptr; | |
| } | |
| impeller::vk::Image vk_image = | |
| impeller::vk::Image(reinterpret_cast<VkImage>(flutter_image.image)); | |
| impeller::TextureDescriptor desc; | |
| desc.format = format.value(); | |
| desc.size = impeller::ISize{size.width(), size.height()}; | |
| desc.storage_mode = impeller::StorageMode::kDevicePrivate; | |
| desc.mip_count = 1; | |
| desc.compression_type = impeller::CompressionType::kLossless; | |
| desc.usage = impeller::TextureUsage::kRenderTarget; | |
| impeller::ContextVK& context_vk = | |
| impeller::ContextVK::Cast(*impeller_context_); | |
| impeller::vk::ImageViewCreateInfo view_info = {}; | |
| view_info.viewType = impeller::vk::ImageViewType::e2D; | |
| view_info.format = ToVKImageFormat(desc.format); | |
| view_info.subresourceRange.aspectMask = | |
| impeller::vk::ImageAspectFlagBits::eColor; | |
| view_info.subresourceRange.baseMipLevel = 0u; | |
| view_info.subresourceRange.baseArrayLayer = 0u; | |
| view_info.subresourceRange.levelCount = 1; | |
| view_info.subresourceRange.layerCount = 1; | |
| view_info.image = vk_image; | |
| auto [result, image_view] = | |
| context_vk.GetDevice().createImageView(view_info); | |
| if (result != impeller::vk::Result::eSuccess) { | |
| FML_LOG(ERROR) << "Failed to create image view for provided image: " | |
| << impeller::vk::to_string(result); | |
| return nullptr; | |
| } | |
| if (transients_ == nullptr) { | |
| transients_ = std::make_shared<impeller::SwapchainTransientsVK>( | |
| impeller_context_, desc, | |
| /*enable_msaa=*/true); | |
| } | |
| auto wrapped_onscreen = | |
| std::make_shared<WrappedTextureSourceVK>(vk_image, image_view, desc); | |
| auto surface = impeller::SurfaceVK::WrapSwapchainImage( | |
| transients_, wrapped_onscreen, [&]() -> bool { return true; }); | |
| impeller::RenderTarget render_target = surface->GetRenderTarget(); | |
| auto cull_rect = render_target.GetRenderTargetSize(); | |
| SurfaceFrame::EncodeCallback encode_callback = [aiks_context = | |
| aiks_context_, // | |
| render_target, | |
| cull_rect // | |
| ](SurfaceFrame& surface_frame, DlCanvas* canvas) mutable -> bool { | |
| if (!aiks_context) { | |
| return false; | |
| } | |
| auto display_list = surface_frame.BuildDisplayList(); | |
| if (!display_list) { | |
| FML_LOG(ERROR) << "Could not build display list for surface frame."; | |
| return false; | |
| } | |
| SkIRect sk_cull_rect = SkIRect::MakeWH(cull_rect.width, cull_rect.height); | |
| return impeller::RenderToOnscreen(aiks_context->GetContentContext(), // | |
| render_target, // | |
| display_list, // | |
| sk_cull_rect, // | |
| /*reset_host_buffer=*/true // | |
| ); | |
| }; | |
| SurfaceFrame::SubmitCallback submit_callback = | |
| [image = flutter_image, delegate = delegate_, | |
| impeller_context = impeller_context_, | |
| wrapped_onscreen](const SurfaceFrame&) -> bool { | |
| TRACE_EVENT0("flutter", "GPUSurfaceVulkan::PresentImage"); | |
| { | |
| const auto& context = impeller::ContextVK::Cast(*impeller_context); | |
| //---------------------------------------------------------------------------- | |
| /// Transition the image to color-attachment-optimal. | |
| /// | |
| auto cmd_buffer = context.CreateCommandBuffer(); | |
| auto vk_final_cmd_buffer = | |
| impeller::CommandBufferVK::Cast(*cmd_buffer).GetCommandBuffer(); | |
| { | |
| impeller::BarrierVK barrier; | |
| barrier.new_layout = | |
| impeller::vk::ImageLayout::eColorAttachmentOptimal; | |
| barrier.cmd_buffer = vk_final_cmd_buffer; | |
| barrier.src_access = | |
| impeller::vk::AccessFlagBits::eColorAttachmentWrite; | |
| barrier.src_stage = | |
| impeller::vk::PipelineStageFlagBits::eColorAttachmentOutput; | |
| barrier.dst_access = {}; | |
| barrier.dst_stage = | |
| impeller::vk::PipelineStageFlagBits::eBottomOfPipe; | |
| if (!wrapped_onscreen->SetLayout(barrier).ok()) { | |
| return false; | |
| } | |
| } | |
| if (!context.GetCommandQueue()->Submit({cmd_buffer}).ok()) { | |
| return false; | |
| } | |
| } | |
| return delegate->PresentImage(reinterpret_cast<VkImage>(image.image), | |
| static_cast<VkFormat>(image.format)); | |
| }; | |
| SurfaceFrame::FramebufferInfo framebuffer_info{.supports_readback = true}; | |
| return std::make_unique<SurfaceFrame>(nullptr, // surface | |
| framebuffer_info, // framebuffer info | |
| encode_callback, // encode callback | |
| submit_callback, | |
| size, // frame size | |
| nullptr, // context result | |
| true // display list fallback | |
| ); | |
| } | |
| } | |
| // |Surface| | |
| SkMatrix GPUSurfaceVulkanImpeller::GetRootTransformation() const { | |
| // This backend does not currently support root surface transformations. Just | |
| // return identity. | |
| return {}; | |
| } | |
| // |Surface| | |
| GrDirectContext* GPUSurfaceVulkanImpeller::GetContext() { | |
| // Impeller != Skia. | |
| return nullptr; | |
| } | |
| // |Surface| | |
| std::unique_ptr<GLContextResult> | |
| GPUSurfaceVulkanImpeller::MakeRenderContextCurrent() { | |
| // This backend has no such concept. | |
| return std::make_unique<GLContextDefaultResult>(true); | |
| } | |
| // |Surface| | |
| bool GPUSurfaceVulkanImpeller::EnableRasterCache() const { | |
| return false; | |
| } | |
| // |Surface| | |
| std::shared_ptr<impeller::AiksContext> | |
| GPUSurfaceVulkanImpeller::GetAiksContext() const { | |
| return aiks_context_; | |
| } | |
| } // namespace flutter |