1 // Copyright 2014 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.h"
8 #include "base/logging.h"
9 #include "base/memory/ref_counted.h"
10 #include "ui/gfx/native_widget_types.h"
11 #include "ui/gl/gl_context.h"
12 #include "ui/gl/gl_image.h"
13 #include "ui/gl/gl_image_linux_dma_buffer.h"
14 #include "ui/gl/gl_implementation.h"
15 #include "ui/gl/gl_surface_egl.h"
16 #include "ui/gl/gl_surface_osmesa.h"
17 #include "ui/gl/gl_surface_stub.h"
18 #include "ui/gl/scoped_binders.h"
19 #include "ui/gl/scoped_make_current.h"
20 #include "ui/ozone/public/native_pixmap.h"
21 #include "ui/ozone/public/surface_factory_ozone.h"
22 #include "ui/ozone/public/surface_ozone_egl.h"
28 // A thin wrapper around GLSurfaceEGL that owns the EGLNativeWindow
29 class GL_EXPORT GLSurfaceOzoneEGL
: public NativeViewGLSurfaceEGL
{
31 GLSurfaceOzoneEGL(scoped_ptr
<ui::SurfaceOzoneEGL
> ozone_surface
,
32 AcceleratedWidget widget
)
33 : NativeViewGLSurfaceEGL(ozone_surface
->GetNativeWindow()),
34 ozone_surface_(ozone_surface
.Pass()),
37 bool Initialize() override
{
38 return Initialize(ozone_surface_
->CreateVSyncProvider());
40 bool Resize(const gfx::Size
& size
) override
{
41 if (!ozone_surface_
->ResizeNativeWindow(size
)) {
42 if (!ReinitializeNativeSurface() ||
43 !ozone_surface_
->ResizeNativeWindow(size
))
47 return NativeViewGLSurfaceEGL::Resize(size
);
49 bool SwapBuffers() override
{
50 if (!NativeViewGLSurfaceEGL::SwapBuffers())
53 return ozone_surface_
->OnSwapBuffers();
55 bool ScheduleOverlayPlane(int z_order
,
56 OverlayTransform transform
,
58 const Rect
& bounds_rect
,
59 const RectF
& crop_rect
) override
{
60 return image
->ScheduleOverlayPlane(
61 widget_
, z_order
, transform
, bounds_rect
, crop_rect
);
65 using NativeViewGLSurfaceEGL::Initialize
;
67 ~GLSurfaceOzoneEGL() override
{
68 Destroy(); // EGL surface must be destroyed before SurfaceOzone
71 bool ReinitializeNativeSurface() {
72 scoped_ptr
<ui::ScopedMakeCurrent
> scoped_make_current
;
73 GLContext
* current_context
= GLContext::GetCurrent();
75 current_context
&& current_context
->IsCurrent(this);
77 scoped_make_current
.reset(
78 new ui::ScopedMakeCurrent(current_context
, this));
83 ui::SurfaceFactoryOzone::GetInstance()->CreateEGLSurfaceForWidget(
85 if (!ozone_surface_
) {
86 LOG(ERROR
) << "Failed to create native surface.";
90 window_
= ozone_surface_
->GetNativeWindow();
92 LOG(ERROR
) << "Failed to initialize.";
99 // The native surface. Deleting this is allowed to free the EGLNativeWindow.
100 scoped_ptr
<ui::SurfaceOzoneEGL
> ozone_surface_
;
101 AcceleratedWidget widget_
;
103 DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneEGL
);
106 class GL_EXPORT GLSurfaceOzoneSurfaceless
: public SurfacelessEGL
{
108 GLSurfaceOzoneSurfaceless(scoped_ptr
<ui::SurfaceOzoneEGL
> ozone_surface
,
109 AcceleratedWidget widget
)
110 : SurfacelessEGL(gfx::Size()),
111 ozone_surface_(ozone_surface
.Pass()),
113 has_implicit_external_sync_(
114 HasEGLExtension("EGL_ARM_implicit_external_sync")) {}
116 bool Initialize() override
{
117 if (!SurfacelessEGL::Initialize())
119 vsync_provider_
= ozone_surface_
->CreateVSyncProvider();
120 if (!vsync_provider_
)
124 bool Resize(const gfx::Size
& size
) override
{
125 if (!ozone_surface_
->ResizeNativeWindow(size
))
128 return SurfacelessEGL::Resize(size
);
130 bool SwapBuffers() override
{
133 return ozone_surface_
->OnSwapBuffers();
135 bool ScheduleOverlayPlane(int z_order
,
136 OverlayTransform transform
,
138 const Rect
& bounds_rect
,
139 const RectF
& crop_rect
) override
{
140 return image
->ScheduleOverlayPlane(
141 widget_
, z_order
, transform
, bounds_rect
, crop_rect
);
143 bool IsOffscreen() override
{ return false; }
144 VSyncProvider
* GetVSyncProvider() override
{ return vsync_provider_
.get(); }
145 bool SupportsPostSubBuffer() override
{ return true; }
146 bool PostSubBuffer(int x
, int y
, int width
, int height
) override
{
147 // The actual sub buffer handling is handled at higher layers.
151 bool SwapBuffersAsync(const SwapCompletionCallback
& callback
) override
{
154 return ozone_surface_
->OnSwapBuffersAsync(callback
);
156 bool PostSubBufferAsync(int x
,
160 const SwapCompletionCallback
& callback
) override
{
161 return SwapBuffersAsync(callback
);
165 ~GLSurfaceOzoneSurfaceless() override
{
166 Destroy(); // EGL surface must be destroyed before SurfaceOzone
171 // TODO: crbug.com/462360 the following should be replaced by a per surface
172 // flush as it gets implemented in GL drivers.
173 if (has_implicit_external_sync_
) {
174 const EGLint attrib_list
[] = {
175 EGL_SYNC_CONDITION_KHR
,
176 EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM
,
179 eglCreateSyncKHR(GetDisplay(), EGL_SYNC_FENCE_KHR
, attrib_list
);
181 // TODO(dbehr): piman@ suggests we could improve here by moving
182 // following wait to right before drmModePageFlip crbug.com/456417.
183 eglClientWaitSyncKHR(GetDisplay(), fence
,
184 EGL_SYNC_FLUSH_COMMANDS_BIT_KHR
, EGL_FOREVER_KHR
);
185 eglDestroySyncKHR(GetDisplay(), fence
);
189 } else if (ozone_surface_
->IsUniversalDisplayLinkDevice()) {
195 // The native surface. Deleting this is allowed to free the EGLNativeWindow.
196 scoped_ptr
<ui::SurfaceOzoneEGL
> ozone_surface_
;
197 AcceleratedWidget widget_
;
198 scoped_ptr
<VSyncProvider
> vsync_provider_
;
199 bool has_implicit_external_sync_
;
200 DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfaceless
);
203 // This provides surface-like semantics implemented through surfaceless.
204 // A framebuffer is bound automatically.
205 class GL_EXPORT GLSurfaceOzoneSurfacelessSurfaceImpl
206 : public GLSurfaceOzoneSurfaceless
{
208 GLSurfaceOzoneSurfacelessSurfaceImpl(
209 scoped_ptr
<ui::SurfaceOzoneEGL
> ozone_surface
,
210 AcceleratedWidget widget
)
211 : GLSurfaceOzoneSurfaceless(ozone_surface
.Pass(), widget
),
213 current_surface_(0) {
214 for (auto& texture
: textures_
)
218 unsigned int GetBackingFrameBufferObject() override
{ return fbo_
; }
220 bool OnMakeCurrent(GLContext
* context
) override
{
222 glGenFramebuffersEXT(1, &fbo_
);
225 glGenTextures(arraysize(textures_
), textures_
);
226 if (!CreatePixmaps())
230 glBindFramebufferEXT(GL_FRAMEBUFFER
, fbo_
);
231 return SurfacelessEGL::OnMakeCurrent(context
);
234 bool Resize(const gfx::Size
& size
) override
{
235 if (size
== GetSize())
237 return GLSurfaceOzoneSurfaceless::Resize(size
) && CreatePixmaps();
240 bool SupportsPostSubBuffer() override
{ return false; }
242 bool SwapBuffers() override
{
243 if (!images_
[current_surface_
]->ScheduleOverlayPlane(
244 widget_
, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE
,
245 gfx::Rect(GetSize()), gfx::RectF(1, 1)))
247 if (!GLSurfaceOzoneSurfaceless::SwapBuffers())
249 current_surface_
^= 1;
254 bool SwapBuffersAsync(const SwapCompletionCallback
& callback
) override
{
255 if (!images_
[current_surface_
]->ScheduleOverlayPlane(
256 widget_
, 0, OverlayTransform::OVERLAY_TRANSFORM_NONE
,
257 gfx::Rect(GetSize()), gfx::RectF(1, 1)))
259 if (!GLSurfaceOzoneSurfaceless::SwapBuffersAsync(callback
))
261 current_surface_
^= 1;
266 void Destroy() override
{
267 GLContext
* current_context
= GLContext::GetCurrent();
268 DCHECK(current_context
&& current_context
->IsCurrent(this));
269 glBindFramebufferEXT(GL_FRAMEBUFFER
, 0);
271 glDeleteTextures(arraysize(textures_
), textures_
);
272 for (auto& texture
: textures_
)
274 glDeleteFramebuffersEXT(1, &fbo_
);
277 for (auto image
: images_
) {
279 image
->Destroy(true);
284 class SurfaceImage
: public GLImageLinuxDMABuffer
{
286 SurfaceImage(const gfx::Size
& size
, unsigned internalformat
)
287 : GLImageLinuxDMABuffer(size
, internalformat
) {}
289 bool Initialize(scoped_refptr
<ui::NativePixmap
> pixmap
,
290 gfx::GpuMemoryBuffer::Format format
) {
291 base::FileDescriptor
handle(pixmap
->GetDmaBufFd(), false);
292 if (!GLImageLinuxDMABuffer::Initialize(handle
, format
,
293 pixmap
->GetDmaBufPitch()))
298 bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
300 gfx::OverlayTransform transform
,
301 const gfx::Rect
& bounds_rect
,
302 const gfx::RectF
& crop_rect
) override
{
303 return ui::SurfaceFactoryOzone::GetInstance()->ScheduleOverlayPlane(
304 widget
, z_order
, transform
, pixmap_
, bounds_rect
, crop_rect
);
308 ~SurfaceImage() override
{}
310 scoped_refptr
<ui::NativePixmap
> pixmap_
;
313 ~GLSurfaceOzoneSurfacelessSurfaceImpl() override
{
315 for (size_t i
= 0; i
< arraysize(textures_
); i
++)
316 DCHECK(!textures_
[i
]) << "texture " << i
<< " not released";
319 void BindFramebuffer() {
320 ScopedFrameBufferBinder
fb(fbo_
);
321 glFramebufferTexture2DEXT(GL_FRAMEBUFFER
, GL_COLOR_ATTACHMENT0
,
322 GL_TEXTURE_2D
, textures_
[current_surface_
], 0);
325 bool CreatePixmaps() {
328 for (size_t i
= 0; i
< arraysize(textures_
); i
++) {
329 scoped_refptr
<ui::NativePixmap
> pixmap
=
330 ui::SurfaceFactoryOzone::GetInstance()->CreateNativePixmap(
331 widget_
, GetSize(), ui::SurfaceFactoryOzone::RGBA_8888
,
332 ui::SurfaceFactoryOzone::SCANOUT
);
335 scoped_refptr
<SurfaceImage
> image
= new SurfaceImage(GetSize(), GL_RGBA
);
336 if (!image
->Initialize(pixmap
, gfx::GpuMemoryBuffer::Format::BGRA_8888
))
339 // Bind image to texture.
340 ScopedTextureBinder
binder(GL_TEXTURE_2D
, textures_
[i
]);
341 if (!images_
[i
]->BindTexImage(GL_TEXTURE_2D
))
349 scoped_refptr
<GLImage
> images_
[2];
350 int current_surface_
;
351 DISALLOW_COPY_AND_ASSIGN(GLSurfaceOzoneSurfacelessSurfaceImpl
);
357 bool GLSurface::InitializeOneOffInternal() {
358 switch (GetGLImplementation()) {
359 case kGLImplementationEGLGLES2
:
360 if (!GLSurfaceEGL::InitializeOneOff()) {
361 LOG(ERROR
) << "GLSurfaceEGL::InitializeOneOff failed.";
366 case kGLImplementationOSMesaGL
:
367 case kGLImplementationMockGL
:
375 scoped_refptr
<GLSurface
> GLSurface::CreateSurfacelessViewGLSurface(
376 gfx::AcceleratedWidget window
) {
377 if (GetGLImplementation() == kGLImplementationEGLGLES2
&&
378 window
!= kNullAcceleratedWidget
&&
379 GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
380 ui::SurfaceFactoryOzone::GetInstance()->CanShowPrimaryPlaneAsOverlay()) {
381 scoped_ptr
<ui::SurfaceOzoneEGL
> surface_ozone
=
382 ui::SurfaceFactoryOzone::GetInstance()
383 ->CreateSurfacelessEGLSurfaceForWidget(window
);
386 scoped_refptr
<GLSurface
> surface
;
387 surface
= new GLSurfaceOzoneSurfaceless(surface_ozone
.Pass(), window
);
388 if (surface
->Initialize())
396 scoped_refptr
<GLSurface
> GLSurface::CreateViewGLSurface(
397 gfx::AcceleratedWidget window
) {
398 if (GetGLImplementation() == kGLImplementationOSMesaGL
) {
399 scoped_refptr
<GLSurface
> surface(new GLSurfaceOSMesaHeadless());
400 if (!surface
->Initialize())
404 DCHECK(GetGLImplementation() == kGLImplementationEGLGLES2
);
405 if (window
!= kNullAcceleratedWidget
) {
406 scoped_refptr
<GLSurface
> surface
;
407 if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
408 ui::SurfaceFactoryOzone::GetInstance()
409 ->CanShowPrimaryPlaneAsOverlay()) {
410 scoped_ptr
<ui::SurfaceOzoneEGL
> surface_ozone
=
411 ui::SurfaceFactoryOzone::GetInstance()
412 ->CreateSurfacelessEGLSurfaceForWidget(window
);
415 surface
= new GLSurfaceOzoneSurfacelessSurfaceImpl(surface_ozone
.Pass(),
418 scoped_ptr
<ui::SurfaceOzoneEGL
> surface_ozone
=
419 ui::SurfaceFactoryOzone::GetInstance()->CreateEGLSurfaceForWidget(
424 surface
= new GLSurfaceOzoneEGL(surface_ozone
.Pass(), window
);
426 if (!surface
->Initialize())
430 scoped_refptr
<GLSurface
> surface
= new GLSurfaceStub();
431 if (surface
->Initialize())
438 scoped_refptr
<GLSurface
> GLSurface::CreateOffscreenGLSurface(
439 const gfx::Size
& size
) {
440 switch (GetGLImplementation()) {
441 case kGLImplementationOSMesaGL
: {
442 scoped_refptr
<GLSurface
> surface(
443 new GLSurfaceOSMesa(OSMesaSurfaceFormatBGRA
, size
));
444 if (!surface
->Initialize())
449 case kGLImplementationEGLGLES2
: {
450 scoped_refptr
<GLSurface
> surface
;
451 if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
452 (size
.width() == 0 && size
.height() == 0)) {
453 surface
= new SurfacelessEGL(size
);
455 surface
= new PbufferGLSurfaceEGL(size
);
457 if (!surface
->Initialize())
467 EGLNativeDisplayType
GetPlatformDefaultEGLNativeDisplay() {
468 return ui::SurfaceFactoryOzone::GetInstance()->GetNativeDisplay();