• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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