Flutter Impeller
capabilities_vk.cc
Go to the documentation of this file.
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 
6 
7 #include <algorithm>
8 
10 #include "impeller/core/formats.h"
12 #include "vulkan/vulkan_core.h"
13 
14 namespace impeller {
15 
16 static constexpr const char* kInstanceLayer = "ImpellerInstance";
17 
18 CapabilitiesVK::CapabilitiesVK(bool enable_validations) {
19  auto extensions = vk::enumerateInstanceExtensionProperties();
20  auto layers = vk::enumerateInstanceLayerProperties();
21 
22  if (extensions.result != vk::Result::eSuccess ||
23  layers.result != vk::Result::eSuccess) {
24  return;
25  }
26 
27  for (const auto& ext : extensions.value) {
28  exts_[kInstanceLayer].insert(ext.extensionName);
29  }
30 
31  for (const auto& layer : layers.value) {
32  const std::string layer_name = layer.layerName;
33  auto layer_exts = vk::enumerateInstanceExtensionProperties(layer_name);
34  if (layer_exts.result != vk::Result::eSuccess) {
35  return;
36  }
37  for (const auto& layer_ext : layer_exts.value) {
38  exts_[layer_name].insert(layer_ext.extensionName);
39  }
40  }
41 
42  validations_enabled_ =
43  enable_validations && HasLayer("VK_LAYER_KHRONOS_validation");
44  if (enable_validations && !validations_enabled_) {
45  FML_LOG(ERROR)
46  << "Requested Impeller context creation with validations but the "
47  "validation layers could not be found. Expect no Vulkan validation "
48  "checks!";
49  }
50  if (validations_enabled_) {
51  FML_LOG(INFO) << "Vulkan validations are enabled.";
52  }
53  is_valid_ = true;
54 }
55 
57 
59  return is_valid_;
60 }
61 
63  return validations_enabled_;
64 }
65 
66 std::optional<std::vector<std::string>> CapabilitiesVK::GetEnabledLayers()
67  const {
68  std::vector<std::string> required;
69 
70  if (validations_enabled_) {
71  // The presence of this layer is already checked in the ctor.
72  required.push_back("VK_LAYER_KHRONOS_validation");
73  }
74 
75  return required;
76 }
77 
78 std::optional<std::vector<std::string>>
80  std::vector<std::string> required;
81 
82  if (!HasExtension("VK_KHR_surface")) {
83  // Swapchain support is required and this is a dependency of
84  // VK_KHR_swapchain.
85  VALIDATION_LOG << "Could not find the surface extension.";
86  return std::nullopt;
87  }
88  required.push_back("VK_KHR_surface");
89 
90  auto has_wsi = false;
91  if (HasExtension("VK_MVK_macos_surface")) {
92  required.push_back("VK_MVK_macos_surface");
93  has_wsi = true;
94  }
95 
96  if (HasExtension("VK_EXT_metal_surface")) {
97  required.push_back("VK_EXT_metal_surface");
98  has_wsi = true;
99  }
100 
101  if (HasExtension("VK_KHR_portability_enumeration")) {
102  required.push_back("VK_KHR_portability_enumeration");
103  has_wsi = true;
104  }
105 
106  if (HasExtension("VK_KHR_win32_surface")) {
107  required.push_back("VK_KHR_win32_surface");
108  has_wsi = true;
109  }
110 
111  if (HasExtension("VK_KHR_android_surface")) {
112  required.push_back("VK_KHR_android_surface");
113  has_wsi = true;
114  }
115 
116  if (HasExtension("VK_KHR_xcb_surface")) {
117  required.push_back("VK_KHR_xcb_surface");
118  has_wsi = true;
119  }
120 
121  if (HasExtension("VK_KHR_xlib_surface")) {
122  required.push_back("VK_KHR_xlib_surface");
123  has_wsi = true;
124  }
125 
126  if (HasExtension("VK_KHR_wayland_surface")) {
127  required.push_back("VK_KHR_wayland_surface");
128  has_wsi = true;
129  }
130 
131  if (!has_wsi) {
132  // Don't really care which WSI extension there is as long there is at least
133  // one.
134  VALIDATION_LOG << "Could not find a WSI extension.";
135  return std::nullopt;
136  }
137 
138  if (validations_enabled_) {
139  if (!HasExtension("VK_EXT_debug_utils")) {
140  VALIDATION_LOG << "Requested validations but could not find the "
141  "VK_EXT_debug_utils extension.";
142  return std::nullopt;
143  }
144  required.push_back("VK_EXT_debug_utils");
145 
146  if (HasExtension("VK_EXT_validation_features")) {
147  // It's valid to not have `VK_EXT_validation_features` available. That's
148  // the case when using AGI as a frame debugger.
149  required.push_back("VK_EXT_validation_features");
150  }
151  }
152 
153  return required;
154 }
155 
157  switch (ext) {
159  return VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME;
161  return VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME;
163  return VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME;
165  return "Unknown";
166  }
167  return "Unknown";
168 }
169 
171  const std::function<void(OptionalDeviceExtensionVK)>& it) {
172  if (!it) {
173  return;
174  }
175  for (size_t i = 0;
176  i < static_cast<uint32_t>(OptionalDeviceExtensionVK::kLast); i++) {
177  it(static_cast<OptionalDeviceExtensionVK>(i));
178  }
179 }
180 
181 static std::optional<std::set<std::string>> GetSupportedDeviceExtensions(
182  const vk::PhysicalDevice& physical_device) {
183  auto device_extensions = physical_device.enumerateDeviceExtensionProperties();
184  if (device_extensions.result != vk::Result::eSuccess) {
185  return std::nullopt;
186  }
187 
188  std::set<std::string> exts;
189  for (const auto& device_extension : device_extensions.value) {
190  exts.insert(device_extension.extensionName);
191  };
192 
193  return exts;
194 }
195 
196 std::optional<std::vector<std::string>>
198  const vk::PhysicalDevice& physical_device) const {
199  auto exts = GetSupportedDeviceExtensions(physical_device);
200 
201  if (!exts.has_value()) {
202  return std::nullopt;
203  }
204 
205  std::vector<std::string> enabled;
206 
207  if (exts->find("VK_KHR_swapchain") == exts->end()) {
208  VALIDATION_LOG << "Device does not support the swapchain extension.";
209  return std::nullopt;
210  }
211  enabled.push_back("VK_KHR_swapchain");
212 
213  // Required for non-conformant implementations like MoltenVK.
214  if (exts->find("VK_KHR_portability_subset") != exts->end()) {
215  enabled.push_back("VK_KHR_portability_subset");
216  }
217 
218 #ifdef FML_OS_ANDROID
219  if (exts->find("VK_ANDROID_external_memory_android_hardware_buffer") ==
220  exts->end()) {
222  << "Device does not support "
223  "VK_ANDROID_external_memory_android_hardware_buffer extension.";
224  return std::nullopt;
225  }
226  enabled.push_back("VK_ANDROID_external_memory_android_hardware_buffer");
227  enabled.push_back("VK_EXT_queue_family_foreign");
228 #endif
229 
230  // Enable all optional extensions if the device supports it.
231  IterateOptionalDeviceExtensions([&](auto ext) {
232  auto ext_name = GetDeviceExtensionName(ext);
233  if (exts->find(ext_name) != exts->end()) {
234  enabled.push_back(ext_name);
235  }
236  });
237 
238  return enabled;
239 }
240 
241 static bool HasSuitableColorFormat(const vk::PhysicalDevice& device,
242  vk::Format format) {
243  const auto props = device.getFormatProperties(format);
244  // This needs to be more comprehensive.
245  return !!(props.optimalTilingFeatures &
246  vk::FormatFeatureFlagBits::eColorAttachment);
247 }
248 
249 static bool HasSuitableDepthStencilFormat(const vk::PhysicalDevice& device,
250  vk::Format format) {
251  const auto props = device.getFormatProperties(format);
252  return !!(props.optimalTilingFeatures &
253  vk::FormatFeatureFlagBits::eDepthStencilAttachment);
254 }
255 
257  const vk::PhysicalDevice& device) {
258  const auto has_color_format =
259  HasSuitableColorFormat(device, vk::Format::eB8G8R8A8Unorm);
260  const auto has_stencil_format =
261  HasSuitableDepthStencilFormat(device, vk::Format::eS8Uint) ||
262  HasSuitableDepthStencilFormat(device, vk::Format::eD32SfloatS8Uint) ||
263  HasSuitableDepthStencilFormat(device, vk::Format::eD24UnormS8Uint);
264  return has_color_format && has_stencil_format;
265 }
266 
267 static bool HasRequiredProperties(const vk::PhysicalDevice& physical_device) {
268  auto properties = physical_device.getProperties();
269  if (!(properties.limits.framebufferColorSampleCounts &
270  (vk::SampleCountFlagBits::e1 | vk::SampleCountFlagBits::e4))) {
271  return false;
272  }
273  return true;
274 }
275 
276 static bool HasRequiredQueues(const vk::PhysicalDevice& physical_device) {
277  auto queue_flags = vk::QueueFlags{};
278  for (const auto& queue : physical_device.getQueueFamilyProperties()) {
279  if (queue.queueCount == 0) {
280  continue;
281  }
282  queue_flags |= queue.queueFlags;
283  }
284  return static_cast<VkQueueFlags>(queue_flags &
285  (vk::QueueFlagBits::eGraphics |
286  vk::QueueFlagBits::eCompute |
287  vk::QueueFlagBits::eTransfer));
288 }
289 
290 std::optional<vk::PhysicalDeviceFeatures>
292  const vk::PhysicalDevice& device) const {
294  VALIDATION_LOG << "Device doesn't support the required formats.";
295  return std::nullopt;
296  }
297 
298  if (!HasRequiredProperties(device)) {
299  VALIDATION_LOG << "Device doesn't support the required properties.";
300  return std::nullopt;
301  }
302 
303  if (!HasRequiredQueues(device)) {
304  VALIDATION_LOG << "Device doesn't support the required queues.";
305  return std::nullopt;
306  }
307 
308  if (!GetEnabledDeviceExtensions(device).has_value()) {
309  VALIDATION_LOG << "Device doesn't support the required queues.";
310  return std::nullopt;
311  }
312 
313  const auto device_features = device.getFeatures();
314 
315  vk::PhysicalDeviceFeatures required;
316 
317  // We require this for enabling wireframes in the playground. But its not
318  // necessarily a big deal if we don't have this feature.
319  required.fillModeNonSolid = device_features.fillModeNonSolid;
320 
321  return required;
322 }
323 
324 bool CapabilitiesVK::HasLayer(const std::string& layer) const {
325  for (const auto& [found_layer, exts] : exts_) {
326  if (found_layer == layer) {
327  return true;
328  }
329  }
330  return false;
331 }
332 
333 bool CapabilitiesVK::HasExtension(const std::string& ext) const {
334  for (const auto& [layer, exts] : exts_) {
335  if (exts.find(ext) != exts.end()) {
336  return true;
337  }
338  }
339  return false;
340 }
341 
343  default_color_format_ = pixel_format;
344 }
345 
346 bool CapabilitiesVK::SetPhysicalDevice(const vk::PhysicalDevice& device) {
347  if (HasSuitableDepthStencilFormat(device, vk::Format::eD32SfloatS8Uint)) {
348  default_depth_stencil_format_ = PixelFormat::kD32FloatS8UInt;
349  } else if (HasSuitableDepthStencilFormat(device,
350  vk::Format::eD24UnormS8Uint)) {
351  default_depth_stencil_format_ = PixelFormat::kD24UnormS8Uint;
352  } else {
353  default_depth_stencil_format_ = PixelFormat::kUnknown;
354  }
355 
356  if (HasSuitableDepthStencilFormat(device, vk::Format::eS8Uint)) {
357  default_stencil_format_ = PixelFormat::kS8UInt;
358  } else if (default_stencil_format_ != PixelFormat::kUnknown) {
359  default_stencil_format_ = default_depth_stencil_format_;
360  } else {
361  return false;
362  }
363 
364  device_properties_ = device.getProperties();
365 
366  auto physical_properties_2 =
367  device.getProperties2<vk::PhysicalDeviceProperties2,
368  vk::PhysicalDeviceSubgroupProperties>();
369 
370  // Currently shaders only want access to arithmetic subgroup features.
371  // If that changes this needs to get updated, and so does Metal (which right
372  // now assumes it from compile time flags based on the MSL target version).
373 
374  supports_compute_subgroups_ =
375  !!(physical_properties_2.get<vk::PhysicalDeviceSubgroupProperties>()
376  .supportedOperations &
377  vk::SubgroupFeatureFlagBits::eArithmetic);
378 
379  {
380  // Query texture support.
381  // TODO(jonahwilliams):
382  // https://github.com/flutter/flutter/issues/129784
383  vk::PhysicalDeviceMemoryProperties memory_properties;
384  device.getMemoryProperties(&memory_properties);
385 
386  for (auto i = 0u; i < memory_properties.memoryTypeCount; i++) {
387  if (memory_properties.memoryTypes[i].propertyFlags &
388  vk::MemoryPropertyFlagBits::eLazilyAllocated) {
389  supports_device_transient_textures_ = true;
390  }
391  }
392  }
393 
394  // Determine the optional device extensions this physical device supports.
395  {
396  optional_device_extensions_.clear();
397  auto exts = GetSupportedDeviceExtensions(device);
398  if (!exts.has_value()) {
399  return false;
400  }
401  IterateOptionalDeviceExtensions([&](auto ext) {
402  auto ext_name = GetDeviceExtensionName(ext);
403  if (exts->find(ext_name) != exts->end()) {
404  optional_device_extensions_.insert(ext);
405  }
406  });
407  }
408 
409  {
410  supports_framebuffer_fetch_ =
411  (optional_device_extensions_.find(
414  optional_device_extensions_.end() ||
415  optional_device_extensions_.find(
418  optional_device_extensions_.end());
419  }
420 
421  return true;
422 }
423 
424 // |Capabilities|
426  return true;
427 }
428 
429 // |Capabilities|
431  return false;
432 }
433 
434 // |Capabilities|
436  return true;
437 }
438 
439 // |Capabilities|
441  return true;
442 }
443 
444 // |Capabilities|
446  return true;
447 }
448 
449 // |Capabilities|
451  return supports_framebuffer_fetch_;
452 }
453 
454 // |Capabilities|
456  // Vulkan 1.1 requires support for compute.
457  return true;
458 }
459 
460 // |Capabilities|
462  // Set by |SetPhysicalDevice|.
463  return supports_compute_subgroups_;
464 }
465 
466 // |Capabilities|
468  return false;
469 }
470 
472  return true;
473 }
474 
475 // |Capabilities|
477  return supports_device_transient_textures_;
478 }
479 
480 // |Capabilities|
482  return default_color_format_;
483 }
484 
485 // |Capabilities|
487  return default_stencil_format_;
488 }
489 
490 // |Capabilities|
492  return default_depth_stencil_format_;
493 }
494 
495 const vk::PhysicalDeviceProperties&
497  return device_properties_;
498 }
499 
501  OptionalDeviceExtensionVK extension) const {
502  return optional_device_extensions_.find(extension) !=
503  optional_device_extensions_.end();
504 }
505 
506 } // namespace impeller
impeller::OptionalDeviceExtensionVK
OptionalDeviceExtensionVK
Definition: capabilities_vk.h:22
impeller::HasRequiredProperties
static bool HasRequiredProperties(const vk::PhysicalDevice &physical_device)
Definition: capabilities_vk.cc:267
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::CapabilitiesVK::SupportsDecalSamplerAddressMode
bool SupportsDecalSamplerAddressMode() const override
Whether the context backend supports SamplerAddressMode::Decal.
Definition: capabilities_vk.cc:471
impeller::CapabilitiesVK::SetOffscreenFormat
void SetOffscreenFormat(PixelFormat pixel_format) const
Definition: capabilities_vk.cc:342
impeller::CapabilitiesVK::GetEnabledInstanceExtensions
std::optional< std::vector< std::string > > GetEnabledInstanceExtensions() const
Definition: capabilities_vk.cc:79
impeller::kInstanceLayer
static constexpr const char * kInstanceLayer
Definition: capabilities_vk.cc:16
impeller::CapabilitiesVK::IsValid
bool IsValid() const
Definition: capabilities_vk.cc:58
impeller::CapabilitiesVK::SupportsOffscreenMSAA
bool SupportsOffscreenMSAA() const override
Whether the context backend supports attaching offscreen MSAA color/stencil textures.
Definition: capabilities_vk.cc:425
impeller::CapabilitiesVK::SupportsFramebufferFetch
bool SupportsFramebufferFetch() const override
Whether the context backend is able to support pipelines with shaders that read from the framebuffer ...
Definition: capabilities_vk.cc:450
impeller::OptionalDeviceExtensionVK::kLast
@ kLast
formats.h
impeller::HasSuitableDepthStencilFormat
static bool HasSuitableDepthStencilFormat(const vk::PhysicalDevice &device, vk::Format format)
Definition: capabilities_vk.cc:249
impeller::CapabilitiesVK::GetDefaultStencilFormat
PixelFormat GetDefaultStencilFormat() const override
Returns a supported PixelFormat for textures that store stencil information. May include a depth chan...
Definition: capabilities_vk.cc:486
impeller::CapabilitiesVK::GetEnabledLayers
std::optional< std::vector< std::string > > GetEnabledLayers() const
Definition: capabilities_vk.cc:66
impeller::OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback
@ kEXTPipelineCreationFeedback
impeller::OptionalDeviceExtensionVK::kARMRasterizationOrderAttachmentAccess
@ kARMRasterizationOrderAttachmentAccess
impeller::CapabilitiesVK::SupportsTextureToTextureBlits
bool SupportsTextureToTextureBlits() const override
Whether the context backend supports blitting from one texture region to another texture region (via ...
Definition: capabilities_vk.cc:445
validation.h
impeller::HasRequiredQueues
static bool HasRequiredQueues(const vk::PhysicalDevice &physical_device)
Definition: capabilities_vk.cc:276
impeller::CapabilitiesVK::SupportsComputeSubgroups
bool SupportsComputeSubgroups() const override
Whether the context backend supports configuring ComputePass command subgroups.
Definition: capabilities_vk.cc:461
impeller::IterateOptionalDeviceExtensions
static void IterateOptionalDeviceExtensions(const std::function< void(OptionalDeviceExtensionVK)> &it)
Definition: capabilities_vk.cc:170
impeller::CapabilitiesVK::HasOptionalDeviceExtension
bool HasOptionalDeviceExtension(OptionalDeviceExtensionVK extension) const
Definition: capabilities_vk.cc:500
impeller::CapabilitiesVK::SupportsReadFromResolve
bool SupportsReadFromResolve() const override
Whether the context backend supports binding the current RenderPass attachments. This is supported if...
Definition: capabilities_vk.cc:467
vk.h
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:94
impeller::PhysicalDeviceSupportsRequiredFormats
static bool PhysicalDeviceSupportsRequiredFormats(const vk::PhysicalDevice &device)
Definition: capabilities_vk.cc:256
impeller::CapabilitiesVK::SetPhysicalDevice
bool SetPhysicalDevice(const vk::PhysicalDevice &physical_device)
Definition: capabilities_vk.cc:346
capabilities_vk.h
impeller::CapabilitiesVK::~CapabilitiesVK
~CapabilitiesVK()
impeller::CapabilitiesVK::GetDefaultColorFormat
PixelFormat GetDefaultColorFormat() const override
Returns a supported PixelFormat for textures that store 4-channel colors (red/green/blue/alpha).
Definition: capabilities_vk.cc:481
impeller::OptionalDeviceExtensionVK::kEXTRasterizationOrderAttachmentAccess
@ kEXTRasterizationOrderAttachmentAccess
impeller::GetSupportedDeviceExtensions
static std::optional< std::set< std::string > > GetSupportedDeviceExtensions(const vk::PhysicalDevice &physical_device)
Definition: capabilities_vk.cc:181
impeller::CapabilitiesVK::GetEnabledDeviceFeatures
std::optional< vk::PhysicalDeviceFeatures > GetEnabledDeviceFeatures(const vk::PhysicalDevice &physical_device) const
Definition: capabilities_vk.cc:291
impeller::CapabilitiesVK::CapabilitiesVK
CapabilitiesVK(bool enable_validations)
Definition: capabilities_vk.cc:18
impeller::PixelFormat::kD24UnormS8Uint
@ kD24UnormS8Uint
impeller::CapabilitiesVK::GetDefaultDepthStencilFormat
PixelFormat GetDefaultDepthStencilFormat() const override
Returns a supported PixelFormat for textures that store both a stencil and depth component....
Definition: capabilities_vk.cc:491
impeller::GetDeviceExtensionName
static const char * GetDeviceExtensionName(OptionalDeviceExtensionVK ext)
Definition: capabilities_vk.cc:156
impeller::CapabilitiesVK::SupportsBufferToTextureBlits
bool SupportsBufferToTextureBlits() const override
Whether the context backend supports blitting from a given DeviceBuffer view to a texture region (via...
Definition: capabilities_vk.cc:440
impeller::CapabilitiesVK::SupportsCompute
bool SupportsCompute() const override
Whether the context backend supports ComputePass.
Definition: capabilities_vk.cc:455
impeller::PixelFormat::kUnknown
@ kUnknown
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:67
impeller::HasSuitableColorFormat
static bool HasSuitableColorFormat(const vk::PhysicalDevice &device, vk::Format format)
Definition: capabilities_vk.cc:241
impeller::CapabilitiesVK::SupportsDeviceTransientTextures
bool SupportsDeviceTransientTextures() const override
Whether the context backend supports allocating StorageMode::kDeviceTransient (aka "memoryless") text...
Definition: capabilities_vk.cc:476
impeller::CapabilitiesVK::GetPhysicalDeviceProperties
const vk::PhysicalDeviceProperties & GetPhysicalDeviceProperties() const
Definition: capabilities_vk.cc:496
impeller::CapabilitiesVK::SupportsSSBO
bool SupportsSSBO() const override
Whether the context backend supports binding Shader Storage Buffer Objects (SSBOs) to pipelines.
Definition: capabilities_vk.cc:435
impeller::CapabilitiesVK::AreValidationsEnabled
bool AreValidationsEnabled() const
Definition: capabilities_vk.cc:62
impeller::CapabilitiesVK::GetEnabledDeviceExtensions
std::optional< std::vector< std::string > > GetEnabledDeviceExtensions(const vk::PhysicalDevice &physical_device) const
Definition: capabilities_vk.cc:197
impeller::CapabilitiesVK::SupportsImplicitResolvingMSAA
bool SupportsImplicitResolvingMSAA() const override
Whether the context backend supports multisampled rendering to the on-screen surface without requirin...
Definition: capabilities_vk.cc:430
impeller
Definition: aiks_context.cc:10