Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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