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.
5 #include "ui/gl/gl_surface_egl.h"
7 #if defined(OS_ANDROID)
8 #include <android/native_window_jni.h>
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "build/build_config.h"
17 #include "ui/gfx/geometry/rect.h"
18 #include "ui/gl/egl_util.h"
19 #include "ui/gl/gl_context.h"
20 #include "ui/gl/gl_implementation.h"
21 #include "ui/gl/gl_surface_stub.h"
22 #include "ui/gl/gl_switches.h"
23 #include "ui/gl/scoped_make_current.h"
24 #include "ui/gl/sync_control_vsync_provider.h"
32 #if defined (USE_OZONE)
33 #include "ui/ozone/public/surface_factory_ozone.h"
36 #if !defined(EGL_FIXED_SIZE_ANGLE)
37 #define EGL_FIXED_SIZE_ANGLE 0x3201
41 // From ANGLE's egl/eglext.h.
42 #if !defined(EGL_PLATFORM_ANGLE_ANGLE)
43 #define EGL_PLATFORM_ANGLE_ANGLE 0x3201
45 #if !defined(EGL_PLATFORM_ANGLE_TYPE_ANGLE)
46 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3202
48 #if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE)
49 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE 0x3206
51 #endif // defined(OS_WIN)
53 using ui::GetLastEGLErrorString;
57 unsigned int NativeViewGLSurfaceEGL::current_swap_generation_ = 0;
63 EGLNativeDisplayType g_native_display;
65 const char* g_egl_extensions = NULL;
66 bool g_egl_create_context_robustness_supported = false;
67 bool g_egl_sync_control_supported = false;
68 bool g_egl_window_fixed_size_supported = false;
69 bool g_egl_surfaceless_context_supported = false;
71 class EGLSyncControlVSyncProvider
72 : public gfx::SyncControlVSyncProvider {
74 explicit EGLSyncControlVSyncProvider(EGLSurface surface)
75 : SyncControlVSyncProvider(),
79 ~EGLSyncControlVSyncProvider() override {}
82 bool GetSyncValues(int64* system_time,
83 int64* media_stream_counter,
84 int64* swap_buffer_counter) override {
85 uint64 u_system_time, u_media_stream_counter, u_swap_buffer_counter;
86 bool result = eglGetSyncValuesCHROMIUM(
87 g_display, surface_, &u_system_time,
88 &u_media_stream_counter, &u_swap_buffer_counter) == EGL_TRUE;
90 *system_time = static_cast<int64>(u_system_time);
91 *media_stream_counter = static_cast<int64>(u_media_stream_counter);
92 *swap_buffer_counter = static_cast<int64>(u_swap_buffer_counter);
97 bool GetMscRate(int32* numerator, int32* denominator) override {
104 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider);
109 GLSurfaceEGL::GLSurfaceEGL() {}
111 bool GLSurfaceEGL::InitializeOneOff() {
112 static bool initialized = false;
116 g_native_display = GetPlatformDefaultEGLNativeDisplay();
119 g_display = GetPlatformDisplay(g_native_display);
121 g_display = eglGetDisplay(g_native_display);
125 LOG(ERROR) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
129 if (!eglInitialize(g_display, NULL, NULL)) {
130 LOG(ERROR) << "eglInitialize failed with error " << GetLastEGLErrorString();
134 // Choose an EGL configuration.
135 // On X this is only used for PBuffer surfaces.
136 static const EGLint kConfigAttribs[] = {
142 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
143 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
147 #if defined(USE_OZONE)
148 const EGLint* config_attribs =
149 ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
152 const EGLint* config_attribs = kConfigAttribs;
156 if (!eglChooseConfig(g_display,
161 LOG(ERROR) << "eglChooseConfig failed with error "
162 << GetLastEGLErrorString();
166 if (num_configs == 0) {
167 LOG(ERROR) << "No suitable EGL configs found.";
171 if (!eglChooseConfig(g_display,
176 LOG(ERROR) << "eglChooseConfig failed with error "
177 << GetLastEGLErrorString();
181 g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS);
182 g_egl_create_context_robustness_supported =
183 HasEGLExtension("EGL_EXT_create_context_robustness");
184 g_egl_sync_control_supported =
185 HasEGLExtension("EGL_CHROMIUM_sync_control");
186 g_egl_window_fixed_size_supported =
187 HasEGLExtension("EGL_ANGLE_window_fixed_size");
189 // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary
190 // workaround, since code written for Android WebView takes different paths
191 // based on whether GL surface objects have underlying EGL surface handles,
192 // conflicting with the use of surfaceless. See https://crbug.com/382349
193 #if defined(OS_ANDROID)
194 DCHECK(!g_egl_surfaceless_context_supported);
196 // Check if SurfacelessEGL is supported.
197 g_egl_surfaceless_context_supported =
198 HasEGLExtension("EGL_KHR_surfaceless_context");
199 if (g_egl_surfaceless_context_supported) {
200 // EGL_KHR_surfaceless_context is supported but ensure
201 // GL_OES_surfaceless_context is also supported. We need a current context
202 // to query for supported GL extensions.
203 scoped_refptr<GLSurface> surface = new SurfacelessEGL(Size(1, 1));
204 scoped_refptr<GLContext> context = GLContext::CreateGLContext(
205 NULL, surface.get(), PreferIntegratedGpu);
206 if (!context->MakeCurrent(surface.get()))
207 g_egl_surfaceless_context_supported = false;
209 // Ensure context supports GL_OES_surfaceless_context.
210 if (g_egl_surfaceless_context_supported) {
211 g_egl_surfaceless_context_supported = context->HasExtension(
212 "GL_OES_surfaceless_context");
213 context->ReleaseCurrent(surface.get());
223 EGLDisplay GLSurfaceEGL::GetDisplay() {
227 EGLDisplay GLSurfaceEGL::GetHardwareDisplay() {
231 EGLNativeDisplayType GLSurfaceEGL::GetNativeDisplay() {
232 return g_native_display;
235 const char* GLSurfaceEGL::GetEGLExtensions() {
236 return g_egl_extensions;
239 bool GLSurfaceEGL::HasEGLExtension(const char* name) {
240 return ExtensionsContain(GetEGLExtensions(), name);
243 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
244 return g_egl_create_context_robustness_supported;
247 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
248 return g_egl_surfaceless_context_supported;
251 GLSurfaceEGL::~GLSurfaceEGL() {}
254 static const EGLint kDisplayAttribsWarp[] {
255 EGL_PLATFORM_ANGLE_TYPE_ANGLE,
256 EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE,
261 EGLDisplay GLSurfaceEGL::GetPlatformDisplay(
262 EGLNativeDisplayType native_display) {
263 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseWarp)) {
264 // Check for availability of WARP via ANGLE extension.
265 bool supports_warp = false;
266 const char* no_display_extensions = eglQueryString(EGL_NO_DISPLAY,
268 // If EGL_EXT_client_extensions not supported this call to eglQueryString
270 if (no_display_extensions)
272 ExtensionsContain(no_display_extensions, "ANGLE_platform_angle") &&
273 ExtensionsContain(no_display_extensions, "ANGLE_platform_angle_d3d");
278 return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, native_display,
279 kDisplayAttribsWarp);
282 return eglGetDisplay(native_display);
286 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window)
289 supports_post_sub_buffer_(false),
293 swap_generation_(0) {
294 #if defined(OS_ANDROID)
296 ANativeWindow_acquire(window);
301 if (GetClientRect(window_, &windowRect))
302 size_ = gfx::Rect(windowRect).size();
306 bool NativeViewGLSurfaceEGL::Initialize() {
307 return Initialize(scoped_ptr<VSyncProvider>());
310 bool NativeViewGLSurfaceEGL::Initialize(
311 scoped_ptr<VSyncProvider> sync_provider) {
315 LOG(ERROR) << "Trying to create surface with invalid display.";
319 std::vector<EGLint> egl_window_attributes;
321 if (g_egl_window_fixed_size_supported) {
322 egl_window_attributes.push_back(EGL_FIXED_SIZE_ANGLE);
323 egl_window_attributes.push_back(EGL_TRUE);
324 egl_window_attributes.push_back(EGL_WIDTH);
325 egl_window_attributes.push_back(size_.width());
326 egl_window_attributes.push_back(EGL_HEIGHT);
327 egl_window_attributes.push_back(size_.height());
330 if (gfx::g_driver_egl.ext.b_EGL_NV_post_sub_buffer) {
331 egl_window_attributes.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV);
332 egl_window_attributes.push_back(EGL_TRUE);
335 egl_window_attributes.push_back(EGL_NONE);
336 // Create a surface for the native window.
337 surface_ = eglCreateWindowSurface(
338 GetDisplay(), GetConfig(), window_, &egl_window_attributes[0]);
341 LOG(ERROR) << "eglCreateWindowSurface failed with error "
342 << GetLastEGLErrorString();
348 EGLBoolean retVal = eglQuerySurface(GetDisplay(),
350 EGL_POST_SUB_BUFFER_SUPPORTED_NV,
352 supports_post_sub_buffer_ = (surfaceVal && retVal) == EGL_TRUE;
355 vsync_provider_.reset(sync_provider.release());
356 else if (g_egl_sync_control_supported)
357 vsync_provider_.reset(new EGLSyncControlVSyncProvider(surface_));
361 void NativeViewGLSurfaceEGL::Destroy() {
363 if (!eglDestroySurface(GetDisplay(), surface_)) {
364 LOG(ERROR) << "eglDestroySurface failed with error "
365 << GetLastEGLErrorString();
371 EGLConfig NativeViewGLSurfaceEGL::GetConfig() {
372 #if !defined(USE_X11)
376 // Get a config compatible with the window
378 XWindowAttributes win_attribs;
379 if (!XGetWindowAttributes(GetNativeDisplay(), window_, &win_attribs)) {
383 // Try matching the window depth with an alpha channel,
384 // because we're worried the destination alpha width could
385 // constrain blending precision.
386 const int kBufferSizeOffset = 1;
387 const int kAlphaSizeOffset = 3;
388 EGLint config_attribs[] = {
394 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
395 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
398 config_attribs[kBufferSizeOffset] = win_attribs.depth;
401 if (!eglChooseConfig(g_display,
406 LOG(ERROR) << "eglChooseConfig failed with error "
407 << GetLastEGLErrorString();
413 if (!eglGetConfigAttrib(g_display,
417 LOG(ERROR) << "eglGetConfigAttrib failed with error "
418 << GetLastEGLErrorString();
422 if (config_depth == win_attribs.depth) {
427 // Try without an alpha channel.
428 config_attribs[kAlphaSizeOffset] = 0;
429 if (!eglChooseConfig(g_display,
434 LOG(ERROR) << "eglChooseConfig failed with error "
435 << GetLastEGLErrorString();
439 if (num_configs == 0) {
440 LOG(ERROR) << "No suitable EGL configs found.";
448 bool NativeViewGLSurfaceEGL::IsOffscreen() {
452 bool NativeViewGLSurfaceEGL::SwapBuffers() {
453 TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
454 "width", GetSize().width(),
455 "height", GetSize().height());
458 bool force_no_vsync = false;
459 if (swap_interval_ != 0) {
460 // This code is a simple way of enforcing that only one surface actually
461 // vsyncs per frame. This provides single window cases a stable refresh
462 // while allowing multi-window cases to not slow down due to multiple syncs
463 // on a single thread. A better way to fix this problem would be to have
464 // each surface present on its own thread.
465 if (current_swap_generation_ == swap_generation_) {
466 current_swap_generation_++;
468 force_no_vsync = true;
469 eglSwapInterval(GetDisplay(), 0);
472 swap_generation_ = current_swap_generation_;
476 if (!eglSwapBuffers(GetDisplay(), surface_)) {
477 DVLOG(1) << "eglSwapBuffers failed with error "
478 << GetLastEGLErrorString();
483 if (force_no_vsync) {
484 eglSwapInterval(GetDisplay(), swap_interval_);
491 gfx::Size NativeViewGLSurfaceEGL::GetSize() {
494 if (!eglQuerySurface(GetDisplay(), surface_, EGL_WIDTH, &width) ||
495 !eglQuerySurface(GetDisplay(), surface_, EGL_HEIGHT, &height)) {
496 NOTREACHED() << "eglQuerySurface failed with error "
497 << GetLastEGLErrorString();
501 return gfx::Size(width, height);
504 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size) {
505 if (size == GetSize())
510 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
511 GLContext* current_context = GLContext::GetCurrent();
513 current_context && current_context->IsCurrent(this);
515 scoped_make_current.reset(
516 new ui::ScopedMakeCurrent(current_context, this));
517 current_context->ReleaseCurrent(this);
523 LOG(ERROR) << "Failed to resize window.";
530 bool NativeViewGLSurfaceEGL::Recreate() {
533 LOG(ERROR) << "Failed to create surface.";
539 EGLSurface NativeViewGLSurfaceEGL::GetHandle() {
543 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
544 return supports_post_sub_buffer_;
547 bool NativeViewGLSurfaceEGL::PostSubBuffer(
548 int x, int y, int width, int height) {
549 DCHECK(supports_post_sub_buffer_);
550 if (!eglPostSubBufferNV(GetDisplay(), surface_, x, y, width, height)) {
551 DVLOG(1) << "eglPostSubBufferNV failed with error "
552 << GetLastEGLErrorString();
558 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() {
559 return vsync_provider_.get();
562 void NativeViewGLSurfaceEGL::SetSwapInterval(int interval) {
563 swap_interval_ = interval;
566 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
568 #if defined(OS_ANDROID)
570 ANativeWindow_release(window_);
574 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size)
577 // Some implementations of Pbuffer do not support having a 0 size. For such
578 // cases use a (1, 1) surface.
579 if (size_.GetArea() == 0)
583 bool PbufferGLSurfaceEGL::Initialize() {
584 EGLSurface old_surface = surface_;
586 EGLDisplay display = GetDisplay();
588 LOG(ERROR) << "Trying to create surface with invalid display.";
592 // Allocate the new pbuffer surface before freeing the old one to ensure
593 // they have different addresses. If they have the same address then a
594 // future call to MakeCurrent might early out because it appears the current
595 // context and surface have not changed.
596 const EGLint pbuffer_attribs[] = {
597 EGL_WIDTH, size_.width(),
598 EGL_HEIGHT, size_.height(),
602 EGLSurface new_surface = eglCreatePbufferSurface(display,
606 LOG(ERROR) << "eglCreatePbufferSurface failed with error "
607 << GetLastEGLErrorString();
612 eglDestroySurface(display, old_surface);
614 surface_ = new_surface;
618 void PbufferGLSurfaceEGL::Destroy() {
620 if (!eglDestroySurface(GetDisplay(), surface_)) {
621 LOG(ERROR) << "eglDestroySurface failed with error "
622 << GetLastEGLErrorString();
628 EGLConfig PbufferGLSurfaceEGL::GetConfig() {
632 bool PbufferGLSurfaceEGL::IsOffscreen() {
636 bool PbufferGLSurfaceEGL::SwapBuffers() {
637 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
641 gfx::Size PbufferGLSurfaceEGL::GetSize() {
645 bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size) {
649 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
650 GLContext* current_context = GLContext::GetCurrent();
652 current_context && current_context->IsCurrent(this);
654 scoped_make_current.reset(
655 new ui::ScopedMakeCurrent(current_context, this));
661 LOG(ERROR) << "Failed to resize pbuffer.";
668 EGLSurface PbufferGLSurfaceEGL::GetHandle() {
672 void* PbufferGLSurfaceEGL::GetShareHandle() {
673 #if defined(OS_ANDROID)
677 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_query_surface_pointer)
680 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle)
684 if (!eglQuerySurfacePointerANGLE(g_display,
686 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
695 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
699 SurfacelessEGL::SurfacelessEGL(const gfx::Size& size)
703 bool SurfacelessEGL::Initialize() {
707 void SurfacelessEGL::Destroy() {
710 EGLConfig SurfacelessEGL::GetConfig() {
714 bool SurfacelessEGL::IsOffscreen() {
718 bool SurfacelessEGL::IsSurfaceless() const {
722 bool SurfacelessEGL::SwapBuffers() {
723 LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL.";
727 gfx::Size SurfacelessEGL::GetSize() {
731 bool SurfacelessEGL::Resize(const gfx::Size& size) {
736 EGLSurface SurfacelessEGL::GetHandle() {
737 return EGL_NO_SURFACE;
740 void* SurfacelessEGL::GetShareHandle() {
744 SurfacelessEGL::~SurfacelessEGL() {