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 "GraphicsDetectorGl.h"
18
19 #include "Egl.h"
20 #include "Gles.h"
21
22 namespace gfxstream {
23 namespace {
24
25 using ::gfxstream::proto::EglAvailability;
26 using GlesContextAvailability = ::gfxstream::proto::EglAvailability::GlesContextAvailability;
27
28 constexpr const char kSurfacelessContextExt[] = "EGL_KHR_surfaceless_context";
29
30 class Closer {
31 public:
Closer(std::function<void ()> on_close)32 Closer(std::function<void()> on_close) : on_close_(std::move(on_close)) {}
~Closer()33 ~Closer() { on_close_(); }
34
35 private:
36 std::function<void()> on_close_;
37 };
38
39 enum class GlesLoadMethod {
40 VIA_EGL,
41 VIA_GLESV2,
42 };
43
GetGlesContextAvailability(Egl & egl,EGLDisplay eglDisplay,EGLConfig eglConfig,EGLint contextVersion,GlesLoadMethod loadMethod)44 gfxstream::expected<GlesContextAvailability, std::string> GetGlesContextAvailability(
45 Egl& egl, EGLDisplay eglDisplay, EGLConfig eglConfig, EGLint contextVersion,
46 GlesLoadMethod loadMethod) {
47 GlesContextAvailability availability;
48
49 const EGLint contextAttributes[] = {
50 // clang-format off
51 EGL_CONTEXT_CLIENT_VERSION, contextVersion,
52 EGL_NONE,
53 // clang-format on
54 };
55
56 EGLContext context =
57 egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttributes);
58 if (context == EGL_NO_CONTEXT) {
59 return gfxstream::unexpected("Failed to create context.");
60 }
61 Closer contextCloser([&]() { egl.eglDestroyContext(eglDisplay, context); });
62
63 if (egl.eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context) != EGL_TRUE) {
64 return gfxstream::unexpected("Failed to make context current.");
65 }
66
67 auto gles = loadMethod == GlesLoadMethod::VIA_EGL ? GFXSTREAM_EXPECT(Gles::LoadFromEgl(&egl))
68 : GFXSTREAM_EXPECT(Gles::Load());
69
70 const GLubyte* gles_vendor = gles.glGetString(GL_VENDOR);
71 if (gles_vendor == nullptr) {
72 return gfxstream::unexpected("Failed to query vendor.");
73 }
74 const std::string gles_vendor_string((const char*)gles_vendor);
75 availability.set_vendor(gles_vendor_string);
76
77 const GLubyte* gles_version = gles.glGetString(GL_VERSION);
78 if (gles_version == nullptr) {
79 gfxstream::unexpected("Failed to query vendor.");
80 }
81 const std::string gles_version_string((const char*)gles_version);
82 availability.set_version(gles_version_string);
83
84 const GLubyte* gles_renderer = gles.glGetString(GL_RENDERER);
85 if (gles_renderer == nullptr) {
86 gfxstream::unexpected("Failed to query renderer.");
87 }
88 const std::string gles_renderer_string((const char*)gles_renderer);
89 availability.set_renderer(gles_renderer_string);
90
91 const GLubyte* gles_extensions = gles.glGetString(GL_EXTENSIONS);
92 if (gles_extensions == nullptr) {
93 return gfxstream::unexpected("Failed to query extensions.");
94 }
95 const std::string gles_extensions_string((const char*)gles_extensions);
96 availability.set_extensions(gles_extensions_string);
97
98 return availability;
99 }
100
101 } // namespace
102
PopulateEglAndGlesAvailability(::gfxstream::proto::GraphicsAvailability * availability)103 gfxstream::expected<Ok, std::string> PopulateEglAndGlesAvailability(
104 ::gfxstream::proto::GraphicsAvailability* availability) {
105 auto egl = GFXSTREAM_EXPECT(Egl::Load());
106
107 EglAvailability* eglAvailability = availability->mutable_egl();
108
109 EGLDisplay display = egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
110 if (display == EGL_NO_DISPLAY) {
111 if (egl.eglGetPlatformDisplayEXT != nullptr) {
112 display = egl.eglGetPlatformDisplayEXT(EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, NULL);
113 }
114 }
115
116 if (display == EGL_NO_DISPLAY) {
117 return gfxstream::unexpected("Failed to find display.");
118 }
119
120 EGLint client_version_major = 0;
121 EGLint client_version_minor = 0;
122 if (egl.eglInitialize(display, &client_version_major,
123 &client_version_minor) != EGL_TRUE) {
124 return gfxstream::unexpected("Failed to initialize display.");
125 }
126
127 const std::string version_string = egl.eglQueryString(display, EGL_VERSION);
128 if (version_string.empty()) {
129 return gfxstream::unexpected("Failed to query client version.");
130 }
131 eglAvailability->set_version(version_string);
132
133 const std::string vendor_string = egl.eglQueryString(display, EGL_VENDOR);
134 if (vendor_string.empty()) {
135 return gfxstream::unexpected("Failed to query vendor.");
136 }
137 eglAvailability->set_vendor(vendor_string);
138
139 const std::string extensions_string =
140 egl.eglQueryString(display, EGL_EXTENSIONS);
141 if (extensions_string.empty()) {
142 return gfxstream::unexpected("Failed to query extensions.");
143 }
144 eglAvailability->set_extensions(extensions_string);
145
146 if (extensions_string.find(kSurfacelessContextExt) == std::string::npos) {
147 return gfxstream::unexpected("Failed to find extension EGL_KHR_surfaceless_context.");
148 }
149
150 const std::string display_apis_string = egl.eglQueryString(display, EGL_CLIENT_APIS);
151 if (display_apis_string.empty()) {
152 return gfxstream::unexpected("Failed to query display apis.");
153 }
154
155 if (egl.eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
156 return gfxstream::unexpected("Failed to bind GLES API.");
157 }
158
159 const EGLint framebufferConfigAttributes[] = {
160 // clang-format off
161 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
162 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
163 EGL_RED_SIZE, 1,
164 EGL_GREEN_SIZE, 1,
165 EGL_BLUE_SIZE, 1,
166 EGL_ALPHA_SIZE, 0,
167 EGL_NONE,
168 // clang-format on
169 };
170
171 EGLConfig framebufferConfig;
172 EGLint numFramebufferConfigs = 0;
173 if (egl.eglChooseConfig(display, framebufferConfigAttributes, &framebufferConfig, 1,
174 &numFramebufferConfigs) != EGL_TRUE) {
175 return gfxstream::unexpected("Failed to find matching framebuffer config.");
176 }
177
178 struct GlesContextCheckOptions {
179 std::function<GlesContextAvailability*()> availabilityProvider;
180 EGLint contextVersion;
181 GlesLoadMethod loadMethod;
182
183 std::string to_string() const {
184 std::string ret;
185 ret += "options {";
186
187 ret += " version: ";
188 ret += std::to_string(contextVersion);
189
190 ret += " load-method: ";
191 ret += loadMethod == GlesLoadMethod::VIA_EGL ? "via-egl" : "via-glesv2";
192
193 ret += " }";
194 return ret;
195 }
196 };
197 const std::vector<GlesContextCheckOptions> contextChecks = {
198 GlesContextCheckOptions{
199 .availabilityProvider = [&]() { return eglAvailability->mutable_gles2_availability(); },
200 .contextVersion = 2,
201 .loadMethod = GlesLoadMethod::VIA_EGL,
202 },
203 GlesContextCheckOptions{
204 .availabilityProvider =
205 [&]() { return eglAvailability->mutable_gles2_direct_availability(); },
206 .contextVersion = 2,
207 .loadMethod = GlesLoadMethod::VIA_GLESV2,
208 },
209 GlesContextCheckOptions{
210 .availabilityProvider = [&]() { return eglAvailability->mutable_gles3_availability(); },
211 .contextVersion = 3,
212 .loadMethod = GlesLoadMethod::VIA_EGL,
213 },
214 GlesContextCheckOptions{
215 .availabilityProvider =
216 [&]() { return eglAvailability->mutable_gles3_direct_availability(); },
217 .contextVersion = 3,
218 .loadMethod = GlesLoadMethod::VIA_GLESV2,
219 },
220 };
221
222 for (const GlesContextCheckOptions& contextCheck : contextChecks) {
223 auto contextCheckResult = GetGlesContextAvailability(
224 egl, display, framebufferConfig, contextCheck.contextVersion, contextCheck.loadMethod);
225 if (contextCheckResult.ok()) {
226 *contextCheck.availabilityProvider() = contextCheckResult.value();
227 } else {
228 eglAvailability->add_errors("Failed to complete GLES context check using " +
229 contextCheck.to_string() + ": " +
230 contextCheckResult.error());
231 }
232 }
233
234 return Ok{};
235 }
236
237 } // namespace gfxstream
238