1 /*
2  * Copyright (C) 2023 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_gl.h"
18 
19 #include <android-base/logging.h>
20 #include <android-base/strings.h>
21 
22 #include "host/libs/graphics_detector/egl.h"
23 #include "host/libs/graphics_detector/gles.h"
24 #include "host/libs/graphics_detector/subprocess.h"
25 
26 namespace cuttlefish {
27 namespace {
28 
29 constexpr const char kSurfacelessContextExt[] = "EGL_KHR_surfaceless_context";
30 
31 class Closer {
32  public:
Closer(std::function<void ()> on_close)33   Closer(std::function<void()> on_close) : on_close_(std::move(on_close)) {}
~Closer()34   ~Closer() { on_close_(); }
35 
36  private:
37   std::function<void()> on_close_;
38 };
39 
PopulateEglAndGlesAvailabilityImpl(GraphicsAvailability * availability)40 void PopulateEglAndGlesAvailabilityImpl(GraphicsAvailability* availability) {
41   auto egl = Egl::Load();
42   if (!egl) {
43     LOG(VERBOSE) << "Failed to load EGL library.";
44     return;
45   }
46   LOG(VERBOSE) << "Loaded EGL library.";
47   availability->has_egl = true;
48 
49   EGLDisplay display = egl->eglGetDisplay(EGL_DEFAULT_DISPLAY);
50   if (display != EGL_NO_DISPLAY) {
51     LOG(VERBOSE) << "Found default display.";
52   } else {
53     LOG(VERBOSE) << "Failed to get default display. " << egl->eglGetError()
54                  << ". Attempting to get surfaceless display via "
55                  << "eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA)";
56 
57     if (egl->eglGetPlatformDisplayEXT == nullptr) {
58       LOG(VERBOSE) << "Failed to find function eglGetPlatformDisplayEXT";
59     } else {
60       display = egl->eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA,
61                                               EGL_DEFAULT_DISPLAY, NULL);
62     }
63   }
64 
65   if (display == EGL_NO_DISPLAY) {
66     LOG(VERBOSE) << "Failed to find display.";
67     return;
68   }
69 
70   EGLint client_version_major = 0;
71   EGLint client_version_minor = 0;
72   if (egl->eglInitialize(display, &client_version_major,
73                          &client_version_minor) != EGL_TRUE) {
74     LOG(VERBOSE) << "Failed to initialize display.";
75     return;
76   }
77   LOG(VERBOSE) << "Initialized display.";
78 
79   const std::string version_string = egl->eglQueryString(display, EGL_VERSION);
80   if (version_string.empty()) {
81     LOG(VERBOSE) << "Failed to query client version.";
82     return;
83   }
84   LOG(VERBOSE) << "Found version: " << version_string;
85   availability->egl_version = version_string;
86 
87   const std::string vendor_string = egl->eglQueryString(display, EGL_VENDOR);
88   if (vendor_string.empty()) {
89     LOG(VERBOSE) << "Failed to query vendor.";
90     return;
91   }
92   LOG(VERBOSE) << "Found vendor: " << vendor_string;
93   availability->egl_vendor = vendor_string;
94 
95   const std::string extensions_string =
96       egl->eglQueryString(display, EGL_EXTENSIONS);
97   if (extensions_string.empty()) {
98     LOG(VERBOSE) << "Failed to query extensions.";
99     return;
100   }
101   LOG(VERBOSE) << "Found extensions: " << extensions_string;
102   availability->egl_extensions = extensions_string;
103 
104   if (extensions_string.find(kSurfacelessContextExt) == std::string::npos) {
105     LOG(VERBOSE) << "Failed to find extension EGL_KHR_surfaceless_context.";
106     return;
107   }
108 
109   const std::string display_apis_string =
110       egl->eglQueryString(display, EGL_CLIENT_APIS);
111   if (display_apis_string.empty()) {
112     LOG(VERBOSE) << "Failed to query display apis.";
113     return;
114   }
115   LOG(VERBOSE) << "Found display apis: " << display_apis_string;
116 
117   if (egl->eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
118     LOG(VERBOSE) << "Failed to bind GLES API.";
119     return;
120   }
121   LOG(VERBOSE) << "Bound GLES API.";
122 
123   const EGLint framebuffer_config_attributes[] = {
124       EGL_SURFACE_TYPE,
125       EGL_PBUFFER_BIT,
126       EGL_RENDERABLE_TYPE,
127       EGL_OPENGL_ES2_BIT,
128       EGL_RED_SIZE,
129       1,
130       EGL_GREEN_SIZE,
131       1,
132       EGL_BLUE_SIZE,
133       1,
134       EGL_ALPHA_SIZE,
135       0,
136       EGL_NONE,
137   };
138 
139   EGLConfig framebuffer_config;
140   EGLint num_framebuffer_configs = 0;
141   if (egl->eglChooseConfig(display, framebuffer_config_attributes,
142                            &framebuffer_config, 1,
143                            &num_framebuffer_configs) != EGL_TRUE) {
144     LOG(VERBOSE) << "Failed to find matching framebuffer config.";
145     return;
146   }
147   LOG(VERBOSE) << "Found matching framebuffer config.";
148 
149   const EGLint gles2_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
150                                              EGL_NONE};
151   EGLContext gles2_context = egl->eglCreateContext(
152       display, framebuffer_config, EGL_NO_CONTEXT, gles2_context_attributes);
153   if (gles2_context == EGL_NO_CONTEXT) {
154     LOG(VERBOSE) << "Failed to create EGL context.";
155   } else {
156     LOG(VERBOSE) << "Created EGL context.";
157     Closer context_closer(
158         [&]() { egl->eglDestroyContext(display, gles2_context); });
159 
160     if (egl->eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
161                             gles2_context) != EGL_TRUE) {
162       LOG(VERBOSE) << "Failed to make GLES2 context current.";
163       return;
164     }
165     LOG(VERBOSE) << "Make GLES2 context current.";
166     availability->can_init_gles2_on_egl_surfaceless = true;
167 
168     auto gles = Gles::LoadFromEgl(&*egl);
169     if (!gles) {
170       LOG(VERBOSE) << "Failed to load GLES library.";
171       return;
172     }
173 
174     const GLubyte* gles2_vendor = gles->glGetString(GL_VENDOR);
175     if (gles2_vendor == nullptr) {
176       LOG(VERBOSE) << "Failed to query GLES2 vendor.";
177       return;
178     }
179     const std::string gles2_vendor_string((const char*)gles2_vendor);
180     LOG(VERBOSE) << "Found GLES2 vendor: " << gles2_vendor_string;
181     availability->gles2_vendor = gles2_vendor_string;
182 
183     const GLubyte* gles2_version = gles->glGetString(GL_VERSION);
184     if (gles2_version == nullptr) {
185       LOG(VERBOSE) << "Failed to query GLES2 vendor.";
186       return;
187     }
188     const std::string gles2_version_string((const char*)gles2_version);
189     LOG(VERBOSE) << "Found GLES2 version: " << gles2_version_string;
190     availability->gles2_version = gles2_version_string;
191 
192     const GLubyte* gles2_renderer = gles->glGetString(GL_RENDERER);
193     if (gles2_renderer == nullptr) {
194       LOG(VERBOSE) << "Failed to query GLES2 renderer.";
195       return;
196     }
197     const std::string gles2_renderer_string((const char*)gles2_renderer);
198     LOG(VERBOSE) << "Found GLES2 renderer: " << gles2_renderer_string;
199     availability->gles2_renderer = gles2_renderer_string;
200 
201     const GLubyte* gles2_extensions = gles->glGetString(GL_EXTENSIONS);
202     if (gles2_extensions == nullptr) {
203       LOG(VERBOSE) << "Failed to query GLES2 extensions.";
204       return;
205     }
206     const std::string gles2_extensions_string((const char*)gles2_extensions);
207     LOG(VERBOSE) << "Found GLES2 extensions: " << gles2_extensions_string;
208     availability->gles2_extensions = gles2_extensions_string;
209   }
210 
211   const EGLint gles3_context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 3,
212                                              EGL_NONE};
213   EGLContext gles3_context = egl->eglCreateContext(
214       display, framebuffer_config, EGL_NO_CONTEXT, gles3_context_attributes);
215   if (gles3_context == EGL_NO_CONTEXT) {
216     LOG(VERBOSE) << "Failed to create GLES3 context.";
217   } else {
218     LOG(VERBOSE) << "Created GLES3 context.";
219     Closer context_closer(
220         [&]() { egl->eglDestroyContext(display, gles3_context); });
221 
222     if (egl->eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
223                             gles3_context) != EGL_TRUE) {
224       LOG(VERBOSE) << "Failed to make GLES3 context current.";
225       return;
226     }
227     LOG(VERBOSE) << "Make GLES3 context current.";
228     availability->can_init_gles3_on_egl_surfaceless = true;
229 
230     auto gles = Gles::LoadFromEgl(&*egl);
231     if (!gles) {
232       LOG(VERBOSE) << "Failed to load GLES library.";
233       return;
234     }
235 
236     const GLubyte* gles3_vendor = gles->glGetString(GL_VENDOR);
237     if (gles3_vendor == nullptr) {
238       LOG(VERBOSE) << "Failed to query GLES3 vendor.";
239       return;
240     }
241     const std::string gles3_vendor_string((const char*)gles3_vendor);
242     LOG(VERBOSE) << "Found GLES3 vendor: " << gles3_vendor_string;
243     availability->gles3_vendor = gles3_vendor_string;
244 
245     const GLubyte* gles3_version = gles->glGetString(GL_VERSION);
246     if (gles3_version == nullptr) {
247       LOG(VERBOSE) << "Failed to query GLES2 vendor.";
248       return;
249     }
250     const std::string gles3_version_string((const char*)gles3_version);
251     LOG(VERBOSE) << "Found GLES3 version: " << gles3_version_string;
252     availability->gles3_version = gles3_version_string;
253 
254     const GLubyte* gles3_renderer = gles->glGetString(GL_RENDERER);
255     if (gles3_renderer == nullptr) {
256       LOG(VERBOSE) << "Failed to query GLES3 renderer.";
257       return;
258     }
259     const std::string gles3_renderer_string((const char*)gles3_renderer);
260     LOG(VERBOSE) << "Found GLES3 renderer: " << gles3_renderer_string;
261     availability->gles3_renderer = gles3_renderer_string;
262 
263     const GLubyte* gles3_extensions = gles->glGetString(GL_EXTENSIONS);
264     if (gles3_extensions == nullptr) {
265       LOG(VERBOSE) << "Failed to query GLES3 extensions.";
266       return;
267     }
268     const std::string gles3_extensions_string((const char*)gles3_extensions);
269     LOG(VERBOSE) << "Found GLES3 extensions: " << gles3_extensions_string;
270     availability->gles3_extensions = gles3_extensions_string;
271   }
272 }
273 
274 }  // namespace
275 
PopulateEglAndGlesAvailability(GraphicsAvailability * availability)276 void PopulateEglAndGlesAvailability(GraphicsAvailability* availability) {
277   DoWithSubprocessCheck("PopulateEglAndGlesAvailability", [&]() {
278     PopulateEglAndGlesAvailabilityImpl(availability);
279   });
280 }
281 
282 }  // namespace cuttlefish
283