// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gl/gl_surface.h" #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_image.h" #include "ui/gl/gl_image_linux_dma_buffer.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_osmesa.h" #include "ui/gl/gl_surface_stub.h" #include "ui/gl/scoped_binders.h" #include "ui/gl/scoped_make_current.h" #include "ui/ozone/public/native_pixmap.h" #include "ui/ozone/public/surface_factory_ozone.h" #include "ui/ozone/public/surface_ozone_egl.h" namespace gfx { namespace { // A thin wrapper around GLSurfaceEGL that owns the EGLNativeWindow class GL_EXPORT GLSurfaceOzoneEGL : public NativeViewGLSurfaceEGL { public: GLSurfaceOzoneEGL(scoped_ptr ozone_surface, AcceleratedWidget widget) : NativeViewGLSurfaceEGL(ozone_surface->GetNativeWindow()), ozone_surface_(ozone_surface.Pass()), widget_(widget) {} bool Initialize() override { return Initialize(ozone_surface_->CreateVSyncProvider()); } bool Resize(const gfx::Size& size) override { if (!ozone_surface_->ResizeNativeWindow(size)) { if (!ReinitializeNativeSurface() || !ozone_surface_->ResizeNativeWindow(size)) return false; } return NativeViewGLSurfaceEGL::Resize(size); } bool SwapBuffers() override { if (!NativeViewGLSurfaceEGL::SwapBuffers()) return false; return ozone_surface_->OnSwapBuffers(); } bool ScheduleOverlayPlane(int z_order, OverlayTransform transform, GLImage* image, const Rect& bounds_rect, const RectF& crop_rect) override { return image->ScheduleOverlayPlane( widget_, z_order, transform, bounds_rect, crop_rect); } private: using NativeViewGLSurfaceEGL::Initialize; ~GLSurfaceOzoneEGL() override { Destroy(); // EGL surface must be destroyed before SurfaceOzone } bool ReinitializeNativeSurface() { scoped_ptr scoped_make_current; GLContext* current_context = GLContext::GetCurrent(); bool was_current = current_context && current_context->IsCurrent(this); if (was_current) { scoped_make_current.reset( new ui::ScopedMakeCurrent(current_context, this)); } Destroy(); ozone_surface_ = ui::SurfaceFactoryOzone::GetInstance()->CreateEGLSurfaceForWidget( widget_).Pass(); if (!ozone_surface_) { LOG(ERROR) << "Failed to create native surface."; return false; } window_ = ozone_surface_->GetNativeWindow(); if (!Initialize()) { LOG(ERROR) << "Failed to initialize."; return false; } return true; } // The native surface. Deleting this is allowed to free the EGLNativeWindow. scoped_ptr ozone_surface_; AcceleratedWidget widget_; DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneEGL); }; class GL_EXPORT GLSurfaceOzoneSurfaceless : public SurfacelessEGL { public: GLSurfaceOzoneSurfaceless(scoped_ptr ozone_surface, AcceleratedWidget widget) : SurfacelessEGL(gfx::Size()), ozone_surface_(ozone_surface.Pass()), widget_(widget), has_implicit_external_sync_( HasEGLExtension("EGL_ARM_implicit_external_sync")) {} bool Initialize() override { if (!SurfacelessEGL::Initialize()) return false; vsync_provider_ = ozone_surface_->CreateVSyncProvider(); if (!vsync_provider_) return false; return true; } bool Resize(const gfx::Size& size) override { if (!ozone_surface_->ResizeNativeWindow(size)) return false; return SurfacelessEGL::Resize(size); } bool SwapBuffers() override { if (!Flush()) return false; return ozone_surface_->OnSwapBuffers(); } bool ScheduleOverlayPlane(int z_order, OverlayTransform transform, GLImage* image, const Rect& bounds_rect, const RectF& crop_rect) override { return image->ScheduleOverlayPlane( widget_, z_order, transform, bounds_rect, crop_rect); } bool IsOffscreen() override { return false; } VSyncProvider* GetVSyncProvider() override { return vsync_provider_.get(); } bool SupportsPostSubBuffer() override { return true; } bool PostSubBuffer(int x, int y, int width, int height) override { // The actual sub buffer handling is handled at higher layers. SwapBuffers(); return true; } bool SwapBuffersAsync(const SwapCompletionCallback& callback) override { if (!Flush()) return false; return ozone_surface_->OnSwapBuffersAsync(callback); } bool PostSubBufferAsync(int x, int y, int width, int height, const SwapCompletionCallback& callback) override { return SwapBuffersAsync(callback); } protected: ~GLSurfaceOzoneSurfaceless() override { Destroy(); // EGL surface must be destroyed before SurfaceOzone } bool Flush() { glFlush(); // TODO: crbug.com/462360 the following should be replaced by a per surface // flush as it gets implemented in GL drivers. if (has_implicit_external_sync_) { const EGLint attrib_list[] = { EGL_SYNC_CONDITION_KHR, EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM, EGL_NONE}; EGLSyncKHR fence = eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR, attrib_list); if (fence) { // TODO(dbehr): piman@ suggests we could improve here by moving // following wait to right before drmModePageFlip crbug.com/456417. eglClientWaitSyncKHR(GetDisplay(), fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR); eglDestroySyncKHR(GetDisplay(), fence); } else { return false; } } else if (ozone_surface_->IsUniversalDisplayLinkDevice()) { glFinish(); } return true; } // The native surface. Deleting this is allowed to free the EGLNativeWindow. scoped_ptr ozone_surface_; AcceleratedWidget widget_; scoped_ptr vsync_provider_; bool has_implicit_external_sync_; DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfaceless); }; // This provides surface-like semantics implemented through surfaceless. // A framebuffer is bound automatically. class GL_EXPORT GLSurfaceOzoneSurfacelessSurfaceImpl : public GLSurfaceOzoneSurfaceless { public: GLSurfaceOzoneSurfacelessSurfaceImpl( scoped_ptr ozone_surface, AcceleratedWidget widget) : GLSurfaceOzoneSurfaceless(ozone_surface.Pass(), widget), fbo_(0), current_surface_(0) { for (auto& texture : textures_) texture = 0; } unsigned int GetBackingFrameBufferObject() override { return fbo_; } bool OnMakeCurrent(GLContext* context) override { if (!fbo_) { glGenFramebuffersEXT(1, &fbo_); if (!fbo_) return false; glGenTextures(arraysize(textures_), textures_); if (!CreatePixmaps()) return false; } BindFramebuffer(); glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_); return SurfacelessEGL::OnMakeCurrent(context); } bool Resize(const gfx::Size& size) override { if (size == GetSize()) return true; return GLSurfaceOzoneSurfaceless::Resize(size) && CreatePixmaps(); } bool SupportsPostSubBuffer() override { return false; } bool SwapBuffers() override { if (!images_[current_surface_]->ScheduleOverlayPlane( widget_, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE, gfx::Rect(GetSize()), gfx::RectF(1, 1))) return false; if (!GLSurfaceOzoneSurfaceless::SwapBuffers()) return false; current_surface_ ^= 1; BindFramebuffer(); return true; } bool SwapBuffersAsync(const SwapCompletionCallback& callback) override { if (!images_[current_surface_]->ScheduleOverlayPlane( widget_, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE, gfx::Rect(GetSize()), gfx::RectF(1, 1))) return false; if (!GLSurfaceOzoneSurfaceless::SwapBuffersAsync(callback)) return false; current_surface_ ^= 1; BindFramebuffer(); return true; } void Destroy() override { GLContext* current_context = GLContext::GetCurrent(); DCHECK(current_context && current_context->IsCurrent(this)); glBindFramebufferEXT(GL_FRAMEBUFFER, 0); if (fbo_) { glDeleteTextures(arraysize(textures_), textures_); for (auto& texture : textures_) texture = 0; glDeleteFramebuffersEXT(1, &fbo_); fbo_ = 0; } for (auto image : images_) { if (image) image->Destroy(true); } } private: class SurfaceImage : public GLImageLinuxDMABuffer { public: SurfaceImage(const gfx::Size& size, unsigned internalformat) : GLImageLinuxDMABuffer(size, internalformat) {} bool Initialize(scoped_refptr pixmap, gfx::GpuMemoryBuffer::Format format) { base::FileDescriptor handle(pixmap->GetDmaBufFd(), false); if (!GLImageLinuxDMABuffer::Initialize(handle, format, pixmap->GetDmaBufPitch())) return false; pixmap_ = pixmap; return true; } bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, const gfx::RectF& crop_rect) override { return ui::SurfaceFactoryOzone::GetInstance()->ScheduleOverlayPlane( widget, z_order, transform, pixmap_, bounds_rect, crop_rect); } private: ~SurfaceImage() override {} scoped_refptr pixmap_; }; ~GLSurfaceOzoneSurfacelessSurfaceImpl() override { DCHECK(!fbo_); for (size_t i = 0; i < arraysize(textures_); i++) DCHECK(!textures_[i]) << "texture " << i << " not released"; } void BindFramebuffer() { ScopedFrameBufferBinder fb(fbo_); glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures_[current_surface_], 0); } bool CreatePixmaps() { if (!fbo_) return true; for (size_t i = 0; i < arraysize(textures_); i++) { scoped_refptr pixmap = ui::SurfaceFactoryOzone::GetInstance()->CreateNativePixmap( widget_, GetSize(), ui::SurfaceFactoryOzone::RGBA_8888, ui::SurfaceFactoryOzone::SCANOUT); if (!pixmap) return false; scoped_refptr image = new SurfaceImage(GetSize(), GL_RGBA); if (!image->Initialize(pixmap, gfx::GpuMemoryBuffer::Format::BGRA_8888)) return false; images_[i] = image; // Bind image to texture. ScopedTextureBinder binder(GL_TEXTURE_2D, textures_[i]); if (!images_[i]->BindTexImage(GL_TEXTURE_2D)) return false; } return true; } GLuint fbo_; GLuint textures_[2]; scoped_refptr images_[2]; int current_surface_; DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfacelessSurfaceImpl); }; } // namespace // static bool GLSurface::InitializeOneOffInternal() { switch (GetGLImplementation()) { case kGLImplementationEGLGLES2: if (!GLSurfaceEGL::InitializeOneOff()) { LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; return false; } return true; case kGLImplementationOSMesaGL: case kGLImplementationMockGL: return true; default: return false; } } // static scoped_refptr GLSurface::CreateSurfacelessViewGLSurface( gfx::AcceleratedWidget window) { if (GetGLImplementation() == kGLImplementationEGLGLES2 && window != kNullAcceleratedWidget && GLSurfaceEGL::IsEGLSurfacelessContextSupported() && ui::SurfaceFactoryOzone::GetInstance()->CanShowPrimaryPlaneAsOverlay()) { scoped_ptr surface_ozone = ui::SurfaceFactoryOzone::GetInstance() ->CreateSurfacelessEGLSurfaceForWidget(window); if (!surface_ozone) return nullptr; scoped_refptr surface; surface = new GLSurfaceOzoneSurfaceless(surface_ozone.Pass(), window); if (surface->Initialize()) return surface; } return nullptr; } // static scoped_refptr GLSurface::CreateViewGLSurface( gfx::AcceleratedWidget window) { if (GetGLImplementation() == kGLImplementationOSMesaGL) { scoped_refptr surface(new GLSurfaceOSMesaHeadless()); if (!surface->Initialize()) return NULL; return surface; } DCHECK(GetGLImplementation() == kGLImplementationEGLGLES2); if (window != kNullAcceleratedWidget) { scoped_refptr surface; if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() && ui::SurfaceFactoryOzone::GetInstance() ->CanShowPrimaryPlaneAsOverlay()) { scoped_ptr surface_ozone = ui::SurfaceFactoryOzone::GetInstance() ->CreateSurfacelessEGLSurfaceForWidget(window); if (!surface_ozone) return NULL; surface = new GLSurfaceOzoneSurfacelessSurfaceImpl(surface_ozone.Pass(), window); } else { scoped_ptr surface_ozone = ui::SurfaceFactoryOzone::GetInstance()->CreateEGLSurfaceForWidget( window); if (!surface_ozone) return NULL; surface = new GLSurfaceOzoneEGL(surface_ozone.Pass(), window); } if (!surface->Initialize()) return NULL; return surface; } else { scoped_refptr surface = new GLSurfaceStub(); if (surface->Initialize()) return surface; } return NULL; } // static scoped_refptr GLSurface::CreateOffscreenGLSurface( const gfx::Size& size) { switch (GetGLImplementation()) { case kGLImplementationOSMesaGL: { scoped_refptr surface( new GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA, size)); if (!surface->Initialize()) return NULL; return surface; } case kGLImplementationEGLGLES2: { scoped_refptr surface; if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() && (size.width() == 0 && size.height() == 0)) { surface = new SurfacelessEGL(size); } else surface = new PbufferGLSurfaceEGL(size); if (!surface->Initialize()) return NULL; return surface; } default: NOTREACHED(); return NULL; } } EGLNativeDisplayType GetPlatformDefaultEGLNativeDisplay() { return ui::SurfaceFactoryOzone::GetInstance()->GetNativeDisplay(); } } // namespace gfx