• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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