1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "vulkan_proc_table.h"
6
7#include <dlfcn.h>
8
9#include "flutter/fml/logging.h"
10
11#define ACQUIRE_PROC(name, context) \
12 if (!(name = AcquireProc("vk" #name, context))) { \
13 FML_DLOG(INFO) << "Could not acquire proc: vk" << #name; \
14 return false; \
15 }
16
17namespace vulkan {
18
19VulkanProcTable::VulkanProcTable()
20 : handle_(nullptr), acquired_mandatory_proc_addresses_(false) {
21 acquired_mandatory_proc_addresses_ =
22 OpenLibraryHandle() && SetupLoaderProcAddresses();
23}
24
25VulkanProcTable::~VulkanProcTable() {
26 CloseLibraryHandle();
27}
28
29bool VulkanProcTable::HasAcquiredMandatoryProcAddresses() const {
30 return acquired_mandatory_proc_addresses_;
31}
32
33bool VulkanProcTable::IsValid() const {
34 return instance_ && device_;
35}
36
37bool VulkanProcTable::AreInstanceProcsSetup() const {
38 return instance_;
39}
40
41bool VulkanProcTable::AreDeviceProcsSetup() const {
42 return device_;
43}
44
45bool VulkanProcTable::SetupLoaderProcAddresses() {
46 if (handle_ == nullptr) {
47 return true;
48 }
49
50 GetInstanceProcAddr =
51#if VULKAN_LINK_STATICALLY
52 GetInstanceProcAddr = &vkGetInstanceProcAddr;
53#else // VULKAN_LINK_STATICALLY
54 reinterpret_cast<PFN_vkGetInstanceProcAddr>(
55 dlsym(handle_, "vkGetInstanceProcAddr"));
56#endif // VULKAN_LINK_STATICALLY
57
58 if (!GetInstanceProcAddr) {
59 FML_DLOG(WARNING) << "Could not acquire vkGetInstanceProcAddr.";
60 return false;
61 }
62
63 VulkanHandle<VkInstance> null_instance(VK_NULL_HANDLE, nullptr);
64
65 ACQUIRE_PROC(CreateInstance, null_instance);
66 ACQUIRE_PROC(EnumerateInstanceExtensionProperties, null_instance);
67 ACQUIRE_PROC(EnumerateInstanceLayerProperties, null_instance);
68
69 return true;
70}
71
72bool VulkanProcTable::SetupInstanceProcAddresses(
73 const VulkanHandle<VkInstance>& handle) {
74 ACQUIRE_PROC(CreateDevice, handle);
75 ACQUIRE_PROC(DestroyDevice, handle);
76 ACQUIRE_PROC(DestroyInstance, handle);
77 ACQUIRE_PROC(EnumerateDeviceLayerProperties, handle);
78 ACQUIRE_PROC(EnumeratePhysicalDevices, handle);
79 ACQUIRE_PROC(GetDeviceProcAddr, handle);
80 ACQUIRE_PROC(GetPhysicalDeviceFeatures, handle);
81 ACQUIRE_PROC(GetPhysicalDeviceQueueFamilyProperties, handle);
82#if OS_ANDROID
83 ACQUIRE_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR, handle);
84 ACQUIRE_PROC(GetPhysicalDeviceSurfaceFormatsKHR, handle);
85 ACQUIRE_PROC(GetPhysicalDeviceSurfacePresentModesKHR, handle);
86 ACQUIRE_PROC(GetPhysicalDeviceSurfaceSupportKHR, handle);
87 ACQUIRE_PROC(DestroySurfaceKHR, handle);
88 ACQUIRE_PROC(CreateAndroidSurfaceKHR, handle);
89#endif // OS_ANDROID
90
91 // The debug report functions are optional. We don't want proc acquisition to
92 // fail here because the optional methods were not present (since ACQUIRE_PROC
93 // returns false on failure). Wrap the optional proc acquisitions in an
94 // anonymous lambda and invoke it. We don't really care about the result since
95 // users of Debug reporting functions check for their presence explicitly.
96 [this, &handle]() -> bool {
97 ACQUIRE_PROC(CreateDebugReportCallbackEXT, handle);
98 ACQUIRE_PROC(DestroyDebugReportCallbackEXT, handle);
99 return true;
100 }();
101
102 instance_ = {handle, nullptr};
103 return true;
104}
105
106bool VulkanProcTable::SetupDeviceProcAddresses(
107 const VulkanHandle<VkDevice>& handle) {
108 ACQUIRE_PROC(AllocateCommandBuffers, handle);
109 ACQUIRE_PROC(AllocateMemory, handle);
110 ACQUIRE_PROC(BeginCommandBuffer, handle);
111 ACQUIRE_PROC(BindImageMemory, handle);
112 ACQUIRE_PROC(CmdPipelineBarrier, handle);
113 ACQUIRE_PROC(CreateCommandPool, handle);
114 ACQUIRE_PROC(CreateFence, handle);
115 ACQUIRE_PROC(CreateImage, handle);
116 ACQUIRE_PROC(CreateSemaphore, handle);
117 ACQUIRE_PROC(DestroyCommandPool, handle);
118 ACQUIRE_PROC(DestroyFence, handle);
119 ACQUIRE_PROC(DestroyImage, handle);
120 ACQUIRE_PROC(DestroySemaphore, handle);
121 ACQUIRE_PROC(DeviceWaitIdle, handle);
122 ACQUIRE_PROC(EndCommandBuffer, handle);
123 ACQUIRE_PROC(FreeCommandBuffers, handle);
124 ACQUIRE_PROC(FreeMemory, handle);
125 ACQUIRE_PROC(GetDeviceQueue, handle);
126 ACQUIRE_PROC(GetImageMemoryRequirements, handle);
127 ACQUIRE_PROC(QueueSubmit, handle);
128 ACQUIRE_PROC(QueueWaitIdle, handle);
129 ACQUIRE_PROC(ResetCommandBuffer, handle);
130 ACQUIRE_PROC(ResetFences, handle);
131 ACQUIRE_PROC(WaitForFences, handle);
132#if OS_ANDROID
133 ACQUIRE_PROC(AcquireNextImageKHR, handle);
134 ACQUIRE_PROC(CreateSwapchainKHR, handle);
135 ACQUIRE_PROC(DestroySwapchainKHR, handle);
136 ACQUIRE_PROC(GetSwapchainImagesKHR, handle);
137 ACQUIRE_PROC(QueuePresentKHR, handle);
138#endif // OS_ANDROID
139#if OS_FUCHSIA
140 ACQUIRE_PROC(GetMemoryZirconHandleFUCHSIA, handle);
141 ACQUIRE_PROC(ImportSemaphoreZirconHandleFUCHSIA, handle);
142#endif // OS_FUCHSIA
143 device_ = {handle, nullptr};
144 return true;
145}
146
147bool VulkanProcTable::OpenLibraryHandle() {
148#if VULKAN_LINK_STATICALLY
149 static char kDummyLibraryHandle = '\0';
150 handle_ = reinterpret_cast<decltype(handle_)>(&kDummyLibraryHandle);
151 return true;
152#else // VULKAN_LINK_STATICALLY
153 dlerror(); // clear existing errors on thread.
154 handle_ = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL);
155 if (handle_ == nullptr) {
156 FML_DLOG(WARNING) << "Could not open the vulkan library: " << dlerror();
157 return false;
158 }
159 return true;
160#endif // VULKAN_LINK_STATICALLY
161}
162
163bool VulkanProcTable::CloseLibraryHandle() {
164#if VULKAN_LINK_STATICALLY
165 handle_ = nullptr;
166 return true;
167#else
168 if (handle_ != nullptr) {
169 dlerror(); // clear existing errors on thread.
170 if (dlclose(handle_) != 0) {
171 FML_DLOG(ERROR) << "Could not close the vulkan library handle. This "
172 "indicates a leak.";
173 FML_DLOG(ERROR) << dlerror();
174 }
175 handle_ = nullptr;
176 }
177 return handle_ == nullptr;
178#endif
179}
180
181PFN_vkVoidFunction VulkanProcTable::AcquireProc(
182 const char* proc_name,
183 const VulkanHandle<VkInstance>& instance) const {
184 if (proc_name == nullptr || !GetInstanceProcAddr) {
185 return nullptr;
186 }
187
188 // A VK_NULL_HANDLE as the instance is an acceptable parameter.
189 return GetInstanceProcAddr(instance, proc_name);
190}
191
192PFN_vkVoidFunction VulkanProcTable::AcquireProc(
193 const char* proc_name,
194 const VulkanHandle<VkDevice>& device) const {
195 if (proc_name == nullptr || !device || !GetDeviceProcAddr) {
196 return nullptr;
197 }
198
199 return GetDeviceProcAddr(device, proc_name);
200}
201
202GrVkGetProc VulkanProcTable::CreateSkiaGetProc() const {
203 if (!IsValid()) {
204 return nullptr;
205 }
206
207 return [this](const char* proc_name, VkInstance instance, VkDevice device) {
208 if (device != VK_NULL_HANDLE) {
209 auto result = AcquireProc(proc_name, {device, nullptr});
210 if (result != nullptr) {
211 return result;
212 }
213 }
214
215 return AcquireProc(proc_name, {instance, nullptr});
216 };
217}
218
219} // namespace vulkan
220