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