/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- // * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GLContext.h" #include "gfxImageSurface.h" #include "gfx2DGlue.h" #include #include "GrallocImages.h" // for GrallocImage #include "mozilla/layers/GrallocTextureHost.h" #include "mozilla/layers/CompositorOGL.h" #include "EGLImageHelpers.h" #include "GLReadTexImageHelper.h" namespace mozilla { namespace layers { using namespace android; static gfx::SurfaceFormat SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat, bool swapRB = false) { switch (aFormat) { case android::PIXEL_FORMAT_BGRA_8888: return swapRB ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8A8; case android::PIXEL_FORMAT_RGBA_8888: return swapRB ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::R8G8B8A8; case android::PIXEL_FORMAT_RGBX_8888: return swapRB ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::R8G8B8X8; case android::PIXEL_FORMAT_RGB_565: return gfx::SurfaceFormat::R5G6B5; case HAL_PIXEL_FORMAT_YCbCr_422_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_422_I: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS: case HAL_PIXEL_FORMAT_YV12: return gfx::SurfaceFormat::B8G8R8A8; // yup, use SurfaceFormat::B8G8R8A8 even though it's a YUV texture. This is an external texture. default: if (aFormat >= 0x100 && aFormat <= 0x1FF) { // Reserved range for HAL specific formats. return gfx::SurfaceFormat::B8G8R8A8; } else { // This is not super-unreachable, there's a bunch of hypothetical pixel // formats we don't deal with. // We only want to abort in debug builds here, since if we crash here // we'll take down the compositor process and thus the phone. This seems // like undesirable behaviour. We'd rather have a subtle artifact. printf_stderr(" xxxxx unknow android format %i\n", (int)aFormat); MOZ_ASSERT(false, "Unknown Android pixel format."); return gfx::SurfaceFormat::UNKNOWN; } } } static GLenum TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat) { switch (aFormat) { case HAL_PIXEL_FORMAT_YCbCr_422_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_422_I: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS: case HAL_PIXEL_FORMAT_YV12: return LOCAL_GL_TEXTURE_EXTERNAL; case android::PIXEL_FORMAT_RGBA_8888: case android::PIXEL_FORMAT_RGBX_8888: case android::PIXEL_FORMAT_RGB_565: return LOCAL_GL_TEXTURE_2D; default: if (aFormat >= 0x100 && aFormat <= 0x1FF) { // Reserved range for HAL specific formats. return LOCAL_GL_TEXTURE_EXTERNAL; } else { // This is not super-unreachable, there's a bunch of hypothetical pixel // formats we don't deal with. // We only want to abort in debug builds here, since if we crash here // we'll take down the compositor process and thus the phone. This seems // like undesirable behaviour. We'd rather have a subtle artifact. MOZ_ASSERT(false, "Unknown Android pixel format."); return LOCAL_GL_TEXTURE_EXTERNAL; } } } GrallocTextureSourceOGL::GrallocTextureSourceOGL(CompositorOGL* aCompositor, android::GraphicBuffer* aGraphicBuffer, gfx::SurfaceFormat aFormat) : mCompositor(aCompositor) , mGraphicBuffer(aGraphicBuffer) , mEGLImage(0) , mFormat(aFormat) , mNeedsReset(true) { MOZ_ASSERT(mGraphicBuffer.get()); } GrallocTextureSourceOGL::~GrallocTextureSourceOGL() { DeallocateDeviceData(); mCompositor = nullptr; } void GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit) { /* * The job of this function is to ensure that the texture is tied to the * android::GraphicBuffer, so that texturing will source the GraphicBuffer. * * To this effect we create an EGLImage wrapping this GraphicBuffer, * using EGLImageCreateFromNativeBuffer, and then we tie this EGLImage to our * texture using fEGLImageTargetTexture2D. */ MOZ_ASSERT(gl()); if (!IsValid()) { return; } gl()->MakeCurrent(); GLuint tex = GetGLTexture(); GLuint textureTarget = GetTextureTarget(); gl()->fActiveTexture(aTextureUnit); gl()->fBindTexture(textureTarget, tex); if (!mEGLImage) { mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); } gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); } bool GrallocTextureSourceOGL::IsValid() const { return !!gl() && !!mGraphicBuffer.get(); } gl::GLContext* GrallocTextureSourceOGL::gl() const { return mCompositor ? mCompositor->gl() : nullptr; } void GrallocTextureSourceOGL::SetCompositor(Compositor* aCompositor) { if (mCompositor && !aCompositor) { DeallocateDeviceData(); } mCompositor = static_cast(aCompositor); } GLenum GrallocTextureSourceOGL::GetTextureTarget() const { MOZ_ASSERT(mGraphicBuffer.get()); if (!mGraphicBuffer.get()) { return LOCAL_GL_TEXTURE_EXTERNAL; } return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); } gfx::SurfaceFormat GrallocTextureSourceOGL::GetFormat() const { if (!mGraphicBuffer.get()) { return gfx::SurfaceFormat::UNKNOWN; } if (GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL) { return gfx::SurfaceFormat::R8G8B8A8; } return mFormat; } void GrallocTextureSourceOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) { if (!aBackendData || !mGraphicBuffer.get()) { mCompositableBackendData = nullptr; DeallocateDeviceData(); return; } if (mCompositableBackendData != aBackendData) { mNeedsReset = true; } if (!mNeedsReset) { // Update binding to the EGLImage gl()->MakeCurrent(); GLuint tex = GetGLTexture(); GLuint textureTarget = GetTextureTarget(); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); gl()->fBindTexture(textureTarget, tex); gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); return; } mCompositableBackendData = aBackendData; if (!mCompositor) { return; } // delete old EGLImage DeallocateDeviceData(); gl()->MakeCurrent(); GLuint tex = GetGLTexture(); GLuint textureTarget = GetTextureTarget(); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); gl()->fBindTexture(textureTarget, tex); // create new EGLImage mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); mNeedsReset = false; } gfx::IntSize GrallocTextureSourceOGL::GetSize() const { if (!IsValid()) { NS_WARNING("Trying to access the size of an invalid GrallocTextureSourceOGL"); return gfx::IntSize(0, 0); } return gfx::IntSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight()); } void GrallocTextureSourceOGL::DeallocateDeviceData() { if (mEGLImage) { MOZ_ASSERT(gl()); gl()->MakeCurrent(); EGLImageDestroy(gl(), mEGLImage); mEGLImage = EGL_NO_IMAGE; } } GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags, const NewSurfaceDescriptorGralloc& aDescriptor) : TextureHost(aFlags) { android::GraphicBuffer* graphicBuffer = nullptr; gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN; mSize = aDescriptor.size(); mGrallocActor = static_cast(aDescriptor.bufferParent()); if (mGrallocActor) { mGrallocActor->AddTextureHost(this); graphicBuffer = mGrallocActor->GetGraphicBuffer(); } if (graphicBuffer) { format = SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(), aFlags & TEXTURE_RB_SWAPPED); } mTextureSource = new GrallocTextureSourceOGL(nullptr, graphicBuffer, format); } GrallocTextureHostOGL::~GrallocTextureHostOGL() { mTextureSource = nullptr; if (mGrallocActor) { mGrallocActor->RemoveTextureHost(); mGrallocActor = nullptr; } } void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor) { mTextureSource->SetCompositor(static_cast(aCompositor)); } bool GrallocTextureHostOGL::Lock() { return IsValid(); } void GrallocTextureHostOGL::Unlock() { // Unlock is done internally by binding the texture to another gralloc buffer } bool GrallocTextureHostOGL::IsValid() const { return mTextureSource->IsValid(); } gfx::SurfaceFormat GrallocTextureHostOGL::GetFormat() const { return mTextureSource->GetFormat(); } void GrallocTextureHostOGL::DeallocateSharedData() { if (mTextureSource) { mTextureSource->ForgetBuffer(); } if (mGrallocActor) { PGrallocBufferParent::Send__delete__(mGrallocActor); } } void GrallocTextureHostOGL::ForgetSharedData() { if (mTextureSource) { mTextureSource->ForgetBuffer(); } } void GrallocTextureHostOGL::DeallocateDeviceData() { mTextureSource->DeallocateDeviceData(); } LayerRenderState GrallocTextureHostOGL::GetRenderState() { if (IsValid()) { uint32_t flags = 0; if (mFlags & TEXTURE_NEEDS_Y_FLIP) { flags |= LAYER_RENDER_STATE_Y_FLIPPED; } if (mFlags & TEXTURE_RB_SWAPPED) { flags |= LAYER_RENDER_STATE_FORMAT_RB_SWAP; } return LayerRenderState(mTextureSource->mGraphicBuffer.get(), gfx::ThebesIntSize(mSize), flags, this); } return LayerRenderState(); } TemporaryRef GrallocTextureHostOGL::GetAsSurface() { return mTextureSource ? mTextureSource->GetAsSurface() : nullptr; } TemporaryRef GrallocTextureSourceOGL::GetAsSurface() { MOZ_ASSERT(gl()); if (!IsValid()) { return nullptr; } gl()->MakeCurrent(); GLuint tex = GetGLTexture(); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); gl()->fBindTexture(GetTextureTarget(), tex); if (!mEGLImage) { mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); } gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); RefPtr surf = IsValid() ? ReadBackSurface(gl(), tex, false, GetFormat()) : nullptr; gl()->fActiveTexture(LOCAL_GL_TEXTURE0); return surf.forget(); } GLuint GrallocTextureSourceOGL::GetGLTexture() { mCompositableBackendData->SetCompositor(mCompositor); return static_cast(mCompositableBackendData.get())->GetTexture(); } void GrallocTextureHostOGL::SetCompositableBackendSpecificData(CompositableBackendSpecificData* aBackendData) { mCompositableBackendData = aBackendData; if (mTextureSource) { mTextureSource->SetCompositableBackendSpecificData(aBackendData); } // Register this object to CompositableBackendSpecificData // as current TextureHost. if (aBackendData) { aBackendData->SetCurrentReleaseFenceTexture(this); } } } // namepsace layers } // namepsace mozilla