Bug 1898238 - Make SharedSurface_EGLImage and (Render)EGLImageTextureHost work. r=sotaro,jgilbert

SharedSurface_EGLImage has mostly avoided bitrotting. However, it now
sometimes gets destructed after the GLContext has been lost, resulting
in a crash when attempting to clean up its resources. To avoid this,
we hold a weak reference to the EglDisplay, and use that to clean up
rather than the GLContext.

To support rendering with SWGL, EGLImageTextureHost ensures the
correct preferCompositorSurface, supportsExternalCompositing, and
imageType values are sent to webrender. RenderEGLImageTextureHost now
implements RenderTextureHostSWGL, as well as the new
CreateTextureSource() function, and EGLImageTextureSource is
initialized with a GL context reference so that it may actually work.

Additionally we use eglWaitSync() rather than eglClientWaitSync()
where available for improved performance. And we ensure that we bind
the EGL images' textures to GL_TEXTURE_2D rather than
GL_TEXTURE_EXTERNAL when running on the Android emulator to work
around the buggy implementation in some emulator versions.

Differential Revision: https://phabricator.services.mozilla.com/D211291
This commit is contained in:
Jamie Nicol
2024-07-22 20:23:38 +00:00
parent c5467289a3
commit 4e1b51a92c
11 changed files with 249 additions and 63 deletions

View File

@@ -3578,9 +3578,11 @@ class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr {
#ifdef MOZ_WIDGET_GTK
return LOCAL_GL_TEXTURE_2D;
#else
return IsExtensionSupported(OES_EGL_image_external)
? LOCAL_GL_TEXTURE_EXTERNAL
: LOCAL_GL_TEXTURE_2D;
if (IsExtensionSupported(OES_EGL_image_external) &&
mRenderer != GLRenderer::AndroidEmulator) {
return LOCAL_GL_TEXTURE_EXTERNAL;
}
return LOCAL_GL_TEXTURE_2D;
#endif
}

View File

@@ -69,18 +69,18 @@ SharedSurface_EGLImage::SharedSurface_EGLImage(const SharedSurfaceDesc& desc,
const EGLImage image)
: SharedSurface(desc, std::move(fb)),
mMutex("SharedSurface_EGLImage mutex"),
mEglDisplay(GLContextEGL::Cast(desc.gl)->mEgl),
mImage(image) {}
SharedSurface_EGLImage::~SharedSurface_EGLImage() {
const auto& gle = GLContextEGL::Cast(mDesc.gl);
const auto& egl = gle->mEgl;
egl->fDestroyImage(mImage);
if (auto display = mEglDisplay.lock()) {
display->fDestroyImage(mImage);
if (mSync) {
// We can't call this unless we have the ext, but we will always have
// the ext if we have something to destroy.
egl->fDestroySync(mSync);
mSync = 0;
display->fDestroySync(mSync);
}
}
}

View File

