| // 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 <cassert> | 
 | #include <memory> | 
 | #include <optional> | 
 |  | 
 | #include "flutter/flutter_vma/flutter_skia_vma.h" | 
 | #include "flutter/fml/logging.h" | 
 | #include "flutter/shell/common/context_options.h" | 
 | #include "flutter/testing/test_vulkan_context.h" | 
 | #include "flutter/vulkan/vulkan_skia_proc_table.h" | 
 |  | 
 | #include "flutter/fml/memory/ref_ptr.h" | 
 | #include "flutter/fml/native_library.h" | 
 | #include "flutter/vulkan/swiftshader_path.h" | 
 | #include "third_party/skia/include/core/SkSurface.h" | 
 | #include "third_party/skia/include/gpu/GrDirectContext.h" | 
 | #include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h" | 
 | #include "third_party/skia/include/gpu/vk/GrVkExtensions.h" | 
 | #include "vulkan/vulkan_core.h" | 
 |  | 
 | namespace flutter { | 
 | namespace testing { | 
 |  | 
 | TestVulkanContext::TestVulkanContext() { | 
 |   // --------------------------------------------------------------------------- | 
 |   // Initialize basic Vulkan state using the Swiftshader ICD. | 
 |   // --------------------------------------------------------------------------- | 
 |  | 
 |   const char* vulkan_icd = VULKAN_SO_PATH; | 
 |  | 
 |   // TODO(96949): Clean this up and pass a native library directly to | 
 |   //              VulkanProcTable. | 
 |   if (!fml::NativeLibrary::Create(VULKAN_SO_PATH)) { | 
 |     FML_LOG(ERROR) << "Couldn't find Vulkan ICD \"" << vulkan_icd | 
 |                    << "\", trying \"libvulkan.so\" instead."; | 
 |     vulkan_icd = "libvulkan.so"; | 
 |   } | 
 |  | 
 |   FML_LOG(INFO) << "Using Vulkan ICD: " << vulkan_icd; | 
 |  | 
 |   vk_ = fml::MakeRefCounted<vulkan::VulkanProcTable>(vulkan_icd); | 
 |   if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { | 
 |     FML_LOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; | 
 |     return; | 
 |   } | 
 |  | 
 |   application_ = std::make_unique<vulkan::VulkanApplication>( | 
 |       *vk_, "Flutter Unittests", std::vector<std::string>{}, | 
 |       VK_MAKE_VERSION(1, 0, 0), VK_MAKE_VERSION(1, 0, 0), true); | 
 |   if (!application_->IsValid()) { | 
 |     FML_LOG(ERROR) << "Failed to initialize basic Vulkan state."; | 
 |     return; | 
 |   } | 
 |   if (!vk_->AreInstanceProcsSetup()) { | 
 |     FML_LOG(ERROR) << "Failed to acquire full proc table."; | 
 |     return; | 
 |   } | 
 |  | 
 |   device_ = application_->AcquireFirstCompatibleLogicalDevice(); | 
 |   if (!device_ || !device_->IsValid()) { | 
 |     FML_LOG(ERROR) << "Failed to create compatible logical device."; | 
 |     return; | 
 |   } | 
 |  | 
 |   // --------------------------------------------------------------------------- | 
 |   // Create a Skia context. | 
 |   // For creating SkSurfaces from VkImages and snapshotting them, etc. | 
 |   // --------------------------------------------------------------------------- | 
 |  | 
 |   uint32_t skia_features = 0; | 
 |   if (!device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) { | 
 |     FML_LOG(ERROR) << "Failed to get physical device features."; | 
 |  | 
 |     return; | 
 |   } | 
 |  | 
 |   auto get_proc = vulkan::CreateSkiaGetProc(vk_); | 
 |   if (get_proc == nullptr) { | 
 |     FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia."; | 
 |     return; | 
 |   } | 
 |  | 
 |   sk_sp<skgpu::VulkanMemoryAllocator> allocator = | 
 |       flutter::FlutterSkiaVulkanMemoryAllocator::Make( | 
 |           VK_MAKE_VERSION(1, 0, 0), application_->GetInstance(), | 
 |           device_->GetPhysicalDeviceHandle(), device_->GetHandle(), vk_, true); | 
 |  | 
 |   GrVkExtensions extensions; | 
 |  | 
 |   GrVkBackendContext backend_context = {}; | 
 |   backend_context.fInstance = application_->GetInstance(); | 
 |   backend_context.fPhysicalDevice = device_->GetPhysicalDeviceHandle(); | 
 |   backend_context.fDevice = device_->GetHandle(); | 
 |   backend_context.fQueue = device_->GetQueueHandle(); | 
 |   backend_context.fGraphicsQueueIndex = device_->GetGraphicsQueueIndex(); | 
 |   backend_context.fMinAPIVersion = VK_MAKE_VERSION(1, 0, 0); | 
 |   backend_context.fMaxAPIVersion = VK_MAKE_VERSION(1, 0, 0); | 
 |   backend_context.fFeatures = skia_features; | 
 |   backend_context.fVkExtensions = &extensions; | 
 |   backend_context.fGetProc = get_proc; | 
 |   backend_context.fOwnsInstanceAndDevice = false; | 
 |   backend_context.fMemoryAllocator = allocator; | 
 |  | 
 |   GrContextOptions options = | 
 |       MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kVulkan); | 
 |   options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; | 
 |   context_ = GrDirectContexts::MakeVulkan(backend_context, options); | 
 | } | 
 |  | 
 | TestVulkanContext::~TestVulkanContext() { | 
 |   if (context_) { | 
 |     context_->releaseResourcesAndAbandonContext(); | 
 |   } | 
 | } | 
 |  | 
 | std::optional<TestVulkanImage> TestVulkanContext::CreateImage( | 
 |     const SkISize& size) const { | 
 |   TestVulkanImage result; | 
 |  | 
 |   VkImageCreateInfo info = { | 
 |       .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | 
 |       .pNext = nullptr, | 
 |       .flags = 0, | 
 |       .imageType = VK_IMAGE_TYPE_2D, | 
 |       .format = VK_FORMAT_R8G8B8A8_UNORM, | 
 |       .extent = VkExtent3D{static_cast<uint32_t>(size.width()), | 
 |                            static_cast<uint32_t>(size.height()), 1}, | 
 |       .mipLevels = 1, | 
 |       .arrayLayers = 1, | 
 |       .samples = VK_SAMPLE_COUNT_1_BIT, | 
 |       .tiling = VK_IMAGE_TILING_OPTIMAL, | 
 |       .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | | 
 |                VK_IMAGE_USAGE_TRANSFER_DST_BIT | | 
 |                VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, | 
 |       .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | 
 |       .queueFamilyIndexCount = 0, | 
 |       .pQueueFamilyIndices = nullptr, | 
 |       .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | 
 |   }; | 
 |  | 
 |   VkImage image; | 
 |   if (VK_CALL_LOG_ERROR(VK_CALL_LOG_ERROR( | 
 |           vk_->CreateImage(device_->GetHandle(), &info, nullptr, &image)))) { | 
 |     return std::nullopt; | 
 |   } | 
 |  | 
 |   result.image_ = vulkan::VulkanHandle<VkImage>( | 
 |       image, [&vk = vk_, &device = device_](VkImage image) { | 
 |         vk->DestroyImage(device->GetHandle(), image, nullptr); | 
 |       }); | 
 |  | 
 |   VkMemoryRequirements mem_req; | 
 |   vk_->GetImageMemoryRequirements(device_->GetHandle(), image, &mem_req); | 
 |   VkMemoryAllocateInfo alloc_info{}; | 
 |   alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | 
 |   alloc_info.allocationSize = mem_req.size; | 
 |   alloc_info.memoryTypeIndex = static_cast<uint32_t>(__builtin_ctz( | 
 |       mem_req.memoryTypeBits & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); | 
 |  | 
 |   VkDeviceMemory memory; | 
 |   if (VK_CALL_LOG_ERROR(vk_->AllocateMemory(device_->GetHandle(), &alloc_info, | 
 |                                             nullptr, &memory)) != VK_SUCCESS) { | 
 |     return std::nullopt; | 
 |   } | 
 |  | 
 |   result.memory_ = vulkan::VulkanHandle<VkDeviceMemory>{ | 
 |       memory, [&vk = vk_, &device = device_](VkDeviceMemory memory) { | 
 |         vk->FreeMemory(device->GetHandle(), memory, nullptr); | 
 |       }}; | 
 |  | 
 |   if (VK_CALL_LOG_ERROR(VK_CALL_LOG_ERROR(vk_->BindImageMemory( | 
 |           device_->GetHandle(), result.image_, result.memory_, 0)))) { | 
 |     return std::nullopt; | 
 |   } | 
 |  | 
 |   result.context_ = | 
 |       fml::RefPtr<TestVulkanContext>(const_cast<TestVulkanContext*>(this)); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | sk_sp<GrDirectContext> TestVulkanContext::GetGrDirectContext() const { | 
 |   return context_; | 
 | } | 
 |  | 
 | }  // namespace testing | 
 | }  // namespace flutter |