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