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