diff options
Diffstat (limited to 'gfx/layers/opengl/TextureHostOGL.cpp')
-rw-r--r-- | gfx/layers/opengl/TextureHostOGL.cpp | 1049 |
1 files changed, 1049 insertions, 0 deletions
diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp new file mode 100644 index 0000000000..f3310dda1c --- /dev/null +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -0,0 +1,1049 @@ +/* -*- 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 "TextureHostOGL.h" + +#include "GLContextEGL.h" // for GLContext, etc +#include "GLLibraryEGL.h" // for GLLibraryEGL +#include "GLUploadHelpers.h" +#include "GLReadTexImageHelper.h" +#include "gfx2DGlue.h" // for ContentForFormat, etc +#include "mozilla/gfx/2D.h" // for DataSourceSurface +#include "mozilla/gfx/BaseSize.h" // for BaseSize +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/Logging.h" // for gfxCriticalError +#include "mozilla/layers/ISurfaceAllocator.h" +#include "mozilla/webrender/RenderEGLImageTextureHost.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "nsRegion.h" // for nsIntRegion +#include "GfxTexturesReporter.h" // for GfxTexturesReporter +#include "GeckoProfiler.h" + +#ifdef XP_MACOSX +# include "mozilla/layers/MacIOSurfaceTextureHostOGL.h" +#endif + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/layers/AndroidHardwareBuffer.h" +# include "mozilla/webrender/RenderAndroidHardwareBufferTextureHost.h" +# include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h" +#endif + +#ifdef MOZ_WAYLAND +# include "mozilla/layers/DMABUFTextureHostOGL.h" +#endif + +using namespace mozilla::gl; +using namespace mozilla::gfx; + +namespace mozilla { +namespace layers { + +class Compositor; + +void ApplySamplingFilterToBoundTexture(gl::GLContext* aGL, + gfx::SamplingFilter aSamplingFilter, + GLuint aTarget) { + GLenum filter = + (aSamplingFilter == gfx::SamplingFilter::POINT ? LOCAL_GL_NEAREST + : LOCAL_GL_LINEAR); + + aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, filter); + aGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, filter); +} + +already_AddRefed<TextureHost> CreateTextureHostOGL( + const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator, + LayersBackend aBackend, TextureFlags aFlags) { + RefPtr<TextureHost> result; + switch (aDesc.type()) { +#ifdef MOZ_WIDGET_ANDROID + case SurfaceDescriptor::TSurfaceTextureDescriptor: { + const SurfaceTextureDescriptor& desc = + aDesc.get_SurfaceTextureDescriptor(); + java::GeckoSurfaceTexture::LocalRef surfaceTexture = + java::GeckoSurfaceTexture::Lookup(desc.handle()); + + result = new SurfaceTextureHost(aFlags, surfaceTexture, desc.size(), + desc.format(), desc.continuous(), + desc.transformOverride()); + break; + } + case SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer: { + const SurfaceDescriptorAndroidHardwareBuffer& desc = + aDesc.get_SurfaceDescriptorAndroidHardwareBuffer(); + result = AndroidHardwareBufferTextureHost::Create(aFlags, desc); + break; + } +#endif + + case SurfaceDescriptor::TEGLImageDescriptor: { + const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor(); + result = new EGLImageTextureHost(aFlags, (EGLImage)desc.image(), + (EGLSync)desc.fence(), desc.size(), + desc.hasAlpha()); + break; + } + +#ifdef MOZ_WAYLAND + case SurfaceDescriptor::TSurfaceDescriptorDMABuf: { + result = new DMABUFTextureHostOGL(aFlags, aDesc); + break; + } +#endif + +#ifdef XP_MACOSX + case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: { + const SurfaceDescriptorMacIOSurface& desc = + aDesc.get_SurfaceDescriptorMacIOSurface(); + result = new MacIOSurfaceTextureHostOGL(aFlags, desc); + break; + } +#endif + + case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: { + const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture(); + result = + new GLTextureHost(aFlags, desc.texture(), desc.target(), + (GLsync)desc.fence(), desc.size(), desc.hasAlpha()); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("Unsupported SurfaceDescriptor type"); + break; + } + } + return result.forget(); +} + +static gl::TextureImage::Flags FlagsToGLFlags(TextureFlags aFlags) { + uint32_t result = TextureImage::NoFlags; + + if (aFlags & TextureFlags::USE_NEAREST_FILTER) + result |= TextureImage::UseNearestFilter; + if (aFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) + result |= TextureImage::OriginBottomLeft; + if (aFlags & TextureFlags::DISALLOW_BIGIMAGE) + result |= TextureImage::DisallowBigImage; + + return static_cast<gl::TextureImage::Flags>(result); +} + +TextureImageTextureSourceOGL::TextureImageTextureSourceOGL( + CompositorOGL* aCompositor, TextureFlags aFlags) + : mGL(aCompositor->gl()), + mCompositor(aCompositor), + mFlags(aFlags), + mIterating(false) { + if (mCompositor) { + mCompositor->RegisterTextureSource(this); + } +} + +TextureImageTextureSourceOGL::~TextureImageTextureSourceOGL() { + DeallocateDeviceData(); +} + +void TextureImageTextureSourceOGL::DeallocateDeviceData() { + mTexImage = nullptr; + mGL = nullptr; + if (mCompositor) { + mCompositor->UnregisterTextureSource(this); + } + SetUpdateSerial(0); +} + +bool TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion, + gfx::IntPoint* aSrcOffset, + gfx::IntPoint* aDstOffset) { + GLContext* gl = mGL; + MOZ_ASSERT(gl); + if (!gl || !gl->MakeCurrent()) { + NS_WARNING( + "trying to update TextureImageTextureSourceOGL without a GLContext"); + return false; + } + if (!aSurface) { + gfxCriticalError() << "Invalid surface for OGL update"; + return false; + } + MOZ_ASSERT(aSurface); + + IntSize size = aSurface->GetSize(); + if (!mTexImage || (mTexImage->GetSize() != size && !aSrcOffset) || + mTexImage->GetContentType() != + gfx::ContentForFormat(aSurface->GetFormat())) { + if (mFlags & TextureFlags::DISALLOW_BIGIMAGE) { + GLint maxTextureSize; + gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize); + if (size.width > maxTextureSize || size.height > maxTextureSize) { + NS_WARNING("Texture exceeds maximum texture size, refusing upload"); + return false; + } + // Explicitly use CreateBasicTextureImage instead of CreateTextureImage, + // because CreateTextureImage might still choose to create a tiled + // texture image. + mTexImage = CreateBasicTextureImage( + gl, size, gfx::ContentForFormat(aSurface->GetFormat()), + LOCAL_GL_CLAMP_TO_EDGE, FlagsToGLFlags(mFlags)); + } else { + // XXX - clarify which size we want to use. IncrementalContentHost will + // require the size of the destination surface to be different from + // the size of aSurface. + // See bug 893300 (tracks the implementation of ContentHost for new + // textures). + mTexImage = CreateTextureImage( + gl, size, gfx::ContentForFormat(aSurface->GetFormat()), + LOCAL_GL_CLAMP_TO_EDGE, FlagsToGLFlags(mFlags), + SurfaceFormatToImageFormat(aSurface->GetFormat())); + } + ClearCachedFilter(); + + if (aDestRegion && !aSrcOffset && + !aDestRegion->IsEqual(gfx::IntRect(0, 0, size.width, size.height))) { + // UpdateFromDataSource will ignore our specified aDestRegion since the + // texture hasn't been allocated with glTexImage2D yet. Call Resize() to + // force the allocation (full size, but no upload), and then we'll only + // upload the pixels we care about below. + mTexImage->Resize(size); + } + } + + return mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset, + aDstOffset); +} + +void TextureImageTextureSourceOGL::EnsureBuffer(const IntSize& aSize, + gfxContentType aContentType) { + if (!mTexImage || mTexImage->GetSize() != aSize || + mTexImage->GetContentType() != aContentType) { + mTexImage = + CreateTextureImage(mGL, aSize, aContentType, LOCAL_GL_CLAMP_TO_EDGE, + FlagsToGLFlags(mFlags)); + } + mTexImage->Resize(aSize); +} + +gfx::IntSize TextureImageTextureSourceOGL::GetSize() const { + if (mTexImage) { + if (mIterating) { + return mTexImage->GetTileRect().Size(); + } + return mTexImage->GetSize(); + } + NS_WARNING("Trying to query the size of an empty TextureSource."); + return gfx::IntSize(0, 0); +} + +gfx::SurfaceFormat TextureImageTextureSourceOGL::GetFormat() const { + if (mTexImage) { + return mTexImage->GetTextureFormat(); + } + NS_WARNING("Trying to query the format of an empty TextureSource."); + return gfx::SurfaceFormat::UNKNOWN; +} + +gfx::IntRect TextureImageTextureSourceOGL::GetTileRect() { + return mTexImage->GetTileRect(); +} + +void TextureImageTextureSourceOGL::BindTexture( + GLenum aTextureUnit, gfx::SamplingFilter aSamplingFilter) { + MOZ_ASSERT(mTexImage, + "Trying to bind a TextureSource that does not have an underlying " + "GL texture."); + mTexImage->BindTexture(aTextureUnit); + SetSamplingFilter(mGL, aSamplingFilter); +} + +//////////////////////////////////////////////////////////////////////// +// GLTextureSource + +GLTextureSource::GLTextureSource(TextureSourceProvider* aProvider, + GLuint aTextureHandle, GLenum aTarget, + gfx::IntSize aSize, gfx::SurfaceFormat aFormat) + : GLTextureSource(aProvider->GetGLContext(), aTextureHandle, aTarget, aSize, + aFormat) {} + +GLTextureSource::GLTextureSource(GLContext* aGL, GLuint aTextureHandle, + GLenum aTarget, gfx::IntSize aSize, + gfx::SurfaceFormat aFormat) + : mGL(aGL), + mTextureHandle(aTextureHandle), + mTextureTarget(aTarget), + mSize(aSize), + mFormat(aFormat) { + MOZ_COUNT_CTOR(GLTextureSource); +} + +GLTextureSource::~GLTextureSource() { + MOZ_COUNT_DTOR(GLTextureSource); + DeleteTextureHandle(); +} + +void GLTextureSource::DeallocateDeviceData() { DeleteTextureHandle(); } + +void GLTextureSource::DeleteTextureHandle() { + GLContext* gl = this->gl(); + if (mTextureHandle != 0 && gl && gl->MakeCurrent()) { + gl->fDeleteTextures(1, &mTextureHandle); + } + mTextureHandle = 0; +} + +void GLTextureSource::BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) { + MOZ_ASSERT(mTextureHandle != 0); + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + return; + } + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(mTextureTarget, mTextureHandle); + ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget); +} + +bool GLTextureSource::IsValid() const { return !!gl() && mTextureHandle != 0; } + +//////////////////////////////////////////////////////////////////////// +// DirectMapTextureSource + +DirectMapTextureSource::DirectMapTextureSource(gl::GLContext* aContext, + gfx::DataSourceSurface* aSurface) + : GLTextureSource(aContext, 0, LOCAL_GL_TEXTURE_RECTANGLE_ARB, + aSurface->GetSize(), aSurface->GetFormat()), + mSync(0) { + MOZ_ASSERT(aSurface); + + UpdateInternal(aSurface, nullptr, nullptr, true); +} + +DirectMapTextureSource::DirectMapTextureSource(TextureSourceProvider* aProvider, + gfx::DataSourceSurface* aSurface) + : DirectMapTextureSource(aProvider->GetGLContext(), aSurface) {} + +DirectMapTextureSource::~DirectMapTextureSource() { + if (!mSync || !gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) { + return; + } + + gl()->fDeleteSync(mSync); + mSync = 0; +} + +bool DirectMapTextureSource::Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion, + gfx::IntPoint* aSrcOffset, + gfx::IntPoint* aDstOffset) { + MOZ_RELEASE_ASSERT(aDstOffset == nullptr); + if (!aSurface) { + return false; + } + + return UpdateInternal(aSurface, aDestRegion, aSrcOffset, false); +} + +void DirectMapTextureSource::MaybeFenceTexture() { + if (!gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) { + return; + } + + if (mSync) { + gl()->fDeleteSync(mSync); + } + mSync = gl()->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0); +} + +bool DirectMapTextureSource::Sync(bool aBlocking) { + if (!gl() || !gl()->MakeCurrent() || gl()->IsDestroyed()) { + // We use this function to decide whether we can unlock the texture + // and clean it up. If we return false here and for whatever reason + // the context is absent or invalid, the compositor will keep a + // reference to this texture forever. + return true; + } + + if (!mSync) { + return false; + } + + GLenum waitResult = + gl()->fClientWaitSync(mSync, LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT, + aBlocking ? LOCAL_GL_TIMEOUT_IGNORED : 0); + return waitResult == LOCAL_GL_ALREADY_SIGNALED || + waitResult == LOCAL_GL_CONDITION_SATISFIED; +} + +bool DirectMapTextureSource::UpdateInternal(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion, + gfx::IntPoint* aSrcOffset, + bool aInit) { + if (!gl() || !gl()->MakeCurrent()) { + return false; + } + + if (aInit) { + gl()->fGenTextures(1, &mTextureHandle); + gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, mTextureHandle); + + gl()->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_STORAGE_HINT_APPLE, + LOCAL_GL_STORAGE_CACHED_APPLE); + gl()->fTextureRangeAPPLE(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + aSurface->Stride() * aSurface->GetSize().height, + aSurface->GetData()); + + gl()->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + gl()->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + } + + MOZ_ASSERT(mTextureHandle); + + // APPLE_client_storage + gl()->fPixelStorei(LOCAL_GL_UNPACK_CLIENT_STORAGE_APPLE, LOCAL_GL_TRUE); + + nsIntRegion destRegion = aDestRegion + ? *aDestRegion + : IntRect(0, 0, aSurface->GetSize().width, + aSurface->GetSize().height); + gfx::IntPoint srcPoint = aSrcOffset ? *aSrcOffset : gfx::IntPoint(0, 0); + mFormat = gl::UploadSurfaceToTexture( + gl(), aSurface, destRegion, mTextureHandle, aSurface->GetSize(), nullptr, + aInit, srcPoint, gfx::IntPoint(0, 0), LOCAL_GL_TEXTURE0, + LOCAL_GL_TEXTURE_RECTANGLE_ARB); + + if (mSync) { + gl()->fDeleteSync(mSync); + mSync = 0; + } + + gl()->fPixelStorei(LOCAL_GL_UNPACK_CLIENT_STORAGE_APPLE, LOCAL_GL_FALSE); + return true; +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// SurfaceTextureHost + +#ifdef MOZ_WIDGET_ANDROID + +SurfaceTextureSource::SurfaceTextureSource( + TextureSourceProvider* aProvider, + mozilla::java::GeckoSurfaceTexture::Ref& aSurfTex, + gfx::SurfaceFormat aFormat, GLenum aTarget, GLenum aWrapMode, + gfx::IntSize aSize, Maybe<gfx::Matrix4x4> aTransformOverride) + : mGL(aProvider->GetGLContext()), + mSurfTex(aSurfTex), + mFormat(aFormat), + mTextureTarget(aTarget), + mWrapMode(aWrapMode), + mSize(aSize), + mTransformOverride(aTransformOverride) {} + +void SurfaceTextureSource::BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) { + MOZ_ASSERT(mSurfTex); + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + NS_WARNING("Trying to bind a texture without a GLContext"); + return; + } + + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(mTextureTarget, mSurfTex->GetTexName()); + + ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget); +} + +bool SurfaceTextureSource::IsValid() const { return !!gl(); } + +gfx::Matrix4x4 SurfaceTextureSource::GetTextureTransform() { + MOZ_ASSERT(mSurfTex); + + gfx::Matrix4x4 ret; + + // GetTransformMatrix() returns the transform set by the producer side of the + // SurfaceTexture that must be applied to texture coordinates when + // sampling. In some cases we may have set an override value, such as in + // AndroidNativeWindowTextureData where we own the producer side, or for + // MediaCodec output on devices where where we know the value is incorrect. + if (mTransformOverride) { + ret = *mTransformOverride; + } else { + const auto& surf = java::sdk::SurfaceTexture::LocalRef( + java::sdk::SurfaceTexture::Ref::From(mSurfTex)); + AndroidSurfaceTexture::GetTransformMatrix(surf, &ret); + } + + return ret; +} + +void SurfaceTextureSource::DeallocateDeviceData() { mSurfTex = nullptr; } + +//////////////////////////////////////////////////////////////////////// + +SurfaceTextureHost::SurfaceTextureHost( + TextureFlags aFlags, mozilla::java::GeckoSurfaceTexture::Ref& aSurfTex, + gfx::IntSize aSize, gfx::SurfaceFormat aFormat, bool aContinuousUpdate, + Maybe<Matrix4x4> aTransformOverride) + : TextureHost(TextureHostType::AndroidSurfaceTexture, aFlags), + mSurfTex(aSurfTex), + mSize(aSize), + mFormat(aFormat), + mContinuousUpdate(aContinuousUpdate), + mTransformOverride(aTransformOverride) { + if (!mSurfTex) { + return; + } + + // Continuous update makes no sense with single buffer mode + MOZ_ASSERT(!mSurfTex->IsSingleBuffer() || !mContinuousUpdate); + + mSurfTex->IncrementUse(); +} + +SurfaceTextureHost::~SurfaceTextureHost() { + if (mSurfTex) { + mSurfTex->DecrementUse(); + mSurfTex = nullptr; + } +} + +gl::GLContext* SurfaceTextureHost::gl() const { return nullptr; } + +gfx::SurfaceFormat SurfaceTextureHost::GetFormat() const { return mFormat; } + +void SurfaceTextureHost::DeallocateDeviceData() { + if (mTextureSource) { + mTextureSource->DeallocateDeviceData(); + } + + if (mSurfTex) { + mSurfTex->DecrementUse(); + mSurfTex = nullptr; + } +} + +void SurfaceTextureHost::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + bool isRemoteTexture = !!(mFlags & TextureFlags::REMOTE_TEXTURE); + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderAndroidSurfaceTextureHost( + mSurfTex, mSize, mFormat, mContinuousUpdate, mTransformOverride, + isRemoteTexture); + wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId, + texture.forget()); +} + +uint32_t SurfaceTextureHost::NumSubTextures() { return mSurfTex ? 1 : 0; } + +void SurfaceTextureHost::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + auto method = aOp == TextureHost::ADD_IMAGE + ? &wr::TransactionBuilder::AddExternalImage + : &wr::TransactionBuilder::UpdateExternalImage; + + // 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: + case gfx::SurfaceFormat::R8G8B8A8: { + MOZ_ASSERT(aImageKeys.length() == 1); + + // XXX Add RGBA handling. Temporary hack to avoid crash + // With BGRA format setting, rendering works without problem. + auto format = GetFormat() == gfx::SurfaceFormat::R8G8B8A8 + ? gfx::SurfaceFormat::B8G8R8A8 + : gfx::SurfaceFormat::B8G8R8X8; + wr::ImageDescriptor descriptor(GetSize(), format); + (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + } + } +} + +void SurfaceTextureHost::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}, + preferCompositorSurface, supportsExternalCompositing); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + } + } +} + +bool SurfaceTextureHost::SupportsExternalCompositing( + WebRenderBackend aBackend) { + 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 + +/* static */ +already_AddRefed<AndroidHardwareBufferTextureHost> +AndroidHardwareBufferTextureHost::Create( + TextureFlags aFlags, const SurfaceDescriptorAndroidHardwareBuffer& aDesc) { + RefPtr<AndroidHardwareBuffer> buffer = + AndroidHardwareBufferManager::Get()->GetBuffer(aDesc.bufferId()); + if (!buffer) { + return nullptr; + } + RefPtr<AndroidHardwareBufferTextureHost> host = + new AndroidHardwareBufferTextureHost(aFlags, buffer); + return host.forget(); +} + +AndroidHardwareBufferTextureHost::AndroidHardwareBufferTextureHost( + TextureFlags aFlags, AndroidHardwareBuffer* aAndroidHardwareBuffer) + : TextureHost(TextureHostType::AndroidHardwareBuffer, aFlags), + mAndroidHardwareBuffer(aAndroidHardwareBuffer) { + MOZ_ASSERT(mAndroidHardwareBuffer); +} + +AndroidHardwareBufferTextureHost::~AndroidHardwareBufferTextureHost() {} + +gl::GLContext* AndroidHardwareBufferTextureHost::gl() const { return nullptr; } + +void AndroidHardwareBufferTextureHost::NotifyNotUsed() { + TextureHost::NotifyNotUsed(); +} + +gfx::SurfaceFormat AndroidHardwareBufferTextureHost::GetFormat() const { + if (mAndroidHardwareBuffer) { + return mAndroidHardwareBuffer->mFormat; + } + return gfx::SurfaceFormat::UNKNOWN; +} + +gfx::IntSize AndroidHardwareBufferTextureHost::GetSize() const { + if (mAndroidHardwareBuffer) { + return mAndroidHardwareBuffer->mSize; + } + return gfx::IntSize(); +} + +void AndroidHardwareBufferTextureHost::DeallocateDeviceData() { + mAndroidHardwareBuffer = nullptr; +} + +void AndroidHardwareBufferTextureHost::SetAcquireFence( + mozilla::ipc::FileDescriptor&& aFenceFd) { + if (!mAndroidHardwareBuffer) { + return; + } + mAndroidHardwareBuffer->SetAcquireFence(std::move(aFenceFd)); +} + +void AndroidHardwareBufferTextureHost::SetReleaseFence( + mozilla::ipc::FileDescriptor&& aFenceFd) { + if (!mAndroidHardwareBuffer) { + return; + } + mAndroidHardwareBuffer->SetReleaseFence(std::move(aFenceFd)); +} + +mozilla::ipc::FileDescriptor +AndroidHardwareBufferTextureHost::GetAndResetReleaseFence() { + if (!mAndroidHardwareBuffer) { + return mozilla::ipc::FileDescriptor(); + } + return mAndroidHardwareBuffer->GetAndResetReleaseFence(); +} + +void AndroidHardwareBufferTextureHost::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderAndroidHardwareBufferTextureHost(mAndroidHardwareBuffer); + wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId, + texture.forget()); +} + +uint32_t AndroidHardwareBufferTextureHost::NumSubTextures() { + return mAndroidHardwareBuffer ? 1 : 0; +} + +void AndroidHardwareBufferTextureHost::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + auto method = aOp == TextureHost::ADD_IMAGE + ? &wr::TransactionBuilder::AddExternalImage + : &wr::TransactionBuilder::UpdateExternalImage; + + // 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: + case gfx::SurfaceFormat::R8G8B8A8: { + MOZ_ASSERT(aImageKeys.length() == 1); + + // XXX Add RGBA handling. Temporary hack to avoid crash + // With BGRA format setting, rendering works without problem. + auto format = GetFormat() == gfx::SurfaceFormat::R8G8B8A8 + ? gfx::SurfaceFormat::B8G8R8A8 + : gfx::SurfaceFormat::B8G8R8X8; + wr::ImageDescriptor descriptor(GetSize(), format); + (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + } + } +} + +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}, + preferCompositorSurface, supportsExternalCompositing); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + } + } +} + +bool AndroidHardwareBufferTextureHost::SupportsExternalCompositing( + WebRenderBackend aBackend) { + return aBackend == WebRenderBackend::SOFTWARE; +} + +#endif // MOZ_WIDGET_ANDROID + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// EGLImage + +EGLImageTextureSource::EGLImageTextureSource(TextureSourceProvider* aProvider, + EGLImage aImage, + gfx::SurfaceFormat aFormat, + GLenum aTarget, GLenum aWrapMode, + gfx::IntSize aSize) + : mImage(aImage), + mFormat(aFormat), + mTextureTarget(aTarget), + mWrapMode(aWrapMode), + mSize(aSize) { + MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D || + mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL); +} + +void EGLImageTextureSource::BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) { + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + NS_WARNING("Trying to bind a texture without a GLContext"); + return; + } + +#ifdef DEBUG + const bool supportsEglImage = [&]() { + const auto& gle = GLContextEGL::Cast(gl); + const auto& egl = gle->mEgl; + + return egl->HasKHRImageBase() && + egl->IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image) && + gl->IsExtensionSupported(GLContext::OES_EGL_image); + }(); + MOZ_ASSERT(supportsEglImage, "EGLImage not supported or disabled in runtime"); +#endif + + GLuint tex = mCompositor->GetTemporaryTexture(mTextureTarget, aTextureUnit); + + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(mTextureTarget, tex); + + gl->fEGLImageTargetTexture2D(mTextureTarget, mImage); + + ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget); +} + +bool EGLImageTextureSource::IsValid() const { return !!gl(); } + +gfx::Matrix4x4 EGLImageTextureSource::GetTextureTransform() { + gfx::Matrix4x4 ret; + return ret; +} + +//////////////////////////////////////////////////////////////////////// + +EGLImageTextureHost::EGLImageTextureHost(TextureFlags aFlags, EGLImage aImage, + EGLSync aSync, gfx::IntSize aSize, + bool hasAlpha) + : TextureHost(TextureHostType::EGLImage, aFlags), + mImage(aImage), + mSync(aSync), + mSize(aSize), + mHasAlpha(hasAlpha) {} + +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; +} + +void EGLImageTextureHost::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderEGLImageTextureHost(mImage, mSync, mSize); + wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId, + texture.forget()); +} + +void EGLImageTextureHost::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + auto method = aOp == TextureHost::ADD_IMAGE + ? &wr::TransactionBuilder::AddExternalImage + : &wr::TransactionBuilder::UpdateExternalImage; + auto imageType = wr::ExternalImageType::TextureHandle( + wr::ImageBufferKind::TextureExternal); + + gfx::SurfaceFormat format = + mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8; + + MOZ_ASSERT(aImageKeys.length() == 1); + // XXX Add RGBA handling. Temporary hack to avoid crash + // With BGRA format setting, rendering works without problem. + auto formatTmp = format == gfx::SurfaceFormat::R8G8B8A8 + ? gfx::SurfaceFormat::B8G8R8A8 + : gfx::SurfaceFormat::B8G8R8X8; + wr::ImageDescriptor descriptor(GetSize(), formatTmp); + (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0); +} + +void EGLImageTextureHost::PushDisplayItems( + wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) { + 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)); +} + +// + +GLTextureHost::GLTextureHost(TextureFlags aFlags, GLuint aTextureHandle, + GLenum aTarget, GLsync aSync, gfx::IntSize aSize, + bool aHasAlpha) + : TextureHost(TextureHostType::GLTexture, aFlags), + mTexture(aTextureHandle), + mTarget(aTarget), + mSync(aSync), + mSize(aSize), + mHasAlpha(aHasAlpha) {} + +GLTextureHost::~GLTextureHost() = default; + +gl::GLContext* GLTextureHost::gl() const { return nullptr; } + +gfx::SurfaceFormat GLTextureHost::GetFormat() const { + MOZ_ASSERT(mTextureSource); + return mTextureSource ? mTextureSource->GetFormat() + : gfx::SurfaceFormat::UNKNOWN; +} + +} // namespace layers +} // namespace mozilla |