On Android we use SurfaceTextures to render content from sources such as the video decoder. These may have a transform set which is supposed to be applied to the texture coordinates used to sample the texture. Webrender (and software webrender), however, do not handle this correctly, meaning videos may be rendered at the incorrect size on some devices. SurfaceTextures should always be rendered with their bottom-left being their origin, eg vertically flipped. Additionally, the texture transform returned on most devices seems to be a simple y-flip transform with no scaling. Webrender currently just ignores the y-flip due to the texture origin, which cancels out us not handling the y-flip from the transform, meaning video looks correct on most devices. Some devices, however, do return a scaling transform which we must handle. This patch removes the override of WebRenderTextureHost::NeedsYFlip() that was causing us to ignore the y-flip due to the texture origin - since we will now apply the transform we must handle this correctly too. It adds a virtual method RenderTextureHost::GetUvCoords(), that returns the texture coordinates that should be used by webrender to sample from external textures. In most cases these are simply (0, 0) and (size.x, size.y), but in RenderAndroidSurfaceTextureHost we override this function to apply the transformation. This ensures we use the correct coordinates whenever the texture is rendered by webrender, eg in both software and hardware webrender when rendering in the non-compositing-path, and by hardware webrender's draw compositor. Additionally, the composite.glsl shader requires a fix to calculate the UV bounds correctly, as the coordinates may now be inverted. Lastly, we fix software webrender with the OpenGL compositor. CompositorOGL already has the required functionality to apply the texture transformation as it was used back in the layers days. We must simply ensure that we pass the value of the mIgnoreTransform flag from the original SurfaceTextureHost, through to the RenderAndroidSurfaceTextureHost, and finally to the SurfaceTextureSource which we hand to CompositorOGL. Differential Revision: https://phabricator.services.mozilla.com/D144306
942 lines
32 KiB
C++
942 lines
32 KiB
C++
/* -*- 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.ignoreTransform());
|
|
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, bool aIgnoreTransform)
|
|
: mGL(aProvider->GetGLContext()),
|
|
mSurfTex(aSurfTex),
|
|
mFormat(aFormat),
|
|
mTextureTarget(aTarget),
|
|
mWrapMode(aWrapMode),
|
|
mSize(aSize),
|
|
mIgnoreTransform(aIgnoreTransform) {}
|
|
|
|
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. We should ignore this if we know the transform should
|
|
// be identity but the producer couldn't set it correctly, like is the
|
|
// case for AndroidNativeWindowTextureData.
|
|
if (!mIgnoreTransform) {
|
|
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,
|
|
bool aIgnoreTransform)
|
|
: TextureHost(aFlags),
|
|
mSurfTex(aSurfTex),
|
|
mSize(aSize),
|
|
mFormat(aFormat),
|
|
mContinuousUpdate(aContinuousUpdate),
|
|
mIgnoreTransform(aIgnoreTransform) {
|
|
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; }
|
|
|
|
bool SurfaceTextureHost::EnsureAttached() {
|
|
GLContext* gl = this->gl();
|
|
if (!gl || !gl->MakeCurrent()) {
|
|
return false;
|
|
}
|
|
|
|
if (!mSurfTex) {
|
|
return false;
|
|
}
|
|
|
|
if (!mSurfTex->IsAttachedToGLContext((int64_t)gl)) {
|
|
GLuint texName;
|
|
gl->fGenTextures(1, &texName);
|
|
if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)gl, texName))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SurfaceTextureHost::NotifyNotUsed() {
|
|
if (mSurfTex && mSurfTex->IsSingleBuffer()) {
|
|
if (!EnsureAttached()) {
|
|
return;
|
|
}
|
|
mSurfTex->ReleaseTexImage();
|
|
}
|
|
|
|
TextureHost::NotifyNotUsed();
|
|
}
|
|
|
|
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) {
|
|
RefPtr<wr::RenderTextureHost> texture =
|
|
new wr::RenderAndroidSurfaceTextureHost(
|
|
mSurfTex, mSize, mFormat, mContinuousUpdate, mIgnoreTransform);
|
|
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// AndroidHardwareBufferTextureHost
|
|
|
|
/* static */
|
|
already_AddRefed<AndroidHardwareBufferTextureHost>
|
|
AndroidHardwareBufferTextureHost::Create(
|
|
TextureFlags aFlags, const SurfaceDescriptorAndroidHardwareBuffer& aDesc) {
|
|
RefPtr<AndroidHardwareBuffer> buffer =
|
|
AndroidHardwareBuffer::FromFileDescriptor(
|
|
const_cast<ipc::FileDescriptor&>(aDesc.handle()), aDesc.bufferId(),
|
|
aDesc.size(), aDesc.format());
|
|
if (!buffer) {
|
|
return nullptr;
|
|
}
|
|
RefPtr<AndroidHardwareBufferTextureHost> host =
|
|
new AndroidHardwareBufferTextureHost(aFlags, buffer);
|
|
return host.forget();
|
|
}
|
|
|
|
AndroidHardwareBufferTextureHost::AndroidHardwareBufferTextureHost(
|
|
TextureFlags aFlags, AndroidHardwareBuffer* aAndroidHardwareBuffer)
|
|
: TextureHost(aFlags),
|
|
mAndroidHardwareBuffer(aAndroidHardwareBuffer),
|
|
mEGLImage(EGL_NO_IMAGE) {}
|
|
|
|
AndroidHardwareBufferTextureHost::~AndroidHardwareBufferTextureHost() {
|
|
DestroyEGLImage();
|
|
}
|
|
|
|
void AndroidHardwareBufferTextureHost::DestroyEGLImage() {
|
|
if (mEGLImage && gl()) {
|
|
const auto& gle = gl::GLContextEGL::Cast(gl());
|
|
const auto& egl = gle->mEgl;
|
|
egl->fDestroyImage(mEGLImage);
|
|
mEGLImage = EGL_NO_IMAGE;
|
|
}
|
|
}
|
|
|
|
gl::GLContext* AndroidHardwareBufferTextureHost::gl() const { return nullptr; }
|
|
|
|
void AndroidHardwareBufferTextureHost::NotifyNotUsed() {
|
|
// XXX Add android fence handling
|
|
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() {
|
|
if (mTextureSource) {
|
|
mTextureSource = nullptr;
|
|
}
|
|
DestroyEGLImage();
|
|
}
|
|
|
|
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;
|
|
auto imageType = 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) {
|
|
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));
|
|
break;
|
|
}
|
|
default: {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
}
|
|
}
|
|
}
|
|
|
|
#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(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(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
|