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 "flutter/vulkan/vulkan_device.h"
6
7 #include <limits>
8 #include <map>
9 #include <vector>
10
11 #include "flutter/vulkan/vulkan_proc_table.h"
12 #include "flutter/vulkan/vulkan_surface.h"
13 #include "flutter/vulkan/vulkan_utilities.h"
14 #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h"
15
16 namespace vulkan {
17
18 constexpr auto kVulkanInvalidGraphicsQueueIndex =
19 std::numeric_limits<uint32_t>::max();
20
FindGraphicsQueueIndex(const std::vector<VkQueueFamilyProperties> & properties)21 static uint32_t FindGraphicsQueueIndex(
22 const std::vector<VkQueueFamilyProperties>& properties) {
23 for (uint32_t i = 0, count = static_cast<uint32_t>(properties.size());
24 i < count; i++) {
25 if (properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
26 return i;
27 }
28 }
29 return kVulkanInvalidGraphicsQueueIndex;
30 }
31
VulkanDevice(VulkanProcTable & p_vk,VulkanHandle<VkPhysicalDevice> physical_device)32 VulkanDevice::VulkanDevice(VulkanProcTable& p_vk,
33 VulkanHandle<VkPhysicalDevice> physical_device)
34 : vk(p_vk),
35 physical_device_(std::move(physical_device)),
36 graphics_queue_index_(std::numeric_limits<uint32_t>::max()),
37 valid_(false) {
38 if (!physical_device_ || !vk.AreInstanceProcsSetup()) {
39 return;
40 }
41
42 graphics_queue_index_ = FindGraphicsQueueIndex(GetQueueFamilyProperties());
43
44 if (graphics_queue_index_ == kVulkanInvalidGraphicsQueueIndex) {
45 FML_DLOG(INFO) << "Could not find the graphics queue index.";
46 return;
47 }
48
49 const float priorities[1] = {1.0f};
50
51 const VkDeviceQueueCreateInfo queue_create = {
52 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
53 .pNext = nullptr,
54 .flags = 0,
55 .queueFamilyIndex = graphics_queue_index_,
56 .queueCount = 1,
57 .pQueuePriorities = priorities,
58 };
59
60 const char* extensions[] = {
61 #if OS_ANDROID
62 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
63 #endif
64 #if OS_FUCHSIA
65 VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
66 VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME,
67 VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
68 VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
69 #endif
70 };
71
72 auto enabled_layers = DeviceLayersToEnable(vk, physical_device_);
73
74 const char* layers[enabled_layers.size()];
75
76 for (size_t i = 0; i < enabled_layers.size(); i++) {
77 layers[i] = enabled_layers[i].c_str();
78 }
79
80 const VkDeviceCreateInfo create_info = {
81 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
82 .pNext = nullptr,
83 .flags = 0,
84 .queueCreateInfoCount = 1,
85 .pQueueCreateInfos = &queue_create,
86 .enabledLayerCount = static_cast<uint32_t>(enabled_layers.size()),
87 .ppEnabledLayerNames = layers,
88 .enabledExtensionCount = sizeof(extensions) / sizeof(const char*),
89 .ppEnabledExtensionNames = extensions,
90 .pEnabledFeatures = nullptr,
91 };
92
93 VkDevice device = VK_NULL_HANDLE;
94
95 if (VK_CALL_LOG_ERROR(vk.CreateDevice(physical_device_, &create_info, nullptr,
96 &device)) != VK_SUCCESS) {
97 FML_DLOG(INFO) << "Could not create device.";
98 return;
99 }
100
101 device_ = {device,
102 [this](VkDevice device) { vk.DestroyDevice(device, nullptr); }};
103
104 if (!vk.SetupDeviceProcAddresses(device_)) {
105 FML_DLOG(INFO) << "Could not setup device proc addresses.";
106 return;
107 }
108
109 VkQueue queue = VK_NULL_HANDLE;
110
111 vk.GetDeviceQueue(device_, graphics_queue_index_, 0, &queue);
112
113 if (queue == VK_NULL_HANDLE) {
114 FML_DLOG(INFO) << "Could not get the device queue handle.";
115 return;
116 }
117
118 queue_ = queue;
119
120 const VkCommandPoolCreateInfo command_pool_create_info = {
121 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
122 .pNext = nullptr,
123 .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
124 .queueFamilyIndex = 0,
125 };
126
127 VkCommandPool command_pool = VK_NULL_HANDLE;
128 if (VK_CALL_LOG_ERROR(vk.CreateCommandPool(device_, &command_pool_create_info,
129 nullptr, &command_pool)) !=
130 VK_SUCCESS) {
131 FML_DLOG(INFO) << "Could not create the command pool.";
132 return;
133 }
134
135 command_pool_ = {command_pool, [this](VkCommandPool pool) {
136 vk.DestroyCommandPool(device_, pool, nullptr);
137 }};
138
139 valid_ = true;
140 }
141
~VulkanDevice()142 VulkanDevice::~VulkanDevice() {
143 FML_ALLOW_UNUSED_LOCAL(WaitIdle());
144 }
145
IsValid() const146 bool VulkanDevice::IsValid() const {
147 return valid_;
148 }
149
WaitIdle() const150 bool VulkanDevice::WaitIdle() const {
151 return VK_CALL_LOG_ERROR(vk.DeviceWaitIdle(device_)) == VK_SUCCESS;
152 }
153
GetHandle() const154 const VulkanHandle<VkDevice>& VulkanDevice::GetHandle() const {
155 return device_;
156 }
157
ReleaseDeviceOwnership()158 void VulkanDevice::ReleaseDeviceOwnership() {
159 device_.ReleaseOwnership();
160 }
161
GetPhysicalDeviceHandle() const162 const VulkanHandle<VkPhysicalDevice>& VulkanDevice::GetPhysicalDeviceHandle()
163 const {
164 return physical_device_;
165 }
166
GetQueueHandle() const167 const VulkanHandle<VkQueue>& VulkanDevice::GetQueueHandle() const {
168 return queue_;
169 }
170
GetCommandPool() const171 const VulkanHandle<VkCommandPool>& VulkanDevice::GetCommandPool() const {
172 return command_pool_;
173 }
174
GetGraphicsQueueIndex() const175 uint32_t VulkanDevice::GetGraphicsQueueIndex() const {
176 return graphics_queue_index_;
177 }
178
GetSurfaceCapabilities(const VulkanSurface & surface,VkSurfaceCapabilitiesKHR * capabilities) const179 bool VulkanDevice::GetSurfaceCapabilities(
180 const VulkanSurface& surface,
181 VkSurfaceCapabilitiesKHR* capabilities) const {
182 #if OS_ANDROID
183 if (!surface.IsValid() || capabilities == nullptr) {
184 return false;
185 }
186
187 bool success =
188 VK_CALL_LOG_ERROR(vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(
189 physical_device_, surface.Handle(), capabilities)) == VK_SUCCESS;
190
191 if (!success) {
192 return false;
193 }
194
195 // Check if the physical device surface capabilities are valid. If so, there
196 // is nothing more to do.
197 if (capabilities->currentExtent.width != 0xFFFFFFFF &&
198 capabilities->currentExtent.height != 0xFFFFFFFF) {
199 return true;
200 }
201
202 // Ask the native surface for its size as a fallback.
203 SkISize size = surface.GetSize();
204
205 if (size.width() == 0 || size.height() == 0) {
206 return false;
207 }
208
209 capabilities->currentExtent.width = size.width();
210 capabilities->currentExtent.height = size.height();
211 return true;
212 #else
213 return false;
214 #endif
215 }
216
GetPhysicalDeviceFeatures(VkPhysicalDeviceFeatures * features) const217 bool VulkanDevice::GetPhysicalDeviceFeatures(
218 VkPhysicalDeviceFeatures* features) const {
219 if (features == nullptr || !physical_device_) {
220 return false;
221 }
222 vk.GetPhysicalDeviceFeatures(physical_device_, features);
223 return true;
224 }
225
GetPhysicalDeviceFeaturesSkia(uint32_t * sk_features) const226 bool VulkanDevice::GetPhysicalDeviceFeaturesSkia(uint32_t* sk_features) const {
227 if (sk_features == nullptr) {
228 return false;
229 }
230
231 VkPhysicalDeviceFeatures features;
232
233 if (!GetPhysicalDeviceFeatures(&features)) {
234 return false;
235 }
236
237 uint32_t flags = 0;
238
239 if (features.geometryShader) {
240 flags |= kGeometryShader_GrVkFeatureFlag;
241 }
242 if (features.dualSrcBlend) {
243 flags |= kDualSrcBlend_GrVkFeatureFlag;
244 }
245 if (features.sampleRateShading) {
246 flags |= kSampleRateShading_GrVkFeatureFlag;
247 }
248
249 *sk_features = flags;
250 return true;
251 }
252
GetQueueFamilyProperties() const253 std::vector<VkQueueFamilyProperties> VulkanDevice::GetQueueFamilyProperties()
254 const {
255 uint32_t count = 0;
256
257 vk.GetPhysicalDeviceQueueFamilyProperties(physical_device_, &count, nullptr);
258
259 std::vector<VkQueueFamilyProperties> properties;
260 properties.resize(count, {});
261
262 vk.GetPhysicalDeviceQueueFamilyProperties(physical_device_, &count,
263 properties.data());
264
265 return properties;
266 }
267
ChooseSurfaceFormat(const VulkanSurface & surface,std::vector<VkFormat> desired_formats,VkSurfaceFormatKHR * format) const268 int VulkanDevice::ChooseSurfaceFormat(const VulkanSurface& surface,
269 std::vector<VkFormat> desired_formats,
270 VkSurfaceFormatKHR* format) const {
271 #if OS_ANDROID
272 if (!surface.IsValid() || format == nullptr) {
273 return -1;
274 }
275
276 uint32_t format_count = 0;
277 if (VK_CALL_LOG_ERROR(vk.GetPhysicalDeviceSurfaceFormatsKHR(
278 physical_device_, surface.Handle(), &format_count, nullptr)) !=
279 VK_SUCCESS) {
280 return -1;
281 }
282
283 if (format_count == 0) {
284 return -1;
285 }
286
287 VkSurfaceFormatKHR formats[format_count];
288 if (VK_CALL_LOG_ERROR(vk.GetPhysicalDeviceSurfaceFormatsKHR(
289 physical_device_, surface.Handle(), &format_count, formats)) !=
290 VK_SUCCESS) {
291 return -1;
292 }
293
294 std::map<VkFormat, VkSurfaceFormatKHR> supported_formats;
295 for (uint32_t i = 0; i < format_count; i++) {
296 supported_formats[formats[i].format] = formats[i];
297 }
298
299 // Try to find the first supported format in the list of desired formats.
300 for (size_t i = 0; i < desired_formats.size(); ++i) {
301 auto found = supported_formats.find(desired_formats[i]);
302 if (found != supported_formats.end()) {
303 *format = found->second;
304 return static_cast<int>(i);
305 }
306 }
307 #endif
308 return -1;
309 }
310
ChoosePresentMode(const VulkanSurface & surface,VkPresentModeKHR * present_mode) const311 bool VulkanDevice::ChoosePresentMode(const VulkanSurface& surface,
312 VkPresentModeKHR* present_mode) const {
313 if (!surface.IsValid() || present_mode == nullptr) {
314 return false;
315 }
316
317 // https://github.com/LunarG/VulkanSamples/issues/98 indicates that
318 // VK_PRESENT_MODE_FIFO_KHR is preferable on mobile platforms. The problems
319 // mentioned in the ticket w.r.t the application being faster that the refresh
320 // rate of the screen should not be faced by any Flutter platforms as they are
321 // powered by Vsync pulses instead of depending the the submit to block.
322 // However, for platforms that don't have VSync providers setup, it is better
323 // to fall back to FIFO. For platforms that do have VSync providers, there
324 // should be little difference. In case there is a need for a mode other than
325 // FIFO, availability checks must be performed here before returning the
326 // result. FIFO is always present.
327 *present_mode = VK_PRESENT_MODE_FIFO_KHR;
328 return true;
329 }
330
QueueSubmit(std::vector<VkPipelineStageFlags> wait_dest_pipeline_stages,const std::vector<VkSemaphore> & wait_semaphores,const std::vector<VkSemaphore> & signal_semaphores,const std::vector<VkCommandBuffer> & command_buffers,const VulkanHandle<VkFence> & fence) const331 bool VulkanDevice::QueueSubmit(
332 std::vector<VkPipelineStageFlags> wait_dest_pipeline_stages,
333 const std::vector<VkSemaphore>& wait_semaphores,
334 const std::vector<VkSemaphore>& signal_semaphores,
335 const std::vector<VkCommandBuffer>& command_buffers,
336 const VulkanHandle<VkFence>& fence) const {
337 if (wait_semaphores.size() != wait_dest_pipeline_stages.size()) {
338 return false;
339 }
340
341 const VkSubmitInfo submit_info = {
342 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
343 .pNext = nullptr,
344 .waitSemaphoreCount = static_cast<uint32_t>(wait_semaphores.size()),
345 .pWaitSemaphores = wait_semaphores.data(),
346 .pWaitDstStageMask = wait_dest_pipeline_stages.data(),
347 .commandBufferCount = static_cast<uint32_t>(command_buffers.size()),
348 .pCommandBuffers = command_buffers.data(),
349 .signalSemaphoreCount = static_cast<uint32_t>(signal_semaphores.size()),
350 .pSignalSemaphores = signal_semaphores.data(),
351 };
352
353 if (VK_CALL_LOG_ERROR(vk.QueueSubmit(queue_, 1, &submit_info, fence)) !=
354 VK_SUCCESS) {
355 return false;
356 }
357
358 return true;
359 }
360
361 } // namespace vulkan
362