| 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
5 #include "impeller/renderer/backend/vulkan/capabilities_vk.h"
6
7 #include <algorithm>
8
9 #include "impeller/base/validation.h"
10 #include "impeller/core/formats.h"
11 #include "impeller/renderer/backend/vulkan/vk.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
55 CapabilitiesVK::~CapabilitiesVK() = default;
56
57 bool CapabilitiesVK::IsValid() const {
58 return is_valid_;
59 }
60
61 bool CapabilitiesVK::AreValidationsEnabled() const {
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>>
78 CapabilitiesVK::GetEnabledInstanceExtensions() const {
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
155 static const char* GetDeviceExtensionName(OptionalDeviceExtensionVK ext) {
156 switch (ext) {
157 case OptionalDeviceExtensionVK::kEXTPipelineCreationFeedback:
158 return VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME;
159 case OptionalDeviceExtensionVK::kLast:
160 return "Unknown";
161 }
162 return "Unknown";
163 }
164
165 static void IterateOptionalDeviceExtensions(
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>>
192 CapabilitiesVK::GetEnabledDeviceExtensions(
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()) {
216 VALIDATION_LOG
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
251 static bool PhysicalDeviceSupportsRequiredFormats(
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>
286 CapabilitiesVK::GetEnabledDeviceFeatures(
287 const vk::PhysicalDevice& device) const {
288 if (!PhysicalDeviceSupportsRequiredFormats(device)) {
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
337 void CapabilitiesVK::SetOffscreenFormat(PixelFormat pixel_format) const {
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|
408 bool CapabilitiesVK::SupportsOffscreenMSAA() const {
409 return true;
410 }
411
412 // |Capabilities|
413 bool CapabilitiesVK::SupportsSSBO() const {
414 return true;
415 }
416
417 // |Capabilities|
418 bool CapabilitiesVK::SupportsBufferToTextureBlits() const {
419 return true;
420 }
421
422 // |Capabilities|
423 bool CapabilitiesVK::SupportsTextureToTextureBlits() const {
424 return true;
425 }
426
427 // |Capabilities|
428 bool CapabilitiesVK::SupportsFramebufferFetch() const {
429 return false;
430 }
431
432 // |Capabilities|
433 bool CapabilitiesVK::SupportsCompute() const {
434 // Vulkan 1.1 requires support for compute.
435 return true;
436 }
437
438 // |Capabilities|
439 bool CapabilitiesVK::SupportsComputeSubgroups() const {
440 // Set by |SetPhysicalDevice|.
441 return supports_compute_subgroups_;
442 }
443
444 // |Capabilities|
445 bool CapabilitiesVK::SupportsReadFromResolve() const {
446 return false;
447 }
448
449 // |Capabilities|
450 bool CapabilitiesVK::SupportsReadFromOnscreenTexture() const {
451 return false;
452 }
453
454 bool CapabilitiesVK::SupportsDecalSamplerAddressMode() const {
455 return true;
456 }
457
458 // |Capabilities|
459 bool CapabilitiesVK::SupportsDeviceTransientTextures() const {
460 return supports_device_transient_textures_;
461 }
462
463 // |Capabilities|
464 PixelFormat CapabilitiesVK::GetDefaultColorFormat() const {
465 return default_color_format_;
466 }
467
468 // |Capabilities|
469 PixelFormat CapabilitiesVK::GetDefaultStencilFormat() const {
470 return default_stencil_format_;
471 }
472
473 // |Capabilities|
474 PixelFormat CapabilitiesVK::GetDefaultDepthStencilFormat() const {
475 return default_depth_stencil_format_;
476 }
477
478 const vk::PhysicalDeviceProperties&
479 CapabilitiesVK::GetPhysicalDeviceProperties() const {
480 return device_properties_;
481 }
482
483 bool CapabilitiesVK::HasOptionalDeviceExtension(
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