@@ -19,6 +19,7 @@
namespace mozilla {
namespace gl {
class EglDisplay;
class GLLibraryEGL;
// -
@@ -27,6 +28,7 @@ class GLLibraryEGL;
class SharedSurface_EGLImage final : public SharedSurface {
mutable Mutex mMutex MOZ_UNANNOTATED;
EGLSync mSync = 0;
const std::weak_ptr<EglDisplay> mEglDisplay;
public:
const EGLImage mImage;

View File

@@ -927,7 +927,9 @@ EGLImageTextureSource::EGLImageTextureSource(TextureSourceProvider* aProvider,
gfx::SurfaceFormat aFormat,
GLenum aTarget, GLenum aWrapMode,
gfx::IntSize aSize)
: mImage(aImage),
: mGL(aProvider->GetGLContext()),
mCompositor(aProvider->AsCompositorOGL()),
mImage(aImage),
mFormat(aFormat),
mTextureTarget(aTarget),
mWrapMode(aWrapMode),
@@ -989,9 +991,8 @@ EGLImageTextureHost::~EGLImageTextureHost() = default;
gl::GLContext* EGLImageTextureHost::gl() const { return nullptr; }
gfx::SurfaceFormat EGLImageTextureHost::GetFormat() const {
MOZ_ASSERT(mTextureSource);
return mTextureSource ? mTextureSource->GetFormat()
: gfx::SurfaceFormat::UNKNOWN;
return mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
: gfx::SurfaceFormat::R8G8B8X8;
}
void EGLImageTextureHost::CreateRenderTexture(
@@ -999,7 +1000,7 @@ void EGLImageTextureHost::CreateRenderTexture(
MOZ_ASSERT(mExternalImageId.isSome());
RefPtr<wr::RenderTextureHost> texture =
new wr::RenderEGLImageTextureHost(mImage, mSync, mSize);
new wr::RenderEGLImageTextureHost(mImage, mSync, mSize, GetFormat());
wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId,
texture.forget());
}
@@ -1010,11 +1011,18 @@ void EGLImageTextureHost::PushResourceUpdates(
auto method = aOp == TextureHost::ADD_IMAGE
? &wr::TransactionBuilder::AddExternalImage
: &wr::TransactionBuilder::UpdateExternalImage;
auto imageType = wr::ExternalImageType::TextureHandle(
// 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);
gfx::SurfaceFormat format =
mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8;
gfx::SurfaceFormat format = GetFormat();
MOZ_ASSERT(aImageKeys.length() == 1);
// XXX Add RGBA handling. Temporary hack to avoid crash
@@ -1030,12 +1038,21 @@ void EGLImageTextureHost::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());
MOZ_ASSERT(aImageKeys.length() == 1);
aBuilder.PushImage(
aBounds, aClip, true, false, aFilter, aImageKeys[0],
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));
preferCompositorSurface, supportsExternalCompositing);
}
bool EGLImageTextureHost::SupportsExternalCompositing(
WebRenderBackend aBackend) {
return aBackend == WebRenderBackend::SOFTWARE;
}
//

View File

@@ -643,6 +643,8 @@ class EGLImageTextureHost final : public TextureHost {
const Range<wr::ImageKey>& aImageKeys,
PushDisplayItemFlagSet aFlags) override;
bool SupportsExternalCompositing(WebRenderBackend aBackend) override;
protected:
const EGLImage mImage;
const EGLSync mSync;

View File

@@ -283,7 +283,8 @@ void RenderCompositorLayersSWGL::AttachExternalImage(
image->AsRenderDXGIYCbCrTextureHost());
#elif defined(ANDROID)
MOZ_RELEASE_ASSERT(image->AsRenderAndroidHardwareBufferTextureHost() ||
image->AsRenderAndroidSurfaceTextureHost());
image->AsRenderAndroidSurfaceTextureHost() ||
image->AsRenderEGLImageTextureHost());
#endif
auto surfaceCursor = mSurfaces.find(aId);

View File

