1 /* 2 ** Copyright 2007, 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 <ctype.h> 18 #include <stdlib.h> 19 #include <string.h> 20 21 #include <hardware/gralloc.h> 22 #include <system/window.h> 23 24 #include <EGL/egl.h> 25 #include <EGL/eglext.h> 26 27 #include <cutils/log.h> 28 #include <cutils/atomic.h> 29 #include <cutils/properties.h> 30 31 #include <utils/CallStack.h> 32 #include <utils/String8.h> 33 34 #include "../egl_impl.h" 35 #include "../glestrace.h" 36 37 #include "egl_tls.h" 38 #include "egldefs.h" 39 #include "Loader.h" 40 41 #include "egl_display.h" 42 #include "egl_object.h" 43 44 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; 45 46 // ---------------------------------------------------------------------------- 47 namespace android { 48 // ---------------------------------------------------------------------------- 49 50 egl_connection_t gEGLImpl; 51 gl_hooks_t gHooks[2]; 52 gl_hooks_t gHooksNoContext; 53 pthread_key_t gGLWrapperKey = -1; 54 55 // ---------------------------------------------------------------------------- 56 57 #if EGL_TRACE 58 59 EGLAPI pthread_key_t gGLTraceKey = -1; 60 61 // ---------------------------------------------------------------------------- 62 63 /** 64 * There are three different tracing methods: 65 * 1. libs/EGL/trace.cpp: Traces all functions to systrace. 66 * To enable: 67 * - set system property "debug.egl.trace" to "systrace" to trace all apps. 68 * 2. libs/EGL/trace.cpp: Logs a stack trace for GL errors after each function call. 69 * To enable: 70 * - set system property "debug.egl.trace" to "error" to trace all apps. 71 * 3. libs/EGL/trace.cpp: Traces all functions to logcat. 72 * To enable: 73 * - set system property "debug.egl.trace" to 1 to trace all apps. 74 * - or call setGLTraceLevel(1) from an app to enable tracing for that app. 75 * 4. libs/GLES_trace: Traces all functions via protobuf to host. 76 * To enable: 77 * - set system property "debug.egl.debug_proc" to the application name. 78 * - or call setGLDebugLevel(1) from the app. 79 */ 80 static int sEGLTraceLevel; 81 static int sEGLApplicationTraceLevel; 82 83 static bool sEGLSystraceEnabled; 84 static bool sEGLGetErrorEnabled; 85 86 static volatile int sEGLDebugLevel; 87 88 extern gl_hooks_t gHooksTrace; 89 extern gl_hooks_t gHooksSystrace; 90 extern gl_hooks_t gHooksErrorTrace; 91 92 int getEGLDebugLevel() { 93 return sEGLDebugLevel; 94 } 95 96 void setEGLDebugLevel(int level) { 97 sEGLDebugLevel = level; 98 } 99 100 static inline void setGlTraceThreadSpecific(gl_hooks_t const *value) { 101 pthread_setspecific(gGLTraceKey, value); 102 } 103 104 gl_hooks_t const* getGLTraceThreadSpecific() { 105 return static_cast<gl_hooks_t*>(pthread_getspecific(gGLTraceKey)); 106 } 107 108 void initEglTraceLevel() { 109 char value[PROPERTY_VALUE_MAX]; 110 property_get("debug.egl.trace", value, "0"); 111 112 sEGLGetErrorEnabled = !strcasecmp(value, "error"); 113 if (sEGLGetErrorEnabled) { 114 sEGLSystraceEnabled = false; 115 sEGLTraceLevel = 0; 116 return; 117 } 118 119 sEGLSystraceEnabled = !strcasecmp(value, "systrace"); 120 if (sEGLSystraceEnabled) { 121 sEGLTraceLevel = 0; 122 return; 123 } 124 125 int propertyLevel = atoi(value); 126 int applicationLevel = sEGLApplicationTraceLevel; 127 sEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel; 128 } 129 130 void initEglDebugLevel() { 131 if (getEGLDebugLevel() == 0) { 132 char value[PROPERTY_VALUE_MAX]; 133 134 // check system property only on userdebug or eng builds 135 property_get("ro.debuggable", value, "0"); 136 if (value[0] == '0') 137 return; 138 139 property_get("debug.egl.debug_proc", value, ""); 140 if (strlen(value) > 0) { 141 FILE * file = fopen("/proc/self/cmdline", "r"); 142 if (file) { 143 char cmdline[256]; 144 if (fgets(cmdline, sizeof(cmdline), file)) { 145 if (!strncmp(value, cmdline, strlen(value))) { 146 // set EGL debug if the "debug.egl.debug_proc" property 147 // matches the prefix of this application's command line 148 setEGLDebugLevel(1); 149 } 150 } 151 fclose(file); 152 } 153 } 154 } 155 156 if (getEGLDebugLevel() > 0) { 157 if (GLTrace_start() < 0) { 158 ALOGE("Error starting Tracer for OpenGL ES. Disabling.."); 159 setEGLDebugLevel(0); 160 } 161 } 162 } 163 164 void setGLHooksThreadSpecific(gl_hooks_t const *value) { 165 if (sEGLGetErrorEnabled) { 166 setGlTraceThreadSpecific(value); 167 setGlThreadSpecific(&gHooksErrorTrace); 168 } else if (sEGLSystraceEnabled) { 169 setGlTraceThreadSpecific(value); 170 setGlThreadSpecific(&gHooksSystrace); 171 } else if (sEGLTraceLevel > 0) { 172 setGlTraceThreadSpecific(value); 173 setGlThreadSpecific(&gHooksTrace); 174 } else if (getEGLDebugLevel() > 0 && value != &gHooksNoContext) { 175 setGlTraceThreadSpecific(value); 176 setGlThreadSpecific(GLTrace_getGLHooks()); 177 } else { 178 setGlTraceThreadSpecific(NULL); 179 setGlThreadSpecific(value); 180 } 181 } 182 183 /* 184 * Global entry point to allow applications to modify their own trace level. 185 * The effective trace level is the max of this level and the value of debug.egl.trace. 186 */ 187 extern "C" 188 void setGLTraceLevel(int level) { 189 sEGLApplicationTraceLevel = level; 190 } 191 192 /* 193 * Global entry point to allow applications to modify their own debug level. 194 * Debugging is enabled if either the application requested it, or if the system property 195 * matches the application's name. 196 * Note that this only sets the debug level. The value is read and used either in 197 * initEglDebugLevel() if the application hasn't initialized its display yet, or when 198 * eglSwapBuffers() is called next. 199 */ 200 void EGLAPI setGLDebugLevel(int level) { 201 setEGLDebugLevel(level); 202 } 203 204 #else 205 206 void setGLHooksThreadSpecific(gl_hooks_t const *value) { 207 setGlThreadSpecific(value); 208 } 209 210 #endif 211 212 /*****************************************************************************/ 213 214 static int gl_no_context() { 215 if (egl_tls_t::logNoContextCall()) { 216 char const* const error = "call to OpenGL ES API with " 217 "no current context (logged once per thread)"; 218 if (LOG_NDEBUG) { 219 ALOGE(error); 220 } else { 221 LOG_ALWAYS_FATAL(error); 222 } 223 char value[PROPERTY_VALUE_MAX]; 224 property_get("debug.egl.callstack", value, "0"); 225 if (atoi(value)) { 226 CallStack stack(LOG_TAG); 227 } 228 } 229 return 0; 230 } 231 232 static void early_egl_init(void) 233 { 234 #if EGL_TRACE 235 pthread_key_create(&gGLTraceKey, NULL); 236 initEglTraceLevel(); 237 #endif 238 int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer); 239 EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext); 240 for (int hook = 0; hook < numHooks; ++hook) { 241 *(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context); 242 } 243 244 setGLHooksThreadSpecific(&gHooksNoContext); 245 } 246 247 static pthread_once_t once_control = PTHREAD_ONCE_INIT; 248 static int sEarlyInitState = pthread_once(&once_control, &early_egl_init); 249 250 // ---------------------------------------------------------------------------- 251 252 egl_display_ptr validate_display(EGLDisplay dpy) { 253 egl_display_ptr dp = get_display(dpy); 254 if (!dp) 255 return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL)); 256 if (!dp->isReady()) 257 return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL)); 258 259 return dp; 260 } 261 262 egl_display_ptr validate_display_connection(EGLDisplay dpy, 263 egl_connection_t*& cnx) { 264 cnx = NULL; 265 egl_display_ptr dp = validate_display(dpy); 266 if (!dp) 267 return dp; 268 cnx = &gEGLImpl; 269 if (cnx->dso == 0) { 270 return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL)); 271 } 272 return dp; 273 } 274 275 // ---------------------------------------------------------------------------- 276 277 const GLubyte * egl_get_string_for_current_context(GLenum name) { 278 // NOTE: returning NULL here will fall-back to the default 279 // implementation. 280 281 EGLContext context = egl_tls_t::getContext(); 282 if (context == EGL_NO_CONTEXT) 283 return NULL; 284 285 egl_context_t const * const c = get_context(context); 286 if (c == NULL) // this should never happen, by construction 287 return NULL; 288 289 if (name != GL_EXTENSIONS) 290 return NULL; 291 292 return (const GLubyte *)c->gl_extensions.string(); 293 } 294 295 // ---------------------------------------------------------------------------- 296 297 // this mutex protects: 298 // d->disp[] 299 // egl_init_drivers_locked() 300 // 301 static EGLBoolean egl_init_drivers_locked() { 302 if (sEarlyInitState) { 303 // initialized by static ctor. should be set here. 304 return EGL_FALSE; 305 } 306 307 // get our driver loader 308 Loader& loader(Loader::getInstance()); 309 310 // dynamically load our EGL implementation 311 egl_connection_t* cnx = &gEGLImpl; 312 if (cnx->dso == 0) { 313 cnx->hooks[egl_connection_t::GLESv1_INDEX] = 314 &gHooks[egl_connection_t::GLESv1_INDEX]; 315 cnx->hooks[egl_connection_t::GLESv2_INDEX] = 316 &gHooks[egl_connection_t::GLESv2_INDEX]; 317 cnx->dso = loader.open(cnx); 318 } 319 320 return cnx->dso ? EGL_TRUE : EGL_FALSE; 321 } 322 323 static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER; 324 325 EGLBoolean egl_init_drivers() { 326 EGLBoolean res; 327 pthread_mutex_lock(&sInitDriverMutex); 328 res = egl_init_drivers_locked(); 329 pthread_mutex_unlock(&sInitDriverMutex); 330 return res; 331 } 332 333 static pthread_mutex_t sLogPrintMutex = PTHREAD_MUTEX_INITIALIZER; 334 static nsecs_t sLogPrintTime = 0; 335 #define NSECS_DURATION 1000000000 336 337 void gl_unimplemented() { 338 bool printLog = false; 339 nsecs_t now = systemTime(); 340 pthread_mutex_lock(&sLogPrintMutex); 341 if ((now - sLogPrintTime) > NSECS_DURATION) { 342 sLogPrintTime = now; 343 printLog = true; 344 } 345 pthread_mutex_unlock(&sLogPrintMutex); 346 if (printLog) { 347 ALOGE("called unimplemented OpenGL ES API"); 348 char value[PROPERTY_VALUE_MAX]; 349 property_get("debug.egl.callstack", value, "0"); 350 if (atoi(value)) { 351 CallStack stack(LOG_TAG); 352 } 353 } 354 } 355 356 void gl_noop() { 357 } 358 359 // ---------------------------------------------------------------------------- 360 361 void setGlThreadSpecific(gl_hooks_t const *value) { 362 gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); 363 tls_hooks[TLS_SLOT_OPENGL_API] = value; 364 } 365 366 // ---------------------------------------------------------------------------- 367 // GL / EGL hooks 368 // ---------------------------------------------------------------------------- 369 370 #undef GL_ENTRY 371 #undef EGL_ENTRY 372 #define GL_ENTRY(_r, _api, ...) #_api, 373 #define EGL_ENTRY(_r, _api, ...) #_api, 374 375 char const * const gl_names[] = { 376 #include "../entries.in" 377 NULL 378 }; 379 380 char const * const egl_names[] = { 381 #include "egl_entries.in" 382 NULL 383 }; 384 385 #undef GL_ENTRY 386 #undef EGL_ENTRY 387 388 389 // ---------------------------------------------------------------------------- 390 }; // namespace android 391 // ---------------------------------------------------------------------------- 392 393