eglApi.cpp revision a73a97728befb5ba5ad647ab3b60058c4d536ba4
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#define ATRACE_TAG ATRACE_TAG_GRAPHICS 18 19#include <ctype.h> 20#include <stdlib.h> 21#include <string.h> 22 23#include <hardware/gralloc.h> 24#include <system/window.h> 25 26#include <EGL/egl.h> 27#include <EGL/eglext.h> 28#include <GLES/gl.h> 29#include <GLES/glext.h> 30 31#include <cutils/log.h> 32#include <cutils/atomic.h> 33#include <cutils/compiler.h> 34#include <cutils/properties.h> 35#include <cutils/memory.h> 36 37#include <utils/KeyedVector.h> 38#include <utils/SortedVector.h> 39#include <utils/String8.h> 40#include <utils/Trace.h> 41 42#include "egl_impl.h" 43#include "egl_tls.h" 44#include "glestrace.h" 45#include "hooks.h" 46 47#include "egl_display.h" 48#include "egl_impl.h" 49#include "egl_object.h" 50#include "egl_tls.h" 51#include "egldefs.h" 52 53using namespace android; 54 55// ---------------------------------------------------------------------------- 56 57#define EGL_VERSION_HW_ANDROID 0x3143 58 59struct extention_map_t { 60 const char* name; 61 __eglMustCastToProperFunctionPointerType address; 62}; 63 64static const extention_map_t sExtentionMap[] = { 65 { "eglLockSurfaceKHR", 66 (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, 67 { "eglUnlockSurfaceKHR", 68 (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, 69 { "eglCreateImageKHR", 70 (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, 71 { "eglDestroyImageKHR", 72 (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, 73 { "eglGetSystemTimeFrequencyNV", 74 (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, 75 { "eglGetSystemTimeNV", 76 (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, 77}; 78 79// accesses protected by sExtensionMapMutex 80static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; 81static int sGLExtentionSlot = 0; 82static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; 83 84static void(*findProcAddress(const char* name, 85 const extention_map_t* map, size_t n))() { 86 for (uint32_t i=0 ; i<n ; i++) { 87 if (!strcmp(name, map[i].name)) { 88 return map[i].address; 89 } 90 } 91 return NULL; 92} 93 94// ---------------------------------------------------------------------------- 95 96namespace android { 97extern void setGLHooksThreadSpecific(gl_hooks_t const *value); 98extern EGLBoolean egl_init_drivers(); 99extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; 100extern int getEGLDebugLevel(); 101extern void setEGLDebugLevel(int level); 102extern gl_hooks_t gHooksTrace; 103} // namespace android; 104 105// ---------------------------------------------------------------------------- 106 107static inline void clearError() { egl_tls_t::clearError(); } 108static inline EGLContext getContext() { return egl_tls_t::getContext(); } 109 110// ---------------------------------------------------------------------------- 111 112EGLDisplay eglGetDisplay(EGLNativeDisplayType display) 113{ 114 clearError(); 115 116 uint32_t index = uint32_t(display); 117 if (index >= NUM_DISPLAYS) { 118 return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); 119 } 120 121 if (egl_init_drivers() == EGL_FALSE) { 122 return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); 123 } 124 125 EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display); 126 return dpy; 127} 128 129// ---------------------------------------------------------------------------- 130// Initialization 131// ---------------------------------------------------------------------------- 132 133EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) 134{ 135 clearError(); 136 137 egl_display_ptr dp = get_display(dpy); 138 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 139 140 EGLBoolean res = dp->initialize(major, minor); 141 142 return res; 143} 144 145EGLBoolean eglTerminate(EGLDisplay dpy) 146{ 147 // NOTE: don't unload the drivers b/c some APIs can be called 148 // after eglTerminate() has been called. eglTerminate() only 149 // terminates an EGLDisplay, not a EGL itself. 150 151 clearError(); 152 153 egl_display_ptr dp = get_display(dpy); 154 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 155 156 EGLBoolean res = dp->terminate(); 157 158 return res; 159} 160 161// ---------------------------------------------------------------------------- 162// configuration 163// ---------------------------------------------------------------------------- 164 165EGLBoolean eglGetConfigs( EGLDisplay dpy, 166 EGLConfig *configs, 167 EGLint config_size, EGLint *num_config) 168{ 169 clearError(); 170 171 const egl_display_ptr dp = validate_display(dpy); 172 if (!dp) return EGL_FALSE; 173 174 if (num_config==0) { 175 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 176 } 177 178 EGLBoolean res = EGL_FALSE; 179 *num_config = 0; 180 181 egl_connection_t* const cnx = &gEGLImpl; 182 if (cnx->dso) { 183 res = cnx->egl.eglGetConfigs( 184 dp->disp.dpy, configs, config_size, num_config); 185 } 186 187 return res; 188} 189 190EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, 191 EGLConfig *configs, EGLint config_size, 192 EGLint *num_config) 193{ 194 clearError(); 195 196 const egl_display_ptr dp = validate_display(dpy); 197 if (!dp) return EGL_FALSE; 198 199 if (num_config==0) { 200 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 201 } 202 203 EGLBoolean res = EGL_FALSE; 204 *num_config = 0; 205 206 egl_connection_t* const cnx = &gEGLImpl; 207 if (cnx->dso) { 208 if (attrib_list) { 209 char value[PROPERTY_VALUE_MAX]; 210 property_get("debug.egl.force_msaa", value, "false"); 211 212 if (!strcmp(value, "true")) { 213 size_t attribCount = 0; 214 EGLint attrib = attrib_list[0]; 215 216 // Only enable MSAA if the context is OpenGL ES 2.0 and 217 // if no caveat is requested 218 const EGLint *attribRendererable = NULL; 219 const EGLint *attribCaveat = NULL; 220 221 // Count the number of attributes and look for 222 // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT 223 while (attrib != EGL_NONE) { 224 attrib = attrib_list[attribCount]; 225 switch (attrib) { 226 case EGL_RENDERABLE_TYPE: 227 attribRendererable = &attrib_list[attribCount]; 228 break; 229 case EGL_CONFIG_CAVEAT: 230 attribCaveat = &attrib_list[attribCount]; 231 break; 232 } 233 attribCount++; 234 } 235 236 if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && 237 (!attribCaveat || attribCaveat[1] != EGL_NONE)) { 238 239 // Insert 2 extra attributes to force-enable MSAA 4x 240 EGLint aaAttribs[attribCount + 4]; 241 aaAttribs[0] = EGL_SAMPLE_BUFFERS; 242 aaAttribs[1] = 1; 243 aaAttribs[2] = EGL_SAMPLES; 244 aaAttribs[3] = 4; 245 246 memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); 247 248 EGLint numConfigAA; 249 EGLBoolean resAA = cnx->egl.eglChooseConfig( 250 dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); 251 252 if (resAA == EGL_TRUE && numConfigAA > 0) { 253 ALOGD("Enabling MSAA 4x"); 254 *num_config = numConfigAA; 255 return resAA; 256 } 257 } 258 } 259 } 260 261 res = cnx->egl.eglChooseConfig( 262 dp->disp.dpy, attrib_list, configs, config_size, num_config); 263 } 264 return res; 265} 266 267EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, 268 EGLint attribute, EGLint *value) 269{ 270 clearError(); 271 272 egl_connection_t* cnx = NULL; 273 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 274 if (!dp) return EGL_FALSE; 275 276 return cnx->egl.eglGetConfigAttrib( 277 dp->disp.dpy, config, attribute, value); 278} 279 280// ---------------------------------------------------------------------------- 281// surfaces 282// ---------------------------------------------------------------------------- 283 284EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, 285 NativeWindowType window, 286 const EGLint *attrib_list) 287{ 288 clearError(); 289 290 egl_connection_t* cnx = NULL; 291 egl_display_ptr dp = validate_display_connection(dpy, cnx); 292 if (dp) { 293 EGLDisplay iDpy = dp->disp.dpy; 294 EGLint format; 295 296 if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) { 297 ALOGE("EGLNativeWindowType %p already connected to another API", 298 window); 299 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); 300 } 301 302 // set the native window's buffers format to match this config 303 if (cnx->egl.eglGetConfigAttrib(iDpy, 304 config, EGL_NATIVE_VISUAL_ID, &format)) { 305 if (format != 0) { 306 int err = native_window_set_buffers_format(window, format); 307 if (err != 0) { 308 ALOGE("error setting native window pixel format: %s (%d)", 309 strerror(-err), err); 310 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 311 return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); 312 } 313 } 314 } 315 316 // the EGL spec requires that a new EGLSurface default to swap interval 317 // 1, so explicitly set that on the window here. 318 ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); 319 anw->setSwapInterval(anw, 1); 320 321 EGLSurface surface = cnx->egl.eglCreateWindowSurface( 322 iDpy, config, window, attrib_list); 323 if (surface != EGL_NO_SURFACE) { 324 egl_surface_t* s = new egl_surface_t(dp.get(), config, window, 325 surface, cnx); 326 return s; 327 } 328 329 // EGLSurface creation failed 330 native_window_set_buffers_format(window, 0); 331 native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); 332 } 333 return EGL_NO_SURFACE; 334} 335 336EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, 337 NativePixmapType pixmap, 338 const EGLint *attrib_list) 339{ 340 clearError(); 341 342 egl_connection_t* cnx = NULL; 343 egl_display_ptr dp = validate_display_connection(dpy, cnx); 344 if (dp) { 345 EGLSurface surface = cnx->egl.eglCreatePixmapSurface( 346 dp->disp.dpy, config, pixmap, attrib_list); 347 if (surface != EGL_NO_SURFACE) { 348 egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, 349 surface, cnx); 350 return s; 351 } 352 } 353 return EGL_NO_SURFACE; 354} 355 356EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, 357 const EGLint *attrib_list) 358{ 359 clearError(); 360 361 egl_connection_t* cnx = NULL; 362 egl_display_ptr dp = validate_display_connection(dpy, cnx); 363 if (dp) { 364 EGLSurface surface = cnx->egl.eglCreatePbufferSurface( 365 dp->disp.dpy, config, attrib_list); 366 if (surface != EGL_NO_SURFACE) { 367 egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, 368 surface, cnx); 369 return s; 370 } 371 } 372 return EGL_NO_SURFACE; 373} 374 375EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) 376{ 377 clearError(); 378 379 const egl_display_ptr dp = validate_display(dpy); 380 if (!dp) return EGL_FALSE; 381 382 SurfaceRef _s(dp.get(), surface); 383 if (!_s.get()) 384 return setError(EGL_BAD_SURFACE, EGL_FALSE); 385 386 egl_surface_t * const s = get_surface(surface); 387 EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); 388 if (result == EGL_TRUE) { 389 _s.terminate(); 390 } 391 return result; 392} 393 394EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, 395 EGLint attribute, EGLint *value) 396{ 397 clearError(); 398 399 const egl_display_ptr dp = validate_display(dpy); 400 if (!dp) return EGL_FALSE; 401 402 SurfaceRef _s(dp.get(), surface); 403 if (!_s.get()) 404 return setError(EGL_BAD_SURFACE, EGL_FALSE); 405 406 egl_surface_t const * const s = get_surface(surface); 407 return s->cnx->egl.eglQuerySurface( 408 dp->disp.dpy, s->surface, attribute, value); 409} 410 411void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { 412 ATRACE_CALL(); 413 clearError(); 414 415 const egl_display_ptr dp = validate_display(dpy); 416 if (!dp) { 417 return; 418 } 419 420 SurfaceRef _s(dp.get(), surface); 421 if (!_s.get()) { 422 setError(EGL_BAD_SURFACE, EGL_FALSE); 423 return; 424 } 425 426 int64_t timestamp = systemTime(SYSTEM_TIME_MONOTONIC); 427 428 egl_surface_t const * const s = get_surface(surface); 429 native_window_set_buffers_timestamp(s->win.get(), timestamp); 430} 431 432// ---------------------------------------------------------------------------- 433// Contexts 434// ---------------------------------------------------------------------------- 435 436EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, 437 EGLContext share_list, const EGLint *attrib_list) 438{ 439 clearError(); 440 441 egl_connection_t* cnx = NULL; 442 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 443 if (dpy) { 444 if (share_list != EGL_NO_CONTEXT) { 445 egl_context_t* const c = get_context(share_list); 446 share_list = c->context; 447 } 448 EGLContext context = cnx->egl.eglCreateContext( 449 dp->disp.dpy, config, share_list, attrib_list); 450 if (context != EGL_NO_CONTEXT) { 451 // figure out if it's a GLESv1 or GLESv2 452 int version = 0; 453 if (attrib_list) { 454 while (*attrib_list != EGL_NONE) { 455 GLint attr = *attrib_list++; 456 GLint value = *attrib_list++; 457 if (attr == EGL_CONTEXT_CLIENT_VERSION) { 458 if (value == 1) { 459 version = egl_connection_t::GLESv1_INDEX; 460 } else if (value == 2) { 461 version = egl_connection_t::GLESv2_INDEX; 462 } 463 } 464 }; 465 } 466 egl_context_t* c = new egl_context_t(dpy, context, config, cnx, 467 version); 468#if EGL_TRACE 469 if (getEGLDebugLevel() > 0) 470 GLTrace_eglCreateContext(version, c); 471#endif 472 return c; 473 } else { 474 EGLint error = eglGetError(); 475 ALOGE_IF(error == EGL_SUCCESS, 476 "eglCreateContext(%p, %p, %p, %p) returned EGL_NO_CONTEXT " 477 "but no EGL error!", 478 dpy, config, share_list, attrib_list); 479 } 480 } 481 return EGL_NO_CONTEXT; 482} 483 484EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) 485{ 486 clearError(); 487 488 const egl_display_ptr dp = validate_display(dpy); 489 if (!dp) 490 return EGL_FALSE; 491 492 ContextRef _c(dp.get(), ctx); 493 if (!_c.get()) 494 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 495 496 egl_context_t * const c = get_context(ctx); 497 EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); 498 if (result == EGL_TRUE) { 499 _c.terminate(); 500 } 501 return result; 502} 503 504EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, 505 EGLSurface read, EGLContext ctx) 506{ 507 clearError(); 508 509 egl_display_ptr dp = validate_display(dpy); 510 if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE); 511 512 // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not 513 // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is 514 // a valid but uninitialized display. 515 if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || 516 (draw != EGL_NO_SURFACE) ) { 517 if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, EGL_FALSE); 518 } 519 520 // get a reference to the object passed in 521 ContextRef _c(dp.get(), ctx); 522 SurfaceRef _d(dp.get(), draw); 523 SurfaceRef _r(dp.get(), read); 524 525 // validate the context (if not EGL_NO_CONTEXT) 526 if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { 527 // EGL_NO_CONTEXT is valid 528 return EGL_FALSE; 529 } 530 531 // these are the underlying implementation's object 532 EGLContext impl_ctx = EGL_NO_CONTEXT; 533 EGLSurface impl_draw = EGL_NO_SURFACE; 534 EGLSurface impl_read = EGL_NO_SURFACE; 535 536 // these are our objects structs passed in 537 egl_context_t * c = NULL; 538 egl_surface_t const * d = NULL; 539 egl_surface_t const * r = NULL; 540 541 // these are the current objects structs 542 egl_context_t * cur_c = get_context(getContext()); 543 544 if (ctx != EGL_NO_CONTEXT) { 545 c = get_context(ctx); 546 impl_ctx = c->context; 547 } else { 548 // no context given, use the implementation of the current context 549 if (cur_c == NULL) { 550 // no current context 551 if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { 552 // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); 553 return setError(EGL_BAD_MATCH, EGL_FALSE); 554 } 555 // not an error, there is just no current context. 556 return EGL_TRUE; 557 } 558 } 559 560 // retrieve the underlying implementation's draw EGLSurface 561 if (draw != EGL_NO_SURFACE) { 562 d = get_surface(draw); 563 impl_draw = d->surface; 564 } 565 566 // retrieve the underlying implementation's read EGLSurface 567 if (read != EGL_NO_SURFACE) { 568 r = get_surface(read); 569 impl_read = r->surface; 570 } 571 572 573 EGLBoolean result = dp->makeCurrent(c, cur_c, 574 draw, read, ctx, 575 impl_draw, impl_read, impl_ctx); 576 577 if (result == EGL_TRUE) { 578 if (c) { 579 setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 580 egl_tls_t::setContext(ctx); 581#if EGL_TRACE 582 if (getEGLDebugLevel() > 0) 583 GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx); 584#endif 585 _c.acquire(); 586 _r.acquire(); 587 _d.acquire(); 588 } else { 589 setGLHooksThreadSpecific(&gHooksNoContext); 590 egl_tls_t::setContext(EGL_NO_CONTEXT); 591 } 592 } else { 593 // this will ALOGE the error 594 result = setError(c->cnx->egl.eglGetError(), EGL_FALSE); 595 } 596 return result; 597} 598 599 600EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, 601 EGLint attribute, EGLint *value) 602{ 603 clearError(); 604 605 const egl_display_ptr dp = validate_display(dpy); 606 if (!dp) return EGL_FALSE; 607 608 ContextRef _c(dp.get(), ctx); 609 if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE); 610 611 egl_context_t * const c = get_context(ctx); 612 return c->cnx->egl.eglQueryContext( 613 dp->disp.dpy, c->context, attribute, value); 614 615} 616 617EGLContext eglGetCurrentContext(void) 618{ 619 // could be called before eglInitialize(), but we wouldn't have a context 620 // then, and this function would correctly return EGL_NO_CONTEXT. 621 622 clearError(); 623 624 EGLContext ctx = getContext(); 625 return ctx; 626} 627 628EGLSurface eglGetCurrentSurface(EGLint readdraw) 629{ 630 // could be called before eglInitialize(), but we wouldn't have a context 631 // then, and this function would correctly return EGL_NO_SURFACE. 632 633 clearError(); 634 635 EGLContext ctx = getContext(); 636 if (ctx) { 637 egl_context_t const * const c = get_context(ctx); 638 if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); 639 switch (readdraw) { 640 case EGL_READ: return c->read; 641 case EGL_DRAW: return c->draw; 642 default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); 643 } 644 } 645 return EGL_NO_SURFACE; 646} 647 648EGLDisplay eglGetCurrentDisplay(void) 649{ 650 // could be called before eglInitialize(), but we wouldn't have a context 651 // then, and this function would correctly return EGL_NO_DISPLAY. 652 653 clearError(); 654 655 EGLContext ctx = getContext(); 656 if (ctx) { 657 egl_context_t const * const c = get_context(ctx); 658 if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); 659 return c->dpy; 660 } 661 return EGL_NO_DISPLAY; 662} 663 664EGLBoolean eglWaitGL(void) 665{ 666 clearError(); 667 668 egl_connection_t* const cnx = &gEGLImpl; 669 if (!cnx->dso) 670 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 671 672 return cnx->egl.eglWaitGL(); 673} 674 675EGLBoolean eglWaitNative(EGLint engine) 676{ 677 clearError(); 678 679 egl_connection_t* const cnx = &gEGLImpl; 680 if (!cnx->dso) 681 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 682 683 return cnx->egl.eglWaitNative(engine); 684} 685 686EGLint eglGetError(void) 687{ 688 EGLint err = EGL_SUCCESS; 689 egl_connection_t* const cnx = &gEGLImpl; 690 if (cnx->dso) { 691 err = cnx->egl.eglGetError(); 692 } 693 if (err == EGL_SUCCESS) { 694 err = egl_tls_t::getError(); 695 } 696 return err; 697} 698 699__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) 700{ 701 // eglGetProcAddress() could be the very first function called 702 // in which case we must make sure we've initialized ourselves, this 703 // happens the first time egl_get_display() is called. 704 705 clearError(); 706 707 if (egl_init_drivers() == EGL_FALSE) { 708 setError(EGL_BAD_PARAMETER, NULL); 709 return NULL; 710 } 711 712 // These extensions should not be exposed to applications. They're used 713 // internally by the Android EGL layer. 714 if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID") || 715 !strcmp(procname, "eglDupNativeFenceFDANDROID") || 716 !strcmp(procname, "eglWaitSyncANDROID") || 717 !strcmp(procname, "eglHibernateProcessIMG") || 718 !strcmp(procname, "eglAwakenProcessIMG")) { 719 return NULL; 720 } 721 722 __eglMustCastToProperFunctionPointerType addr; 723 addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap)); 724 if (addr) return addr; 725 726 727 // this protects accesses to sGLExtentionMap and sGLExtentionSlot 728 pthread_mutex_lock(&sExtensionMapMutex); 729 730 /* 731 * Since eglGetProcAddress() is not associated to anything, it needs 732 * to return a function pointer that "works" regardless of what 733 * the current context is. 734 * 735 * For this reason, we return a "forwarder", a small stub that takes 736 * care of calling the function associated with the context 737 * currently bound. 738 * 739 * We first look for extensions we've already resolved, if we're seeing 740 * this extension for the first time, we go through all our 741 * implementations and call eglGetProcAddress() and record the 742 * result in the appropriate implementation hooks and return the 743 * address of the forwarder corresponding to that hook set. 744 * 745 */ 746 747 const String8 name(procname); 748 addr = sGLExtentionMap.valueFor(name); 749 const int slot = sGLExtentionSlot; 750 751 ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, 752 "no more slots for eglGetProcAddress(\"%s\")", 753 procname); 754 755#if EGL_TRACE 756 gl_hooks_t *debugHooks = GLTrace_getGLHooks(); 757#endif 758 759 if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { 760 bool found = false; 761 762 egl_connection_t* const cnx = &gEGLImpl; 763 if (cnx->dso && cnx->egl.eglGetProcAddress) { 764 found = true; 765 // Extensions are independent of the bound context 766 cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = 767 cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = 768#if EGL_TRACE 769 debugHooks->ext.extensions[slot] = 770 gHooksTrace.ext.extensions[slot] = 771#endif 772 cnx->egl.eglGetProcAddress(procname); 773 } 774 775 if (found) { 776 addr = gExtensionForwarders[slot]; 777 sGLExtentionMap.add(name, addr); 778 sGLExtentionSlot++; 779 } 780 } 781 782 pthread_mutex_unlock(&sExtensionMapMutex); 783 return addr; 784} 785 786class FrameCompletionThread : public Thread { 787public: 788 789 static void queueSync(EGLSyncKHR sync) { 790 static sp<FrameCompletionThread> thread(new FrameCompletionThread); 791 static bool running = false; 792 if (!running) { 793 thread->run("GPUFrameCompletion"); 794 running = true; 795 } 796 { 797 Mutex::Autolock lock(thread->mMutex); 798 ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d", 799 thread->mFramesQueued).string()); 800 thread->mQueue.push_back(sync); 801 thread->mCondition.signal(); 802 thread->mFramesQueued++; 803 ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size()); 804 } 805 } 806 807private: 808 FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {} 809 810 virtual bool threadLoop() { 811 EGLSyncKHR sync; 812 uint32_t frameNum; 813 { 814 Mutex::Autolock lock(mMutex); 815 while (mQueue.isEmpty()) { 816 mCondition.wait(mMutex); 817 } 818 sync = mQueue[0]; 819 frameNum = mFramesCompleted; 820 } 821 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); 822 { 823 ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d", 824 frameNum).string()); 825 EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); 826 if (result == EGL_FALSE) { 827 ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); 828 } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { 829 ALOGE("FrameCompletion: timeout waiting for fence"); 830 } 831 eglDestroySyncKHR(dpy, sync); 832 } 833 { 834 Mutex::Autolock lock(mMutex); 835 mQueue.removeAt(0); 836 mFramesCompleted++; 837 ATRACE_INT("GPU Frames Outstanding", mQueue.size()); 838 } 839 return true; 840 } 841 842 uint32_t mFramesQueued; 843 uint32_t mFramesCompleted; 844 Vector<EGLSyncKHR> mQueue; 845 Condition mCondition; 846 Mutex mMutex; 847}; 848 849EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) 850{ 851 ATRACE_CALL(); 852 clearError(); 853 854 const egl_display_ptr dp = validate_display(dpy); 855 if (!dp) return EGL_FALSE; 856 857 SurfaceRef _s(dp.get(), draw); 858 if (!_s.get()) 859 return setError(EGL_BAD_SURFACE, EGL_FALSE); 860 861#if EGL_TRACE 862 gl_hooks_t const *trace_hooks = getGLTraceThreadSpecific(); 863 if (getEGLDebugLevel() > 0) { 864 if (trace_hooks == NULL) { 865 if (GLTrace_start() < 0) { 866 ALOGE("Disabling Tracer for OpenGL ES"); 867 setEGLDebugLevel(0); 868 } else { 869 // switch over to the trace version of hooks 870 EGLContext ctx = egl_tls_t::getContext(); 871 egl_context_t * const c = get_context(ctx); 872 if (c) { 873 setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 874 GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx); 875 } 876 } 877 } 878 879 GLTrace_eglSwapBuffers(dpy, draw); 880 } else if (trace_hooks != NULL) { 881 // tracing is now disabled, so switch back to the non trace version 882 EGLContext ctx = egl_tls_t::getContext(); 883 egl_context_t * const c = get_context(ctx); 884 if (c) setGLHooksThreadSpecific(c->cnx->hooks[c->version]); 885 GLTrace_stop(); 886 } 887#endif 888 889 egl_surface_t const * const s = get_surface(draw); 890 891 if (CC_UNLIKELY(dp->finishOnSwap)) { 892 uint32_t pixel; 893 egl_context_t * const c = get_context( egl_tls_t::getContext() ); 894 if (c) { 895 // glReadPixels() ensures that the frame is complete 896 s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, 897 GL_RGBA,GL_UNSIGNED_BYTE,&pixel); 898 } 899 } 900 901 EGLBoolean result = s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); 902 903 if (CC_UNLIKELY(dp->traceGpuCompletion)) { 904 EGLSyncKHR sync = EGL_NO_SYNC_KHR; 905 { 906 sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); 907 } 908 if (sync != EGL_NO_SYNC_KHR) { 909 FrameCompletionThread::queueSync(sync); 910 } 911 } 912 913 return result; 914} 915 916EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, 917 NativePixmapType target) 918{ 919 clearError(); 920 921 const egl_display_ptr dp = validate_display(dpy); 922 if (!dp) return EGL_FALSE; 923 924 SurfaceRef _s(dp.get(), surface); 925 if (!_s.get()) 926 return setError(EGL_BAD_SURFACE, EGL_FALSE); 927 928 egl_surface_t const * const s = get_surface(surface); 929 return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); 930} 931 932const char* eglQueryString(EGLDisplay dpy, EGLint name) 933{ 934 clearError(); 935 936 const egl_display_ptr dp = validate_display(dpy); 937 if (!dp) return (const char *) NULL; 938 939 switch (name) { 940 case EGL_VENDOR: 941 return dp->getVendorString(); 942 case EGL_VERSION: 943 return dp->getVersionString(); 944 case EGL_EXTENSIONS: 945 return dp->getExtensionString(); 946 case EGL_CLIENT_APIS: 947 return dp->getClientApiString(); 948 case EGL_VERSION_HW_ANDROID: 949 return dp->disp.queryString.version; 950 } 951 return setError(EGL_BAD_PARAMETER, (const char *)0); 952} 953 954 955// ---------------------------------------------------------------------------- 956// EGL 1.1 957// ---------------------------------------------------------------------------- 958 959EGLBoolean eglSurfaceAttrib( 960 EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) 961{ 962 clearError(); 963 964 const egl_display_ptr dp = validate_display(dpy); 965 if (!dp) return EGL_FALSE; 966 967 SurfaceRef _s(dp.get(), surface); 968 if (!_s.get()) 969 return setError(EGL_BAD_SURFACE, EGL_FALSE); 970 971 egl_surface_t const * const s = get_surface(surface); 972 if (s->cnx->egl.eglSurfaceAttrib) { 973 return s->cnx->egl.eglSurfaceAttrib( 974 dp->disp.dpy, s->surface, attribute, value); 975 } 976 return setError(EGL_BAD_SURFACE, EGL_FALSE); 977} 978 979EGLBoolean eglBindTexImage( 980 EGLDisplay dpy, EGLSurface surface, EGLint buffer) 981{ 982 clearError(); 983 984 const egl_display_ptr dp = validate_display(dpy); 985 if (!dp) return EGL_FALSE; 986 987 SurfaceRef _s(dp.get(), surface); 988 if (!_s.get()) 989 return setError(EGL_BAD_SURFACE, EGL_FALSE); 990 991 egl_surface_t const * const s = get_surface(surface); 992 if (s->cnx->egl.eglBindTexImage) { 993 return s->cnx->egl.eglBindTexImage( 994 dp->disp.dpy, s->surface, buffer); 995 } 996 return setError(EGL_BAD_SURFACE, EGL_FALSE); 997} 998 999EGLBoolean eglReleaseTexImage( 1000 EGLDisplay dpy, EGLSurface surface, EGLint buffer) 1001{ 1002 clearError(); 1003 1004 const egl_display_ptr dp = validate_display(dpy); 1005 if (!dp) return EGL_FALSE; 1006 1007 SurfaceRef _s(dp.get(), surface); 1008 if (!_s.get()) 1009 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1010 1011 egl_surface_t const * const s = get_surface(surface); 1012 if (s->cnx->egl.eglReleaseTexImage) { 1013 return s->cnx->egl.eglReleaseTexImage( 1014 dp->disp.dpy, s->surface, buffer); 1015 } 1016 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1017} 1018 1019EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) 1020{ 1021 clearError(); 1022 1023 const egl_display_ptr dp = validate_display(dpy); 1024 if (!dp) return EGL_FALSE; 1025 1026 EGLBoolean res = EGL_TRUE; 1027 egl_connection_t* const cnx = &gEGLImpl; 1028 if (cnx->dso && cnx->egl.eglSwapInterval) { 1029 res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); 1030 } 1031 1032 return res; 1033} 1034 1035 1036// ---------------------------------------------------------------------------- 1037// EGL 1.2 1038// ---------------------------------------------------------------------------- 1039 1040EGLBoolean eglWaitClient(void) 1041{ 1042 clearError(); 1043 1044 egl_connection_t* const cnx = &gEGLImpl; 1045 if (!cnx->dso) 1046 return setError(EGL_BAD_CONTEXT, EGL_FALSE); 1047 1048 EGLBoolean res; 1049 if (cnx->egl.eglWaitClient) { 1050 res = cnx->egl.eglWaitClient(); 1051 } else { 1052 res = cnx->egl.eglWaitGL(); 1053 } 1054 return res; 1055} 1056 1057EGLBoolean eglBindAPI(EGLenum api) 1058{ 1059 clearError(); 1060 1061 if (egl_init_drivers() == EGL_FALSE) { 1062 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1063 } 1064 1065 // bind this API on all EGLs 1066 EGLBoolean res = EGL_TRUE; 1067 egl_connection_t* const cnx = &gEGLImpl; 1068 if (cnx->dso && cnx->egl.eglBindAPI) { 1069 res = cnx->egl.eglBindAPI(api); 1070 } 1071 return res; 1072} 1073 1074EGLenum eglQueryAPI(void) 1075{ 1076 clearError(); 1077 1078 if (egl_init_drivers() == EGL_FALSE) { 1079 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1080 } 1081 1082 egl_connection_t* const cnx = &gEGLImpl; 1083 if (cnx->dso && cnx->egl.eglQueryAPI) { 1084 return cnx->egl.eglQueryAPI(); 1085 } 1086 1087 // or, it can only be OpenGL ES 1088 return EGL_OPENGL_ES_API; 1089} 1090 1091EGLBoolean eglReleaseThread(void) 1092{ 1093 clearError(); 1094 1095 // If there is context bound to the thread, release it 1096 egl_display_t::loseCurrent(get_context(getContext())); 1097 1098 egl_connection_t* const cnx = &gEGLImpl; 1099 if (cnx->dso && cnx->egl.eglReleaseThread) { 1100 cnx->egl.eglReleaseThread(); 1101 } 1102 1103 egl_tls_t::clearTLS(); 1104#if EGL_TRACE 1105 if (getEGLDebugLevel() > 0) 1106 GLTrace_eglReleaseThread(); 1107#endif 1108 return EGL_TRUE; 1109} 1110 1111EGLSurface eglCreatePbufferFromClientBuffer( 1112 EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, 1113 EGLConfig config, const EGLint *attrib_list) 1114{ 1115 clearError(); 1116 1117 egl_connection_t* cnx = NULL; 1118 const egl_display_ptr dp = validate_display_connection(dpy, cnx); 1119 if (!dp) return EGL_FALSE; 1120 if (cnx->egl.eglCreatePbufferFromClientBuffer) { 1121 return cnx->egl.eglCreatePbufferFromClientBuffer( 1122 dp->disp.dpy, buftype, buffer, config, attrib_list); 1123 } 1124 return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); 1125} 1126 1127// ---------------------------------------------------------------------------- 1128// EGL_EGLEXT_VERSION 3 1129// ---------------------------------------------------------------------------- 1130 1131EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, 1132 const EGLint *attrib_list) 1133{ 1134 clearError(); 1135 1136 const egl_display_ptr dp = validate_display(dpy); 1137 if (!dp) return EGL_FALSE; 1138 1139 SurfaceRef _s(dp.get(), surface); 1140 if (!_s.get()) 1141 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1142 1143 egl_surface_t const * const s = get_surface(surface); 1144 if (s->cnx->egl.eglLockSurfaceKHR) { 1145 return s->cnx->egl.eglLockSurfaceKHR( 1146 dp->disp.dpy, s->surface, attrib_list); 1147 } 1148 return setError(EGL_BAD_DISPLAY, EGL_FALSE); 1149} 1150 1151EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) 1152{ 1153 clearError(); 1154 1155 const egl_display_ptr dp = validate_display(dpy); 1156 if (!dp) return EGL_FALSE; 1157 1158 SurfaceRef _s(dp.get(), surface); 1159 if (!_s.get()) 1160 return setError(EGL_BAD_SURFACE, EGL_FALSE); 1161 1162 egl_surface_t const * const s = get_surface(surface); 1163 if (s->cnx->egl.eglUnlockSurfaceKHR) { 1164 return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); 1165 } 1166 return setError(EGL_BAD_DISPLAY, EGL_FALSE); 1167} 1168 1169EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, 1170 EGLClientBuffer buffer, const EGLint *attrib_list) 1171{ 1172 clearError(); 1173 1174 const egl_display_ptr dp = validate_display(dpy); 1175 if (!dp) return EGL_NO_IMAGE_KHR; 1176 1177 ContextRef _c(dp.get(), ctx); 1178 egl_context_t * const c = _c.get(); 1179 1180 EGLImageKHR result = EGL_NO_IMAGE_KHR; 1181 egl_connection_t* const cnx = &gEGLImpl; 1182 if (cnx->dso && cnx->egl.eglCreateImageKHR) { 1183 result = cnx->egl.eglCreateImageKHR( 1184 dp->disp.dpy, 1185 c ? c->context : EGL_NO_CONTEXT, 1186 target, buffer, attrib_list); 1187 } 1188 return result; 1189} 1190 1191EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) 1192{ 1193 clearError(); 1194 1195 const egl_display_ptr dp = validate_display(dpy); 1196 if (!dp) return EGL_FALSE; 1197 1198 EGLBoolean result = EGL_FALSE; 1199 egl_connection_t* const cnx = &gEGLImpl; 1200 if (cnx->dso && cnx->egl.eglDestroyImageKHR) { 1201 result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img); 1202 } 1203 return result; 1204} 1205 1206// ---------------------------------------------------------------------------- 1207// EGL_EGLEXT_VERSION 5 1208// ---------------------------------------------------------------------------- 1209 1210 1211EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) 1212{ 1213 clearError(); 1214 1215 const egl_display_ptr dp = validate_display(dpy); 1216 if (!dp) return EGL_NO_SYNC_KHR; 1217 1218 EGLSyncKHR result = EGL_NO_SYNC_KHR; 1219 egl_connection_t* const cnx = &gEGLImpl; 1220 if (cnx->dso && cnx->egl.eglCreateSyncKHR) { 1221 result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list); 1222 } 1223 return result; 1224} 1225 1226EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) 1227{ 1228 clearError(); 1229 1230 const egl_display_ptr dp = validate_display(dpy); 1231 if (!dp) return EGL_FALSE; 1232 1233 EGLBoolean result = EGL_FALSE; 1234 egl_connection_t* const cnx = &gEGLImpl; 1235 if (cnx->dso && cnx->egl.eglDestroySyncKHR) { 1236 result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync); 1237 } 1238 return result; 1239} 1240 1241EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, 1242 EGLint flags, EGLTimeKHR timeout) 1243{ 1244 clearError(); 1245 1246 const egl_display_ptr dp = validate_display(dpy); 1247 if (!dp) return EGL_FALSE; 1248 1249 EGLBoolean result = EGL_FALSE; 1250 egl_connection_t* const cnx = &gEGLImpl; 1251 if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) { 1252 result = cnx->egl.eglClientWaitSyncKHR( 1253 dp->disp.dpy, sync, flags, timeout); 1254 } 1255 return result; 1256} 1257 1258EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, 1259 EGLint attribute, EGLint *value) 1260{ 1261 clearError(); 1262 1263 const egl_display_ptr dp = validate_display(dpy); 1264 if (!dp) return EGL_FALSE; 1265 1266 EGLBoolean result = EGL_FALSE; 1267 egl_connection_t* const cnx = &gEGLImpl; 1268 if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) { 1269 result = cnx->egl.eglGetSyncAttribKHR( 1270 dp->disp.dpy, sync, attribute, value); 1271 } 1272 return result; 1273} 1274 1275// ---------------------------------------------------------------------------- 1276// ANDROID extensions 1277// ---------------------------------------------------------------------------- 1278 1279EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) 1280{ 1281 clearError(); 1282 1283 const egl_display_ptr dp = validate_display(dpy); 1284 if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; 1285 1286 EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; 1287 egl_connection_t* const cnx = &gEGLImpl; 1288 if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { 1289 result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); 1290 } 1291 return result; 1292} 1293 1294EGLint eglWaitSyncANDROID(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) 1295{ 1296 clearError(); 1297 1298 const egl_display_ptr dp = validate_display(dpy); 1299 if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; 1300 1301 EGLint result = EGL_FALSE; 1302 egl_connection_t* const cnx = &gEGLImpl; 1303 if (cnx->dso && cnx->egl.eglWaitSyncANDROID) { 1304 result = cnx->egl.eglWaitSyncANDROID(dp->disp.dpy, sync, flags); 1305 } 1306 return result; 1307} 1308 1309// ---------------------------------------------------------------------------- 1310// NVIDIA extensions 1311// ---------------------------------------------------------------------------- 1312EGLuint64NV eglGetSystemTimeFrequencyNV() 1313{ 1314 clearError(); 1315 1316 if (egl_init_drivers() == EGL_FALSE) { 1317 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1318 } 1319 1320 EGLuint64NV ret = 0; 1321 egl_connection_t* const cnx = &gEGLImpl; 1322 1323 if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { 1324 return cnx->egl.eglGetSystemTimeFrequencyNV(); 1325 } 1326 1327 return setErrorQuiet(EGL_BAD_DISPLAY, 0); 1328} 1329 1330EGLuint64NV eglGetSystemTimeNV() 1331{ 1332 clearError(); 1333 1334 if (egl_init_drivers() == EGL_FALSE) { 1335 return setError(EGL_BAD_PARAMETER, EGL_FALSE); 1336 } 1337 1338 EGLuint64NV ret = 0; 1339 egl_connection_t* const cnx = &gEGLImpl; 1340 1341 if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { 1342 return cnx->egl.eglGetSystemTimeNV(); 1343 } 1344 1345 return setErrorQuiet(EGL_BAD_DISPLAY, 0); 1346} 1347