Loader.cpp revision c07b52060acd627c8510c1a9151e0753fce76330
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 "../glestrace.h" 31 32#include "egldefs.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 178static void* load_wrapper(const char* path) { 179 void* so = dlopen(path, RTLD_NOW | RTLD_LOCAL); 180 ALOGE_IF(!so, "dlopen(\"%s\") failed: %s", path, dlerror()); 181 return so; 182} 183 184void* Loader::open(egl_connection_t* cnx) 185{ 186 void* dso; 187 driver_t* hnd = 0; 188 189 char const* tag = mDriverTag.string(); 190 if (tag) { 191 dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2); 192 if (dso) { 193 hnd = new driver_t(dso); 194 } else { 195 // Always load EGL first 196 dso = load_driver("EGL", tag, cnx, EGL); 197 if (dso) { 198 hnd = new driver_t(dso); 199 // TODO: make this more automated 200 hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM ); 201 hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 ); 202 } 203 } 204 } 205 206 LOG_FATAL_IF(!index && !hnd, 207 "couldn't find the default OpenGL ES implementation " 208 "for default display"); 209 210 cnx->libGles2 = load_wrapper("system/lib/libGLESv2.so"); 211 cnx->libGles1 = load_wrapper("system/lib/libGLESv1_CM.so"); 212 LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1, 213 "couldn't load system OpenGL ES wrapper libraries"); 214 215 return (void*)hnd; 216} 217 218status_t Loader::close(void* driver) 219{ 220 driver_t* hnd = (driver_t*)driver; 221 delete hnd; 222 return NO_ERROR; 223} 224 225void Loader::init_api(void* dso, 226 char const * const * api, 227 __eglMustCastToProperFunctionPointerType* curr, 228 getProcAddressType getProcAddress) 229{ 230 const ssize_t SIZE = 256; 231 char scrap[SIZE]; 232 while (*api) { 233 char const * name = *api; 234 __eglMustCastToProperFunctionPointerType f = 235 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); 236 if (f == NULL) { 237 // couldn't find the entry-point, use eglGetProcAddress() 238 f = getProcAddress(name); 239 } 240 if (f == NULL) { 241 // Try without the OES postfix 242 ssize_t index = ssize_t(strlen(name)) - 3; 243 if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) { 244 strncpy(scrap, name, index); 245 scrap[index] = 0; 246 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap); 247 //ALOGD_IF(f, "found <%s> instead", scrap); 248 } 249 } 250 if (f == NULL) { 251 // Try with the OES postfix 252 ssize_t index = ssize_t(strlen(name)) - 3; 253 if (index>0 && strcmp(name+index, "OES")) { 254 snprintf(scrap, SIZE, "%sOES", name); 255 f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap); 256 //ALOGD_IF(f, "found <%s> instead", scrap); 257 } 258 } 259 if (f == NULL) { 260 //ALOGD("%s", name); 261 f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; 262 263 /* 264 * GL_EXT_debug_label is special, we always report it as 265 * supported, it's handled by GLES_trace. If GLES_trace is not 266 * enabled, then these are no-ops. 267 */ 268 if (!strcmp(name, "glInsertEventMarkerEXT")) { 269 f = (__eglMustCastToProperFunctionPointerType)gl_noop; 270 } else if (!strcmp(name, "glPushGroupMarkerEXT")) { 271 f = (__eglMustCastToProperFunctionPointerType)gl_noop; 272 } else if (!strcmp(name, "glPopGroupMarkerEXT")) { 273 f = (__eglMustCastToProperFunctionPointerType)gl_noop; 274 } 275 } 276 *curr++ = f; 277 api++; 278 } 279} 280 281void *Loader::load_driver(const char* kind, const char *tag, 282 egl_connection_t* cnx, uint32_t mask) 283{ 284 char driver_absolute_path[PATH_MAX]; 285 const char* const search1 = "/vendor/lib/egl/lib%s_%s.so"; 286 const char* const search2 = "/system/lib/egl/lib%s_%s.so"; 287 288 snprintf(driver_absolute_path, PATH_MAX, search1, kind, tag); 289 if (access(driver_absolute_path, R_OK)) { 290 snprintf(driver_absolute_path, PATH_MAX, search2, kind, tag); 291 if (access(driver_absolute_path, R_OK)) { 292 // this happens often, we don't want to log an error 293 return 0; 294 } 295 } 296 297 void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); 298 if (dso == 0) { 299 const char* err = dlerror(); 300 ALOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown"); 301 return 0; 302 } 303 304 ALOGD("loaded %s", driver_absolute_path); 305 306 if (mask & EGL) { 307 getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); 308 309 ALOGE_IF(!getProcAddress, 310 "can't find eglGetProcAddress() in %s", driver_absolute_path); 311 312#ifdef SYSTEMUI_PBSIZE_HACK 313#warning "SYSTEMUI_PBSIZE_HACK enabled" 314 /* 315 * TODO: replace SYSTEMUI_PBSIZE_HACK by something less hackish 316 * 317 * Here we adjust the PB size from its default value to 512KB which 318 * is the minimum acceptable for the systemui process. 319 * We do this on low-end devices only because it allows us to enable 320 * h/w acceleration in the systemui process while keeping the 321 * memory usage down. 322 * 323 * Obviously, this is the wrong place and wrong way to make this 324 * adjustment, but at the time of this writing this was the safest 325 * solution. 326 */ 327 const char *cmdline = getProcessCmdline(); 328 if (strstr(cmdline, "systemui")) { 329 void *imgegl = dlopen("/vendor/lib/libIMGegl.so", RTLD_LAZY); 330 if (imgegl) { 331 unsigned int *PVRDefaultPBS = 332 (unsigned int *)dlsym(imgegl, "PVRDefaultPBS"); 333 if (PVRDefaultPBS) { 334 ALOGD("setting default PBS to 512KB, was %d KB", *PVRDefaultPBS / 1024); 335 *PVRDefaultPBS = 512*1024; 336 } 337 } 338 } 339#endif 340 341 egl_t* egl = &cnx->egl; 342 __eglMustCastToProperFunctionPointerType* curr = 343 (__eglMustCastToProperFunctionPointerType*)egl; 344 char const * const * api = egl_names; 345 while (*api) { 346 char const * name = *api; 347 __eglMustCastToProperFunctionPointerType f = 348 (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); 349 if (f == NULL) { 350 // couldn't find the entry-point, use eglGetProcAddress() 351 f = getProcAddress(name); 352 if (f == NULL) { 353 f = (__eglMustCastToProperFunctionPointerType)0; 354 } 355 } 356 *curr++ = f; 357 api++; 358 } 359 } 360 361 if (mask & GLESv1_CM) { 362 init_api(dso, gl_names, 363 (__eglMustCastToProperFunctionPointerType*) 364 &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, 365 getProcAddress); 366 } 367 368 if (mask & GLESv2) { 369 init_api(dso, gl_names, 370 (__eglMustCastToProperFunctionPointerType*) 371 &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, 372 getProcAddress); 373 } 374 375 return dso; 376} 377 378// ---------------------------------------------------------------------------- 379}; // namespace android 380// ---------------------------------------------------------------------------- 381