| // 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_surface_vulkan.h" | |
| #include <utility> | |
| #include "flutter/flutter_vma/flutter_skia_vma.h" | |
| #include "flutter/shell/common/shell_io_manager.h" | |
| #include "flutter/shell/gpu/gpu_surface_vulkan.h" | |
| #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" | |
| #include "flutter/vulkan/vulkan_skia_proc_table.h" | |
| #include "include/gpu/GrDirectContext.h" | |
| #include "include/gpu/vk/GrVkBackendContext.h" | |
| #include "include/gpu/vk/VulkanExtensions.h" | |
| #include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" | |
| namespace flutter { | |
| EmbedderSurfaceVulkan::EmbedderSurfaceVulkan( | |
| uint32_t version, | |
| VkInstance instance, | |
| size_t instance_extension_count, | |
| const char** instance_extensions, | |
| size_t device_extension_count, | |
| const char** device_extensions, | |
| VkPhysicalDevice physical_device, | |
| VkDevice device, | |
| uint32_t queue_family_index, | |
| VkQueue queue, | |
| const VulkanDispatchTable& vulkan_dispatch_table, | |
| std::shared_ptr<EmbedderExternalViewEmbedder> external_view_embedder) | |
| : vk_(fml::MakeRefCounted<vulkan::VulkanProcTable>( | |
| vulkan_dispatch_table.get_instance_proc_address)), | |
| device_(*vk_, | |
| vulkan::VulkanHandle<VkPhysicalDevice>{physical_device}, | |
| vulkan::VulkanHandle<VkDevice>{device}, | |
| queue_family_index, | |
| vulkan::VulkanHandle<VkQueue>{queue}), | |
| vulkan_dispatch_table_(vulkan_dispatch_table), | |
| external_view_embedder_(std::move(external_view_embedder)) { | |
| // Make sure all required members of the dispatch table are checked. | |
| if (!vulkan_dispatch_table_.get_instance_proc_address || | |
| !vulkan_dispatch_table_.get_next_image || | |
| !vulkan_dispatch_table_.present_image) { | |
| return; | |
| } | |
| bool success = vk_->SetupInstanceProcAddresses( | |
| vulkan::VulkanHandle<VkInstance>{instance}); | |
| if (!success) { | |
| FML_LOG(ERROR) << "Could not setup instance proc addresses."; | |
| return; | |
| } | |
| success = | |
| vk_->SetupDeviceProcAddresses(vulkan::VulkanHandle<VkDevice>{device}); | |
| if (!success) { | |
| FML_LOG(ERROR) << "Could not setup device proc addresses."; | |
| return; | |
| } | |
| if (!vk_->IsValid()) { | |
| FML_LOG(ERROR) << "VulkanProcTable invalid."; | |
| return; | |
| } | |
| main_context_ = CreateGrContext(instance, version, instance_extension_count, | |
| instance_extensions, device_extension_count, | |
| device_extensions, ContextType::kRender); | |
| // TODO(96954): Add a second (optional) queue+family index to the Embedder API | |
| // to allow embedders to specify a dedicated transfer queue for | |
| // use by the resource context. Queue families with graphics | |
| // capability can always be used for memory transferring, but it | |
| // would be advantageous to use a dedicated transter queue here. | |
| resource_context_ = CreateGrContext( | |
| instance, version, instance_extension_count, instance_extensions, | |
| device_extension_count, device_extensions, ContextType::kResource); | |
| valid_ = main_context_ && resource_context_; | |
| } | |
| EmbedderSurfaceVulkan::~EmbedderSurfaceVulkan() { | |
| if (main_context_) { | |
| main_context_->releaseResourcesAndAbandonContext(); | |
| } | |
| if (resource_context_) { | |
| resource_context_->releaseResourcesAndAbandonContext(); | |
| } | |
| } | |
| // |GPUSurfaceVulkanDelegate| | |
| const vulkan::VulkanProcTable& EmbedderSurfaceVulkan::vk() { | |
| return *vk_; | |
| } | |
| // |GPUSurfaceVulkanDelegate| | |
| FlutterVulkanImage EmbedderSurfaceVulkan::AcquireImage(const SkISize& size) { | |
| return vulkan_dispatch_table_.get_next_image(size); | |
| } | |
| // |GPUSurfaceVulkanDelegate| | |
| bool EmbedderSurfaceVulkan::PresentImage(VkImage image, VkFormat format) { | |
| return vulkan_dispatch_table_.present_image(image, format); | |
| } | |
| // |EmbedderSurface| | |
| bool EmbedderSurfaceVulkan::IsValid() const { | |
| return valid_; | |
| } | |
| // |EmbedderSurface| | |
| std::unique_ptr<Surface> EmbedderSurfaceVulkan::CreateGPUSurface() { | |
| const bool render_to_surface = !external_view_embedder_; | |
| return std::make_unique<GPUSurfaceVulkan>(this, main_context_, | |
| render_to_surface); | |
| } | |
| // |EmbedderSurface| | |
| sk_sp<GrDirectContext> EmbedderSurfaceVulkan::CreateResourceContext() const { | |
| return resource_context_; | |
| } | |
| sk_sp<GrDirectContext> EmbedderSurfaceVulkan::CreateGrContext( | |
| VkInstance instance, | |
| uint32_t version, | |
| size_t instance_extension_count, | |
| const char** instance_extensions, | |
| size_t device_extension_count, | |
| const char** device_extensions, | |
| ContextType context_type) const { | |
| VkPhysicalDeviceFeatures features; | |
| if (!device_.GetPhysicalDeviceFeatures(&features)) { | |
| FML_LOG(ERROR) << "Failed to get physical device features."; | |
| return nullptr; | |
| } | |
| auto get_proc = CreateSkiaGetProc(vk_); | |
| if (get_proc == nullptr) { | |
| FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia."; | |
| return nullptr; | |
| } | |
| skgpu::VulkanExtensions extensions; | |
| GrVkBackendContext backend_context = {}; | |
| backend_context.fInstance = instance; | |
| backend_context.fPhysicalDevice = device_.GetPhysicalDeviceHandle(); | |
| backend_context.fDevice = device_.GetHandle(); | |
| backend_context.fQueue = device_.GetQueueHandle(); | |
| backend_context.fGraphicsQueueIndex = device_.GetGraphicsQueueIndex(); | |
| backend_context.fMaxAPIVersion = version; | |
| backend_context.fDeviceFeatures = &features; | |
| backend_context.fVkExtensions = &extensions; | |
| backend_context.fGetProc = get_proc; | |
| uint32_t vulkan_api_version = version; | |
| sk_sp<skgpu::VulkanMemoryAllocator> allocator = | |
| flutter::FlutterSkiaVulkanMemoryAllocator::Make( | |
| vulkan_api_version, instance, device_.GetPhysicalDeviceHandle(), | |
| device_.GetHandle(), vk_, true); | |
| backend_context.fMemoryAllocator = allocator; | |
| extensions.init(backend_context.fGetProc, backend_context.fInstance, | |
| backend_context.fPhysicalDevice, instance_extension_count, | |
| instance_extensions, device_extension_count, | |
| device_extensions); | |
| GrContextOptions options = | |
| MakeDefaultContextOptions(context_type, GrBackendApi::kVulkan); | |
| options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; | |
| return GrDirectContexts::MakeVulkan(backend_context, options); | |
| } | |
| } // namespace flutter |