Flutter Impeller
context_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 #ifdef FML_OS_ANDROID
8 #include <pthread.h>
9 #include <sys/resource.h>
10 #include <sys/time.h>
11 #endif // FML_OS_ANDROID
12 
13 #include <map>
14 #include <memory>
15 #include <optional>
16 #include <string>
17 #include <vector>
18 
19 #include "flutter/fml/cpu_affinity.h"
20 #include "flutter/fml/trace_event.h"
32 
33 VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
34 
35 namespace impeller {
36 
37 // TODO(csg): Fix this after caps are reworked.
38 static bool gHasValidationLayers = false;
39 
41  return gHasValidationLayers;
42 }
43 
44 static std::optional<vk::PhysicalDevice> PickPhysicalDevice(
45  const CapabilitiesVK& caps,
46  const vk::Instance& instance) {
47  for (const auto& device : instance.enumeratePhysicalDevices().value) {
48  if (caps.GetEnabledDeviceFeatures(device).has_value()) {
49  return device;
50  }
51  }
52  return std::nullopt;
53 }
54 
55 static std::vector<vk::DeviceQueueCreateInfo> GetQueueCreateInfos(
56  std::initializer_list<QueueIndexVK> queues) {
57  std::map<size_t /* family */, size_t /* index */> family_index_map;
58  for (const auto& queue : queues) {
59  family_index_map[queue.family] = 0;
60  }
61  for (const auto& queue : queues) {
62  auto value = family_index_map[queue.family];
63  family_index_map[queue.family] = std::max(value, queue.index);
64  }
65 
66  static float kQueuePriority = 1.0f;
67  std::vector<vk::DeviceQueueCreateInfo> infos;
68  for (const auto& item : family_index_map) {
69  vk::DeviceQueueCreateInfo info;
70  info.setQueueFamilyIndex(item.first);
71  info.setQueueCount(item.second + 1);
72  info.setQueuePriorities(kQueuePriority);
73  infos.push_back(info);
74  }
75  return infos;
76 }
77 
78 static std::optional<QueueIndexVK> PickQueue(const vk::PhysicalDevice& device,
79  vk::QueueFlagBits flags) {
80  // This can be modified to ensure that dedicated queues are returned for each
81  // queue type depending on support.
82  const auto families = device.getQueueFamilyProperties();
83  for (size_t i = 0u; i < families.size(); i++) {
84  if (!(families[i].queueFlags & flags)) {
85  continue;
86  }
87  return QueueIndexVK{.family = i, .index = 0};
88  }
89  return std::nullopt;
90 }
91 
92 std::shared_ptr<ContextVK> ContextVK::Create(Settings settings) {
93  auto context = std::shared_ptr<ContextVK>(new ContextVK());
94  context->Setup(std::move(settings));
95  if (!context->IsValid()) {
96  return nullptr;
97  }
98  return context;
99 }
100 
101 namespace {
102 thread_local uint64_t tls_context_count = 0;
103 uint64_t CalculateHash(void* ptr) {
104  // You could make a context once per nanosecond for 584 years on one thread
105  // before this overflows.
106  return ++tls_context_count;
107 }
108 } // namespace
109 
110 ContextVK::ContextVK() : hash_(CalculateHash(this)) {}
111 
113  if (device_holder_ && device_holder_->device) {
114  [[maybe_unused]] auto result = device_holder_->device->waitIdle();
115  }
117 }
118 
121 }
122 
123 void ContextVK::Setup(Settings settings) {
124  TRACE_EVENT0("impeller", "ContextVK::Setup");
125 
126  if (!settings.proc_address_callback) {
127  return;
128  }
129 
130  raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
131  std::min(4u, std::thread::hardware_concurrency()));
132  raster_message_loop_->PostTaskToAllWorkers([]() {
133  // Currently we only use the worker task pool for small parts of a frame
134  // workload, if this changes this setting may need to be adjusted.
135  fml::RequestAffinity(fml::CpuAffinity::kNotPerformance);
136 #ifdef FML_OS_ANDROID
137  if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) {
138  FML_LOG(ERROR) << "Failed to set Workers task runner priority";
139  }
140 #endif // FML_OS_ANDROID
141  });
142 
143  auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER;
144  dispatcher.init(settings.proc_address_callback);
145 
146  // Enable Vulkan validation if either:
147  // 1. The user has explicitly enabled it.
148  // 2. We are in a combination of debug mode, and running on Android.
149  // (It's possible 2 is overly conservative and we can simplify this)
150  auto enable_validation = settings.enable_validation;
151 
152 #if defined(FML_OS_ANDROID) && !defined(NDEBUG)
153  enable_validation = true;
154 #endif
155 
156  auto caps =
157  std::shared_ptr<CapabilitiesVK>(new CapabilitiesVK(enable_validation));
158 
159  if (!caps->IsValid()) {
160  VALIDATION_LOG << "Could not determine device capabilities.";
161  return;
162  }
163 
164  gHasValidationLayers = caps->AreValidationsEnabled();
165 
166  auto enabled_layers = caps->GetEnabledLayers();
167  auto enabled_extensions = caps->GetEnabledInstanceExtensions();
168 
169  if (!enabled_layers.has_value() || !enabled_extensions.has_value()) {
170  VALIDATION_LOG << "Device has insufficient capabilities.";
171  return;
172  }
173 
174  vk::InstanceCreateFlags instance_flags = {};
175 
176  if (std::find(enabled_extensions.value().begin(),
177  enabled_extensions.value().end(),
178  "VK_KHR_portability_enumeration") !=
179  enabled_extensions.value().end()) {
180  instance_flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
181  }
182 
183  std::vector<const char*> enabled_layers_c;
184  std::vector<const char*> enabled_extensions_c;
185 
186  for (const auto& layer : enabled_layers.value()) {
187  enabled_layers_c.push_back(layer.c_str());
188  }
189 
190  for (const auto& ext : enabled_extensions.value()) {
191  enabled_extensions_c.push_back(ext.c_str());
192  }
193 
194  vk::ApplicationInfo application_info;
195  application_info.setApplicationVersion(VK_API_VERSION_1_0);
196  application_info.setApiVersion(VK_API_VERSION_1_1);
197  application_info.setEngineVersion(VK_API_VERSION_1_0);
198  application_info.setPEngineName("Impeller");
199  application_info.setPApplicationName("Impeller");
200 
201  vk::StructureChain<vk::InstanceCreateInfo, vk::ValidationFeaturesEXT>
202  instance_chain;
203 
204  if (!caps->AreValidationsEnabled()) {
205  instance_chain.unlink<vk::ValidationFeaturesEXT>();
206  }
207 
208  std::vector<vk::ValidationFeatureEnableEXT> enabled_validations = {
209  vk::ValidationFeatureEnableEXT::eSynchronizationValidation,
210  };
211 
212  auto validation = instance_chain.get<vk::ValidationFeaturesEXT>();
213  validation.setEnabledValidationFeatures(enabled_validations);
214 
215  auto instance_info = instance_chain.get<vk::InstanceCreateInfo>();
216  instance_info.setPEnabledLayerNames(enabled_layers_c);
217  instance_info.setPEnabledExtensionNames(enabled_extensions_c);
218  instance_info.setPApplicationInfo(&application_info);
219  instance_info.setFlags(instance_flags);
220 
221  auto device_holder = std::make_shared<DeviceHolderImpl>();
222  {
223  auto instance = vk::createInstanceUnique(instance_info);
224  if (instance.result != vk::Result::eSuccess) {
225  VALIDATION_LOG << "Could not create Vulkan instance: "
226  << vk::to_string(instance.result);
227  return;
228  }
229  device_holder->instance = std::move(instance.value);
230  }
231  dispatcher.init(device_holder->instance.get());
232 
233  //----------------------------------------------------------------------------
234  /// Setup the debug report.
235  ///
236  /// Do this as early as possible since we could use the debug report from
237  /// initialization issues.
238  ///
239  auto debug_report =
240  std::make_unique<DebugReportVK>(*caps, device_holder->instance.get());
241 
242  if (!debug_report->IsValid()) {
243  VALIDATION_LOG << "Could not set up debug report.";
244  return;
245  }
246 
247  //----------------------------------------------------------------------------
248  /// Pick the physical device.
249  ///
250  {
251  auto physical_device =
252  PickPhysicalDevice(*caps, device_holder->instance.get());
253  if (!physical_device.has_value()) {
254  VALIDATION_LOG << "No valid Vulkan device found.";
255  return;
256  }
257  device_holder->physical_device = physical_device.value();
258  }
259 
260  //----------------------------------------------------------------------------
261  /// Pick device queues.
262  ///
263  auto graphics_queue =
264  PickQueue(device_holder->physical_device, vk::QueueFlagBits::eGraphics);
265  auto transfer_queue =
266  PickQueue(device_holder->physical_device, vk::QueueFlagBits::eTransfer);
267  auto compute_queue =
268  PickQueue(device_holder->physical_device, vk::QueueFlagBits::eCompute);
269 
270  if (!graphics_queue.has_value()) {
271  VALIDATION_LOG << "Could not pick graphics queue.";
272  return;
273  }
274  if (!transfer_queue.has_value()) {
275  FML_LOG(INFO) << "Dedicated transfer queue not avialable.";
276  transfer_queue = graphics_queue.value();
277  }
278  if (!compute_queue.has_value()) {
279  VALIDATION_LOG << "Could not pick compute queue.";
280  return;
281  }
282 
283  //----------------------------------------------------------------------------
284  /// Create the logical device.
285  ///
286  auto enabled_device_extensions =
287  caps->GetEnabledDeviceExtensions(device_holder->physical_device);
288  if (!enabled_device_extensions.has_value()) {
289  // This shouldn't happen since we already did device selection. But
290  // doesn't hurt to check again.
291  return;
292  }
293 
294  std::vector<const char*> enabled_device_extensions_c;
295  for (const auto& ext : enabled_device_extensions.value()) {
296  enabled_device_extensions_c.push_back(ext.c_str());
297  }
298 
299  const auto queue_create_infos = GetQueueCreateInfos(
300  {graphics_queue.value(), compute_queue.value(), transfer_queue.value()});
301 
302  const auto enabled_features =
303  caps->GetEnabledDeviceFeatures(device_holder->physical_device);
304  if (!enabled_features.has_value()) {
305  // This shouldn't happen since the device can't be picked if this was not
306  // true. But doesn't hurt to check.
307  return;
308  }
309 
310  vk::DeviceCreateInfo device_info;
311 
312  device_info.setQueueCreateInfos(queue_create_infos);
313  device_info.setPEnabledExtensionNames(enabled_device_extensions_c);
314  device_info.setPEnabledFeatures(&enabled_features.value());
315  // Device layers are deprecated and ignored.
316 
317  {
318  auto device_result =
319  device_holder->physical_device.createDeviceUnique(device_info);
320  if (device_result.result != vk::Result::eSuccess) {
321  VALIDATION_LOG << "Could not create logical device.";
322  return;
323  }
324  device_holder->device = std::move(device_result.value);
325  }
326 
327  if (!caps->SetPhysicalDevice(device_holder->physical_device)) {
328  VALIDATION_LOG << "Capabilities could not be updated.";
329  return;
330  }
331 
332  //----------------------------------------------------------------------------
333  /// Create the allocator.
334  ///
335  auto allocator = std::shared_ptr<AllocatorVK>(new AllocatorVK(
336  weak_from_this(), //
337  application_info.apiVersion, //
338  device_holder->physical_device, //
339  device_holder, //
340  device_holder->instance.get(), //
341  *caps //
342  ));
343 
344  if (!allocator->IsValid()) {
345  VALIDATION_LOG << "Could not create memory allocator.";
346  return;
347  }
348 
349  //----------------------------------------------------------------------------
350  /// Setup the pipeline library.
351  ///
352  auto pipeline_library = std::shared_ptr<PipelineLibraryVK>(
353  new PipelineLibraryVK(device_holder, //
354  caps, //
355  std::move(settings.cache_directory), //
356  raster_message_loop_->GetTaskRunner() //
357  ));
358 
359  if (!pipeline_library->IsValid()) {
360  VALIDATION_LOG << "Could not create pipeline library.";
361  return;
362  }
363 
364  auto sampler_library =
365  std::shared_ptr<SamplerLibraryVK>(new SamplerLibraryVK(device_holder));
366 
367  auto shader_library = std::shared_ptr<ShaderLibraryVK>(
368  new ShaderLibraryVK(device_holder, //
369  settings.shader_libraries_data) //
370  );
371 
372  if (!shader_library->IsValid()) {
373  VALIDATION_LOG << "Could not create shader library.";
374  return;
375  }
376 
377  //----------------------------------------------------------------------------
378  /// Create the fence waiter.
379  ///
380  auto fence_waiter =
381  std::shared_ptr<FenceWaiterVK>(new FenceWaiterVK(device_holder));
382 
383  //----------------------------------------------------------------------------
384  /// Create the resource manager and command pool recycler.
385  ///
386  auto resource_manager = ResourceManagerVK::Create();
387  if (!resource_manager) {
388  VALIDATION_LOG << "Could not create resource manager.";
389  return;
390  }
391 
392  auto command_pool_recycler =
393  std::make_shared<CommandPoolRecyclerVK>(weak_from_this());
394  if (!command_pool_recycler) {
395  VALIDATION_LOG << "Could not create command pool recycler.";
396  return;
397  }
398 
399  //----------------------------------------------------------------------------
400  /// Fetch the queues.
401  ///
402  QueuesVK queues(device_holder->device.get(), //
403  graphics_queue.value(), //
404  compute_queue.value(), //
405  transfer_queue.value() //
406  );
407  if (!queues.IsValid()) {
408  VALIDATION_LOG << "Could not fetch device queues.";
409  return;
410  }
411 
412  VkPhysicalDeviceProperties physical_device_properties;
413  dispatcher.vkGetPhysicalDeviceProperties(device_holder->physical_device,
414  &physical_device_properties);
415 
416  //----------------------------------------------------------------------------
417  /// All done!
418  ///
419  device_holder_ = std::move(device_holder);
420  debug_report_ = std::move(debug_report);
421  allocator_ = std::move(allocator);
422  shader_library_ = std::move(shader_library);
423  sampler_library_ = std::move(sampler_library);
424  pipeline_library_ = std::move(pipeline_library);
425  queues_ = std::move(queues);
426  device_capabilities_ = std::move(caps);
427  fence_waiter_ = std::move(fence_waiter);
428  resource_manager_ = std::move(resource_manager);
429  command_pool_recycler_ = std::move(command_pool_recycler);
430  device_name_ = std::string(physical_device_properties.deviceName);
431  is_valid_ = true;
432 
433  //----------------------------------------------------------------------------
434  /// Label all the relevant objects. This happens after setup so that the
435  /// debug messengers have had a chance to be set up.
436  ///
437  SetDebugName(GetDevice(), device_holder_->device.get(), "ImpellerDevice");
438 }
439 
441  CapabilitiesVK::Cast(*device_capabilities_).SetOffscreenFormat(pixel_format);
442 }
443 
444 // |Context|
445 std::string ContextVK::DescribeGpuModel() const {
446  return device_name_;
447 }
448 
449 bool ContextVK::IsValid() const {
450  return is_valid_;
451 }
452 
453 std::shared_ptr<Allocator> ContextVK::GetResourceAllocator() const {
454  return allocator_;
455 }
456 
457 std::shared_ptr<ShaderLibrary> ContextVK::GetShaderLibrary() const {
458  return shader_library_;
459 }
460 
461 std::shared_ptr<SamplerLibrary> ContextVK::GetSamplerLibrary() const {
462  return sampler_library_;
463 }
464 
465 std::shared_ptr<PipelineLibrary> ContextVK::GetPipelineLibrary() const {
466  return pipeline_library_;
467 }
468 
469 std::shared_ptr<CommandBuffer> ContextVK::CreateCommandBuffer() const {
470  return std::shared_ptr<CommandBufferVK>(
471  new CommandBufferVK(shared_from_this(), //
472  CreateGraphicsCommandEncoderFactory()) //
473  );
474 }
475 
476 vk::Instance ContextVK::GetInstance() const {
477  return *device_holder_->instance;
478 }
479 
480 const vk::Device& ContextVK::GetDevice() const {
481  return device_holder_->device.get();
482 }
483 
484 const std::shared_ptr<fml::ConcurrentTaskRunner>
486  return raster_message_loop_->GetTaskRunner();
487 }
488 
490  // There are multiple objects, for example |CommandPoolVK|, that in their
491  // destructors make a strong reference to |ContextVK|. Resetting these shared
492  // pointers ensures that cleanup happens in a correct order.
493  //
494  // tl;dr: Without it, we get thread::join failures on shutdown.
495  fence_waiter_.reset();
496  resource_manager_.reset();
497 
498  raster_message_loop_->Terminate();
499 }
500 
501 std::shared_ptr<SurfaceContextVK> ContextVK::CreateSurfaceContext() {
502  return std::make_shared<SurfaceContextVK>(shared_from_this());
503 }
504 
505 const std::shared_ptr<const Capabilities>& ContextVK::GetCapabilities() const {
506  return device_capabilities_;
507 }
508 
509 const std::shared_ptr<QueueVK>& ContextVK::GetGraphicsQueue() const {
510  return queues_.graphics_queue;
511 }
512 
513 vk::PhysicalDevice ContextVK::GetPhysicalDevice() const {
514  return device_holder_->physical_device;
515 }
516 
517 std::shared_ptr<FenceWaiterVK> ContextVK::GetFenceWaiter() const {
518  return fence_waiter_;
519 }
520 
521 std::shared_ptr<ResourceManagerVK> ContextVK::GetResourceManager() const {
522  return resource_manager_;
523 }
524 
525 std::shared_ptr<CommandPoolRecyclerVK> ContextVK::GetCommandPoolRecycler()
526  const {
527  return command_pool_recycler_;
528 }
529 
530 std::unique_ptr<CommandEncoderFactoryVK>
531 ContextVK::CreateGraphicsCommandEncoderFactory() const {
532  return std::make_unique<CommandEncoderFactoryVK>(weak_from_this());
533 }
534 
535 } // namespace impeller
impeller::ContextVK::GetCapabilities
const std::shared_ptr< const Capabilities > & GetCapabilities() const override
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
Definition: context_vk.cc:505
impeller::ContextVK::GetConcurrentWorkerTaskRunner
const std::shared_ptr< fml::ConcurrentTaskRunner > GetConcurrentWorkerTaskRunner() const
Definition: context_vk.cc:485
fence_waiter_vk.h
impeller::CapabilitiesVK::SetOffscreenFormat
void SetOffscreenFormat(PixelFormat pixel_format) const
Definition: capabilities_vk.cc:337
impeller::ResourceManagerVK::Create
static std::shared_ptr< ResourceManagerVK > Create()
Creates a shared resource manager (a dedicated thread).
Definition: resource_manager_vk.cc:14
impeller::QueueIndexVK
Definition: queue_vk.h:15
allocator_vk.h
impeller::gHasValidationLayers
static bool gHasValidationLayers
Definition: context_vk.cc:38
impeller::CommandPoolRecyclerVK::DestroyThreadLocalPools
static void DestroyThreadLocalPools(const ContextVK *context)
Clean up resources held by all per-thread command pools associated with the given context.
Definition: command_pool_vk.cc:244
impeller::Context::BackendType
BackendType
Definition: context.h:49
impeller::ContextVK::IsValid
bool IsValid() const override
Determines if a context is valid. If the caller ever receives an invalid context, they must discard i...
Definition: context_vk.cc:449
impeller::ContextVK::GetInstance
vk::Instance GetInstance() const
Definition: context_vk.cc:476
impeller::PickPhysicalDevice
static std::optional< vk::PhysicalDevice > PickPhysicalDevice(const CapabilitiesVK &caps, const vk::Instance &instance)
Definition: context_vk.cc:44
command_encoder_vk.h
impeller::ContextVK::GetPhysicalDevice
vk::PhysicalDevice GetPhysicalDevice() const
Definition: context_vk.cc:513
impeller::ContextVK::GetResourceAllocator
std::shared_ptr< Allocator > GetResourceAllocator() const override
Returns the allocator used to create textures and buffers on the device.
Definition: context_vk.cc:453
impeller::ContextVK::GetBackendType
BackendType GetBackendType() const override
Get the graphics backend of an Impeller context.
Definition: context_vk.cc:119
impeller::ContextVK::CreateCommandBuffer
std::shared_ptr< CommandBuffer > CreateCommandBuffer() const override
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
Definition: context_vk.cc:469
surface_context_vk.h
validation.h
impeller::GetQueueCreateInfos
static std::vector< vk::DeviceQueueCreateInfo > GetQueueCreateInfos(std::initializer_list< QueueIndexVK > queues)
Definition: context_vk.cc:55
capabilities_vk.h
command_pool_vk.h
impeller::ContextVK::Settings
Definition: context_vk.h:40
command_buffer_vk.h
impeller::ContextVK::CreateSurfaceContext
std::shared_ptr< SurfaceContextVK > CreateSurfaceContext()
Definition: context_vk.cc:501
debug_report_vk.h
impeller::ContextVK::GetShaderLibrary
std::shared_ptr< ShaderLibrary > GetShaderLibrary() const override
Returns the library of shaders used to specify the programmable stages of a pipeline.
Definition: context_vk.cc:457
impeller::ContextVK::GetGraphicsQueue
const std::shared_ptr< QueueVK > & GetGraphicsQueue() const
Definition: context_vk.cc:509
impeller::QueuesVK::graphics_queue
std::shared_ptr< QueueVK > graphics_queue
Definition: queue_vk.h:58
impeller::CapabilitiesVK::GetEnabledDeviceFeatures
std::optional< vk::PhysicalDeviceFeatures > GetEnabledDeviceFeatures(const vk::PhysicalDevice &physical_device) const
Definition: capabilities_vk.cc:286
capabilities.h
impeller::ContextVK::Create
static std::shared_ptr< ContextVK > Create(Settings settings)
Definition: context_vk.cc:92
impeller::CapabilitiesVK
The Vulkan layers and extensions wrangler.
Definition: capabilities_vk.h:30
impeller::CommandBufferVK
Definition: command_buffer_vk.h:18
impeller::ContextVK::SetDebugName
bool SetDebugName(T handle, std::string_view label) const
Definition: context_vk.h:96
impeller::ContextVK::GetCommandPoolRecycler
std::shared_ptr< CommandPoolRecyclerVK > GetCommandPoolRecycler() const
Definition: context_vk.cc:525
impeller::ContextVK
Definition: context_vk.h:36
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
impeller::Context::BackendType::kVulkan
@ kVulkan
resource_manager_vk.h
impeller::ContextVK::~ContextVK
~ContextVK() override
Definition: context_vk.cc:112
impeller::ContextVK::GetSamplerLibrary
std::shared_ptr< SamplerLibrary > GetSamplerLibrary() const override
Returns the library of combined image samplers used in shaders.
Definition: context_vk.cc:461
impeller::ContextVK::GetDevice
const vk::Device & GetDevice() const
Definition: context_vk.cc:480
impeller::BackendCast< CapabilitiesVK, Capabilities >::Cast
static CapabilitiesVK & Cast(Capabilities &base)
Definition: backend_cast.h:14
impeller::ContextVK::GetResourceManager
std::shared_ptr< ResourceManagerVK > GetResourceManager() const
Definition: context_vk.cc:521
impeller::PickQueue
static std::optional< QueueIndexVK > PickQueue(const vk::PhysicalDevice &device, vk::QueueFlagBits flags)
Definition: context_vk.cc:78
impeller::ContextVK::GetPipelineLibrary
std::shared_ptr< PipelineLibrary > GetPipelineLibrary() const override
Returns the library of pipelines used by render or compute commands.
Definition: context_vk.cc:465
impeller::ContextVK::DescribeGpuModel
std::string DescribeGpuModel() const override
Definition: context_vk.cc:445
impeller::ContextVK::GetFenceWaiter
std::shared_ptr< FenceWaiterVK > GetFenceWaiter() const
Definition: context_vk.cc:517
impeller::QueueIndexVK::family
size_t family
Definition: queue_vk.h:16
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:94
impeller::ContextVK::SetOffscreenFormat
void SetOffscreenFormat(PixelFormat pixel_format)
Definition: context_vk.cc:440
context_vk.h
impeller::HasValidationLayers
bool HasValidationLayers()
Definition: context_vk.cc:40
impeller::ContextVK::Shutdown
void Shutdown() override
Force all pending asynchronous work to finish. This is achieved by deleting all owned concurrent mess...
Definition: context_vk.cc:489
impeller
Definition: aiks_context.cc:10