| // 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/vulkan/vulkan_proc_table.h" |
| |
| #include <dlfcn.h> |
| |
| #include "flutter/fml/logging.h" |
| |
| #define ACQUIRE_PROC(name, context) \ |
| if (!(name = AcquireProc("vk" #name, context))) { \ |
| FML_DLOG(INFO) << "Could not acquire proc: vk" << #name; \ |
| return false; \ |
| } |
| |
| namespace vulkan { |
| |
| VulkanProcTable::VulkanProcTable() |
| : handle_(nullptr), acquired_mandatory_proc_addresses_(false) { |
| acquired_mandatory_proc_addresses_ = |
| OpenLibraryHandle() && SetupLoaderProcAddresses(); |
| } |
| |
| VulkanProcTable::~VulkanProcTable() { |
| CloseLibraryHandle(); |
| } |
| |
| bool VulkanProcTable::HasAcquiredMandatoryProcAddresses() const { |
| return acquired_mandatory_proc_addresses_; |
| } |
| |
| bool VulkanProcTable::IsValid() const { |
| return instance_ && device_; |
| } |
| |
| bool VulkanProcTable::AreInstanceProcsSetup() const { |
| return instance_; |
| } |
| |
| bool VulkanProcTable::AreDeviceProcsSetup() const { |
| return device_; |
| } |
| |
| bool VulkanProcTable::SetupLoaderProcAddresses() { |
| if (handle_ == nullptr) { |
| return true; |
| } |
| |
| GetInstanceProcAddr = |
| #if VULKAN_LINK_STATICALLY |
| GetInstanceProcAddr = &vkGetInstanceProcAddr; |
| #else // VULKAN_LINK_STATICALLY |
| reinterpret_cast<PFN_vkGetInstanceProcAddr>( |
| dlsym(handle_, "vkGetInstanceProcAddr")); |
| #endif // VULKAN_LINK_STATICALLY |
| |
| if (!GetInstanceProcAddr) { |
| FML_DLOG(WARNING) << "Could not acquire vkGetInstanceProcAddr."; |
| return false; |
| } |
| |
| VulkanHandle<VkInstance> null_instance(VK_NULL_HANDLE, nullptr); |
| |
| ACQUIRE_PROC(CreateInstance, null_instance); |
| ACQUIRE_PROC(EnumerateInstanceExtensionProperties, null_instance); |
| ACQUIRE_PROC(EnumerateInstanceLayerProperties, null_instance); |
| |
| return true; |
| } |
| |
| bool VulkanProcTable::SetupInstanceProcAddresses( |
| const VulkanHandle<VkInstance>& handle) { |
| ACQUIRE_PROC(CreateDevice, handle); |
| ACQUIRE_PROC(DestroyDevice, handle); |
| ACQUIRE_PROC(DestroyInstance, handle); |
| ACQUIRE_PROC(EnumerateDeviceLayerProperties, handle); |
| ACQUIRE_PROC(EnumeratePhysicalDevices, handle); |
| ACQUIRE_PROC(GetDeviceProcAddr, handle); |
| ACQUIRE_PROC(GetPhysicalDeviceFeatures, handle); |
| ACQUIRE_PROC(GetPhysicalDeviceQueueFamilyProperties, handle); |
| #if OS_ANDROID |
| ACQUIRE_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR, handle); |
| ACQUIRE_PROC(GetPhysicalDeviceSurfaceFormatsKHR, handle); |
| ACQUIRE_PROC(GetPhysicalDeviceSurfacePresentModesKHR, handle); |
| ACQUIRE_PROC(GetPhysicalDeviceSurfaceSupportKHR, handle); |
| ACQUIRE_PROC(DestroySurfaceKHR, handle); |
| ACQUIRE_PROC(CreateAndroidSurfaceKHR, handle); |
| #endif // OS_ANDROID |
| |
| // The debug report functions are optional. We don't want proc acquisition to |
| // fail here because the optional methods were not present (since ACQUIRE_PROC |
| // returns false on failure). Wrap the optional proc acquisitions in an |
| // anonymous lambda and invoke it. We don't really care about the result since |
| // users of Debug reporting functions check for their presence explicitly. |
| [this, &handle]() -> bool { |
| ACQUIRE_PROC(CreateDebugReportCallbackEXT, handle); |
| ACQUIRE_PROC(DestroyDebugReportCallbackEXT, handle); |
| return true; |
| }(); |
| |
| instance_ = {handle, nullptr}; |
| return true; |
| } |
| |
| bool VulkanProcTable::SetupDeviceProcAddresses( |
| const VulkanHandle<VkDevice>& handle) { |
| ACQUIRE_PROC(AllocateCommandBuffers, handle); |
| ACQUIRE_PROC(AllocateMemory, handle); |
| ACQUIRE_PROC(BeginCommandBuffer, handle); |
| ACQUIRE_PROC(BindImageMemory, handle); |
| ACQUIRE_PROC(CmdPipelineBarrier, handle); |
| ACQUIRE_PROC(CreateCommandPool, handle); |
| ACQUIRE_PROC(CreateFence, handle); |
| ACQUIRE_PROC(CreateImage, handle); |
| ACQUIRE_PROC(CreateSemaphore, handle); |
| ACQUIRE_PROC(DestroyCommandPool, handle); |
| ACQUIRE_PROC(DestroyFence, handle); |
| ACQUIRE_PROC(DestroyImage, handle); |
| ACQUIRE_PROC(DestroySemaphore, handle); |
| ACQUIRE_PROC(DeviceWaitIdle, handle); |
| ACQUIRE_PROC(EndCommandBuffer, handle); |
| ACQUIRE_PROC(FreeCommandBuffers, handle); |
| ACQUIRE_PROC(FreeMemory, handle); |
| ACQUIRE_PROC(GetDeviceQueue, handle); |
| ACQUIRE_PROC(GetImageMemoryRequirements, handle); |
| ACQUIRE_PROC(QueueSubmit, handle); |
| ACQUIRE_PROC(QueueWaitIdle, handle); |
| ACQUIRE_PROC(ResetCommandBuffer, handle); |
| ACQUIRE_PROC(ResetFences, handle); |
| ACQUIRE_PROC(WaitForFences, handle); |
| #if OS_ANDROID |
| ACQUIRE_PROC(AcquireNextImageKHR, handle); |
| ACQUIRE_PROC(CreateSwapchainKHR, handle); |
| ACQUIRE_PROC(DestroySwapchainKHR, handle); |
| ACQUIRE_PROC(GetSwapchainImagesKHR, handle); |
| ACQUIRE_PROC(QueuePresentKHR, handle); |
| #endif // OS_ANDROID |
| #if OS_FUCHSIA |
| ACQUIRE_PROC(GetMemoryZirconHandleFUCHSIA, handle); |
| ACQUIRE_PROC(ImportSemaphoreZirconHandleFUCHSIA, handle); |
| #endif // OS_FUCHSIA |
| device_ = {handle, nullptr}; |
| return true; |
| } |
| |
| bool VulkanProcTable::OpenLibraryHandle() { |
| #if VULKAN_LINK_STATICALLY |
| static char kDummyLibraryHandle = '\0'; |
| handle_ = reinterpret_cast<decltype(handle_)>(&kDummyLibraryHandle); |
| return true; |
| #else // VULKAN_LINK_STATICALLY |
| dlerror(); // clear existing errors on thread. |
| handle_ = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); |
| if (handle_ == nullptr) { |
| FML_DLOG(WARNING) << "Could not open the vulkan library: " << dlerror(); |
| return false; |
| } |
| return true; |
| #endif // VULKAN_LINK_STATICALLY |
| } |
| |
| bool VulkanProcTable::CloseLibraryHandle() { |
| #if VULKAN_LINK_STATICALLY |
| handle_ = nullptr; |
| return true; |
| #else |
| if (handle_ != nullptr) { |
| dlerror(); // clear existing errors on thread. |
| if (dlclose(handle_) != 0) { |
| FML_DLOG(ERROR) << "Could not close the vulkan library handle. This " |
| "indicates a leak."; |
| FML_DLOG(ERROR) << dlerror(); |
| } |
| handle_ = nullptr; |
| } |
| return handle_ == nullptr; |
| #endif |
| } |
| |
| PFN_vkVoidFunction VulkanProcTable::AcquireProc( |
| const char* proc_name, |
| const VulkanHandle<VkInstance>& instance) const { |
| if (proc_name == nullptr || !GetInstanceProcAddr) { |
| return nullptr; |
| } |
| |
| // A VK_NULL_HANDLE as the instance is an acceptable parameter. |
| return GetInstanceProcAddr(instance, proc_name); |
| } |
| |
| PFN_vkVoidFunction VulkanProcTable::AcquireProc( |
| const char* proc_name, |
| const VulkanHandle<VkDevice>& device) const { |
| if (proc_name == nullptr || !device || !GetDeviceProcAddr) { |
| return nullptr; |
| } |
| |
| return GetDeviceProcAddr(device, proc_name); |
| } |
| |
| GrVkGetProc VulkanProcTable::CreateSkiaGetProc() const { |
| if (!IsValid()) { |
| return nullptr; |
| } |
| |
| return [this](const char* proc_name, VkInstance instance, VkDevice device) { |
| if (device != VK_NULL_HANDLE) { |
| auto result = AcquireProc(proc_name, {device, nullptr}); |
| if (result != nullptr) { |
| return result; |
| } |
| } |
| |
| return AcquireProc(proc_name, {instance, nullptr}); |
| }; |
| } |
| |
| } // namespace vulkan |