Add a graphics detector
... which can be used to figure out what libs/gpus/drivers/quirks
are present on a machine.
This previously existed in Cuttlefish. However, due to
incompatibilities between Android built host tools and non-Android
built host GPU userspace drivers/libs, we would like to move this
to a prebuilt. Gfxstream is already available as a prebuilt and
the detector seems like it could be more broadly useful so let's
move it into Gfxstream.
Bug: b/297563172
Test: m gfxstream_graphics_detector && gfxstream_graphics_detector
Change-Id: I0e3267eaf948d41fc8f474c48c9d541b05173e05
diff --git a/common/detector/Egl.cpp b/common/detector/Egl.cpp
new file mode 100644
index 0000000..20f9c66
--- /dev/null
+++ b/common/detector/Egl.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Egl.h"
+
+#include <GLES/gl.h>
+
+namespace gfxstream {
+namespace {
+
+constexpr const char kEglLib[] = "libEGL.so";
+constexpr const char kEglLibAlt[] = "libEGL.so.1";
+
+gfxstream::expected<Lib, std::string> LoadEglLib() {
+ for (const auto* possible_name : {kEglLib, kEglLibAlt}) {
+ auto result = Lib::Load(possible_name);
+ if (result.ok()) {
+ return std::move(result.value());
+ }
+ }
+ return gfxstream::unexpected("Failed to load libEGL.");
+}
+
+} // namespace
+
+/*static*/
+gfxstream::expected<Egl, std::string> Egl::Load() {
+ Egl egl;
+ egl.mLib = GFXSTREAM_EXPECT(LoadEglLib());
+
+#define LOAD_EGL_FUNCTION_POINTER(return_type, function_name, signature) \
+ egl.function_name = reinterpret_cast<return_type(GL_APIENTRY*) signature>( \
+ egl.mLib.GetSymbol(#function_name)); \
+ if (egl.function_name == nullptr) { \
+ egl.function_name = reinterpret_cast<return_type(GL_APIENTRY*) signature>( \
+ egl.eglGetProcAddress(#function_name)); \
+ }
+
+ FOR_EACH_EGL_FUNCTION(LOAD_EGL_FUNCTION_POINTER);
+
+ GFXSTREAM_EXPECT(egl.Init());
+
+ return std::move(egl);
+}
+
+gfxstream::expected<Ok, std::string> Egl::Init() {
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (display == EGL_NO_DISPLAY) {
+ return gfxstream::unexpected("Failed to get default display");
+ }
+
+ EGLint clientVersionMajor = 0;
+ EGLint clientVersionMinor = 0;
+ if (eglInitialize(display, &clientVersionMajor, &clientVersionMinor) != EGL_TRUE) {
+ return gfxstream::unexpected("Failed to initialize display.");
+ }
+
+ const std::string vendorString = eglQueryString(display, EGL_VENDOR);
+ if (vendorString.empty()) {
+ return gfxstream::unexpected("Failed to query vendor.");
+ }
+
+ const std::string extensionsString = eglQueryString(display, EGL_EXTENSIONS);
+ if (extensionsString.empty()) {
+ return gfxstream::unexpected("Failed to query extensions.");
+ }
+
+ if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
+ return gfxstream::unexpected("Failed to bind GLES API.");
+ }
+
+ const EGLint attribs[] = {
+ // clang-format off
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_NONE,
+ // clang-format on
+ };
+
+ EGLConfig config;
+ EGLint numConfigs = 0;
+ if (eglChooseConfig(display, attribs, &config, 1, &numConfigs) != EGL_TRUE) {
+ return gfxstream::unexpected("Failed to find matching framebuffer config.");
+ }
+
+ const EGLint pbufferAttribs[] = {
+ // clang-format off
+ EGL_WIDTH, 720,
+ EGL_HEIGHT, 720,
+ EGL_NONE,
+ // clang-format on
+ };
+
+ EGLSurface primarySurface = eglCreatePbufferSurface(display, config, pbufferAttribs);
+ if (primarySurface == EGL_NO_SURFACE) {
+ return gfxstream::unexpected("Failed to create EGL surface.");
+ }
+
+ const EGLint contextAttribs[] = {
+ // clang-format off
+ EGL_CONTEXT_CLIENT_VERSION, 3,
+ EGL_NONE
+ // clang-format on
+ };
+
+ EGLContext primaryContext = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
+ if (primaryContext == EGL_NO_CONTEXT) {
+ return gfxstream::unexpected("Failed to create EGL context.");
+ }
+
+ if (eglMakeCurrent(display, primarySurface, primarySurface, primaryContext) == EGL_FALSE) {
+ return gfxstream::unexpected("Failed to make primary EGL context/surface current.");
+ }
+
+ return {};
+}
+
+} // namespace gfxstream
\ No newline at end of file