Flutter Impeller
playground_impl_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 "flutter/fml/paths.h"
9 
10 #define GLFW_INCLUDE_VULKAN
11 #include <GLFW/glfw3.h>
12 
13 #include "flutter/fml/logging.h"
14 #include "flutter/fml/mapping.h"
15 #include "impeller/entity/vk/entity_shaders_vk.h"
16 #include "impeller/entity/vk/framebuffer_blend_shaders_vk.h"
17 #include "impeller/entity/vk/modern_shaders_vk.h"
18 #include "impeller/fixtures/vk/fixtures_shaders_vk.h"
19 #include "impeller/fixtures/vk/modern_fixtures_shaders_vk.h"
20 #include "impeller/playground/imgui/vk/imgui_shaders_vk.h"
26 #include "impeller/renderer/vk/compute_shaders_vk.h"
27 
28 namespace impeller {
29 
30 static std::vector<std::shared_ptr<fml::Mapping>>
32  return {
33  std::make_shared<fml::NonOwnedMapping>(impeller_entity_shaders_vk_data,
34  impeller_entity_shaders_vk_length),
35  std::make_shared<fml::NonOwnedMapping>(impeller_modern_shaders_vk_data,
36  impeller_modern_shaders_vk_length),
37  std::make_shared<fml::NonOwnedMapping>(
38  impeller_framebuffer_blend_shaders_vk_data,
39  impeller_framebuffer_blend_shaders_vk_length),
40  std::make_shared<fml::NonOwnedMapping>(
41  impeller_fixtures_shaders_vk_data,
42  impeller_fixtures_shaders_vk_length),
43  std::make_shared<fml::NonOwnedMapping>(
44  impeller_modern_fixtures_shaders_vk_data,
45  impeller_modern_fixtures_shaders_vk_length),
46  std::make_shared<fml::NonOwnedMapping>(impeller_imgui_shaders_vk_data,
47  impeller_imgui_shaders_vk_length),
48  std::make_shared<fml::NonOwnedMapping>(
49  impeller_compute_shaders_vk_data, impeller_compute_shaders_vk_length),
50  };
51 }
52 
53 // A global Vulkan instance that is reused across all Vulkan playgrounds.
54 // This instance is kept for the entire process lifetime. It is not cleaned
55 // up during shutdown to avoid conflicts with destruction of other globals
56 // in dependencies like the Vulkan validation layers.
57 VkInstance PlaygroundImplVK::global_instance_ = VK_NULL_HANDLE;
58 
59 void PlaygroundImplVK::DestroyWindowHandle(WindowHandle handle) {
60  if (!handle) {
61  return;
62  }
63  ::glfwDestroyWindow(reinterpret_cast<GLFWwindow*>(handle));
64 }
65 
67  : PlaygroundImpl(switches), handle_(nullptr, &DestroyWindowHandle) {
68  FML_CHECK(IsVulkanDriverPresent());
69 
70  InitGlobalVulkanInstance();
71 
72  ::glfwDefaultWindowHints();
73  ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
74  ::glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
75 
76  auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, nullptr);
77  if (!window) {
78  VALIDATION_LOG << "Unable to create glfw window";
79  return;
80  }
81 
82  int width = 0;
83  int height = 0;
84  ::glfwGetWindowSize(window, &width, &height);
85  size_ = ISize{width, height};
86 
87  handle_.reset(window);
88 
89  ContextVK::Settings context_settings;
90  context_settings.proc_address_callback =
91  reinterpret_cast<PFN_vkGetInstanceProcAddr>(
92  &::glfwGetInstanceProcAddress);
94  context_settings.cache_directory = fml::paths::GetCachesDirectory();
96  context_settings.fatal_missing_validations =
98  context_settings.flags = switches_.flags;
99 
100  auto context_vk = ContextVK::Create(std::move(context_settings));
101  if (!context_vk || !context_vk->IsValid()) {
102  VALIDATION_LOG << "Could not create Vulkan context in the playground.";
103  return;
104  }
105 
106  VkSurfaceKHR vk_surface;
107  auto res = vk::Result{::glfwCreateWindowSurface(
108  context_vk->GetInstance(), // instance
109  window, // window
110  nullptr, // allocator
111  &vk_surface // surface
112  )};
113  if (res != vk::Result::eSuccess) {
114  VALIDATION_LOG << "Could not create surface for GLFW window: "
115  << vk::to_string(res);
116  return;
117  }
118 
119  vk::UniqueSurfaceKHR surface{vk_surface, context_vk->GetInstance()};
120  auto context = context_vk->CreateSurfaceContext();
121  if (!context->SetWindowSurface(std::move(surface), size_)) {
122  VALIDATION_LOG << "Could not set up surface for context.";
123  return;
124  }
125 
126  context_ = std::move(context);
127 }
128 
130 
131 // |PlaygroundImpl|
132 std::shared_ptr<Context> PlaygroundImplVK::GetContext() const {
133  return context_;
134 }
135 
136 // |PlaygroundImpl|
137 PlaygroundImpl::WindowHandle PlaygroundImplVK::GetWindowHandle() const {
138  return handle_.get();
139 }
140 
141 // |PlaygroundImpl|
142 std::unique_ptr<Surface> PlaygroundImplVK::AcquireSurfaceFrame(
143  std::shared_ptr<Context> context) {
144  SurfaceContextVK* surface_context_vk =
145  reinterpret_cast<SurfaceContextVK*>(context_.get());
146 
147  int width = 0;
148  int height = 0;
149  ::glfwGetFramebufferSize(reinterpret_cast<GLFWwindow*>(handle_.get()), &width,
150  &height);
151  size_ = ISize{width, height};
152  surface_context_vk->UpdateSurfaceSize(ISize{width, height});
153 
154  return surface_context_vk->AcquireNextSurface();
155 }
156 
157 // Create a global instance of Vulkan in order to prevent unloading of the
158 // Vulkan library.
159 // A test suite may repeatedly create and destroy PlaygroundImplVK instances,
160 // and if the PlaygroundImplVK's Vulkan instance is the only one in the
161 // process then the Vulkan library will be unloaded when the instance is
162 // destroyed. Repeated loading and unloading of SwiftShader was leaking
163 // resources, so this will work around that leak.
164 // (see https://github.com/flutter/flutter/issues/138028)
165 void PlaygroundImplVK::InitGlobalVulkanInstance() {
166  if (global_instance_) {
167  return;
168  }
169 
170  VULKAN_HPP_DEFAULT_DISPATCHER.init(::glfwGetInstanceProcAddress);
171 
172  vk::ApplicationInfo application_info;
173  application_info.setApplicationVersion(VK_API_VERSION_1_0);
174  application_info.setApiVersion(VK_API_VERSION_1_1);
175  application_info.setEngineVersion(VK_API_VERSION_1_0);
176  application_info.setPEngineName("PlaygroundImplVK");
177  application_info.setPApplicationName("PlaygroundImplVK");
178 
179  auto caps = std::shared_ptr<CapabilitiesVK>(
180  new CapabilitiesVK(/*enable_validations=*/true));
181  FML_DCHECK(caps->IsValid());
182 
183  std::optional<std::vector<std::string>> enabled_layers =
184  caps->GetEnabledLayers();
185  std::optional<std::vector<std::string>> enabled_extensions =
186  caps->GetEnabledInstanceExtensions();
187  FML_DCHECK(enabled_layers.has_value() && enabled_extensions.has_value());
188 
189  std::vector<const char*> enabled_layers_c;
190  std::vector<const char*> enabled_extensions_c;
191 
192  if (enabled_layers.has_value()) {
193  for (const auto& layer : enabled_layers.value()) {
194  enabled_layers_c.push_back(layer.c_str());
195  }
196  }
197 
198  if (enabled_extensions.has_value()) {
199  for (const auto& ext : enabled_extensions.value()) {
200  enabled_extensions_c.push_back(ext.c_str());
201  }
202  }
203 
204  vk::InstanceCreateFlags instance_flags = {};
205  instance_flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
206  vk::InstanceCreateInfo instance_info;
207  instance_info.setPEnabledLayerNames(enabled_layers_c);
208  instance_info.setPEnabledExtensionNames(enabled_extensions_c);
209  instance_info.setPApplicationInfo(&application_info);
210  instance_info.setFlags(instance_flags);
211  auto instance_result = vk::createInstanceUnique(instance_info);
212  FML_CHECK(instance_result.result == vk::Result::eSuccess)
213  << "Unable to initialize global Vulkan instance";
214  global_instance_ = instance_result.value.release();
215 }
216 
218  const std::shared_ptr<Capabilities>& capabilities) {
219  return fml::Status(
220  fml::StatusCode::kUnimplemented,
221  "PlaygroundImplVK doesn't support setting the capabilities.");
222 }
223 
225  if (::glfwVulkanSupported()) {
226  return true;
227  }
228 #ifdef TARGET_OS_MAC
229  FML_LOG(ERROR) << "Attempting to initialize a Vulkan playground on macOS "
230  "where Vulkan cannot be found. It can be installed via "
231  "MoltenVK and make sure to install it globally so "
232  "dlopen can find it.";
233 #else // TARGET_OS_MAC
234  FML_LOG(ERROR) << "Attempting to initialize a Vulkan playground on a system "
235  "that does not support Vulkan.";
236 #endif // TARGET_OS_MAC
237  return false;
238 }
239 
240 // |PlaygroundImpl|
242 PlaygroundImplVK::CreateVKProcAddressResolver() const {
243  return [](void* instance, const char* proc_name) -> void* {
244  return reinterpret_cast<void*>(::glfwGetInstanceProcAddress(
245  reinterpret_cast<VkInstance>(instance), proc_name));
246  };
247 }
248 
249 } // namespace impeller
static std::shared_ptr< ContextVK > Create(Settings settings)
Definition: context_vk.cc:105
std::function< void *(void *instance, const char *proc_name)> VKProcAddressResolver
Definition: playground.h:122
const PlaygroundSwitches switches_
fml::Status SetCapabilities(const std::shared_ptr< Capabilities > &capabilities) override
PlaygroundImplVK(PlaygroundSwitches switches)
static std::vector< std::shared_ptr< fml::Mapping > > ShaderLibraryMappingsForPlayground()
ISize64 ISize
Definition: size.h:162
std::vector< std::shared_ptr< fml::Mapping > > shader_libraries_data
Definition: context_vk.h:81
PFN_vkGetInstanceProcAddr proc_address_callback
Definition: context_vk.h:80
bool fatal_missing_validations
If validations are requested but cannot be enabled, log a fatal error.
Definition: context_vk.h:87
fml::UniqueFD cache_directory
Definition: context_vk.h:82
#define VALIDATION_LOG
Definition: validation.h:91