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_ANGLE)
49 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3207
51 #if !defined(EGL_PLATFORM_ANGLE_USE_WARP_ANGLE)
52 #define EGL_PLATFORM_ANGLE_USE_WARP_ANGLE 0x3208
54 #endif // defined(OS_WIN)
56 using ui::GetLastEGLErrorString
;
64 EGLNativeDisplayType g_native_display
;
66 const char* g_egl_extensions
= NULL
;
67 bool g_egl_create_context_robustness_supported
= false;
68 bool g_egl_sync_control_supported
= false;
69 bool g_egl_window_fixed_size_supported
= false;
70 bool g_egl_surfaceless_context_supported
= false;
72 class EGLSyncControlVSyncProvider
73 : public gfx::SyncControlVSyncProvider
{
75 explicit EGLSyncControlVSyncProvider(EGLSurface surface
)
76 : SyncControlVSyncProvider(),
80 ~EGLSyncControlVSyncProvider() override
{}
83 bool GetSyncValues(int64
* system_time
,
84 int64
* media_stream_counter
,
85 int64
* swap_buffer_counter
) override
{
86 uint64 u_system_time
, u_media_stream_counter
, u_swap_buffer_counter
;
87 bool result
= eglGetSyncValuesCHROMIUM(
88 g_display
, surface_
, &u_system_time
,
89 &u_media_stream_counter
, &u_swap_buffer_counter
) == EGL_TRUE
;
91 *system_time
= static_cast<int64
>(u_system_time
);
92 *media_stream_counter
= static_cast<int64
>(u_media_stream_counter
);
93 *swap_buffer_counter
= static_cast<int64
>(u_swap_buffer_counter
);
98 bool GetMscRate(int32
* numerator
, int32
* denominator
) override
{
105 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider
);
110 GLSurfaceEGL::GLSurfaceEGL() {}
112 bool GLSurfaceEGL::InitializeOneOff() {
113 static bool initialized
= false;
117 g_native_display
= GetPlatformDefaultEGLNativeDisplay();
120 g_display
= GetPlatformDisplay(g_native_display
);
122 g_display
= eglGetDisplay(g_native_display
);
126 LOG(ERROR
) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
130 if (!eglInitialize(g_display
, NULL
, NULL
)) {
131 LOG(ERROR
) << "eglInitialize failed with error " << GetLastEGLErrorString();
135 // Choose an EGL configuration.
136 // On X this is only used for PBuffer surfaces.
137 static const EGLint kConfigAttribs
[] = {
143 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
144 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
148 #if defined(USE_OZONE)
149 const EGLint
* config_attribs
=
150 ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
153 const EGLint
* config_attribs
= kConfigAttribs
;
157 if (!eglChooseConfig(g_display
,
162 LOG(ERROR
) << "eglChooseConfig failed with error "
163 << GetLastEGLErrorString();
167 if (num_configs
== 0) {
168 LOG(ERROR
) << "No suitable EGL configs found.";
172 if (!eglChooseConfig(g_display
,
177 LOG(ERROR
) << "eglChooseConfig failed with error "
178 << GetLastEGLErrorString();
182 g_egl_extensions
= eglQueryString(g_display
, EGL_EXTENSIONS
);
183 g_egl_create_context_robustness_supported
=
184 HasEGLExtension("EGL_EXT_create_context_robustness");
185 g_egl_sync_control_supported
=
186 HasEGLExtension("EGL_CHROMIUM_sync_control");
187 g_egl_window_fixed_size_supported
=
188 HasEGLExtension("EGL_ANGLE_window_fixed_size");
190 // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary
191 // workaround, since code written for Android WebView takes different paths
192 // based on whether GL surface objects have underlying EGL surface handles,
193 // conflicting with the use of surfaceless. See https://crbug.com/382349
194 #if defined(OS_ANDROID)
195 DCHECK(!g_egl_surfaceless_context_supported
);
197 // Check if SurfacelessEGL is supported.
198 g_egl_surfaceless_context_supported
=
199 HasEGLExtension("EGL_KHR_surfaceless_context");
200 if (g_egl_surfaceless_context_supported
) {
201 // EGL_KHR_surfaceless_context is supported but ensure
202 // GL_OES_surfaceless_context is also supported. We need a current context
203 // to query for supported GL extensions.
204 scoped_refptr
<GLSurface
> surface
= new SurfacelessEGL(Size(1, 1));
205 scoped_refptr
<GLContext
> context
= GLContext::CreateGLContext(
206 NULL
, surface
.get(), PreferIntegratedGpu
);
207 if (!context
->MakeCurrent(surface
.get()))
208 g_egl_surfaceless_context_supported
= false;
210 // Ensure context supports GL_OES_surfaceless_context.
211 if (g_egl_surfaceless_context_supported
) {
212 g_egl_surfaceless_context_supported
= context
->HasExtension(
213 "GL_OES_surfaceless_context");
214 context
->ReleaseCurrent(surface
.get());
224 EGLDisplay
GLSurfaceEGL::GetDisplay() {
228 EGLDisplay
GLSurfaceEGL::GetHardwareDisplay() {
232 EGLNativeDisplayType
GLSurfaceEGL::GetNativeDisplay() {
233 return g_native_display
;
236 const char* GLSurfaceEGL::GetEGLExtensions() {
237 return g_egl_extensions
;
240 bool GLSurfaceEGL::HasEGLExtension(const char* name
) {
241 return ExtensionsContain(GetEGLExtensions(), name
);
244 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
245 return g_egl_create_context_robustness_supported
;
248 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
249 return g_egl_surfaceless_context_supported
;
252 GLSurfaceEGL::~GLSurfaceEGL() {}
255 static const EGLint kDisplayAttribsWarp
[] {
256 EGL_PLATFORM_ANGLE_TYPE_ANGLE
, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE
,
257 EGL_PLATFORM_ANGLE_USE_WARP_ANGLE
, EGL_TRUE
,
262 EGLDisplay
GLSurfaceEGL::GetPlatformDisplay(
263 EGLNativeDisplayType native_display
) {
264 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseWarp
)) {
265 // Check for availability of WARP via ANGLE extension.
266 bool supports_warp
= false;
267 const char* no_display_extensions
= eglQueryString(EGL_NO_DISPLAY
,
269 // If EGL_EXT_client_extensions not supported this call to eglQueryString
271 if (no_display_extensions
)
273 ExtensionsContain(no_display_extensions
, "ANGLE_platform_angle") &&
274 ExtensionsContain(no_display_extensions
, "ANGLE_platform_angle_d3d");
279 return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE
, native_display
,
280 kDisplayAttribsWarp
);
283 return eglGetDisplay(native_display
);
287 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window
)
290 supports_post_sub_buffer_(false),
293 #if defined(OS_ANDROID)
295 ANativeWindow_acquire(window
);
300 if (GetClientRect(window_
, &windowRect
))
301 size_
= gfx::Rect(windowRect
).size();
305 bool NativeViewGLSurfaceEGL::Initialize() {
306 return Initialize(scoped_ptr
<VSyncProvider
>());
309 bool NativeViewGLSurfaceEGL::Initialize(
310 scoped_ptr
<VSyncProvider
> sync_provider
) {
314 LOG(ERROR
) << "Trying to create surface with invalid display.";
318 std::vector
<EGLint
> egl_window_attributes
;
320 if (g_egl_window_fixed_size_supported
) {
321 egl_window_attributes
.push_back(EGL_FIXED_SIZE_ANGLE
);
322 egl_window_attributes
.push_back(EGL_TRUE
);
323 egl_window_attributes
.push_back(EGL_WIDTH
);
324 egl_window_attributes
.push_back(size_
.width());
325 egl_window_attributes
.push_back(EGL_HEIGHT
);
326 egl_window_attributes
.push_back(size_
.height());
329 if (gfx::g_driver_egl
.ext
.b_EGL_NV_post_sub_buffer
) {
330 egl_window_attributes
.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV
);
331 egl_window_attributes
.push_back(EGL_TRUE
);
334 egl_window_attributes
.push_back(EGL_NONE
);
335 // Create a surface for the native window.
336 surface_
= eglCreateWindowSurface(
337 GetDisplay(), GetConfig(), window_
, &egl_window_attributes
[0]);
340 LOG(ERROR
) << "eglCreateWindowSurface failed with error "
341 << GetLastEGLErrorString();
347 EGLBoolean retVal
= eglQuerySurface(GetDisplay(),
349 EGL_POST_SUB_BUFFER_SUPPORTED_NV
,
351 supports_post_sub_buffer_
= (surfaceVal
&& retVal
) == EGL_TRUE
;
354 vsync_provider_
.reset(sync_provider
.release());
355 else if (g_egl_sync_control_supported
)
356 vsync_provider_
.reset(new EGLSyncControlVSyncProvider(surface_
));
360 void NativeViewGLSurfaceEGL::Destroy() {
362 if (!eglDestroySurface(GetDisplay(), surface_
)) {
363 LOG(ERROR
) << "eglDestroySurface failed with error "
364 << GetLastEGLErrorString();
370 EGLConfig
NativeViewGLSurfaceEGL::GetConfig() {
371 #if !defined(USE_X11)
375 // Get a config compatible with the window
377 XWindowAttributes win_attribs
;
378 if (!XGetWindowAttributes(GetNativeDisplay(), window_
, &win_attribs
)) {
382 // Try matching the window depth with an alpha channel,
383 // because we're worried the destination alpha width could
384 // constrain blending precision.
385 const int kBufferSizeOffset
= 1;
386 const int kAlphaSizeOffset
= 3;
387 EGLint config_attribs
[] = {
393 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
394 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
397 config_attribs
[kBufferSizeOffset
] = win_attribs
.depth
;
400 if (!eglChooseConfig(g_display
,
405 LOG(ERROR
) << "eglChooseConfig failed with error "
406 << GetLastEGLErrorString();
412 if (!eglGetConfigAttrib(g_display
,
416 LOG(ERROR
) << "eglGetConfigAttrib failed with error "
417 << GetLastEGLErrorString();
421 if (config_depth
== win_attribs
.depth
) {
426 // Try without an alpha channel.
427 config_attribs
[kAlphaSizeOffset
] = 0;
428 if (!eglChooseConfig(g_display
,
433 LOG(ERROR
) << "eglChooseConfig failed with error "
434 << GetLastEGLErrorString();
438 if (num_configs
== 0) {
439 LOG(ERROR
) << "No suitable EGL configs found.";
447 bool NativeViewGLSurfaceEGL::IsOffscreen() {
451 bool NativeViewGLSurfaceEGL::SwapBuffers() {
452 TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
453 "width", GetSize().width(),
454 "height", GetSize().height());
456 if (!eglSwapBuffers(GetDisplay(), surface_
)) {
457 DVLOG(1) << "eglSwapBuffers failed with error "
458 << GetLastEGLErrorString();
465 gfx::Size
NativeViewGLSurfaceEGL::GetSize() {
468 if (!eglQuerySurface(GetDisplay(), surface_
, EGL_WIDTH
, &width
) ||
469 !eglQuerySurface(GetDisplay(), surface_
, EGL_HEIGHT
, &height
)) {
470 NOTREACHED() << "eglQuerySurface failed with error "
471 << GetLastEGLErrorString();
475 return gfx::Size(width
, height
);
478 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size
& size
) {
479 if (size
== GetSize())
484 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
485 GLContext
* current_context
= GLContext::GetCurrent();
487 current_context
&& current_context
->IsCurrent(this);
489 scoped_make_current
.reset(
490 new ui::ScopedMakeCurrent(current_context
, this));
491 current_context
->ReleaseCurrent(this);
497 LOG(ERROR
) << "Failed to resize window.";
504 bool NativeViewGLSurfaceEGL::Recreate() {
507 LOG(ERROR
) << "Failed to create surface.";
513 EGLSurface
NativeViewGLSurfaceEGL::GetHandle() {
517 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
518 return supports_post_sub_buffer_
;
521 bool NativeViewGLSurfaceEGL::PostSubBuffer(
522 int x
, int y
, int width
, int height
) {
523 DCHECK(supports_post_sub_buffer_
);
524 if (!eglPostSubBufferNV(GetDisplay(), surface_
, x
, y
, width
, height
)) {
525 DVLOG(1) << "eglPostSubBufferNV failed with error "
526 << GetLastEGLErrorString();
532 VSyncProvider
* NativeViewGLSurfaceEGL::GetVSyncProvider() {
533 return vsync_provider_
.get();
536 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
538 #if defined(OS_ANDROID)
540 ANativeWindow_release(window_
);
544 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size
& size
)
547 // Some implementations of Pbuffer do not support having a 0 size. For such
548 // cases use a (1, 1) surface.
549 if (size_
.GetArea() == 0)
553 bool PbufferGLSurfaceEGL::Initialize() {
554 EGLSurface old_surface
= surface_
;
556 EGLDisplay display
= GetDisplay();
558 LOG(ERROR
) << "Trying to create surface with invalid display.";
562 // Allocate the new pbuffer surface before freeing the old one to ensure
563 // they have different addresses. If they have the same address then a
564 // future call to MakeCurrent might early out because it appears the current
565 // context and surface have not changed.
566 const EGLint pbuffer_attribs
[] = {
567 EGL_WIDTH
, size_
.width(),
568 EGL_HEIGHT
, size_
.height(),
572 EGLSurface new_surface
= eglCreatePbufferSurface(display
,
576 LOG(ERROR
) << "eglCreatePbufferSurface failed with error "
577 << GetLastEGLErrorString();
582 eglDestroySurface(display
, old_surface
);
584 surface_
= new_surface
;
588 void PbufferGLSurfaceEGL::Destroy() {
590 if (!eglDestroySurface(GetDisplay(), surface_
)) {
591 LOG(ERROR
) << "eglDestroySurface failed with error "
592 << GetLastEGLErrorString();
598 EGLConfig
PbufferGLSurfaceEGL::GetConfig() {
602 bool PbufferGLSurfaceEGL::IsOffscreen() {
606 bool PbufferGLSurfaceEGL::SwapBuffers() {
607 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
611 gfx::Size
PbufferGLSurfaceEGL::GetSize() {
615 bool PbufferGLSurfaceEGL::Resize(const gfx::Size
& size
) {
619 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
620 GLContext
* current_context
= GLContext::GetCurrent();
622 current_context
&& current_context
->IsCurrent(this);
624 scoped_make_current
.reset(
625 new ui::ScopedMakeCurrent(current_context
, this));
631 LOG(ERROR
) << "Failed to resize pbuffer.";
638 EGLSurface
PbufferGLSurfaceEGL::GetHandle() {
642 void* PbufferGLSurfaceEGL::GetShareHandle() {
643 #if defined(OS_ANDROID)
647 if (!gfx::g_driver_egl
.ext
.b_EGL_ANGLE_query_surface_pointer
)
650 if (!gfx::g_driver_egl
.ext
.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle
)
654 if (!eglQuerySurfacePointerANGLE(g_display
,
656 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE
,
665 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
669 SurfacelessEGL::SurfacelessEGL(const gfx::Size
& size
)
673 bool SurfacelessEGL::Initialize() {
677 void SurfacelessEGL::Destroy() {
680 EGLConfig
SurfacelessEGL::GetConfig() {
684 bool SurfacelessEGL::IsOffscreen() {
688 bool SurfacelessEGL::IsSurfaceless() const {
692 bool SurfacelessEGL::SwapBuffers() {
693 LOG(ERROR
) << "Attempted to call SwapBuffers with SurfacelessEGL.";
697 gfx::Size
SurfacelessEGL::GetSize() {
701 bool SurfacelessEGL::Resize(const gfx::Size
& size
) {
706 EGLSurface
SurfacelessEGL::GetHandle() {
707 return EGL_NO_SURFACE
;
710 void* SurfacelessEGL::GetShareHandle() {
714 SurfacelessEGL::~SurfacelessEGL() {