diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index bd6ae97ff8b6f..e0b73a3442098 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -169,6 +169,8 @@ template("embedder_source_set") { if (embedder_enable_vulkan) { sources += [ + "embedder_external_texture_vulkan.cc", + "embedder_external_texture_vulkan.h", "embedder_surface_vulkan.cc", "embedder_surface_vulkan.h", ] diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 5acf151c9bb02..1b15ef496f609 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2056,6 +2056,28 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, external_texture_metal_callback); } } +#endif +#ifdef SHELL_ENABLE_VULKAN + flutter::EmbedderExternalTextureVulkan::ExternalTextureCallback + external_texture_vulkan_callback; + if (config->type == kVulkan) { + const FlutterVulkanRendererConfig* vulkan_config = &config->vulkan; + if (SAFE_ACCESS(vulkan_config, external_texture_frame_callback, nullptr)) { + external_texture_vulkan_callback = + [ptr = vulkan_config->external_texture_frame_callback, user_data]( + int64_t texture_identifier, size_t width, + size_t height) -> std::unique_ptr { + std::unique_ptr texture = + std::make_unique(); + if (!ptr(user_data, texture_identifier, width, height, texture.get())) { + return nullptr; + } + return texture; + }; + external_texture_resolver = std::make_unique( + external_texture_vulkan_callback); + } + } #endif auto custom_task_runners = SAFE_ACCESS(args, custom_task_runners, nullptr); auto thread_config_callback = [&custom_task_runners]( diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 117aa714c6f82..86adb724454d2 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -758,10 +758,37 @@ typedef bool (*FlutterVulkanPresentCallback)( void* /* user data */, const FlutterVulkanImage* /* image */); +typedef struct { + /// Handle to the VkImage that is owned by the embedder. The engine will + /// bind this image for writing the frame. + FlutterVulkanImageHandle image; + /// The VkFormat of the image (for example: VK_FORMAT_R8G8B8A8_UNORM). + uint32_t format; + /// User data to be returned on the invocation of the destruction callback. + void* user_data; + /// Callback invoked (on an engine managed thread) that asks the embedder to + /// collect the texture. + VoidCallback destruction_callback; + /// Optional parameters for texture height/width, default is 0, non-zero means + /// the texture has the specified width/height. + /// Width of the texture. + size_t width; + /// Height of the texture. + size_t height; +} FlutterVulkanTexture; + +/// Callback to provide an external texture for a given texture_id. +/// See: external_texture_frame_callback. +typedef bool (*FlutterVulkanTextureFrameCallback)( + void* /* user data */, + int64_t /* texture identifier */, + size_t /* width */, + size_t /* height */, + FlutterVulkanTexture* /* texture out */); + typedef struct { /// The size of this struct. Must be sizeof(FlutterVulkanRendererConfig). size_t struct_size; - /// The Vulkan API version. This should match the value set in /// VkApplicationInfo::apiVersion when the VkInstance was created. uint32_t version; @@ -821,7 +848,11 @@ typedef struct { /// without any additional synchronization. /// Not used if a FlutterCompositor is supplied in FlutterProjectArgs. FlutterVulkanPresentCallback present_image_callback; - + /// When the embedder specifies that a texture has a frame available, the + /// engine will call this method (on an internal engine managed thread) so + /// that external texture details can be supplied to the engine for subsequent + /// composition. + FlutterVulkanTextureFrameCallback external_texture_frame_callback; } FlutterVulkanRendererConfig; typedef struct { diff --git a/shell/platform/embedder/embedder_external_texture_resolver.cc b/shell/platform/embedder/embedder_external_texture_resolver.cc index bbe83b875d5fd..6604016ca9aed 100644 --- a/shell/platform/embedder/embedder_external_texture_resolver.cc +++ b/shell/platform/embedder/embedder_external_texture_resolver.cc @@ -26,6 +26,12 @@ EmbedderExternalTextureResolver::EmbedderExternalTextureResolver( : metal_callback_(std::move(metal_callback)) {} #endif +#ifdef SHELL_ENABLE_VULKAN +EmbedderExternalTextureResolver::EmbedderExternalTextureResolver( + EmbedderExternalTextureVulkan::ExternalTextureCallback vulkan_callback) + : vulkan_callback_(std::move(vulkan_callback)) {} +#endif + std::unique_ptr EmbedderExternalTextureResolver::ResolveExternalTexture(int64_t texture_id) { #ifdef SHELL_ENABLE_GL @@ -48,6 +54,13 @@ EmbedderExternalTextureResolver::ResolveExternalTexture(int64_t texture_id) { } #endif +#ifdef SHELL_ENABLE_VULKAN + if (vulkan_callback_) { + return std::make_unique( + texture_id, vulkan_callback_); + } +#endif + return nullptr; } diff --git a/shell/platform/embedder/embedder_external_texture_resolver.h b/shell/platform/embedder/embedder_external_texture_resolver.h index da421e2b837a1..1fda0f8514908 100644 --- a/shell/platform/embedder/embedder_external_texture_resolver.h +++ b/shell/platform/embedder/embedder_external_texture_resolver.h @@ -17,6 +17,10 @@ #include "flutter/shell/platform/embedder/embedder_external_texture_metal.h" #endif +#ifdef SHELL_ENABLE_VULKAN +#include "flutter/shell/platform/embedder/embedder_external_texture_vulkan.h" +#endif + namespace flutter { class EmbedderExternalTextureResolver { public: @@ -35,6 +39,11 @@ class EmbedderExternalTextureResolver { EmbedderExternalTextureMetal::ExternalTextureCallback metal_callback); #endif +#ifdef SHELL_ENABLE_VULKAN + explicit EmbedderExternalTextureResolver( + EmbedderExternalTextureVulkan::ExternalTextureCallback vulkan_callback); +#endif + std::unique_ptr ResolveExternalTexture(int64_t texture_id); bool SupportsExternalTextures(); @@ -47,6 +56,10 @@ class EmbedderExternalTextureResolver { #ifdef SHELL_ENABLE_METAL EmbedderExternalTextureMetal::ExternalTextureCallback metal_callback_; #endif + +#ifdef SHELL_ENABLE_VULKAN + EmbedderExternalTextureVulkan::ExternalTextureCallback vulkan_callback_; +#endif bool enable_impeller_ = false; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureResolver); diff --git a/shell/platform/embedder/embedder_external_texture_vulkan.cc b/shell/platform/embedder/embedder_external_texture_vulkan.cc new file mode 100644 index 0000000000000..356417300585c --- /dev/null +++ b/shell/platform/embedder/embedder_external_texture_vulkan.cc @@ -0,0 +1,158 @@ +// 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/platform/embedder/embedder_external_texture_vulkan.h" + +#include "flutter/fml/logging.h" +#include "impeller/core/texture_descriptor.h" +#include "impeller/renderer/context.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkPaint.h" +#include "third_party/skia/include/core/SkAlphaType.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" +#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" +#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h" +#include "third_party/skia/include/gpu/gl/GrGLTypes.h" + +namespace flutter { +EmbedderExternalTextureVulkan::EmbedderExternalTextureVulkan( + int64_t texture_identifier, + const ExternalTextureCallback& callback) + : Texture(texture_identifier), external_texture_callback_(callback) { + FML_DCHECK(external_texture_callback_); + } + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::Paint(PaintContext& context, + const SkRect& bounds, + bool freeze, + const DlImageSampling sampling) { + if (last_image_ == nullptr) { + last_image_ = + ResolveTexture(Id(), // + context.gr_context, // + context.aiks_context, // + SkISize::Make(bounds.width(), bounds.height()) // + ); + } + + DlCanvas* canvas = context.canvas; + const DlPaint* paint = context.paint; + + if (last_image_) { + SkRect image_bounds = SkRect::Make(last_image_->bounds()); + if (bounds != image_bounds) { + canvas->DrawImageRect(last_image_, image_bounds, bounds, sampling, paint); + } else { + canvas->DrawImage(last_image_, {bounds.x(), bounds.y()}, sampling, paint); + } + } +} + +sk_sp EmbedderExternalTextureVulkan::ResolveTexture( + int64_t texture_id, + GrDirectContext* context, + impeller::AiksContext* aiks_context, + const SkISize& size) { + if (!!aiks_context) { + return ResolveTextureImpeller(texture_id, aiks_context, size); + } else { + return ResolveTextureSkia(texture_id, context, size); + } +} + +sk_sp EmbedderExternalTextureVulkan::ResolveTextureSkia( + int64_t texture_id, + GrDirectContext* context, + const SkISize& size) { + context->flushAndSubmit(); + context->resetContext(kAll_GrBackendState); + std::unique_ptr texture; + + if (!texture) { + return nullptr; + } + + size_t width = size.width(); + size_t height = size.height(); + + if (texture->width != 0 && texture->height != 0) { + width = texture->width; + height = texture->height; + } + + GrVkImageInfo image_info = { + .fImage = reinterpret_cast(texture->image), + .fImageTiling = VK_IMAGE_TILING_OPTIMAL, + .fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .fFormat = static_cast(texture->format), + .fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT, + .fSampleCount = 1, + .fLevelCount = 1, + }; + + auto gr_backend_texture = + GrBackendTextures::MakeVk(width, height, image_info); + SkImages::TextureReleaseProc release_proc = texture->destruction_callback; + auto image = + SkImages::BorrowTextureFrom(context, // context + gr_backend_texture, // texture handle + kTopLeft_GrSurfaceOrigin, // origin + kRGBA_8888_SkColorType, // color type + kPremul_SkAlphaType, // alpha type + nullptr, // colorspace + release_proc, // texture release proc + texture->user_data // texture release context + ); + + if (!image) { + // In case Skia rejects the image, call the release proc so that + // embedders can perform collection of intermediates. + if (release_proc) { + release_proc(texture->user_data); + } + FML_LOG(ERROR) << "Could not create external texture->"; + return nullptr; + } + + return DlImage::Make(std::move(image)); +} + +sk_sp EmbedderExternalTextureVulkan::ResolveTextureImpeller( + int64_t texture_id, + impeller::AiksContext* aiks_context, + const SkISize& size) { + std::unique_ptr texture = + external_texture_callback_(texture_id, size.width(), size.height()); + if(!texture){ + return nullptr; + } + return nullptr; +} + +EmbedderExternalTextureVulkan::~EmbedderExternalTextureVulkan() = default; + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::OnGrContextCreated() {} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::OnGrContextDestroyed() {} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::MarkNewFrameAvailable() { + last_image_ = nullptr; +} + +// |flutter::Texture| +void EmbedderExternalTextureVulkan::OnTextureUnregistered() {} + +} // namespace flutter \ No newline at end of file diff --git a/shell/platform/embedder/embedder_external_texture_vulkan.h b/shell/platform/embedder/embedder_external_texture_vulkan.h new file mode 100644 index 0000000000000..7c104dba95462 --- /dev/null +++ b/shell/platform/embedder/embedder_external_texture_vulkan.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_VULKAN_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_VULKAN_H_ + +#include "flutter/common/graphics/texture.h" +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "third_party/skia/include/core/SkSize.h" + +namespace flutter { + +class EmbedderExternalTextureVulkan : public flutter::Texture { + public: + using ExternalTextureCallback = std::function< + std::unique_ptr(int64_t, size_t, size_t)>; + EmbedderExternalTextureVulkan(int64_t texture_identifier, + const ExternalTextureCallback& callback); + + ~EmbedderExternalTextureVulkan(); + + private: + const ExternalTextureCallback& external_texture_callback_; + + sk_sp last_image_; + + sk_sp ResolveTexture(int64_t texture_id, + GrDirectContext* context, + impeller::AiksContext* aiks_context, + const SkISize& size); + sk_sp ResolveTextureSkia(int64_t texture_id, + GrDirectContext* context, + const SkISize& size); + sk_sp ResolveTextureImpeller(int64_t texture_id, + impeller::AiksContext* aiks_context, + const SkISize& size); + // |flutter::Texture| + void Paint(PaintContext& context, + const SkRect& bounds, + bool freeze, + const DlImageSampling sampling) override; + + // |flutter::Texture| + void OnGrContextCreated() override; + + // |flutter::Texture| + void OnGrContextDestroyed() override; + + // |flutter::Texture| + void MarkNewFrameAvailable() override; + + // |flutter::Texture| + void OnTextureUnregistered() override; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureVulkan); +}; +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_VULKAN_H_ \ No newline at end of file