Skip to content

Commit

Permalink
Bug 1813267 - Add WebRender (Software OpenGL) handling for AndroidHar…
Browse files Browse the repository at this point in the history
…dwareBufferTextureHost r=gfx-reviewers,jnicol

Implementation is borrowed from RenderAndroidSurfaceTextureHost. Since Android SurfaceTexture and AndroidHardwareBuffer use same android GraphicBuffer.

Differential Revision: https://phabricator.services.mozilla.com/D168764
  • Loading branch information
sotaro committed Feb 6, 2023
1 parent c4caa90 commit 6535ce6
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 32 deletions.
169 changes: 161 additions & 8 deletions gfx/layers/opengl/TextureHostOGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,140 @@ bool SurfaceTextureHost::SupportsExternalCompositing(
return aBackend == WebRenderBackend::SOFTWARE;
}

////////////////////////////////////////////////////////////////////////
// AndroidHardwareBufferTextureSource

AndroidHardwareBufferTextureSource::AndroidHardwareBufferTextureSource(
TextureSourceProvider* aProvider,
AndroidHardwareBuffer* aAndroidHardwareBuffer, gfx::SurfaceFormat aFormat,
GLenum aTarget, GLenum aWrapMode, gfx::IntSize aSize)
: mGL(aProvider->GetGLContext()),
mAndroidHardwareBuffer(aAndroidHardwareBuffer),
mFormat(aFormat),
mTextureTarget(aTarget),
mWrapMode(aWrapMode),
mSize(aSize),
mEGLImage(EGL_NO_IMAGE),
mTextureHandle(0) {}

AndroidHardwareBufferTextureSource::~AndroidHardwareBufferTextureSource() {
DeleteTextureHandle();
DestroyEGLImage();
}

bool AndroidHardwareBufferTextureSource::EnsureEGLImage() {
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);

return true;
}

void AndroidHardwareBufferTextureSource::DeleteTextureHandle() {
if (!mTextureHandle) {
return;
}
MOZ_ASSERT(mGL);
mGL->fDeleteTextures(1, &mTextureHandle);
mTextureHandle = 0;
}

void AndroidHardwareBufferTextureSource::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;
}

void AndroidHardwareBufferTextureSource::BindTexture(
GLenum aTextureUnit, gfx::SamplingFilter aSamplingFilter) {
MOZ_ASSERT(mAndroidHardwareBuffer);
GLContext* gl = this->gl();
if (!gl || !gl->MakeCurrent()) {
NS_WARNING("Trying to bind a texture without a GLContext");
return;
}

if (!EnsureEGLImage()) {
return;
}

gl->fActiveTexture(aTextureUnit);
gl->fBindTexture(mTextureTarget, mTextureHandle);

ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget);
}

bool AndroidHardwareBufferTextureSource::IsValid() const { return !!gl(); }

void AndroidHardwareBufferTextureSource::DeallocateDeviceData() {
DestroyEGLImage();
DeleteTextureHandle();
mAndroidHardwareBuffer = nullptr;
}

////////////////////////////////////////////////////////////////////////
// AndroidHardwareBufferTextureHost

