michael@0: /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "SharedSurfaceGL.h" michael@0: #include "GLContext.h" michael@0: #include "GLBlitHelper.h" michael@0: #include "ScopedGLHelpers.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "GLReadTexImageHelper.h" michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace mozilla { michael@0: namespace gl { michael@0: michael@0: // |src| must begin and end locked, though we may michael@0: // temporarily unlock it if we need to. michael@0: void michael@0: SharedSurface_GL::ProdCopy(SharedSurface_GL* src, SharedSurface_GL* dest, michael@0: SurfaceFactory_GL* factory) michael@0: { michael@0: GLContext* gl = src->GL(); michael@0: michael@0: gl->MakeCurrent(); michael@0: michael@0: if (src->AttachType() == AttachmentType::Screen && michael@0: dest->AttachType() == AttachmentType::Screen) michael@0: { michael@0: // Here, we actually need to blit through a temp surface, so let's make one. michael@0: nsAutoPtr tempSurf( michael@0: SharedSurface_GLTexture::Create(gl, gl, michael@0: factory->Formats(), michael@0: src->Size(), michael@0: factory->Caps().alpha)); michael@0: michael@0: ProdCopy(src, tempSurf, factory); michael@0: ProdCopy(tempSurf, dest, factory); michael@0: return; michael@0: } michael@0: michael@0: if (src->AttachType() == AttachmentType::Screen) { michael@0: SharedSurface_GL* origLocked = gl->GetLockedSurface(); michael@0: bool srcNeedsUnlock = false; michael@0: bool origNeedsRelock = false; michael@0: if (origLocked != src) { michael@0: if (origLocked) { michael@0: origLocked->UnlockProd(); michael@0: origNeedsRelock = true; michael@0: } michael@0: michael@0: src->LockProd(); michael@0: srcNeedsUnlock = true; michael@0: } michael@0: michael@0: if (dest->AttachType() == AttachmentType::GLTexture) { michael@0: GLuint destTex = dest->ProdTexture(); michael@0: GLenum destTarget = dest->ProdTextureTarget(); michael@0: michael@0: gl->BlitHelper()->BlitFramebufferToTexture(0, destTex, src->Size(), dest->Size(), destTarget); michael@0: } else if (dest->AttachType() == AttachmentType::GLRenderbuffer) { michael@0: GLuint destRB = dest->ProdRenderbuffer(); michael@0: ScopedFramebufferForRenderbuffer destWrapper(gl, destRB); michael@0: michael@0: gl->BlitHelper()->BlitFramebufferToFramebuffer(0, destWrapper.FB(), michael@0: src->Size(), dest->Size()); michael@0: } else { michael@0: MOZ_CRASH("Unhandled dest->AttachType()."); michael@0: } michael@0: michael@0: if (srcNeedsUnlock) michael@0: src->UnlockProd(); michael@0: michael@0: if (origNeedsRelock) michael@0: origLocked->LockProd(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (dest->AttachType() == AttachmentType::Screen) { michael@0: SharedSurface_GL* origLocked = gl->GetLockedSurface(); michael@0: bool destNeedsUnlock = false; michael@0: bool origNeedsRelock = false; michael@0: if (origLocked != dest) { michael@0: if (origLocked) { michael@0: origLocked->UnlockProd(); michael@0: origNeedsRelock = true; michael@0: } michael@0: michael@0: dest->LockProd(); michael@0: destNeedsUnlock = true; michael@0: } michael@0: michael@0: if (src->AttachType() == AttachmentType::GLTexture) { michael@0: GLuint srcTex = src->ProdTexture(); michael@0: GLenum srcTarget = src->ProdTextureTarget(); michael@0: michael@0: gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, 0, src->Size(), dest->Size(), srcTarget); michael@0: } else if (src->AttachType() == AttachmentType::GLRenderbuffer) { michael@0: GLuint srcRB = src->ProdRenderbuffer(); michael@0: ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB); michael@0: michael@0: gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), 0, michael@0: src->Size(), dest->Size()); michael@0: } else { michael@0: MOZ_CRASH("Unhandled src->AttachType()."); michael@0: } michael@0: michael@0: if (destNeedsUnlock) michael@0: dest->UnlockProd(); michael@0: michael@0: if (origNeedsRelock) michael@0: origLocked->LockProd(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: // Alright, done with cases involving Screen types. michael@0: // Only {src,dest}x{texture,renderbuffer} left. michael@0: michael@0: if (src->AttachType() == AttachmentType::GLTexture) { michael@0: GLuint srcTex = src->ProdTexture(); michael@0: GLenum srcTarget = src->ProdTextureTarget(); michael@0: michael@0: if (dest->AttachType() == AttachmentType::GLTexture) { michael@0: GLuint destTex = dest->ProdTexture(); michael@0: GLenum destTarget = dest->ProdTextureTarget(); michael@0: michael@0: gl->BlitHelper()->BlitTextureToTexture(srcTex, destTex, michael@0: src->Size(), dest->Size(), michael@0: srcTarget, destTarget); michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (dest->AttachType() == AttachmentType::GLRenderbuffer) { michael@0: GLuint destRB = dest->ProdRenderbuffer(); michael@0: ScopedFramebufferForRenderbuffer destWrapper(gl, destRB); michael@0: michael@0: gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, destWrapper.FB(), michael@0: src->Size(), dest->Size(), srcTarget); michael@0: michael@0: return; michael@0: } michael@0: michael@0: MOZ_CRASH("Unhandled dest->AttachType()."); michael@0: } michael@0: michael@0: if (src->AttachType() == AttachmentType::GLRenderbuffer) { michael@0: GLuint srcRB = src->ProdRenderbuffer(); michael@0: ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB); michael@0: michael@0: if (dest->AttachType() == AttachmentType::GLTexture) { michael@0: GLuint destTex = dest->ProdTexture(); michael@0: GLenum destTarget = dest->ProdTextureTarget(); michael@0: michael@0: gl->BlitHelper()->BlitFramebufferToTexture(srcWrapper.FB(), destTex, michael@0: src->Size(), dest->Size(), destTarget); michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (dest->AttachType() == AttachmentType::GLRenderbuffer) { michael@0: GLuint destRB = dest->ProdRenderbuffer(); michael@0: ScopedFramebufferForRenderbuffer destWrapper(gl, destRB); michael@0: michael@0: gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), destWrapper.FB(), michael@0: src->Size(), dest->Size()); michael@0: michael@0: return; michael@0: } michael@0: michael@0: MOZ_CRASH("Unhandled dest->AttachType()."); michael@0: } michael@0: michael@0: MOZ_CRASH("Unhandled src->AttachType()."); michael@0: } michael@0: michael@0: void michael@0: SharedSurface_GL::LockProd() michael@0: { michael@0: MOZ_ASSERT(!mIsLocked); michael@0: michael@0: LockProdImpl(); michael@0: michael@0: mGL->LockSurface(this); michael@0: mIsLocked = true; michael@0: } michael@0: michael@0: void michael@0: SharedSurface_GL::UnlockProd() michael@0: { michael@0: if (!mIsLocked) michael@0: return; michael@0: michael@0: UnlockProdImpl(); michael@0: michael@0: mGL->UnlockSurface(this); michael@0: mIsLocked = false; michael@0: } michael@0: michael@0: michael@0: SurfaceFactory_GL::SurfaceFactory_GL(GLContext* gl, michael@0: SharedSurfaceType type, michael@0: const SurfaceCaps& caps) michael@0: : SurfaceFactory(type, caps) michael@0: , mGL(gl) michael@0: , mFormats(gl->ChooseGLFormats(caps)) michael@0: { michael@0: ChooseBufferBits(caps, mDrawCaps, mReadCaps); michael@0: } michael@0: michael@0: void michael@0: SurfaceFactory_GL::ChooseBufferBits(const SurfaceCaps& caps, michael@0: SurfaceCaps& drawCaps, michael@0: SurfaceCaps& readCaps) const michael@0: { michael@0: SurfaceCaps screenCaps; michael@0: michael@0: screenCaps.color = caps.color; michael@0: screenCaps.alpha = caps.alpha; michael@0: screenCaps.bpp16 = caps.bpp16; michael@0: michael@0: screenCaps.depth = caps.depth; michael@0: screenCaps.stencil = caps.stencil; michael@0: michael@0: screenCaps.antialias = caps.antialias; michael@0: screenCaps.preserve = caps.preserve; michael@0: michael@0: if (caps.antialias) { michael@0: drawCaps = screenCaps; michael@0: readCaps.Clear(); michael@0: michael@0: // Color caps need to be duplicated in readCaps. michael@0: readCaps.color = caps.color; michael@0: readCaps.alpha = caps.alpha; michael@0: readCaps.bpp16 = caps.bpp16; michael@0: } else { michael@0: drawCaps.Clear(); michael@0: readCaps = screenCaps; michael@0: } michael@0: } michael@0: michael@0: michael@0: SharedSurface_Basic* michael@0: SharedSurface_Basic::Create(GLContext* gl, michael@0: const GLFormats& formats, michael@0: const IntSize& size, michael@0: bool hasAlpha) michael@0: { michael@0: gl->MakeCurrent(); michael@0: GLuint tex = CreateTexture(gl, formats.color_texInternalFormat, michael@0: formats.color_texFormat, michael@0: formats.color_texType, michael@0: size); michael@0: michael@0: SurfaceFormat format = SurfaceFormat::B8G8R8X8; michael@0: switch (formats.color_texInternalFormat) { michael@0: case LOCAL_GL_RGB: michael@0: case LOCAL_GL_RGB8: michael@0: if (formats.color_texType == LOCAL_GL_UNSIGNED_SHORT_5_6_5) michael@0: format = SurfaceFormat::R5G6B5; michael@0: else michael@0: format = SurfaceFormat::B8G8R8X8; michael@0: break; michael@0: case LOCAL_GL_RGBA: michael@0: case LOCAL_GL_RGBA8: michael@0: format = SurfaceFormat::B8G8R8A8; michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Unhandled Tex format."); michael@0: } michael@0: return new SharedSurface_Basic(gl, size, hasAlpha, format, tex); michael@0: } michael@0: michael@0: SharedSurface_Basic::SharedSurface_Basic(GLContext* gl, michael@0: const IntSize& size, michael@0: bool hasAlpha, michael@0: SurfaceFormat format, michael@0: GLuint tex) michael@0: : SharedSurface_GL(SharedSurfaceType::Basic, michael@0: AttachmentType::GLTexture, michael@0: gl, michael@0: size, michael@0: hasAlpha) michael@0: , mTex(tex), mFB(0) michael@0: { michael@0: mGL->MakeCurrent(); michael@0: mGL->fGenFramebuffers(1, &mFB); michael@0: michael@0: ScopedBindFramebuffer autoFB(mGL, mFB); michael@0: mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, michael@0: LOCAL_GL_COLOR_ATTACHMENT0, michael@0: LOCAL_GL_TEXTURE_2D, michael@0: mTex, michael@0: 0); michael@0: michael@0: GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); michael@0: if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { michael@0: mGL->fDeleteFramebuffers(1, &mFB); michael@0: mFB = 0; michael@0: } michael@0: michael@0: mData = Factory::CreateDataSourceSurfaceWithStride(size, format, michael@0: GetAlignedStride<4>(size.width * BytesPerPixel(format))); michael@0: } michael@0: michael@0: SharedSurface_Basic::~SharedSurface_Basic() michael@0: { michael@0: if (!mGL->MakeCurrent()) michael@0: return; michael@0: michael@0: if (mFB) michael@0: mGL->fDeleteFramebuffers(1, &mFB); michael@0: michael@0: mGL->fDeleteTextures(1, &mTex); michael@0: } michael@0: michael@0: void michael@0: SharedSurface_Basic::Fence() michael@0: { michael@0: mGL->MakeCurrent(); michael@0: michael@0: ScopedBindFramebuffer autoFB(mGL, mFB); michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: mData->Map(DataSourceSurface::MapType::WRITE, &map); michael@0: nsRefPtr wrappedData = michael@0: new gfxImageSurface(map.mData, michael@0: ThebesIntSize(mData->GetSize()), michael@0: map.mStride, michael@0: SurfaceFormatToImageFormat(mData->GetFormat())); michael@0: ReadPixelsIntoImageSurface(mGL, wrappedData); michael@0: mData->Unmap(); michael@0: } michael@0: michael@0: michael@0: michael@0: SharedSurface_GLTexture* michael@0: SharedSurface_GLTexture::Create(GLContext* prodGL, michael@0: GLContext* consGL, michael@0: const GLFormats& formats, michael@0: const gfx::IntSize& size, michael@0: bool hasAlpha, michael@0: GLuint texture) michael@0: { michael@0: MOZ_ASSERT(prodGL); michael@0: MOZ_ASSERT(!consGL || prodGL->SharesWith(consGL)); michael@0: michael@0: prodGL->MakeCurrent(); michael@0: michael@0: GLuint tex = texture; michael@0: michael@0: bool ownsTex = false; michael@0: michael@0: if (!tex) { michael@0: tex = CreateTextureForOffscreen(prodGL, formats, size); michael@0: ownsTex = true; michael@0: } michael@0: michael@0: return new SharedSurface_GLTexture(prodGL, consGL, size, hasAlpha, tex, ownsTex); michael@0: } michael@0: michael@0: SharedSurface_GLTexture::~SharedSurface_GLTexture() michael@0: { michael@0: if (!mGL->MakeCurrent()) michael@0: return; michael@0: michael@0: if (mOwnsTex) { michael@0: mGL->fDeleteTextures(1, &mTex); michael@0: } michael@0: michael@0: if (mSync) { michael@0: mGL->fDeleteSync(mSync); michael@0: } michael@0: } michael@0: michael@0: void michael@0: SharedSurface_GLTexture::Fence() michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: mGL->MakeCurrent(); michael@0: michael@0: if (mConsGL && mGL->IsExtensionSupported(GLContext::ARB_sync)) { michael@0: if (mSync) { michael@0: mGL->fDeleteSync(mSync); michael@0: mSync = 0; michael@0: } michael@0: michael@0: mSync = mGL->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0); michael@0: if (mSync) { michael@0: mGL->fFlush(); michael@0: return; michael@0: } michael@0: } michael@0: MOZ_ASSERT(!mSync); michael@0: michael@0: mGL->fFinish(); michael@0: } michael@0: michael@0: bool michael@0: SharedSurface_GLTexture::WaitSync() michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: if (!mSync) { michael@0: // We must have used glFinish instead of glFenceSync. michael@0: return true; michael@0: } michael@0: michael@0: mConsGL->MakeCurrent(); michael@0: MOZ_ASSERT(mConsGL->IsExtensionSupported(GLContext::ARB_sync)); michael@0: michael@0: mConsGL->fWaitSync(mSync, michael@0: 0, michael@0: LOCAL_GL_TIMEOUT_IGNORED); michael@0: mConsGL->fDeleteSync(mSync); michael@0: mSync = 0; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: GLuint michael@0: SharedSurface_GLTexture::ConsTexture(GLContext* consGL) michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: MOZ_ASSERT(consGL); michael@0: MOZ_ASSERT(mGL->SharesWith(consGL)); michael@0: MOZ_ASSERT_IF(mConsGL, consGL == mConsGL); michael@0: michael@0: mConsGL = consGL; michael@0: michael@0: return mTex; michael@0: } michael@0: michael@0: } /* namespace gfx */ michael@0: } /* namespace mozilla */