michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 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 "GLContext.h" michael@0: #include "gfx2DGlue.h" michael@0: #include michael@0: #include "GrallocImages.h" // for GrallocImage michael@0: #include "mozilla/layers/GrallocTextureHost.h" michael@0: #include "mozilla/layers/CompositorOGL.h" michael@0: #include "EGLImageHelpers.h" michael@0: #include "GLReadTexImageHelper.h" michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: using namespace android; michael@0: michael@0: static gfx::SurfaceFormat michael@0: SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat, michael@0: bool swapRB = false) michael@0: { michael@0: switch (aFormat) { michael@0: case android::PIXEL_FORMAT_BGRA_8888: michael@0: return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8; michael@0: case android::PIXEL_FORMAT_RGBA_8888: michael@0: return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8; michael@0: case android::PIXEL_FORMAT_RGBX_8888: michael@0: return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8; michael@0: case android::PIXEL_FORMAT_RGB_565: michael@0: return gfx::SurfaceFormat::R5G6B5; michael@0: case HAL_PIXEL_FORMAT_YCbCr_422_SP: michael@0: case HAL_PIXEL_FORMAT_YCrCb_420_SP: michael@0: case HAL_PIXEL_FORMAT_YCbCr_422_I: michael@0: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: michael@0: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS: michael@0: case HAL_PIXEL_FORMAT_YV12: michael@0: return gfx::SurfaceFormat::R8G8B8A8; // yup, use SurfaceFormat::R8G8B8A8 even though it's a YUV texture. This is an external texture. michael@0: default: michael@0: if (aFormat >= 0x100 && aFormat <= 0x1FF) { michael@0: // Reserved range for HAL specific formats. michael@0: return gfx::SurfaceFormat::R8G8B8A8; michael@0: } else { michael@0: // This is not super-unreachable, there's a bunch of hypothetical pixel michael@0: // formats we don't deal with. michael@0: // We only want to abort in debug builds here, since if we crash here michael@0: // we'll take down the compositor process and thus the phone. This seems michael@0: // like undesirable behaviour. We'd rather have a subtle artifact. michael@0: printf_stderr(" xxxxx unknow android format %i\n", (int)aFormat); michael@0: MOZ_ASSERT(false, "Unknown Android pixel format."); michael@0: return gfx::SurfaceFormat::UNKNOWN; michael@0: } michael@0: } michael@0: } michael@0: michael@0: static GLenum michael@0: TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat) michael@0: { michael@0: switch (aFormat) { michael@0: case HAL_PIXEL_FORMAT_YCbCr_422_SP: michael@0: case HAL_PIXEL_FORMAT_YCrCb_420_SP: michael@0: case HAL_PIXEL_FORMAT_YCbCr_422_I: michael@0: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: michael@0: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS: michael@0: case HAL_PIXEL_FORMAT_YV12: michael@0: return LOCAL_GL_TEXTURE_EXTERNAL; michael@0: case android::PIXEL_FORMAT_BGRA_8888: michael@0: case android::PIXEL_FORMAT_RGBA_8888: michael@0: case android::PIXEL_FORMAT_RGBX_8888: michael@0: case android::PIXEL_FORMAT_RGB_565: michael@0: return LOCAL_GL_TEXTURE_2D; michael@0: default: michael@0: if (aFormat >= 0x100 && aFormat <= 0x1FF) { michael@0: // Reserved range for HAL specific formats. michael@0: return LOCAL_GL_TEXTURE_EXTERNAL; michael@0: } else { michael@0: // This is not super-unreachable, there's a bunch of hypothetical pixel michael@0: // formats we don't deal with. michael@0: // We only want to abort in debug builds here, since if we crash here michael@0: // we'll take down the compositor process and thus the phone. This seems michael@0: // like undesirable behaviour. We'd rather have a subtle artifact. michael@0: MOZ_ASSERT(false, "Unknown Android pixel format."); michael@0: return LOCAL_GL_TEXTURE_EXTERNAL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: GrallocTextureSourceOGL::GrallocTextureSourceOGL(CompositorOGL* aCompositor, michael@0: android::GraphicBuffer* aGraphicBuffer, michael@0: gfx::SurfaceFormat aFormat) michael@0: : mCompositor(aCompositor) michael@0: , mGraphicBuffer(aGraphicBuffer) michael@0: , mEGLImage(0) michael@0: , mFormat(aFormat) michael@0: , mNeedsReset(true) michael@0: { michael@0: MOZ_ASSERT(mGraphicBuffer.get()); michael@0: } michael@0: michael@0: GrallocTextureSourceOGL::~GrallocTextureSourceOGL() michael@0: { michael@0: DeallocateDeviceData(); michael@0: mCompositor = nullptr; michael@0: } michael@0: michael@0: void michael@0: GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) michael@0: { michael@0: /* michael@0: * The job of this function is to ensure that the texture is tied to the michael@0: * android::GraphicBuffer, so that texturing will source the GraphicBuffer. michael@0: * michael@0: * To this effect we create an EGLImage wrapping this GraphicBuffer, michael@0: * using EGLImageCreateFromNativeBuffer, and then we tie this EGLImage to our michael@0: * texture using fEGLImageTargetTexture2D. michael@0: */ michael@0: MOZ_ASSERT(gl()); michael@0: if (!IsValid()) { michael@0: return; michael@0: } michael@0: gl()->MakeCurrent(); michael@0: michael@0: GLuint tex = GetGLTexture(); michael@0: GLuint textureTarget = GetTextureTarget(); michael@0: michael@0: gl()->fActiveTexture(aTextureUnit); michael@0: gl()->fBindTexture(textureTarget, tex); michael@0: michael@0: if (mCompositableBackendData) { michael@0: // There are two paths for locking/unlocking - if mCompositableBackendData is michael@0: // set, we use the texture on there, otherwise we use michael@0: // CompositorBackendSpecificData from the compositor and bind the EGLImage michael@0: // only in Lock(). michael@0: if (!mEGLImage) { michael@0: mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); michael@0: } michael@0: gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); michael@0: } michael@0: michael@0: ApplyFilterToBoundTexture(gl(), aFilter, textureTarget); michael@0: } michael@0: michael@0: void GrallocTextureSourceOGL::Lock() michael@0: { michael@0: if (mCompositableBackendData) return; michael@0: michael@0: MOZ_ASSERT(IsValid()); michael@0: michael@0: mTexture = mCompositor->GetTemporaryTexture(GetTextureTarget(), LOCAL_GL_TEXTURE0); michael@0: michael@0: GLuint textureTarget = GetTextureTarget(); michael@0: michael@0: gl()->MakeCurrent(); michael@0: gl()->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: gl()->fBindTexture(textureTarget, mTexture); michael@0: if (!mEGLImage) { michael@0: mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); michael@0: } michael@0: gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); michael@0: } michael@0: michael@0: bool michael@0: GrallocTextureSourceOGL::IsValid() const michael@0: { michael@0: return !!gl() && !!mGraphicBuffer.get() && (!!mCompositor || !!mCompositableBackendData); michael@0: } michael@0: michael@0: gl::GLContext* michael@0: GrallocTextureSourceOGL::gl() const michael@0: { michael@0: return mCompositor ? mCompositor->gl() : nullptr; michael@0: } michael@0: michael@0: void michael@0: GrallocTextureSourceOGL::SetCompositor(Compositor* aCompositor) michael@0: { michael@0: if (mCompositor && !aCompositor) { michael@0: DeallocateDeviceData(); michael@0: } michael@0: mCompositor = static_cast(aCompositor); michael@0: } michael@0: michael@0: michael@0: GLenum michael@0: GrallocTextureSourceOGL::GetTextureTarget() const michael@0: { michael@0: MOZ_ASSERT(gl()); michael@0: MOZ_ASSERT(mGraphicBuffer.get()); michael@0: michael@0: if (!gl() || !mGraphicBuffer.get()) { michael@0: return LOCAL_GL_TEXTURE_EXTERNAL; michael@0: } michael@0: michael@0: // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will michael@0: // result in black pixels when trying to draw from bound textures. michael@0: // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on michael@0: // performance. michael@0: // See Bug 950050. michael@0: if (gl()->Renderer() == gl::GLRenderer::SGX530 || michael@0: gl()->Renderer() == gl::GLRenderer::SGX540) { michael@0: return LOCAL_GL_TEXTURE_EXTERNAL; michael@0: } michael@0: michael@0: return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); michael@0: } michael@0: michael@0: void michael@0: GrallocTextureSourceOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) michael@0: { michael@0: if (!aBackendData) { michael@0: mCompositableBackendData = nullptr; michael@0: DeallocateDeviceData(); michael@0: return; michael@0: } michael@0: michael@0: if (mCompositableBackendData != aBackendData) { michael@0: mNeedsReset = true; michael@0: } michael@0: michael@0: if (!mNeedsReset) { michael@0: // Update binding to the EGLImage michael@0: gl()->MakeCurrent(); michael@0: GLuint tex = GetGLTexture(); michael@0: GLuint textureTarget = GetTextureTarget(); michael@0: gl()->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: gl()->fBindTexture(textureTarget, tex); michael@0: gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); michael@0: return; michael@0: } michael@0: michael@0: mCompositableBackendData = aBackendData; michael@0: michael@0: if (!mCompositor) { michael@0: return; michael@0: } michael@0: michael@0: // delete old EGLImage michael@0: DeallocateDeviceData(); michael@0: michael@0: gl()->MakeCurrent(); michael@0: GLuint tex = GetGLTexture(); michael@0: GLuint textureTarget = GetTextureTarget(); michael@0: michael@0: gl()->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: gl()->fBindTexture(textureTarget, tex); michael@0: // create new EGLImage michael@0: mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); michael@0: gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); michael@0: mNeedsReset = false; michael@0: } michael@0: michael@0: gfx::IntSize michael@0: GrallocTextureSourceOGL::GetSize() const michael@0: { michael@0: if (!IsValid()) { michael@0: NS_WARNING("Trying to access the size of an invalid GrallocTextureSourceOGL"); michael@0: return gfx::IntSize(0, 0); michael@0: } michael@0: return gfx::IntSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight()); michael@0: } michael@0: michael@0: void michael@0: GrallocTextureSourceOGL::DeallocateDeviceData() michael@0: { michael@0: if (mEGLImage) { michael@0: MOZ_ASSERT(gl()); michael@0: gl()->MakeCurrent(); michael@0: EGLImageDestroy(gl(), mEGLImage); michael@0: mEGLImage = EGL_NO_IMAGE; michael@0: } michael@0: } michael@0: michael@0: GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags, michael@0: const NewSurfaceDescriptorGralloc& aDescriptor) michael@0: : TextureHost(aFlags) michael@0: { michael@0: android::GraphicBuffer* graphicBuffer = nullptr; michael@0: gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN; michael@0: michael@0: mSize = aDescriptor.size(); michael@0: mGrallocActor = michael@0: static_cast(aDescriptor.bufferParent()); michael@0: michael@0: if (mGrallocActor) { michael@0: mGrallocActor->AddTextureHost(this); michael@0: graphicBuffer = mGrallocActor->GetGraphicBuffer(); michael@0: } michael@0: michael@0: if (graphicBuffer) { michael@0: format = michael@0: SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(), michael@0: aFlags & TEXTURE_RB_SWAPPED); michael@0: } michael@0: mTextureSource = new GrallocTextureSourceOGL(nullptr, michael@0: graphicBuffer, michael@0: format); michael@0: } michael@0: michael@0: GrallocTextureHostOGL::~GrallocTextureHostOGL() michael@0: { michael@0: mTextureSource = nullptr; michael@0: if (mGrallocActor) { michael@0: mGrallocActor->RemoveTextureHost(); michael@0: mGrallocActor = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor) michael@0: { michael@0: mTextureSource->SetCompositor(static_cast(aCompositor)); michael@0: } michael@0: michael@0: bool michael@0: GrallocTextureHostOGL::Lock() michael@0: { michael@0: if (IsValid()) { michael@0: mTextureSource->Lock(); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: GrallocTextureHostOGL::Unlock() michael@0: { michael@0: // Unlock is done internally by binding the texture to another gralloc buffer michael@0: } michael@0: michael@0: bool michael@0: GrallocTextureHostOGL::IsValid() const michael@0: { michael@0: return mTextureSource->IsValid(); michael@0: } michael@0: michael@0: gfx::SurfaceFormat michael@0: GrallocTextureHostOGL::GetFormat() const michael@0: { michael@0: return mTextureSource->GetFormat(); michael@0: } michael@0: michael@0: void michael@0: GrallocTextureHostOGL::DeallocateSharedData() michael@0: { michael@0: if (mTextureSource) { michael@0: mTextureSource->ForgetBuffer(); michael@0: } michael@0: if (mGrallocActor) { michael@0: PGrallocBufferParent::Send__delete__(mGrallocActor); michael@0: } michael@0: } michael@0: michael@0: void michael@0: GrallocTextureHostOGL::ForgetSharedData() michael@0: { michael@0: if (mTextureSource) { michael@0: mTextureSource->ForgetBuffer(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: GrallocTextureHostOGL::DeallocateDeviceData() michael@0: { michael@0: mTextureSource->DeallocateDeviceData(); michael@0: } michael@0: michael@0: LayerRenderState michael@0: GrallocTextureHostOGL::GetRenderState() michael@0: { michael@0: if (IsValid()) { michael@0: uint32_t flags = 0; michael@0: if (mFlags & TEXTURE_NEEDS_Y_FLIP) { michael@0: flags |= LAYER_RENDER_STATE_Y_FLIPPED; michael@0: } michael@0: if (mFlags & TEXTURE_RB_SWAPPED) { michael@0: flags |= LAYER_RENDER_STATE_FORMAT_RB_SWAP; michael@0: } michael@0: return LayerRenderState(mTextureSource->mGraphicBuffer.get(), michael@0: gfx::ThebesIntSize(mSize), michael@0: flags, michael@0: this); michael@0: } michael@0: michael@0: return LayerRenderState(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: GrallocTextureHostOGL::GetAsSurface() { michael@0: return mTextureSource ? mTextureSource->GetAsSurface() michael@0: : nullptr; michael@0: } michael@0: michael@0: TemporaryRef michael@0: GrallocTextureSourceOGL::GetAsSurface() { michael@0: if (!IsValid()) { michael@0: return nullptr; michael@0: } michael@0: gl()->MakeCurrent(); michael@0: michael@0: GLuint tex = GetGLTexture(); michael@0: gl()->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: gl()->fBindTexture(GetTextureTarget(), tex); michael@0: if (!mEGLImage) { michael@0: mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); michael@0: } michael@0: gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); michael@0: michael@0: RefPtr surf = michael@0: IsValid() ? ReadBackSurface(gl(), tex, false, GetFormat()) michael@0: : nullptr; michael@0: michael@0: gl()->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: return surf.forget(); michael@0: } michael@0: michael@0: GLuint michael@0: GrallocTextureSourceOGL::GetGLTexture() michael@0: { michael@0: if (mCompositableBackendData) { michael@0: mCompositableBackendData->SetCompositor(mCompositor); michael@0: return static_cast(mCompositableBackendData.get())->GetTexture(); michael@0: } michael@0: michael@0: return mTexture; michael@0: } michael@0: michael@0: void michael@0: GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) michael@0: { michael@0: mCompositableBackendData = aBackendData; michael@0: if (mTextureSource) { michael@0: mTextureSource->SetCompositableBackendSpecificData(aBackendData); michael@0: } michael@0: // Register this object to CompositableBackendSpecificData michael@0: // as current TextureHost. michael@0: if (aBackendData) { michael@0: aBackendData->SetCurrentReleaseFenceTexture(this); michael@0: } michael@0: } michael@0: michael@0: } // namepsace layers michael@0: } // namepsace mozilla