1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/libs/graphics_detector/graphics_detector.h"
18
19 #include <sstream>
20 #include <vector>
21
22 #include <EGL/egl.h>
23 #include <EGL/eglext.h>
24 #include <android-base/logging.h>
25 #include <android-base/strings.h>
26 #include <dlfcn.h>
27 #include <sys/wait.h>
28 #include <vulkan/vulkan.h>
29
30 namespace cuttlefish {
31 namespace {
32
33 constexpr const char kEglLib[] = "libEGL.so.1";
34 constexpr const char kGlLib[] = "libOpenGL.so.0";
35 constexpr const char kGles1Lib[] = "libGLESv1_CM.so.1";
36 constexpr const char kGles2Lib[] = "libGLESv2.so.2";
37 constexpr const char kVulkanLib[] = "libvulkan.so.1";
38
39 constexpr const char kSurfacelessContextExt[] = "EGL_KHR_surfaceless_context";
40
41 class Closer {
42 public:
Closer(std::function<void ()> on_close)43 Closer(std::function<void()> on_close) : on_close_(on_close) {}
~Closer()44 ~Closer() { on_close_(); }
45
46 private:
47 std::function<void()> on_close_;
48 };
49
50 struct LibraryCloser {
51 public:
operator ()cuttlefish::__anona11fd8f50111::LibraryCloser52 void operator()(void* library) { dlclose(library); }
53 };
54
55 using ManagedLibrary = std::unique_ptr<void, LibraryCloser>;
56
PopulateGlAvailability(GraphicsAvailability * availability)57 void PopulateGlAvailability(GraphicsAvailability* availability) {
58 ManagedLibrary gl_lib(dlopen(kGlLib, RTLD_NOW | RTLD_LOCAL));
59 if (!gl_lib) {
60 LOG(VERBOSE) << "Failed to dlopen " << kGlLib << ".";
61 return;
62 }
63 LOG(VERBOSE) << "Loaded " << kGlLib << ".";
64 availability->has_gl = true;
65 }
66
PopulateGles1Availability(GraphicsAvailability * availability)67 void PopulateGles1Availability(GraphicsAvailability* availability) {
68 ManagedLibrary gles1_lib(dlopen(kGles1Lib, RTLD_NOW | RTLD_LOCAL));
69 if (!gles1_lib) {
70 LOG(VERBOSE) << "Failed to dlopen " << kGles1Lib << ".";
71 return;
72 }
73 LOG(VERBOSE) << "Loaded " << kGles1Lib << ".";
74 availability->has_gles1 = true;
75 }
76
PopulateGles2Availability(GraphicsAvailability * availability)77 void PopulateGles2Availability(GraphicsAvailability* availability) {
78 ManagedLibrary gles2_lib(dlopen(kGles2Lib, RTLD_NOW | RTLD_LOCAL));
79 if (!gles2_lib) {
80 LOG(VERBOSE) << "Failed to dlopen " << kGles2Lib << ".";
81 return;
82 }
83 LOG(VERBOSE) << "Loaded " << kGles2Lib << ".";
84 availability->has_gles2 = true;
85 }
86
PopulateEglAvailability(GraphicsAvailability * availability)87 void PopulateEglAvailability(GraphicsAvailability* availability) {
88 ManagedLibrary egllib(dlopen(kEglLib, RTLD_NOW | RTLD_LOCAL));
89 if (!egllib) {
90 LOG(VERBOSE) << "Failed to dlopen " << kEglLib << ".";
91 return;
92 }
93 LOG(VERBOSE) << "Loaded " << kEglLib << ".";
94 availability->has_egl = true;
95
96 PFNEGLGETPROCADDRESSPROC eglGetProcAddress =
97 reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(
98 dlsym(egllib.get(), "eglGetProcAddress"));
99 if (eglGetProcAddress == nullptr) {
100 LOG(VERBOSE) << "Failed to find function eglGetProcAddress.";
101 return;
102 }
103 LOG(VERBOSE) << "Loaded eglGetProcAddress.";
104
105 // Some implementations have it so that eglGetProcAddress is only for
106 // loading EXT functions.
107 auto EglLoadFunction = [&](const char* name) {
108 void* func = dlsym(egllib.get(), name);
109 if (func == NULL) {
110 func = reinterpret_cast<void*>(eglGetProcAddress(name));
111 }
112 return func;
113 };
114
115 PFNEGLGETERRORPROC eglGetError =
116 reinterpret_cast<PFNEGLGETERRORPROC>(EglLoadFunction("eglGetError"));
117 if (eglGetError == nullptr) {
118 LOG(VERBOSE) << "Failed to find function eglGetError.";
119 return;
120 }
121 LOG(VERBOSE) << "Loaded eglGetError.";
122
123 PFNEGLGETDISPLAYPROC eglGetDisplay =
124 reinterpret_cast<PFNEGLGETDISPLAYPROC>(EglLoadFunction("eglGetDisplay"));
125 if (eglGetDisplay == nullptr) {
126 LOG(VERBOSE) << "Failed to find function eglGetDisplay.";
127 return;
128 }
129 LOG(VERBOSE) << "Loaded eglGetDisplay.";
130
131 EGLDisplay default_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
132 if (default_display == EGL_NO_DISPLAY) {
133 LOG(VERBOSE) << "Failed to get default display. " << eglGetError();
134 return;
135 }
136 LOG(VERBOSE) << "Found default display.";
137 availability->has_egl_default_display = true;
138
139 PFNEGLINITIALIZEPROC eglInitialize =
140 reinterpret_cast<PFNEGLINITIALIZEPROC>(EglLoadFunction("eglInitialize"));
141 if (eglInitialize == nullptr) {
142 LOG(VERBOSE) << "Failed to find function eglQueryString";
143 return;
144 }
145
146 EGLint client_version_major = 0;
147 EGLint client_version_minor = 0;
148 if (eglInitialize(default_display,
149 &client_version_major,
150 &client_version_minor) != EGL_TRUE) {
151 LOG(VERBOSE) << "Failed to initialize default display.";
152 return;
153 }
154 LOG(VERBOSE) << "Initialized default display.";
155
156 PFNEGLQUERYSTRINGPROC eglQueryString =
157 reinterpret_cast<PFNEGLQUERYSTRINGPROC>(EglLoadFunction("eglQueryString"));
158 if (eglQueryString == nullptr) {
159 LOG(VERBOSE) << "Failed to find function eglQueryString";
160 return;
161 }
162 LOG(VERBOSE) << "Loaded eglQueryString.";
163
164 std::string client_extensions;
165 if (client_version_major >= 1 && client_version_minor >= 5) {
166 client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
167 }
168 availability->egl_client_extensions = client_extensions;
169
170 EGLDisplay display = EGL_NO_DISPLAY;
171
172 if (client_extensions.find("EGL_EXT_platform_base") != std::string::npos) {
173 LOG(VERBOSE) << "Client extension EGL_EXT_platform_base is supported.";
174
175 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
176 reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
177 EglLoadFunction("eglGetPlatformDisplayEXT"));
178 if (eglGetPlatformDisplayEXT == nullptr) {
179 LOG(VERBOSE) << "Failed to find function eglGetPlatformDisplayEXT";
180 return;
181 }
182
183 display =
184 eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
185 EGL_DEFAULT_DISPLAY,
186 NULL);
187 } else {
188 LOG(VERBOSE) << "Failed to find client extension EGL_EXT_platform_base.";
189 }
190 if (display == EGL_NO_DISPLAY) {
191 LOG(VERBOSE) << "Failed to get EGL_PLATFORM_SURFACELESS_MESA display..."
192 << "failing back to EGL_DEFAULT_DISPLAY display.";
193 display = default_display;
194 }
195 if (display == EGL_NO_DISPLAY) {
196 LOG(VERBOSE) << "Failed to find display.";
197 return;
198 }
199
200 if (eglInitialize(display,
201 &client_version_major,
202 &client_version_minor) != EGL_TRUE) {
203 LOG(VERBOSE) << "Failed to initialize surfaceless display.";
204 return;
205 }
206 LOG(VERBOSE) << "Initialized surfaceless display.";
207
208 const std::string version_string = eglQueryString(display, EGL_VERSION);
209 if (version_string.empty()) {
210 LOG(VERBOSE) << "Failed to query client version.";
211 return;
212 }
213 LOG(VERBOSE) << "Found version: " << version_string;
214 availability->egl_version = version_string;
215
216 const std::string vendor_string = eglQueryString(display, EGL_VENDOR);
217 if (vendor_string.empty()) {
218 LOG(VERBOSE) << "Failed to query vendor.";
219 return;
220 }
221 LOG(VERBOSE) << "Found vendor: " << vendor_string;
222 availability->egl_vendor = vendor_string;
223
224 const std::string extensions_string = eglQueryString(display, EGL_EXTENSIONS);
225 if (extensions_string.empty()) {
226 LOG(VERBOSE) << "Failed to query extensions.";
227 return;
228 }
229 LOG(VERBOSE) << "Found extensions: " << extensions_string;
230 availability->egl_extensions = extensions_string;
231
232 if (extensions_string.find(kSurfacelessContextExt) == std::string::npos) {
233 LOG(VERBOSE) << "Failed to find extension EGL_KHR_surfaceless_context.";
234 return;
235 }
236
237 const std::string display_apis_string = eglQueryString(display,
238 EGL_CLIENT_APIS);
239 if (display_apis_string.empty()) {
240 LOG(VERBOSE) << "Failed to query display apis.";
241 return;
242 }
243 LOG(VERBOSE) << "Found display apis: " << display_apis_string;
244
245 PFNEGLBINDAPIPROC eglBindAPI =
246 reinterpret_cast<PFNEGLBINDAPIPROC>(EglLoadFunction("eglBindAPI"));
247 if (eglBindAPI == nullptr) {
248 LOG(VERBOSE) << "Failed to find function eglBindAPI";
249 return;
250 }
251 LOG(VERBOSE) << "Loaded eglBindAPI.";
252
253 if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
254 LOG(VERBOSE) << "Failed to bind GLES API.";
255 return;
256 }
257 LOG(VERBOSE) << "Bound GLES API.";
258
259 PFNEGLCHOOSECONFIGPROC eglChooseConfig =
260 reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(
261 EglLoadFunction("eglChooseConfig"));
262 if (eglChooseConfig == nullptr) {
263 LOG(VERBOSE) << "Failed to find function eglChooseConfig";
264 return;
265 }
266 LOG(VERBOSE) << "Loaded eglChooseConfig.";
267
268 const EGLint framebuffer_config_attributes[] = {
269 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
270 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
271 EGL_RED_SIZE, 1,
272 EGL_GREEN_SIZE, 1,
273 EGL_BLUE_SIZE, 1,
274 EGL_ALPHA_SIZE, 0,
275 EGL_NONE,
276 };
277
278 EGLConfig framebuffer_config;
279 EGLint num_framebuffer_configs = 0;
280 if (eglChooseConfig(display,
281 framebuffer_config_attributes,
282 &framebuffer_config,
283 1,
284 &num_framebuffer_configs) != EGL_TRUE) {
285 LOG(VERBOSE) << "Failed to find matching framebuffer config.";
286 return;
287 }
288 LOG(VERBOSE) << "Found matching framebuffer config.";
289
290 PFNEGLCREATECONTEXTPROC eglCreateContext =
291 reinterpret_cast<PFNEGLCREATECONTEXTPROC>(
292 EglLoadFunction("eglCreateContext"));
293 if (eglCreateContext == nullptr) {
294 LOG(VERBOSE) << "Failed to find function eglCreateContext";
295 return;
296 }
297 LOG(VERBOSE) << "Loaded eglCreateContext.";
298
299 PFNEGLDESTROYCONTEXTPROC eglDestroyContext =
300 reinterpret_cast<PFNEGLDESTROYCONTEXTPROC>(
301 EglLoadFunction("eglDestroyContext"));
302 if (eglDestroyContext == nullptr) {
303 LOG(VERBOSE) << "Failed to find function eglDestroyContext";
304 return;
305 }
306 LOG(VERBOSE) << "Loaded eglDestroyContext.";
307
308 const EGLint context_attributes[] = {
309 EGL_CONTEXT_CLIENT_VERSION, 2,
310 EGL_NONE
311 };
312
313 EGLContext context = eglCreateContext(display,
314 framebuffer_config,
315 EGL_NO_CONTEXT,
316 context_attributes);
317 if (context == EGL_NO_CONTEXT) {
318 LOG(VERBOSE) << "Failed to create EGL context.";
319 return;
320 }
321 LOG(VERBOSE) << "Created EGL context.";
322 Closer context_closer([&]() { eglDestroyContext(display, context); });
323
324 PFNEGLMAKECURRENTPROC eglMakeCurrent =
325 reinterpret_cast<PFNEGLMAKECURRENTPROC>(EglLoadFunction("eglMakeCurrent"));
326 if (eglMakeCurrent == nullptr) {
327 LOG(VERBOSE) << "Failed to find function eglMakeCurrent";
328 return;
329 }
330 LOG(VERBOSE) << "Loaded eglMakeCurrent.";
331
332 if (eglMakeCurrent(display,
333 EGL_NO_SURFACE,
334 EGL_NO_SURFACE,
335 context) != EGL_TRUE) {
336 LOG(VERBOSE) << "Failed to make EGL context current.";
337 return;
338 }
339 LOG(VERBOSE) << "Make EGL context current.";
340 availability->has_egl_surfaceless_with_gles = true;
341 }
342
PopulateVulkanAvailability(GraphicsAvailability * availability)343 void PopulateVulkanAvailability(GraphicsAvailability* availability) {
344 ManagedLibrary vklib(dlopen(kVulkanLib, RTLD_NOW | RTLD_LOCAL));
345 if (!vklib) {
346 LOG(VERBOSE) << "Failed to dlopen " << kVulkanLib << ".";
347 return;
348 }
349 LOG(VERBOSE) << "Loaded " << kVulkanLib << ".";
350 availability->has_vulkan = true;
351
352 uint32_t instance_version = 0;
353
354 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
355 reinterpret_cast<PFN_vkGetInstanceProcAddr>(
356 dlsym(vklib.get(), "vkGetInstanceProcAddr"));
357 if (vkGetInstanceProcAddr == nullptr) {
358 LOG(VERBOSE) << "Failed to find symbol vkGetInstanceProcAddr.";
359 return;
360 }
361
362 PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion =
363 reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
364 dlsym(vklib.get(), "vkEnumerateInstanceVersion"));
365 if (vkEnumerateInstanceVersion == nullptr ||
366 vkEnumerateInstanceVersion(&instance_version) != VK_SUCCESS) {
367 instance_version = VK_API_VERSION_1_0;
368 }
369
370 PFN_vkCreateInstance vkCreateInstance =
371 reinterpret_cast<PFN_vkCreateInstance>(
372 vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"));
373 if (vkCreateInstance == nullptr) {
374 LOG(VERBOSE) << "Failed to get function vkCreateInstance.";
375 return;
376 }
377
378 VkApplicationInfo application_info;
379 application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
380 application_info.pNext = nullptr;
381 application_info.pApplicationName = "";
382 application_info.applicationVersion = 1;
383 application_info.pEngineName = "";
384 application_info.engineVersion = 1;
385 application_info.apiVersion = instance_version;
386
387 VkInstanceCreateInfo instance_create_info = {};
388 instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
389 instance_create_info.pNext = nullptr;
390 instance_create_info.flags = 0;
391 instance_create_info.pApplicationInfo = &application_info;
392 instance_create_info.enabledLayerCount = 0;
393 instance_create_info.ppEnabledLayerNames = nullptr;
394 instance_create_info.enabledExtensionCount = 0;
395 instance_create_info.ppEnabledExtensionNames = nullptr;
396
397 VkInstance instance = VK_NULL_HANDLE;
398 VkResult result = vkCreateInstance(&instance_create_info, nullptr, &instance);
399 if (result != VK_SUCCESS) {
400 if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
401 LOG(VERBOSE) << "Failed to create Vulkan instance: "
402 << "VK_ERROR_OUT_OF_HOST_MEMORY.";
403 } else if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
404 LOG(VERBOSE) << "Failed to create Vulkan instance: "
405 << "VK_ERROR_OUT_OF_DEVICE_MEMORY.";
406 } else if (result == VK_ERROR_INITIALIZATION_FAILED) {
407 LOG(VERBOSE) << "Failed to create Vulkan instance: "
408 << "VK_ERROR_INITIALIZATION_FAILED.";
409 } else if (result == VK_ERROR_LAYER_NOT_PRESENT) {
410 LOG(VERBOSE) << "Failed to create Vulkan instance: "
411 << "VK_ERROR_LAYER_NOT_PRESENT.";
412 } else if (result == VK_ERROR_EXTENSION_NOT_PRESENT) {
413 LOG(VERBOSE) << "Failed to create Vulkan instance: "
414 << "VK_ERROR_EXTENSION_NOT_PRESENT.";
415 } else if (result == VK_ERROR_INCOMPATIBLE_DRIVER) {
416 LOG(VERBOSE) << "Failed to create Vulkan instance: "
417 << "VK_ERROR_INCOMPATIBLE_DRIVER.";
418 } else {
419 LOG(VERBOSE) << "Failed to create Vulkan instance.";
420 }
421 return;
422 }
423
424 PFN_vkDestroyInstance vkDestroyInstance =
425 reinterpret_cast<PFN_vkDestroyInstance>(
426 vkGetInstanceProcAddr(instance, "vkDestroyInstance"));
427 if (vkDestroyInstance == nullptr) {
428 LOG(VERBOSE) << "Failed to get function vkDestroyInstance.";
429 return;
430 }
431
432 Closer instancecloser([&]() {vkDestroyInstance(instance, nullptr); });
433
434 PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices =
435 reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
436 vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices"));
437 if (vkEnumeratePhysicalDevices == nullptr) {
438 LOG(VERBOSE) << "Failed to "
439 << "vkGetInstanceProcAddr(vkEnumeratePhysicalDevices).";
440 return;
441 }
442
443 PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties =
444 reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
445 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties"));
446 if (vkGetPhysicalDeviceProperties == nullptr) {
447 LOG(VERBOSE) << "Failed to "
448 << "vkGetInstanceProcAddr(vkGetPhysicalDeviceProperties).";
449 return;
450 }
451
452 auto vkEnumerateDeviceExtensionProperties =
453 reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
454 vkGetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties"));
455 if (vkEnumerateDeviceExtensionProperties == nullptr) {
456 LOG(VERBOSE) << "Failed to "
457 << "vkGetInstanceProcAddr("
458 << "vkEnumerateDeviceExtensionProperties"
459 << ").";
460 return;
461 }
462
463 uint32_t device_count = 0;
464 result = vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
465 if (result != VK_SUCCESS) {
466 if (result == VK_INCOMPLETE) {
467 LOG(VERBOSE) << "Failed to enumerate physical device count: "
468 << "VK_INCOMPLETE";
469 } else if (result == VK_ERROR_OUT_OF_HOST_MEMORY) {
470 LOG(VERBOSE) << "Failed to enumerate physical device count: "
471 << "VK_ERROR_OUT_OF_HOST_MEMORY";
472 } else if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY) {
473 LOG(VERBOSE) << "Failed to enumerate physical device count: "
474 << "VK_ERROR_OUT_OF_DEVICE_MEMORY";
475 } else if (result == VK_ERROR_INITIALIZATION_FAILED) {
476 LOG(VERBOSE) << "Failed to enumerate physical device count: "
477 << "VK_ERROR_INITIALIZATION_FAILED";
478 } else {
479 LOG(VERBOSE) << "Failed to enumerate physical device count.";
480 }
481 return;
482 }
483
484 if (device_count == 0) {
485 LOG(VERBOSE) << "No physical devices present.";
486 return;
487 }
488
489 std::vector<VkPhysicalDevice> devices(device_count, VK_NULL_HANDLE);
490 result = vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
491 if (result != VK_SUCCESS) {
492 LOG(VERBOSE) << "Failed to enumerate physical devices.";
493 return;
494 }
495
496 for (VkPhysicalDevice device : devices) {
497 VkPhysicalDeviceProperties device_properties = {};
498 vkGetPhysicalDeviceProperties(device, &device_properties);
499
500 uint32_t device_extensions_count = 0;
501 vkEnumerateDeviceExtensionProperties(device,
502 nullptr,
503 &device_extensions_count,
504 nullptr);
505
506 std::vector<VkExtensionProperties> device_extensions;
507 device_extensions.resize(device_extensions_count);
508
509 vkEnumerateDeviceExtensionProperties(device,
510 nullptr,
511 &device_extensions_count,
512 device_extensions.data());
513
514 std::vector<const char*> device_extensions_strings;
515 for (const VkExtensionProperties& device_extension : device_extensions) {
516 device_extensions_strings.push_back(device_extension.extensionName);
517 }
518
519 std::string device_extensions_string =
520 android::base::Join(device_extensions_strings, ' ');
521
522 if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
523 availability->has_discrete_gpu = true;
524 availability->discrete_gpu_device_name = device_properties.deviceName;
525 availability->discrete_gpu_device_extensions = device_extensions_string;
526 break;
527 }
528 }
529 }
530
GetGraphicsAvailability()531 GraphicsAvailability GetGraphicsAvailability() {
532 GraphicsAvailability availability;
533
534 PopulateEglAvailability(&availability);
535 PopulateGlAvailability(&availability);
536 PopulateGles1Availability(&availability);
537 PopulateGles2Availability(&availability);
538 PopulateVulkanAvailability(&availability);
539
540 return availability;
541 }
542
543 } // namespace
544
ShouldEnableAcceleratedRendering(const GraphicsAvailability & availability)545 bool ShouldEnableAcceleratedRendering(
546 const GraphicsAvailability& availability) {
547 return availability.has_egl && availability.has_egl_surfaceless_with_gles &&
548 availability.has_discrete_gpu;
549 }
550
551 // Runs GetGraphicsAvailability() inside of a subprocess first to ensure that
552 // GetGraphicsAvailability() can complete successfully without crashing
553 // assemble_cvd. Configurations such as GCE instances without a GPU but with GPU
554 // drivers for example have seen crashes.
GetGraphicsAvailabilityWithSubprocessCheck()555 GraphicsAvailability GetGraphicsAvailabilityWithSubprocessCheck() {
556 pid_t pid = fork();
557 if (pid == 0) {
558 GetGraphicsAvailability();
559 std::exit(0);
560 }
561 int status;
562 if (waitpid(pid, &status, 0) != pid) {
563 PLOG(ERROR) << "Failed to wait for graphics check subprocess";
564 return GraphicsAvailability{};
565 }
566 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
567 return GetGraphicsAvailability();
568 }
569 LOG(VERBOSE) << "Subprocess for detect_graphics failed with " << status;
570 return GraphicsAvailability{};
571 }
572
operator <<(std::ostream & stream,const GraphicsAvailability & availability)573 std::ostream& operator<<(std::ostream& stream,
574 const GraphicsAvailability& availability) {
575 std::ios_base::fmtflags flags_backup(stream.flags());
576 stream << std::boolalpha;
577 stream << "Graphics Availability:\n";
578 stream << "OpenGL available: " << availability.has_gl << "\n";
579 stream << "OpenGL ES1 available: " << availability.has_gles1 << "\n";
580 stream << "OpenGL ES2 available: " << availability.has_gles2 << "\n";
581 stream << "EGL available: " << availability.has_egl << "\n";
582 stream << "EGL client extensions: " << availability.egl_client_extensions
583 << "\n";
584 stream << "EGL default display available: "
585 << availability.has_egl_default_display << "\n";
586 stream << "EGL display vendor: " << availability.egl_vendor << "\n";
587 stream << "EGL display version: " << availability.egl_version << "\n";
588 stream << "EGL display extensions: " << availability.egl_extensions << "\n";
589 stream << "EGL surfaceless display with GLES: "
590 << availability.has_egl_surfaceless_with_gles << "\n";
591 stream << "Vulkan available: " << availability.has_vulkan << "\n";
592 stream << "Vulkan discrete GPU detected: " << availability.has_discrete_gpu
593 << "\n";
594 if (availability.has_discrete_gpu) {
595 stream << "Vulkan discrete GPU device name: "
596 << availability.discrete_gpu_device_name << "\n";
597 stream << "Vulkan discrete GPU device extensions: "
598 << availability.discrete_gpu_device_extensions << "\n";
599 }
600 stream.flags(flags_backup);
601 return stream;
602 }
603
604 } // namespace cuttlefish
605