From 330a338cf73c2c23347f180ca9903a10c76573b2 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 24 Sep 2026 14:52:01 +0800 Subject: [PATCH 1/2] Apply new external texture api --- flutter/shell/platform/embedder/embedder.h | 25 ++++++++++--------- .../shell/platform/tizen/external_texture.h | 7 +++--- .../tizen/external_texture_pixel_vulkan.cc | 15 +++++------ .../tizen/external_texture_pixel_vulkan.h | 7 +++--- .../tizen/external_texture_surface_vulkan.cc | 7 +++--- .../tizen/external_texture_surface_vulkan.h | 7 +++--- .../tizen/flutter_tizen_texture_registrar.cc | 2 +- .../tizen/flutter_tizen_texture_registrar.h | 2 +- .../platform/tizen/tizen_renderer_vulkan.cc | 2 +- 9 files changed, 40 insertions(+), 34 deletions(-) diff --git a/flutter/shell/platform/embedder/embedder.h b/flutter/shell/platform/embedder/embedder.h index 058f2259..1de52851 100644 --- a/flutter/shell/platform/embedder/embedder.h +++ b/flutter/shell/platform/embedder/embedder.h @@ -930,9 +930,6 @@ typedef void* FlutterVulkanQueueHandle; /// Alias for VkImage. typedef uint64_t FlutterVulkanImageHandle; -/// Alias for VkDeviceMemory. -typedef uint64_t FlutterVulkanDeviceMemoryHandle; - typedef struct { /// The size of this struct. Must be sizeof(FlutterVulkanImage). size_t struct_size; @@ -962,25 +959,27 @@ typedef bool (*FlutterVulkanPresentCallback)( const FlutterVulkanImage* /* image */); typedef struct { + /// The size of this struct. Must be sizeof(FlutterVulkanExternalTexture). + size_t struct_size; /// Handle to the VkImage that is owned by the embedder. The engine will - /// bind this image for writing the frame. + /// sample from this image during composition. The VkImage must be in the + /// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL layout when provided to the + /// engine. FlutterVulkanImageHandle image; - /// The VkDeviceMemory that backs the iamge. - FlutterVulkanDeviceMemoryHandle image_memory; /// 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. + /// collect the texture. This is optional and can be null. 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. + /// Physical width of the texture. size_t width; - /// Height of the texture. + /// Physical height of the texture. size_t height; -} FlutterVulkanTexture; +} FlutterVulkanExternalTexture; /// Callback to provide an external texture for a given texture_id. /// See: external_texture_frame_callback. @@ -989,7 +988,7 @@ typedef bool (*FlutterVulkanTextureFrameCallback)( int64_t /* texture identifier */, size_t /* width */, size_t /* height */, - FlutterVulkanTexture* /* texture out */); + FlutterVulkanExternalTexture* /* texture out */); typedef struct { /// The size of this struct. Must be sizeof(FlutterVulkanRendererConfig). @@ -1057,7 +1056,9 @@ typedef struct { /// 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. + /// composition. Prior to returning from this callback, the embedder must + /// perform a host sync, and so the engine can sample the VkImage without any + /// additional synchronization. FlutterVulkanTextureFrameCallback external_texture_frame_callback; /// The path to the Vulkan pipeline cache data. /// The string can be collected after the call to `FlutterEngineRun` returns. diff --git a/flutter/shell/platform/tizen/external_texture.h b/flutter/shell/platform/tizen/external_texture.h index fe697ce6..8f4c292d 100644 --- a/flutter/shell/platform/tizen/external_texture.h +++ b/flutter/shell/platform/tizen/external_texture.h @@ -55,9 +55,10 @@ class ExternalVulkanTexture : public ExternalTexture { public: ExternalVulkanTexture() : ExternalTexture() {} - virtual bool PopulateVulkanTexture(size_t width, - size_t height, - FlutterVulkanTexture* vulkan_texture) = 0; + virtual bool PopulateVulkanTexture( + size_t width, + size_t height, + FlutterVulkanExternalTexture* vulkan_texture) = 0; }; } // namespace flutter diff --git a/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc b/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc index 7a748603..e0c09337 100644 --- a/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc +++ b/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc @@ -23,7 +23,7 @@ ExternalTexturePixelVulkan::~ExternalTexturePixelVulkan() { bool ExternalTexturePixelVulkan::PopulateVulkanTexture( size_t width, size_t height, - FlutterVulkanTexture* flutter_texture) { + FlutterVulkanExternalTexture* flutter_texture) { if (!texture_callback_) { FT_LOG(Error) << "texture_callback_ is nullptr"; return false; @@ -75,12 +75,13 @@ bool ExternalTexturePixelVulkan::PopulateVulkanTexture( return false; } - FlutterVulkanTexture* vulkan_texture = - static_cast(flutter_texture); - vulkan_texture->image = reinterpret_cast(image_); - vulkan_texture->format = VK_FORMAT_R8G8B8A8_UNORM; - vulkan_texture->width = width_; - vulkan_texture->height = height_; + flutter_texture->struct_size = sizeof(FlutterVulkanExternalTexture); + flutter_texture->image = reinterpret_cast(image_); + flutter_texture->format = VK_FORMAT_R8G8B8A8_UNORM; + flutter_texture->user_data = nullptr; + flutter_texture->destruction_callback = nullptr; + flutter_texture->width = width_; + flutter_texture->height = height_; return true; } diff --git a/flutter/shell/platform/tizen/external_texture_pixel_vulkan.h b/flutter/shell/platform/tizen/external_texture_pixel_vulkan.h index 9ba666e2..8f63e5e8 100644 --- a/flutter/shell/platform/tizen/external_texture_pixel_vulkan.h +++ b/flutter/shell/platform/tizen/external_texture_pixel_vulkan.h @@ -20,9 +20,10 @@ class ExternalTexturePixelVulkan : public ExternalVulkanTexture { virtual ~ExternalTexturePixelVulkan(); - bool PopulateVulkanTexture(size_t width, - size_t height, - FlutterVulkanTexture* flutter_texture) override; + bool PopulateVulkanTexture( + size_t width, + size_t height, + FlutterVulkanExternalTexture* flutter_texture) override; private: bool AllocateMemory(const VkMemoryRequirements& memory_requirements, diff --git a/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc b/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc index 6183dcf0..c69d18bd 100644 --- a/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc +++ b/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc @@ -109,7 +109,7 @@ bool ExternalTextureSurfaceVulkan::IsSupportDisjoint( bool ExternalTextureSurfaceVulkan::PopulateVulkanTexture( size_t width, size_t height, - FlutterVulkanTexture* vulkan_texture) { + FlutterVulkanExternalTexture* vulkan_texture) { if (!texture_callback_ || !vulkan_texture) { return false; } @@ -126,11 +126,12 @@ bool ExternalTextureSurfaceVulkan::PopulateVulkanTexture( return false; } + vulkan_texture->struct_size = sizeof(FlutterVulkanExternalTexture); vulkan_texture->image = reinterpret_cast(vulkan_buffer_->GetImage()); vulkan_texture->format = vulkan_buffer_->GetFormat(); - vulkan_texture->image_memory = - reinterpret_cast(vulkan_buffer_->GetMemory()); + vulkan_texture->user_data = nullptr; + vulkan_texture->destruction_callback = nullptr; vulkan_texture->width = width; vulkan_texture->height = height; return true; diff --git a/flutter/shell/platform/tizen/external_texture_surface_vulkan.h b/flutter/shell/platform/tizen/external_texture_surface_vulkan.h index f2fa0a6d..260b7571 100644 --- a/flutter/shell/platform/tizen/external_texture_surface_vulkan.h +++ b/flutter/shell/platform/tizen/external_texture_surface_vulkan.h @@ -28,9 +28,10 @@ class ExternalTextureSurfaceVulkan : public ExternalVulkanTexture { virtual ~ExternalTextureSurfaceVulkan(); - bool PopulateVulkanTexture(size_t width, - size_t height, - FlutterVulkanTexture* vulkan_texture) override; + bool PopulateVulkanTexture( + size_t width, + size_t height, + FlutterVulkanExternalTexture* vulkan_texture) override; private: bool CreateOrUpdateImage( diff --git a/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.cc b/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.cc index 66e6ae1d..b102c2c7 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.cc @@ -157,7 +157,7 @@ bool FlutterTizenTextureRegistrar::PopulateVulkanTexture( int64_t texture_id, size_t width, size_t height, - FlutterVulkanTexture* vulkan_texture) { + FlutterVulkanExternalTexture* vulkan_texture) { ExternalTexture* texture; { std::lock_guard lock(map_mutex_); diff --git a/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.h b/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.h index 2a6ae880..2b2880d7 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.h +++ b/flutter/shell/platform/tizen/flutter_tizen_texture_registrar.h @@ -55,7 +55,7 @@ class FlutterTizenTextureRegistrar { bool PopulateVulkanTexture(int64_t texture_id, size_t width, size_t height, - FlutterVulkanTexture* texture); + FlutterVulkanExternalTexture* texture); private: FlutterTizenEngine* engine_ = nullptr; diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc index 5f1bbacb..99f2c1b9 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc @@ -201,7 +201,7 @@ FlutterRendererConfig TizenRendererVulkan::GetRendererConfig() { }; config.vulkan.external_texture_frame_callback = [](void* user_data, int64_t texture_id, size_t width, size_t height, - FlutterVulkanTexture* texture) -> bool { + FlutterVulkanExternalTexture* texture) -> bool { auto* engine = reinterpret_cast(user_data); if (!engine->view()) { return false; From 2908ea9328dd2d34f91ad7ff7bcff72f80c42137 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 9 Oct 2026 18:41:00 +0800 Subject: [PATCH 2/2] Fix code review issues --- .../tizen/external_texture_pixel_vulkan.cc | 40 +++++++++++++++++++ .../tizen/external_texture_surface_vulkan.cc | 6 +++ .../external_texture_surface_vulkan_buffer.cc | 34 ++++++++++++++++ .../external_texture_surface_vulkan_buffer.h | 2 + 4 files changed, 82 insertions(+) diff --git a/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc b/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc index e0c09337..73656425 100644 --- a/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc +++ b/flutter/shell/platform/tizen/external_texture_pixel_vulkan.cc @@ -226,9 +226,49 @@ bool ExternalTexturePixelVulkan::CopyBufferToImage(const uint8_t* src_buffer, region.imageExtent = {static_cast(width_), static_cast(height_), 1}; + { + VkImageMemoryBarrier pre_copy_barrier = {}; + pre_copy_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + pre_copy_barrier.srcAccessMask = 0; + pre_copy_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + pre_copy_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + pre_copy_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + pre_copy_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + pre_copy_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + pre_copy_barrier.image = image_; + pre_copy_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + pre_copy_barrier.subresourceRange.baseMipLevel = 0; + pre_copy_barrier.subresourceRange.levelCount = 1; + pre_copy_barrier.subresourceRange.baseArrayLayer = 0; + pre_copy_barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, + nullptr, 1, &pre_copy_barrier); + } + vkCmdCopyBufferToImage(command_buffer, staging_buffer_, image_, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + { + VkImageMemoryBarrier post_copy_barrier = {}; + post_copy_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + post_copy_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + post_copy_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + post_copy_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + post_copy_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + post_copy_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + post_copy_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + post_copy_barrier.image = image_; + post_copy_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + post_copy_barrier.subresourceRange.baseMipLevel = 0; + post_copy_barrier.subresourceRange.levelCount = 1; + post_copy_barrier.subresourceRange.baseArrayLayer = 0; + post_copy_barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, + 0, nullptr, 1, &post_copy_barrier); + } + vulkan_renderer_->EndSingleTimeCommands(command_buffer); return true; } diff --git a/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc b/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc index c69d18bd..0d1e7aa3 100644 --- a/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc +++ b/flutter/shell/platform/tizen/external_texture_surface_vulkan.cc @@ -51,6 +51,12 @@ bool ExternalTextureSurfaceVulkan::CreateBuffer( return false; } + if (!vulkan_buffer_->TransitionToShaderReadLayout( + vulkan_buffer_->GetImage())) { + FT_LOG(Error) << "Fail to transition image layout"; + vulkan_buffer_->ReleaseImage(); + return false; + } return true; } diff --git a/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.cc b/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.cc index 0dfbb5e5..9ae8a524 100644 --- a/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.cc +++ b/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.cc @@ -38,4 +38,38 @@ VkDevice ExternalTextureSurfaceVulkanBuffer::GetDevice() const { return static_cast(vulkan_renderer_->GetDeviceHandle()); } +bool ExternalTextureSurfaceVulkanBuffer::TransitionToShaderReadLayout( + VkImage image) { + if (image == VK_NULL_HANDLE) { + FT_LOG(Error) << "Cannot transition layout of a null VkImage"; + return false; + } + + VkCommandBuffer command_buffer = vulkan_renderer_->BeginSingleTimeCommands(); + if (command_buffer == VK_NULL_HANDLE) { + FT_LOG(Error) << "Failed to begin single time commands"; + return false; + } + + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + vulkan_renderer_->EndSingleTimeCommands(command_buffer); + return true; +} + } // namespace flutter diff --git a/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.h b/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.h index c9f8a040..aa97f990 100644 --- a/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.h +++ b/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer.h @@ -29,6 +29,8 @@ class ExternalTextureSurfaceVulkanBuffer { virtual VkImage GetImage() = 0; virtual VkDeviceMemory GetMemory() = 0; + bool TransitionToShaderReadLayout(VkImage image); + protected: VkFormat ConvertFormat(tbm_format format); VkDevice GetDevice() const;