1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 4; -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "SharedSurfaceEGL.h"
7 
8 #include "GLBlitHelper.h"
9 #include "GLContextEGL.h"
10 #include "GLContextProvider.h"
11 #include "GLLibraryEGL.h"
12 #include "GLReadTexImageHelper.h"
13 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
14 #include "SharedSurface.h"
15 
16 #if defined(MOZ_WIDGET_ANDROID)
17 #  include "mozilla/java/SurfaceAllocatorWrappers.h"
18 #endif  // defined(MOZ_WIDGET_ANDROID)
19 
20 namespace mozilla {
21 namespace gl {
22 
23 /*static*/
Create(GLContext * prodGL,const GLFormats & formats,const gfx::IntSize & size,bool hasAlpha,EGLContext context)24 UniquePtr<SharedSurface_EGLImage> SharedSurface_EGLImage::Create(
25     GLContext* prodGL, const GLFormats& formats, const gfx::IntSize& size,
26     bool hasAlpha, EGLContext context) {
27   const auto& gle = GLContextEGL::Cast(prodGL);
28   const auto& egl = gle->mEgl;
29   MOZ_ASSERT(egl);
30   MOZ_ASSERT(context);
31 
32   UniquePtr<SharedSurface_EGLImage> ret;
33 
34   if (!HasExtensions(egl, prodGL)) {
35     return ret;
36   }
37 
38   MOZ_ALWAYS_TRUE(prodGL->MakeCurrent());
39   GLuint prodTex = CreateTextureForOffscreen(prodGL, formats, size);
40   if (!prodTex) {
41     return ret;
42   }
43 
44   EGLClientBuffer buffer =
45       reinterpret_cast<EGLClientBuffer>(uintptr_t(prodTex));
46   EGLImage image = egl->fCreateImage(egl->Display(), context,
47                                      LOCAL_EGL_GL_TEXTURE_2D, buffer, nullptr);
48   if (!image) {
49     prodGL->fDeleteTextures(1, &prodTex);
50     return ret;
51   }
52 
53   ret.reset(new SharedSurface_EGLImage(prodGL, size, hasAlpha, formats, prodTex,
54                                        image));
55   return ret;
56 }
57 
HasExtensions(GLLibraryEGL * egl,GLContext * gl)58 bool SharedSurface_EGLImage::HasExtensions(GLLibraryEGL* egl, GLContext* gl) {
59   return egl->HasKHRImageBase() &&
60          egl->IsExtensionSupported(GLLibraryEGL::KHR_gl_texture_2D_image) &&
61          (gl->IsExtensionSupported(GLContext::OES_EGL_image_external) ||
62           gl->IsExtensionSupported(GLContext::OES_EGL_image));
63 }
64 
SharedSurface_EGLImage(GLContext * gl,const gfx::IntSize & size,bool hasAlpha,const GLFormats & formats,GLuint prodTex,EGLImage image)65 SharedSurface_EGLImage::SharedSurface_EGLImage(GLContext* gl,
66                                                const gfx::IntSize& size,
67                                                bool hasAlpha,
68                                                const GLFormats& formats,
69                                                GLuint prodTex, EGLImage image)
70     : SharedSurface(
71           SharedSurfaceType::EGLImageShare, AttachmentType::GLTexture, gl, size,
72           hasAlpha,
73           false)  // Can't recycle, as mSync changes never update TextureHost.
74       ,
75       mMutex("SharedSurface_EGLImage mutex"),
76       mFormats(formats),
77       mProdTex(prodTex),
78       mImage(image),
79       mSync(0) {}
80 
~SharedSurface_EGLImage()81 SharedSurface_EGLImage::~SharedSurface_EGLImage() {
82   const auto& gle = GLContextEGL::Cast(mGL);
83   const auto& egl = gle->mEgl;
84   egl->fDestroyImage(egl->Display(), mImage);
85 
86   if (mSync) {
87     // We can't call this unless we have the ext, but we will always have
88     // the ext if we have something to destroy.
89     egl->fDestroySync(egl->Display(), mSync);
90     mSync = 0;
91   }
92 
93   if (!mGL || !mGL->MakeCurrent()) return;
94 
95   mGL->fDeleteTextures(1, &mProdTex);
96   mProdTex = 0;
97 }
98 
ProducerReleaseImpl()99 void SharedSurface_EGLImage::ProducerReleaseImpl() {
100   const auto& gle = GLContextEGL::Cast(mGL);
101   const auto& egl = gle->mEgl;
102 
103   MutexAutoLock lock(mMutex);
104   mGL->MakeCurrent();
105 
106   if (egl->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) &&
107       mGL->IsExtensionSupported(GLContext::OES_EGL_sync)) {
108     if (mSync) {
109       MOZ_RELEASE_ASSERT(false, "GFX: Non-recycleable should not Fence twice.");
110       MOZ_ALWAYS_TRUE(egl->fDestroySync(egl->Display(), mSync));
111       mSync = 0;
112     }
113 
114     mSync = egl->fCreateSync(egl->Display(), LOCAL_EGL_SYNC_FENCE, nullptr);
115     if (mSync) {
116       mGL->fFlush();
117       return;
118     }
119   }
120 
121   MOZ_ASSERT(!mSync);
122   mGL->fFinish();
123 }
124 
ProducerReadAcquireImpl()125 void SharedSurface_EGLImage::ProducerReadAcquireImpl() {
126   const auto& gle = GLContextEGL::Cast(mGL);
127   const auto& egl = gle->mEgl;
128   // Wait on the fence, because presumably we're going to want to read this
129   // surface
130   if (mSync) {
131     egl->fClientWaitSync(egl->Display(), mSync, 0, LOCAL_EGL_FOREVER);
132   }
133 }
134 
ToSurfaceDescriptor(layers::SurfaceDescriptor * const out_descriptor)135 bool SharedSurface_EGLImage::ToSurfaceDescriptor(
136     layers::SurfaceDescriptor* const out_descriptor) {
137   *out_descriptor = layers::EGLImageDescriptor(
138       (uintptr_t)mImage, (uintptr_t)mSync, mSize, mHasAlpha);
139   return true;
140 }
141 
ReadbackBySharedHandle(gfx::DataSourceSurface * out_surface)142 bool SharedSurface_EGLImage::ReadbackBySharedHandle(
143     gfx::DataSourceSurface* out_surface) {
144   const auto& gle = GLContextEGL::Cast(mGL);
145   const auto& egl = gle->mEgl;
146   MOZ_ASSERT(out_surface);
147   MOZ_ASSERT(NS_IsMainThread());
148   return egl->ReadbackEGLImage(mImage, out_surface);
149 }
150 
151 ////////////////////////////////////////////////////////////////////////
152 
153 /*static*/
Create(GLContext * prodGL,const SurfaceCaps & caps,const RefPtr<layers::LayersIPCChannel> & allocator,const layers::TextureFlags & flags)154 UniquePtr<SurfaceFactory_EGLImage> SurfaceFactory_EGLImage::Create(
155     GLContext* prodGL, const SurfaceCaps& caps,
156     const RefPtr<layers::LayersIPCChannel>& allocator,
157     const layers::TextureFlags& flags) {
158   const auto& gle = GLContextEGL::Cast(prodGL);
159   const auto& egl = gle->mEgl;
160   const auto& context = gle->mContext;
161 
162   typedef SurfaceFactory_EGLImage ptrT;
163   UniquePtr<ptrT> ret;
164 
165   if (SharedSurface_EGLImage::HasExtensions(egl, prodGL)) {
166     ret.reset(new ptrT(prodGL, caps, allocator, flags, context));
167   }
168 
169   return ret;
170 }
171 
172 ////////////////////////////////////////////////////////////////////////
173 
174 #ifdef MOZ_WIDGET_ANDROID
175 
176 /*static*/
Create(GLContext * prodGL,const GLFormats & formats,const gfx::IntSize & size,bool hasAlpha,java::GeckoSurface::Param surface)177 UniquePtr<SharedSurface_SurfaceTexture> SharedSurface_SurfaceTexture::Create(
178     GLContext* prodGL, const GLFormats& formats, const gfx::IntSize& size,
179     bool hasAlpha, java::GeckoSurface::Param surface) {
180   MOZ_ASSERT(surface);
181 
182   UniquePtr<SharedSurface_SurfaceTexture> ret;
183 
184   AndroidNativeWindow window(surface);
185   const auto& gle = GLContextEGL::Cast(prodGL);
186   MOZ_ASSERT(gle);
187   EGLSurface eglSurface = gle->CreateCompatibleSurface(window.NativeWindow());
188   if (!eglSurface) {
189     return ret;
190   }
191 
192   ret.reset(new SharedSurface_SurfaceTexture(prodGL, size, hasAlpha, formats,
193                                              surface, eglSurface));
194   return ret;
195 }
196 
SharedSurface_SurfaceTexture(GLContext * gl,const gfx::IntSize & size,bool hasAlpha,const GLFormats & formats,java::GeckoSurface::Param surface,EGLSurface eglSurface)197 SharedSurface_SurfaceTexture::SharedSurface_SurfaceTexture(
198     GLContext* gl, const gfx::IntSize& size, bool hasAlpha,
199     const GLFormats& formats, java::GeckoSurface::Param surface,
200     EGLSurface eglSurface)
201     : SharedSurface(SharedSurfaceType::AndroidSurfaceTexture,
202                     AttachmentType::Screen, gl, size, hasAlpha, true),
203       mSurface(surface),
204       mEglSurface(eglSurface) {}
205 
~SharedSurface_SurfaceTexture()206 SharedSurface_SurfaceTexture::~SharedSurface_SurfaceTexture() {
207   if (mOrigEglSurface) {
208     // We are about to destroy mEglSurface.
209     // Make sure gl->SetEGLSurfaceOverride() doesn't keep a reference
210     // to the surface.
211     UnlockProd();
212   }
213   GLContextProviderEGL::DestroyEGLSurface(mEglSurface);
214   java::SurfaceAllocator::DisposeSurface(mSurface);
215 }
216 
LockProdImpl()217 void SharedSurface_SurfaceTexture::LockProdImpl() {
218   MOZ_RELEASE_ASSERT(mSurface->GetAvailable());
219 
220   GLContextEGL* gl = GLContextEGL::Cast(mGL);
221   mOrigEglSurface = gl->GetEGLSurfaceOverride();
222   gl->SetEGLSurfaceOverride(mEglSurface);
223 }
224 
UnlockProdImpl()225 void SharedSurface_SurfaceTexture::UnlockProdImpl() {
226   MOZ_RELEASE_ASSERT(mSurface->GetAvailable());
227 
228   GLContextEGL* gl = GLContextEGL::Cast(mGL);
229   MOZ_ASSERT(gl->GetEGLSurfaceOverride() == mEglSurface);
230 
231   gl->SetEGLSurfaceOverride(mOrigEglSurface);
232   mOrigEglSurface = nullptr;
233 }
234 
Commit()235 void SharedSurface_SurfaceTexture::Commit() {
236   MOZ_RELEASE_ASSERT(mSurface->GetAvailable());
237 
238   LockProdImpl();
239   mGL->SwapBuffers();
240   UnlockProdImpl();
241   mSurface->SetAvailable(false);
242 }
243 
WaitForBufferOwnership()244 void SharedSurface_SurfaceTexture::WaitForBufferOwnership() {
245   mSurface->SetAvailable(true);
246 }
247 
IsBufferAvailable() const248 bool SharedSurface_SurfaceTexture::IsBufferAvailable() const {
249   return mSurface->GetAvailable();
250 }
251 
ToSurfaceDescriptor(layers::SurfaceDescriptor * const out_descriptor)252 bool SharedSurface_SurfaceTexture::ToSurfaceDescriptor(
253     layers::SurfaceDescriptor* const out_descriptor) {
254   *out_descriptor = layers::SurfaceTextureDescriptor(
255       mSurface->GetHandle(), mSize, gfx::SurfaceFormat::R8G8B8A8,
256       false /* NOT continuous */, false /* Do not ignore transform */);
257   return true;
258 }
259 
260 ////////////////////////////////////////////////////////////////////////
261 
262 /*static*/
Create(GLContext * prodGL,const SurfaceCaps & caps,const RefPtr<layers::LayersIPCChannel> & allocator,const layers::TextureFlags & flags)263 UniquePtr<SurfaceFactory_SurfaceTexture> SurfaceFactory_SurfaceTexture::Create(
264     GLContext* prodGL, const SurfaceCaps& caps,
265     const RefPtr<layers::LayersIPCChannel>& allocator,
266     const layers::TextureFlags& flags) {
267   UniquePtr<SurfaceFactory_SurfaceTexture> ret(
268       new SurfaceFactory_SurfaceTexture(prodGL, caps, allocator, flags));
269   return ret;
270 }
271 
CreateShared(const gfx::IntSize & size)272 UniquePtr<SharedSurface> SurfaceFactory_SurfaceTexture::CreateShared(
273     const gfx::IntSize& size) {
274   bool hasAlpha = mReadCaps.alpha;
275 
276   jni::Object::LocalRef surface =
277       java::SurfaceAllocator::AcquireSurface(size.width, size.height, true);
278   if (!surface) {
279     // Try multi-buffer mode
280     surface =
281         java::SurfaceAllocator::AcquireSurface(size.width, size.height, false);
282     if (!surface) {
283       // Give up
284       NS_WARNING("Failed to allocate SurfaceTexture!");
285       return nullptr;
286     }
287   }
288 
289   return SharedSurface_SurfaceTexture::Create(
290       mGL, mFormats, size, hasAlpha, java::GeckoSurface::Ref::From(surface));
291 }
292 
293 #endif  // MOZ_WIDGET_ANDROID
294 
295 }  // namespace gl
296 
297 } /* namespace mozilla */
298