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/debug/trace_event.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "build/build_config.h"
16 #include "ui/gfx/geometry/rect.h"
17 #include "ui/gl/egl_util.h"
18 #include "ui/gl/gl_context.h"
19 #include "ui/gl/gl_implementation.h"
20 #include "ui/gl/gl_surface_stub.h"
21 #include "ui/gl/gl_switches.h"
22 #include "ui/gl/scoped_make_current.h"
23 #include "ui/gl/sync_control_vsync_provider.h"
31 #if defined (USE_OZONE)
32 #include "ui/ozone/public/surface_factory_ozone.h"
35 #if !defined(EGL_FIXED_SIZE_ANGLE)
36 #define EGL_FIXED_SIZE_ANGLE 0x3201
39 using ui::GetLastEGLErrorString
;
47 EGLNativeDisplayType g_native_display
;
49 const char* g_egl_extensions
= NULL
;
50 bool g_egl_create_context_robustness_supported
= false;
51 bool g_egl_sync_control_supported
= false;
52 bool g_egl_window_fixed_size_supported
= false;
53 bool g_egl_surfaceless_context_supported
= false;
55 class EGLSyncControlVSyncProvider
56 : public gfx::SyncControlVSyncProvider
{
58 explicit EGLSyncControlVSyncProvider(EGLSurface surface
)
59 : SyncControlVSyncProvider(),
63 virtual ~EGLSyncControlVSyncProvider() { }
66 virtual bool GetSyncValues(int64
* system_time
,
67 int64
* media_stream_counter
,
68 int64
* swap_buffer_counter
) OVERRIDE
{
69 uint64 u_system_time
, u_media_stream_counter
, u_swap_buffer_counter
;
70 bool result
= eglGetSyncValuesCHROMIUM(
71 g_display
, surface_
, &u_system_time
,
72 &u_media_stream_counter
, &u_swap_buffer_counter
) == EGL_TRUE
;
74 *system_time
= static_cast<int64
>(u_system_time
);
75 *media_stream_counter
= static_cast<int64
>(u_media_stream_counter
);
76 *swap_buffer_counter
= static_cast<int64
>(u_swap_buffer_counter
);
81 virtual bool GetMscRate(int32
* numerator
, int32
* denominator
) OVERRIDE
{
88 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider
);
93 GLSurfaceEGL::GLSurfaceEGL() {}
95 bool GLSurfaceEGL::InitializeOneOff() {
96 static bool initialized
= false;
100 g_native_display
= GetPlatformDefaultEGLNativeDisplay();
101 g_display
= eglGetDisplay(g_native_display
);
103 LOG(ERROR
) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
107 if (!eglInitialize(g_display
, NULL
, NULL
)) {
108 LOG(ERROR
) << "eglInitialize failed with error " << GetLastEGLErrorString();
112 // Choose an EGL configuration.
113 // On X this is only used for PBuffer surfaces.
114 static const EGLint kConfigAttribs
[] = {
120 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
121 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
125 #if defined(USE_OZONE)
126 const EGLint
* config_attribs
=
127 ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
130 const EGLint
* config_attribs
= kConfigAttribs
;
134 if (!eglChooseConfig(g_display
,
139 LOG(ERROR
) << "eglChooseConfig failed with error "
140 << GetLastEGLErrorString();
144 if (num_configs
== 0) {
145 LOG(ERROR
) << "No suitable EGL configs found.";
149 if (!eglChooseConfig(g_display
,
154 LOG(ERROR
) << "eglChooseConfig failed with error "
155 << GetLastEGLErrorString();
159 g_egl_extensions
= eglQueryString(g_display
, EGL_EXTENSIONS
);
160 g_egl_create_context_robustness_supported
=
161 HasEGLExtension("EGL_EXT_create_context_robustness");
162 g_egl_sync_control_supported
=
163 HasEGLExtension("EGL_CHROMIUM_sync_control");
164 g_egl_window_fixed_size_supported
=
165 HasEGLExtension("EGL_ANGLE_window_fixed_size");
167 // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary
168 // workaround, since code written for Android WebView takes different paths
169 // based on whether GL surface objects have underlying EGL surface handles,
170 // conflicting with the use of surfaceless. See https://crbug.com/382349
171 #if defined(OS_ANDROID)
172 DCHECK(!g_egl_surfaceless_context_supported
);
174 // Check if SurfacelessEGL is supported.
175 g_egl_surfaceless_context_supported
=
176 HasEGLExtension("EGL_KHR_surfaceless_context");
177 if (g_egl_surfaceless_context_supported
) {
178 // EGL_KHR_surfaceless_context is supported but ensure
179 // GL_OES_surfaceless_context is also supported. We need a current context
180 // to query for supported GL extensions.
181 scoped_refptr
<GLSurface
> surface
= new SurfacelessEGL(Size(1, 1));
182 scoped_refptr
<GLContext
> context
= GLContext::CreateGLContext(
183 NULL
, surface
.get(), PreferIntegratedGpu
);
184 if (!context
->MakeCurrent(surface
.get()))
185 g_egl_surfaceless_context_supported
= false;
187 // Ensure context supports GL_OES_surfaceless_context.
188 if (g_egl_surfaceless_context_supported
) {
189 g_egl_surfaceless_context_supported
= context
->HasExtension(
190 "GL_OES_surfaceless_context");
191 context
->ReleaseCurrent(surface
.get());
201 EGLDisplay
GLSurfaceEGL::GetDisplay() {
205 EGLDisplay
GLSurfaceEGL::GetHardwareDisplay() {
209 EGLNativeDisplayType
GLSurfaceEGL::GetNativeDisplay() {
210 return g_native_display
;
213 const char* GLSurfaceEGL::GetEGLExtensions() {
214 return g_egl_extensions
;
217 bool GLSurfaceEGL::HasEGLExtension(const char* name
) {
218 return ExtensionsContain(GetEGLExtensions(), name
);
221 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
222 return g_egl_create_context_robustness_supported
;
225 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
226 return g_egl_surfaceless_context_supported
;
229 GLSurfaceEGL::~GLSurfaceEGL() {}
231 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window
)
234 supports_post_sub_buffer_(false),
237 #if defined(OS_ANDROID)
239 ANativeWindow_acquire(window
);
244 if (GetClientRect(window_
, &windowRect
))
245 size_
= gfx::Rect(windowRect
).size();
249 bool NativeViewGLSurfaceEGL::Initialize() {
250 return Initialize(scoped_ptr
<VSyncProvider
>());
253 bool NativeViewGLSurfaceEGL::Initialize(
254 scoped_ptr
<VSyncProvider
> sync_provider
) {
258 LOG(ERROR
) << "Trying to create surface with invalid display.";
262 std::vector
<EGLint
> egl_window_attributes
;
264 if (g_egl_window_fixed_size_supported
) {
265 egl_window_attributes
.push_back(EGL_FIXED_SIZE_ANGLE
);
266 egl_window_attributes
.push_back(EGL_TRUE
);
267 egl_window_attributes
.push_back(EGL_WIDTH
);
268 egl_window_attributes
.push_back(size_
.width());
269 egl_window_attributes
.push_back(EGL_HEIGHT
);
270 egl_window_attributes
.push_back(size_
.height());
273 if (gfx::g_driver_egl
.ext
.b_EGL_NV_post_sub_buffer
) {
274 egl_window_attributes
.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV
);
275 egl_window_attributes
.push_back(EGL_TRUE
);
278 egl_window_attributes
.push_back(EGL_NONE
);
279 // Create a surface for the native window.
280 surface_
= eglCreateWindowSurface(
281 GetDisplay(), GetConfig(), window_
, &egl_window_attributes
[0]);
284 LOG(ERROR
) << "eglCreateWindowSurface failed with error "
285 << GetLastEGLErrorString();
291 EGLBoolean retVal
= eglQuerySurface(GetDisplay(),
293 EGL_POST_SUB_BUFFER_SUPPORTED_NV
,
295 supports_post_sub_buffer_
= (surfaceVal
&& retVal
) == EGL_TRUE
;
298 vsync_provider_
.reset(sync_provider
.release());
299 else if (g_egl_sync_control_supported
)
300 vsync_provider_
.reset(new EGLSyncControlVSyncProvider(surface_
));
304 void NativeViewGLSurfaceEGL::Destroy() {
306 if (!eglDestroySurface(GetDisplay(), surface_
)) {
307 LOG(ERROR
) << "eglDestroySurface failed with error "
308 << GetLastEGLErrorString();
314 EGLConfig
NativeViewGLSurfaceEGL::GetConfig() {
315 #if !defined(USE_X11)
319 // Get a config compatible with the window
321 XWindowAttributes win_attribs
;
322 if (!XGetWindowAttributes(GetNativeDisplay(), window_
, &win_attribs
)) {
326 // Try matching the window depth with an alpha channel,
327 // because we're worried the destination alpha width could
328 // constrain blending precision.
329 const int kBufferSizeOffset
= 1;
330 const int kAlphaSizeOffset
= 3;
331 EGLint config_attribs
[] = {
337 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
338 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
341 config_attribs
[kBufferSizeOffset
] = win_attribs
.depth
;
344 if (!eglChooseConfig(g_display
,
349 LOG(ERROR
) << "eglChooseConfig failed with error "
350 << GetLastEGLErrorString();
356 if (!eglGetConfigAttrib(g_display
,
360 LOG(ERROR
) << "eglGetConfigAttrib failed with error "
361 << GetLastEGLErrorString();
365 if (config_depth
== win_attribs
.depth
) {
370 // Try without an alpha channel.
371 config_attribs
[kAlphaSizeOffset
] = 0;
372 if (!eglChooseConfig(g_display
,
377 LOG(ERROR
) << "eglChooseConfig failed with error "
378 << GetLastEGLErrorString();
382 if (num_configs
== 0) {
383 LOG(ERROR
) << "No suitable EGL configs found.";
391 bool NativeViewGLSurfaceEGL::IsOffscreen() {
395 bool NativeViewGLSurfaceEGL::SwapBuffers() {
396 TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
397 "width", GetSize().width(),
398 "height", GetSize().height());
400 if (!eglSwapBuffers(GetDisplay(), surface_
)) {
401 DVLOG(1) << "eglSwapBuffers failed with error "
402 << GetLastEGLErrorString();
409 gfx::Size
NativeViewGLSurfaceEGL::GetSize() {
412 if (!eglQuerySurface(GetDisplay(), surface_
, EGL_WIDTH
, &width
) ||
413 !eglQuerySurface(GetDisplay(), surface_
, EGL_HEIGHT
, &height
)) {
414 NOTREACHED() << "eglQuerySurface failed with error "
415 << GetLastEGLErrorString();
419 return gfx::Size(width
, height
);
422 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size
& size
) {
423 if (size
== GetSize())
428 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
429 GLContext
* current_context
= GLContext::GetCurrent();
431 current_context
&& current_context
->IsCurrent(this);
433 scoped_make_current
.reset(
434 new ui::ScopedMakeCurrent(current_context
, this));
435 current_context
->ReleaseCurrent(this);
441 LOG(ERROR
) << "Failed to resize window.";
448 bool NativeViewGLSurfaceEGL::Recreate() {
451 LOG(ERROR
) << "Failed to create surface.";
457 EGLSurface
NativeViewGLSurfaceEGL::GetHandle() {
461 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
462 return supports_post_sub_buffer_
;
465 bool NativeViewGLSurfaceEGL::PostSubBuffer(
466 int x
, int y
, int width
, int height
) {
467 DCHECK(supports_post_sub_buffer_
);
468 if (!eglPostSubBufferNV(GetDisplay(), surface_
, x
, y
, width
, height
)) {
469 DVLOG(1) << "eglPostSubBufferNV failed with error "
470 << GetLastEGLErrorString();
476 VSyncProvider
* NativeViewGLSurfaceEGL::GetVSyncProvider() {
477 return vsync_provider_
.get();
480 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
482 #if defined(OS_ANDROID)
484 ANativeWindow_release(window_
);
488 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size
& size
)
491 // Some implementations of Pbuffer do not support having a 0 size. For such
492 // cases use a (1, 1) surface.
493 if (size_
.GetArea() == 0)
497 bool PbufferGLSurfaceEGL::Initialize() {
498 EGLSurface old_surface
= surface_
;
500 EGLDisplay display
= GetDisplay();
502 LOG(ERROR
) << "Trying to create surface with invalid display.";
506 // Allocate the new pbuffer surface before freeing the old one to ensure
507 // they have different addresses. If they have the same address then a
508 // future call to MakeCurrent might early out because it appears the current
509 // context and surface have not changed.
510 const EGLint pbuffer_attribs
[] = {
511 EGL_WIDTH
, size_
.width(),
512 EGL_HEIGHT
, size_
.height(),
516 EGLSurface new_surface
= eglCreatePbufferSurface(display
,
520 LOG(ERROR
) << "eglCreatePbufferSurface failed with error "
521 << GetLastEGLErrorString();
526 eglDestroySurface(display
, old_surface
);
528 surface_
= new_surface
;
532 void PbufferGLSurfaceEGL::Destroy() {
534 if (!eglDestroySurface(GetDisplay(), surface_
)) {
535 LOG(ERROR
) << "eglDestroySurface failed with error "
536 << GetLastEGLErrorString();
542 EGLConfig
PbufferGLSurfaceEGL::GetConfig() {
546 bool PbufferGLSurfaceEGL::IsOffscreen() {
550 bool PbufferGLSurfaceEGL::SwapBuffers() {
551 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
555 gfx::Size
PbufferGLSurfaceEGL::GetSize() {
559 bool PbufferGLSurfaceEGL::Resize(const gfx::Size
& size
) {
563 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
564 GLContext
* current_context
= GLContext::GetCurrent();
566 current_context
&& current_context
->IsCurrent(this);
568 scoped_make_current
.reset(
569 new ui::ScopedMakeCurrent(current_context
, this));
575 LOG(ERROR
) << "Failed to resize pbuffer.";
582 EGLSurface
PbufferGLSurfaceEGL::GetHandle() {
586 void* PbufferGLSurfaceEGL::GetShareHandle() {
587 #if defined(OS_ANDROID)
591 if (!gfx::g_driver_egl
.ext
.b_EGL_ANGLE_query_surface_pointer
)
594 if (!gfx::g_driver_egl
.ext
.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle
)
598 if (!eglQuerySurfacePointerANGLE(g_display
,
600 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE
,
609 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
613 SurfacelessEGL::SurfacelessEGL(const gfx::Size
& size
)
617 bool SurfacelessEGL::Initialize() {
621 void SurfacelessEGL::Destroy() {
624 EGLConfig
SurfacelessEGL::GetConfig() {
628 bool SurfacelessEGL::IsOffscreen() {
632 bool SurfacelessEGL::SwapBuffers() {
633 LOG(ERROR
) << "Attempted to call SwapBuffers with SurfacelessEGL.";
637 gfx::Size
SurfacelessEGL::GetSize() {
641 bool SurfacelessEGL::Resize(const gfx::Size
& size
) {
646 EGLSurface
SurfacelessEGL::GetHandle() {
647 return EGL_NO_SURFACE
;
650 void* SurfacelessEGL::GetShareHandle() {
654 SurfacelessEGL::~SurfacelessEGL() {