1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // This include must be here so that the includes provided transitively 6 // by gl_surface_egl.h don't make it impossible to compile this code. 7 #include "third_party/mesa/src/include/GL/osmesa.h" 8 9 #include "ui/gl/gl_surface_egl.h" 10 11 #if defined(OS_ANDROID) 12 #include <android/native_window_jni.h> 13 #endif 14 15 #include "base/debug/trace_event.h" 16 #include "base/logging.h" 17 #include "base/memory/scoped_ptr.h" 18 #include "base/message_loop/message_loop.h" 19 #include "build/build_config.h" 20 #include "ui/gfx/geometry/rect.h" 21 #include "ui/gl/egl_util.h" 22 #include "ui/gl/gl_context.h" 23 #include "ui/gl/gl_implementation.h" 24 #include "ui/gl/gl_surface_osmesa.h" 25 #include "ui/gl/gl_surface_stub.h" 26 #include "ui/gl/gl_switches.h" 27 #include "ui/gl/scoped_make_current.h" 28 #include "ui/gl/sync_control_vsync_provider.h" 29 30 #if defined(USE_X11) 31 extern "C" { 32 #include <X11/Xlib.h> 33 } 34 #endif 35 36 #if defined (USE_OZONE) 37 #include "ui/ozone/public/surface_factory_ozone.h" 38 #endif 39 40 #if !defined(EGL_FIXED_SIZE_ANGLE) 41 #define EGL_FIXED_SIZE_ANGLE 0x3201 42 #endif 43 44 using ui::GetLastEGLErrorString; 45 46 namespace gfx { 47 48 namespace { 49 50 EGLConfig g_config; 51 EGLDisplay g_display; 52 EGLNativeDisplayType g_native_display; 53 54 const char* g_egl_extensions = NULL; 55 bool g_egl_create_context_robustness_supported = false; 56 bool g_egl_sync_control_supported = false; 57 bool g_egl_window_fixed_size_supported = false; 58 bool g_egl_surfaceless_context_supported = false; 59 60 class EGLSyncControlVSyncProvider 61 : public gfx::SyncControlVSyncProvider { 62 public: 63 explicit EGLSyncControlVSyncProvider(EGLSurface surface) 64 : SyncControlVSyncProvider(), 65 surface_(surface) { 66 } 67 68 virtual ~EGLSyncControlVSyncProvider() { } 69 70 protected: 71 virtual bool GetSyncValues(int64* system_time, 72 int64* media_stream_counter, 73 int64* swap_buffer_counter) OVERRIDE { 74 uint64 u_system_time, u_media_stream_counter, u_swap_buffer_counter; 75 bool result = eglGetSyncValuesCHROMIUM( 76 g_display, surface_, &u_system_time, 77 &u_media_stream_counter, &u_swap_buffer_counter) == EGL_TRUE; 78 if (result) { 79 *system_time = static_cast<int64>(u_system_time); 80 *media_stream_counter = static_cast<int64>(u_media_stream_counter); 81 *swap_buffer_counter = static_cast<int64>(u_swap_buffer_counter); 82 } 83 return result; 84 } 85 86 virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE { 87 return false; 88 } 89 90 private: 91 EGLSurface surface_; 92 93 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider); 94 }; 95 96 } // namespace 97 98 GLSurfaceEGL::GLSurfaceEGL() {} 99 100 bool GLSurfaceEGL::InitializeOneOff() { 101 static bool initialized = false; 102 if (initialized) 103 return true; 104 105 g_native_display = GetPlatformDefaultEGLNativeDisplay(); 106 g_display = eglGetDisplay(g_native_display); 107 if (!g_display) { 108 LOG(ERROR) << "eglGetDisplay failed with error " << GetLastEGLErrorString(); 109 return false; 110 } 111 112 if (!eglInitialize(g_display, NULL, NULL)) { 113 LOG(ERROR) << "eglInitialize failed with error " << GetLastEGLErrorString(); 114 return false; 115 } 116 117 // Choose an EGL configuration. 118 // On X this is only used for PBuffer surfaces. 119 static const EGLint kConfigAttribs[] = { 120 EGL_BUFFER_SIZE, 32, 121 EGL_ALPHA_SIZE, 8, 122 EGL_BLUE_SIZE, 8, 123 EGL_GREEN_SIZE, 8, 124 EGL_RED_SIZE, 8, 125 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 126 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, 127 EGL_NONE 128 }; 129 130 #if defined(USE_OZONE) 131 const EGLint* config_attribs = 132 ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties( 133 kConfigAttribs); 134 #else 135 const EGLint* config_attribs = kConfigAttribs; 136 #endif 137 138 EGLint num_configs; 139 if (!eglChooseConfig(g_display, 140 config_attribs, 141 NULL, 142 0, 143 &num_configs)) { 144 LOG(ERROR) << "eglChooseConfig failed with error " 145 << GetLastEGLErrorString(); 146 return false; 147 } 148 149 if (num_configs == 0) { 150 LOG(ERROR) << "No suitable EGL configs found."; 151 return false; 152 } 153 154 if (!eglChooseConfig(g_display, 155 config_attribs, 156 &g_config, 157 1, 158 &num_configs)) { 159 LOG(ERROR) << "eglChooseConfig failed with error " 160 << GetLastEGLErrorString(); 161 return false; 162 } 163 164 g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS); 165 g_egl_create_context_robustness_supported = 166 HasEGLExtension("EGL_EXT_create_context_robustness"); 167 g_egl_sync_control_supported = 168 HasEGLExtension("EGL_CHROMIUM_sync_control"); 169 g_egl_window_fixed_size_supported = 170 HasEGLExtension("EGL_ANGLE_window_fixed_size"); 171 172 // TODO(oetuaho (at) nvidia.com): Surfaceless is disabled on Android as a temporary 173 // workaround, since code written for Android WebView takes different paths 174 // based on whether GL surface objects have underlying EGL surface handles, 175 // conflicting with the use of surfaceless. See https://crbug.com/382349 176 #if defined(OS_ANDROID) 177 DCHECK(!g_egl_surfaceless_context_supported); 178 #else 179 // Check if SurfacelessEGL is supported. 180 g_egl_surfaceless_context_supported = 181 HasEGLExtension("EGL_KHR_surfaceless_context"); 182 if (g_egl_surfaceless_context_supported) { 183 // EGL_KHR_surfaceless_context is supported but ensure 184 // GL_OES_surfaceless_context is also supported. We need a current context 185 // to query for supported GL extensions. 186 scoped_refptr<GLSurface> surface = new SurfacelessEGL(Size(1, 1)); 187 scoped_refptr<GLContext> context = GLContext::CreateGLContext( 188 NULL, surface.get(), PreferIntegratedGpu); 189 if (!context->MakeCurrent(surface.get())) 190 g_egl_surfaceless_context_supported = false; 191 192 // Ensure context supports GL_OES_surfaceless_context. 193 if (g_egl_surfaceless_context_supported) { 194 g_egl_surfaceless_context_supported = context->HasExtension( 195 "GL_OES_surfaceless_context"); 196 context->ReleaseCurrent(surface.get()); 197 } 198 } 199 #endif 200 201 initialized = true; 202 203 return true; 204 } 205 206 EGLDisplay GLSurfaceEGL::GetDisplay() { 207 return g_display; 208 } 209 210 EGLDisplay GLSurfaceEGL::GetHardwareDisplay() { 211 return g_display; 212 } 213 214 EGLNativeDisplayType GLSurfaceEGL::GetNativeDisplay() { 215 return g_native_display; 216 } 217 218 const char* GLSurfaceEGL::GetEGLExtensions() { 219 return g_egl_extensions; 220 } 221 222 bool GLSurfaceEGL::HasEGLExtension(const char* name) { 223 return ExtensionsContain(GetEGLExtensions(), name); 224 } 225 226 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() { 227 return g_egl_create_context_robustness_supported; 228 } 229 230 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() { 231 return g_egl_surfaceless_context_supported; 232 } 233 234 GLSurfaceEGL::~GLSurfaceEGL() {} 235 236 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window) 237 : window_(window), 238 surface_(NULL), 239 supports_post_sub_buffer_(false), 240 config_(NULL), 241 size_(1, 1) { 242 #if defined(OS_ANDROID) 243 if (window) 244 ANativeWindow_acquire(window); 245 #endif 246 247 #if defined(OS_WIN) 248 RECT windowRect; 249 if (GetClientRect(window_, &windowRect)) 250 size_ = gfx::Rect(windowRect).size(); 251 #endif 252 } 253 254 bool NativeViewGLSurfaceEGL::Initialize() { 255 return Initialize(scoped_ptr<VSyncProvider>()); 256 } 257 258 bool NativeViewGLSurfaceEGL::Initialize( 259 scoped_ptr<VSyncProvider> sync_provider) { 260 DCHECK(!surface_); 261 262 if (!GetDisplay()) { 263 LOG(ERROR) << "Trying to create surface with invalid display."; 264 return false; 265 } 266 267 std::vector<EGLint> egl_window_attributes; 268 269 if (g_egl_window_fixed_size_supported) { 270 egl_window_attributes.push_back(EGL_FIXED_SIZE_ANGLE); 271 egl_window_attributes.push_back(EGL_TRUE); 272 egl_window_attributes.push_back(EGL_WIDTH); 273 egl_window_attributes.push_back(size_.width()); 274 egl_window_attributes.push_back(EGL_HEIGHT); 275 egl_window_attributes.push_back(size_.height()); 276 } 277 278 if (gfx::g_driver_egl.ext.b_EGL_NV_post_sub_buffer) { 279 egl_window_attributes.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV); 280 egl_window_attributes.push_back(EGL_TRUE); 281 } 282 283 egl_window_attributes.push_back(EGL_NONE); 284 // Create a surface for the native window. 285 surface_ = eglCreateWindowSurface( 286 GetDisplay(), GetConfig(), window_, &egl_window_attributes[0]); 287 288 if (!surface_) { 289 LOG(ERROR) << "eglCreateWindowSurface failed with error " 290 << GetLastEGLErrorString(); 291 Destroy(); 292 return false; 293 } 294 295 EGLint surfaceVal; 296 EGLBoolean retVal = eglQuerySurface(GetDisplay(), 297 surface_, 298 EGL_POST_SUB_BUFFER_SUPPORTED_NV, 299 &surfaceVal); 300 supports_post_sub_buffer_ = (surfaceVal && retVal) == EGL_TRUE; 301 302 if (sync_provider) 303 vsync_provider_.reset(sync_provider.release()); 304 else if (g_egl_sync_control_supported) 305 vsync_provider_.reset(new EGLSyncControlVSyncProvider(surface_)); 306 return true; 307 } 308 309 void NativeViewGLSurfaceEGL::Destroy() { 310 if (surface_) { 311 if (!eglDestroySurface(GetDisplay(), surface_)) { 312 LOG(ERROR) << "eglDestroySurface failed with error " 313 << GetLastEGLErrorString(); 314 } 315 surface_ = NULL; 316 } 317 } 318 319 EGLConfig NativeViewGLSurfaceEGL::GetConfig() { 320 #if !defined(USE_X11) 321 return g_config; 322 #else 323 if (!config_) { 324 // Get a config compatible with the window 325 DCHECK(window_); 326 XWindowAttributes win_attribs; 327 if (!XGetWindowAttributes(GetNativeDisplay(), window_, &win_attribs)) { 328 return NULL; 329 } 330 331 // Try matching the window depth with an alpha channel, 332 // because we're worried the destination alpha width could 333 // constrain blending precision. 334 const int kBufferSizeOffset = 1; 335 const int kAlphaSizeOffset = 3; 336 EGLint config_attribs[] = { 337 EGL_BUFFER_SIZE, ~0, 338 EGL_ALPHA_SIZE, 8, 339 EGL_BLUE_SIZE, 8, 340 EGL_GREEN_SIZE, 8, 341 EGL_RED_SIZE, 8, 342 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 343 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, 344 EGL_NONE 345 }; 346 config_attribs[kBufferSizeOffset] = win_attribs.depth; 347 348 EGLint num_configs; 349 if (!eglChooseConfig(g_display, 350 config_attribs, 351 &config_, 352 1, 353 &num_configs)) { 354 LOG(ERROR) << "eglChooseConfig failed with error " 355 << GetLastEGLErrorString(); 356 return NULL; 357 } 358 359 if (num_configs) { 360 EGLint config_depth; 361 if (!eglGetConfigAttrib(g_display, 362 config_, 363 EGL_BUFFER_SIZE, 364 &config_depth)) { 365 LOG(ERROR) << "eglGetConfigAttrib failed with error " 366 << GetLastEGLErrorString(); 367 return NULL; 368 } 369 370 if (config_depth == win_attribs.depth) { 371 return config_; 372 } 373 } 374 375 // Try without an alpha channel. 376 config_attribs[kAlphaSizeOffset] = 0; 377 if (!eglChooseConfig(g_display, 378 config_attribs, 379 &config_, 380 1, 381 &num_configs)) { 382 LOG(ERROR) << "eglChooseConfig failed with error " 383 << GetLastEGLErrorString(); 384 return NULL; 385 } 386 387 if (num_configs == 0) { 388 LOG(ERROR) << "No suitable EGL configs found."; 389 return NULL; 390 } 391 } 392 return config_; 393 #endif 394 } 395 396 bool NativeViewGLSurfaceEGL::IsOffscreen() { 397 return false; 398 } 399 400 bool NativeViewGLSurfaceEGL::SwapBuffers() { 401 TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers", 402 "width", GetSize().width(), 403 "height", GetSize().height()); 404 405 if (!eglSwapBuffers(GetDisplay(), surface_)) { 406 DVLOG(1) << "eglSwapBuffers failed with error " 407 << GetLastEGLErrorString(); 408 return false; 409 } 410 411 return true; 412 } 413 414 gfx::Size NativeViewGLSurfaceEGL::GetSize() { 415 EGLint width; 416 EGLint height; 417 if (!eglQuerySurface(GetDisplay(), surface_, EGL_WIDTH, &width) || 418 !eglQuerySurface(GetDisplay(), surface_, EGL_HEIGHT, &height)) { 419 NOTREACHED() << "eglQuerySurface failed with error " 420 << GetLastEGLErrorString(); 421 return gfx::Size(); 422 } 423 424 return gfx::Size(width, height); 425 } 426 427 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size) { 428 if (size == GetSize()) 429 return true; 430 431 size_ = size; 432 433 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current; 434 GLContext* current_context = GLContext::GetCurrent(); 435 bool was_current = 436 current_context && current_context->IsCurrent(this); 437 if (was_current) { 438 scoped_make_current.reset( 439 new ui::ScopedMakeCurrent(current_context, this)); 440 current_context->ReleaseCurrent(this); 441 } 442 443 Destroy(); 444 445 if (!Initialize()) { 446 LOG(ERROR) << "Failed to resize window."; 447 return false; 448 } 449 450 return true; 451 } 452 453 bool NativeViewGLSurfaceEGL::Recreate() { 454 Destroy(); 455 if (!Initialize()) { 456 LOG(ERROR) << "Failed to create surface."; 457 return false; 458 } 459 return true; 460 } 461 462 EGLSurface NativeViewGLSurfaceEGL::GetHandle() { 463 return surface_; 464 } 465 466 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() { 467 return supports_post_sub_buffer_; 468 } 469 470 bool NativeViewGLSurfaceEGL::PostSubBuffer( 471 int x, int y, int width, int height) { 472 DCHECK(supports_post_sub_buffer_); 473 if (!eglPostSubBufferNV(GetDisplay(), surface_, x, y, width, height)) { 474 DVLOG(1) << "eglPostSubBufferNV failed with error " 475 << GetLastEGLErrorString(); 476 return false; 477 } 478 return true; 479 } 480 481 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() { 482 return vsync_provider_.get(); 483 } 484 485 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() { 486 Destroy(); 487 #if defined(OS_ANDROID) 488 if (window_) 489 ANativeWindow_release(window_); 490 #endif 491 } 492 493 void NativeViewGLSurfaceEGL::SetHandle(EGLSurface surface) { 494 surface_ = surface; 495 } 496 497 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size) 498 : size_(size), 499 surface_(NULL) { 500 // Some implementations of Pbuffer do not support having a 0 size. For such 501 // cases use a (1, 1) surface. 502 if (size_.GetArea() == 0) 503 size_.SetSize(1, 1); 504 } 505 506 bool PbufferGLSurfaceEGL::Initialize() { 507 EGLSurface old_surface = surface_; 508 509 EGLDisplay display = GetDisplay(); 510 if (!display) { 511 LOG(ERROR) << "Trying to create surface with invalid display."; 512 return false; 513 } 514 515 // Allocate the new pbuffer surface before freeing the old one to ensure 516 // they have different addresses. If they have the same address then a 517 // future call to MakeCurrent might early out because it appears the current 518 // context and surface have not changed. 519 const EGLint pbuffer_attribs[] = { 520 EGL_WIDTH, size_.width(), 521 EGL_HEIGHT, size_.height(), 522 EGL_NONE 523 }; 524 525 EGLSurface new_surface = eglCreatePbufferSurface(display, 526 GetConfig(), 527 pbuffer_attribs); 528 if (!new_surface) { 529 LOG(ERROR) << "eglCreatePbufferSurface failed with error " 530 << GetLastEGLErrorString(); 531 return false; 532 } 533 534 if (old_surface) 535 eglDestroySurface(display, old_surface); 536 537 surface_ = new_surface; 538 return true; 539 } 540 541 void PbufferGLSurfaceEGL::Destroy() { 542 if (surface_) { 543 if (!eglDestroySurface(GetDisplay(), surface_)) { 544 LOG(ERROR) << "eglDestroySurface failed with error " 545 << GetLastEGLErrorString(); 546 } 547 surface_ = NULL; 548 } 549 } 550 551 EGLConfig PbufferGLSurfaceEGL::GetConfig() { 552 return g_config; 553 } 554 555 bool PbufferGLSurfaceEGL::IsOffscreen() { 556 return true; 557 } 558 559 bool PbufferGLSurfaceEGL::SwapBuffers() { 560 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL."; 561 return false; 562 } 563 564 gfx::Size PbufferGLSurfaceEGL::GetSize() { 565 return size_; 566 } 567 568 bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size) { 569 if (size == size_) 570 return true; 571 572 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current; 573 GLContext* current_context = GLContext::GetCurrent(); 574 bool was_current = 575 current_context && current_context->IsCurrent(this); 576 if (was_current) { 577 scoped_make_current.reset( 578 new ui::ScopedMakeCurrent(current_context, this)); 579 } 580 581 size_ = size; 582 583 if (!Initialize()) { 584 LOG(ERROR) << "Failed to resize pbuffer."; 585 return false; 586 } 587 588 return true; 589 } 590 591 EGLSurface PbufferGLSurfaceEGL::GetHandle() { 592 return surface_; 593 } 594 595 void* PbufferGLSurfaceEGL::GetShareHandle() { 596 #if defined(OS_ANDROID) 597 NOTREACHED(); 598 return NULL; 599 #else 600 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_query_surface_pointer) 601 return NULL; 602 603 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle) 604 return NULL; 605 606 void* handle; 607 if (!eglQuerySurfacePointerANGLE(g_display, 608 GetHandle(), 609 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, 610 &handle)) { 611 return NULL; 612 } 613 614 return handle; 615 #endif 616 } 617 618 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() { 619 Destroy(); 620 } 621 622 SurfacelessEGL::SurfacelessEGL(const gfx::Size& size) 623 : size_(size) { 624 } 625 626 bool SurfacelessEGL::Initialize() { 627 return true; 628 } 629 630 void SurfacelessEGL::Destroy() { 631 } 632 633 EGLConfig SurfacelessEGL::GetConfig() { 634 return g_config; 635 } 636 637 bool SurfacelessEGL::IsOffscreen() { 638 return true; 639 } 640 641 bool SurfacelessEGL::SwapBuffers() { 642 LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL."; 643 return false; 644 } 645 646 gfx::Size SurfacelessEGL::GetSize() { 647 return size_; 648 } 649 650 bool SurfacelessEGL::Resize(const gfx::Size& size) { 651 size_ = size; 652 return true; 653 } 654 655 EGLSurface SurfacelessEGL::GetHandle() { 656 return EGL_NO_SURFACE; 657 } 658 659 void* SurfacelessEGL::GetShareHandle() { 660 return NULL; 661 } 662 663 SurfacelessEGL::~SurfacelessEGL() { 664 } 665 666 } // namespace gfx 667