• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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