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 // 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"
9 #include "ui/gl/gl_surface_egl.h"
11 #if defined(OS_ANDROID)
12 #include <android/native_window_jni.h>
13 #include "base/android/sys_utils.h"
16 #include "base/debug/trace_event.h"
17 #include "base/logging.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "build/build_config.h"
21 #include "ui/gfx/geometry/rect.h"
22 #include "ui/gl/egl_util.h"
23 #include "ui/gl/gl_context.h"
24 #include "ui/gl/gl_implementation.h"
25 #include "ui/gl/gl_surface_osmesa.h"
26 #include "ui/gl/gl_surface_stub.h"
27 #include "ui/gl/gl_switches.h"
28 #include "ui/gl/scoped_make_current.h"
29 #include "ui/gl/sync_control_vsync_provider.h"
37 #if defined (USE_OZONE)
38 #include "ui/gfx/ozone/surface_factory_ozone.h"
41 #if !defined(EGL_FIXED_SIZE_ANGLE)
42 #define EGL_FIXED_SIZE_ANGLE 0x3201
45 using ui::GetLastEGLErrorString
;
53 EGLNativeDisplayType g_native_display
;
55 const char* g_egl_extensions
= NULL
;
56 bool g_egl_create_context_robustness_supported
= false;
57 bool g_egl_sync_control_supported
= false;
58 bool g_egl_window_fixed_size_supported
= false;
59 bool g_egl_surfaceless_context_supported
= false;
61 class EGLSyncControlVSyncProvider
62 : public gfx::SyncControlVSyncProvider
{
64 explicit EGLSyncControlVSyncProvider(EGLSurface surface
)
65 : SyncControlVSyncProvider(),
69 virtual ~EGLSyncControlVSyncProvider() { }
72 virtual bool GetSyncValues(int64
* system_time
,
73 int64
* media_stream_counter
,
74 int64
* swap_buffer_counter
) OVERRIDE
{
75 uint64 u_system_time
, u_media_stream_counter
, u_swap_buffer_counter
;
76 bool result
= eglGetSyncValuesCHROMIUM(
77 g_display
, surface_
, &u_system_time
,
78 &u_media_stream_counter
, &u_swap_buffer_counter
) == EGL_TRUE
;
80 *system_time
= static_cast<int64
>(u_system_time
);
81 *media_stream_counter
= static_cast<int64
>(u_media_stream_counter
);
82 *swap_buffer_counter
= static_cast<int64
>(u_swap_buffer_counter
);
87 virtual bool GetMscRate(int32
* numerator
, int32
* denominator
) OVERRIDE
{
94 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider
);
97 bool ValidateEglConfig(EGLDisplay display
,
98 const EGLint
* config_attribs
,
99 EGLint
* num_configs
) {
100 if (!eglChooseConfig(display
,
105 LOG(ERROR
) << "eglChooseConfig failed with error "
106 << GetLastEGLErrorString();
109 if (*num_configs
== 0) {
110 LOG(ERROR
) << "No suitable EGL configs found.";
118 GLSurfaceEGL::GLSurfaceEGL() {}
120 bool GLSurfaceEGL::InitializeOneOff() {
121 static bool initialized
= false;
125 g_native_display
= GetPlatformDefaultEGLNativeDisplay();
126 g_display
= eglGetDisplay(g_native_display
);
128 LOG(ERROR
) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
132 if (!eglInitialize(g_display
, NULL
, NULL
)) {
133 LOG(ERROR
) << "eglInitialize failed with error " << GetLastEGLErrorString();
137 // Choose an EGL configuration.
138 // On X this is only used for PBuffer surfaces.
139 static EGLint config_attribs_8888
[] = {
145 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
146 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
150 #if defined(OS_ANDROID)
151 static EGLint config_attribs_565
[] = {
156 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
157 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
161 EGLint
* choose_attributes
= config_attribs_8888
;
163 #if defined(OS_ANDROID)
164 if (base::android::SysUtils::IsLowEndDevice()) {
165 choose_attributes
= config_attribs_565
;
169 #if defined(USE_OZONE)
170 const EGLint
* config_attribs
=
171 SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
174 const EGLint
* config_attribs
= choose_attributes
;
178 EGLint config_size
= 1;
179 EGLConfig
* config_data
= &g_config
;
180 // Validate if there are any configs for given atrribs.
181 if (!ValidateEglConfig(g_display
,
187 #if defined(OS_ANDROID)
188 scoped_ptr
<EGLConfig
[]> matching_configs(new EGLConfig
[num_configs
]);
189 if (base::android::SysUtils::IsLowEndDevice()) {
190 config_size
= num_configs
;
191 config_data
= matching_configs
.get();
195 if (!eglChooseConfig(g_display
,
200 LOG(ERROR
) << "eglChooseConfig failed with error "
201 << GetLastEGLErrorString();
205 #if defined(OS_ANDROID)
206 if (base::android::SysUtils::IsLowEndDevice()) {
207 // Because of the EGL config sort order, we have to iterate
208 // through all of them (it'll put higher sum(R,G,B) bits
209 // first with the above attribs).
210 bool match_found
= false;
211 for (int i
= 0; i
< num_configs
; i
++) {
213 EGLint red
, green
, blue
;
214 // Read the relevent attributes of the EGLConfig.
215 success
= eglGetConfigAttrib(g_display
, matching_configs
[i
],
217 success
&= eglGetConfigAttrib(g_display
, matching_configs
[i
],
218 EGL_BLUE_SIZE
, &blue
);
219 success
&= eglGetConfigAttrib(g_display
, matching_configs
[i
],
220 EGL_GREEN_SIZE
, &green
);
221 if ((success
== EGL_TRUE
) && (red
== 5) &&
222 (green
== 6) && (blue
== 5)) {
223 g_config
= matching_configs
[i
];
229 // To fall back to default 32 bit format, choose with
230 // the right attributes again.
231 if (!ValidateEglConfig(g_display
,
236 if (!eglChooseConfig(g_display
,
241 LOG(ERROR
) << "eglChooseConfig failed with error "
242 << GetLastEGLErrorString();
250 g_egl_extensions
= eglQueryString(g_display
, EGL_EXTENSIONS
);
251 g_egl_create_context_robustness_supported
=
252 HasEGLExtension("EGL_EXT_create_context_robustness");
253 g_egl_sync_control_supported
=
254 HasEGLExtension("EGL_CHROMIUM_sync_control");
255 g_egl_window_fixed_size_supported
=
256 HasEGLExtension("EGL_ANGLE_window_fixed_size");
258 // Check if SurfacelessEGL is supported.
259 g_egl_surfaceless_context_supported
=
260 HasEGLExtension("EGL_KHR_surfaceless_context");
261 if (g_egl_surfaceless_context_supported
) {
262 // EGL_KHR_surfaceless_context is supported but ensure
263 // GL_OES_surfaceless_context is also supported. We need a current context
264 // to query for supported GL extensions.
265 scoped_refptr
<GLSurface
> surface
= new SurfacelessEGL(Size(1, 1));
266 scoped_refptr
<GLContext
> context
= GLContext::CreateGLContext(
267 NULL
, surface
.get(), PreferIntegratedGpu
);
268 if (!context
->MakeCurrent(surface
.get()))
269 g_egl_surfaceless_context_supported
= false;
271 // Ensure context supports GL_OES_surfaceless_context.
272 if (g_egl_surfaceless_context_supported
) {
273 g_egl_surfaceless_context_supported
= context
->HasExtension(
274 "GL_OES_surfaceless_context");
275 context
->ReleaseCurrent(surface
.get());
284 EGLDisplay
GLSurfaceEGL::GetDisplay() {
288 EGLDisplay
GLSurfaceEGL::GetHardwareDisplay() {
292 EGLNativeDisplayType
GLSurfaceEGL::GetNativeDisplay() {
293 return g_native_display
;
296 const char* GLSurfaceEGL::GetEGLExtensions() {
297 return g_egl_extensions
;
300 bool GLSurfaceEGL::HasEGLExtension(const char* name
) {
301 return ExtensionsContain(GetEGLExtensions(), name
);
304 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
305 return g_egl_create_context_robustness_supported
;
308 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
309 return g_egl_surfaceless_context_supported
;
312 GLSurfaceEGL::~GLSurfaceEGL() {}
314 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window
)
317 supports_post_sub_buffer_(false),
320 #if defined(OS_ANDROID)
322 ANativeWindow_acquire(window
);
327 if (GetClientRect(window_
, &windowRect
))
328 size_
= gfx::Rect(windowRect
).size();
332 bool NativeViewGLSurfaceEGL::Initialize() {
333 return Initialize(scoped_ptr
<VSyncProvider
>());
336 bool NativeViewGLSurfaceEGL::Initialize(
337 scoped_ptr
<VSyncProvider
> sync_provider
) {
341 LOG(ERROR
) << "Trying to create surface with invalid display.";
345 std::vector
<EGLint
> egl_window_attributes
;
347 if (g_egl_window_fixed_size_supported
) {
348 egl_window_attributes
.push_back(EGL_FIXED_SIZE_ANGLE
);
349 egl_window_attributes
.push_back(EGL_TRUE
);
350 egl_window_attributes
.push_back(EGL_WIDTH
);
351 egl_window_attributes
.push_back(size_
.width());
352 egl_window_attributes
.push_back(EGL_HEIGHT
);
353 egl_window_attributes
.push_back(size_
.height());
356 if (gfx::g_driver_egl
.ext
.b_EGL_NV_post_sub_buffer
) {
357 egl_window_attributes
.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV
);
358 egl_window_attributes
.push_back(EGL_TRUE
);
361 egl_window_attributes
.push_back(EGL_NONE
);
362 // Create a surface for the native window.
363 surface_
= eglCreateWindowSurface(
364 GetDisplay(), GetConfig(), window_
, &egl_window_attributes
[0]);
367 LOG(ERROR
) << "eglCreateWindowSurface failed with error "
368 << GetLastEGLErrorString();
374 EGLBoolean retVal
= eglQuerySurface(GetDisplay(),
376 EGL_POST_SUB_BUFFER_SUPPORTED_NV
,
378 supports_post_sub_buffer_
= (surfaceVal
&& retVal
) == EGL_TRUE
;
381 vsync_provider_
.reset(sync_provider
.release());
382 else if (g_egl_sync_control_supported
)
383 vsync_provider_
.reset(new EGLSyncControlVSyncProvider(surface_
));
387 void NativeViewGLSurfaceEGL::Destroy() {
389 if (!eglDestroySurface(GetDisplay(), surface_
)) {
390 LOG(ERROR
) << "eglDestroySurface failed with error "
391 << GetLastEGLErrorString();
397 EGLConfig
NativeViewGLSurfaceEGL::GetConfig() {
398 #if !defined(USE_X11)
402 // Get a config compatible with the window
404 XWindowAttributes win_attribs
;
405 if (!XGetWindowAttributes(GetNativeDisplay(), window_
, &win_attribs
)) {
409 // Try matching the window depth with an alpha channel,
410 // because we're worried the destination alpha width could
411 // constrain blending precision.
412 const int kBufferSizeOffset
= 1;
413 const int kAlphaSizeOffset
= 3;
414 EGLint config_attribs
[] = {
420 EGL_RENDERABLE_TYPE
, EGL_OPENGL_ES2_BIT
,
421 EGL_SURFACE_TYPE
, EGL_WINDOW_BIT
| EGL_PBUFFER_BIT
,
424 config_attribs
[kBufferSizeOffset
] = win_attribs
.depth
;
427 if (!eglChooseConfig(g_display
,
432 LOG(ERROR
) << "eglChooseConfig failed with error "
433 << GetLastEGLErrorString();
439 if (!eglGetConfigAttrib(g_display
,
443 LOG(ERROR
) << "eglGetConfigAttrib failed with error "
444 << GetLastEGLErrorString();
448 if (config_depth
== win_attribs
.depth
) {
453 // Try without an alpha channel.
454 config_attribs
[kAlphaSizeOffset
] = 0;
455 if (!eglChooseConfig(g_display
,
460 LOG(ERROR
) << "eglChooseConfig failed with error "
461 << GetLastEGLErrorString();
465 if (num_configs
== 0) {
466 LOG(ERROR
) << "No suitable EGL configs found.";
474 bool NativeViewGLSurfaceEGL::IsOffscreen() {
478 bool NativeViewGLSurfaceEGL::SwapBuffers() {
479 TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
480 "width", GetSize().width(),
481 "height", GetSize().height());
483 if (!eglSwapBuffers(GetDisplay(), surface_
)) {
484 DVLOG(1) << "eglSwapBuffers failed with error "
485 << GetLastEGLErrorString();
492 gfx::Size
NativeViewGLSurfaceEGL::GetSize() {
495 if (!eglQuerySurface(GetDisplay(), surface_
, EGL_WIDTH
, &width
) ||
496 !eglQuerySurface(GetDisplay(), surface_
, EGL_HEIGHT
, &height
)) {
497 NOTREACHED() << "eglQuerySurface failed with error "
498 << GetLastEGLErrorString();
502 return gfx::Size(width
, height
);
505 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size
& size
) {
506 if (size
== GetSize())
511 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
512 GLContext
* current_context
= GLContext::GetCurrent();
514 current_context
&& current_context
->IsCurrent(this);
516 scoped_make_current
.reset(
517 new ui::ScopedMakeCurrent(current_context
, this));
518 current_context
->ReleaseCurrent(this);
524 LOG(ERROR
) << "Failed to resize window.";
531 bool NativeViewGLSurfaceEGL::Recreate() {
534 LOG(ERROR
) << "Failed to create surface.";
540 EGLSurface
NativeViewGLSurfaceEGL::GetHandle() {
544 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
545 return supports_post_sub_buffer_
;
548 bool NativeViewGLSurfaceEGL::PostSubBuffer(
549 int x
, int y
, int width
, int height
) {
550 DCHECK(supports_post_sub_buffer_
);
551 if (!eglPostSubBufferNV(GetDisplay(), surface_
, x
, y
, width
, height
)) {
552 DVLOG(1) << "eglPostSubBufferNV failed with error "
553 << GetLastEGLErrorString();
559 VSyncProvider
* NativeViewGLSurfaceEGL::GetVSyncProvider() {
560 return vsync_provider_
.get();
563 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
565 #if defined(OS_ANDROID)
567 ANativeWindow_release(window_
);
571 void NativeViewGLSurfaceEGL::SetHandle(EGLSurface surface
) {
575 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size
& size
)
580 bool PbufferGLSurfaceEGL::Initialize() {
581 EGLSurface old_surface
= surface_
;
583 EGLDisplay display
= GetDisplay();
585 LOG(ERROR
) << "Trying to create surface with invalid display.";
589 if (size_
.GetArea() == 0) {
590 LOG(ERROR
) << "Error: surface has zero area "
591 << size_
.width() << " x " << size_
.height();
595 // Allocate the new pbuffer surface before freeing the old one to ensure
596 // they have different addresses. If they have the same address then a
597 // future call to MakeCurrent might early out because it appears the current
598 // context and surface have not changed.
599 const EGLint pbuffer_attribs
[] = {
600 EGL_WIDTH
, size_
.width(),
601 EGL_HEIGHT
, size_
.height(),
605 EGLSurface new_surface
= eglCreatePbufferSurface(display
,
609 LOG(ERROR
) << "eglCreatePbufferSurface failed with error "
610 << GetLastEGLErrorString();
615 eglDestroySurface(display
, old_surface
);
617 surface_
= new_surface
;
621 void PbufferGLSurfaceEGL::Destroy() {
623 if (!eglDestroySurface(GetDisplay(), surface_
)) {
624 LOG(ERROR
) << "eglDestroySurface failed with error "
625 << GetLastEGLErrorString();
631 EGLConfig
PbufferGLSurfaceEGL::GetConfig() {
635 bool PbufferGLSurfaceEGL::IsOffscreen() {
639 bool PbufferGLSurfaceEGL::SwapBuffers() {
640 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
644 gfx::Size
PbufferGLSurfaceEGL::GetSize() {
648 bool PbufferGLSurfaceEGL::Resize(const gfx::Size
& size
) {
652 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
653 GLContext
* current_context
= GLContext::GetCurrent();
655 current_context
&& current_context
->IsCurrent(this);
657 scoped_make_current
.reset(
658 new ui::ScopedMakeCurrent(current_context
, this));
664 LOG(ERROR
) << "Failed to resize pbuffer.";
671 EGLSurface
PbufferGLSurfaceEGL::GetHandle() {
675 void* PbufferGLSurfaceEGL::GetShareHandle() {
676 #if defined(OS_ANDROID)
680 if (!gfx::g_driver_egl
.ext
.b_EGL_ANGLE_query_surface_pointer
)
683 if (!gfx::g_driver_egl
.ext
.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle
)
687 if (!eglQuerySurfacePointerANGLE(g_display
,
689 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE
,
698 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
702 SurfacelessEGL::SurfacelessEGL(const gfx::Size
& size
)
706 bool SurfacelessEGL::Initialize() {
710 void SurfacelessEGL::Destroy() {
713 EGLConfig
SurfacelessEGL::GetConfig() {
717 bool SurfacelessEGL::IsOffscreen() {
721 bool SurfacelessEGL::SwapBuffers() {
722 LOG(ERROR
) << "Attempted to call SwapBuffers with SurfacelessEGL.";
726 gfx::Size
SurfacelessEGL::GetSize() {
730 bool SurfacelessEGL::Resize(const gfx::Size
& size
) {
735 EGLSurface
SurfacelessEGL::GetHandle() {
736 return EGL_NO_SURFACE
;
739 void* SurfacelessEGL::GetShareHandle() {
743 SurfacelessEGL::~SurfacelessEGL() {