Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 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 | |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 18 | #ifndef ANDROID_UI_EGLUTILS_H |
| 19 | #define ANDROID_UI_EGLUTILS_H |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 20 | |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 21 | #include <stdint.h> |
| 22 | #include <stdlib.h> |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 23 | #include <vector> |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 24 | |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 25 | #include <EGL/egl.h> |
| 26 | #include <EGL/eglext.h> |
| 27 | #include <GLES2/gl2.h> |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 28 | #include <system/window.h> |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 29 | #include <utils/Errors.h> |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 30 | #include <utils/String8.h> |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 31 | |
Jiyong Park | 00b15b8 | 2017-08-10 20:30:56 +0900 | [diff] [blame] | 32 | extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 33 | |
| 34 | // ---------------------------------------------------------------------------- |
| 35 | namespace android { |
| 36 | // ---------------------------------------------------------------------------- |
| 37 | |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 38 | class EGLUtils |
| 39 | { |
| 40 | public: |
| 41 | |
| 42 | static inline const char *strerror(EGLint err); |
| 43 | |
| 44 | static inline status_t selectConfigForPixelFormat( |
| 45 | EGLDisplay dpy, |
| 46 | EGLint const* attrs, |
| 47 | int32_t format, |
| 48 | EGLConfig* outConfig); |
| 49 | |
| 50 | static inline status_t selectConfigForNativeWindow( |
| 51 | EGLDisplay dpy, |
| 52 | EGLint const* attrs, |
| 53 | EGLNativeWindowType window, |
| 54 | EGLConfig* outConfig); |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 55 | |
| 56 | static inline String8 printGLString(const char* name, GLenum s); |
| 57 | static inline String8 printEGLString(EGLDisplay dpy, const char* name, GLenum s); |
| 58 | static inline String8 checkEglError(const char* op, EGLBoolean returnVal); |
| 59 | static inline String8 checkGlError(const char* op); |
| 60 | static inline String8 printEGLConfiguration(EGLDisplay dpy, EGLConfig config); |
| 61 | static inline bool printEGLConfigurations(EGLDisplay dpy, String8& msg); |
| 62 | static inline bool printEGLConfigurations(FILE* output, EGLDisplay dpy); |
| 63 | static inline String8 decodeColorSpace(EGLint colorSpace); |
| 64 | static inline bool hasEglExtension(EGLDisplay dpy, const char* name); |
| 65 | static inline bool hasExtension(const char* exts, const char* name); |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 66 | }; |
| 67 | |
| 68 | // ---------------------------------------------------------------------------- |
| 69 | |
Mathias Agopian | 8c12c7a | 2009-08-07 16:37:21 -0700 | [diff] [blame] | 70 | const char *EGLUtils::strerror(EGLint err) |
| 71 | { |
| 72 | switch (err){ |
| 73 | case EGL_SUCCESS: return "EGL_SUCCESS"; |
| 74 | case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; |
| 75 | case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; |
| 76 | case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; |
| 77 | case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; |
| 78 | case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; |
| 79 | case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; |
| 80 | case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; |
| 81 | case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; |
| 82 | case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; |
| 83 | case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; |
| 84 | case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; |
| 85 | case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; |
| 86 | case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; |
| 87 | case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; |
| 88 | default: return "UNKNOWN"; |
| 89 | } |
| 90 | } |
| 91 | |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 92 | status_t EGLUtils::selectConfigForPixelFormat( |
| 93 | EGLDisplay dpy, |
| 94 | EGLint const* attrs, |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 95 | int32_t format, |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 96 | EGLConfig* outConfig) |
| 97 | { |
| 98 | EGLint numConfigs = -1, n=0; |
| 99 | |
Mathias Agopian | 42db9dc | 2009-08-06 20:46:44 -0700 | [diff] [blame] | 100 | if (!attrs) |
| 101 | return BAD_VALUE; |
| 102 | |
Yi Kong | 48a6cd2 | 2018-07-18 10:07:09 -0700 | [diff] [blame] | 103 | if (outConfig == nullptr) |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 104 | return BAD_VALUE; |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 105 | |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 106 | // Get all the "potential match" configs... |
Yi Kong | 48a6cd2 | 2018-07-18 10:07:09 -0700 | [diff] [blame] | 107 | if (eglGetConfigs(dpy, nullptr, 0, &numConfigs) == EGL_FALSE) |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 108 | return BAD_VALUE; |
| 109 | |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 110 | std::vector<EGLConfig> configs(numConfigs); |
| 111 | if (eglChooseConfig(dpy, attrs, configs.data(), numConfigs, &n) == EGL_FALSE) { |
Mathias Agopian | f9606df | 2011-08-15 15:15:40 -0700 | [diff] [blame] | 112 | return BAD_VALUE; |
| 113 | } |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 114 | |
Mathias Agopian | f9606df | 2011-08-15 15:15:40 -0700 | [diff] [blame] | 115 | int i; |
Yi Kong | 48a6cd2 | 2018-07-18 10:07:09 -0700 | [diff] [blame] | 116 | EGLConfig config = nullptr; |
Mathias Agopian | f9606df | 2011-08-15 15:15:40 -0700 | [diff] [blame] | 117 | for (i=0 ; i<n ; i++) { |
| 118 | EGLint nativeVisualId = 0; |
| 119 | eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId); |
| 120 | if (nativeVisualId>0 && format == nativeVisualId) { |
Mathias Agopian | d5ea3db | 2011-01-16 17:57:20 -0800 | [diff] [blame] | 121 | config = configs[i]; |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 122 | break; |
| 123 | } |
Mathias Agopian | f9606df | 2011-08-15 15:15:40 -0700 | [diff] [blame] | 124 | } |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 125 | |
Mathias Agopian | f9606df | 2011-08-15 15:15:40 -0700 | [diff] [blame] | 126 | if (i<n) { |
| 127 | *outConfig = config; |
| 128 | return NO_ERROR; |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | return NAME_NOT_FOUND; |
| 132 | } |
| 133 | |
| 134 | status_t EGLUtils::selectConfigForNativeWindow( |
| 135 | EGLDisplay dpy, |
| 136 | EGLint const* attrs, |
| 137 | EGLNativeWindowType window, |
| 138 | EGLConfig* outConfig) |
| 139 | { |
| 140 | int err; |
| 141 | int format; |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 142 | |
Mathias Agopian | 42db9dc | 2009-08-06 20:46:44 -0700 | [diff] [blame] | 143 | if (!window) |
| 144 | return BAD_VALUE; |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 145 | |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 146 | if ((err = window->query(window, NATIVE_WINDOW_FORMAT, &format)) < 0) { |
| 147 | return err; |
| 148 | } |
| 149 | |
| 150 | return selectConfigForPixelFormat(dpy, attrs, format, outConfig); |
| 151 | } |
| 152 | |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 153 | String8 EGLUtils::printGLString(const char* name, GLenum s) { |
| 154 | String8 msg; |
| 155 | const char* v = reinterpret_cast<const char*>(glGetString(s)); |
| 156 | msg.appendFormat("GL %s = %s\n", name, v); |
| 157 | return msg; |
| 158 | } |
| 159 | |
| 160 | String8 EGLUtils::printEGLString(EGLDisplay dpy, const char* name, GLenum s) { |
| 161 | String8 msg; |
| 162 | const char* v = static_cast<const char*>(eglQueryString(dpy, s)); |
| 163 | msg.appendFormat("GL %s = %s\n", name, v); |
| 164 | const char* va = (const char*)eglQueryStringImplementationANDROID(dpy, s); |
| 165 | msg.appendFormat("ImplementationANDROID: %s = %s\n", name, va); |
| 166 | return msg; |
| 167 | } |
| 168 | |
| 169 | String8 EGLUtils::checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { |
| 170 | String8 msg; |
| 171 | if (returnVal != EGL_TRUE) { |
| 172 | msg.appendFormat("%s() returned %d\n", op, returnVal); |
| 173 | } |
| 174 | |
| 175 | for (EGLint error = eglGetError(); error != EGL_SUCCESS; error = eglGetError()) { |
| 176 | msg.appendFormat("after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error), error); |
| 177 | } |
| 178 | return msg; |
| 179 | } |
| 180 | |
| 181 | String8 EGLUtils::checkGlError(const char* op) { |
| 182 | String8 msg; |
| 183 | for (GLint error = glGetError(); error != GL_NO_ERROR; error = glGetError()) { |
| 184 | msg.appendFormat("after %s() glError (0x%x)\n", op, error); |
| 185 | } |
| 186 | return msg; |
| 187 | } |
| 188 | |
| 189 | String8 EGLUtils::printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { |
| 190 | #define X(VAL) \ |
| 191 | { VAL, #VAL } |
| 192 | struct { |
| 193 | EGLint attribute; |
| 194 | const char* name; |
| 195 | } names[] = { |
| 196 | X(EGL_BUFFER_SIZE), |
| 197 | X(EGL_ALPHA_SIZE), |
| 198 | X(EGL_BLUE_SIZE), |
| 199 | X(EGL_GREEN_SIZE), |
| 200 | X(EGL_RED_SIZE), |
| 201 | X(EGL_DEPTH_SIZE), |
| 202 | X(EGL_STENCIL_SIZE), |
| 203 | X(EGL_CONFIG_CAVEAT), |
| 204 | X(EGL_CONFIG_ID), |
| 205 | X(EGL_LEVEL), |
| 206 | X(EGL_MAX_PBUFFER_HEIGHT), |
| 207 | X(EGL_MAX_PBUFFER_PIXELS), |
| 208 | X(EGL_MAX_PBUFFER_WIDTH), |
| 209 | X(EGL_NATIVE_RENDERABLE), |
| 210 | X(EGL_NATIVE_VISUAL_ID), |
| 211 | X(EGL_NATIVE_VISUAL_TYPE), |
| 212 | X(EGL_SAMPLES), |
| 213 | X(EGL_SAMPLE_BUFFERS), |
| 214 | X(EGL_SURFACE_TYPE), |
| 215 | X(EGL_TRANSPARENT_TYPE), |
| 216 | X(EGL_TRANSPARENT_RED_VALUE), |
| 217 | X(EGL_TRANSPARENT_GREEN_VALUE), |
| 218 | X(EGL_TRANSPARENT_BLUE_VALUE), |
| 219 | X(EGL_BIND_TO_TEXTURE_RGB), |
| 220 | X(EGL_BIND_TO_TEXTURE_RGBA), |
| 221 | X(EGL_MIN_SWAP_INTERVAL), |
| 222 | X(EGL_MAX_SWAP_INTERVAL), |
| 223 | X(EGL_LUMINANCE_SIZE), |
| 224 | X(EGL_ALPHA_MASK_SIZE), |
| 225 | X(EGL_COLOR_BUFFER_TYPE), |
| 226 | X(EGL_RENDERABLE_TYPE), |
| 227 | X(EGL_CONFORMANT), |
| 228 | }; |
| 229 | #undef X |
| 230 | |
| 231 | String8 msg; |
| 232 | for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { |
| 233 | EGLint value = -1; |
| 234 | EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); |
| 235 | EGLint error = eglGetError(); |
| 236 | if (returnVal && error == EGL_SUCCESS) { |
| 237 | msg.appendFormat(" %s: %d (0x%x)", names[j].name, value, value); |
| 238 | } |
| 239 | } |
| 240 | msg.append("\n"); |
| 241 | return msg; |
| 242 | } |
| 243 | |
| 244 | bool EGLUtils::printEGLConfigurations(EGLDisplay dpy, String8& msg) { |
| 245 | EGLint numConfig = 0; |
Yi Kong | 48a6cd2 | 2018-07-18 10:07:09 -0700 | [diff] [blame] | 246 | EGLint returnVal = eglGetConfigs(dpy, nullptr, 0, &numConfig); |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 247 | msg.append(checkEglError("eglGetConfigs", returnVal)); |
| 248 | if (!returnVal) { |
| 249 | return false; |
| 250 | } |
| 251 | |
| 252 | msg.appendFormat("Number of EGL configuration: %d\n", numConfig); |
| 253 | |
| 254 | std::vector<EGLConfig> configs(numConfig); |
| 255 | |
| 256 | returnVal = eglGetConfigs(dpy, configs.data(), numConfig, &numConfig); |
| 257 | msg.append(checkEglError("eglGetConfigs", returnVal)); |
| 258 | if (!returnVal) { |
| 259 | return false; |
| 260 | } |
| 261 | |
| 262 | for (int i = 0; i < numConfig; i++) { |
| 263 | msg.appendFormat("Configuration %d\n", i); |
| 264 | msg.append(printEGLConfiguration(dpy, configs[i])); |
| 265 | } |
| 266 | |
| 267 | return true; |
| 268 | } |
| 269 | |
| 270 | bool EGLUtils::printEGLConfigurations(FILE* output, EGLDisplay dpy) { |
| 271 | String8 msg; |
| 272 | bool status = printEGLConfigurations(dpy, msg); |
| 273 | fprintf(output, "%s", msg.c_str()); |
| 274 | return status; |
| 275 | } |
| 276 | |
| 277 | String8 EGLUtils::decodeColorSpace(EGLint colorSpace) { |
| 278 | switch (colorSpace) { |
| 279 | case EGL_GL_COLORSPACE_SRGB_KHR: |
| 280 | return String8("EGL_GL_COLORSPACE_SRGB_KHR"); |
| 281 | case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: |
| 282 | return String8("EGL_GL_COLORSPACE_DISPLAY_P3_EXT"); |
Peiyong Lin | e0ff377 | 2018-12-08 22:23:20 -0800 | [diff] [blame] | 283 | case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: |
| 284 | return String8("EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"); |
Courtney Goeltzenleuchter | df0518c | 2017-07-27 08:11:24 -0600 | [diff] [blame] | 285 | case EGL_GL_COLORSPACE_LINEAR_KHR: |
| 286 | return String8("EGL_GL_COLORSPACE_LINEAR_KHR"); |
| 287 | default: |
| 288 | return String8::format("UNKNOWN ColorSpace %d", colorSpace); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | bool EGLUtils::hasExtension(const char* exts, const char* name) { |
| 293 | size_t nameLen = strlen(name); |
| 294 | if (exts) { |
| 295 | for (const char* match = strstr(exts, name); match; match = strstr(match + nameLen, name)) { |
| 296 | if (match[nameLen] == '\0' || match[nameLen] == ' ') { |
| 297 | return true; |
| 298 | } |
| 299 | } |
| 300 | } |
| 301 | return false; |
| 302 | } |
| 303 | |
| 304 | bool EGLUtils::hasEglExtension(EGLDisplay dpy, const char* name) { |
| 305 | return hasExtension(eglQueryString(dpy, EGL_EXTENSIONS), name); |
| 306 | } |
| 307 | |
Mathias Agopian | 6cf50a7 | 2009-08-06 16:05:39 -0700 | [diff] [blame] | 308 | // ---------------------------------------------------------------------------- |
| 309 | }; // namespace android |
| 310 | // ---------------------------------------------------------------------------- |
Mathias Agopian | 870b8aa | 2012-02-24 16:42:46 -0800 | [diff] [blame] | 311 | |
| 312 | #endif /* ANDROID_UI_EGLUTILS_H */ |