Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / gl / gl_surface_egl.cc
blob5a0538e0adda45cc8264c9fc78aea58e4b2e00ee
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>
9 #endif
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"
25 #if defined(USE_X11)
26 extern "C" {
27 #include <X11/Xlib.h>
29 #endif
31 #if defined (USE_OZONE)
32 #include "ui/ozone/public/surface_factory_ozone.h"
33 #endif
35 #if !defined(EGL_FIXED_SIZE_ANGLE)
36 #define EGL_FIXED_SIZE_ANGLE 0x3201
37 #endif
39 using ui::GetLastEGLErrorString;
41 namespace gfx {
43 namespace {
45 EGLConfig g_config;
46 EGLDisplay g_display;
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 {
57 public:
58 explicit EGLSyncControlVSyncProvider(EGLSurface surface)
59 : SyncControlVSyncProvider(),
60 surface_(surface) {
63 virtual ~EGLSyncControlVSyncProvider() { }
65 protected:
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;
73 if (result) {
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);
78 return result;
81 virtual bool GetMscRate(int32* numerator, int32* denominator) OVERRIDE {
82 return false;
85 private:
86 EGLSurface surface_;
88 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider);
91 } // namespace
93 GLSurfaceEGL::GLSurfaceEGL() {}
95 bool GLSurfaceEGL::InitializeOneOff() {
96 static bool initialized = false;
97 if (initialized)
98 return true;
100 g_native_display = GetPlatformDefaultEGLNativeDisplay();
101 g_display = eglGetDisplay(g_native_display);
102 if (!g_display) {
103 LOG(ERROR) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
104 return false;
107 if (!eglInitialize(g_display, NULL, NULL)) {
108 LOG(ERROR) << "eglInitialize failed with error " << GetLastEGLErrorString();
109 return false;
112 // Choose an EGL configuration.
113 // On X this is only used for PBuffer surfaces.
114 static const EGLint kConfigAttribs[] = {
115 EGL_BUFFER_SIZE, 32,
116 EGL_ALPHA_SIZE, 8,
117 EGL_BLUE_SIZE, 8,
118 EGL_GREEN_SIZE, 8,
119 EGL_RED_SIZE, 8,
120 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
121 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
122 EGL_NONE
125 #if defined(USE_OZONE)
126 const EGLint* config_attribs =
127 ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
128 kConfigAttribs);
129 #else
130 const EGLint* config_attribs = kConfigAttribs;
131 #endif
133 EGLint num_configs;
134 if (!eglChooseConfig(g_display,
135 config_attribs,
136 NULL,
138 &num_configs)) {
139 LOG(ERROR) << "eglChooseConfig failed with error "
140 << GetLastEGLErrorString();
141 return false;
144 if (num_configs == 0) {
145 LOG(ERROR) << "No suitable EGL configs found.";
146 return false;
149 if (!eglChooseConfig(g_display,
150 config_attribs,
151 &g_config,
153 &num_configs)) {
154 LOG(ERROR) << "eglChooseConfig failed with error "
155 << GetLastEGLErrorString();
156 return false;
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);
173 #else
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());
194 #endif
196 initialized = true;
198 return true;
201 EGLDisplay GLSurfaceEGL::GetDisplay() {
202 return g_display;
205 EGLDisplay GLSurfaceEGL::GetHardwareDisplay() {
206 return g_display;
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)
232 : window_(window),
233 surface_(NULL),
234 supports_post_sub_buffer_(false),
235 config_(NULL),
236 size_(1, 1) {
237 #if defined(OS_ANDROID)
238 if (window)
239 ANativeWindow_acquire(window);
240 #endif
242 #if defined(OS_WIN)
243 RECT windowRect;
244 if (GetClientRect(window_, &windowRect))
245 size_ = gfx::Rect(windowRect).size();
246 #endif
249 bool NativeViewGLSurfaceEGL::Initialize() {
250 return Initialize(scoped_ptr<VSyncProvider>());
253 bool NativeViewGLSurfaceEGL::Initialize(
254 scoped_ptr<VSyncProvider> sync_provider) {
255 DCHECK(!surface_);
257 if (!GetDisplay()) {
258 LOG(ERROR) << "Trying to create surface with invalid display.";
259 return false;
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]);
283 if (!surface_) {
284 LOG(ERROR) << "eglCreateWindowSurface failed with error "
285 << GetLastEGLErrorString();
286 Destroy();
287 return false;
290 EGLint surfaceVal;
291 EGLBoolean retVal = eglQuerySurface(GetDisplay(),
292 surface_,
293 EGL_POST_SUB_BUFFER_SUPPORTED_NV,
294 &surfaceVal);
295 supports_post_sub_buffer_ = (surfaceVal && retVal) == EGL_TRUE;
297 if (sync_provider)
298 vsync_provider_.reset(sync_provider.release());
299 else if (g_egl_sync_control_supported)
300 vsync_provider_.reset(new EGLSyncControlVSyncProvider(surface_));
301 return true;
304 void NativeViewGLSurfaceEGL::Destroy() {
305 if (surface_) {
306 if (!eglDestroySurface(GetDisplay(), surface_)) {
307 LOG(ERROR) << "eglDestroySurface failed with error "
308 << GetLastEGLErrorString();
310 surface_ = NULL;
314 EGLConfig NativeViewGLSurfaceEGL::GetConfig() {
315 #if !defined(USE_X11)
316 return g_config;
317 #else
318 if (!config_) {
319 // Get a config compatible with the window
320 DCHECK(window_);
321 XWindowAttributes win_attribs;
322 if (!XGetWindowAttributes(GetNativeDisplay(), window_, &win_attribs)) {
323 return NULL;
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[] = {
332 EGL_BUFFER_SIZE, ~0,
333 EGL_ALPHA_SIZE, 8,
334 EGL_BLUE_SIZE, 8,
335 EGL_GREEN_SIZE, 8,
336 EGL_RED_SIZE, 8,
337 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
338 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
339 EGL_NONE
341 config_attribs[kBufferSizeOffset] = win_attribs.depth;
343 EGLint num_configs;
344 if (!eglChooseConfig(g_display,
345 config_attribs,
346 &config_,
348 &num_configs)) {
349 LOG(ERROR) << "eglChooseConfig failed with error "
350 << GetLastEGLErrorString();
351 return NULL;
354 if (num_configs) {
355 EGLint config_depth;
356 if (!eglGetConfigAttrib(g_display,
357 config_,
358 EGL_BUFFER_SIZE,
359 &config_depth)) {
360 LOG(ERROR) << "eglGetConfigAttrib failed with error "
361 << GetLastEGLErrorString();
362 return NULL;
365 if (config_depth == win_attribs.depth) {
366 return config_;
370 // Try without an alpha channel.
371 config_attribs[kAlphaSizeOffset] = 0;
372 if (!eglChooseConfig(g_display,
373 config_attribs,
374 &config_,
376 &num_configs)) {
377 LOG(ERROR) << "eglChooseConfig failed with error "
378 << GetLastEGLErrorString();
379 return NULL;
382 if (num_configs == 0) {
383 LOG(ERROR) << "No suitable EGL configs found.";
384 return NULL;
387 return config_;
388 #endif
391 bool NativeViewGLSurfaceEGL::IsOffscreen() {
392 return false;
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();
403 return false;
406 return true;
409 gfx::Size NativeViewGLSurfaceEGL::GetSize() {
410 EGLint width;
411 EGLint height;
412 if (!eglQuerySurface(GetDisplay(), surface_, EGL_WIDTH, &width) ||
413 !eglQuerySurface(GetDisplay(), surface_, EGL_HEIGHT, &height)) {
414 NOTREACHED() << "eglQuerySurface failed with error "
415 << GetLastEGLErrorString();
416 return gfx::Size();
419 return gfx::Size(width, height);
422 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size) {
423 if (size == GetSize())
424 return true;
426 size_ = size;
428 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
429 GLContext* current_context = GLContext::GetCurrent();
430 bool was_current =
431 current_context && current_context->IsCurrent(this);
432 if (was_current) {
433 scoped_make_current.reset(
434 new ui::ScopedMakeCurrent(current_context, this));
435 current_context->ReleaseCurrent(this);
438 Destroy();
440 if (!Initialize()) {
441 LOG(ERROR) << "Failed to resize window.";
442 return false;
445 return true;
448 bool NativeViewGLSurfaceEGL::Recreate() {
449 Destroy();
450 if (!Initialize()) {
451 LOG(ERROR) << "Failed to create surface.";
452 return false;
454 return true;
457 EGLSurface NativeViewGLSurfaceEGL::GetHandle() {
458 return surface_;
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();
471 return false;
473 return true;
476 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() {
477 return vsync_provider_.get();
480 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
481 Destroy();
482 #if defined(OS_ANDROID)
483 if (window_)
484 ANativeWindow_release(window_);
485 #endif
488 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size)
489 : size_(size),
490 surface_(NULL) {
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)
494 size_.SetSize(1, 1);
497 bool PbufferGLSurfaceEGL::Initialize() {
498 EGLSurface old_surface = surface_;
500 EGLDisplay display = GetDisplay();
501 if (!display) {
502 LOG(ERROR) << "Trying to create surface with invalid display.";
503 return false;
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(),
513 EGL_NONE
516 EGLSurface new_surface = eglCreatePbufferSurface(display,
517 GetConfig(),
518 pbuffer_attribs);
519 if (!new_surface) {
520 LOG(ERROR) << "eglCreatePbufferSurface failed with error "
521 << GetLastEGLErrorString();
522 return false;
525 if (old_surface)
526 eglDestroySurface(display, old_surface);
528 surface_ = new_surface;
529 return true;
532 void PbufferGLSurfaceEGL::Destroy() {
533 if (surface_) {
534 if (!eglDestroySurface(GetDisplay(), surface_)) {
535 LOG(ERROR) << "eglDestroySurface failed with error "
536 << GetLastEGLErrorString();
538 surface_ = NULL;
542 EGLConfig PbufferGLSurfaceEGL::GetConfig() {
543 return g_config;
546 bool PbufferGLSurfaceEGL::IsOffscreen() {
547 return true;
550 bool PbufferGLSurfaceEGL::SwapBuffers() {
551 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
552 return false;
555 gfx::Size PbufferGLSurfaceEGL::GetSize() {
556 return size_;
559 bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size) {
560 if (size == size_)
561 return true;
563 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
564 GLContext* current_context = GLContext::GetCurrent();
565 bool was_current =
566 current_context && current_context->IsCurrent(this);
567 if (was_current) {
568 scoped_make_current.reset(
569 new ui::ScopedMakeCurrent(current_context, this));
572 size_ = size;
574 if (!Initialize()) {
575 LOG(ERROR) << "Failed to resize pbuffer.";
576 return false;
579 return true;
582 EGLSurface PbufferGLSurfaceEGL::GetHandle() {
583 return surface_;
586 void* PbufferGLSurfaceEGL::GetShareHandle() {
587 #if defined(OS_ANDROID)
588 NOTREACHED();
589 return NULL;
590 #else
591 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_query_surface_pointer)
592 return NULL;
594 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle)
595 return NULL;
597 void* handle;
598 if (!eglQuerySurfacePointerANGLE(g_display,
599 GetHandle(),
600 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
601 &handle)) {
602 return NULL;
605 return handle;
606 #endif
609 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
610 Destroy();
613 SurfacelessEGL::SurfacelessEGL(const gfx::Size& size)
614 : size_(size) {
617 bool SurfacelessEGL::Initialize() {
618 return true;
621 void SurfacelessEGL::Destroy() {
624 EGLConfig SurfacelessEGL::GetConfig() {
625 return g_config;
628 bool SurfacelessEGL::IsOffscreen() {
629 return true;
632 bool SurfacelessEGL::SwapBuffers() {
633 LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL.";
634 return false;
637 gfx::Size SurfacelessEGL::GetSize() {
638 return size_;
641 bool SurfacelessEGL::Resize(const gfx::Size& size) {
642 size_ = size;
643 return true;
646 EGLSurface SurfacelessEGL::GetHandle() {
647 return EGL_NO_SURFACE;
650 void* SurfacelessEGL::GetShareHandle() {
651 return NULL;
654 SurfacelessEGL::~SurfacelessEGL() {
657 } // namespace gfx