1 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
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/. */
6 #include "SharedSurface.h"
9 #include "GLBlitHelper.h"
10 #include "GLContext.h"
11 #include "GLReadTexImageHelper.h"
12 #include "nsThreadUtils.h"
13 #include "ScopedGLHelpers.h"
14 #include "SharedSurfaceGL.h"
20 SharedSurface::ProdCopy(SharedSurface
* src
, SharedSurface
* dest
,
21 SurfaceFactory
* factory
)
23 GLContext
* gl
= src
->mGL
;
25 // If `src` begins locked, it must end locked, though we may
26 // temporarily unlock it if we need to.
27 MOZ_ASSERT((src
== gl
->GetLockedSurface()) == src
->IsLocked());
31 if (src
->mAttachType
== AttachmentType::Screen
&&
32 dest
->mAttachType
== AttachmentType::Screen
)
34 // Here, we actually need to blit through a temp surface, so let's make one.
35 UniquePtr
<SharedSurface_GLTexture
> tempSurf
;
36 tempSurf
= SharedSurface_GLTexture::Create(gl
,
40 factory
->mCaps
.alpha
);
42 ProdCopy(src
, tempSurf
.get(), factory
);
43 ProdCopy(tempSurf
.get(), dest
, factory
);
47 if (src
->mAttachType
== AttachmentType::Screen
) {
48 SharedSurface
* origLocked
= gl
->GetLockedSurface();
49 bool srcNeedsUnlock
= false;
50 bool origNeedsRelock
= false;
51 if (origLocked
!= src
) {
53 origLocked
->UnlockProd();
54 origNeedsRelock
= true;
58 srcNeedsUnlock
= true;
61 if (dest
->mAttachType
== AttachmentType::GLTexture
) {
62 GLuint destTex
= dest
->ProdTexture();
63 GLenum destTarget
= dest
->ProdTextureTarget();
65 gl
->BlitHelper()->BlitFramebufferToTexture(0, destTex
,
70 } else if (dest
->mAttachType
== AttachmentType::GLRenderbuffer
) {
71 GLuint destRB
= dest
->ProdRenderbuffer();
72 ScopedFramebufferForRenderbuffer
destWrapper(gl
, destRB
);
74 gl
->BlitHelper()->BlitFramebufferToFramebuffer(0,
80 MOZ_CRASH("Unhandled dest->mAttachType.");
87 origLocked
->LockProd();
92 if (dest
->mAttachType
== AttachmentType::Screen
) {
93 SharedSurface
* origLocked
= gl
->GetLockedSurface();
94 bool destNeedsUnlock
= false;
95 bool origNeedsRelock
= false;
96 if (origLocked
!= dest
) {
98 origLocked
->UnlockProd();
99 origNeedsRelock
= true;
103 destNeedsUnlock
= true;
106 if (src
->mAttachType
== AttachmentType::GLTexture
) {
107 GLuint srcTex
= src
->ProdTexture();
108 GLenum srcTarget
= src
->ProdTextureTarget();
110 gl
->BlitHelper()->BlitTextureToFramebuffer(srcTex
, 0,
115 } else if (src
->mAttachType
== AttachmentType::GLRenderbuffer
) {
116 GLuint srcRB
= src
->ProdRenderbuffer();
117 ScopedFramebufferForRenderbuffer
srcWrapper(gl
, srcRB
);
119 gl
->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper
.FB(),
125 MOZ_CRASH("Unhandled src->mAttachType.");
132 origLocked
->LockProd();
137 // Alright, done with cases involving Screen types.
138 // Only {src,dest}x{texture,renderbuffer} left.
140 if (src
->mAttachType
== AttachmentType::GLTexture
) {
141 GLuint srcTex
= src
->ProdTexture();
142 GLenum srcTarget
= src
->ProdTextureTarget();
144 if (dest
->mAttachType
== AttachmentType::GLTexture
) {
145 GLuint destTex
= dest
->ProdTexture();
146 GLenum destTarget
= dest
->ProdTextureTarget();
148 gl
->BlitHelper()->BlitTextureToTexture(srcTex
, destTex
,
149 src
->mSize
, dest
->mSize
,
150 srcTarget
, destTarget
);
155 if (dest
->mAttachType
== AttachmentType::GLRenderbuffer
) {
156 GLuint destRB
= dest
->ProdRenderbuffer();
157 ScopedFramebufferForRenderbuffer
destWrapper(gl
, destRB
);
159 gl
->BlitHelper()->BlitTextureToFramebuffer(srcTex
, destWrapper
.FB(),
160 src
->mSize
, dest
->mSize
, srcTarget
);
165 MOZ_CRASH("Unhandled dest->mAttachType.");
168 if (src
->mAttachType
== AttachmentType::GLRenderbuffer
) {
169 GLuint srcRB
= src
->ProdRenderbuffer();
170 ScopedFramebufferForRenderbuffer
srcWrapper(gl
, srcRB
);
172 if (dest
->mAttachType
== AttachmentType::GLTexture
) {
173 GLuint destTex
= dest
->ProdTexture();
174 GLenum destTarget
= dest
->ProdTextureTarget();
176 gl
->BlitHelper()->BlitFramebufferToTexture(srcWrapper
.FB(), destTex
,
177 src
->mSize
, dest
->mSize
, destTarget
);
182 if (dest
->mAttachType
== AttachmentType::GLRenderbuffer
) {
183 GLuint destRB
= dest
->ProdRenderbuffer();
184 ScopedFramebufferForRenderbuffer
destWrapper(gl
, destRB
);
186 gl
->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper
.FB(), destWrapper
.FB(),
187 src
->mSize
, dest
->mSize
);
192 MOZ_CRASH("Unhandled dest->mAttachType.");
195 MOZ_CRASH("Unhandled src->mAttachType.");
198 ////////////////////////////////////////////////////////////////////////
202 SharedSurface::SharedSurface(SharedSurfaceType type
,
203 AttachmentType attachType
,
205 const gfx::IntSize
& size
,
208 , mAttachType(attachType
)
211 , mHasAlpha(hasAlpha
)
213 , mIsProducerAcquired(false)
214 , mIsConsumerAcquired(false)
216 , mOwningThread(NS_GetCurrentThread())
222 SharedSurface::LockProd()
224 MOZ_ASSERT(!mIsLocked
);
228 mGL
->LockSurface(this);
233 SharedSurface::UnlockProd()
240 mGL
->UnlockSurface(this);
245 SharedSurface::Fence_ContentThread()
247 MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread
);
248 Fence_ContentThread_Impl();
252 SharedSurface::WaitSync_ContentThread()
254 MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread
);
255 return WaitSync_ContentThread_Impl();
259 SharedSurface::PollSync_ContentThread()
261 MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread
);
262 return PollSync_ContentThread_Impl();
267 ////////////////////////////////////////////////////////////////////////
271 ChooseBufferBits(const SurfaceCaps
& caps
,
272 SurfaceCaps
* const out_drawCaps
,
273 SurfaceCaps
* const out_readCaps
)
275 MOZ_ASSERT(out_drawCaps
);
276 MOZ_ASSERT(out_readCaps
);
278 SurfaceCaps screenCaps
;
280 screenCaps
.color
= caps
.color
;
281 screenCaps
.alpha
= caps
.alpha
;
282 screenCaps
.bpp16
= caps
.bpp16
;
284 screenCaps
.depth
= caps
.depth
;
285 screenCaps
.stencil
= caps
.stencil
;
287 screenCaps
.antialias
= caps
.antialias
;
288 screenCaps
.preserve
= caps
.preserve
;
290 if (caps
.antialias
) {
291 *out_drawCaps
= screenCaps
;
292 out_readCaps
->Clear();
294 // Color caps need to be duplicated in readCaps.
295 out_readCaps
->color
= caps
.color
;
296 out_readCaps
->alpha
= caps
.alpha
;
297 out_readCaps
->bpp16
= caps
.bpp16
;
299 out_drawCaps
->Clear();
300 *out_readCaps
= screenCaps
;
304 SurfaceFactory::SurfaceFactory(GLContext
* gl
,
305 SharedSurfaceType type
,
306 const SurfaceCaps
& caps
)
310 , mFormats(gl
->ChooseGLFormats(caps
))
312 ChooseBufferBits(mCaps
, &mDrawCaps
, &mReadCaps
);
315 SurfaceFactory::~SurfaceFactory()
317 while (!mScraps
.Empty()) {
322 UniquePtr
<SharedSurface
>
323 SurfaceFactory::NewSharedSurface(const gfx::IntSize
& size
)
325 // Attempt to reuse an old surface.
326 while (!mScraps
.Empty()) {
327 UniquePtr
<SharedSurface
> cur
= mScraps
.Pop();
329 if (cur
->mSize
== size
)
332 // Let `cur` be destroyed as it falls out of scope, if it wasn't
336 return CreateShared(size
);
339 TemporaryRef
<ShSurfHandle
>
340 SurfaceFactory::NewShSurfHandle(const gfx::IntSize
& size
)
342 auto surf
= NewSharedSurface(size
);
346 // Before next use, wait until SharedSurface's buffer
347 // is no longer being used.
348 surf
->WaitForBufferOwnership();
350 return new ShSurfHandle(this, Move(surf
));
353 // Auto-deletes surfs of the wrong type.
355 SurfaceFactory::Recycle(UniquePtr
<SharedSurface
> surf
)
359 if (surf
->mType
== mType
) {
360 mScraps
.Push(Move(surf
));
364 ////////////////////////////////////////////////////////////////////////////////
367 ScopedReadbackFB::ScopedReadbackFB(SharedSurface
* src
)
372 , mSurfToUnlock(nullptr)
373 , mSurfToLock(nullptr)
375 switch (src
->mAttachType
) {
376 case AttachmentType::GLRenderbuffer
:
378 mGL
->fGenFramebuffers(1, &mTempFB
);
379 mGL
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mTempFB
);
381 GLuint rb
= src
->ProdRenderbuffer();
382 mGL
->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER
,
383 LOCAL_GL_COLOR_ATTACHMENT0
,
384 LOCAL_GL_RENDERBUFFER
, rb
);
387 case AttachmentType::GLTexture
:
389 mGL
->fGenFramebuffers(1, &mTempFB
);
390 mGL
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, mTempFB
);
392 GLuint tex
= src
->ProdTexture();
393 GLenum texImageTarget
= src
->ProdTextureTarget();
394 mGL
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
395 LOCAL_GL_COLOR_ATTACHMENT0
,
396 texImageTarget
, tex
, 0);
399 case AttachmentType::Screen
:
401 SharedSurface
* origLocked
= mGL
->GetLockedSurface();
402 if (origLocked
!= src
) {
404 mSurfToLock
= origLocked
;
405 mSurfToLock
->UnlockProd();
409 mSurfToUnlock
->LockProd();
412 // TODO: This should just be BindFB, but we don't have
413 // the patch for this yet. (bug 1045955)
414 MOZ_ASSERT(mGL
->Screen());
415 mGL
->Screen()->BindReadFB_Internal(0);
419 MOZ_CRASH("Unhandled `mAttachType`.");
422 if (src
->NeedsIndirectReads()) {
423 mGL
->fGenTextures(1, &mTempTex
);
426 ScopedBindTexture
autoTex(mGL
, mTempTex
);
428 GLenum format
= src
->mHasAlpha
? LOCAL_GL_RGBA
430 auto width
= src
->mSize
.width
;
431 auto height
= src
->mSize
.height
;
432 mGL
->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, format
, 0, 0, width
,
436 mGL
->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER
,
437 LOCAL_GL_COLOR_ATTACHMENT0
,
438 LOCAL_GL_TEXTURE_2D
, mTempTex
, 0);
442 ScopedReadbackFB::~ScopedReadbackFB()
445 mGL
->fDeleteFramebuffers(1, &mTempFB
);
448 mGL
->fDeleteTextures(1, &mTempTex
);
451 mSurfToUnlock
->UnlockProd();
454 mSurfToLock
->LockProd();
458 ////////////////////////////////////////////////////////////////////////////////
462 gfx::DrawTarget
* mDT
;
463 uint8_t* mLockedBits
;
466 explicit AutoLockBits(gfx::DrawTarget
* dt
)
468 , mLockedBits(nullptr)
473 bool Lock(uint8_t** data
, gfx::IntSize
* size
, int32_t* stride
,
474 gfx::SurfaceFormat
* format
)
476 if (!mDT
->LockBits(data
, size
, stride
, format
))
485 mDT
->ReleaseBits(mLockedBits
);
490 ReadbackSharedSurface(SharedSurface
* src
, gfx::DrawTarget
* dst
)
492 AutoLockBits
lock(dst
);
495 gfx::IntSize dstSize
;
497 gfx::SurfaceFormat dstFormat
;
498 if (!lock
.Lock(&dstBytes
, &dstSize
, &dstStride
, &dstFormat
))
501 const bool isDstRGBA
= (dstFormat
== gfx::SurfaceFormat::R8G8B8A8
||
502 dstFormat
== gfx::SurfaceFormat::R8G8B8X8
);
503 MOZ_ASSERT_IF(!isDstRGBA
, dstFormat
== gfx::SurfaceFormat::B8G8R8A8
||
504 dstFormat
== gfx::SurfaceFormat::B8G8R8X8
);
506 size_t width
= src
->mSize
.width
;
507 size_t height
= src
->mSize
.height
;
508 MOZ_ASSERT(width
== (size_t)dstSize
.width
);
509 MOZ_ASSERT(height
== (size_t)dstSize
.height
);
515 ScopedReadbackFB
autoReadback(src
);
518 // We have a source FB, now we need a format.
519 GLenum dstGLFormat
= isDstRGBA
? LOCAL_GL_BGRA
: LOCAL_GL_RGBA
;
520 GLenum dstType
= LOCAL_GL_UNSIGNED_BYTE
;
522 // We actually don't care if they match, since we can handle
523 // any read{Format,Type} we get.
524 GLContext
* gl
= src
->mGL
;
525 GetActualReadFormats(gl
, dstGLFormat
, dstType
, &readGLFormat
,
528 MOZ_ASSERT(readGLFormat
== LOCAL_GL_RGBA
||
529 readGLFormat
== LOCAL_GL_BGRA
);
530 MOZ_ASSERT(readType
== LOCAL_GL_UNSIGNED_BYTE
);
532 // ReadPixels from the current FB into lockedBits.
534 size_t alignment
= 8;
535 if (dstStride
% 4 == 0)
537 ScopedPackAlignment
autoAlign(gl
, alignment
);
539 gl
->raw_fReadPixels(0, 0, width
, height
, readGLFormat
, readType
,
544 const bool isReadRGBA
= readGLFormat
== LOCAL_GL_RGBA
;
546 if (isReadRGBA
!= isDstRGBA
) {
547 for (size_t j
= 0; j
< height
; ++j
) {
548 uint8_t* rowItr
= dstBytes
+ j
*dstStride
;
549 uint8_t* rowEnd
= rowItr
+ 4*width
;
550 while (rowItr
!= rowEnd
) {
551 Swap(rowItr
[0], rowItr
[2]);
560 } /* namespace gfx */
561 } /* namespace mozilla */