1 /* -*- Mode: c++; c-basic-offset: 4; 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 "SharedSurface.h"
7 
8 #include "../2d/2D.h"
9 #include "GLBlitHelper.h"
10 #include "GLContext.h"
11 #include "GLReadTexImageHelper.h"
12 #include "GLScreenBuffer.h"
13 #include "nsThreadUtils.h"
14 #include "ScopedGLHelpers.h"
15 #include "SharedSurfaceGL.h"
16 #include "mozilla/layers/CompositorTypes.h"
17 #include "mozilla/layers/TextureClientSharedSurface.h"
18 #include "mozilla/layers/TextureForwarder.h"
19 #include "mozilla/Unused.h"
20 #include "VRManagerChild.h"
21 
22 namespace mozilla {
23 namespace gl {
24 
ProdCopy(SharedSurface * src,SharedSurface * dest,SurfaceFactory * factory)25 /*static*/ void SharedSurface::ProdCopy(SharedSurface* src, SharedSurface* dest,
26                                         SurfaceFactory* factory) {
27   GLContext* gl = src->mGL;
28 
29   // If `src` begins locked, it must end locked, though we may
30   // temporarily unlock it if we need to.
31   MOZ_ASSERT((src == gl->GetLockedSurface()) == src->IsLocked());
32 
33   gl->MakeCurrent();
34 
35   if (src->mAttachType == AttachmentType::Screen &&
36       dest->mAttachType == AttachmentType::Screen) {
37     // Here, we actually need to blit through a temp surface, so let's make one.
38     UniquePtr<SharedSurface_Basic> tempSurf;
39     tempSurf = SharedSurface_Basic::Create(gl, factory->mFormats, src->mSize,
40                                            factory->mCaps.alpha);
41 
42     ProdCopy(src, tempSurf.get(), factory);
43     ProdCopy(tempSurf.get(), dest, factory);
44     return;
45   }
46 
47   if (src->mAttachType == AttachmentType::Screen) {
48     SharedSurface* origLocked = gl->GetLockedSurface();
49     bool srcNeedsUnlock = false;
50     bool origNeedsRelock = false;
51     if (origLocked != src) {
52       if (origLocked) {
53         origLocked->UnlockProd();
54         origNeedsRelock = true;
55       }
56 
57       src->LockProd();
58       srcNeedsUnlock = true;
59     }
60 
61     if (dest->mAttachType == AttachmentType::GLTexture) {
62       GLuint destTex = dest->ProdTexture();
63       GLenum destTarget = dest->ProdTextureTarget();
64 
65       const ScopedBindFramebuffer bindFB(gl, 0);
66 
67       gl->BlitHelper()->BlitFramebufferToTexture(destTex, src->mSize,
68                                                  dest->mSize, destTarget);
69     } else if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
70       GLuint destRB = dest->ProdRenderbuffer();
71       ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
72 
73       gl->BlitHelper()->BlitFramebufferToFramebuffer(0, destWrapper.FB(),
74                                                      src->mSize, dest->mSize);
75     } else {
76       MOZ_CRASH("GFX: Unhandled dest->mAttachType 1.");
77     }
78 
79     if (srcNeedsUnlock) src->UnlockProd();
80 
81     if (origNeedsRelock) origLocked->LockProd();
82 
83     return;
84   }
85 
86   if (dest->mAttachType == AttachmentType::Screen) {
87     SharedSurface* origLocked = gl->GetLockedSurface();
88     bool destNeedsUnlock = false;
89     bool origNeedsRelock = false;
90     if (origLocked != dest) {
91       if (origLocked) {
92         origLocked->UnlockProd();
93         origNeedsRelock = true;
94       }
95 
96       dest->LockProd();
97       destNeedsUnlock = true;
98     }
99 
100     if (src->mAttachType == AttachmentType::GLTexture) {
101       GLuint srcTex = src->ProdTexture();
102       GLenum srcTarget = src->ProdTextureTarget();
103 
104       const ScopedBindFramebuffer bindFB(gl, 0);
105 
106       gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, src->mSize,
107                                                  dest->mSize, srcTarget);
108     } else if (src->mAttachType == AttachmentType::GLRenderbuffer) {
109       GLuint srcRB = src->ProdRenderbuffer();
110       ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
111 
112       gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), 0,
113                                                      src->mSize, dest->mSize);
114     } else {
115       MOZ_CRASH("GFX: Unhandled src->mAttachType 2.");
116     }
117 
118     if (destNeedsUnlock) dest->UnlockProd();
119 
120     if (origNeedsRelock) origLocked->LockProd();
121 
122     return;
123   }
124 
125   // Alright, done with cases involving Screen types.
126   // Only {src,dest}x{texture,renderbuffer} left.
127 
128   if (src->mAttachType == AttachmentType::GLTexture) {
129     GLuint srcTex = src->ProdTexture();
130     GLenum srcTarget = src->ProdTextureTarget();
131 
132     if (dest->mAttachType == AttachmentType::GLTexture) {
133       GLuint destTex = dest->ProdTexture();
134       GLenum destTarget = dest->ProdTextureTarget();
135 
136       gl->BlitHelper()->BlitTextureToTexture(
137           srcTex, destTex, src->mSize, dest->mSize, srcTarget, destTarget);
138 
139       return;
140     }
141 
142     if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
143       GLuint destRB = dest->ProdRenderbuffer();
144       ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
145       const ScopedBindFramebuffer bindFB(gl, destWrapper.FB());
146       gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, src->mSize,
147                                                  dest->mSize, srcTarget);
148 
149       return;
150     }
151 
152     MOZ_CRASH("GFX: Unhandled dest->mAttachType 3.");
153   }
154 
155   if (src->mAttachType == AttachmentType::GLRenderbuffer) {
156     GLuint srcRB = src->ProdRenderbuffer();
157     ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
158 
159     if (dest->mAttachType == AttachmentType::GLTexture) {
160       GLuint destTex = dest->ProdTexture();
161       GLenum destTarget = dest->ProdTextureTarget();
162       const ScopedBindFramebuffer bindFB(gl, srcWrapper.FB());
163 
164       gl->BlitHelper()->BlitFramebufferToTexture(destTex, src->mSize,
165                                                  dest->mSize, destTarget);
166 
167       return;
168     }
169 
170     if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
171       GLuint destRB = dest->ProdRenderbuffer();
172       ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
173 
174       gl->BlitHelper()->BlitFramebufferToFramebuffer(
175           srcWrapper.FB(), destWrapper.FB(), src->mSize, dest->mSize);
176 
177       return;
178     }
179 
180     MOZ_CRASH("GFX: Unhandled dest->mAttachType 4.");
181   }
182 
183   MOZ_CRASH("GFX: Unhandled src->mAttachType 5.");
184 }
185 
186 ////////////////////////////////////////////////////////////////////////
187 // SharedSurface
188 
SharedSurface(SharedSurfaceType type,AttachmentType attachType,GLContext * gl,const gfx::IntSize & size,bool hasAlpha,bool canRecycle)189 SharedSurface::SharedSurface(SharedSurfaceType type, AttachmentType attachType,
190                              GLContext* gl, const gfx::IntSize& size,
191                              bool hasAlpha, bool canRecycle)
192     : mType(type),
193       mAttachType(attachType),
194       mGL(gl),
195       mSize(size),
196       mHasAlpha(hasAlpha),
197       mCanRecycle(canRecycle),
198       mIsLocked(false),
199       mIsProducerAcquired(false) {}
200 
201 SharedSurface::~SharedSurface() = default;
202 
GetTextureFlags() const203 layers::TextureFlags SharedSurface::GetTextureFlags() const {
204   return layers::TextureFlags::NO_FLAGS;
205 }
206 
LockProd()207 void SharedSurface::LockProd() {
208   MOZ_ASSERT(!mIsLocked);
209 
210   LockProdImpl();
211 
212   mGL->LockSurface(this);
213   mIsLocked = true;
214 }
215 
UnlockProd()216 void SharedSurface::UnlockProd() {
217   if (!mIsLocked) return;
218 
219   UnlockProdImpl();
220 
221   mGL->UnlockSurface(this);
222   mIsLocked = false;
223 }
224 
225 ////////////////////////////////////////////////////////////////////////
226 // SurfaceFactory
227 
ChooseBufferBits(const SurfaceCaps & caps,SurfaceCaps * const out_drawCaps,SurfaceCaps * const out_readCaps)228 static void ChooseBufferBits(const SurfaceCaps& caps,
229                              SurfaceCaps* const out_drawCaps,
230                              SurfaceCaps* const out_readCaps) {
231   MOZ_ASSERT(out_drawCaps);
232   MOZ_ASSERT(out_readCaps);
233 
234   SurfaceCaps screenCaps;
235 
236   screenCaps.color = caps.color;
237   screenCaps.alpha = caps.alpha;
238   screenCaps.bpp16 = caps.bpp16;
239 
240   screenCaps.depth = caps.depth;
241   screenCaps.stencil = caps.stencil;
242 
243   screenCaps.antialias = caps.antialias;
244   screenCaps.preserve = caps.preserve;
245 
246   if (caps.antialias) {
247     *out_drawCaps = screenCaps;
248     out_readCaps->Clear();
249 
250     // Color caps need to be duplicated in readCaps.
251     out_readCaps->color = caps.color;
252     out_readCaps->alpha = caps.alpha;
253     out_readCaps->bpp16 = caps.bpp16;
254   } else {
255     out_drawCaps->Clear();
256     *out_readCaps = screenCaps;
257   }
258 }
259 
SurfaceFactory(SharedSurfaceType type,GLContext * gl,const SurfaceCaps & caps,const RefPtr<layers::LayersIPCChannel> & allocator,const layers::TextureFlags & flags)260 SurfaceFactory::SurfaceFactory(
261     SharedSurfaceType type, GLContext* gl, const SurfaceCaps& caps,
262     const RefPtr<layers::LayersIPCChannel>& allocator,
263     const layers::TextureFlags& flags)
264     : mType(type),
265       mGL(gl),
266       mCaps(caps),
267       mAllocator(allocator),
268       mFlags(flags),
269       mFormats(gl->ChooseGLFormats(caps)),
270       mMutex("SurfaceFactor::mMutex") {
271   ChooseBufferBits(mCaps, &mDrawCaps, &mReadCaps);
272 }
273 
~SurfaceFactory()274 SurfaceFactory::~SurfaceFactory() {
275   while (!mRecycleTotalPool.empty()) {
276     RefPtr<layers::SharedSurfaceTextureClient> tex = *mRecycleTotalPool.begin();
277     StopRecycling(tex);
278     tex->CancelWaitForRecycle();
279   }
280 
281   MOZ_RELEASE_ASSERT(mRecycleTotalPool.empty(),
282                      "GFX: Surface recycle pool not empty.");
283 
284   // If we mRecycleFreePool.clear() before StopRecycling(), we may try to
285   // recycle it, fail, call StopRecycling(), then return here and call it again.
286   mRecycleFreePool.clear();
287 }
288 
289 already_AddRefed<layers::SharedSurfaceTextureClient>
NewTexClient(const gfx::IntSize & size)290 SurfaceFactory::NewTexClient(const gfx::IntSize& size) {
291   while (!mRecycleFreePool.empty()) {
292     RefPtr<layers::SharedSurfaceTextureClient> cur = mRecycleFreePool.front();
293     mRecycleFreePool.pop();
294 
295     if (cur->Surf()->mSize == size) {
296       cur->Surf()->WaitForBufferOwnership();
297       return cur.forget();
298     }
299 
300     StopRecycling(cur);
301   }
302 
303   UniquePtr<SharedSurface> surf = Move(CreateShared(size));
304   if (!surf) return nullptr;
305 
306   RefPtr<layers::SharedSurfaceTextureClient> ret;
307   ret = layers::SharedSurfaceTextureClient::Create(Move(surf), this, mAllocator,
308                                                    mFlags);
309 
310   StartRecycling(ret);
311 
312   return ret.forget();
313 }
314 
StartRecycling(layers::SharedSurfaceTextureClient * tc)315 void SurfaceFactory::StartRecycling(layers::SharedSurfaceTextureClient* tc) {
316   tc->SetRecycleCallback(&SurfaceFactory::RecycleCallback,
317                          static_cast<void*>(this));
318 
319   bool didInsert = mRecycleTotalPool.insert(tc);
320   MOZ_RELEASE_ASSERT(
321       didInsert,
322       "GFX: Shared surface texture client was not inserted to recycle.");
323   mozilla::Unused << didInsert;
324 }
325 
StopRecycling(layers::SharedSurfaceTextureClient * tc)326 void SurfaceFactory::StopRecycling(layers::SharedSurfaceTextureClient* tc) {
327   MutexAutoLock autoLock(mMutex);
328   // Must clear before releasing ref.
329   tc->ClearRecycleCallback();
330 
331   bool didErase = mRecycleTotalPool.erase(tc);
332   MOZ_RELEASE_ASSERT(didErase,
333                      "GFX: Shared texture surface client was not erased.");
334   mozilla::Unused << didErase;
335 }
336 
RecycleCallback(layers::TextureClient * rawTC,void * rawFactory)337 /*static*/ void SurfaceFactory::RecycleCallback(layers::TextureClient* rawTC,
338                                                 void* rawFactory) {
339   RefPtr<layers::SharedSurfaceTextureClient> tc;
340   tc = static_cast<layers::SharedSurfaceTextureClient*>(rawTC);
341   SurfaceFactory* factory = static_cast<SurfaceFactory*>(rawFactory);
342 
343   if (tc->Surf()->mCanRecycle) {
344     if (factory->Recycle(tc)) return;
345   }
346 
347   // Did not recover the tex client. End the (re)cycle!
348   factory->StopRecycling(tc);
349 }
350 
Recycle(layers::SharedSurfaceTextureClient * texClient)351 bool SurfaceFactory::Recycle(layers::SharedSurfaceTextureClient* texClient) {
352   MOZ_ASSERT(texClient);
353   MutexAutoLock autoLock(mMutex);
354 
355   if (mRecycleFreePool.size() >= 2) {
356     return false;
357   }
358 
359   RefPtr<layers::SharedSurfaceTextureClient> texClientRef = texClient;
360   mRecycleFreePool.push(texClientRef);
361   return true;
362 }
363 
364 ////////////////////////////////////////////////////////////////////////////////
365 // ScopedReadbackFB
366 
ScopedReadbackFB(SharedSurface * src)367 ScopedReadbackFB::ScopedReadbackFB(SharedSurface* src)
368     : mGL(src->mGL),
369       mAutoFB(mGL),
370       mTempFB(0),
371       mTempTex(0),
372       mSurfToUnlock(nullptr),
373       mSurfToLock(nullptr) {
374   switch (src->mAttachType) {
375     case AttachmentType::GLRenderbuffer: {
376       mGL->fGenFramebuffers(1, &mTempFB);
377       mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mTempFB);
378 
379       GLuint rb = src->ProdRenderbuffer();
380       mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
381                                     LOCAL_GL_COLOR_ATTACHMENT0,
382                                     LOCAL_GL_RENDERBUFFER, rb);
383       break;
384     }
385     case AttachmentType::GLTexture: {
386       mGL->fGenFramebuffers(1, &mTempFB);
387       mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mTempFB);
388 
389       GLuint tex = src->ProdTexture();
390       GLenum texImageTarget = src->ProdTextureTarget();
391       mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
392                                  LOCAL_GL_COLOR_ATTACHMENT0, texImageTarget,
393                                  tex, 0);
394       break;
395     }
396     case AttachmentType::Screen: {
397       SharedSurface* origLocked = mGL->GetLockedSurface();
398       if (origLocked != src) {
399         if (origLocked) {
400           mSurfToLock = origLocked;
401           mSurfToLock->UnlockProd();
402         }
403 
404         mSurfToUnlock = src;
405         mSurfToUnlock->LockProd();
406       }
407 
408       // TODO: This should just be BindFB, but we don't have
409       // the patch for this yet. (bug 1045955)
410       MOZ_ASSERT(mGL->Screen());
411       mGL->Screen()->BindReadFB_Internal(0);
412       break;
413     }
414     default:
415       MOZ_CRASH("GFX: Unhandled `mAttachType`.");
416   }
417 
418   if (src->NeedsIndirectReads()) {
419     mGL->fGenTextures(1, &mTempTex);
420 
421     {
422       ScopedBindTexture autoTex(mGL, mTempTex);
423 
424       GLenum format = src->mHasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
425       auto width = src->mSize.width;
426       auto height = src->mSize.height;
427       mGL->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, format, 0, 0, width, height,
428                            0);
429     }
430 
431     mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
432                                LOCAL_GL_TEXTURE_2D, mTempTex, 0);
433   }
434 }
435 
~ScopedReadbackFB()436 ScopedReadbackFB::~ScopedReadbackFB() {
437   if (mTempFB) {
438     mGL->fDeleteFramebuffers(1, &mTempFB);
439   }
440   if (mTempTex) {
441     mGL->fDeleteTextures(1, &mTempTex);
442   }
443   if (mSurfToUnlock) {
444     mSurfToUnlock->UnlockProd();
445   }
446   if (mSurfToLock) {
447     mSurfToLock->LockProd();
448   }
449 }
450 
451 ////////////////////////////////////////////////////////////////////////////////
452 
453 class AutoLockBits {
454   gfx::DrawTarget* mDT;
455   uint8_t* mLockedBits;
456 
457  public:
AutoLockBits(gfx::DrawTarget * dt)458   explicit AutoLockBits(gfx::DrawTarget* dt) : mDT(dt), mLockedBits(nullptr) {
459     MOZ_ASSERT(mDT);
460   }
461 
Lock(uint8_t ** data,gfx::IntSize * size,int32_t * stride,gfx::SurfaceFormat * format)462   bool Lock(uint8_t** data, gfx::IntSize* size, int32_t* stride,
463             gfx::SurfaceFormat* format) {
464     if (!mDT->LockBits(data, size, stride, format)) return false;
465 
466     mLockedBits = *data;
467     return true;
468   }
469 
~AutoLockBits()470   ~AutoLockBits() {
471     if (mLockedBits) mDT->ReleaseBits(mLockedBits);
472   }
473 };
474 
ReadbackSharedSurface(SharedSurface * src,gfx::DrawTarget * dst)475 bool ReadbackSharedSurface(SharedSurface* src, gfx::DrawTarget* dst) {
476   AutoLockBits lock(dst);
477 
478   uint8_t* dstBytes;
479   gfx::IntSize dstSize;
480   int32_t dstStride;
481   gfx::SurfaceFormat dstFormat;
482   if (!lock.Lock(&dstBytes, &dstSize, &dstStride, &dstFormat)) return false;
483 
484   const bool isDstRGBA = (dstFormat == gfx::SurfaceFormat::R8G8B8A8 ||
485                           dstFormat == gfx::SurfaceFormat::R8G8B8X8);
486   MOZ_ASSERT_IF(!isDstRGBA, dstFormat == gfx::SurfaceFormat::B8G8R8A8 ||
487                                 dstFormat == gfx::SurfaceFormat::B8G8R8X8);
488 
489   size_t width = src->mSize.width;
490   size_t height = src->mSize.height;
491   MOZ_ASSERT(width == (size_t)dstSize.width);
492   MOZ_ASSERT(height == (size_t)dstSize.height);
493 
494   GLenum readGLFormat;
495   GLenum readType;
496 
497   {
498     ScopedReadbackFB autoReadback(src);
499 
500     // We have a source FB, now we need a format.
501     GLenum dstGLFormat = isDstRGBA ? LOCAL_GL_BGRA : LOCAL_GL_RGBA;
502     GLenum dstType = LOCAL_GL_UNSIGNED_BYTE;
503 
504     // We actually don't care if they match, since we can handle
505     // any read{Format,Type} we get.
506     GLContext* gl = src->mGL;
507     GetActualReadFormats(gl, dstGLFormat, dstType, &readGLFormat, &readType);
508 
509     MOZ_ASSERT(readGLFormat == LOCAL_GL_RGBA || readGLFormat == LOCAL_GL_BGRA);
510     MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
511 
512     // ReadPixels from the current FB into lockedBits.
513     {
514       size_t alignment = 8;
515       if (dstStride % 4 == 0) alignment = 4;
516 
517       ScopedPackState scopedPackState(gl);
518       if (alignment != 4) {
519         gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, alignment);
520       }
521 
522       gl->raw_fReadPixels(0, 0, width, height, readGLFormat, readType,
523                           dstBytes);
524     }
525   }
526 
527   const bool isReadRGBA = readGLFormat == LOCAL_GL_RGBA;
528 
529   if (isReadRGBA != isDstRGBA) {
530     for (size_t j = 0; j < height; ++j) {
531       uint8_t* rowItr = dstBytes + j * dstStride;
532       uint8_t* rowEnd = rowItr + 4 * width;
533       while (rowItr != rowEnd) {
534         Swap(rowItr[0], rowItr[2]);
535         rowItr += 4;
536       }
537     }
538   }
539 
540   return true;
541 }
542 
ReadPixel(SharedSurface * src)543 uint32_t ReadPixel(SharedSurface* src) {
544   GLContext* gl = src->mGL;
545 
546   uint32_t pixel;
547 
548   ScopedReadbackFB a(src);
549   {
550     ScopedPackState scopedPackState(gl);
551 
552     UniquePtr<uint8_t[]> bytes(new uint8_t[4]);
553     gl->raw_fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE,
554                         bytes.get());
555     memcpy(&pixel, bytes.get(), 4);
556   }
557 
558   return pixel;
559 }
560 
561 }  // namespace gl
562 
563 } /* namespace mozilla */
564