Expand All @@ -632,7 +766,9 @@ AndroidHardwareBufferTextureHost::Create(
AndroidHardwareBufferTextureHost::AndroidHardwareBufferTextureHost(
TextureFlags aFlags, AndroidHardwareBuffer* aAndroidHardwareBuffer)
: TextureHost(TextureHostType::AndroidHardwareBuffer, aFlags),
mAndroidHardwareBuffer(aAndroidHardwareBuffer) {}
mAndroidHardwareBuffer(aAndroidHardwareBuffer) {
MOZ_ASSERT(mAndroidHardwareBuffer);
}

AndroidHardwareBufferTextureHost::~AndroidHardwareBufferTextureHost() {}

Expand Down Expand Up @@ -702,8 +838,16 @@ void AndroidHardwareBufferTextureHost::PushResourceUpdates(
auto method = aOp == TextureHost::ADD_IMAGE
? &wr::TransactionBuilder::AddExternalImage
: &wr::TransactionBuilder::UpdateExternalImage;
auto imageType = wr::ExternalImageType::TextureHandle(
wr::ImageBufferKind::TextureExternal);

// Prefer TextureExternal unless the backend requires TextureRect.
TextureHost::NativeTexturePolicy policy =
TextureHost::BackendNativeTexturePolicy(aResources.GetBackendType(),
GetSize());
auto imageType = policy == TextureHost::NativeTexturePolicy::REQUIRE
? wr::ExternalImageType::TextureHandle(
wr::ImageBufferKind::TextureRect)
: wr::ExternalImageType::TextureHandle(
wr::ImageBufferKind::TextureExternal);

switch (GetFormat()) {
case gfx::SurfaceFormat::R8G8B8X8:
Expand All @@ -729,17 +873,21 @@ void AndroidHardwareBufferTextureHost::PushDisplayItems(
wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) {
bool preferCompositorSurface =
aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE);
bool supportsExternalCompositing =
SupportsExternalCompositing(aBuilder.GetBackendType());

switch (GetFormat()) {
case gfx::SurfaceFormat::R8G8B8X8:
case gfx::SurfaceFormat::R8G8B8A8:
case gfx::SurfaceFormat::B8G8R8A8:
case gfx::SurfaceFormat::B8G8R8X8: {
MOZ_ASSERT(aImageKeys.length() == 1);
aBuilder.PushImage(
aBounds, aClip, true, false, aFilter, aImageKeys[0],
!(mFlags & TextureFlags::NON_PREMULTIPLIED),
wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE));
aBuilder.PushImage(aBounds, aClip, true, false, aFilter, aImageKeys[0],
!(mFlags & TextureFlags::NON_PREMULTIPLIED),
wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
preferCompositorSurface, supportsExternalCompositing);
break;
}
default: {
Expand All @@ -748,6 +896,11 @@ void AndroidHardwareBufferTextureHost::PushDisplayItems(
}
}

bool AndroidHardwareBufferTextureHost::SupportsExternalCompositing(
WebRenderBackend aBackend) {
return aBackend == WebRenderBackend::SOFTWARE;
}

#endif // MOZ_WIDGET_ANDROID

////////////////////////////////////////////////////////////////////////
Expand Down
49 changes: 49 additions & 0 deletions gfx/layers/opengl/TextureHostOGL.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,53 @@ class SurfaceTextureHost : public TextureHost {
RefPtr<SurfaceTextureSource> mTextureSource;
};

class AndroidHardwareBufferTextureSource : public TextureSource,
public TextureSourceOGL {
public:
AndroidHardwareBufferTextureSource(
TextureSourceProvider* aProvider,
AndroidHardwareBuffer* aAndroidHardwareBuffer, gfx::SurfaceFormat aFormat,
GLenum aTarget, GLenum aWrapMode, gfx::IntSize aSize);

const char* Name() const override { return "SurfaceTextureSource"; }

TextureSourceOGL* AsSourceOGL() override { return this; }

void BindTexture(GLenum activetex,
gfx::SamplingFilter aSamplingFilter) override;

bool IsValid() const override;

gfx::IntSize GetSize() const override { return mSize; }

gfx::SurfaceFormat GetFormat() const override { return mFormat; }

GLenum GetTextureTarget() const override { return mTextureTarget; }

GLenum GetWrapMode() const override { return mWrapMode; }

void DeallocateDeviceData() override;

gl::GLContext* gl() const { return mGL; }

protected:
virtual ~AndroidHardwareBufferTextureSource();

bool EnsureEGLImage();
void DestroyEGLImage();
void DeleteTextureHandle();

RefPtr<gl::GLContext> mGL;
RefPtr<AndroidHardwareBuffer> mAndroidHardwareBuffer;
const gfx::SurfaceFormat mFormat;
const GLenum mTextureTarget;
const GLenum mWrapMode;
const gfx::IntSize mSize;

EGLImage mEGLImage;
GLuint mTextureHandle;
};

class AndroidHardwareBufferTextureHost : public TextureHost {
public:
static already_AddRefed<AndroidHardwareBufferTextureHost> Create(
Expand Down Expand Up @@ -500,6 +547,8 @@ class AndroidHardwareBufferTextureHost : public TextureHost {
return mAndroidHardwareBuffer;
}

bool SupportsExternalCompositing(WebRenderBackend aBackend) override;

// gecko does not need deferred deletion with WebRender
// GPU/hardware task end could be checked by android fence.
bool NeedsDeferredDeletion() const override { return false; }
Expand Down
83 changes: 83 additions & 0 deletions gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
#include "RenderAndroidHardwareBufferTextureHost.h"

#include "mozilla/layers/AndroidHardwareBuffer.h"
#include "mozilla/webrender/RenderThread.h"
#include "GLContextEGL.h"
#include "GLLibraryEGL.h"
#include "GLReadTexImageHelper.h"
#include "OGLShaderConfig.h"

namespace mozilla {
namespace wr {
Expand Down Expand Up @@ -159,5 +162,85 @@ void RenderAndroidHardwareBufferTextureHost::DestroyEGLImage() {
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<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<DataSourceSurface> surf = 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;
}

DataSourceSurface::MappedSurface map;
if (!readback->Map(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
Loading

0 comments on commit 6535ce6

Please sign in to comment.