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 | |
17 | namespace vulkan { |
18 | |
19 | VulkanProcTable::VulkanProcTable() |
20 | : handle_(nullptr), acquired_mandatory_proc_addresses_(false) { |
21 | acquired_mandatory_proc_addresses_ = |
22 | OpenLibraryHandle() && SetupLoaderProcAddresses(); |
23 | } |
24 | |
25 | VulkanProcTable::~VulkanProcTable() { |
26 | CloseLibraryHandle(); |
27 | } |
28 | |
29 | bool VulkanProcTable::HasAcquiredMandatoryProcAddresses() const { |
30 | return acquired_mandatory_proc_addresses_; |
31 | } |
32 | |
33 | bool VulkanProcTable::IsValid() const { |
34 | return instance_ && device_; |
35 | } |
36 | |
37 | bool VulkanProcTable::AreInstanceProcsSetup() const { |
38 | return instance_; |
39 | } |
40 | |
41 | bool VulkanProcTable::AreDeviceProcsSetup() const { |
42 | return device_; |
43 | } |
44 | |
45 | bool 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 | |
72 | bool 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 | |
106 | bool 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 | |
147 | bool 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 | |
163 | bool 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 | |
181 | PFN_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 | |
192 | PFN_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 | |
202 | GrVkGetProc 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 | |