diff options
Diffstat (limited to 'gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp')
-rw-r--r-- | gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp b/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp new file mode 100644 index 0000000000..779b376290 --- /dev/null +++ b/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "RenderAndroidHardwareBufferTextureHost.h" + +#include "mozilla/layers/AndroidHardwareBuffer.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/gfx/2D.h" +#include "GLContextEGL.h" +#include "GLLibraryEGL.h" +#include "GLReadTexImageHelper.h" +#include "OGLShaderConfig.h" + +namespace mozilla { +namespace wr { + +RenderAndroidHardwareBufferTextureHost::RenderAndroidHardwareBufferTextureHost( + layers::AndroidHardwareBuffer* aAndroidHardwareBuffer) + : mAndroidHardwareBuffer(aAndroidHardwareBuffer), + mEGLImage(EGL_NO_IMAGE), + mTextureHandle(0) { + MOZ_ASSERT(mAndroidHardwareBuffer); + MOZ_COUNT_CTOR_INHERITED(RenderAndroidHardwareBufferTextureHost, + RenderTextureHost); +} + +RenderAndroidHardwareBufferTextureHost:: + ~RenderAndroidHardwareBufferTextureHost() { + MOZ_COUNT_DTOR_INHERITED(RenderAndroidHardwareBufferTextureHost, + RenderTextureHost); + DeleteTextureHandle(); + DestroyEGLImage(); +} + +gfx::IntSize RenderAndroidHardwareBufferTextureHost::GetSize() const { + if (mAndroidHardwareBuffer) { + return mAndroidHardwareBuffer->mSize; + } + return gfx::IntSize(); +} + +bool RenderAndroidHardwareBufferTextureHost::EnsureLockable() { + if (!mAndroidHardwareBuffer) { + return false; + } + + auto fenceFd = mAndroidHardwareBuffer->GetAndResetAcquireFence(); + if (fenceFd.IsValid()) { + const auto& gle = gl::GLContextEGL::Cast(mGL); + const auto& egl = gle->mEgl; + + auto rawFD = fenceFd.TakePlatformHandle(); + const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID, + rawFD.get(), LOCAL_EGL_NONE}; + + EGLSync sync = + egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync) { + // Release fd here, since it is owned by EGLSync + Unused << rawFD.release(); + + if (egl->IsExtensionSupported(gl::EGLExtension::KHR_wait_sync)) { + egl->fWaitSync(sync, 0); + } else { + egl->fClientWaitSync(sync, 0, LOCAL_EGL_FOREVER); + } + egl->fDestroySync(sync); + } else { + gfxCriticalNote << "Failed to create EGLSync from acquire fence fd"; + } + } + + if (mTextureHandle) { + return true; + } + + if (!mEGLImage) { + // XXX add crop handling for video + // Should only happen the first time. + const auto& gle = gl::GLContextEGL::Cast(mGL); + const auto& egl = gle->mEgl; + + const EGLint attrs[] = { + LOCAL_EGL_IMAGE_PRESERVED, + LOCAL_EGL_TRUE, + LOCAL_EGL_NONE, + LOCAL_EGL_NONE, + }; + + EGLClientBuffer clientBuffer = egl->mLib->fGetNativeClientBufferANDROID( + mAndroidHardwareBuffer->GetNativeBuffer()); + mEGLImage = egl->fCreateImage( + EGL_NO_CONTEXT, LOCAL_EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + } + MOZ_ASSERT(mEGLImage); + + mGL->fGenTextures(1, &mTextureHandle); + mGL->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, mTextureHandle); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_T, + LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_S, + LOCAL_GL_CLAMP_TO_EDGE); + mGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_EXTERNAL, mEGLImage); + + ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, + LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureHandle); + return true; +} + +wr::WrExternalImage RenderAndroidHardwareBufferTextureHost::Lock( + uint8_t aChannelIndex, gl::GLContext* aGL) { + MOZ_ASSERT(aChannelIndex == 0); + + if (mGL.get() != aGL) { + if (mGL) { + // This should not happen. + MOZ_ASSERT_UNREACHABLE("Unexpected GL context"); + return InvalidToWrExternalImage(); + } + mGL = aGL; + } + + if (!mGL || !mGL->MakeCurrent()) { + return InvalidToWrExternalImage(); + } + + if (!EnsureLockable()) { + return InvalidToWrExternalImage(); + } + + const auto uvs = GetUvCoords(GetSize()); + return NativeTextureToWrExternalImage( + mTextureHandle, uvs.first.x, uvs.first.y, uvs.second.x, uvs.second.y); +} + +void RenderAndroidHardwareBufferTextureHost::Unlock() {} + +size_t RenderAndroidHardwareBufferTextureHost::Bytes() { + return GetSize().width * GetSize().height * + BytesPerPixel(mAndroidHardwareBuffer->mFormat); +} + +void RenderAndroidHardwareBufferTextureHost::DeleteTextureHandle() { + if (!mTextureHandle) { + return; + } + MOZ_ASSERT(mGL); + mGL->fDeleteTextures(1, &mTextureHandle); + mTextureHandle = 0; +} + +void RenderAndroidHardwareBufferTextureHost::DestroyEGLImage() { + if (!mEGLImage) { + return; + } + MOZ_ASSERT(mGL); + const auto& gle = gl::GLContextEGL::Cast(mGL); + const auto& egl = gle->mEgl; + egl->fDestroyImage(mEGLImage); + mEGLImage = EGL_NO_IMAGE; +} + +gfx::SurfaceFormat RenderAndroidHardwareBufferTextureHost::GetFormat() const { + MOZ_ASSERT(mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8A8 || + mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8X8); + + if (mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8A8) { + return gfx::SurfaceFormat::B8G8R8A8; + } + + if (mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8X8) { + return gfx::SurfaceFormat::B8G8R8X8; + } + + gfxCriticalNoteOnce + << "Unexpected color format of RenderAndroidSurfaceTextureHost"; + + return gfx::SurfaceFormat::UNKNOWN; +} + +already_AddRefed<gfx::DataSourceSurface> +RenderAndroidHardwareBufferTextureHost::ReadTexImage() { + if (!mGL) { + mGL = RenderThread::Get()->SingletonGL(); + if (!mGL) { + return nullptr; + } + } + + if (!EnsureLockable()) { + return nullptr; + } + + /* Allocate resulting image surface */ + int32_t stride = GetSize().width * BytesPerPixel(GetFormat()); + RefPtr<gfx::DataSourceSurface> surf = + gfx::Factory::CreateDataSourceSurfaceWithStride(GetSize(), GetFormat(), + stride); + if (!surf) { + return nullptr; + } + + layers::ShaderConfigOGL config = layers::ShaderConfigFromTargetAndFormat( + LOCAL_GL_TEXTURE_EXTERNAL, mAndroidHardwareBuffer->mFormat); + int shaderConfig = config.mFeatures; + + bool ret = mGL->ReadTexImageHelper()->ReadTexImage( + surf, mTextureHandle, LOCAL_GL_TEXTURE_EXTERNAL, GetSize(), shaderConfig, + /* aYInvert */ false); + if (!ret) { + return nullptr; + } + + return surf.forget(); +} + +bool RenderAndroidHardwareBufferTextureHost::MapPlane( + RenderCompositor* aCompositor, uint8_t aChannelIndex, + PlaneInfo& aPlaneInfo) { + RefPtr<gfx::DataSourceSurface> readback = ReadTexImage(); + if (!readback) { + return false; + } + + gfx::DataSourceSurface::MappedSurface map; + if (!readback->Map(gfx::DataSourceSurface::MapType::READ, &map)) { + return false; + } + + mReadback = readback; + aPlaneInfo.mSize = GetSize(); + aPlaneInfo.mStride = map.mStride; + aPlaneInfo.mData = map.mData; + return true; +} + +void RenderAndroidHardwareBufferTextureHost::UnmapPlanes() { + if (mReadback) { + mReadback->Unmap(); + mReadback = nullptr; + } +} + +} // namespace wr +} // namespace mozilla |