1 /*
2 * Copyright (C) 2011 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 #include "eglDisplay.h"
17 #include "HostConnection.h"
18 #include "KeyedVectorUtils.h"
19
20 #ifdef HOST_BUILD
21 #include "android/base/files/PathUtils.cpp"
22 #include "android/base/system/System.cpp"
23 #endif
24
25 #include <string>
26
27 #include <dlfcn.h>
28
29 static const int systemEGLVersionMajor = 1;
30 static const int systemEGLVersionMinor = 4;
31 static const char systemEGLVendor[] = "Google Android emulator";
32
33 // list of extensions supported by this EGL implementation
34 // NOTE that each extension name should be suffixed with space
35 static const char systemStaticEGLExtensions[] =
36 "EGL_ANDROID_image_native_buffer "
37 "EGL_KHR_fence_sync "
38 "EGL_KHR_image_base "
39 "EGL_KHR_gl_texture_2d_image ";
40
41 // extensions to add dynamically depending on host-side support
42 static const char kDynamicEGLExtNativeSync[] = "EGL_ANDROID_native_fence_sync ";
43 static const char kDynamicEGLExtWaitSync[] = "EGL_KHR_wait_sync ";
44
45 static void *s_gles_lib = NULL;
46 static void *s_gles2_lib = NULL;
47
48 // The following function will be called when we (libEGL)
49 // gets unloaded
50 // At this point we want to unload the gles libraries we
51 // might have loaded during initialization
do_on_unload(void)52 static void __attribute__ ((destructor)) do_on_unload(void)
53 {
54 if (s_gles_lib) {
55 dlclose(s_gles_lib);
56 }
57
58 if (s_gles2_lib) {
59 dlclose(s_gles2_lib);
60 }
61 }
62
eglDisplay()63 eglDisplay::eglDisplay() :
64 m_initialized(false),
65 m_major(0),
66 m_minor(0),
67 m_hostRendererVersion(0),
68 m_numConfigs(0),
69 m_numConfigAttribs(0),
70 m_attribs(),
71 m_configs(NULL),
72 m_gles_iface(NULL),
73 m_gles2_iface(NULL),
74 m_versionString(NULL),
75 m_vendorString(NULL),
76 m_extensionString(NULL)
77 {
78 pthread_mutex_init(&m_lock, NULL);
79 pthread_mutex_init(&m_ctxLock, NULL);
80 pthread_mutex_init(&m_surfaceLock, NULL);
81 }
82
~eglDisplay()83 eglDisplay::~eglDisplay()
84 {
85 terminate();
86 pthread_mutex_destroy(&m_lock);
87 pthread_mutex_destroy(&m_ctxLock);
88 pthread_mutex_destroy(&m_surfaceLock);
89 }
90
91
92
initialize(EGLClient_eglInterface * eglIface)93 bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
94 {
95 pthread_mutex_lock(&m_lock);
96 if (!m_initialized) {
97
98 //
99 // load GLES client API
100 //
101 m_gles_iface = loadGLESClientAPI("libGLESv1_CM_emulation",
102 eglIface,
103 &s_gles_lib);
104 if (!m_gles_iface) {
105 pthread_mutex_unlock(&m_lock);
106 ALOGE("Failed to load gles1 iface");
107 return false;
108 }
109
110 #ifdef WITH_GLES2
111 m_gles2_iface = loadGLESClientAPI("libGLESv2_emulation",
112 eglIface,
113 &s_gles2_lib);
114 // Note that if loading gles2 failed, we can still run with no
115 // GLES2 support, having GLES2 is not mandatory.
116 #endif
117
118 //
119 // establish connection with the host
120 //
121 HostConnection *hcon = HostConnection::get();
122 if (!hcon) {
123 pthread_mutex_unlock(&m_lock);
124 ALOGE("Failed to establish connection with the host\n");
125 return false;
126 }
127 hcon->setGrallocOnly(false);
128
129 //
130 // get renderControl encoder instance
131 //
132 renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
133 if (!rcEnc) {
134 pthread_mutex_unlock(&m_lock);
135 ALOGE("Failed to get renderControl encoder instance");
136 return false;
137 }
138
139 //
140 // Query host reneder and EGL version
141 //
142 m_hostRendererVersion = rcEnc->rcGetRendererVersion(rcEnc);
143 EGLint status = rcEnc->rcGetEGLVersion(rcEnc, &m_major, &m_minor);
144 if (status != EGL_TRUE) {
145 // host EGL initialization failed !!
146 pthread_mutex_unlock(&m_lock);
147 return false;
148 }
149
150 //
151 // Take minimum version beween what we support and what the host support
152 //
153 if (m_major > systemEGLVersionMajor) {
154 m_major = systemEGLVersionMajor;
155 m_minor = systemEGLVersionMinor;
156 }
157 else if (m_major == systemEGLVersionMajor &&
158 m_minor > systemEGLVersionMinor) {
159 m_minor = systemEGLVersionMinor;
160 }
161
162 //
163 // Query the host for the set of configs
164 //
165 m_numConfigs = rcEnc->rcGetNumConfigs(rcEnc, (uint32_t*)&m_numConfigAttribs);
166 if (m_numConfigs <= 0 || m_numConfigAttribs <= 0) {
167 // just sanity check - should never happen
168 pthread_mutex_unlock(&m_lock);
169 return false;
170 }
171
172 uint32_t nInts = m_numConfigAttribs * (m_numConfigs + 1);
173 EGLint tmp_buf[nInts];
174 m_configs = new EGLint[nInts-m_numConfigAttribs];
175 if (!m_configs) {
176 pthread_mutex_unlock(&m_lock);
177 return false;
178 }
179
180 EGLint n = rcEnc->rcGetConfigs(rcEnc, nInts*sizeof(EGLint), (GLuint*)tmp_buf);
181 if (n != m_numConfigs) {
182 pthread_mutex_unlock(&m_lock);
183 return false;
184 }
185
186 // Fill the attributes vector.
187 // The first m_numConfigAttribs values of tmp_buf are the actual attributes enums.
188 for (int i=0; i<m_numConfigAttribs; i++) {
189 m_attribs[tmp_buf[i]] = i;
190 }
191
192 memcpy(m_configs, tmp_buf + m_numConfigAttribs,
193 m_numConfigs*m_numConfigAttribs*sizeof(EGLint));
194
195 m_initialized = true;
196 }
197 pthread_mutex_unlock(&m_lock);
198
199 processConfigs();
200
201 return true;
202 }
203
processConfigs()204 void eglDisplay::processConfigs()
205 {
206 for (intptr_t i=0; i<m_numConfigs; i++) {
207 EGLConfig config = (EGLConfig)i;
208 PixelFormat format;
209 if (getConfigNativePixelFormat(config, &format)) {
210 setConfigAttrib(config, EGL_NATIVE_VISUAL_ID, format);
211 }
212 }
213 }
214
terminate()215 void eglDisplay::terminate()
216 {
217 pthread_mutex_lock(&m_lock);
218 if (m_initialized) {
219 // Cannot use the for loop in the following code because
220 // eglDestroyContext may erase elements.
221 EGLContextSet::iterator ctxIte = m_contexts.begin();
222 while (ctxIte != m_contexts.end()) {
223 EGLContextSet::iterator ctxToDelete = ctxIte;
224 ctxIte ++;
225 eglDestroyContext(static_cast<EGLDisplay>(this), *ctxToDelete);
226 }
227 EGLSurfaceSet::iterator surfaceIte = m_surfaces.begin();
228 while (surfaceIte != m_surfaces.end()) {
229 EGLSurfaceSet::iterator surfaceToDelete = surfaceIte;
230 surfaceIte ++;
231 eglDestroySurface(static_cast<EGLDisplay>(this), *surfaceToDelete);
232 }
233 m_initialized = false;
234 delete [] m_configs;
235 m_configs = NULL;
236
237 if (m_versionString) {
238 free(m_versionString);
239 m_versionString = NULL;
240 }
241 if (m_vendorString) {
242 free(m_vendorString);
243 m_vendorString = NULL;
244 }
245 if (m_extensionString) {
246 free(m_extensionString);
247 m_extensionString = NULL;
248 }
249 }
250 pthread_mutex_unlock(&m_lock);
251 }
252
253 #ifdef __APPLE__
254 #define LIBSUFFIX ".dylib"
255 #else
256 #ifdef _WIN32
257 #define LIBSUFFIX ".dll"
258 #else
259 #define LIBSUFFIX ".so"
260 #endif // !_WIN32 (linux)
261 #endif // !__APPLE__
262
263 #ifndef HOST_BUILD
264 #if PLATFORM_SDK_VERSION >= 26
265 #define PARTITION "/vendor"
266 #else
267 #define PARTITION "/system"
268 #endif // !PLATFORM_SDK_VERSION >= 26
269 #if __LP64__
270 #define LIBDIR "/lib64/egl/"
271 #else
272 #define LIBDIR "/lib/egl/"
273 #endif // !__LP64__
274 #endif // !HOST_BUILD
275
loadGLESClientAPI(const char * basename,EGLClient_eglInterface * eglIface,void ** libHandle)276 EGLClient_glesInterface *eglDisplay::loadGLESClientAPI(const char *basename,
277 EGLClient_eglInterface *eglIface,
278 void **libHandle)
279 {
280 #ifdef HOST_BUILD
281 std::string baseDir =
282 android::base::System::get()->getProgramDirectory();
283 std::string path =
284 android::base::pj(
285 baseDir, "lib64", std::string(basename) + LIBSUFFIX);
286 void *lib = dlopen(path.c_str(), RTLD_NOW);
287 #else
288 std::string path(PARTITION);
289 path += LIBDIR;
290 path += basename;
291 path += LIBSUFFIX;
292 void *lib = dlopen(path.c_str(), RTLD_NOW);
293 #endif
294
295 if (!lib) {
296 ALOGE("Failed to dlopen %s", basename);
297 return NULL;
298 }
299
300 init_emul_gles_t init_gles_func = (init_emul_gles_t)dlsym(lib,"init_emul_gles");
301 if (!init_gles_func) {
302 ALOGE("Failed to find init_emul_gles");
303 dlclose((void*)lib);
304 return NULL;
305 }
306
307 *libHandle = lib;
308 return (*init_gles_func)(eglIface);
309 }
310
queryHostEGLString(EGLint name)311 static char *queryHostEGLString(EGLint name)
312 {
313 HostConnection *hcon = HostConnection::get();
314 if (hcon) {
315 renderControl_encoder_context_t *rcEnc = hcon->rcEncoder();
316 if (rcEnc) {
317 int n = rcEnc->rcQueryEGLString(rcEnc, name, NULL, 0);
318 if (n < 0) {
319 // allocate space for the string.
320 char *str = (char *)malloc(-n);
321 n = rcEnc->rcQueryEGLString(rcEnc, name, str, -n);
322 if (n > 0) {
323 return str;
324 }
325
326 free(str);
327 }
328 }
329 }
330
331 return NULL;
332 }
333
findExtInList(const char * token,int tokenlen,const char * list)334 static bool findExtInList(const char* token, int tokenlen, const char* list)
335 {
336 const char* p = list;
337 while (*p != '\0') {
338 const char* q = strchr(p, ' ');
339 if (q == NULL) {
340 /* should not happen, list must be space-terminated */
341 break;
342 }
343 if (tokenlen == (q - p) && !memcmp(token, p, tokenlen)) {
344 return true; /* found it */
345 }
346 p = q+1;
347 }
348 return false; /* not found */
349 }
350
buildExtensionString()351 static char *buildExtensionString()
352 {
353 //Query host extension string
354 char *hostExt = queryHostEGLString(EGL_EXTENSIONS);
355 if (!hostExt || (hostExt[1] == '\0')) {
356 // no extensions on host - only static extension list supported
357 return strdup(systemStaticEGLExtensions);
358 }
359
360 int n = strlen(hostExt);
361 if (n > 0) {
362 char *initialEGLExts;
363 char *finalEGLExts;
364
365 HostConnection *hcon = HostConnection::get();
366 // If we got here, we must have succeeded in queryHostEGLString
367 // and we thus should have a valid connection
368 assert(hcon);
369
370 asprintf(&initialEGLExts,"%s%s", systemStaticEGLExtensions, hostExt);
371
372 std::string dynamicEGLExtensions;
373
374 if (hcon->rcEncoder()->hasNativeSync() &&
375 !strstr(initialEGLExts, kDynamicEGLExtNativeSync)) {
376 dynamicEGLExtensions += kDynamicEGLExtNativeSync;
377
378 if (hcon->rcEncoder()->hasNativeSyncV3()) {
379 dynamicEGLExtensions += kDynamicEGLExtWaitSync;
380 }
381 }
382
383 asprintf(&finalEGLExts, "%s%s", initialEGLExts, dynamicEGLExtensions.c_str());
384
385 free((char*)hostExt);
386 return finalEGLExts;
387 }
388 else {
389 free((char*)hostExt);
390 return strdup(systemStaticEGLExtensions);
391 }
392 }
393
queryString(EGLint name)394 const char *eglDisplay::queryString(EGLint name)
395 {
396 if (name == EGL_CLIENT_APIS) {
397 return "OpenGL_ES";
398 }
399 else if (name == EGL_VERSION) {
400 pthread_mutex_lock(&m_lock);
401 if (m_versionString) {
402 pthread_mutex_unlock(&m_lock);
403 return m_versionString;
404 }
405
406 // build version string
407 asprintf(&m_versionString, "%d.%d", m_major, m_minor);
408 pthread_mutex_unlock(&m_lock);
409
410 return m_versionString;
411 }
412 else if (name == EGL_VENDOR) {
413 pthread_mutex_lock(&m_lock);
414 if (m_vendorString) {
415 pthread_mutex_unlock(&m_lock);
416 return m_vendorString;
417 }
418
419 // build vendor string
420 const char *hostVendor = queryHostEGLString(EGL_VENDOR);
421
422 if (hostVendor) {
423 asprintf(&m_vendorString, "%s Host: %s",
424 systemEGLVendor, hostVendor);
425 free((char*)hostVendor);
426 }
427 else {
428 m_vendorString = (char *)systemEGLVendor;
429 }
430 pthread_mutex_unlock(&m_lock);
431
432 return m_vendorString;
433 }
434 else if (name == EGL_EXTENSIONS) {
435 pthread_mutex_lock(&m_lock);
436 if (m_extensionString) {
437 pthread_mutex_unlock(&m_lock);
438 return m_extensionString;
439 }
440
441 // build extension string
442 m_extensionString = buildExtensionString();
443 pthread_mutex_unlock(&m_lock);
444
445 return m_extensionString;
446 }
447 else {
448 ALOGE("[%s] Unknown name %d\n", __FUNCTION__, name);
449 return NULL;
450 }
451 }
452
453 /* To get the value of attribute <a> of config <c> use the following formula:
454 * value = *(m_configs + (int)c*m_numConfigAttribs + a);
455 */
getAttribValue(EGLConfig config,EGLint attribIdx,EGLint * value)456 EGLBoolean eglDisplay::getAttribValue(EGLConfig config, EGLint attribIdx, EGLint * value)
457 {
458 if (attribIdx == ATTRIBUTE_NONE)
459 {
460 ALOGE("[%s] Bad attribute idx\n", __FUNCTION__);
461 return EGL_FALSE;
462 }
463 *value = *(m_configs + (intptr_t)config*m_numConfigAttribs + attribIdx);
464 return EGL_TRUE;
465 }
466
467 #define EGL_COLOR_COMPONENT_TYPE_EXT 0x3339
468 #define EGL_COLOR_COMPONENT_TYPE_FIXED_EXT 0x333A
469
getConfigAttrib(EGLConfig config,EGLint attrib,EGLint * value)470 EGLBoolean eglDisplay::getConfigAttrib(EGLConfig config, EGLint attrib, EGLint * value)
471 {
472 if (attrib == EGL_FRAMEBUFFER_TARGET_ANDROID) {
473 *value = EGL_TRUE;
474 return EGL_TRUE;
475 }
476 if (attrib == EGL_COVERAGE_SAMPLES_NV ||
477 attrib == EGL_COVERAGE_BUFFERS_NV) {
478 *value = 0;
479 return EGL_TRUE;
480 }
481 if (attrib == EGL_DEPTH_ENCODING_NV) {
482 *value = EGL_DEPTH_ENCODING_NONE_NV;
483 return EGL_TRUE;
484 }
485 if (attrib == EGL_COLOR_COMPONENT_TYPE_EXT) {
486 *value = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
487 return EGL_TRUE;
488 }
489 //Though it seems that valueFor() is thread-safe, we don't take chanses
490 pthread_mutex_lock(&m_lock);
491 EGLBoolean ret =
492 getAttribValue(
493 config,
494 findObjectOrDefault(
495 m_attribs, attrib, EGL_DONT_CARE),
496 value);
497 pthread_mutex_unlock(&m_lock);
498 return ret;
499 }
500
dumpConfig(EGLConfig config)501 void eglDisplay::dumpConfig(EGLConfig config)
502 {
503 EGLint value = 0;
504 DBG("^^^^^^^^^^ dumpConfig %d ^^^^^^^^^^^^^^^^^^", (int)config);
505 for (int i=0; i<m_numConfigAttribs; i++) {
506 getAttribValue(config, i, &value);
507 DBG("{%d}[%d] %d\n", (int)config, i, value);
508 }
509 }
510
511 /* To set the value of attribute <a> of config <c> use the following formula:
512 * *(m_configs + (int)c*m_numConfigAttribs + a) = value;
513 */
setAttribValue(EGLConfig config,EGLint attribIdx,EGLint value)514 EGLBoolean eglDisplay::setAttribValue(EGLConfig config, EGLint attribIdx, EGLint value)
515 {
516 if (attribIdx == ATTRIBUTE_NONE)
517 {
518 ALOGE("[%s] Bad attribute idx\n", __FUNCTION__);
519 return EGL_FALSE;
520 }
521 *(m_configs + (intptr_t)config*m_numConfigAttribs + attribIdx) = value;
522 return EGL_TRUE;
523 }
524
setConfigAttrib(EGLConfig config,EGLint attrib,EGLint value)525 EGLBoolean eglDisplay::setConfigAttrib(EGLConfig config, EGLint attrib, EGLint value)
526 {
527 //Though it seems that valueFor() is thread-safe, we don't take chanses
528 pthread_mutex_lock(&m_lock);
529 EGLBoolean ret =
530 setAttribValue(
531 config,
532 findObjectOrDefault(
533 m_attribs,
534 attrib,
535 EGL_DONT_CARE),
536 value);
537 pthread_mutex_unlock(&m_lock);
538 return ret;
539 }
540
541
getConfigNativePixelFormat(EGLConfig config,PixelFormat * format)542 EGLBoolean eglDisplay::getConfigNativePixelFormat(EGLConfig config, PixelFormat * format)
543 {
544 EGLint redSize, blueSize, greenSize, alphaSize;
545
546 if (!(
547 getAttribValue(
548 config,
549 findObjectOrDefault(m_attribs, EGL_RED_SIZE, EGL_DONT_CARE),
550 &redSize) &&
551 getAttribValue(
552 config,
553 findObjectOrDefault(m_attribs, EGL_BLUE_SIZE, EGL_DONT_CARE),
554 &blueSize) &&
555 getAttribValue(
556 config,
557 findObjectOrDefault(m_attribs, EGL_GREEN_SIZE, EGL_DONT_CARE),
558 &greenSize) &&
559 getAttribValue(
560 config,
561 findObjectOrDefault(m_attribs, EGL_ALPHA_SIZE, EGL_DONT_CARE),
562 &alphaSize))) {
563 ALOGE("Couldn't find value for one of the pixel format attributes");
564 return EGL_FALSE;
565 }
566
567 //calculate the GL internal format
568 if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==8)) *format = PIXEL_FORMAT_RGBA_8888; //XXX: BGR?
569 else if ((redSize==8)&&(greenSize==8)&&(blueSize==8)&&(alphaSize==0)) *format = PIXEL_FORMAT_RGBX_8888; //XXX or PIXEL_FORMAT_RGB_888
570 else if ((redSize==5)&&(greenSize==6)&&(blueSize==5)&&(alphaSize==0)) *format = PIXEL_FORMAT_RGB_565;
571 else if ((redSize==5)&&(greenSize==5)&&(blueSize==5)&&(alphaSize==1)) *format = PIXEL_FORMAT_RGBA_5551;
572 else if ((redSize==4)&&(greenSize==4)&&(blueSize==4)&&(alphaSize==4)) *format = PIXEL_FORMAT_RGBA_4444;
573 else {
574 return EGL_FALSE;
575 }
576 return EGL_TRUE;
577 }
getConfigGLPixelFormat(EGLConfig config,GLenum * format)578 EGLBoolean eglDisplay::getConfigGLPixelFormat(EGLConfig config, GLenum * format)
579 {
580 EGLint redSize, blueSize, greenSize, alphaSize;
581
582 if (!(
583 getAttribValue(
584 config,
585 findObjectOrDefault(m_attribs, EGL_RED_SIZE, EGL_DONT_CARE),
586 &redSize) &&
587 getAttribValue(
588 config,
589 findObjectOrDefault(m_attribs, EGL_BLUE_SIZE, EGL_DONT_CARE),
590 &blueSize) &&
591 getAttribValue(
592 config,
593 findObjectOrDefault(m_attribs, EGL_GREEN_SIZE, EGL_DONT_CARE),
594 &greenSize) &&
595 getAttribValue(
596 config,
597 findObjectOrDefault(m_attribs, EGL_ALPHA_SIZE, EGL_DONT_CARE),
598 &alphaSize))) {
599 ALOGE("Couldn't find value for one of the pixel format attributes");
600 return EGL_FALSE;
601 }
602
603 //calculate the GL internal format
604 if ((redSize == greenSize) && (redSize == blueSize) &&
605 ((redSize == 8) || (redSize == 16) || (redSize == 32)))
606 {
607 if (alphaSize == 0) *format = GL_RGB;
608 else *format = GL_RGBA;
609 }
610 else if ((redSize==5)&&(greenSize==6)&&(blueSize==5)&&(alphaSize==0)) *format = GL_RGB565_OES;
611 else if ((redSize==5)&&(greenSize==5)&&(blueSize==5)&&(alphaSize==1)) *format = GL_RGB5_A1_OES;
612 else if ((redSize==4)&&(greenSize==4)&&(blueSize==4)&&(alphaSize==4)) *format = GL_RGBA4_OES;
613 else return EGL_FALSE;
614
615 return EGL_TRUE;
616 }
617
onCreateContext(EGLContext ctx)618 void eglDisplay::onCreateContext(EGLContext ctx) {
619 pthread_mutex_lock(&m_ctxLock);
620 m_contexts.insert(ctx);
621 pthread_mutex_unlock(&m_ctxLock);
622 }
623
onCreateSurface(EGLSurface surface)624 void eglDisplay::onCreateSurface(EGLSurface surface) {
625 pthread_mutex_lock(&m_surfaceLock);
626 m_surfaces.insert(surface);
627 pthread_mutex_unlock(&m_surfaceLock);
628 }
629
onDestroyContext(EGLContext ctx)630 void eglDisplay::onDestroyContext(EGLContext ctx) {
631 pthread_mutex_lock(&m_ctxLock);
632 m_contexts.erase(ctx);
633 pthread_mutex_unlock(&m_ctxLock);
634 }
635
onDestroySurface(EGLSurface surface)636 void eglDisplay::onDestroySurface(EGLSurface surface) {
637 pthread_mutex_lock(&m_surfaceLock);
638 m_surfaces.erase(surface);
639 pthread_mutex_unlock(&m_surfaceLock);
640 }
641
isContext(EGLContext ctx)642 bool eglDisplay::isContext(EGLContext ctx) {
643 pthread_mutex_lock(&m_ctxLock);
644 bool res = m_contexts.find(ctx) != m_contexts.end();
645 pthread_mutex_unlock(&m_ctxLock);
646 return res;
647 }
648
isSurface(EGLSurface surface)649 bool eglDisplay::isSurface(EGLSurface surface) {
650 pthread_mutex_lock(&m_surfaceLock);
651 bool res = m_surfaces.find(surface) != m_surfaces.end();
652 pthread_mutex_unlock(&m_surfaceLock);
653 return res;
654 }
655