From de822c0e7cf072ef54ea689cd82e735b3dacc112 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 14 Aug 2025 13:15:34 +0800 Subject: [PATCH 1/9] Support vulkan backend --- flutter/shell/platform/tizen/BUILD.gn | 2 + flutter/shell/platform/tizen/flutter_tizen.cc | 4 +- .../platform/tizen/flutter_tizen_engine.cc | 3 + .../platform/tizen/public/flutter_tizen.h | 10 +- .../platform/tizen/tizen_renderer_vulkan.cc | 964 ++++++++++++++++++ .../platform/tizen/tizen_renderer_vulkan.h | 105 ++ .../platform/tizen/tizen_window_ecore_wl2.cc | 71 +- .../platform/tizen/tizen_window_ecore_wl2.h | 9 +- 8 files changed, 1131 insertions(+), 37 deletions(-) create mode 100644 flutter/shell/platform/tizen/tizen_renderer_vulkan.cc create mode 100644 flutter/shell/platform/tizen/tizen_renderer_vulkan.h diff --git a/flutter/shell/platform/tizen/BUILD.gn b/flutter/shell/platform/tizen/BUILD.gn index 17c27ac..8e4874e 100644 --- a/flutter/shell/platform/tizen/BUILD.gn +++ b/flutter/shell/platform/tizen/BUILD.gn @@ -115,6 +115,7 @@ template("embedder") { "tizen_renderer.cc", "tizen_renderer_egl.cc", "tizen_renderer_evas_gl.cc", + "tizen_renderer_vulkan.cc", "tizen_renderer_gl.cc", "tizen_view_elementary.cc", "tizen_vsync_waiter.cc", @@ -151,6 +152,7 @@ template("embedder") { "tdm-client", "tizen-extension-client", "vconf", + "vulkan", "wayland-client", "EGL", "GLESv2", diff --git a/flutter/shell/platform/tizen/flutter_tizen.cc b/flutter/shell/platform/tizen/flutter_tizen.cc index 592b03c..6e0fd01 100644 --- a/flutter/shell/platform/tizen/flutter_tizen.cc +++ b/flutter/shell/platform/tizen/flutter_tizen.cc @@ -209,7 +209,9 @@ FlutterDesktopViewRef FlutterDesktopViewCreateFromNewWindow( window = std::make_unique( window_geometry, window_properties.transparent, window_properties.focusable, window_properties.top_level, - window_properties.window_handle); + window_properties.window_handle, + window_properties.renderer_type == + FlutterDesktopRendererType::kEcoreVulkan); } auto view = std::make_unique( diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.cc b/flutter/shell/platform/tizen/flutter_tizen_engine.cc index 769c273..ae1e3d4 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.cc @@ -18,6 +18,7 @@ #include "flutter/shell/platform/tizen/tizen_input_method_context.h" #include "flutter/shell/platform/tizen/tizen_renderer_egl.h" #include "flutter/shell/platform/tizen/tizen_renderer_evas_gl.h" +#include "flutter/shell/platform/tizen/tizen_renderer_vulkan.h" #ifdef NUI_SUPPORT #include "flutter/shell/platform/tizen/tizen_renderer_nui_gl.h" @@ -99,6 +100,8 @@ std::unique_ptr FlutterTizenEngine::CreateRenderer( #endif return std::make_unique( view_->tizen_view(), project_->HasArgument("--enable-impeller")); + case FlutterDesktopRendererType::kEcoreVulkan: + return std::make_unique(view_->tizen_view()); } } diff --git a/flutter/shell/platform/tizen/public/flutter_tizen.h b/flutter/shell/platform/tizen/public/flutter_tizen.h index 6d95099..90f2c02 100644 --- a/flutter/shell/platform/tizen/public/flutter_tizen.h +++ b/flutter/shell/platform/tizen/public/flutter_tizen.h @@ -30,6 +30,8 @@ typedef enum { kEvasGL, // The renderer based on EGL. kEGL, + // The renderer based on Vulkan. + kEcoreVulkan } FlutterDesktopRendererType; typedef enum { @@ -128,8 +130,8 @@ FlutterDesktopEngineGetPluginRegistrar(FlutterDesktopEngineRef engine, const char* plugin_name); // Returns the messenger associated with the engine. -FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger( - FlutterDesktopEngineRef engine); +FLUTTER_EXPORT FlutterDesktopMessengerRef +FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine); // Posts an app control to the engine instance. FLUTTER_EXPORT void FlutterDesktopEngineNotifyAppControl( @@ -207,8 +209,8 @@ FLUTTER_EXPORT void* FlutterDesktopViewGetNativeHandle( FlutterDesktopViewRef view); // Returns the resource id of current window. -FLUTTER_EXPORT uint32_t FlutterDesktopViewGetResourceId( - FlutterDesktopViewRef view); +FLUTTER_EXPORT uint32_t +FlutterDesktopViewGetResourceId(FlutterDesktopViewRef view); // Resizes the view. // @warning This API is a work-in-progress and may change. diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc new file mode 100644 index 0000000..064179a --- /dev/null +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc @@ -0,0 +1,964 @@ + +// Copyright 2024 Samsung Electronics Co., Ltd. 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/tizen/tizen_renderer_vulkan.h" +#include +#include +#include +#include +#include +#include "flutter/shell/platform/tizen/flutter_tizen_engine.h" +#include "flutter/shell/platform/tizen/logger.h" + +namespace flutter { + +const std::vector validation_layers = { + "VK_LAYER_KHRONOS_validation"}; + +VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger) { + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator) { + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } +} + +TizenRendererVulkan::TizenRendererVulkan(TizenViewBase* view) { + InitVulkan(view); +} + +bool TizenRendererVulkan::InitVulkan(TizenViewBase* view) { + if (!CreateInstance()) { + FT_LOG(Error) << "Failed to create Vulkan instance"; + return false; + } + if (enable_validation_layers_) { + SetupDebugMessenger(); + } + + if (!TizenRenderer::CreateSurface(view)) { + FT_LOG(Error) << "Failed to create surface"; + return false; + } + if (!PickPhysicalDevice()) { + FT_LOG(Error) << "Filed to pick physical device"; + return false; + } + if (!CreateLogicalDevice()) { + FT_LOG(Error) << "Filed to create logical device"; + return false; + } + if (!GetDeviceQueue()) { + FT_LOG(Error) << "Filed to get device queue"; + return false; + } + if (!CreateSemaphore()) { + FT_LOG(Error) << "Filed to create semaphore"; + return false; + } + if (!CreateFence()) { + FT_LOG(Error) << "Filed to create fence"; + return false; + } + if (!CreateCommandPool()) { + FT_LOG(Error) << "Filed to create command pool"; + return false; + } + if (!InitializeSwapchain()) { + FT_LOG(Error) << "Filed to initialize swapchain"; + return false; + } + is_valid_ = true; + return true; +} + +void TizenRendererVulkan::Cleanup() { + if (logical_device_) { + for (size_t i = 0; i < present_transition_buffers_.size(); ++i) { + vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, + &present_transition_buffers_[i]); + } + for (size_t i = 0; i < swapchain_images_.size(); ++i) { + vkDestroyImage(logical_device_, swapchain_images_[i], nullptr); + } + + if (swapchain_ != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(logical_device_, swapchain_, nullptr); + swapchain_ = VK_NULL_HANDLE; + } + DestroyCommandPool(); + vkDestroyFence(logical_device_, image_ready_fence_, nullptr); + vkDestroySemaphore(logical_device_, present_transition_semaphore_, nullptr); + vkDestroyDevice(logical_device_, nullptr); + } + DestroySurface(); + if (enable_validation_layers_) { + DestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, nullptr); + } + if (instance_ != VK_NULL_HANDLE) { + vkDestroyInstance(instance_, nullptr); + } +} + +std::unique_ptr TizenRendererVulkan::CreateExternalTexture( + const FlutterDesktopTextureInfo* texture_info) { + return nullptr; +} + +FlutterRendererConfig TizenRendererVulkan::GetRendererConfig() { + FlutterRendererConfig config = {}; + config.type = kVulkan; + config.vulkan.struct_size = sizeof(config.vulkan); + config.vulkan.version = GetVersion(); + config.vulkan.instance = GetInstanceHandle(); + config.vulkan.physical_device = GetPhysicalDeviceHandle(); + config.vulkan.device = GetDeviceHandle(); + config.vulkan.queue = GetQueueHandle(); + config.vulkan.queue_family_index = GetQueueIndex(); + config.vulkan.enabled_instance_extension_count = + GetEnabledInstanceExtensionCount(); + config.vulkan.enabled_instance_extensions = GetEnabledInstanceExtensions(); + config.vulkan.enabled_device_extension_count = + GetEnabledDeviceExtensionCount(); + config.vulkan.enabled_device_extensions = GetEnabledDeviceExtensions(); + config.vulkan.get_instance_proc_address_callback = + [](void* user_data, FlutterVulkanInstanceHandle instance, + const char* name) -> void* { + auto* engine = reinterpret_cast(user_data); + if (!engine->view()) { + return nullptr; + } + return dynamic_cast(engine->renderer()) + ->GetInstanceProcAddress(instance, name); + }; + config.vulkan.get_next_image_callback = + [](void* user_data, const FlutterFrameInfo* frame) -> FlutterVulkanImage { + auto* engine = reinterpret_cast(user_data); + if (!engine->view()) { + return FlutterVulkanImage(); + } + return dynamic_cast(engine->renderer()) + ->GetNextImage(frame); + }; + config.vulkan.present_image_callback = + [](void* user_data, const FlutterVulkanImage* image) -> bool { + auto* engine = reinterpret_cast(user_data); + if (!engine->view()) { + return false; + } + + return dynamic_cast(engine->renderer()) + ->Present(image); + }; + return config; +} + +bool TizenRendererVulkan::CreateInstance() { + if (enable_validation_layers_ && !CheckValidationLayerSupport()) { + FT_LOG(Error) << "Validation layers requested, but not available"; + return false; + } + + if (!GetRequiredExtensions(enabled_instance_extensions_)) { + FT_LOG(Error) << "Failed to get required extensions"; + return false; + } + + // Create instance. + VkApplicationInfo app_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = nullptr, + .pApplicationName = "Tizen Embedder", + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "Tizen Embedder", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_1, + }; + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + create_info.enabledExtensionCount = + static_cast(enabled_instance_extensions_.size()); + create_info.ppEnabledExtensionNames = enabled_instance_extensions_.data(); + create_info.flags = 0; + + VkDebugUtilsMessengerCreateInfoEXT debug_create_info{}; + + if (enable_validation_layers_) { + create_info.enabledLayerCount = + static_cast(validation_layers.size()); + create_info.ppEnabledLayerNames = validation_layers.data(); + PopulateDebugMessengerCreateInfo(debug_create_info); + create_info.pNext = reinterpret_cast( + &debug_create_info); + } else { + create_info.enabledLayerCount = 0; + create_info.pNext = nullptr; + } + + if (vkCreateInstance(&create_info, nullptr, &instance_) != VK_SUCCESS) { + FT_LOG(Error) << "Create instance failed."; + return false; + } + return true; +} + +bool TizenRendererVulkan::GetRequiredExtensions( + std::vector& extensions) { + uint32_t instance_extension_count = 0; + bool has_surface_extension = false; + + if (vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, + nullptr) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to enumerate instance extension count"; + return false; + } + if (instance_extension_count > 0) { + std::vector instance_extension_properties( + instance_extension_count); + + if (vkEnumerateInstanceExtensionProperties( + nullptr, &instance_extension_count, + instance_extension_properties.data()) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to enumerate instance extension properties"; + return false; + } + for (const auto& ext : instance_extension_properties) { + if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME, ext.extensionName)) { + has_surface_extension = true; + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + } else if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, + ext.extensionName)) { + has_surface_extension = true; + extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); + } else if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + ext.extensionName)) { + if (enable_validation_layers_) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + } + } + } + + if (!has_surface_extension) { + FT_LOG(Error) << "vkEnumerateInstanceExtensionProperties failed to find " + "the extensions"; + return false; + } + + return true; +} + +void TizenRendererVulkan::SetupDebugMessenger() { + VkDebugUtilsMessengerCreateInfoEXT debug_create_info; + PopulateDebugMessengerCreateInfo(debug_create_info); + + if (CreateDebugUtilsMessengerEXT(instance_, &debug_create_info, nullptr, + &debug_messenger_) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to set up debug messenger"; + } +} + +bool TizenRendererVulkan::CheckValidationLayerSupport() { + uint32_t layer_count; + if (vkEnumerateInstanceLayerProperties(&layer_count, nullptr) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to enumerate instance layer properties"; + return false; + } + std::vector available_layers(layer_count); + if (vkEnumerateInstanceLayerProperties( + &layer_count, available_layers.data()) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to enumerate instance layer properties"; + return false; + } + + for (const char* layer_name : validation_layers) { + bool layer_found = false; + for (const auto& layer_properties : available_layers) { + FT_LOG(Info) << "layer_properties.layerName : " + << layer_properties.layerName; + if (strcmp(layer_name, layer_properties.layerName) == 0) { + layer_found = true; + break; + } + } + if (!layer_found) { + FT_LOG(Error) << "Layer requested is not available"; + return false; + } + } + return true; +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL DebugMessengerCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) { + FT_LOG(Error) << pCallbackData->pMessage; + return VK_FALSE; +} + +void TizenRendererVulkan::PopulateDebugMessengerCreateInfo( + VkDebugUtilsMessengerCreateInfoEXT& createInfo) { + createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + createInfo.pfnUserCallback = DebugMessengerCallback; +} + +bool TizenRendererVulkan::PickPhysicalDevice() { + uint32_t gpu_count = 0; + if (vkEnumeratePhysicalDevices(instance_, &gpu_count, nullptr) != + VK_SUCCESS || + gpu_count == 0) { + FT_LOG(Error) << "Error occurred during physical devices enumeration."; + return false; + } + + std::vector physical_devices(gpu_count); + if (vkEnumeratePhysicalDevices(instance_, &gpu_count, + physical_devices.data()) != VK_SUCCESS) { + FT_LOG(Error) << "Error occurred during physical devices enumeration."; + return false; + } + + FT_LOG(Info) << "Enumerating " << gpu_count << " physical device(s)."; + + uint32_t selected_score = 0; + for (const auto& physical_device : physical_devices) { + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceProperties(physical_device, &properties); + vkGetPhysicalDeviceFeatures(physical_device, &features); + FT_LOG(Info) << "Checking device : " << properties.deviceName; + uint32_t score = 0; + std::vector supported_extensions; + uint32_t qfp_count; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &qfp_count, + nullptr); + std::vector qfp(qfp_count); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &qfp_count, + qfp.data()); + std::optional graphics_queue_family; + for (uint32_t i = 0; i < qfp.size(); i++) { + // Only pick graphics queues that can also present to the surface. + // Graphics queues that can't present are rare if not nonexistent, but + // the spec allows for this, so check it anyhow. + VkBool32 surface_present_supported; + vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, i, surface_, + &surface_present_supported); + + if (!graphics_queue_family.has_value() && + qfp[i].queueFlags & VK_QUEUE_GRAPHICS_BIT && + surface_present_supported) { + graphics_queue_family = i; + } + } + // Skip physical devices that don't have a graphics queue. + if (!graphics_queue_family.has_value()) { + FT_LOG(Info) << "Skipping due to no suitable graphics queues."; + continue; + } + + // Prefer discrete GPUs. + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + score += 1 << 30; + } + uint32_t extension_count; + vkEnumerateDeviceExtensionProperties(physical_device, nullptr, + &extension_count, nullptr); + std::vector available_extensions(extension_count); + vkEnumerateDeviceExtensionProperties(physical_device, nullptr, + &extension_count, + available_extensions.data()); + + bool supports_swapchain = false; + for (const auto& available_extension : available_extensions) { + if (strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, + available_extension.extensionName) == 0) { + supports_swapchain = true; + supported_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + } + // The spec requires VK_KHR_portability_subset be enabled whenever it's + // available on a device. It's present on compatibility ICDs like + // MoltenVK. + else if (strcmp("VK_KHR_portability_subset", + available_extension.extensionName) == 0) { + supported_extensions.push_back("VK_KHR_portability_subset"); + } + // Prefer GPUs that support VK_KHR_get_memory_requirements2. + else if (strcmp(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, + available_extension.extensionName) == 0) { + score += 1 << 29; + supported_extensions.push_back( + VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + } else if (strcmp(available_extension.extensionName, + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME) == 0) { + supported_extensions.push_back( + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME); + } else if (strcmp(available_extension.extensionName, + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME) == 0) { + supported_extensions.push_back( + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); + } + } + // Skip physical devices that don't have swapchain support. + if (!supports_swapchain) { + FT_LOG(Info) << "Skipping due to lack of swapchain support."; + continue; + } + // Prefer GPUs with larger max texture sizes. + score += properties.limits.maxImageDimension2D; + if (selected_score < score) { + FT_LOG(Info) << "This is the best device so far. Score: " << score; + + selected_score = score; + physical_device_ = physical_device; + enabled_device_extensions_ = supported_extensions; + graphics_queue_family_index_ = + graphics_queue_family.value_or(std::numeric_limits::max()); + } + } + return physical_device_ != VK_NULL_HANDLE; +} + +TizenRendererVulkan::~TizenRendererVulkan() { + Cleanup(); +} +bool TizenRendererVulkan::CreateSurface(void* render_target, + void* render_target_display, + int32_t width, + int32_t height) { + width_ = width; + height_ = height; + VkWaylandSurfaceCreateInfoKHR createInfo; + memset(&createInfo, 0, sizeof(createInfo)); + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.display = static_cast(render_target_display); + createInfo.surface = static_cast(render_target); + + PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; + vkCreateWaylandSurfaceKHR = + (PFN_vkCreateWaylandSurfaceKHR)vkGetInstanceProcAddr( + instance_, "vkCreateWaylandSurfaceKHR"); + + if (!vkCreateWaylandSurfaceKHR) { + FT_LOG(Error) << "Wayland: Vulkan instance missing " + "VK_KHR_wayland_surface extension"; + return false; + } + + if (vkCreateWaylandSurfaceKHR(instance_, &createInfo, nullptr, &surface_) != + VK_SUCCESS) { + FT_LOG(Error) << "Failed to create surface."; + return false; + } + return true; +} + +bool TizenRendererVulkan::CreateLogicalDevice() { + VkDeviceQueueCreateInfo queue_info{}; + queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info.queueFamilyIndex = graphics_queue_family_index_; + queue_info.queueCount = 1; + float priority = 1.0f; + queue_info.pQueuePriorities = &priority; + + VkPhysicalDeviceFeatures device_features{}; + VkDeviceCreateInfo device_info{}; + device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_info.queueCreateInfoCount = 1; + device_info.pQueueCreateInfos = &queue_info; + device_info.enabledExtensionCount = + static_cast(enabled_device_extensions_.size()); + device_info.ppEnabledExtensionNames = enabled_device_extensions_.data(); + device_info.pEnabledFeatures = &device_features; + + if (vkCreateDevice(physical_device_, &device_info, nullptr, + &logical_device_) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to create device."; + return false; + } + return true; +} + +bool TizenRendererVulkan::GetDeviceQueue() { + vkGetDeviceQueue(logical_device_, graphics_queue_family_index_, 0, + &graphics_queue_); + return graphics_queue_ != VK_NULL_HANDLE; +} + +bool TizenRendererVulkan::CreateCommandPool() { + VkCommandPoolCreateInfo pool_info; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + pool_info.queueFamilyIndex = graphics_queue_family_index_; + if (vkCreateCommandPool(logical_device_, &pool_info, nullptr, + &swapchain_command_pool_) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to create command pool."; + return false; + } + return true; +} + +void TizenRendererVulkan::DestroyCommandPool() { + if (swapchain_command_pool_) { + vkDestroyCommandPool(logical_device_, swapchain_command_pool_, nullptr); + swapchain_command_pool_ = VK_NULL_HANDLE; + } +} + +bool TizenRendererVulkan::CreateSemaphore() { + VkSemaphoreCreateInfo semaphore_info{}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + if (vkCreateSemaphore(logical_device_, &semaphore_info, nullptr, + &present_transition_semaphore_) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to create semaphore."; + return false; + } + return true; +} + +bool TizenRendererVulkan::CreateFence() { + VkFenceCreateInfo fence_info{}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + if (vkCreateFence(logical_device_, &fence_info, nullptr, + &image_ready_fence_) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to create image ready fence."; + return false; + } + + return true; +} + +VkSurfaceFormatKHR TizenRendererVulkan::GetSwapChainFormat( + std::vector& surface_formats) { + // If the list contains only one entry with undefined format + // it means that there are no preferred surface formats and any can be chosen + if ((surface_formats.size() == 1) && + (surface_formats[0].format == VK_FORMAT_UNDEFINED)) { + return {VK_FORMAT_R8G8B8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR}; + } + + // Check if list contains most widely used R8 G8 B8 A8 format + // with nonlinear color space + for (VkSurfaceFormatKHR& surface_format : surface_formats) { + if (surface_format.format == VK_FORMAT_R8G8B8A8_UNORM) { + return surface_format; + } + } + + // Return the first format from the list + return surface_formats[0]; +} + +VkExtent2D TizenRendererVulkan::GetSwapChainExtent( + VkSurfaceCapabilitiesKHR& surface_capabilities) { + if (surface_capabilities.currentExtent.width != UINT32_MAX) { + // If the surface reports a specific extent, we must use it. + return surface_capabilities.currentExtent; + } else { + VkExtent2D swap_chain_extent{}; + swap_chain_extent.width = width_; + swap_chain_extent.height = height_; + + swap_chain_extent.width = + std::max(surface_capabilities.minImageExtent.width, + std::min(surface_capabilities.maxImageExtent.width, + swap_chain_extent.width)); + swap_chain_extent.height = + std::max(surface_capabilities.minImageExtent.height, + std::min(surface_capabilities.maxImageExtent.height, + swap_chain_extent.height)); + return swap_chain_extent; + } +} + +uint32_t TizenRendererVulkan::GetSwapChainNumImages( + VkSurfaceCapabilitiesKHR& surface_capabilities) { + const uint32_t maxImageCount = surface_capabilities.maxImageCount; + const uint32_t minImageCount = surface_capabilities.minImageCount; + uint32_t desiredImageCount = minImageCount + 1; + + // According to section 30.5 of VK 1.1, maxImageCount of zero means "that + // there is no limit on the number of images, though there may be limits + // related to the total amount of memory used by presentable images." + if (maxImageCount != 0 && desiredImageCount > maxImageCount) { + desiredImageCount = surface_capabilities.minImageCount; + } + return desiredImageCount; +} + +VkPresentModeKHR TizenRendererVulkan::GetSwapChainPresentMode( + std::vector& present_modes) { + VkPresentModeKHR present_mode = present_modes[0]; + for (const auto& mode : present_modes) { + if (mode == VK_PRESENT_MODE_FIFO_KHR) { + present_mode = mode; + break; + } + } + return present_mode; +} + +VkCompositeAlphaFlagBitsKHR TizenRendererVulkan::GetSwapChainCompositeAlpha( + VkSurfaceCapabilitiesKHR& surface_capabilities) { + if (surface_capabilities.supportedCompositeAlpha & + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) { + return VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + } else { + return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + } +} + +bool TizenRendererVulkan::InitializeSwapchain() { + VkSwapchainKHR oldSwapchain = swapchain_; + uint32_t format_count; + if (vkGetPhysicalDeviceSurfaceFormatsKHR( + physical_device_, surface_, &format_count, nullptr) != VK_SUCCESS || + format_count == 0) { + FT_LOG(Error) + << "Error occurred during presentation surface formats enumeration"; + return false; + } + std::vector formats(format_count); + if (vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, + &format_count, + formats.data()) != VK_SUCCESS) { + FT_LOG(Error) + << "Error occurred during presentation surface formats enumeration"; + return false; + } + VkSurfaceCapabilitiesKHR surface_capabilities; + if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + physical_device_, surface_, &surface_capabilities) != VK_SUCCESS) { + FT_LOG(Error) << "Could not check presentation surface capabilities"; + return false; + } + + uint32_t mode_count; + if (vkGetPhysicalDeviceSurfacePresentModesKHR( + physical_device_, surface_, &mode_count, nullptr) != VK_SUCCESS) { + FT_LOG(Error) << "Error occurred during presentation surface present modes " + "enumeration"; + return false; + } + std::vector modes(mode_count); + if (vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device_, surface_, + &mode_count, modes.data())) { + FT_LOG(Error) << "Error occurred during presentation surface present modes " + "enumeration"; + return false; + } + + surface_format_ = GetSwapChainFormat(formats); + VkExtent2D clientSize = GetSwapChainExtent(surface_capabilities); + uint32_t desiredImageCount = GetSwapChainNumImages(surface_capabilities); + VkPresentModeKHR present_mode = GetSwapChainPresentMode(modes); + VkCompositeAlphaFlagBitsKHR compositeAlpha = + GetSwapChainCompositeAlpha(surface_capabilities); + + VkSwapchainCreateInfoKHR info{}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.surface = surface_; + info.minImageCount = desiredImageCount; + info.imageFormat = surface_format_.format; + info.imageColorSpace = surface_format_.colorSpace; + info.imageExtent = clientSize; + info.imageArrayLayers = 1; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.queueFamilyIndexCount = 0; + info.pQueueFamilyIndices = nullptr; + info.preTransform = surface_capabilities.currentTransform; + info.compositeAlpha = compositeAlpha; + info.presentMode = present_mode; + info.clipped = VK_TRUE; + + if (vkCreateSwapchainKHR(logical_device_, &info, nullptr, &swapchain_) != + VK_SUCCESS) { + FT_LOG(Error) << "Could not create swap chain KHR"; + return false; + } + + if (oldSwapchain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(logical_device_, oldSwapchain, nullptr); + } + + // -------------------------------------------------------------------------- + // Fetch swapchain images + // -------------------------------------------------------------------------- + + uint32_t image_count; + if (vkGetSwapchainImagesKHR(logical_device_, swapchain_, &image_count, + nullptr) != VK_SUCCESS) { + FT_LOG(Error) << "Could not get swap chain images count"; + return false; + } + // swapchain_images_.reserve(image_count); + swapchain_images_.resize(image_count); + if (vkGetSwapchainImagesKHR(logical_device_, swapchain_, &image_count, + swapchain_images_.data()) != VK_SUCCESS) { + FT_LOG(Error) << "Could not get swap chain images"; + return false; + } + + // -------------------------------------------------------------------------- + // Record a command buffer for each of the images to be executed prior to + // presenting. + // -------------------------------------------------------------------------- + + present_transition_buffers_.resize(swapchain_images_.size()); + + VkCommandBufferAllocateInfo buffers_info{}; + buffers_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + buffers_info.commandPool = swapchain_command_pool_; + buffers_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + buffers_info.commandBufferCount = + static_cast(present_transition_buffers_.size()); + + if (vkAllocateCommandBuffers(logical_device_, &buffers_info, + present_transition_buffers_.data()) != + VK_SUCCESS) { + FT_LOG(Error) << "Could not allocate command buffers for swapchain images!"; + return false; + } + for (size_t i = 0; i < swapchain_images_.size(); i++) { + auto image = swapchain_images_[i]; + auto buffer = present_transition_buffers_[i]; + + VkCommandBufferBeginInfo begin_info{}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + if (vkBeginCommandBuffer(buffer, &begin_info) != VK_SUCCESS) { + FT_LOG(Error) << "Could not begin command buffer!"; + return false; + } + + // Filament Engine hands back the image after writing to it + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + if (vkEndCommandBuffer(buffer) != VK_SUCCESS) { + FT_LOG(Error) << "Could not end command buffer!"; + return false; + } + } + return true; +} + +bool TizenRendererVulkan::RecreateSwapChain() { + vkDeviceWaitIdle(logical_device_); + resize_pending_ = false; + for (size_t i = 0; i < present_transition_buffers_.size(); ++i) { + vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, + &present_transition_buffers_[i]); + } + DestroyCommandPool(); + if (!CreateCommandPool()) { + FT_LOG(Error) << "Fail to create command pool!"; + return false; + } + if (!InitializeSwapchain()) { + FT_LOG(Error) << "Fail to create swapchain!"; + return false; + } + return true; +} + +void TizenRendererVulkan::DestroySurface() { + if (surface_) { + vkDestroySurfaceKHR(instance_, surface_, nullptr); + } +} + +void TizenRendererVulkan::ResizeSurface(int32_t width, int32_t height) { + if (width_ != width || height_ != height) { + width_ = width; + height_ = height; + resize_pending_ = true; + } +} +uint32_t TizenRendererVulkan::GetVersion() { + return VK_MAKE_VERSION(1, 0, 0); +} + +FlutterVulkanInstanceHandle TizenRendererVulkan::GetInstanceHandle() { + return instance_; +} + +FlutterVulkanQueueHandle TizenRendererVulkan::GetQueueHandle() { + return graphics_queue_; +} + +FlutterVulkanPhysicalDeviceHandle +TizenRendererVulkan::GetPhysicalDeviceHandle() { + return physical_device_; +} + +FlutterVulkanDeviceHandle TizenRendererVulkan::GetDeviceHandle() { + return logical_device_; +} + +uint32_t TizenRendererVulkan::GetQueueIndex() { + return graphics_queue_family_index_; +} + +const char** TizenRendererVulkan::GetEnabledInstanceExtensions() { + return enabled_instance_extensions_.data(); +} + +const char** TizenRendererVulkan::GetEnabledDeviceExtensions() { + return enabled_device_extensions_.data(); +} + +void* TizenRendererVulkan::GetInstanceProcAddress( + FlutterVulkanInstanceHandle instance, + const char* name) { + return reinterpret_cast( + vkGetInstanceProcAddr(reinterpret_cast(instance), name)); +} + +FlutterVulkanImage TizenRendererVulkan::GetNextImage( + const FlutterFrameInfo* frameInfo) { + if (resize_pending_) { + RecreateSwapChain(); + } + VkResult result; + do { + result = vkAcquireNextImageKHR(logical_device_, swapchain_, UINT64_MAX, + VK_NULL_HANDLE, image_ready_fence_, + &last_image_index_); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + RecreateSwapChain(); + } else if (result == VK_SUBOPTIMAL_KHR) { + break; + } + } while (result != VK_SUCCESS); + + vkWaitForFences(logical_device_, 1, &image_ready_fence_, VK_TRUE, UINT64_MAX); + vkResetFences(logical_device_, 1, &image_ready_fence_); + return { + .struct_size = sizeof(FlutterVulkanImage), + .image = reinterpret_cast(swapchain_images_[last_image_index_]), + .format = static_cast(surface_format_.format), + }; +} + +bool TizenRendererVulkan::Present(const FlutterVulkanImage* image) { + VkPipelineStageFlags stage_flags = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo submit_info{}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.waitSemaphoreCount = 0; + submit_info.pWaitSemaphores = VK_NULL_HANDLE; + submit_info.pWaitDstStageMask = &stage_flags; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &present_transition_buffers_[last_image_index_]; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &present_transition_semaphore_; + vkQueueSubmit(graphics_queue_, 1, &submit_info, VK_NULL_HANDLE); + + VkPresentInfoKHR present_info{}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &present_transition_semaphore_; + present_info.swapchainCount = 1; + present_info.pSwapchains = &swapchain_; + present_info.pImageIndices = &last_image_index_; + VkResult result = vkQueuePresentKHR(graphics_queue_, &present_info); + + if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) { + RecreateSwapChain(); + } + vkDeviceWaitIdle(logical_device_); + return result == VK_SUCCESS; +} + +size_t TizenRendererVulkan::GetEnabledInstanceExtensionCount() { + return enabled_instance_extensions_.size(); +} + +size_t TizenRendererVulkan::GetEnabledDeviceExtensionCount() { + return enabled_device_extensions_.size(); +} + +VkCommandBuffer TizenRendererVulkan::BeginSingleTimeCommands() { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = swapchain_command_pool_; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(logical_device_, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + return commandBuffer; +} + +void TizenRendererVulkan::EndSingleTimeCommands(VkCommandBuffer commandBuffer) { + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(graphics_queue_, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(graphics_queue_); + + vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, + &commandBuffer); +} + +} // namespace flutter diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.h b/flutter/shell/platform/tizen/tizen_renderer_vulkan.h new file mode 100644 index 0000000..6fb6cb6 --- /dev/null +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.h @@ -0,0 +1,105 @@ + +// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EMBEDDER_TIZEN_RENDERER_VULKAN_H_ +#define EMBEDDER_TIZEN_RENDERER_VULKAN_H_ + +#include "flutter/shell/platform/tizen/tizen_renderer.h" +#include "flutter/shell/platform/tizen/tizen_view_base.h" + +#include +#include +#include +#include + +namespace flutter { + +class TizenRendererVulkan : public TizenRenderer { + public: + explicit TizenRendererVulkan(TizenViewBase* view); + virtual ~TizenRendererVulkan(); + + std::unique_ptr CreateExternalTexture( + const FlutterDesktopTextureInfo* texture_info) override; + + FlutterRendererConfig GetRendererConfig() override; + + bool CreateSurface(void* render_target, + void* render_target_display, + int32_t width, + int32_t height) override; + void DestroySurface() override; + void ResizeSurface(int32_t width, int32_t height) override; + uint32_t GetVersion(); + FlutterVulkanInstanceHandle GetInstanceHandle(); + FlutterVulkanQueueHandle GetQueueHandle(); + FlutterVulkanPhysicalDeviceHandle GetPhysicalDeviceHandle(); + FlutterVulkanDeviceHandle GetDeviceHandle(); + uint32_t GetQueueIndex(); + size_t GetEnabledInstanceExtensionCount(); + const char** GetEnabledInstanceExtensions(); + size_t GetEnabledDeviceExtensionCount(); + const char** GetEnabledDeviceExtensions(); + void* GetInstanceProcAddress(FlutterVulkanInstanceHandle instance, + const char* name); + FlutterVulkanImage GetNextImage(const FlutterFrameInfo* frameInfo); + bool Present(const FlutterVulkanImage* image); + VkCommandBuffer BeginSingleTimeCommands(); + void EndSingleTimeCommands(VkCommandBuffer commandBuffer); + + private: + bool CreateCommandPool(); + bool CreateInstance(); + bool CreateLogicalDevice(); + bool CreateFence(); + bool CreateSemaphore(); + void Cleanup(); + bool CheckValidationLayerSupport(); + void DestroyCommandPool(); + bool GetDeviceQueue(); + bool GetRequiredExtensions(std::vector& extensions); + VkSurfaceFormatKHR GetSwapChainFormat( + std::vector& surface_formats); + VkExtent2D GetSwapChainExtent(VkSurfaceCapabilitiesKHR& surface_capabilities); + uint32_t GetSwapChainNumImages( + VkSurfaceCapabilitiesKHR& surface_capabilities); + VkPresentModeKHR GetSwapChainPresentMode( + std::vector& present_modes); + VkCompositeAlphaFlagBitsKHR GetSwapChainCompositeAlpha( + VkSurfaceCapabilitiesKHR& surface_capabilities); + bool InitVulkan(TizenViewBase* view); + bool InitializeSwapchain(); + bool RecreateSwapChain(); + void PopulateDebugMessengerCreateInfo( + VkDebugUtilsMessengerCreateInfoEXT& createInfo); + bool PickPhysicalDevice(); + void SetupDebugMessenger(); + + bool enable_validation_layers_ = false; + + VkDebugUtilsMessengerEXT debug_messenger_ = VK_NULL_HANDLE; + VkDevice logical_device_ = VK_NULL_HANDLE; + VkInstance instance_ = VK_NULL_HANDLE; + VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; + VkQueue graphics_queue_ = VK_NULL_HANDLE; + VkSurfaceKHR surface_ = VK_NULL_HANDLE; + VkSurfaceFormatKHR surface_format_; + VkSemaphore present_transition_semaphore_ = VK_NULL_HANDLE; + VkFence image_ready_fence_ = VK_NULL_HANDLE; + VkSwapchainKHR swapchain_ = VK_NULL_HANDLE; + VkCommandPool swapchain_command_pool_ = VK_NULL_HANDLE; + std::vector swapchain_images_; + std::vector present_transition_buffers_; + std::vector enabled_device_extensions_; + std::vector enabled_instance_extensions_; + uint32_t graphics_queue_family_index_ = 0; + uint32_t last_image_index_ = 0; + bool resize_pending_ = false; + int32_t width_ = 0; + int32_t height_ = 0; +}; +} // namespace flutter + +#endif // EMBEDDER_TIZEN_RENDERER_VULKAN_H_ diff --git a/flutter/shell/platform/tizen/tizen_window_ecore_wl2.cc b/flutter/shell/platform/tizen/tizen_window_ecore_wl2.cc index 076bb3e..c009844 100644 --- a/flutter/shell/platform/tizen/tizen_window_ecore_wl2.cc +++ b/flutter/shell/platform/tizen/tizen_window_ecore_wl2.cc @@ -53,8 +53,10 @@ TizenWindowEcoreWl2::TizenWindowEcoreWl2(TizenGeometry geometry, bool transparent, bool focusable, bool top_level, - void* window_handle = nullptr) - : TizenWindow(geometry, transparent, focusable, top_level) { + void* window_handle = nullptr, + bool is_vulkan = false) + : TizenWindow(geometry, transparent, focusable, top_level), + is_vulkan_(is_vulkan) { if (!CreateWindow(window_handle)) { FT_LOG(Error) << "Failed to create a platform window."; return; @@ -109,9 +111,14 @@ bool TizenWindowEcoreWl2::CreateWindow(void* window_handle) { ecore_wl2_window_ = static_cast(window_handle); } - ecore_wl2_egl_window_ = ecore_wl2_egl_window_create( - ecore_wl2_window_, initial_geometry_.width, initial_geometry_.height); - return ecore_wl2_egl_window_ && wl2_display_; + if (is_vulkan_) { + wl2_surface_ = ecore_wl2_window_surface_get(ecore_wl2_window_); + return wl2_surface_ && wl2_display_; + } else { + ecore_wl2_egl_window_ = ecore_wl2_egl_window_create( + ecore_wl2_window_, initial_geometry_.width, initial_geometry_.height); + return ecore_wl2_egl_window_ && wl2_display_; + } } void TizenWindowEcoreWl2::SetWindowOptions() { @@ -245,30 +252,30 @@ void TizenWindowEcoreWl2::RegisterEventHandlers() { return ECORE_CALLBACK_PASS_ON; }, this)); - - ecore_event_handlers_.push_back(ecore_event_handler_add( - ECORE_WL2_EVENT_WINDOW_CONFIGURE, - [](void* data, int type, void* event) -> Eina_Bool { - auto* self = static_cast(data); - if (self->view_delegate_) { - auto* configure_event = - reinterpret_cast(event); - if (configure_event->win == self->GetWindowId()) { - ecore_wl2_egl_window_resize_with_rotation( - self->ecore_wl2_egl_window_, configure_event->x, - configure_event->y, configure_event->w, configure_event->h, - self->GetRotation()); - - self->view_delegate_->OnResize( - configure_event->x, configure_event->y, configure_event->w, - configure_event->h); - return ECORE_CALLBACK_DONE; + if (!is_vulkan_) { + ecore_event_handlers_.push_back(ecore_event_handler_add( + ECORE_WL2_EVENT_WINDOW_CONFIGURE, + [](void* data, int type, void* event) -> Eina_Bool { + auto* self = static_cast(data); + if (self->view_delegate_) { + auto* configure_event = + reinterpret_cast(event); + if (configure_event->win == self->GetWindowId()) { + ecore_wl2_egl_window_resize_with_rotation( + self->ecore_wl2_egl_window_, configure_event->x, + configure_event->y, configure_event->w, configure_event->h, + self->GetRotation()); + + self->view_delegate_->OnResize( + configure_event->x, configure_event->y, configure_event->w, + configure_event->h); + return ECORE_CALLBACK_DONE; + } } - } - return ECORE_CALLBACK_PASS_ON; - }, - this)); - + return ECORE_CALLBACK_PASS_ON; + }, + this)); + } ecore_event_handlers_.push_back(ecore_event_handler_add( ECORE_EVENT_MOUSE_BUTTON_DOWN, [](void* data, int type, void* event) -> Eina_Bool { @@ -649,4 +656,12 @@ void TizenWindowEcoreWl2::PrepareInputMethod() { [this](std::string str) { view_delegate_->OnCommit(str); }); } +void* TizenWindowEcoreWl2::GetRenderTarget() { + if (is_vulkan_) { + return wl2_surface_; + } else { + return ecore_wl2_egl_window_; + } +} + } // namespace flutter diff --git a/flutter/shell/platform/tizen/tizen_window_ecore_wl2.h b/flutter/shell/platform/tizen/tizen_window_ecore_wl2.h index 09614f4..4b33856 100644 --- a/flutter/shell/platform/tizen/tizen_window_ecore_wl2.h +++ b/flutter/shell/platform/tizen/tizen_window_ecore_wl2.h @@ -23,7 +23,8 @@ class TizenWindowEcoreWl2 : public TizenWindow { bool transparent, bool focusable, bool top_level, - void* window_handle); + void* window_handle, + bool is_vulkan); ~TizenWindowEcoreWl2(); @@ -33,7 +34,7 @@ class TizenWindowEcoreWl2 : public TizenWindow { TizenGeometry GetScreenGeometry() override; - void* GetRenderTarget() override { return ecore_wl2_egl_window_; } + void* GetRenderTarget() override; void* GetRenderTargetDisplay() override { return wl2_display_; } @@ -74,13 +75,13 @@ class TizenWindowEcoreWl2 : public TizenWindow { Ecore_Wl2_Display* ecore_wl2_display_ = nullptr; Ecore_Wl2_Window* ecore_wl2_window_ = nullptr; - Ecore_Wl2_Egl_Window* ecore_wl2_egl_window_ = nullptr; wl_display* wl2_display_ = nullptr; + wl_surface* wl2_surface_ = nullptr; std::vector ecore_event_handlers_; - tizen_policy* tizen_policy_ = nullptr; uint32_t resource_id_ = 0; + bool is_vulkan_ = false; }; } // namespace flutter From c71e81875ad1478cd139ec6dbd185d5c42420c2a Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 14 Aug 2025 13:44:47 +0800 Subject: [PATCH 2/9] Fix format error --- flutter/shell/platform/tizen/BUILD.gn | 2 +- flutter/shell/platform/tizen/public/flutter_tizen.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flutter/shell/platform/tizen/BUILD.gn b/flutter/shell/platform/tizen/BUILD.gn index 8e4874e..6fa7475 100644 --- a/flutter/shell/platform/tizen/BUILD.gn +++ b/flutter/shell/platform/tizen/BUILD.gn @@ -115,8 +115,8 @@ template("embedder") { "tizen_renderer.cc", "tizen_renderer_egl.cc", "tizen_renderer_evas_gl.cc", - "tizen_renderer_vulkan.cc", "tizen_renderer_gl.cc", + "tizen_renderer_vulkan.cc", "tizen_view_elementary.cc", "tizen_vsync_waiter.cc", "tizen_window_ecore_wl2.cc", diff --git a/flutter/shell/platform/tizen/public/flutter_tizen.h b/flutter/shell/platform/tizen/public/flutter_tizen.h index 90f2c02..b2e63e2 100644 --- a/flutter/shell/platform/tizen/public/flutter_tizen.h +++ b/flutter/shell/platform/tizen/public/flutter_tizen.h @@ -130,8 +130,8 @@ FlutterDesktopEngineGetPluginRegistrar(FlutterDesktopEngineRef engine, const char* plugin_name); // Returns the messenger associated with the engine. -FLUTTER_EXPORT FlutterDesktopMessengerRef -FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine); +FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopEngineGetMessenger( + FlutterDesktopEngineRef engine); // Posts an app control to the engine instance. FLUTTER_EXPORT void FlutterDesktopEngineNotifyAppControl( @@ -209,8 +209,8 @@ FLUTTER_EXPORT void* FlutterDesktopViewGetNativeHandle( FlutterDesktopViewRef view); // Returns the resource id of current window. -FLUTTER_EXPORT uint32_t -FlutterDesktopViewGetResourceId(FlutterDesktopViewRef view); +FLUTTER_EXPORT uint32_t FlutterDesktopViewGetResourceId( + FlutterDesktopViewRef view); // Resizes the view. // @warning This API is a work-in-progress and may change. From 25198811046f96274d2e3e073a121dc584f00304 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Wed, 27 Aug 2025 13:45:11 +0800 Subject: [PATCH 3/9] Minor refactor 1.Fix spelling issues. 2.Do not need to destroy swapchain image when destory the renderer. 3.Replace memset with zero-init. 4.Free command buffer when begin command buffer or end command buffer failed. 5.Fix return wrong api version issue. --- .../platform/tizen/tizen_renderer_vulkan.cc | 66 +++++++++++-------- .../platform/tizen/tizen_renderer_vulkan.h | 1 + 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc index 064179a..b2f8466 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc @@ -4,8 +4,6 @@ // found in the LICENSE file. #include "flutter/shell/platform/tizen/tizen_renderer_vulkan.h" -#include -#include #include #include #include @@ -59,31 +57,31 @@ bool TizenRendererVulkan::InitVulkan(TizenViewBase* view) { return false; } if (!PickPhysicalDevice()) { - FT_LOG(Error) << "Filed to pick physical device"; + FT_LOG(Error) << "Failed to pick physical device"; return false; } if (!CreateLogicalDevice()) { - FT_LOG(Error) << "Filed to create logical device"; + FT_LOG(Error) << "Failed to create logical device"; return false; } if (!GetDeviceQueue()) { - FT_LOG(Error) << "Filed to get device queue"; + FT_LOG(Error) << "Failed to get device queue"; return false; } if (!CreateSemaphore()) { - FT_LOG(Error) << "Filed to create semaphore"; + FT_LOG(Error) << "Failed to create semaphore"; return false; } if (!CreateFence()) { - FT_LOG(Error) << "Filed to create fence"; + FT_LOG(Error) << "Failed to create fence"; return false; } if (!CreateCommandPool()) { - FT_LOG(Error) << "Filed to create command pool"; + FT_LOG(Error) << "Failed to create command pool"; return false; } if (!InitializeSwapchain()) { - FT_LOG(Error) << "Filed to initialize swapchain"; + FT_LOG(Error) << "Failed to initialize swapchain"; return false; } is_valid_ = true; @@ -92,14 +90,13 @@ bool TizenRendererVulkan::InitVulkan(TizenViewBase* view) { void TizenRendererVulkan::Cleanup() { if (logical_device_) { + // Ensure all GPU work is complete before destroying anything. + vkDeviceWaitIdle(logical_device_); + for (size_t i = 0; i < present_transition_buffers_.size(); ++i) { vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, &present_transition_buffers_[i]); } - for (size_t i = 0; i < swapchain_images_.size(); ++i) { - vkDestroyImage(logical_device_, swapchain_images_[i], nullptr); - } - if (swapchain_ != VK_NULL_HANDLE) { vkDestroySwapchainKHR(logical_device_, swapchain_, nullptr); swapchain_ = VK_NULL_HANDLE; @@ -108,6 +105,7 @@ void TizenRendererVulkan::Cleanup() { vkDestroyFence(logical_device_, image_ready_fence_, nullptr); vkDestroySemaphore(logical_device_, present_transition_semaphore_, nullptr); vkDestroyDevice(logical_device_, nullptr); + logical_device_ = VK_NULL_HANDLE; } DestroySurface(); if (enable_validation_layers_) { @@ -115,6 +113,7 @@ void TizenRendererVulkan::Cleanup() { } if (instance_ != VK_NULL_HANDLE) { vkDestroyInstance(instance_, nullptr); + instance_ = VK_NULL_HANDLE; } } @@ -402,17 +401,11 @@ bool TizenRendererVulkan::PickPhysicalDevice() { available_extension.extensionName) == 0) { supports_swapchain = true; supported_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - } - // The spec requires VK_KHR_portability_subset be enabled whenever it's - // available on a device. It's present on compatibility ICDs like - // MoltenVK. - else if (strcmp("VK_KHR_portability_subset", - available_extension.extensionName) == 0) { + } else if (strcmp("VK_KHR_portability_subset", + available_extension.extensionName) == 0) { supported_extensions.push_back("VK_KHR_portability_subset"); - } - // Prefer GPUs that support VK_KHR_get_memory_requirements2. - else if (strcmp(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, - available_extension.extensionName) == 0) { + } else if (strcmp(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, + available_extension.extensionName) == 0) { score += 1 << 29; supported_extensions.push_back( VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); @@ -455,8 +448,7 @@ bool TizenRendererVulkan::CreateSurface(void* render_target, int32_t height) { width_ = width; height_ = height; - VkWaylandSurfaceCreateInfoKHR createInfo; - memset(&createInfo, 0, sizeof(createInfo)); + VkWaylandSurfaceCreateInfoKHR createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; createInfo.pNext = nullptr; createInfo.flags = 0; @@ -824,7 +816,7 @@ void TizenRendererVulkan::ResizeSurface(int32_t width, int32_t height) { } } uint32_t TizenRendererVulkan::GetVersion() { - return VK_MAKE_VERSION(1, 0, 0); + return VK_API_VERSION_1_1; } FlutterVulkanInstanceHandle TizenRendererVulkan::GetInstanceHandle() { @@ -941,20 +933,36 @@ VkCommandBuffer TizenRendererVulkan::BeginSingleTimeCommands() { beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - vkBeginCommandBuffer(commandBuffer, &beginInfo); + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, + &commandBuffer); + FT_LOG(Error) << "Failed to begin one-time command buffer."; + return VK_NULL_HANDLE; + } return commandBuffer; } void TizenRendererVulkan::EndSingleTimeCommands(VkCommandBuffer commandBuffer) { - vkEndCommandBuffer(commandBuffer); + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + FT_LOG(Error) << "Failed to end one-time command buffer."; + vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, + &commandBuffer); + return; + } VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &commandBuffer; - vkQueueSubmit(graphics_queue_, 1, &submitInfo, VK_NULL_HANDLE); + if (vkQueueSubmit(graphics_queue_, 1, &submitInfo, VK_NULL_HANDLE) != + VK_SUCCESS) { + FT_LOG(Error) << "Failed to submit one-time command buffer."; + vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, + &commandBuffer); + return; + } vkQueueWaitIdle(graphics_queue_); vkFreeCommandBuffers(logical_device_, swapchain_command_pool_, 1, diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.h b/flutter/shell/platform/tizen/tizen_renderer_vulkan.h index 6fb6cb6..cacd5d5 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.h +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace flutter { From f6dda15cc116fc44fbf1d8b2b3d63593d69c9b32 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 26 Sep 2025 17:33:57 +0800 Subject: [PATCH 4/9] Fix code review issues --- flutter/shell/platform/tizen/tizen_renderer_vulkan.cc | 5 +++-- flutter/shell/platform/tizen/tizen_renderer_vulkan.h | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc index b2f8466..44f4767 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc @@ -1,12 +1,13 @@ - -// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved. +// Copyright 2025 Samsung Electronics Co., Ltd. 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/tizen/tizen_renderer_vulkan.h" + #include #include #include + #include "flutter/shell/platform/tizen/flutter_tizen_engine.h" #include "flutter/shell/platform/tizen/logger.h" diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.h b/flutter/shell/platform/tizen/tizen_renderer_vulkan.h index cacd5d5..e2a71d4 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.h +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.h @@ -1,5 +1,4 @@ - -// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved. +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. From 6f7c767f9a5591673b734d1de4bf2b5620d9de82 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Sun, 28 Sep 2025 11:00:06 +0800 Subject: [PATCH 5/9] Change render type EcoreVulkan to EVulkan --- flutter/shell/platform/tizen/flutter_tizen.cc | 3 +-- flutter/shell/platform/tizen/flutter_tizen_engine.cc | 2 +- flutter/shell/platform/tizen/public/flutter_tizen.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/flutter/shell/platform/tizen/flutter_tizen.cc b/flutter/shell/platform/tizen/flutter_tizen.cc index d9ca62d..1eda450 100644 --- a/flutter/shell/platform/tizen/flutter_tizen.cc +++ b/flutter/shell/platform/tizen/flutter_tizen.cc @@ -212,8 +212,7 @@ FlutterDesktopViewRef FlutterDesktopViewCreateFromNewWindow( window_properties.pointing_device_support, window_properties.floating_menu_support, window_properties.window_handle, - window_properties.renderer_type == - FlutterDesktopRendererType::kEcoreVulkan); + window_properties.renderer_type == kEVulkan); } auto view = std::make_unique( diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.cc b/flutter/shell/platform/tizen/flutter_tizen_engine.cc index ae1e3d4..54b6c2a 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.cc @@ -100,7 +100,7 @@ std::unique_ptr FlutterTizenEngine::CreateRenderer( #endif return std::make_unique( view_->tizen_view(), project_->HasArgument("--enable-impeller")); - case FlutterDesktopRendererType::kEcoreVulkan: + case FlutterDesktopRendererType::kEVulkan: return std::make_unique(view_->tizen_view()); } } diff --git a/flutter/shell/platform/tizen/public/flutter_tizen.h b/flutter/shell/platform/tizen/public/flutter_tizen.h index 174a750..be8b7ce 100644 --- a/flutter/shell/platform/tizen/public/flutter_tizen.h +++ b/flutter/shell/platform/tizen/public/flutter_tizen.h @@ -31,7 +31,7 @@ typedef enum { // The renderer based on EGL. kEGL, // The renderer based on Vulkan. - kEcoreVulkan + kEVulkan } FlutterDesktopRendererType; typedef enum { From a2e4c254af8844646eb51f4e8bceb654a9e159e2 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 9 Oct 2025 19:51:32 +0800 Subject: [PATCH 6/9] Add more result check for vkAcquireNextImageKHR --- .../platform/tizen/tizen_renderer_vulkan.cc | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc index 44f4767..5e12b5f 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc @@ -862,16 +862,34 @@ FlutterVulkanImage TizenRendererVulkan::GetNextImage( RecreateSwapChain(); } VkResult result; - do { + while (true) { result = vkAcquireNextImageKHR(logical_device_, swapchain_, UINT64_MAX, VK_NULL_HANDLE, image_ready_fence_, &last_image_index_); - if (result == VK_ERROR_OUT_OF_DATE_KHR) { + if (result == VK_SUCCESS) { + // Image successfully acquired. + break; + } else if (result == VK_ERROR_OUT_OF_DATE_KHR) { + // Swapchain is out of date, recreate it and try again. RecreateSwapChain(); + continue; } else if (result == VK_SUBOPTIMAL_KHR) { + // Swapchain is suboptimal, but we can present with it. Break to proceed. + break; + } else { + // An unexpected error occurred. Log it and break to avoid an infinite + // loop. + FT_LOG(Error) << "vkAcquireNextImageKHR failed with critical error: " + << result; break; } - } while (result != VK_SUCCESS); + } + + // Check if the acquisition was successful or suboptimal. + // If not, return an empty image to signal failure to the Flutter engine. + if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + return FlutterVulkanImage{}; + } vkWaitForFences(logical_device_, 1, &image_ready_fence_, VK_TRUE, UINT64_MAX); vkResetFences(logical_device_, 1, &image_ready_fence_); From ebb73f77744e3ad5412cfb243deaa545325ab81b Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 9 Oct 2025 20:16:06 +0800 Subject: [PATCH 7/9] Return RecreateSwapChain result as Present result when vkQueuePresentKHR result is VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR. --- flutter/shell/platform/tizen/tizen_renderer_vulkan.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc index 5e12b5f..764ac5c 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc +++ b/flutter/shell/platform/tizen/tizen_renderer_vulkan.cc @@ -924,10 +924,11 @@ bool TizenRendererVulkan::Present(const FlutterVulkanImage* image) { VkResult result = vkQueuePresentKHR(graphics_queue_, &present_info); if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) { - RecreateSwapChain(); + return RecreateSwapChain(); + } else { + vkDeviceWaitIdle(logical_device_); + return result == VK_SUCCESS; } - vkDeviceWaitIdle(logical_device_); - return result == VK_SUCCESS; } size_t TizenRendererVulkan::GetEnabledInstanceExtensionCount() { From a310c30a1cb95a4ceb5daad030cf1d4bdc8ebfd4 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 10 Oct 2025 17:52:11 +0800 Subject: [PATCH 8/9] Move vulkan renderer code to experimantal --- flutter/shell/platform/tizen/BUILD.gn | 34 +++++++++++++++++-- .../platform/tizen/flutter_tizen_engine.cc | 7 ++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/flutter/shell/platform/tizen/BUILD.gn b/flutter/shell/platform/tizen/BUILD.gn index 130952e..5ecf06f 100644 --- a/flutter/shell/platform/tizen/BUILD.gn +++ b/flutter/shell/platform/tizen/BUILD.gn @@ -116,7 +116,6 @@ template("embedder") { "tizen_renderer_egl.cc", "tizen_renderer_evas_gl.cc", "tizen_renderer_gl.cc", - "tizen_renderer_vulkan.cc", "tizen_view_elementary.cc", "tizen_vsync_waiter.cc", "tizen_window_ecore_wl2.cc", @@ -153,13 +152,13 @@ template("embedder") { "tdm-client", "tizen-extension-client", "vconf", - "vulkan", "wayland-client", "EGL", "GLESv2", ] - if (target_name == "flutter_tizen_common") { + if (target_name == "flutter_tizen_common" || + target_name == "flutter_tizen_common_experimental") { sources += [ "channels/tizen_shell.cc" ] libs += [ @@ -171,6 +170,14 @@ template("embedder") { defines += invoker.defines defines += [ "FLUTTER_ENGINE_NO_PROTOTYPES" ] + if (target_name == "flutter_tizen_mobile_experimental" || + target_name == "flutter_tizen_tv_experimental" || + target_name == "flutter_tizen_common_experimental") { + defines += [ "FLUTTER_TIZEN_EXPERIMENTAL" ] + sources += [ "tizen_renderer_vulkan.cc" ] + libs += [ "vulkan" ] + } + if (api_version != "6.0") { sources += [ "flutter_tizen_nui.cc", @@ -220,18 +227,36 @@ embedder("flutter_tizen_mobile") { defines = [ "MOBILE_PROFILE" ] } +embedder("flutter_tizen_mobile_experimental") { + target_type = "shared_library" + + defines = [ "MOBILE_PROFILE" ] +} + embedder("flutter_tizen_tv") { target_type = "shared_library" defines = [ "TV_PROFILE" ] } +embedder("flutter_tizen_tv_experimental") { + target_type = "shared_library" + + defines = [ "TV_PROFILE" ] +} + embedder("flutter_tizen_common") { target_type = "shared_library" defines = [ "COMMON_PROFILE" ] } +embedder("flutter_tizen_common_experimental") { + target_type = "shared_library" + + defines = [ "COMMON_PROFILE" ] +} + embedder("flutter_tizen_source") { target_type = "source_set" @@ -276,8 +301,11 @@ copy("publish_headers_tizen") { group("flutter_tizen") { deps = [ ":flutter_tizen_common", + ":flutter_tizen_common_experimental", ":flutter_tizen_mobile", + ":flutter_tizen_mobile_experimental", ":flutter_tizen_tv", + ":flutter_tizen_tv_experimental", ":publish_cpp_client_wrapper", ":publish_headers_tizen", ] diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.cc b/flutter/shell/platform/tizen/flutter_tizen_engine.cc index 54b6c2a..af5e0f5 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.cc @@ -18,7 +18,10 @@ #include "flutter/shell/platform/tizen/tizen_input_method_context.h" #include "flutter/shell/platform/tizen/tizen_renderer_egl.h" #include "flutter/shell/platform/tizen/tizen_renderer_evas_gl.h" + +#ifdef FLUTTER_TIZEN_EXPERIMENTAL #include "flutter/shell/platform/tizen/tizen_renderer_vulkan.h" +#endif #ifdef NUI_SUPPORT #include "flutter/shell/platform/tizen/tizen_renderer_nui_gl.h" @@ -101,7 +104,11 @@ std::unique_ptr FlutterTizenEngine::CreateRenderer( return std::make_unique( view_->tizen_view(), project_->HasArgument("--enable-impeller")); case FlutterDesktopRendererType::kEVulkan: +#ifdef FLUTTER_TIZEN_EXPERIMENTAL return std::make_unique(view_->tizen_view()); +#else + return nullptr; +#endif } } From 133d12a7a6feebced9ccd8973958835e913997f8 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 10 Oct 2025 18:11:57 +0800 Subject: [PATCH 9/9] Update build.yml --- .github/workflows/build.yml | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c82a74..cf4bc04 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,17 @@ jobs: - uses: actions/upload-artifact@v4 with: name: tizen-${{ matrix.api-version }}-${{ matrix.arch }} - path: src/out/build/libflutter_tizen*.so + path: | + src/out/build/libflutter_tizen_common.so + src/out/build/libflutter_tizen_mobile.so + src/out/build/libflutter_tizen_tv.so + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + with: + name: tizen-${{ matrix.api-version }}-${{ matrix.arch }}_experimental + path: | + src/out/build/libflutter_tizen*_experimental.so if-no-files-found: error - uses: actions/upload-artifact@v4 @@ -85,7 +95,17 @@ jobs: if: ${{ github.event_name == 'push' }} with: name: tizen-${{ matrix.api-version }}-${{ matrix.arch }}_symbols - path: src/out/build/so.unstripped/libflutter_tizen*.so + path: | + src/out/build/so.unstripped/libflutter_tizen_common.so + src/out/build/so.unstripped/libflutter_tizen_mobile.so + src/out/build/so.unstripped/libflutter_tizen_tv.so + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + if: ${{ github.event_name == 'push' }} + with: + name: tizen-${{ matrix.api-version }}-${{ matrix.arch }}_experimental_symbols + path: src/out/build/so.unstripped/libflutter_tizen*_experimental.so if-no-files-found: error - uses: actions/upload-artifact@v4