MacViews: Get c/b/ui/views/tabs to build on Mac
[chromium-blink-merge.git] / ui / gl / gl_surface_egl.cc
blobb8c29d7385c62cab447cacb9011f25e241a1ae58
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/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"
26 #if defined(USE_X11)
27 extern "C" {
28 #include <X11/Xlib.h>
30 #endif
32 #if defined (USE_OZONE)
33 #include "ui/ozone/public/surface_factory_ozone.h"
34 #endif
36 #if !defined(EGL_FIXED_SIZE_ANGLE)
37 #define EGL_FIXED_SIZE_ANGLE 0x3201
38 #endif
40 #if defined(OS_WIN)
41 // From ANGLE's egl/eglext.h.
42 #if !defined(EGL_PLATFORM_ANGLE_ANGLE)
43 #define EGL_PLATFORM_ANGLE_ANGLE 0x3201
44 #endif
45 #if !defined(EGL_PLATFORM_ANGLE_TYPE_ANGLE)
46 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3202
47 #endif
48 #if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE)
49 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE 0x3206
50 #endif
51 #endif // defined(OS_WIN)
53 using ui::GetLastEGLErrorString;
55 namespace gfx {
57 namespace {
59 EGLConfig g_config;
60 EGLDisplay g_display;
61 EGLNativeDisplayType g_native_display;
63 const char* g_egl_extensions = NULL;
64 bool g_egl_create_context_robustness_supported = false;
65 bool g_egl_sync_control_supported = false;
66 bool g_egl_window_fixed_size_supported = false;
67 bool g_egl_surfaceless_context_supported = false;
69 class EGLSyncControlVSyncProvider
70 : public gfx::SyncControlVSyncProvider {
71 public:
72 explicit EGLSyncControlVSyncProvider(EGLSurface surface)
73 : SyncControlVSyncProvider(),
74 surface_(surface) {
77 virtual ~EGLSyncControlVSyncProvider() { }
79 protected:
80 virtual bool GetSyncValues(int64* system_time,
81 int64* media_stream_counter,
82 int64* swap_buffer_counter) override {
83 uint64 u_system_time, u_media_stream_counter, u_swap_buffer_counter;
84 bool result = eglGetSyncValuesCHROMIUM(
85 g_display, surface_, &u_system_time,
86 &u_media_stream_counter, &u_swap_buffer_counter) == EGL_TRUE;
87 if (result) {
88 *system_time = static_cast<int64>(u_system_time);
89 *media_stream_counter = static_cast<int64>(u_media_stream_counter);
90 *swap_buffer_counter = static_cast<int64>(u_swap_buffer_counter);
92 return result;
95 virtual bool GetMscRate(int32* numerator, int32* denominator) override {
96 return false;
99 private:
100 EGLSurface surface_;
102 DISALLOW_COPY_AND_ASSIGN(EGLSyncControlVSyncProvider);
105 } // namespace
107 GLSurfaceEGL::GLSurfaceEGL() {}
109 bool GLSurfaceEGL::InitializeOneOff() {
110 static bool initialized = false;
111 if (initialized)
112 return true;
114 g_native_display = GetPlatformDefaultEGLNativeDisplay();
116 #if defined(OS_WIN)
117 g_display = GetPlatformDisplay(g_native_display);
118 #else
119 g_display = eglGetDisplay(g_native_display);
120 #endif
122 if (!g_display) {
123 LOG(ERROR) << "eglGetDisplay failed with error " << GetLastEGLErrorString();
124 return false;
127 if (!eglInitialize(g_display, NULL, NULL)) {
128 LOG(ERROR) << "eglInitialize failed with error " << GetLastEGLErrorString();
129 return false;
132 // Choose an EGL configuration.
133 // On X this is only used for PBuffer surfaces.
134 static const EGLint kConfigAttribs[] = {
135 EGL_BUFFER_SIZE, 32,
136 EGL_ALPHA_SIZE, 8,
137 EGL_BLUE_SIZE, 8,
138 EGL_GREEN_SIZE, 8,
139 EGL_RED_SIZE, 8,
140 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
141 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
142 EGL_NONE
145 #if defined(USE_OZONE)
146 const EGLint* config_attribs =
147 ui::SurfaceFactoryOzone::GetInstance()->GetEGLSurfaceProperties(
148 kConfigAttribs);
149 #else
150 const EGLint* config_attribs = kConfigAttribs;
151 #endif
153 EGLint num_configs;
154 if (!eglChooseConfig(g_display,
155 config_attribs,
156 NULL,
158 &num_configs)) {
159 LOG(ERROR) << "eglChooseConfig failed with error "
160 << GetLastEGLErrorString();
161 return false;
164 if (num_configs == 0) {
165 LOG(ERROR) << "No suitable EGL configs found.";
166 return false;
169 if (!eglChooseConfig(g_display,
170 config_attribs,
171 &g_config,
173 &num_configs)) {
174 LOG(ERROR) << "eglChooseConfig failed with error "
175 << GetLastEGLErrorString();
176 return false;
179 g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS);
180 g_egl_create_context_robustness_supported =
181 HasEGLExtension("EGL_EXT_create_context_robustness");
182 g_egl_sync_control_supported =
183 HasEGLExtension("EGL_CHROMIUM_sync_control");
184 g_egl_window_fixed_size_supported =
185 HasEGLExtension("EGL_ANGLE_window_fixed_size");
187 // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary
188 // workaround, since code written for Android WebView takes different paths
189 // based on whether GL surface objects have underlying EGL surface handles,
190 // conflicting with the use of surfaceless. See https://crbug.com/382349
191 #if defined(OS_ANDROID)
192 DCHECK(!g_egl_surfaceless_context_supported);
193 #else
194 // Check if SurfacelessEGL is supported.
195 g_egl_surfaceless_context_supported =
196 HasEGLExtension("EGL_KHR_surfaceless_context");
197 if (g_egl_surfaceless_context_supported) {
198 // EGL_KHR_surfaceless_context is supported but ensure
199 // GL_OES_surfaceless_context is also supported. We need a current context
200 // to query for supported GL extensions.
201 scoped_refptr<GLSurface> surface = new SurfacelessEGL(Size(1, 1));
202 scoped_refptr<GLContext> context = GLContext::CreateGLContext(
203 NULL, surface.get(), PreferIntegratedGpu);
204 if (!context->MakeCurrent(surface.get()))
205 g_egl_surfaceless_context_supported = false;
207 // Ensure context supports GL_OES_surfaceless_context.
208 if (g_egl_surfaceless_context_supported) {
209 g_egl_surfaceless_context_supported = context->HasExtension(
210 "GL_OES_surfaceless_context");
211 context->ReleaseCurrent(surface.get());
214 #endif
216 initialized = true;
218 return true;
221 EGLDisplay GLSurfaceEGL::GetDisplay() {
222 return g_display;
225 EGLDisplay GLSurfaceEGL::GetHardwareDisplay() {
226 return g_display;
229 EGLNativeDisplayType GLSurfaceEGL::GetNativeDisplay() {
230 return g_native_display;
233 const char* GLSurfaceEGL::GetEGLExtensions() {
234 return g_egl_extensions;
237 bool GLSurfaceEGL::HasEGLExtension(const char* name) {
238 return ExtensionsContain(GetEGLExtensions(), name);
241 bool GLSurfaceEGL::IsCreateContextRobustnessSupported() {
242 return g_egl_create_context_robustness_supported;
245 bool GLSurfaceEGL::IsEGLSurfacelessContextSupported() {
246 return g_egl_surfaceless_context_supported;
249 GLSurfaceEGL::~GLSurfaceEGL() {}
251 #if defined(OS_WIN)
252 static const EGLint kDisplayAttribsWarp[] {
253 EGL_PLATFORM_ANGLE_TYPE_ANGLE,
254 EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE,
255 EGL_NONE
258 // static
259 EGLDisplay GLSurfaceEGL::GetPlatformDisplay(
260 EGLNativeDisplayType native_display) {
261 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseWarp)) {
262 // Check for availability of WARP via ANGLE extension.
263 bool supports_warp = false;
264 const char* no_display_extensions = eglQueryString(EGL_NO_DISPLAY,
265 EGL_EXTENSIONS);
266 // If EGL_EXT_client_extensions not supported this call to eglQueryString
267 // will return NULL.
268 if (no_display_extensions)
269 supports_warp =
270 ExtensionsContain(no_display_extensions, "ANGLE_platform_angle") &&
271 ExtensionsContain(no_display_extensions, "ANGLE_platform_angle_d3d");
273 if (!supports_warp)
274 return NULL;
276 return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, native_display,
277 kDisplayAttribsWarp);
280 return eglGetDisplay(native_display);
282 #endif
284 NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window)
285 : window_(window),
286 surface_(NULL),
287 supports_post_sub_buffer_(false),
288 config_(NULL),
289 size_(1, 1) {
290 #if defined(OS_ANDROID)
291 if (window)
292 ANativeWindow_acquire(window);
293 #endif
295 #if defined(OS_WIN)
296 RECT windowRect;
297 if (GetClientRect(window_, &windowRect))
298 size_ = gfx::Rect(windowRect).size();
299 #endif
302 bool NativeViewGLSurfaceEGL::Initialize() {
303 return Initialize(scoped_ptr<VSyncProvider>());
306 bool NativeViewGLSurfaceEGL::Initialize(
307 scoped_ptr<VSyncProvider> sync_provider) {
308 DCHECK(!surface_);
310 if (!GetDisplay()) {
311 LOG(ERROR) << "Trying to create surface with invalid display.";
312 return false;
315 std::vector<EGLint> egl_window_attributes;
317 if (g_egl_window_fixed_size_supported) {
318 egl_window_attributes.push_back(EGL_FIXED_SIZE_ANGLE);
319 egl_window_attributes.push_back(EGL_TRUE);
320 egl_window_attributes.push_back(EGL_WIDTH);
321 egl_window_attributes.push_back(size_.width());
322 egl_window_attributes.push_back(EGL_HEIGHT);
323 egl_window_attributes.push_back(size_.height());
326 if (gfx::g_driver_egl.ext.b_EGL_NV_post_sub_buffer) {
327 egl_window_attributes.push_back(EGL_POST_SUB_BUFFER_SUPPORTED_NV);
328 egl_window_attributes.push_back(EGL_TRUE);
331 egl_window_attributes.push_back(EGL_NONE);
332 // Create a surface for the native window.
333 surface_ = eglCreateWindowSurface(
334 GetDisplay(), GetConfig(), window_, &egl_window_attributes[0]);
336 if (!surface_) {
337 LOG(ERROR) << "eglCreateWindowSurface failed with error "
338 << GetLastEGLErrorString();
339 Destroy();
340 return false;
343 EGLint surfaceVal;
344 EGLBoolean retVal = eglQuerySurface(GetDisplay(),
345 surface_,
346 EGL_POST_SUB_BUFFER_SUPPORTED_NV,
347 &surfaceVal);
348 supports_post_sub_buffer_ = (surfaceVal && retVal) == EGL_TRUE;
350 if (sync_provider)
351 vsync_provider_.reset(sync_provider.release());
352 else if (g_egl_sync_control_supported)
353 vsync_provider_.reset(new EGLSyncControlVSyncProvider(surface_));
354 return true;
357 void NativeViewGLSurfaceEGL::Destroy() {
358 if (surface_) {
359 if (!eglDestroySurface(GetDisplay(), surface_)) {
360 LOG(ERROR) << "eglDestroySurface failed with error "
361 << GetLastEGLErrorString();
363 surface_ = NULL;
367 EGLConfig NativeViewGLSurfaceEGL::GetConfig() {
368 #if !defined(USE_X11)
369 return g_config;
370 #else
371 if (!config_) {
372 // Get a config compatible with the window
373 DCHECK(window_);
374 XWindowAttributes win_attribs;
375 if (!XGetWindowAttributes(GetNativeDisplay(), window_, &win_attribs)) {
376 return NULL;
379 // Try matching the window depth with an alpha channel,
380 // because we're worried the destination alpha width could
381 // constrain blending precision.
382 const int kBufferSizeOffset = 1;
383 const int kAlphaSizeOffset = 3;
384 EGLint config_attribs[] = {
385 EGL_BUFFER_SIZE, ~0,
386 EGL_ALPHA_SIZE, 8,
387 EGL_BLUE_SIZE, 8,
388 EGL_GREEN_SIZE, 8,
389 EGL_RED_SIZE, 8,
390 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
391 EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
392 EGL_NONE
394 config_attribs[kBufferSizeOffset] = win_attribs.depth;
396 EGLint num_configs;
397 if (!eglChooseConfig(g_display,
398 config_attribs,
399 &config_,
401 &num_configs)) {
402 LOG(ERROR) << "eglChooseConfig failed with error "
403 << GetLastEGLErrorString();
404 return NULL;
407 if (num_configs) {
408 EGLint config_depth;
409 if (!eglGetConfigAttrib(g_display,
410 config_,
411 EGL_BUFFER_SIZE,
412 &config_depth)) {
413 LOG(ERROR) << "eglGetConfigAttrib failed with error "
414 << GetLastEGLErrorString();
415 return NULL;
418 if (config_depth == win_attribs.depth) {
419 return config_;
423 // Try without an alpha channel.
424 config_attribs[kAlphaSizeOffset] = 0;
425 if (!eglChooseConfig(g_display,
426 config_attribs,
427 &config_,
429 &num_configs)) {
430 LOG(ERROR) << "eglChooseConfig failed with error "
431 << GetLastEGLErrorString();
432 return NULL;
435 if (num_configs == 0) {
436 LOG(ERROR) << "No suitable EGL configs found.";
437 return NULL;
440 return config_;
441 #endif
444 bool NativeViewGLSurfaceEGL::IsOffscreen() {
445 return false;
448 bool NativeViewGLSurfaceEGL::SwapBuffers() {
449 TRACE_EVENT2("gpu", "NativeViewGLSurfaceEGL:RealSwapBuffers",
450 "width", GetSize().width(),
451 "height", GetSize().height());
453 if (!eglSwapBuffers(GetDisplay(), surface_)) {
454 DVLOG(1) << "eglSwapBuffers failed with error "
455 << GetLastEGLErrorString();
456 return false;
459 return true;
462 gfx::Size NativeViewGLSurfaceEGL::GetSize() {
463 EGLint width;
464 EGLint height;
465 if (!eglQuerySurface(GetDisplay(), surface_, EGL_WIDTH, &width) ||
466 !eglQuerySurface(GetDisplay(), surface_, EGL_HEIGHT, &height)) {
467 NOTREACHED() << "eglQuerySurface failed with error "
468 << GetLastEGLErrorString();
469 return gfx::Size();
472 return gfx::Size(width, height);
475 bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size) {
476 if (size == GetSize())
477 return true;
479 size_ = size;
481 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
482 GLContext* current_context = GLContext::GetCurrent();
483 bool was_current =
484 current_context && current_context->IsCurrent(this);
485 if (was_current) {
486 scoped_make_current.reset(
487 new ui::ScopedMakeCurrent(current_context, this));
488 current_context->ReleaseCurrent(this);
491 Destroy();
493 if (!Initialize()) {
494 LOG(ERROR) << "Failed to resize window.";
495 return false;
498 return true;
501 bool NativeViewGLSurfaceEGL::Recreate() {
502 Destroy();
503 if (!Initialize()) {
504 LOG(ERROR) << "Failed to create surface.";
505 return false;
507 return true;
510 EGLSurface NativeViewGLSurfaceEGL::GetHandle() {
511 return surface_;
514 bool NativeViewGLSurfaceEGL::SupportsPostSubBuffer() {
515 return supports_post_sub_buffer_;
518 bool NativeViewGLSurfaceEGL::PostSubBuffer(
519 int x, int y, int width, int height) {
520 DCHECK(supports_post_sub_buffer_);
521 if (!eglPostSubBufferNV(GetDisplay(), surface_, x, y, width, height)) {
522 DVLOG(1) << "eglPostSubBufferNV failed with error "
523 << GetLastEGLErrorString();
524 return false;
526 return true;
529 VSyncProvider* NativeViewGLSurfaceEGL::GetVSyncProvider() {
530 return vsync_provider_.get();
533 NativeViewGLSurfaceEGL::~NativeViewGLSurfaceEGL() {
534 Destroy();
535 #if defined(OS_ANDROID)
536 if (window_)
537 ANativeWindow_release(window_);
538 #endif
541 PbufferGLSurfaceEGL::PbufferGLSurfaceEGL(const gfx::Size& size)
542 : size_(size),
543 surface_(NULL) {
544 // Some implementations of Pbuffer do not support having a 0 size. For such
545 // cases use a (1, 1) surface.
546 if (size_.GetArea() == 0)
547 size_.SetSize(1, 1);
550 bool PbufferGLSurfaceEGL::Initialize() {
551 EGLSurface old_surface = surface_;
553 EGLDisplay display = GetDisplay();
554 if (!display) {
555 LOG(ERROR) << "Trying to create surface with invalid display.";
556 return false;
559 // Allocate the new pbuffer surface before freeing the old one to ensure
560 // they have different addresses. If they have the same address then a
561 // future call to MakeCurrent might early out because it appears the current
562 // context and surface have not changed.
563 const EGLint pbuffer_attribs[] = {
564 EGL_WIDTH, size_.width(),
565 EGL_HEIGHT, size_.height(),
566 EGL_NONE
569 EGLSurface new_surface = eglCreatePbufferSurface(display,
570 GetConfig(),
571 pbuffer_attribs);
572 if (!new_surface) {
573 LOG(ERROR) << "eglCreatePbufferSurface failed with error "
574 << GetLastEGLErrorString();
575 return false;
578 if (old_surface)
579 eglDestroySurface(display, old_surface);
581 surface_ = new_surface;
582 return true;
585 void PbufferGLSurfaceEGL::Destroy() {
586 if (surface_) {
587 if (!eglDestroySurface(GetDisplay(), surface_)) {
588 LOG(ERROR) << "eglDestroySurface failed with error "
589 << GetLastEGLErrorString();
591 surface_ = NULL;
595 EGLConfig PbufferGLSurfaceEGL::GetConfig() {
596 return g_config;
599 bool PbufferGLSurfaceEGL::IsOffscreen() {
600 return true;
603 bool PbufferGLSurfaceEGL::SwapBuffers() {
604 NOTREACHED() << "Attempted to call SwapBuffers on a PbufferGLSurfaceEGL.";
605 return false;
608 gfx::Size PbufferGLSurfaceEGL::GetSize() {
609 return size_;
612 bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size) {
613 if (size == size_)
614 return true;
616 scoped_ptr<ui::ScopedMakeCurrent> scoped_make_current;
617 GLContext* current_context = GLContext::GetCurrent();
618 bool was_current =
619 current_context && current_context->IsCurrent(this);
620 if (was_current) {
621 scoped_make_current.reset(
622 new ui::ScopedMakeCurrent(current_context, this));
625 size_ = size;
627 if (!Initialize()) {
628 LOG(ERROR) << "Failed to resize pbuffer.";
629 return false;
632 return true;
635 EGLSurface PbufferGLSurfaceEGL::GetHandle() {
636 return surface_;
639 void* PbufferGLSurfaceEGL::GetShareHandle() {
640 #if defined(OS_ANDROID)
641 NOTREACHED();
642 return NULL;
643 #else
644 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_query_surface_pointer)
645 return NULL;
647 if (!gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle)
648 return NULL;
650 void* handle;
651 if (!eglQuerySurfacePointerANGLE(g_display,
652 GetHandle(),
653 EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
654 &handle)) {
655 return NULL;
658 return handle;
659 #endif
662 PbufferGLSurfaceEGL::~PbufferGLSurfaceEGL() {
663 Destroy();
666 SurfacelessEGL::SurfacelessEGL(const gfx::Size& size)
667 : size_(size) {
670 bool SurfacelessEGL::Initialize() {
671 return true;
674 void SurfacelessEGL::Destroy() {
677 EGLConfig SurfacelessEGL::GetConfig() {
678 return g_config;
681 bool SurfacelessEGL::IsOffscreen() {
682 return true;
685 bool SurfacelessEGL::SwapBuffers() {
686 LOG(ERROR) << "Attempted to call SwapBuffers with SurfacelessEGL.";
687 return false;
690 gfx::Size SurfacelessEGL::GetSize() {
691 return size_;
694 bool SurfacelessEGL::Resize(const gfx::Size& size) {
695 size_ = size;
696 return true;
699 EGLSurface SurfacelessEGL::GetHandle() {
700 return EGL_NO_SURFACE;
703 void* SurfacelessEGL::GetShareHandle() {
704 return NULL;
707 SurfacelessEGL::~SurfacelessEGL() {
710 } // namespace gfx