@@ -9,16 +9,20 @@
#include "mozilla/gfx/Logging.h"
#include "GLContextEGL.h"
#include "GLLibraryEGL.h"
#include "GLReadTexImageHelper.h"
#include "OGLShaderConfig.h"
namespace mozilla {
namespace wr {
RenderEGLImageTextureHost::RenderEGLImageTextureHost(EGLImage aImage,
EGLSync aSync,
gfx::IntSize aSize)
gfx::IntSize aSize,
gfx::SurfaceFormat aFormat)
: mImage(aImage),
mSync(aSync),
mSize(aSize),
mFormat(aFormat),
mTextureTarget(LOCAL_GL_TEXTURE_2D),
mTextureHandle(0) {
MOZ_COUNT_CTOR_INHERITED(RenderEGLImageTextureHost, RenderTextureHost);
@@ -35,8 +39,7 @@ wr::WrExternalImage RenderEGLImageTextureHost::Lock(uint8_t aChannelIndex,
if (mGL.get() != aGL) {
if (mGL) {
// This should not happen. SharedSurface_EGLImage is created only in
// parent process.
// This should not happen. On android, SingletonGL is used.
MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
return InvalidToWrExternalImage();
}
@@ -47,35 +50,10 @@ wr::WrExternalImage RenderEGLImageTextureHost::Lock(uint8_t aChannelIndex,
return InvalidToWrExternalImage();
}
EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
if (mSync) {
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
MOZ_ASSERT(egl->IsExtensionSupported(gl::EGLExtension::KHR_fence_sync));
status = egl->fClientWaitSync(mSync, 0, LOCAL_EGL_FOREVER);
// We do not need to delete sync here. It is deleted by
// SharedSurface_EGLImage.
mSync = 0;
}
if (status != LOCAL_EGL_CONDITION_SATISFIED) {
MOZ_ASSERT(
status != 0,
"ClientWaitSync generated an error. Has mSync already been destroyed?");
if (!WaitSync() || !CreateTextureHandle()) {
return InvalidToWrExternalImage();
}
if (!mTextureHandle) {
mTextureTarget = mGL->GetPreferredEGLImageTextureTarget();
MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D ||
mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL);
mGL->fGenTextures(1, &mTextureHandle);
ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, mTextureTarget,
mTextureHandle);
mGL->fEGLImageTargetTexture2D(mTextureTarget, mImage);
}
const auto uvs = GetUvCoords(mSize);
return NativeTextureToWrExternalImage(
mTextureHandle, uvs.first.x, uvs.first.y, uvs.second.x, uvs.second.y);
@@ -83,14 +61,162 @@ wr::WrExternalImage RenderEGLImageTextureHost::Lock(uint8_t aChannelIndex,
void RenderEGLImageTextureHost::Unlock() {}
RefPtr<layers::TextureSource> RenderEGLImageTextureHost::CreateTextureSource(
layers::TextureSourceProvider* aProvider) {
gl::GLContext* gl = aProvider->GetGLContext();
if (mGL.get() != gl) {
if (mGL) {
// This should not happen. On android, SingletonGL is used.
MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
return nullptr;
}
mGL = gl;
}
if (!WaitSync()) {
return nullptr;
}
return new layers::EGLImageTextureSource(
aProvider, mImage, mFormat, gl->GetPreferredEGLImageTextureTarget(),
LOCAL_GL_CLAMP_TO_EDGE, mSize);
}
gfx::SurfaceFormat RenderEGLImageTextureHost::GetFormat() const {
MOZ_ASSERT(mFormat == gfx::SurfaceFormat::R8G8B8A8 ||
mFormat == gfx::SurfaceFormat::R8G8B8X8);
// SWGL does not support RGBA/RGBX so we must provide data in BGRA/BGRX
// format. ReadTexImage() called by MapPlane() will ensure that data gets
// converted correctly.
if (mFormat == gfx::SurfaceFormat::R8G8B8A8) {
return gfx::SurfaceFormat::B8G8R8A8;
}
if (mFormat == gfx::SurfaceFormat::R8G8B8X8) {
return gfx::SurfaceFormat::B8G8R8X8;
}
gfxCriticalNoteOnce << "Unexpected color format of RenderEGLImageTextureHost";
return gfx::SurfaceFormat::UNKNOWN;
}
bool RenderEGLImageTextureHost::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 = mSize;
aPlaneInfo.mStride = map.mStride;
aPlaneInfo.mData = map.mData;
return true;
}
void RenderEGLImageTextureHost::UnmapPlanes() {
if (mReadback) {
mReadback->Unmap();
mReadback = nullptr;
}
}
bool RenderEGLImageTextureHost::CreateTextureHandle() {
if (mTextureHandle) {
return true;
}
mTextureTarget = mGL->GetPreferredEGLImageTextureTarget();
MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D ||
mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL);
mGL->fGenTextures(1, &mTextureHandle);
ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, mTextureTarget,
mTextureHandle);
mGL->fEGLImageTargetTexture2D(mTextureTarget, mImage);
return true;
}
void RenderEGLImageTextureHost::DeleteTextureHandle() {
if (mTextureHandle) {
if (mGL && mGL->MakeCurrent()) {
// XXX recycle gl texture, since SharedSurface_EGLImage and
// RenderEGLImageTextureHost is not recycled.
mGL->fDeleteTextures(1, &mTextureHandle);
}
mTextureHandle = 0;
}
}
bool RenderEGLImageTextureHost::WaitSync() {
bool syncSucceeded = true;
if (mSync) {
const auto& gle = gl::GLContextEGL::Cast(mGL);
const auto& egl = gle->mEgl;
MOZ_ASSERT(egl->IsExtensionSupported(gl::EGLExtension::KHR_fence_sync));
if (egl->IsExtensionSupported(gl::EGLExtension::KHR_wait_sync)) {
syncSucceeded = egl->fWaitSync(mSync, 0) == LOCAL_EGL_TRUE;
} else {
syncSucceeded = egl->fClientWaitSync(mSync, 0, LOCAL_EGL_FOREVER) ==
LOCAL_EGL_CONDITION_SATISFIED;
}
// We do not need to delete sync here. It is deleted by
// SharedSurface_EGLImage.
mSync = 0;
}
MOZ_ASSERT(
syncSucceeded,
"(Client)WaitSync generated an error. Has mSync already been destroyed?");
return syncSucceeded;
}
already_AddRefed<gfx::DataSourceSurface>
RenderEGLImageTextureHost::ReadTexImage() {
if (!mGL) {
mGL = RenderThread::Get()->SingletonGL();
if (!mGL) {
return nullptr;
}
}
if (!WaitSync() || !CreateTextureHandle()) {
return nullptr;
}
// Allocate resulting image surface.
// Use GetFormat() rather than mFormat for the DataSourceSurface. eg BGRA
// rather than RGBA, as the latter is not supported by swgl.
// ReadTexImageHelper will take care of converting the data for us.
const gfx::SurfaceFormat surfFormat = GetFormat();
int32_t stride = mSize.width * BytesPerPixel(surfFormat);
RefPtr<gfx::DataSourceSurface> surf =
gfx::Factory::CreateDataSourceSurfaceWithStride(mSize, surfFormat,
stride);
if (!surf) {
return nullptr;
}
layers::ShaderConfigOGL config =
layers::ShaderConfigFromTargetAndFormat(mTextureTarget, mFormat);
int shaderConfig = config.mFeatures;
bool ret = mGL->ReadTexImageHelper()->ReadTexImage(
surf, mTextureHandle, mTextureTarget, mSize, shaderConfig,
/* aYInvert */ false);
if (!ret) {
return nullptr;
}
return surf.forget();
}
} // namespace wr
} // namespace mozilla

