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 <stdio.h> 20#include <string.h> 21#include <errno.h> 22#include <dlfcn.h> 23#include <limits.h> 24 25#include <cutils/log.h> 26#include <cutils/properties.h> 27 28#include <EGL/egl.h> 29 30#include "egldefs.h" 31#include "glestrace.h" 32#include "hooks.h" 33#include "Loader.h" 34 35// ---------------------------------------------------------------------------- 36namespace android { 37// ---------------------------------------------------------------------------- 38 39 40/* 41 * EGL drivers are called 42 * 43 * /system/lib/egl/lib{[EGL|GLESv1_CM|GLESv2] | GLES}_$TAG.so 44 * 45 */ 46 47ANDROID_SINGLETON_STATIC_INSTANCE( Loader ) 48 49/* This function is called to check whether we run inside the emulator, 50 * and if this is the case whether GLES GPU emulation is supported. 51 * 52 * Returned values are: 53 * -1 -> not running inside the emulator 54 * 0 -> running inside the emulator, but GPU emulation not supported 55 * 1 -> running inside the emulator, GPU emulation is supported 56 * through the "emulation" config. 57 */ 58static int 59checkGlesEmulationStatus(void) 60{ 61 /* We're going to check for the following kernel parameters: 62 * 63 * qemu=1 -> tells us that we run inside the emulator 64 * android.qemu.gles=<number> -> tells us the GLES GPU emulation status 65 * 66 * Note that we will return <number> if we find it. This let us support 67 * more additionnal emulation modes in the future. 68 */ 69 char prop[PROPERTY_VALUE_MAX]; 70 int result = -1; 71 72 /* First, check for qemu=1 */ 73 property_get("ro.kernel.qemu",prop,"0"); 74 if (atoi(prop) != 1) 75 return -1; 76 77 /* We are in the emulator, get GPU status value */ 78 property_get("ro.kernel.qemu.gles",prop,"0"); 79 return atoi(prop); 80} 81 82// ---------------------------------------------------------------------------- 83 84static char const * getProcessCmdline() { 85 long pid = getpid(); 86 char procPath[128]; 87 snprintf(procPath, 128, "/proc/%ld/cmdline", pid); 88 FILE * file = fopen(procPath, "r"); 89 if (file) { 90 static char cmdline[256]; 91 char *str = fgets(cmdline, sizeof(cmdline) - 1, file); 92 fclose(file); 93 if (str) { 94 return cmdline; 95 } 96 } 97 return NULL; 98} 99 100// ---------------------------------------------------------------------------- 101 102Loader::driver_t::driver_t(void* gles) 103{ 104 dso[0] = gles; 105 for (size_t i=1 ; i<NELEM(dso) ; i++) 106 dso[i] = 0; 107} 108 109Loader::driver_t::~driver_t() 110{ 111 for (size_t i=0 ; i<NELEM(dso) ; i++) { 112 if (dso[i]) { 113 dlclose(dso[i]); 114 dso[i] = 0; 115 } 116 } 117} 118 119status_t Loader::driver_t::set(void* hnd, int32_t api) 120{ 121 switch (api) { 122 case EGL: 123 dso[0] = hnd; 124 break; 125 case GLESv1_CM: 126 dso[1] = hnd; 127 break; 128 case GLESv2: 129 dso[2] = hnd; 130 break; 131 default: 132 return BAD_INDEX; 133 } 134 return NO_ERROR; 135} 136 137// ---------------------------------------------------------------------------- 138 139Loader::Loader() 140{ 141 char line[256]; 142 char tag[256]; 143 144 /* Special case for GLES emulation */ 145 if (checkGlesEmulationStatus() == 0) { 146 ALOGD("Emulator without GPU support detected. " 147 "Fallback to software renderer."); 148 mDriverTag.setTo("android"); 149 return; 150 } 151 152 /* Otherwise, use egl.cfg */ 153 FILE* cfg = fopen("/system/lib/egl/egl.cfg", "r"); 154 if (cfg == NULL) { 155 // default config 156 ALOGD("egl.cfg not found, using default config"); 157 mDriverTag.setTo("android"); 158 } else { 159 while (fgets(line, 256, cfg)) { 160 int dpy, impl; 161 if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) { 162 //ALOGD(">>> %u %u %s", dpy, impl, tag); 163 // We only load the h/w accelerated implementation 164 if (tag != String8("android")) { 165 mDriverTag = tag; 166 } 167 } 168 } 169 fclose(cfg); 170 } 171} 172 173Loader::~Loader() 174{ 175 GLTrace_stop(); 176} 177 178void* Loader::open(egl_connection_t* cnx) 179{ 180 void* dso; 181 driver_t* hnd = 0; 182 183 char const* tag = mDriverTag.string(); 184 if (tag) { 185 dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2); 186 if (dso) { 187 hnd = new driver_t(dso); 188 } else { 189 // Always load EGL first 190 dso = load_driver("EGL", tag, cnx, EGL); 191 if (dso) { 192 hnd = new driver_t(dso); 193 // TODO: make this more automated 194 hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM ); 195 hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 ); 196 } 197 } 198 } 199 200 LOG_FATAL_IF(!index && !hnd, 201 "couldn't find the default OpenGL ES implementation " 202 "for default display"); 203 204 return (void*)hnd; 205} 206 207status_t Loader::close(void* driver) 208{ 209 driver_t* hnd = (driver_t*)driver; 210 delete hnd; 211 return NO_ERROR; 212} 213 214void Loader::init_api(void* dso, 215 char const * const * api, 216 __eglMustCastToProperFunctionPointerType* curr, 217 getProcAddressType getProcAddress) 218{ 219 const ssize_t SIZE = 256; 220 char scrap[SIZE]; 221 while (*api) { 222 char const * name = *api; 223 __eglMustCastToProperFunctionPointerType f = 224 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); 225 if (f == NULL) { 226 // couldn't find the entry-point, use eglGetProcAddress() 227 f = getProcAddress(name); 228 } 229 if (f == NULL) { 230 // Try without the OES postfix 231 ssize_t index = ssize_t(strlen(name)) - 3; 232 if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) { 233 strncpy(scrap, name, index); 234 scrap[index] = 0; 235 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap); 236 //ALOGD_IF(f, "found <%s> instead", scrap); 237 } 238 } 239 if (f == NULL) { 240 // Try with the OES postfix 241 ssize_t index = ssize_t(strlen(name)) - 3; 242 if (index>0 && strcmp(name+index, "OES")) { 243 snprintf(scrap, SIZE, "%sOES", name); 244 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap); 245 //ALOGD_IF(f, "found <%s> instead", scrap); 246 } 247 } 248 if (f == NULL) { 249 //ALOGD("%s", name); 250 f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; 251 252 /* 253 * GL_EXT_debug_label is special, we always report it as 254 * supported, it's handled by GLES_trace. If GLES_trace is not 255 * enabled, then these are no-ops. 256 */ 257 if (!strcmp(name, "glInsertEventMarkerEXT")) { 258 f = (__eglMustCastToProperFunctionPointerType)gl_noop; 259 } else if (!strcmp(name, "glPushGroupMarkerEXT")) { 260 f = (__eglMustCastToProperFunctionPointerType)gl_noop; 261 } else if (!strcmp(name, "glPopGroupMarkerEXT")) { 262 f = (__eglMustCastToProperFunctionPointerType)gl_noop; 263 } 264 } 265 *curr++ = f; 266 api++; 267 } 268} 269 270void *Loader::load_driver(const char* kind, const char *tag, 271 egl_connection_t* cnx, uint32_t mask) 272{ 273 char driver_absolute_path[PATH_MAX]; 274 const char* const search1 = "/vendor/lib/egl/lib%s_%s.so"; 275 const char* const search2 = "/system/lib/egl/lib%s_%s.so"; 276 277 snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag); 278 if (access(driver_absolute_path, R_OK)) { 279 snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag); 280 if (access(driver_absolute_path, R_OK)) { 281 // this happens often, we don't want to log an error 282 return 0; 283 } 284 } 285 286 void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); 287 if (dso == 0) { 288 const char* err = dlerror(); 289 ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown"); 290 return 0; 291 } 292 293 ALOGD("loaded %s", driver_absolute_path); 294 295 if (mask & EGL) { 296 getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); 297 298 ALOGE_IF(!getProcAddress, 299 "can't find eglGetProcAddress() in %s", driver_absolute_path); 300 301#ifdef SYSTEMUI_PBSIZE_HACK 302#warning "SYSTEMUI_PBSIZE_HACK enabled" 303 /* 304 * TODO: replace SYSTEMUI_PBSIZE_HACK by something less hackish 305 * 306 * Here we adjust the PB size from its default value to 512KB which 307 * is the minimum acceptable for the systemui process. 308 * We do this on low-end devices only because it allows us to enable 309 * h/w acceleration in the systemui process while keeping the 310 * memory usage down. 311 * 312 * Obviously, this is the wrong place and wrong way to make this 313 * adjustment, but at the time of this writing this was the safest 314 * solution. 315 */ 316 const char *cmdline = getProcessCmdline(); 317 if (strstr(cmdline, "systemui")) { 318 void *imgegl = dlopen("/vendor/lib/libIMGegl.so", RTLD_LAZY); 319 if (imgegl) { 320 unsigned int *PVRDefaultPBS = 321 (unsigned int *)dlsym(imgegl, "PVRDefaultPBS"); 322 if (PVRDefaultPBS) { 323 ALOGD("setting default PBS to 512KB, was %d KB", *PVRDefaultPBS / 1024); 324 *PVRDefaultPBS = 512*1024; 325 } 326 } 327 } 328#endif 329 330 egl_t* egl = &cnx->egl; 331 __eglMustCastToProperFunctionPointerType* curr = 332 (__eglMustCastToProperFunctionPointerType*)egl; 333 char const * const * api = egl_names; 334 while (*api) { 335 char const * name = *api; 336 __eglMustCastToProperFunctionPointerType f = 337 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); 338 if (f == NULL) { 339 // couldn't find the entry-point, use eglGetProcAddress() 340 f = getProcAddress(name); 341 if (f == NULL) { 342 f = (__eglMustCastToProperFunctionPointerType)0; 343 } 344 } 345 *curr++ = f; 346 api++; 347 } 348 } 349 350 if (mask & GLESv1_CM) { 351 init_api(dso, gl_names, 352 (__eglMustCastToProperFunctionPointerType*) 353 &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, 354 getProcAddress); 355 } 356 357 if (mask & GLESv2) { 358 init_api(dso, gl_names, 359 (__eglMustCastToProperFunctionPointerType*) 360 &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, 361 getProcAddress); 362 } 363 364 return dso; 365} 366 367// ---------------------------------------------------------------------------- 368}; // namespace android 369// ---------------------------------------------------------------------------- 370