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