View File

@@ -8,7 +8,7 @@
#define MOZILLA_GFX_RENDEREGLIMAGETEXTUREHOST_H
#include "mozilla/layers/TextureHostOGL.h"
#include "RenderTextureHost.h"
#include "RenderTextureHostSWGL.h"
namespace mozilla {
@@ -16,28 +16,50 @@ namespace wr {
// RenderEGLImageTextureHost is created only for SharedSurface_EGLImage that is
// created in parent process.
class RenderEGLImageTextureHost final : public RenderTextureHost {
class RenderEGLImageTextureHost final : public RenderTextureHostSWGL {
public:
RenderEGLImageTextureHost(EGLImage aImage, EGLSync aSync, gfx::IntSize aSize);
RenderEGLImageTextureHost(EGLImage aImage, EGLSync aSync, gfx::IntSize aSize,
gfx::SurfaceFormat aFormat);
wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
void Unlock() override;
size_t Bytes() override {
// XXX: we don't have a format so we can't get bpp.
return mSize.width * mSize.height;
return mSize.width * mSize.height * BytesPerPixel(mFormat);
}
RenderEGLImageTextureHost* AsRenderEGLImageTextureHost() override {
return this;
}
RefPtr<layers::TextureSource> CreateTextureSource(
layers::TextureSourceProvider* aProvider) override;
// RenderTextureHostSWGL
gfx::SurfaceFormat GetFormat() const override;
gfx::ColorDepth GetColorDepth() const override {
return gfx::ColorDepth::COLOR_8;
}
size_t GetPlaneCount() const override { return 1; };
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;
void UnmapPlanes() override;
private:
virtual ~RenderEGLImageTextureHost();
bool CreateTextureHandle();
void DeleteTextureHandle();
bool WaitSync();
already_AddRefed<gfx::DataSourceSurface> ReadTexImage();
const EGLImage mImage;
EGLSync mSync;
const gfx::IntSize mSize;
const gfx::SurfaceFormat mFormat;
RefPtr<gl::GLContext> mGL;
GLenum mTextureTarget;
GLuint mTextureHandle;
RefPtr<gfx::DataSourceSurface> mReadback;
};
} // namespace wr

View File

@@ -28,6 +28,7 @@ class TextureSourceProvider;
namespace wr {
class RenderEGLImageTextureHost;
class RenderAndroidHardwareBufferTextureHost;
class RenderAndroidSurfaceTextureHost;
class RenderCompositor;
@@ -104,6 +105,10 @@ class RenderTextureHost {
return nullptr;
}
virtual RenderEGLImageTextureHost* AsRenderEGLImageTextureHost() {
return nullptr;
}
virtual RenderAndroidHardwareBufferTextureHost*
AsRenderAndroidHardwareBufferTextureHost() {
return nullptr;

View File

@@ -151,6 +151,14 @@ RenderTextureHostWrapper::AsRenderAndroidSurfaceTextureHost() {
return mTextureHost->AsRenderAndroidSurfaceTextureHost();
}
RenderEGLImageTextureHost*
RenderTextureHostWrapper::AsRenderEGLImageTextureHost() {
if (!mTextureHost) {
return nullptr;
}
return mTextureHost->AsRenderEGLImageTextureHost();
}
RenderTextureHostSWGL* RenderTextureHostWrapper::EnsureRenderTextureHostSWGL()
const {
if (!mTextureHost) {

View File

@@ -45,6 +45,7 @@ class RenderTextureHostWrapper final : public RenderTextureHostSWGL {
RenderAndroidHardwareBufferTextureHost*
AsRenderAndroidHardwareBufferTextureHost() override;
RenderAndroidSurfaceTextureHost* AsRenderAndroidSurfaceTextureHost() override;
RenderEGLImageTextureHost* AsRenderEGLImageTextureHost() override;
RenderTextureHostSWGL* AsRenderTextureHostSWGL() override;
void SetIsSoftwareDecodedVideo() override;
bool IsSoftwareDecodedVideo() override;