summaryrefslogtreecommitdiffstats
path: root/gfx/layers/opengl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /gfx/layers/opengl
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/opengl')
-rw-r--r--gfx/layers/opengl/CompositingRenderTargetOGL.cpp117
-rw-r--r--gfx/layers/opengl/CompositingRenderTargetOGL.h212
-rw-r--r--gfx/layers/opengl/CompositorOGL.cpp1747
-rw-r--r--gfx/layers/opengl/CompositorOGL.h446
-rw-r--r--gfx/layers/opengl/DMABUFTextureClientOGL.cpp123
-rw-r--r--gfx/layers/opengl/DMABUFTextureClientOGL.h69
-rw-r--r--gfx/layers/opengl/DMABUFTextureHostOGL.cpp188
-rw-r--r--gfx/layers/opengl/DMABUFTextureHostOGL.h69
-rw-r--r--gfx/layers/opengl/EGLImageHelpers.cpp58
-rw-r--r--gfx/layers/opengl/EGLImageHelpers.h28
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp120
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h64
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp242
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h84
-rw-r--r--gfx/layers/opengl/OGLShaderConfig.h255
-rw-r--r--gfx/layers/opengl/OGLShaderProgram.cpp1058
-rw-r--r--gfx/layers/opengl/OGLShaderProgram.h377
-rw-r--r--gfx/layers/opengl/TextureClientOGL.cpp331
-rw-r--r--gfx/layers/opengl/TextureClientOGL.h169
-rw-r--r--gfx/layers/opengl/TextureHostOGL.cpp1064
-rw-r--r--gfx/layers/opengl/TextureHostOGL.h653
21 files changed, 7474 insertions, 0 deletions
diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
new file mode 100644
index 0000000000..9fbc207a6b
--- /dev/null
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "CompositingRenderTargetOGL.h"
+#include "GLContext.h"
+#include "GLReadTexImageHelper.h"
+#include "ScopedGLHelpers.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+CompositingRenderTargetOGL::~CompositingRenderTargetOGL() {
+ if (mGLResourceOwnership == GLResourceOwnership::OWNED_BY_RENDER_TARGET &&
+ mGL && mGL->MakeCurrent()) {
+ mGL->fDeleteTextures(1, &mTextureHandle);
+ mGL->fDeleteFramebuffers(1, &mFBO);
+ }
+}
+
+void CompositingRenderTargetOGL::BindTexture(GLenum aTextureUnit,
+ GLenum aTextureTarget) {
+ MOZ_ASSERT(!mNeedInitialization);
+ MOZ_ASSERT(mTextureHandle != 0);
+ mGL->fActiveTexture(aTextureUnit);
+ mGL->fBindTexture(aTextureTarget, mTextureHandle);
+}
+
+void CompositingRenderTargetOGL::BindRenderTarget() {
+ bool needsClear = false;
+
+ if (mNeedInitialization) {
+ Initialize(mNeedInitialization->mFBOTextureTarget);
+ if (mNeedInitialization->mInitMode == INIT_MODE_CLEAR) {
+ needsClear = true;
+ mClearOnBind = false;
+ }
+ mNeedInitialization = Nothing();
+ } else {
+ GLuint fbo = GetFBO();
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
+ GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ // The main framebuffer (0) of non-offscreen contexts
+ // might be backed by a EGLSurface that needs to be renewed.
+ if (mFBO == 0 && !mGL->IsOffscreen()) {
+ mGL->RenewSurface(mCompositor->GetWidget());
+ result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ }
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ nsAutoCString msg;
+ msg.AppendPrintf(
+ "Framebuffer not complete -- CheckFramebufferStatus returned 0x%x, "
+ "GLContext=%p, IsOffscreen()=%d, mFBO=%d, "
+ "aRect.width=%d, aRect.height=%d",
+ result, mGL.get(), mGL->IsOffscreen(), mFBO, mSize.width,
+ mSize.height);
+ NS_WARNING(msg.get());
+ }
+ }
+
+ needsClear = mClearOnBind;
+ }
+
+ if (needsClear) {
+ ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGL, 0, 0, mSize.width, mSize.height);
+ mGL->fClearColor(0.0, 0.0, 0.0, 0.0);
+ mGL->fClearDepth(0.0);
+ mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
+ }
+}
+
+GLuint CompositingRenderTargetOGL::GetFBO() const {
+ MOZ_ASSERT(!mNeedInitialization);
+ return mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+}
+
+#ifdef MOZ_DUMP_PAINTING
+already_AddRefed<DataSourceSurface> CompositingRenderTargetOGL::Dump(
+ Compositor* aCompositor) {
+ MOZ_ASSERT(!mNeedInitialization);
+ CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL();
+ return ReadBackSurface(mGL, mTextureHandle, true,
+ compositorOGL->GetFBOFormat());
+}
+#endif
+
+void CompositingRenderTargetOGL::Initialize(GLenum aFBOTextureTarget) {
+ // TODO: call mGL->GetBackbufferFB(), use that
+ GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO;
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fbo);
+ mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+ aFBOTextureTarget, mTextureHandle, 0);
+
+ // Making this call to fCheckFramebufferStatus prevents a crash on
+ // PowerVR. See bug 695246.
+ GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+ if (result != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ nsAutoCString msg;
+ msg.AppendPrintf(
+ "Framebuffer not complete -- error 0x%x, aFBOTextureTarget 0x%x, mFBO "
+ "%d, mTextureHandle %d, aRect.width %d, aRect.height %d",
+ result, aFBOTextureTarget, mFBO, mTextureHandle, mSize.width,
+ mSize.height);
+ NS_ERROR(msg.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.h b/gfx/layers/opengl/CompositingRenderTargetOGL.h
new file mode 100644
index 0000000000..14e7265706
--- /dev/null
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h
@@ -0,0 +1,212 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_COMPOSITINGRENDERTARGETOGL_H
+#define MOZILLA_GFX_COMPOSITINGRENDERTARGETOGL_H
+
+#include "GLContextTypes.h" // for GLContext
+#include "GLDefs.h" // for GLenum, LOCAL_GL_FRAMEBUFFER, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize, IntSizeTyped
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/Compositor.h" // for SurfaceInitMode, etc
+#include "mozilla/layers/TextureHost.h" // for CompositingRenderTarget
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsAString.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ERROR, NS_WARNING
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace gl {
+class BindableTexture;
+} // namespace gl
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class TextureSource;
+
+class CompositingRenderTargetOGL : public CompositingRenderTarget {
+ typedef mozilla::gl::GLContext GLContext;
+
+ friend class CompositorOGL;
+
+ enum class GLResourceOwnership : uint8_t {
+ // Framebuffer and texture will be deleted when the RenderTarget is
+ // destroyed.
+ OWNED_BY_RENDER_TARGET,
+
+ // Framebuffer and texture are only used by the RenderTarget, but never
+ // deleted.
+ EXTERNALLY_OWNED
+ };
+
+ struct InitParams {
+ GLenum mFBOTextureTarget;
+ SurfaceInitMode mInitMode;
+ };
+
+ public:
+ ~CompositingRenderTargetOGL();
+
+ const char* Name() const override { return "CompositingRenderTargetOGL"; }
+
+ /**
+ * Create a render target around the default FBO, for rendering straight to
+ * the window.
+ */
+ static already_AddRefed<CompositingRenderTargetOGL> CreateForWindow(
+ CompositorOGL* aCompositor, const gfx::IntSize& aSize) {
+ RefPtr<CompositingRenderTargetOGL> result = new CompositingRenderTargetOGL(
+ aCompositor, gfx::IntRect(gfx::IntPoint(), aSize), gfx::IntPoint(),
+ aSize, GLResourceOwnership::EXTERNALLY_OWNED, 0, 0, Nothing());
+ return result.forget();
+ }
+
+ static already_AddRefed<CompositingRenderTargetOGL>
+ CreateForNewFBOAndTakeOwnership(CompositorOGL* aCompositor, GLuint aTexture,
+ GLuint aFBO, const gfx::IntRect& aRect,
+ const gfx::IntPoint& aClipSpaceOrigin,
+ const gfx::IntSize& aPhySize,
+ GLenum aFBOTextureTarget,
+ SurfaceInitMode aInit) {
+ RefPtr<CompositingRenderTargetOGL> result = new CompositingRenderTargetOGL(
+ aCompositor, aRect, aClipSpaceOrigin, aPhySize,
+ GLResourceOwnership::OWNED_BY_RENDER_TARGET, aTexture, aFBO,
+ Some(InitParams{aFBOTextureTarget, aInit}));
+ return result.forget();
+ }
+
+ static already_AddRefed<CompositingRenderTargetOGL>
+ CreateForExternallyOwnedFBO(CompositorOGL* aCompositor, GLuint aFBO,
+ const gfx::IntRect& aRect,
+ const gfx::IntPoint& aClipSpaceOrigin) {
+ RefPtr<CompositingRenderTargetOGL> result = new CompositingRenderTargetOGL(
+ aCompositor, aRect, aClipSpaceOrigin, aRect.Size(),
+ GLResourceOwnership::EXTERNALLY_OWNED, 0, aFBO, Nothing());
+ return result.forget();
+ }
+
+ void BindTexture(GLenum aTextureUnit, GLenum aTextureTarget);
+
+ /**
+ * Call when we want to draw into our FBO
+ */
+ void BindRenderTarget();
+
+ bool IsWindow() { return mFBO == 0; }
+
+ GLuint GetFBO() const;
+
+ GLuint GetTextureHandle() const {
+ MOZ_ASSERT(!mNeedInitialization);
+ return mTextureHandle;
+ }
+
+ // TextureSourceOGL
+ TextureSourceOGL* AsSourceOGL() override {
+ // XXX - Bug 900770
+ MOZ_ASSERT(
+ false,
+ "CompositingRenderTargetOGL should not be used as a TextureSource");
+ return nullptr;
+ }
+ gfx::IntSize GetSize() const override { return mSize; }
+
+ // The point that DrawGeometry's aClipRect is relative to. Will be (0, 0) for
+ // root render targets and equal to GetOrigin() for non-root render targets.
+ gfx::IntPoint GetClipSpaceOrigin() const { return mClipSpaceOrigin; }
+
+ gfx::SurfaceFormat GetFormat() const override {
+ // XXX - Should it be implemented ? is the above assert true ?
+ MOZ_ASSERT(false, "Not implemented");
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ // In render target coordinates, i.e. the same space as GetOrigin().
+ // NOT relative to mClipSpaceOrigin!
+ void SetClipRect(const Maybe<gfx::IntRect>& aRect) { mClipRect = aRect; }
+ const Maybe<gfx::IntRect>& GetClipRect() const { return mClipRect; }
+
+#ifdef MOZ_DUMP_PAINTING
+ already_AddRefed<gfx::DataSourceSurface> Dump(
+ Compositor* aCompositor) override;
+#endif
+
+ const gfx::IntSize& GetInitSize() const { return mSize; }
+ const gfx::IntSize& GetPhysicalSize() const { return mPhySize; }
+
+ protected:
+ CompositingRenderTargetOGL(CompositorOGL* aCompositor,
+ const gfx::IntRect& aRect,
+ const gfx::IntPoint& aClipSpaceOrigin,
+ const gfx::IntSize& aPhySize,
+ GLResourceOwnership aGLResourceOwnership,
+ GLuint aTexure, GLuint aFBO,
+ const Maybe<InitParams>& aNeedInitialization)
+ : CompositingRenderTarget(aRect.TopLeft()),
+ mNeedInitialization(aNeedInitialization),
+ mSize(aRect.Size()),
+ mPhySize(aPhySize),
+ mCompositor(aCompositor),
+ mGL(aCompositor->gl()),
+ mClipSpaceOrigin(aClipSpaceOrigin),
+ mGLResourceOwnership(aGLResourceOwnership),
+ mTextureHandle(aTexure),
+ mFBO(aFBO) {
+ MOZ_ASSERT(mGL);
+ }
+
+ /**
+ * Actually do the initialisation.
+ * We do this lazily so that when we first set this render target on the
+ * compositor we do not have to re-bind the FBO after unbinding it, or
+ * alternatively leave the FBO bound after creation. Note that we leave our
+ * FBO bound, and so calling this method is only suitable when about to use
+ * this render target.
+ */
+ void Initialize(GLenum aFBOTextureTarget);
+
+ /**
+ * Some() between construction and Initialize, if initialization was
+ * requested.
+ */
+ Maybe<InitParams> mNeedInitialization;
+
+ /*
+ * Users of render target would draw in logical size, but it is
+ * actually drawn to a surface in physical size. GL surfaces have
+ * a limitation on their size, a smaller surface would be
+ * allocated for the render target if the caller requests in a
+ * size too big.
+ */
+ gfx::IntSize mSize; // Logical size, the expected by callers.
+ gfx::IntSize mPhySize; // Physical size, the real size of the surface.
+
+ /**
+ * There is temporary a cycle between the compositor and the render target,
+ * each having a strong ref to the other. The compositor's reference to
+ * the target is always cleared at the end of a frame.
+ */
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<GLContext> mGL;
+ Maybe<gfx::IntRect> mClipRect;
+ gfx::IntPoint mClipSpaceOrigin;
+ GLResourceOwnership mGLResourceOwnership;
+ GLuint mTextureHandle;
+ GLuint mFBO;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_SURFACEOGL_H */
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
new file mode 100644
index 0000000000..e435b7ed1d
--- /dev/null
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -0,0 +1,1747 @@
+/* -*- 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 "CompositorOGL.h"
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t
+#include <stdlib.h> // for free, malloc
+#include "GLContextProvider.h" // for GLContextProvider
+#include "GLContext.h" // for GLContext
+#include "GLUploadHelpers.h"
+#include "gfxCrashReporterUtils.h" // for ScopedGfxFeatureReporter
+#include "gfxEnv.h" // for gfxEnv
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h" // for gfxUtils, etc
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/Preferences.h" // for Preferences
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_nglayout.h"
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix
+#include "mozilla/gfx/Triangle.h" // for Triangle
+#include "mozilla/gfx/gfxVars.h" // for gfxVars
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/NativeLayer.h"
+#include "mozilla/layers/CompositingRenderTargetOGL.h"
+#include "mozilla/layers/Effects.h" // for EffectChain, TexturedEffect, etc
+#include "mozilla/layers/TextureHost.h" // for TextureSource, etc
+#include "mozilla/layers/TextureHostOGL.h" // for TextureSourceOGL, etc
+#include "mozilla/layers/PTextureParent.h" // for OtherPid() on PTextureParent
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsAppRunner.h"
+#include "nsAString.h"
+#include "nsClassHashtable.h"
+#include "nsIConsoleService.h" // for nsIConsoleService, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsLiteralString.h" // for NS_LITERAL_STRING
+#include "nsMathUtils.h" // for NS_roundf
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsServiceManagerUtils.h" // for do_GetService
+#include "nsString.h" // for nsString, nsAutoCString, etc
+#include "OGLShaderProgram.h" // for ShaderProgramOGL, etc
+#include "ScopedGLHelpers.h"
+#include "GLReadTexImageHelper.h"
+#include "HeapCopyOfStackArray.h"
+#include "GLBlitHelper.h"
+#include "mozilla/gfx/Swizzle.h"
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/widget/GtkCompositorWidget.h"
+#endif
+#if MOZ_WIDGET_ANDROID
+# include "GLContextEGL.h"
+# include "GLLibraryEGL.h"
+# include "mozilla/java/GeckoSurfaceTextureWrappers.h"
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+#endif
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+using namespace mozilla::gl;
+
+static const GLuint kCoordinateAttributeIndex = 0;
+static const GLuint kTexCoordinateAttributeIndex = 1;
+
+class DummyTextureSourceOGL : public TextureSource, public TextureSourceOGL {
+ public:
+ const char* Name() const override { return "DummyTextureSourceOGL"; }
+
+ TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override {}
+
+ bool IsValid() const override { return false; }
+
+ gfx::IntSize GetSize() const override { return gfx::IntSize(); }
+
+ gfx::SurfaceFormat GetFormat() const override {
+ return gfx::SurfaceFormat::B8G8R8A8;
+ }
+
+ GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ protected:
+};
+
+class AsyncReadbackBufferOGL final : public AsyncReadbackBuffer {
+ public:
+ AsyncReadbackBufferOGL(GLContext* aGL, const IntSize& aSize);
+
+ bool MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const override;
+
+ void Bind() const {
+ mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, mBufferHandle);
+ mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 1);
+ }
+
+ protected:
+ virtual ~AsyncReadbackBufferOGL();
+
+ private:
+ GLContext* mGL;
+ GLuint mBufferHandle;
+};
+
+AsyncReadbackBufferOGL::AsyncReadbackBufferOGL(GLContext* aGL,
+ const IntSize& aSize)
+ : AsyncReadbackBuffer(aSize), mGL(aGL), mBufferHandle(0) {
+ size_t bufferByteCount = mSize.width * mSize.height * 4;
+ mGL->fGenBuffers(1, &mBufferHandle);
+
+ ScopedPackState scopedPackState(mGL);
+ Bind();
+ mGL->fBufferData(LOCAL_GL_PIXEL_PACK_BUFFER, bufferByteCount, nullptr,
+ LOCAL_GL_STREAM_READ);
+}
+
+AsyncReadbackBufferOGL::~AsyncReadbackBufferOGL() {
+ if (mGL && mGL->MakeCurrent()) {
+ mGL->fDeleteBuffers(1, &mBufferHandle);
+ }
+}
+
+bool AsyncReadbackBufferOGL::MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) const {
+ MOZ_RELEASE_ASSERT(aReadSize <= aSurface->GetSize());
+
+ if (!mGL || !mGL->MakeCurrent()) {
+ return false;
+ }
+
+ ScopedPackState scopedPackState(mGL);
+ Bind();
+
+ const uint8_t* srcData = nullptr;
+ if (mGL->IsSupported(GLFeature::map_buffer_range)) {
+ srcData = static_cast<uint8_t*>(mGL->fMapBufferRange(
+ LOCAL_GL_PIXEL_PACK_BUFFER, 0, aReadSize.height * aReadSize.width * 4,
+ LOCAL_GL_MAP_READ_BIT));
+ } else {
+ srcData = static_cast<uint8_t*>(
+ mGL->fMapBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, LOCAL_GL_READ_ONLY));
+ }
+
+ if (!srcData) {
+ return false;
+ }
+
+ int32_t srcStride = mSize.width * 4; // Bind() sets an alignment of 1
+ DataSourceSurface::ScopedMap map(aSurface, DataSourceSurface::WRITE);
+ uint8_t* destData = map.GetData();
+ int32_t destStride = map.GetStride();
+ SurfaceFormat destFormat = aSurface->GetFormat();
+ for (int32_t destRow = 0; destRow < aReadSize.height; destRow++) {
+ // Turn srcData upside down during the copy.
+ int32_t srcRow = aReadSize.height - 1 - destRow;
+ const uint8_t* src = &srcData[srcRow * srcStride];
+ uint8_t* dest = &destData[destRow * destStride];
+ SwizzleData(src, srcStride, SurfaceFormat::R8G8B8A8, dest, destStride,
+ destFormat, IntSize(aReadSize.width, 1));
+ }
+
+ mGL->fUnmapBuffer(LOCAL_GL_PIXEL_PACK_BUFFER);
+
+ return true;
+}
+
+PerUnitTexturePoolOGL::PerUnitTexturePoolOGL(gl::GLContext* aGL)
+ : mTextureTarget(0), // zero is never a valid texture target
+ mGL(aGL) {}
+
+PerUnitTexturePoolOGL::~PerUnitTexturePoolOGL() { DestroyTextures(); }
+
+CompositorOGL::CompositorOGL(widget::CompositorWidget* aWidget,
+ int aSurfaceWidth, int aSurfaceHeight,
+ bool aUseExternalSurfaceSize)
+ : Compositor(aWidget),
+ mWidgetSize(-1, -1),
+ mSurfaceSize(aSurfaceWidth, aSurfaceHeight),
+ mFBOTextureTarget(0),
+ mWindowRenderTarget(nullptr),
+ mQuadVBO(0),
+ mTriangleVBO(0),
+ mPreviousFrameDoneSync(nullptr),
+ mThisFrameDoneSync(nullptr),
+ mHasBGRA(0),
+ mUseExternalSurfaceSize(aUseExternalSurfaceSize),
+ mFrameInProgress(false),
+ mDestroyed(false),
+ mViewportSize(0, 0) {
+ if (aWidget->GetNativeLayerRoot()) {
+ // We can only render into native layers, our GLContext won't have a usable
+ // default framebuffer.
+ mCanRenderToDefaultFramebuffer = false;
+ }
+ MOZ_COUNT_CTOR(CompositorOGL);
+}
+
+CompositorOGL::~CompositorOGL() { MOZ_COUNT_DTOR(CompositorOGL); }
+
+already_AddRefed<mozilla::gl::GLContext> CompositorOGL::CreateContext() {
+ RefPtr<GLContext> context;
+
+ // Used by mock widget to create an offscreen context
+ nsIWidget* widget = mWidget->RealWidget();
+ void* widgetOpenGLContext =
+ widget ? widget->GetNativeData(NS_NATIVE_OPENGL_CONTEXT) : nullptr;
+ if (widgetOpenGLContext) {
+ GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
+ return already_AddRefed<GLContext>(alreadyRefed);
+ }
+
+#ifdef XP_WIN
+ if (gfxEnv::MOZ_LAYERS_PREFER_EGL()) {
+ printf_stderr("Trying GL layers...\n");
+ context = gl::GLContextProviderEGL::CreateForCompositorWidget(
+ mWidget, /* aHardwareWebRender */ false, /* aForceAccelerated */ false);
+ }
+#endif
+
+ // Allow to create offscreen GL context for main Layer Manager
+ if (!context && gfxEnv::MOZ_LAYERS_PREFER_OFFSCREEN()) {
+ nsCString discardFailureId;
+ context = GLContextProvider::CreateHeadless(
+ {CreateContextFlags::REQUIRE_COMPAT_PROFILE}, &discardFailureId);
+ if (!context->CreateOffscreenDefaultFb(mSurfaceSize)) {
+ context = nullptr;
+ }
+ }
+
+ if (!context) {
+ context = gl::GLContextProvider::CreateForCompositorWidget(
+ mWidget,
+ /* aHardwareWebRender */ false,
+ gfxVars::RequiresAcceleratedGLContextForCompositorOGL());
+ }
+
+ if (!context) {
+ NS_WARNING("Failed to create CompositorOGL context");
+ }
+
+ return context.forget();
+}
+
+void CompositorOGL::Destroy() {
+ Compositor::Destroy();
+
+ if (mTexturePool) {
+ mTexturePool->Clear();
+ mTexturePool = nullptr;
+ }
+
+ if (!mDestroyed) {
+ mDestroyed = true;
+ CleanupResources();
+ }
+}
+
+void CompositorOGL::CleanupResources() {
+ if (!mGLContext) return;
+
+ if (mSurfacePoolHandle) {
+ mSurfacePoolHandle->Pool()->DestroyGLResourcesForContext(mGLContext);
+ mSurfacePoolHandle = nullptr;
+ }
+
+ RefPtr<GLContext> ctx = mGLContext->GetSharedContext();
+ if (!ctx) {
+ ctx = mGLContext;
+ }
+
+ if (!ctx->MakeCurrent()) {
+ // Leak resources!
+ mQuadVBO = 0;
+ mTriangleVBO = 0;
+ mPreviousFrameDoneSync = nullptr;
+ mThisFrameDoneSync = nullptr;
+ mProgramsHolder = nullptr;
+ mGLContext = nullptr;
+ mNativeLayersReferenceRT = nullptr;
+ mFullWindowRenderTarget = nullptr;
+ return;
+ }
+
+ mProgramsHolder = nullptr;
+ mNativeLayersReferenceRT = nullptr;
+ mFullWindowRenderTarget = nullptr;
+
+#ifdef MOZ_WIDGET_GTK
+ // TextureSources might hold RefPtr<gl::GLContext>.
+ // All of them needs to be released to destroy GLContext.
+ // GLContextGLX has to be destroyed before related gtk window is destroyed.
+ for (auto textureSource : mRegisteredTextureSources) {
+ textureSource->DeallocateDeviceData();
+ }
+ mRegisteredTextureSources.clear();
+#endif
+
+ ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (mQuadVBO) {
+ ctx->fDeleteBuffers(1, &mQuadVBO);
+ mQuadVBO = 0;
+ }
+
+ if (mTriangleVBO) {
+ ctx->fDeleteBuffers(1, &mTriangleVBO);
+ mTriangleVBO = 0;
+ }
+
+ mGLContext->MakeCurrent();
+
+ if (mPreviousFrameDoneSync) {
+ mGLContext->fDeleteSync(mPreviousFrameDoneSync);
+ mPreviousFrameDoneSync = nullptr;
+ }
+
+ if (mThisFrameDoneSync) {
+ mGLContext->fDeleteSync(mThisFrameDoneSync);
+ mThisFrameDoneSync = nullptr;
+ }
+
+ if (mOwnsGLContext) {
+ // On the main thread the Widget will be destroyed soon and calling
+ // MakeCurrent after that could cause a crash (at least with GLX, see bug
+ // 1059793), unless context is marked as destroyed. There may be some
+ // textures still alive that will try to call MakeCurrent on the context so
+ // let's make sure it is marked destroyed now.
+ mGLContext->MarkDestroyed();
+ }
+
+ mGLContext = nullptr;
+}
+
+bool CompositorOGL::Initialize(GLContext* aGLContext,
+ RefPtr<ShaderProgramOGLsHolder> aProgramsHolder,
+ nsCString* const out_failureReason) {
+ MOZ_ASSERT(!mDestroyed);
+ MOZ_ASSERT(!mGLContext);
+
+ mGLContext = aGLContext;
+ mProgramsHolder = aProgramsHolder;
+ mOwnsGLContext = false;
+
+ return Initialize(out_failureReason);
+}
+
+bool CompositorOGL::Initialize(nsCString* const out_failureReason) {
+ ScopedGfxFeatureReporter reporter("GL Layers");
+
+ // Do not allow double initialization
+ MOZ_ASSERT(mGLContext == nullptr || !mOwnsGLContext,
+ "Don't reinitialize CompositorOGL");
+
+ if (!mGLContext) {
+ MOZ_ASSERT(mOwnsGLContext);
+ mGLContext = CreateContext();
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (!mGLContext) {
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_NO_ANDROID_CONTEXT";
+ MOZ_CRASH("We need a context on Android");
+ }
+#endif
+
+ if (!mGLContext) {
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_CREATE_CONTEXT";
+ return false;
+ }
+
+ if (!mProgramsHolder) {
+ mProgramsHolder = new ShaderProgramOGLsHolder(mGLContext);
+ }
+
+ MakeCurrent();
+
+ mHasBGRA = mGLContext->IsExtensionSupported(
+ gl::GLContext::EXT_texture_format_BGRA8888) ||
+ mGLContext->IsExtensionSupported(gl::GLContext::EXT_bgra);
+
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ mGLContext->fEnable(LOCAL_GL_BLEND);
+
+ // initialise a common shader to check that we can actually compile a shader
+ RefPtr<DummyTextureSourceOGL> source = new DummyTextureSourceOGL;
+ RefPtr<EffectRGB> effect =
+ new EffectRGB(source, /* aPremultiplied */ true, SamplingFilter::GOOD);
+ ShaderConfigOGL config = GetShaderConfigFor(effect);
+ if (!GetShaderProgramFor(config)) {
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_COMPILE_SHADER";
+ return false;
+ }
+
+ if (mGLContext->WorkAroundDriverBugs()) {
+ /**
+ * We'll test the ability here to bind NPOT textures to a framebuffer, if
+ * this fails we'll try ARB_texture_rectangle.
+ */
+
+ GLenum textureTargets[] = {LOCAL_GL_TEXTURE_2D, LOCAL_GL_NONE};
+
+ if (!mGLContext->IsGLES()) {
+ // No TEXTURE_RECTANGLE_ARB available on ES2
+ textureTargets[1] = LOCAL_GL_TEXTURE_RECTANGLE_ARB;
+ }
+
+ mFBOTextureTarget = LOCAL_GL_NONE;
+
+ GLuint testFBO = 0;
+ mGLContext->fGenFramebuffers(1, &testFBO);
+ GLuint testTexture = 0;
+
+ for (uint32_t i = 0; i < ArrayLength(textureTargets); i++) {
+ GLenum target = textureTargets[i];
+ if (!target) continue;
+
+ mGLContext->fGenTextures(1, &testTexture);
+ mGLContext->fBindTexture(target, testTexture);
+ mGLContext->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_NEAREST);
+ mGLContext->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_NEAREST);
+ mGLContext->fTexImage2D(
+ target, 0, LOCAL_GL_RGBA, 5, 3, /* sufficiently NPOT */
+ 0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
+
+ // unbind this texture, in preparation for binding it to the FBO
+ mGLContext->fBindTexture(target, 0);
+
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, testFBO);
+ mGLContext->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0, target,
+ testTexture, 0);
+
+ if (mGLContext->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) ==
+ LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+ mFBOTextureTarget = target;
+ mGLContext->fDeleteTextures(1, &testTexture);
+ break;
+ }
+
+ mGLContext->fDeleteTextures(1, &testTexture);
+ }
+
+ if (testFBO) {
+ mGLContext->fDeleteFramebuffers(1, &testFBO);
+ }
+
+ if (mFBOTextureTarget == LOCAL_GL_NONE) {
+ /* Unable to find a texture target that works with FBOs and NPOT textures
+ */
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_NO_TEXTURE_TARGET";
+ return false;
+ }
+ } else {
+ // not trying to work around driver bugs, so TEXTURE_2D should just work
+ mFBOTextureTarget = LOCAL_GL_TEXTURE_2D;
+ }
+
+ // back to default framebuffer, to avoid confusion
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ if (mFBOTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
+ /* If we're using TEXTURE_RECTANGLE, then we must have the ARB
+ * extension -- the EXT variant does not provide support for
+ * texture rectangle access inside GLSL (sampler2DRect,
+ * texture2DRect).
+ */
+ if (!mGLContext->IsExtensionSupported(
+ gl::GLContext::ARB_texture_rectangle)) {
+ *out_failureReason = "FEATURE_FAILURE_OPENGL_ARB_EXT";
+ return false;
+ }
+ }
+
+ // Create a VBO for triangle vertices.
+ mGLContext->fGenBuffers(1, &mTriangleVBO);
+
+ /* Create a simple quad VBO */
+ mGLContext->fGenBuffers(1, &mQuadVBO);
+
+ // 4 quads, with the number of the quad (vertexID) encoded in w.
+ GLfloat vertices[] = {
+ 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
+
+ 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
+
+ 0.0f, 0.0f, 0.0f, 2.0f, 1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 2.0f,
+ 1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 2.0f, 1.0f, 1.0f, 0.0f, 2.0f,
+
+ 0.0f, 0.0f, 0.0f, 3.0f, 1.0f, 0.0f, 0.0f, 3.0f, 0.0f, 1.0f, 0.0f, 3.0f,
+ 1.0f, 0.0f, 0.0f, 3.0f, 0.0f, 1.0f, 0.0f, 3.0f, 1.0f, 1.0f, 0.0f, 3.0f,
+ };
+ HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices);
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+ mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER, verticesOnHeap.ByteLength(),
+ verticesOnHeap.Data(), LOCAL_GL_STATIC_DRAW);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ nsCOMPtr<nsIConsoleService> console(
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+
+ if (console) {
+ nsString msg;
+ msg += nsLiteralString(
+ u"OpenGL compositor Initialized Succesfully.\nVersion: ");
+ msg += NS_ConvertUTF8toUTF16(nsDependentCString(
+ (const char*)mGLContext->fGetString(LOCAL_GL_VERSION)));
+ msg += u"\nVendor: "_ns;
+ msg += NS_ConvertUTF8toUTF16(nsDependentCString(
+ (const char*)mGLContext->fGetString(LOCAL_GL_VENDOR)));
+ msg += u"\nRenderer: "_ns;
+ msg += NS_ConvertUTF8toUTF16(nsDependentCString(
+ (const char*)mGLContext->fGetString(LOCAL_GL_RENDERER)));
+ msg += u"\nFBO Texture Target: "_ns;
+ if (mFBOTextureTarget == LOCAL_GL_TEXTURE_2D)
+ msg += u"TEXTURE_2D"_ns;
+ else
+ msg += u"TEXTURE_RECTANGLE"_ns;
+ console->LogStringMessage(msg.get());
+ }
+
+ reporter.SetSuccessful();
+
+ return true;
+}
+
+/*
+ * Returns a size that is equal to, or larger than and closest to,
+ * aSize where both width and height are powers of two.
+ * If the OpenGL setup is capable of using non-POT textures,
+ * then it will just return aSize.
+ */
+static IntSize CalculatePOTSize(const IntSize& aSize, GLContext* gl) {
+ if (CanUploadNonPowerOfTwo(gl)) return aSize;
+
+ return IntSize(RoundUpPow2(aSize.width), RoundUpPow2(aSize.height));
+}
+
+gfx::Rect CompositorOGL::GetTextureCoordinates(gfx::Rect textureRect,
+ TextureSource* aTexture) {
+ // If the OpenGL setup does not support non-power-of-two textures then the
+ // texture's width and height will have been increased to the next
+ // power-of-two (unless already a power of two). In that case we must scale
+ // the texture coordinates to account for that.
+ if (!CanUploadNonPowerOfTwo(mGLContext)) {
+ const IntSize& textureSize = aTexture->GetSize();
+ const IntSize potSize = CalculatePOTSize(textureSize, mGLContext);
+ if (potSize != textureSize) {
+ const float xScale = (float)textureSize.width / (float)potSize.width;
+ const float yScale = (float)textureSize.height / (float)potSize.height;
+ textureRect.Scale(xScale, yScale);
+ }
+ }
+
+ return textureRect;
+}
+
+void CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget) {
+ MOZ_ASSERT(aRenderTarget);
+ // Logical surface size.
+ const gfx::IntSize& size = aRenderTarget->GetSize();
+ // Physical surface size.
+ const gfx::IntSize& phySize = aRenderTarget->GetPhysicalSize();
+
+ // Set the viewport correctly.
+ mGLContext->fViewport(mSurfaceOrigin.x, mSurfaceOrigin.y, phySize.width,
+ phySize.height);
+
+ mViewportSize = size;
+
+ if (!aRenderTarget->HasComplexProjection()) {
+ // We flip the view matrix around so that everything is right-side up; we're
+ // drawing directly into the window's back buffer, so this keeps things
+ // looking correct.
+ // XXX: We keep track of whether the window size changed, so we could skip
+ // this update if it hadn't changed since the last call.
+
+ // Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
+ // 2, 2) and flip the contents.
+ Matrix viewMatrix;
+ viewMatrix.PreTranslate(-1.0, 1.0);
+ viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height));
+ viewMatrix.PreScale(1.0f, -1.0f);
+
+ MOZ_ASSERT(mCurrentRenderTarget, "No destination");
+
+ Matrix4x4 matrix3d = Matrix4x4::From2D(viewMatrix);
+ matrix3d._33 = 0.0f;
+ mProjMatrix = matrix3d;
+ mGLContext->fDepthRange(0.0f, 1.0f);
+ } else {
+ bool depthEnable;
+ float zNear, zFar;
+ aRenderTarget->GetProjection(mProjMatrix, depthEnable, zNear, zFar);
+ mGLContext->fDepthRange(zNear, zFar);
+ }
+}
+
+already_AddRefed<CompositingRenderTarget> CompositorOGL::CreateRenderTarget(
+ const IntRect& aRect, SurfaceInitMode aInit) {
+ MOZ_ASSERT(!aRect.IsZeroArea(),
+ "Trying to create a render target of invalid size");
+
+ if (aRect.IsZeroArea()) {
+ return nullptr;
+ }
+
+ if (!gl()) {
+ // CompositingRenderTargetOGL does not work without a gl context.
+ return nullptr;
+ }
+
+ GLuint tex = 0;
+ GLuint fbo = 0;
+ IntRect rect = aRect;
+ IntSize fboSize;
+ CreateFBOWithTexture(rect, false, 0, &fbo, &tex, &fboSize);
+ return CompositingRenderTargetOGL::CreateForNewFBOAndTakeOwnership(
+ this, tex, fbo, aRect, aRect.TopLeft(), aRect.Size(), mFBOTextureTarget,
+ aInit);
+}
+
+void CompositorOGL::SetRenderTarget(CompositingRenderTarget* aSurface) {
+ MOZ_ASSERT(aSurface);
+ CompositingRenderTargetOGL* surface =
+ static_cast<CompositingRenderTargetOGL*>(aSurface);
+ if (mCurrentRenderTarget != surface) {
+ mCurrentRenderTarget = surface;
+ surface->BindRenderTarget();
+ }
+
+ PrepareViewport(mCurrentRenderTarget);
+}
+
+already_AddRefed<CompositingRenderTarget>
+CompositorOGL::GetCurrentRenderTarget() const {
+ return do_AddRef(mCurrentRenderTarget);
+}
+
+already_AddRefed<CompositingRenderTarget> CompositorOGL::GetWindowRenderTarget()
+ const {
+ return do_AddRef(mWindowRenderTarget);
+}
+
+already_AddRefed<AsyncReadbackBuffer> CompositorOGL::CreateAsyncReadbackBuffer(
+ const IntSize& aSize) {
+ return MakeAndAddRef<AsyncReadbackBufferOGL>(mGLContext, aSize);
+}
+
+bool CompositorOGL::ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) {
+ IntSize size = aSource->GetSize();
+ MOZ_RELEASE_ASSERT(aDest->GetSize() == size);
+
+ RefPtr<CompositingRenderTarget> previousTarget = GetCurrentRenderTarget();
+ if (previousTarget != aSource) {
+ SetRenderTarget(aSource);
+ }
+
+ ScopedPackState scopedPackState(mGLContext);
+ static_cast<AsyncReadbackBufferOGL*>(aDest)->Bind();
+
+ mGLContext->fReadPixels(0, 0, size.width, size.height, LOCAL_GL_RGBA,
+ LOCAL_GL_UNSIGNED_BYTE, 0);
+
+ if (previousTarget != aSource) {
+ SetRenderTarget(previousTarget);
+ }
+ return true;
+}
+
+bool CompositorOGL::BlitRenderTarget(CompositingRenderTarget* aSource,
+ const gfx::IntSize& aSourceSize,
+ const gfx::IntSize& aDestSize) {
+ if (!mGLContext->IsSupported(GLFeature::framebuffer_blit)) {
+ return false;
+ }
+ CompositingRenderTargetOGL* source =
+ static_cast<CompositingRenderTargetOGL*>(aSource);
+ GLuint srcFBO = source->GetFBO();
+ GLuint destFBO = mCurrentRenderTarget->GetFBO();
+ mGLContext->BlitHelper()->BlitFramebufferToFramebuffer(
+ srcFBO, destFBO, IntRect(IntPoint(), aSourceSize),
+ IntRect(IntPoint(), aDestSize), LOCAL_GL_LINEAR);
+ return true;
+}
+
+static GLenum GetFrameBufferInternalFormat(
+ GLContext* gl, GLuint aFrameBuffer,
+ mozilla::widget::CompositorWidget* aWidget) {
+ if (aFrameBuffer == 0) { // default framebuffer
+ return aWidget->GetGLFrameBufferFormat();
+ }
+ return LOCAL_GL_RGBA;
+}
+
+Maybe<IntRect> CompositorOGL::BeginFrameForWindow(
+ const nsIntRegion& aInvalidRegion, const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds, const nsIntRegion& aOpaqueRegion) {
+ MOZ_RELEASE_ASSERT(!mTarget, "mTarget not cleared properly");
+ return BeginFrame(aInvalidRegion, aClipRect, aRenderBounds, aOpaqueRegion);
+}
+
+Maybe<IntRect> CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
+ const Maybe<IntRect>& aClipRect,
+ const IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion) {
+ AUTO_PROFILER_LABEL("CompositorOGL::BeginFrame", GRAPHICS);
+
+ MOZ_ASSERT(!mFrameInProgress,
+ "frame still in progress (should have called EndFrame");
+
+ IntRect rect;
+ if (mUseExternalSurfaceSize) {
+ rect = IntRect(IntPoint(), mSurfaceSize);
+ } else {
+ rect = aRenderBounds;
+ }
+
+ // We can't draw anything to something with no area
+ // so just return
+ if (rect.IsZeroArea()) {
+ return Nothing();
+ }
+
+ // If the widget size changed, we have to force a MakeCurrent
+ // to make sure that GL sees the updated widget size.
+ if (mWidgetSize.ToUnknownSize() != rect.Size()) {
+ MakeCurrent(ForceMakeCurrent);
+
+ mWidgetSize = LayoutDeviceIntSize::FromUnknownSize(rect.Size());
+#ifdef MOZ_WIDGET_GTK
+ if (mWidget && mWidget->AsGTK()) {
+ if (!mWidget->AsGTK()->SetEGLNativeWindowSize(mWidgetSize)) {
+ // We don't have correct window size to paint into.
+ return Nothing();
+ }
+ }
+#endif
+ } else {
+ MakeCurrent();
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ java::GeckoSurfaceTexture::DestroyUnused((int64_t)mGLContext.get());
+ mGLContext->MakeCurrent(); // DestroyUnused can change the current context!
+#endif
+
+ // Default blend function implements "OVER"
+ mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ mGLContext->fEnable(LOCAL_GL_BLEND);
+
+ RefPtr<CompositingRenderTarget> rt;
+ if (mCanRenderToDefaultFramebuffer) {
+ rt = CompositingRenderTargetOGL::CreateForWindow(this, rect.Size());
+ } else if (mTarget) {
+ rt = CreateRenderTarget(rect, INIT_MODE_CLEAR);
+ } else {
+ MOZ_CRASH("Unexpected call");
+ }
+
+ if (!rt) {
+ return Nothing();
+ }
+
+ // We're about to actually draw a frame.
+ mFrameInProgress = true;
+
+ SetRenderTarget(rt);
+ mWindowRenderTarget = mCurrentRenderTarget;
+
+ for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& r = iter.Get();
+ mCurrentFrameInvalidRegion.OrWith(
+ IntRect(r.X(), FlipY(r.YMost()), r.Width(), r.Height()));
+ }
+ // Check to see if there is any transparent dirty region that would require
+ // clearing. If not, just invalidate the framebuffer if supported.
+ // TODO: Currently we initialize the clear region to the widget bounds as
+ // SwapBuffers will update the entire framebuffer. On platforms that support
+ // damage regions, we could initialize this to mCurrentFrameInvalidRegion.
+ IntRegion regionToClear(rect);
+ regionToClear.SubOut(aOpaqueRegion);
+ GLbitfield clearBits = LOCAL_GL_DEPTH_BUFFER_BIT;
+ if (regionToClear.IsEmpty() &&
+ mGLContext->IsSupported(GLFeature::invalidate_framebuffer)) {
+ GLenum attachments[] = {LOCAL_GL_COLOR};
+ mGLContext->fInvalidateFramebuffer(
+ LOCAL_GL_FRAMEBUFFER, MOZ_ARRAY_LENGTH(attachments), attachments);
+ } else {
+ clearBits |= LOCAL_GL_COLOR_BUFFER_BIT;
+ }
+
+#if defined(MOZ_WIDGET_ANDROID)
+ if ((mSurfaceOrigin.x > 0) || (mSurfaceOrigin.y > 0)) {
+ mGLContext->fClearColor(
+ StaticPrefs::gfx_compositor_override_clear_color_r(),
+ StaticPrefs::gfx_compositor_override_clear_color_g(),
+ StaticPrefs::gfx_compositor_override_clear_color_b(),
+ StaticPrefs::gfx_compositor_override_clear_color_a());
+ } else {
+ mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b,
+ mClearColor.a);
+ }
+#else
+ mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b,
+ mClearColor.a);
+#endif // defined(MOZ_WIDGET_ANDROID)
+ mGLContext->fClear(clearBits);
+
+ return Some(rect);
+}
+
+void CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect,
+ bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ GLuint* aFBO, GLuint* aTexture,
+ gfx::IntSize* aAllocSize) {
+ *aTexture =
+ CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer, aAllocSize);
+ mGLContext->fGenFramebuffers(1, aFBO);
+}
+
+// Should be called after calls to fReadPixels or fCopyTexImage2D, and other
+// GL read calls.
+static void WorkAroundAppleIntelHD3000GraphicsGLDriverBug(GLContext* aGL) {
+#ifdef XP_MACOSX
+ if (aGL->WorkAroundDriverBugs() &&
+ aGL->Renderer() == GLRenderer::IntelHD3000) {
+ // Work around a bug in the Apple Intel HD Graphics 3000 driver (bug
+ // 1586627, filed with Apple as FB7379358). This bug has been found present
+ // on 10.9.3 and on 10.13.6, so it likely affects all shipped versions of
+ // this driver. (macOS 10.14 does not support this GPU.)
+ // The bug manifests as follows: Reading from a framebuffer puts that
+ // framebuffer into a state such that deleting that framebuffer can break
+ // other framebuffers in certain cases. More specifically, if you have two
+ // framebuffers A and B, the following sequence of events breaks subsequent
+ // drawing to B:
+ // 1. A becomes "most recently read-from framebuffer".
+ // 2. B is drawn to.
+ // 3. A is deleted, and other GL state (such as GL_SCISSOR enabled state)
+ // is touched.
+ // 4. B is drawn to again.
+ // Now all draws to framebuffer B, including the draw from step 4, will
+ // render at the wrong position and upside down.
+ //
+ // When AfterGLReadCall() is called, the currently bound framebuffer is the
+ // framebuffer that has been read from most recently. So in the presence of
+ // this bug, deleting this framebuffer has now become dangerous. We work
+ // around the bug by creating a new short-lived framebuffer, making that new
+ // framebuffer the most recently read-from framebuffer (using
+ // glCopyTexImage2D), and then deleting it under controlled circumstances.
+ // This deletion is not affected by the bug because our deletion call is not
+ // interleaved with draw calls to another framebuffer and a touching of the
+ // GL scissor enabled state.
+
+ ScopedTexture texForReading(aGL);
+ {
+ // Initialize a 1x1 texture.
+ ScopedBindTexture autoBindTexForReading(aGL, texForReading);
+ aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 1, 1, 0,
+ LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+ }
+ // Make a framebuffer around the texture.
+ ScopedFramebufferForTexture autoFBForReading(aGL, texForReading);
+ if (autoFBForReading.IsComplete()) {
+ // "Read" from the framebuffer, by initializing a new texture using
+ // glCopyTexImage2D. This flips the bad bit on autoFBForReading.FB().
+ ScopedBindFramebuffer autoFB(aGL, autoFBForReading.FB());
+ ScopedTexture texReadingDest(aGL);
+ ScopedBindTexture autoBindTexReadingDest(aGL, texReadingDest);
+ aGL->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 0, 0, 1, 1,
+ 0);
+ }
+ // When autoFBForReading goes out of scope, the "poisoned" framebuffer is
+ // deleted, and the bad state seems to go away along with it.
+ }
+#endif
+}
+
+GLuint CompositorOGL::CreateTexture(const IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ IntSize* aAllocSize) {
+ // we're about to create a framebuffer backed by textures to use as an
+ // intermediate surface. What to do if its size (as given by aRect) would
+ // exceed the maximum texture size supported by the GL? The present code
+ // chooses the compromise of just clamping the framebuffer's size to the max
+ // supported size. This gives us a lower resolution rendering of the
+ // intermediate surface (children layers). See bug 827170 for a discussion.
+ IntRect clampedRect = aRect;
+ int32_t maxTexSize = GetMaxTextureSize();
+ clampedRect.SetWidth(std::min(clampedRect.Width(), maxTexSize));
+ clampedRect.SetHeight(std::min(clampedRect.Height(), maxTexSize));
+
+ auto clampedRectWidth = clampedRect.Width();
+ auto clampedRectHeight = clampedRect.Height();
+
+ GLuint tex;
+
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
+ mGLContext->fGenTextures(1, &tex);
+ mGLContext->fBindTexture(mFBOTextureTarget, tex);
+
+ if (aCopyFromSource) {
+ GLuint curFBO = mCurrentRenderTarget->GetFBO();
+ if (curFBO != aSourceFrameBuffer) {
+ mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aSourceFrameBuffer);
+ }
+
+ // We're going to create an RGBA temporary fbo. But to
+ // CopyTexImage() from the current framebuffer, the framebuffer's
+ // format has to be compatible with the new texture's. So we
+ // check the format of the framebuffer here and take a slow path
+ // if it's incompatible.
+ GLenum format =
+ GetFrameBufferInternalFormat(gl(), aSourceFrameBuffer, mWidget);
+
+ bool isFormatCompatibleWithRGBA =
+ gl()->IsGLES() ? (format == LOCAL_GL_RGBA) : true;
+
+ if (isFormatCompatibleWithRGBA) {
+ mGLContext->fCopyTexImage2D(mFBOTextureTarget, 0, LOCAL_GL_RGBA,
+ clampedRect.X(), FlipY(clampedRect.YMost()),
+ clampedRectWidth, clampedRectHeight, 0);
+ WorkAroundAppleIntelHD3000GraphicsGLDriverBug(mGLContext);
+ } else {
+ // Curses, incompatible formats. Take a slow path.
+
+ // RGBA
+ size_t bufferSize = clampedRectWidth * clampedRectHeight * 4;
+ auto buf = MakeUnique<uint8_t[]>(bufferSize);
+
+ mGLContext->fReadPixels(clampedRect.X(), clampedRect.Y(),
+ clampedRectWidth, clampedRectHeight,
+ LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf.get());
+ WorkAroundAppleIntelHD3000GraphicsGLDriverBug(mGLContext);
+ mGLContext->fTexImage2D(mFBOTextureTarget, 0, LOCAL_GL_RGBA,
+ clampedRectWidth, clampedRectHeight, 0,
+ LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, buf.get());
+ }
+
+ GLenum error = mGLContext->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ nsAutoCString msg;
+ msg.AppendPrintf(
+ "Texture initialization failed! -- error 0x%x, Source %d, Source "
+ "format %d, RGBA Compat %d",
+ error, aSourceFrameBuffer, format, isFormatCompatibleWithRGBA);
+ NS_ERROR(msg.get());
+ }
+ } else {
+ mGLContext->fTexImage2D(mFBOTextureTarget, 0, LOCAL_GL_RGBA,
+ clampedRectWidth, clampedRectHeight, 0,
+ LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
+ }
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGLContext->fTexParameteri(mFBOTextureTarget, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGLContext->fBindTexture(mFBOTextureTarget, 0);
+
+ if (aAllocSize) {
+ aAllocSize->width = clampedRectWidth;
+ aAllocSize->height = clampedRectHeight;
+ }
+
+ return tex;
+}
+
+ShaderConfigOGL CompositorOGL::GetShaderConfigFor(Effect* aEffect,
+ bool aDEAAEnabled) const {
+ ShaderConfigOGL config;
+
+ switch (aEffect->mType) {
+ case EffectTypes::YCBCR: {
+ config.SetYCbCr(true);
+ EffectYCbCr* effectYCbCr = static_cast<EffectYCbCr*>(aEffect);
+ config.SetColorMultiplier(
+ RescalingFactorForColorDepth(effectYCbCr->mColorDepth));
+ config.SetTextureTarget(
+ effectYCbCr->mTexture->AsSourceOGL()->GetTextureTarget());
+ break;
+ }
+ case EffectTypes::NV12:
+ config.SetNV12(true);
+ if (gl()->IsExtensionSupported(gl::GLContext::ARB_texture_rectangle)) {
+ config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+ } else {
+ config.SetTextureTarget(LOCAL_GL_TEXTURE_2D);
+ }
+ break;
+ default: {
+ MOZ_ASSERT(aEffect->mType == EffectTypes::RGB);
+ TexturedEffect* texturedEffect = static_cast<TexturedEffect*>(aEffect);
+ TextureSourceOGL* source = texturedEffect->mTexture->AsSourceOGL();
+ MOZ_ASSERT_IF(
+ source->GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL,
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+ source->GetFormat() == gfx::SurfaceFormat::B8G8R8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16);
+ MOZ_ASSERT_IF(
+ source->GetTextureTarget() == LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+ source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16 ||
+ source->GetFormat() == gfx::SurfaceFormat::YUV422);
+ config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
+ source->GetFormat());
+ if (!texturedEffect->mPremultiplied) {
+ config.SetNoPremultipliedAlpha();
+ }
+ break;
+ }
+ }
+ config.SetDEAA(aDEAAEnabled);
+ return config;
+}
+
+ShaderProgramOGL* CompositorOGL::GetShaderProgramFor(
+ const ShaderConfigOGL& aConfig) {
+ ShaderProgramOGL* shader = mProgramsHolder->GetShaderProgramFor(aConfig);
+ return shader;
+}
+
+void CompositorOGL::ResetProgram() { mProgramsHolder->ResetCurrentProgram(); }
+
+static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode,
+ bool aIsPremultiplied = true) {
+ if (BlendOpIsMixBlendMode(aBlendMode)) {
+ // Mix-blend modes require an extra step (or more) that cannot be expressed
+ // in the fixed-function blending capabilities of opengl. We handle them
+ // separately in shaders, and the shaders assume we will use our default
+ // blend function for compositing (premultiplied OP_OVER).
+ return false;
+ }
+ if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) {
+ return false;
+ }
+
+ GLenum srcBlend;
+ GLenum dstBlend;
+ GLenum srcAlphaBlend = LOCAL_GL_ONE;
+ GLenum dstAlphaBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
+
+ switch (aBlendMode) {
+ case gfx::CompositionOp::OP_OVER:
+ MOZ_ASSERT(!aIsPremultiplied);
+ srcBlend = LOCAL_GL_SRC_ALPHA;
+ dstBlend = LOCAL_GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case gfx::CompositionOp::OP_SOURCE:
+ srcBlend = aIsPremultiplied ? LOCAL_GL_ONE : LOCAL_GL_SRC_ALPHA;
+ dstBlend = LOCAL_GL_ZERO;
+ srcAlphaBlend = LOCAL_GL_ONE;
+ dstAlphaBlend = LOCAL_GL_ZERO;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported blend mode!");
+ return false;
+ }
+
+ aGL->fBlendFuncSeparate(srcBlend, dstBlend, srcAlphaBlend, dstAlphaBlend);
+ return true;
+}
+
+gfx::Point3D CompositorOGL::GetLineCoefficients(const gfx::Point& aPoint1,
+ const gfx::Point& aPoint2) {
+ // Return standard coefficients for a line between aPoint1 and aPoint2
+ // for standard line equation:
+ //
+ // Ax + By + C = 0
+ //
+ // A = (p1.y – p2.y)
+ // B = (p2.x – p1.x)
+ // C = (p1.x * p2.y) – (p2.x * p1.y)
+
+ gfx::Point3D coeffecients;
+ coeffecients.x = aPoint1.y - aPoint2.y;
+ coeffecients.y = aPoint2.x - aPoint1.x;
+ coeffecients.z =
+ aPoint1.x.value * aPoint2.y.value - aPoint2.x.value * aPoint1.y.value;
+
+ coeffecients *= 1.0f / sqrtf(coeffecients.x * coeffecients.x +
+ coeffecients.y * coeffecients.y);
+
+ // Offset outwards by 0.5 pixel as the edge is considered to be 1 pixel
+ // wide and included within the interior of the polygon
+ coeffecients.z += 0.5f;
+
+ return coeffecients;
+}
+
+void CompositorOGL::DrawQuad(const Rect& aRect, const IntRect& aClipRect,
+ const EffectChain& aEffectChain, Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) {
+ AUTO_PROFILER_LABEL("CompositorOGL::DrawQuad", GRAPHICS);
+
+ DrawGeometry(aRect, aRect, aClipRect, aEffectChain, aOpacity, aTransform,
+ aVisibleRect);
+}
+
+template <typename Geometry>
+void CompositorOGL::DrawGeometry(const Geometry& aGeometry,
+ const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain,
+ gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) {
+ MOZ_ASSERT(mFrameInProgress, "frame not started");
+ MOZ_ASSERT(mCurrentRenderTarget, "No destination");
+
+ MakeCurrent();
+
+ // Convert aClipRect into render target space, and intersect it with the
+ // render target's clip.
+ IntRect clipRect = aClipRect + mCurrentRenderTarget->GetClipSpaceOrigin();
+ if (Maybe<IntRect> rtClip = mCurrentRenderTarget->GetClipRect()) {
+ clipRect = clipRect.Intersect(*rtClip);
+ }
+
+ Rect destRect = aTransform.TransformAndClipBounds(
+ aRect, Rect(mCurrentRenderTarget->GetRect().Intersect(clipRect)));
+ if (destRect.IsEmpty()) {
+ return;
+ }
+
+ // Move clipRect into device space.
+ IntPoint offset = mCurrentRenderTarget->GetOrigin();
+ clipRect -= offset;
+
+ if (!mTarget && mCurrentRenderTarget->IsWindow()) {
+ clipRect.MoveBy(mSurfaceOrigin.x, -mSurfaceOrigin.y);
+ }
+
+ ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true);
+ ScopedScissorRect autoScissorRect(mGLContext, clipRect.X(),
+ FlipY(clipRect.Y() + clipRect.Height()),
+ clipRect.Width(), clipRect.Height());
+
+ // Only apply DEAA to quads that have been transformed such that aliasing
+ // could be visible
+ bool bEnableAA = StaticPrefs::layers_deaa_enabled() &&
+ !aTransform.Is2DIntegerTranslation();
+
+ ShaderConfigOGL config =
+ GetShaderConfigFor(aEffectChain.mPrimaryEffect, bEnableAA);
+
+ config.SetOpacity(aOpacity != 1.f);
+ ApplyPrimitiveConfig(config, aGeometry);
+
+ ShaderProgramOGL* program = mProgramsHolder->ActivateProgram(config);
+ if (!program) {
+ return;
+ }
+ program->SetProjectionMatrix(mProjMatrix);
+ program->SetLayerTransform(aTransform);
+ program->SetRenderOffset(offset.x, offset.y);
+
+ if (aOpacity != 1.f) program->SetLayerOpacity(aOpacity);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ TextureSourceOGL* source = nullptr;
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ source = texturedEffect->mTexture->AsSourceOGL();
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then
+ // 0,0..1,1.
+ program->SetTexCoordMultiplier(source->GetSize().width,
+ source->GetSize().height);
+ }
+
+ // XXX kip - These calculations could be performed once per layer rather than
+ // for every tile. This might belong in Compositor.cpp once DEAA
+ // is implemented for DirectX.
+ if (bEnableAA) {
+ // Calculate the transformed vertices of aVisibleRect in screen space
+ // pixels, mirroring the calculations in the vertex shader
+ Matrix4x4 flatTransform = aTransform;
+ flatTransform.PostTranslate(-offset.x, -offset.y, 0.0f);
+ flatTransform *= mProjMatrix;
+
+ Rect viewportClip = Rect(-1.0f, -1.0f, 2.0f, 2.0f);
+ size_t edgeCount = 0;
+ Point3D coefficients[4];
+
+ Point points[Matrix4x4::kTransformAndClipRectMaxVerts];
+ size_t pointCount =
+ flatTransform.TransformAndClipRect(aVisibleRect, viewportClip, points);
+ for (size_t i = 0; i < pointCount; i++) {
+ points[i] = Point((points[i].x * 0.5f + 0.5f) * mViewportSize.width,
+ (points[i].y * 0.5f + 0.5f) * mViewportSize.height);
+ }
+ if (pointCount > 2) {
+ // Use shoelace formula on a triangle in the clipped quad to determine if
+ // winding order is reversed. Iterate through the triangles until one is
+ // found with a non-zero area.
+ float winding = 0.0f;
+ size_t wp = 0;
+ while (winding == 0.0f && wp < pointCount) {
+ int wp1 = (wp + 1) % pointCount;
+ int wp2 = (wp + 2) % pointCount;
+ winding = (points[wp1].x - points[wp].x).value *
+ (points[wp1].y + points[wp].y).value +
+ (points[wp2].x - points[wp1].x).value *
+ (points[wp2].y + points[wp1].y).value +
+ (points[wp].x - points[wp2].x).value *
+ (points[wp].y + points[wp2].y).value;
+ wp++;
+ }
+ bool frontFacing = winding >= 0.0f;
+
+ // Calculate the line coefficients used by the DEAA shader to determine
+ // the sub-pixel coverage of the edge pixels
+ for (size_t i = 0; i < pointCount; i++) {
+ const Point& p1 = points[i];
+ const Point& p2 = points[(i + 1) % pointCount];
+ // Create a DEAA edge for any non-straight lines, to a maximum of 4
+ if (p1.x != p2.x && p1.y != p2.y && edgeCount < 4) {
+ if (frontFacing) {
+ coefficients[edgeCount++] = GetLineCoefficients(p2, p1);
+ } else {
+ coefficients[edgeCount++] = GetLineCoefficients(p1, p2);
+ }
+ }
+ }
+ }
+
+ // The coefficients that are not needed must not cull any fragments.
+ // We fill these unused coefficients with a clipping plane that has no
+ // effect.
+ for (size_t i = edgeCount; i < 4; i++) {
+ coefficients[i] = Point3D(0.0f, 1.0f, mViewportSize.height);
+ }
+
+ // Set uniforms required by DEAA shader
+ Matrix4x4 transformInverted = aTransform;
+ transformInverted.Invert();
+ program->SetLayerTransformInverse(transformInverted);
+ program->SetDEAAEdges(coefficients);
+ program->SetVisibleCenter(aVisibleRect.Center());
+ program->SetViewportSize(mViewportSize);
+ }
+
+ bool didSetBlendMode = false;
+
+ switch (aEffectChain.mPrimaryEffect->mType) {
+ case EffectTypes::RGB: {
+ TexturedEffect* texturedEffect =
+ static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* source = texturedEffect->mTexture;
+
+ didSetBlendMode = SetBlendMode(gl(), gfx::CompositionOp::OP_OVER,
+ texturedEffect->mPremultiplied);
+
+ gfx::SamplingFilter samplingFilter = texturedEffect->mSamplingFilter;
+
+ source->AsSourceOGL()->BindTexture(LOCAL_GL_TEXTURE0, samplingFilter);
+
+ program->SetTextureUnit(0);
+
+ Matrix4x4 textureTransform = source->AsSourceOGL()->GetTextureTransform();
+ program->SetTextureTransform(textureTransform);
+
+ BindAndDrawGeometryWithTextureRect(
+ program, aGeometry, texturedEffect->mTextureCoords, source);
+ source->AsSourceOGL()->MaybeFenceTexture();
+ } break;
+ case EffectTypes::YCBCR: {
+ EffectYCbCr* effectYCbCr =
+ static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* sourceYCbCr = effectYCbCr->mTexture;
+ const int Y = 0, Cb = 1, Cr = 2;
+ TextureSourceOGL* sourceY = sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
+ TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
+ TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
+
+ if (!sourceY || !sourceCb || !sourceCr) {
+ NS_WARNING("Invalid layer texture.");
+ return;
+ }
+
+ sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectYCbCr->mSamplingFilter);
+ sourceCb->BindTexture(LOCAL_GL_TEXTURE1, effectYCbCr->mSamplingFilter);
+ sourceCr->BindTexture(LOCAL_GL_TEXTURE2, effectYCbCr->mSamplingFilter);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then
+ // 0,0..1,1.
+ program->SetCbCrTexCoordMultiplier(sourceCb->GetSize().width,
+ sourceCb->GetSize().height);
+ }
+
+ program->SetYCbCrTextureUnits(Y, Cb, Cr);
+ program->SetTextureTransform(Matrix4x4());
+ program->SetYUVColorSpace(effectYCbCr->mYUVColorSpace);
+
+ BindAndDrawGeometryWithTextureRect(program, aGeometry,
+ effectYCbCr->mTextureCoords,
+ sourceYCbCr->GetSubSource(Y));
+ sourceY->MaybeFenceTexture();
+ sourceCb->MaybeFenceTexture();
+ sourceCr->MaybeFenceTexture();
+ } break;
+ case EffectTypes::NV12: {
+ EffectNV12* effectNV12 =
+ static_cast<EffectNV12*>(aEffectChain.mPrimaryEffect.get());
+ TextureSource* sourceNV12 = effectNV12->mTexture;
+ const int Y = 0, CbCr = 1;
+ TextureSourceOGL* sourceY = sourceNV12->GetSubSource(Y)->AsSourceOGL();
+ TextureSourceOGL* sourceCbCr =
+ sourceNV12->GetSubSource(CbCr)->AsSourceOGL();
+
+ if (!sourceY || !sourceCbCr) {
+ NS_WARNING("Invalid layer texture.");
+ return;
+ }
+
+ sourceY->BindTexture(LOCAL_GL_TEXTURE0, effectNV12->mSamplingFilter);
+ sourceCbCr->BindTexture(LOCAL_GL_TEXTURE1, effectNV12->mSamplingFilter);
+
+ if (config.mFeatures & ENABLE_TEXTURE_RECT) {
+ // This is used by IOSurface that use 0,0...w,h coordinate rather then
+ // 0,0..1,1.
+ program->SetCbCrTexCoordMultiplier(sourceCbCr->GetSize().width,
+ sourceCbCr->GetSize().height);
+ }
+
+ program->SetNV12TextureUnits(Y, CbCr);
+ program->SetTextureTransform(Matrix4x4());
+ program->SetYUVColorSpace(effectNV12->mYUVColorSpace);
+
+ BindAndDrawGeometryWithTextureRect(program, aGeometry,
+ effectNV12->mTextureCoords,
+ sourceNV12->GetSubSource(Y));
+ sourceY->MaybeFenceTexture();
+ sourceCbCr->MaybeFenceTexture();
+ } break;
+ default:
+ MOZ_ASSERT(false, "Unhandled effect type");
+ break;
+ }
+
+ if (didSetBlendMode) {
+ gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
+ LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ // in case rendering has used some other GL context
+ MakeCurrent();
+}
+
+void CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const gfx::Rect& aRect) {
+ BindAndDrawQuad(aProgram, aRect);
+}
+
+void CompositorOGL::BindAndDrawGeometry(
+ ShaderProgramOGL* aProgram,
+ const nsTArray<gfx::TexturedTriangle>& aTriangles) {
+ NS_ASSERTION(aProgram->HasInitialized(),
+ "Shader program not correctly initialized");
+
+ const nsTArray<TexturedVertex> vertices =
+ TexturedTrianglesToVertexArray(aTriangles);
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTriangleVBO);
+ mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER,
+ vertices.Length() * sizeof(TexturedVertex),
+ vertices.Elements(), LOCAL_GL_STREAM_DRAW);
+
+ const GLsizei stride = 4 * sizeof(GLfloat);
+ InitializeVAO(kCoordinateAttributeIndex, 2, stride, 0);
+ InitializeVAO(kTexCoordinateAttributeIndex, 2, stride, 2 * sizeof(GLfloat));
+
+ mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, vertices.Length());
+
+ mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+ mGLContext->fDisableVertexAttribArray(kTexCoordinateAttributeIndex);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+}
+
+// |aRect| is the rectangle we want to draw to. We will draw it with
+// up to 4 draw commands if necessary to avoid wrapping.
+// |aTexCoordRect| is the rectangle from the texture that we want to
+// draw using the given program.
+// |aTexture| is the texture we are drawing. Its actual size can be
+// larger than the rectangle given by |texCoordRect|.
+void CompositorOGL::BindAndDrawGeometryWithTextureRect(
+ ShaderProgramOGL* aProg, const Rect& aRect, const Rect& aTexCoordRect,
+ TextureSource* aTexture) {
+ Rect scaledTexCoordRect = GetTextureCoordinates(aTexCoordRect, aTexture);
+ Rect layerRects[4];
+ Rect textureRects[4];
+ size_t rects = DecomposeIntoNoRepeatRects(aRect, scaledTexCoordRect,
+ &layerRects, &textureRects);
+
+ BindAndDrawQuads(aProg, rects, layerRects, textureRects);
+}
+
+void CompositorOGL::BindAndDrawGeometryWithTextureRect(
+ ShaderProgramOGL* aProg, const nsTArray<gfx::TexturedTriangle>& aTriangles,
+ const gfx::Rect& aTexCoordRect, TextureSource* aTexture) {
+ BindAndDrawGeometry(aProg, aTriangles);
+}
+
+void CompositorOGL::BindAndDrawQuads(ShaderProgramOGL* aProg, int aQuads,
+ const Rect* aLayerRects,
+ const Rect* aTextureRects) {
+ NS_ASSERTION(aProg->HasInitialized(),
+ "Shader program not correctly initialized");
+
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mQuadVBO);
+ InitializeVAO(kCoordinateAttributeIndex, 4, 0, 0);
+
+ aProg->SetLayerRects(aLayerRects);
+ if (aProg->GetTextureCount() > 0) {
+ aProg->SetTextureRects(aTextureRects);
+ }
+
+ // We are using GL_TRIANGLES here because the Mac Intel drivers fail to
+ // properly process uniform arrays with GL_TRIANGLE_STRIP. Go figure.
+ mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 6 * aQuads);
+ mGLContext->fDisableVertexAttribArray(kCoordinateAttributeIndex);
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+}
+
+void CompositorOGL::InitializeVAO(const GLuint aAttrib, const GLint aComponents,
+ const GLsizei aStride, const size_t aOffset) {
+ mGLContext->fVertexAttribPointer(aAttrib, aComponents, LOCAL_GL_FLOAT,
+ LOCAL_GL_FALSE, aStride,
+ reinterpret_cast<GLvoid*>(aOffset));
+ mGLContext->fEnableVertexAttribArray(aAttrib);
+}
+
+#ifdef MOZ_DUMP_PAINTING
+template <typename T>
+void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf) {
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ gfxUtils::DumpAsDataURI(aSurf, gfxUtils::sDumpPaintFile);
+ } else {
+ nsCString uri = gfxUtils::GetAsDataURI(aSurf);
+ nsPrintfCString string(R"(array["%s-%)" PRIu64 R"("]="%s";\n)",
+ aObj->Name(), uint64_t(aObj), uri.BeginReading());
+ fprintf_stderr(gfxUtils::sDumpPaintFile, "%s", string.get());
+ }
+}
+
+void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget) {
+ RefPtr<SourceSurface> surf = aTarget->Snapshot();
+ RefPtr<DataSourceSurface> dSurf = surf->GetDataSurface();
+ WriteSnapshotToDumpFile_internal(aCompositor, dSurf);
+}
+#endif
+
+void CompositorOGL::EndFrame() {
+ AUTO_PROFILER_LABEL("CompositorOGL::EndFrame", GRAPHICS);
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxEnv::MOZ_DISABLE_FORCE_PRESENT()) {
+ LayoutDeviceIntSize size;
+ if (mUseExternalSurfaceSize) {
+ size = LayoutDeviceIntSize(mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ size = mWidget->GetClientSize();
+ }
+ RefPtr<DrawTarget> target =
+ gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ IntSize(size.width, size.height), SurfaceFormat::B8G8R8A8);
+ if (target) {
+ CopyToTarget(target, nsIntPoint(), Matrix());
+ WriteSnapshotToDumpFile(this, target);
+ }
+ }
+#endif
+
+ mFrameInProgress = false;
+ mShouldInvalidateWindow = false;
+
+ if (mTarget) {
+ CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix());
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+ mTarget = nullptr;
+ mWindowRenderTarget = nullptr;
+ mCurrentRenderTarget = nullptr;
+ Compositor::EndFrame();
+ return;
+ }
+
+ mWindowRenderTarget = nullptr;
+ mCurrentRenderTarget = nullptr;
+
+ if (mTexturePool) {
+ mTexturePool->EndFrame();
+ }
+
+ InsertFrameDoneSync();
+
+ mGLContext->SetDamage(mCurrentFrameInvalidRegion);
+ mGLContext->SwapBuffers();
+ mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+ // Unbind all textures
+ for (GLuint i = 0; i <= 4; i++) {
+ mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
+ if (!mGLContext->IsGLES()) {
+ mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
+ }
+ }
+
+ mCurrentFrameInvalidRegion.SetEmpty();
+
+ Compositor::EndFrame();
+}
+
+void CompositorOGL::InsertFrameDoneSync() {
+#ifdef XP_MACOSX
+ // Only do this on macOS.
+ // On other platforms, SwapBuffers automatically applies back-pressure.
+ if (mThisFrameDoneSync) {
+ mGLContext->fDeleteSync(mThisFrameDoneSync);
+ }
+ mThisFrameDoneSync =
+ mGLContext->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+#elif defined(MOZ_WIDGET_ANDROID)
+ const auto& gle = gl::GLContextEGL::Cast(mGLContext);
+ const auto& egl = gle->mEgl;
+
+ EGLSync sync = nullptr;
+ if (AndroidHardwareBufferApi::Get()) {
+ sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ }
+ if (sync) {
+ int fenceFd = egl->fDupNativeFenceFDANDROID(sync);
+ if (fenceFd >= 0) {
+ mReleaseFenceFd = ipc::FileDescriptor(UniqueFileHandle(fenceFd));
+ }
+ egl->fDestroySync(sync);
+ sync = nullptr;
+ }
+#endif
+}
+
+ipc::FileDescriptor CompositorOGL::GetReleaseFence() { return mReleaseFenceFd; }
+
+bool CompositorOGL::NeedToRecreateFullWindowRenderTarget() const {
+ if (!ShouldRecordFrames()) {
+ return false;
+ }
+ if (!mFullWindowRenderTarget) {
+ return true;
+ }
+ IntSize windowSize = mWidget->GetClientSize().ToUnknownSize();
+ return mFullWindowRenderTarget->GetSize() != windowSize;
+}
+
+void CompositorOGL::SetDestinationSurfaceSize(const IntSize& aSize) {
+ mSurfaceSize.width = aSize.width;
+ mSurfaceSize.height = aSize.height;
+}
+
+void CompositorOGL::CopyToTarget(DrawTarget* aTarget,
+ const nsIntPoint& aTopLeft,
+ const gfx::Matrix& aTransform) {
+ MOZ_ASSERT(aTarget);
+ IntRect rect;
+ if (mUseExternalSurfaceSize) {
+ rect = IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
+ } else {
+ rect = IntRect(0, 0, mWidgetSize.width, mWidgetSize.height);
+ }
+ GLint width = rect.Width();
+ GLint height = rect.Height();
+
+ if ((int64_t(width) * int64_t(height) * int64_t(4)) > INT32_MAX) {
+ NS_ERROR("Widget size too big - integer overflow!");
+ return;
+ }
+
+ RefPtr<DataSourceSurface> source = Factory::CreateDataSourceSurface(
+ rect.Size(), gfx::SurfaceFormat::B8G8R8A8);
+ if (NS_WARN_IF(!source)) {
+ return;
+ }
+
+ ReadPixelsIntoDataSurface(mGLContext, source);
+
+ // Map from GL space to Cairo space and reverse the world transform.
+ Matrix glToCairoTransform = aTransform;
+ glToCairoTransform.Invert();
+ glToCairoTransform.PreScale(1.0, -1.0);
+ glToCairoTransform.PreTranslate(0.0, -height);
+
+ glToCairoTransform.PostTranslate(-aTopLeft.x, -aTopLeft.y);
+
+ Matrix oldMatrix = aTarget->GetTransform();
+ aTarget->SetTransform(glToCairoTransform);
+ Rect floatRect = Rect(rect.X(), rect.Y(), width, height);
+ aTarget->DrawSurface(source, floatRect, floatRect, DrawSurfaceOptions(),
+ DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+ aTarget->SetTransform(oldMatrix);
+ aTarget->Flush();
+}
+
+void CompositorOGL::Pause() {
+#ifdef MOZ_WIDGET_ANDROID
+ if (!gl() || gl()->IsDestroyed()) return;
+ // ReleaseSurface internally calls MakeCurrent
+ gl()->ReleaseSurface();
+#elif defined(MOZ_WIDGET_GTK)
+ // ReleaseSurface internally calls MakeCurrent
+ gl()->ReleaseSurface();
+#endif
+}
+
+bool CompositorOGL::Resume() {
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT) || \
+ defined(MOZ_WIDGET_GTK)
+ if (!gl() || gl()->IsDestroyed()) return false;
+
+ // RenewSurface internally calls MakeCurrent.
+ return gl()->RenewSurface(GetWidget());
+#else
+ return true;
+#endif
+}
+
+already_AddRefed<DataTextureSource> CompositorOGL::CreateDataTextureSource(
+ TextureFlags aFlags) {
+ if (!gl()) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureImageTextureSourceOGL>(this, aFlags);
+}
+
+int32_t CompositorOGL::GetMaxTextureSize() const {
+ MOZ_ASSERT(mGLContext);
+ GLint texSize = 0;
+ mGLContext->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &texSize);
+ MOZ_ASSERT(texSize != 0);
+ return texSize;
+}
+
+void CompositorOGL::MakeCurrent(MakeCurrentFlags aFlags) {
+ if (mDestroyed) {
+ NS_WARNING("Call on destroyed layer manager");
+ return;
+ }
+ mGLContext->MakeCurrent(aFlags & ForceMakeCurrent);
+}
+
+GLuint CompositorOGL::GetTemporaryTexture(GLenum aTarget, GLenum aUnit) {
+ if (!mTexturePool) {
+ mTexturePool = new PerUnitTexturePoolOGL(gl());
+ }
+ return mTexturePool->GetTexture(aTarget, aUnit);
+}
+
+GLuint PerUnitTexturePoolOGL::GetTexture(GLenum aTarget, GLenum aTextureUnit) {
+ if (mTextureTarget == 0) {
+ mTextureTarget = aTarget;
+ }
+ MOZ_ASSERT(mTextureTarget == aTarget);
+
+ size_t index = aTextureUnit - LOCAL_GL_TEXTURE0;
+ // lazily grow the array of temporary textures
+ if (mTextures.Length() <= index) {
+ size_t prevLength = mTextures.Length();
+ mTextures.SetLength(index + 1);
+ for (unsigned int i = prevLength; i <= index; ++i) {
+ mTextures[i] = 0;
+ }
+ }
+ // lazily initialize the temporary textures
+ if (!mTextures[index]) {
+ if (!mGL->MakeCurrent()) {
+ return 0;
+ }
+ mGL->fGenTextures(1, &mTextures[index]);
+ mGL->fBindTexture(aTarget, mTextures[index]);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(aTarget, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ }
+ return mTextures[index];
+}
+
+void PerUnitTexturePoolOGL::DestroyTextures() {
+ if (mGL && mGL->MakeCurrent()) {
+ if (mTextures.Length() > 0) {
+ mGL->fDeleteTextures(mTextures.Length(), &mTextures[0]);
+ }
+ }
+ mTextures.SetLength(0);
+}
+
+void CompositorOGL::RegisterTextureSource(TextureSource* aTextureSource) {
+#ifdef MOZ_WIDGET_GTK
+ if (mDestroyed) {
+ return;
+ }
+ mRegisteredTextureSources.insert(aTextureSource);
+#endif
+}
+
+void CompositorOGL::UnregisterTextureSource(TextureSource* aTextureSource) {
+#ifdef MOZ_WIDGET_GTK
+ if (mDestroyed) {
+ return;
+ }
+ mRegisteredTextureSources.erase(aTextureSource);
+#endif
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h
new file mode 100644
index 0000000000..878372aa64
--- /dev/null
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -0,0 +1,446 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_COMPOSITOROGL_H
+#define MOZILLA_GFX_COMPOSITOROGL_H
+
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "gfx2DGlue.h"
+#include "GLContextTypes.h" // for GLContext, etc
+#include "GLDefs.h" // for GLuint, LOCAL_GL_TEXTURE_2D, etc
+#include "OGLShaderConfig.h" // for ShaderConfigOGL
+#include "Units.h" // for ScreenPoint
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override, final
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefPtr
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, Point
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Triangle.h" // for Triangle
+#include "mozilla/gfx/Types.h" // for Float, SurfaceFormat, etc
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/layers/Compositor.h" // for SurfaceInitMode, Compositor, etc
+#include "mozilla/layers/CompositorTypes.h" // for MaskType::MaskType::NumMaskTypes, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsTArray.h" // for AutoTArray, nsTArray, etc
+#include "nsThreadUtils.h" // for nsRunnable
+#include "nsXULAppAPI.h" // for XRE_GetProcessType
+#include "nscore.h" // for NS_IMETHOD
+
+class nsIWidget;
+
+namespace mozilla {
+
+namespace layers {
+
+class CompositingRenderTarget;
+class CompositingRenderTargetOGL;
+class DataTextureSource;
+class ShaderProgramOGL;
+class ShaderProgramOGLsHolder;
+class TextureSource;
+class TextureSourceOGL;
+class BufferTextureHost;
+struct Effect;
+struct EffectChain;
+
+/**
+ * Interface for pools of temporary gl textures for the compositor.
+ * The textures are fully owned by the pool, so the latter is responsible
+ * calling fDeleteTextures accordingly.
+ * Users of GetTexture receive a texture that is only valid for the duration
+ * of the current frame.
+ * This is primarily intended for direct texturing APIs that need to attach
+ * shared objects (such as an EGLImage) to a gl texture.
+ */
+class CompositorTexturePoolOGL {
+ protected:
+ virtual ~CompositorTexturePoolOGL() = default;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(CompositorTexturePoolOGL)
+
+ virtual void Clear() = 0;
+
+ virtual GLuint GetTexture(GLenum aTarget, GLenum aEnum) = 0;
+
+ virtual void EndFrame() = 0;
+};
+
+/**
+ * Agressively reuses textures. One gl texture per texture unit in total.
+ * So far this hasn't shown the best results on b2g.
+ */
+class PerUnitTexturePoolOGL : public CompositorTexturePoolOGL {
+ public:
+ explicit PerUnitTexturePoolOGL(gl::GLContext* aGL);
+ virtual ~PerUnitTexturePoolOGL();
+
+ void Clear() override { DestroyTextures(); }
+
+ GLuint GetTexture(GLenum aTarget, GLenum aUnit) override;
+
+ void EndFrame() override {}
+
+ protected:
+ void DestroyTextures();
+
+ GLenum mTextureTarget;
+ nsTArray<GLuint> mTextures;
+ RefPtr<gl::GLContext> mGL;
+};
+
+// If you want to make this class not final, first remove calls to virtual
+// methods (Destroy) that are made in the destructor.
+class CompositorOGL final : public Compositor {
+ typedef mozilla::gl::GLContext GLContext;
+
+ friend class CompositingRenderTargetOGL;
+
+ RefPtr<ShaderProgramOGLsHolder> mProgramsHolder;
+
+ public:
+ explicit CompositorOGL(widget::CompositorWidget* aWidget,
+ int aSurfaceWidth = -1, int aSurfaceHeight = -1,
+ bool aUseExternalSurfaceSize = false);
+
+ protected:
+ virtual ~CompositorOGL();
+
+ public:
+ CompositorOGL* AsCompositorOGL() override { return this; }
+
+ already_AddRefed<DataTextureSource> CreateDataTextureSource(
+ TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+ bool Initialize(GLContext* aGLContext,
+ RefPtr<ShaderProgramOGLsHolder> aProgramsHolder,
+ nsCString* const out_failureReason);
+
+ bool Initialize(nsCString* const out_failureReason) override;
+
+ void Destroy() override;
+
+ // Returns a render target for the native layer.
+ // aInvalidRegion is in window coordinates, i.e. in the same space as
+ // aNativeLayer->GetPosition().
+ already_AddRefed<CompositingRenderTargetOGL> RenderTargetForNativeLayer(
+ NativeLayer* aNativeLayer, const gfx::IntRegion& aInvalidRegion);
+
+ already_AddRefed<CompositingRenderTarget> CreateRenderTarget(
+ const gfx::IntRect& aRect, SurfaceInitMode aInit) override;
+
+ void SetRenderTarget(CompositingRenderTarget* aSurface) override;
+ already_AddRefed<CompositingRenderTarget> GetCurrentRenderTarget()
+ const override;
+ already_AddRefed<CompositingRenderTarget> GetWindowRenderTarget()
+ const override;
+
+ bool ReadbackRenderTarget(CompositingRenderTarget* aSource,
+ AsyncReadbackBuffer* aDest) override;
+
+ already_AddRefed<AsyncReadbackBuffer> CreateAsyncReadbackBuffer(
+ const gfx::IntSize& aSize) override;
+
+ bool BlitRenderTarget(CompositingRenderTarget* aSource,
+ const gfx::IntSize& aSourceSize,
+ const gfx::IntSize& aDestSize) override;
+
+ void DrawQuad(const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect) override;
+
+ void EndFrame() override;
+
+ int32_t GetMaxTextureSize() const override;
+
+ /**
+ * Set the size of the EGL surface we're rendering to, if we're rendering to
+ * an EGL surface.
+ */
+ void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override;
+
+ typedef uint32_t MakeCurrentFlags;
+ static const MakeCurrentFlags ForceMakeCurrent = 0x1;
+ void MakeCurrent(MakeCurrentFlags aFlags = 0);
+
+#ifdef MOZ_DUMP_PAINTING
+ const char* Name() const override { return "OGL"; }
+#endif // MOZ_DUMP_PAINTING
+
+ void Pause() override;
+ bool Resume() override;
+
+ GLContext* gl() const { return mGLContext; }
+ GLContext* GetGLContext() const override { return mGLContext; }
+
+ /**
+ * Clear the program state. This must be called
+ * before operating on the GLContext directly. */
+ void ResetProgram();
+
+ gfx::SurfaceFormat GetFBOFormat() const {
+ return gfx::SurfaceFormat::R8G8B8A8;
+ }
+
+ /**
+ * The compositor provides with temporary textures for use with direct
+ * textruing.
+ */
+ GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit);
+
+ const gfx::IntSize GetDestinationSurfaceSize() const {
+ return gfx::IntSize(mSurfaceSize.width, mSurfaceSize.height);
+ }
+
+ /**
+ * Allow the origin of the surface to be offset so that content does not
+ * start at (0, 0) on the surface.
+ */
+ void SetSurfaceOrigin(const ScreenIntPoint& aOrigin) {
+ mSurfaceOrigin = aOrigin;
+ }
+
+ // Register TextureSource which own device data that have to be deleted before
+ // destroying this CompositorOGL.
+ void RegisterTextureSource(TextureSource* aTextureSource);
+ void UnregisterTextureSource(TextureSource* aTextureSource);
+
+ ipc::FileDescriptor GetReleaseFence();
+
+ private:
+ template <typename Geometry>
+ void DrawGeometry(const Geometry& aGeometry, const gfx::Rect& aRect,
+ const gfx::IntRect& aClipRect,
+ const EffectChain& aEffectChain, gfx::Float aOpacity,
+ const gfx::Matrix4x4& aTransform,
+ const gfx::Rect& aVisibleRect);
+
+ void PrepareViewport(CompositingRenderTargetOGL* aRenderTarget);
+
+ void InsertFrameDoneSync();
+
+ bool NeedToRecreateFullWindowRenderTarget() const;
+
+ /** Widget associated with this compositor */
+ LayoutDeviceIntSize mWidgetSize;
+ RefPtr<GLContext> mGLContext;
+ bool mOwnsGLContext = true;
+ RefPtr<SurfacePoolHandle> mSurfacePoolHandle;
+ gfx::Matrix4x4 mProjMatrix;
+ bool mCanRenderToDefaultFramebuffer = true;
+
+ /** The size of the surface we are rendering to */
+ gfx::IntSize mSurfaceSize;
+
+ /** The origin of the content on the surface */
+ ScreenIntPoint mSurfaceOrigin;
+
+ already_AddRefed<mozilla::gl::GLContext> CreateContext();
+
+ /** Texture target to use for FBOs */
+ GLenum mFBOTextureTarget;
+
+ /** Currently bound render target */
+ RefPtr<CompositingRenderTargetOGL> mCurrentRenderTarget;
+
+ // The 1x1 dummy render target that's the "current" render target between
+ // BeginFrameForNativeLayers and EndFrame but outside pairs of
+ // Begin/EndRenderingToNativeLayer. Created on demand.
+ RefPtr<CompositingRenderTarget> mNativeLayersReferenceRT;
+
+ // The render target that profiler screenshots / frame recording read from.
+ // This will be the actual window framebuffer when rendering to a window, and
+ // it will be mFullWindowRenderTarget when rendering to native layers.
+ RefPtr<CompositingRenderTargetOGL> mWindowRenderTarget;
+
+ // Non-null when using native layers and frame recording is requested.
+ // EndNormalDrawing() maintains a copy of the entire window contents in this
+ // render target, by copying from the native layer render targets.
+ RefPtr<CompositingRenderTargetOGL> mFullWindowRenderTarget;
+
+ /**
+ * VBO that has some basics in it for a textured quad, including vertex
+ * coords and texcoords.
+ */
+ GLuint mQuadVBO;
+
+ /**
+ * VBO that stores dynamic triangle geometry.
+ */
+ GLuint mTriangleVBO;
+
+ // Used to apply back-pressure in WaitForPreviousFrameDoneSync().
+ GLsync mPreviousFrameDoneSync;
+ GLsync mThisFrameDoneSync;
+
+ bool mHasBGRA;
+
+ /**
+ * When rendering to some EGL surfaces (e.g. on Android), we rely on being
+ * told about size changes (via SetSurfaceSize) rather than pulling this
+ * information from the widget.
+ */
+ bool mUseExternalSurfaceSize;
+
+ /**
+ * Have we had DrawQuad calls since the last frame was rendered?
+ */
+ bool mFrameInProgress;
+
+ // Only true between BeginFromeForNativeLayers and EndFrame, and only if the
+ // full window render target needed to be recreated in the current frame.
+ bool mShouldInvalidateWindow = false;
+
+ /* Start a new frame.
+ */
+ Maybe<gfx::IntRect> BeginFrameForWindow(
+ const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion) override;
+
+ Maybe<gfx::IntRect> BeginFrame(const nsIntRegion& aInvalidRegion,
+ const Maybe<gfx::IntRect>& aClipRect,
+ const gfx::IntRect& aRenderBounds,
+ const nsIntRegion& aOpaqueRegion);
+
+ ShaderConfigOGL GetShaderConfigFor(Effect* aEffect,
+ bool aDEAAEnabled = false) const;
+
+ ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL& aConfig);
+
+ void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig, const gfx::Rect&) {
+ aConfig.SetDynamicGeometry(false);
+ }
+
+ void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig,
+ const nsTArray<gfx::TexturedTriangle>&) {
+ aConfig.SetDynamicGeometry(true);
+ }
+
+ /**
+ * Create a FBO backed by a texture.
+ * Note that the texture target type will be
+ * of the type returned by FBOTextureTarget; different
+ * shaders are required to sample from the different
+ * texture types.
+ */
+ void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer, GLuint* aFBO,
+ GLuint* aTexture,
+ gfx::IntSize* aAllocSize = nullptr);
+
+ GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
+ GLuint aSourceFrameBuffer,
+ gfx::IntSize* aAllocSize = nullptr);
+
+ gfx::Point3D GetLineCoefficients(const gfx::Point& aPoint1,
+ const gfx::Point& aPoint2);
+
+ void CleanupResources();
+
+ void BindAndDrawQuads(ShaderProgramOGL* aProg, int aQuads,
+ const gfx::Rect* aLayerRect,
+ const gfx::Rect* aTextureRect);
+
+ void BindAndDrawQuad(ShaderProgramOGL* aProg, const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect = gfx::Rect(0.0f, 0.0f,
+ 1.0f, 1.0f)) {
+ gfx::Rect layerRects[4];
+ gfx::Rect textureRects[4];
+ layerRects[0] = aLayerRect;
+ textureRects[0] = aTextureRect;
+ BindAndDrawQuads(aProg, 1, layerRects, textureRects);
+ }
+
+ void BindAndDrawGeometry(ShaderProgramOGL* aProgram, const gfx::Rect& aRect);
+
+ void BindAndDrawGeometry(ShaderProgramOGL* aProgram,
+ const nsTArray<gfx::TexturedTriangle>& aTriangles);
+
+ void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL* aProg,
+ const gfx::Rect& aRect,
+ const gfx::Rect& aTexCoordRect,
+ TextureSource* aTexture);
+
+ void BindAndDrawGeometryWithTextureRect(
+ ShaderProgramOGL* aProg,
+ const nsTArray<gfx::TexturedTriangle>& aTriangles,
+ const gfx::Rect& aTexCoordRect, TextureSource* aTexture);
+
+ void InitializeVAO(const GLuint aAttribIndex, const GLint aComponents,
+ const GLsizei aStride, const size_t aOffset);
+
+ gfx::Rect GetTextureCoordinates(gfx::Rect textureRect,
+ TextureSource* aTexture);
+
+ /**
+ * Copies the content of the current render target to the set transaction
+ * target.
+ */
+ void CopyToTarget(gfx::DrawTarget* aTarget, const nsIntPoint& aTopLeft,
+ const gfx::Matrix& aWorldMatrix);
+
+ /**
+ * Implements the flipping of the y-axis to convert from layers/compositor
+ * coordinates to OpenGL coordinates.
+ *
+ * Indeed, the only coordinate system that OpenGL knows has the y-axis
+ * pointing upwards, but the layers/compositor coordinate system has the
+ * y-axis pointing downwards, for good reason as Web pages are typically
+ * scrolled downwards. So, some flipping has to take place; FlippedY does it.
+ */
+ GLint FlipY(GLint y) const { return mViewportSize.height - y; }
+
+ // The DrawTarget from BeginFrameForTarget, which EndFrame needs to copy the
+ // window contents into.
+ // Only non-null between BeginFrameForTarget and EndFrame.
+ RefPtr<gfx::DrawTarget> mTarget;
+ gfx::IntRect mTargetBounds;
+
+ RefPtr<CompositorTexturePoolOGL> mTexturePool;
+
+ // The native layer that we're currently rendering to, if any.
+ // Non-null only between BeginFrame and EndFrame if BeginFrame has been called
+ // with a non-null aNativeLayer.
+ RefPtr<NativeLayer> mCurrentNativeLayer;
+
+#ifdef MOZ_WIDGET_GTK
+ // Hold TextureSources which own device data that have to be deleted before
+ // destroying this CompositorOGL.
+ std::unordered_set<TextureSource*> mRegisteredTextureSources;
+#endif
+
+ // FileDescriptor of release fence.
+ // Release fence is a fence that is used for waiting until usage/composite of
+ // AHardwareBuffer is ended. The fence is delivered to client side via
+ // ImageBridge. It is used only on android.
+ ipc::FileDescriptor mReleaseFenceFd;
+
+ bool mDestroyed;
+
+ /**
+ * Size of the OpenGL context's primary framebuffer in pixels. Used by
+ * FlipY for the y-flipping calculation and by the DEAA shader.
+ */
+ gfx::IntSize mViewportSize;
+
+ gfx::IntRegion mCurrentFrameInvalidRegion;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_COMPOSITOROGL_H */
diff --git a/gfx/layers/opengl/DMABUFTextureClientOGL.cpp b/gfx/layers/opengl/DMABUFTextureClientOGL.cpp
new file mode 100644
index 0000000000..017c9aba8c
--- /dev/null
+++ b/gfx/layers/opengl/DMABUFTextureClientOGL.cpp
@@ -0,0 +1,123 @@
+/* -*- 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 "DMABUFTextureClientOGL.h"
+#include "mozilla/widget/DMABufSurface.h"
+#include "gfxPlatform.h"
+
+namespace mozilla::layers {
+
+using namespace gfx;
+
+DMABUFTextureData::DMABUFTextureData(DMABufSurface* aSurface,
+ BackendType aBackend)
+ : mSurface(aSurface), mBackend(aBackend) {
+ MOZ_ASSERT(mSurface);
+}
+
+DMABUFTextureData::~DMABUFTextureData() = default;
+
+/* static */ DMABUFTextureData* DMABUFTextureData::Create(
+ const IntSize& aSize, SurfaceFormat aFormat, BackendType aBackend) {
+ if (aFormat != SurfaceFormat::B8G8R8A8 &&
+ aFormat != SurfaceFormat::B8G8R8X8) {
+ // TODO
+ NS_WARNING("DMABUFTextureData::Create() - wrong surface format!");
+ return nullptr;
+ }
+
+ int flags = DMABUF_TEXTURE;
+ if (aFormat == SurfaceFormat::B8G8R8A8) {
+ flags |= DMABUF_ALPHA;
+ }
+ RefPtr<DMABufSurface> surf =
+ DMABufSurfaceRGBA::CreateDMABufSurface(aSize.width, aSize.height, flags);
+ if (!surf) {
+ NS_WARNING("DMABUFTextureData::Create() failed!");
+ return nullptr;
+ }
+ return new DMABUFTextureData(surf, aBackend);
+}
+
+TextureData* DMABUFTextureData::CreateSimilar(
+ LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
+ TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
+ return DMABUFTextureData::Create(
+ gfx::IntSize(mSurface->GetWidth(), mSurface->GetHeight()),
+ mSurface->GetFormat(), mBackend);
+}
+
+bool DMABUFTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
+ return mSurface->Serialize(aOutDescriptor);
+}
+
+void DMABUFTextureData::GetSubDescriptor(
+ RemoteDecoderVideoSubDescriptor* const aOutDesc) {
+ SurfaceDescriptor desc;
+ if (!mSurface->Serialize(desc)) {
+ return;
+ }
+ *aOutDesc = static_cast<SurfaceDescriptorDMABuf>(desc);
+}
+
+void DMABUFTextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = gfx::IntSize(mSurface->GetWidth(), mSurface->GetHeight());
+ aInfo.format = mSurface->GetFormat();
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+bool DMABUFTextureData::Lock(OpenMode) {
+ auto surf = mSurface->GetAsDMABufSurfaceRGBA();
+ MOZ_ASSERT(!surf->IsMapped(), "Already locked?");
+ surf->Map();
+ return true;
+}
+
+void DMABUFTextureData::Unlock() {
+ auto surf = mSurface->GetAsDMABufSurfaceRGBA();
+ MOZ_ASSERT(surf->IsMapped(), "Already unlocked?");
+ surf->Unmap();
+}
+
+already_AddRefed<DataSourceSurface> DMABUFTextureData::GetAsSurface() {
+ // TODO: Update for debug purposes.
+ return nullptr;
+}
+
+already_AddRefed<DrawTarget> DMABUFTextureData::BorrowDrawTarget() {
+ MOZ_ASSERT(mBackend != BackendType::NONE);
+ if (mBackend == BackendType::NONE) {
+ // shouldn't happen, but degrade gracefully
+ return nullptr;
+ }
+ auto surf = mSurface->GetAsDMABufSurfaceRGBA();
+ if (!surf->GetMappedRegion()) {
+ return nullptr;
+ }
+ return Factory::CreateDrawTargetForData(
+ mBackend, (unsigned char*)surf->GetMappedRegion(),
+ IntSize(surf->GetWidth(), surf->GetHeight()),
+ surf->GetMappedRegionStride(), surf->GetFormat(), true);
+}
+
+void DMABUFTextureData::Deallocate(LayersIPCChannel*) { mSurface = nullptr; }
+
+void DMABUFTextureData::Forget(LayersIPCChannel*) { mSurface = nullptr; }
+
+bool DMABUFTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()),
+ IntPoint());
+ return true;
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/opengl/DMABUFTextureClientOGL.h b/gfx/layers/opengl/DMABUFTextureClientOGL.h
new file mode 100644
index 0000000000..df69ba8fbd
--- /dev/null
+++ b/gfx/layers/opengl/DMABUFTextureClientOGL.h
@@ -0,0 +1,69 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+
+#include "mozilla/layers/TextureClientOGL.h"
+
+class DMABufSurface;
+
+namespace mozilla {
+namespace layers {
+
+class DMABUFTextureData : public TextureData {
+ public:
+ static DMABUFTextureData* Create(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::BackendType aBackend);
+
+ static DMABUFTextureData* Create(DMABufSurface* aSurface,
+ gfx::BackendType aBackend) {
+ return new DMABUFTextureData(aSurface, aBackend);
+ }
+
+ ~DMABUFTextureData();
+
+ virtual TextureData* CreateSimilar(
+ LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+ TextureType GetTextureType() const override { return TextureType::DMABUF; }
+
+ void FillInfo(TextureData::Info& aInfo) const override;
+
+ bool Lock(OpenMode) override;
+
+ void Unlock() override;
+
+ already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ void GetSubDescriptor(
+ RemoteDecoderVideoSubDescriptor* const aOutDesc) override;
+
+ void Deallocate(LayersIPCChannel*) override;
+
+ void Forget(LayersIPCChannel*) override;
+
+ bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ // For debugging purposes only.
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+ protected:
+ DMABUFTextureData(DMABufSurface* aSurface, gfx::BackendType aBackend);
+
+ RefPtr<DMABufSurface> mSurface;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
diff --git a/gfx/layers/opengl/DMABUFTextureHostOGL.cpp b/gfx/layers/opengl/DMABUFTextureHostOGL.cpp
new file mode 100644
index 0000000000..4ef5e97372
--- /dev/null
+++ b/gfx/layers/opengl/DMABUFTextureHostOGL.cpp
@@ -0,0 +1,188 @@
+/* -*- 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 "DMABUFTextureHostOGL.h"
+#include "mozilla/widget/DMABufSurface.h"
+#include "mozilla/webrender/RenderDMABUFTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "GLContextEGL.h"
+
+namespace mozilla::layers {
+
+DMABUFTextureHostOGL::DMABUFTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptor& aDesc)
+ : TextureHost(TextureHostType::DMABUF, aFlags) {
+ MOZ_COUNT_CTOR(DMABUFTextureHostOGL);
+
+ // DMABufSurface::CreateDMABufSurface() can fail, for instance when we're run
+ // out of file descriptors.
+ mSurface =
+ DMABufSurface::CreateDMABufSurface(aDesc.get_SurfaceDescriptorDMABuf());
+}
+
+DMABUFTextureHostOGL::~DMABUFTextureHostOGL() {
+ MOZ_COUNT_DTOR(DMABUFTextureHostOGL);
+}
+
+gfx::SurfaceFormat DMABUFTextureHostOGL::GetFormat() const {
+ if (!mSurface) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mSurface->GetFormat();
+}
+
+gfx::YUVColorSpace DMABUFTextureHostOGL::GetYUVColorSpace() const {
+ if (!mSurface) {
+ return gfx::YUVColorSpace::Identity;
+ }
+ return mSurface->GetYUVColorSpace();
+}
+
+gfx::ColorRange DMABUFTextureHostOGL::GetColorRange() const {
+ if (!mSurface) {
+ return gfx::ColorRange::LIMITED;
+ }
+ return mSurface->IsFullRange() ? gfx::ColorRange::FULL
+ : gfx::ColorRange::LIMITED;
+}
+
+uint32_t DMABUFTextureHostOGL::NumSubTextures() {
+ return mSurface ? mSurface->GetTextureCount() : 0;
+}
+
+gfx::IntSize DMABUFTextureHostOGL::GetSize() const {
+ if (!mSurface) {
+ return gfx::IntSize();
+ }
+ return gfx::IntSize(mSurface->GetWidth(), mSurface->GetHeight());
+}
+
+gl::GLContext* DMABUFTextureHostOGL::gl() const { return nullptr; }
+
+void DMABUFTextureHostOGL::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+
+ if (!mSurface) {
+ return;
+ }
+ RefPtr<wr::RenderTextureHost> texture =
+ new wr::RenderDMABUFTextureHost(mSurface);
+ wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId,
+ texture.forget());
+}
+
+void DMABUFTextureHostOGL::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ if (!mSurface) {
+ return;
+ }
+
+ auto method = aOp == TextureHost::ADD_IMAGE
+ ? &wr::TransactionBuilder::AddExternalImage
+ : &wr::TransactionBuilder::UpdateExternalImage;
+ auto imageType =
+ wr::ExternalImageType::TextureHandle(wr::ImageBufferKind::Texture2D);
+
+ switch (mSurface->GetFormat()) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ case gfx::SurfaceFormat::B8G8R8A8: {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ // XXX Add RGBA handling. Temporary hack to avoid crash
+ // With BGRA format setting, rendering works without problem.
+ wr::ImageDescriptor descriptor(GetSize(), mSurface->GetFormat());
+ (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0);
+ break;
+ }
+ case gfx::SurfaceFormat::NV12: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSurface->GetTextureCount() == 2);
+ wr::ImageDescriptor descriptor0(
+ gfx::IntSize(mSurface->GetWidth(0), mSurface->GetHeight(0)),
+ gfx::SurfaceFormat::A8);
+ wr::ImageDescriptor descriptor1(
+ gfx::IntSize(mSurface->GetWidth(1), mSurface->GetHeight(1)),
+ gfx::SurfaceFormat::R8G8);
+ (aResources.*method)(aImageKeys[0], descriptor0, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], descriptor1, aExtID, imageType, 1);
+ break;
+ }
+ case gfx::SurfaceFormat::YUV: {
+ MOZ_ASSERT(aImageKeys.length() == 3);
+ MOZ_ASSERT(mSurface->GetTextureCount() == 3);
+ wr::ImageDescriptor descriptor0(
+ gfx::IntSize(mSurface->GetWidth(0), mSurface->GetHeight(0)),
+ gfx::SurfaceFormat::A8);
+ wr::ImageDescriptor descriptor1(
+ gfx::IntSize(mSurface->GetWidth(1), mSurface->GetHeight(1)),
+ gfx::SurfaceFormat::A8);
+ (aResources.*method)(aImageKeys[0], descriptor0, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], descriptor1, aExtID, imageType, 1);
+ (aResources.*method)(aImageKeys[2], descriptor1, aExtID, imageType, 2);
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ }
+}
+
+void DMABUFTextureHostOGL::PushDisplayItems(
+ wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) {
+ if (!mSurface) {
+ return;
+ }
+ bool preferCompositorSurface =
+ aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE);
+ switch (mSurface->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);
+ break;
+ }
+ case gfx::SurfaceFormat::NV12: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSurface->GetTextureCount() == 2);
+ // Those images can only be generated at present by the VAAPI H264 decoder
+ // which only supports 8 bits color depth.
+ aBuilder.PushNV12Image(aBounds, aClip, true, aImageKeys[0], aImageKeys[1],
+ wr::ColorDepth::Color8,
+ wr::ToWrYuvColorSpace(GetYUVColorSpace()),
+ wr::ToWrColorRange(GetColorRange()), aFilter,
+ preferCompositorSurface);
+ break;
+ }
+ case gfx::SurfaceFormat::YUV: {
+ MOZ_ASSERT(aImageKeys.length() == 3);
+ MOZ_ASSERT(mSurface->GetTextureCount() == 3);
+ // Those images can only be generated at present by the VAAPI vp8 decoder
+ // which only supports 8 bits color depth.
+ aBuilder.PushYCbCrPlanarImage(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1], aImageKeys[2],
+ wr::ColorDepth::Color8, wr::ToWrYuvColorSpace(GetYUVColorSpace()),
+ wr::ToWrColorRange(GetColorRange()), aFilter,
+ preferCompositorSurface);
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ }
+}
+
+} // namespace mozilla::layers
diff --git a/gfx/layers/opengl/DMABUFTextureHostOGL.h b/gfx/layers/opengl/DMABUFTextureHostOGL.h
new file mode 100644
index 0000000000..13510a3c03
--- /dev/null
+++ b/gfx/layers/opengl/DMABUFTextureHostOGL.h
@@ -0,0 +1,69 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_DMABUFTEXTUREHOSTOGL_H
+#define MOZILLA_GFX_DMABUFTEXTUREHOSTOGL_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+
+class DMABufSurface;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A TextureHost for shared class DMABufSurface;
+ */
+class DMABUFTextureHostOGL : public TextureHost {
+ public:
+ DMABUFTextureHostOGL(TextureFlags aFlags, const SurfaceDescriptor& aDesc);
+ virtual ~DMABUFTextureHostOGL();
+
+ bool IsValid() override { return !!mSurface; }
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ const char* Name() override { return "DMABUFTextureHostOGL"; }
+#endif
+ uint32_t NumSubTextures() override;
+
+ gfx::YUVColorSpace GetYUVColorSpace() const override;
+ gfx::ColorRange GetColorRange() const override;
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ protected:
+ RefPtr<GLTextureSource> mTextureSource;
+ RefPtr<DMABufSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_DMABUFTEXTUREHOSTOGL_H
diff --git a/gfx/layers/opengl/EGLImageHelpers.cpp b/gfx/layers/opengl/EGLImageHelpers.cpp
new file mode 100644
index 0000000000..a5abfe449f
--- /dev/null
+++ b/gfx/layers/opengl/EGLImageHelpers.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "EGLImageHelpers.h"
+#include "GLContext.h"
+#include "GLLibraryEGL.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gl;
+
+EGLImage EGLImageCreateFromNativeBuffer(GLContext* aGL, void* aBuffer,
+ const gfx::IntSize& aCropSize) {
+ EGLint attrs[] = {
+ LOCAL_EGL_IMAGE_PRESERVED,
+ LOCAL_EGL_TRUE,
+ LOCAL_EGL_NONE,
+ LOCAL_EGL_NONE,
+ };
+
+ EGLint cropAttrs[] = {
+ LOCAL_EGL_IMAGE_PRESERVED,
+ LOCAL_EGL_TRUE,
+ LOCAL_EGL_IMAGE_CROP_LEFT_ANDROID,
+ 0,
+ LOCAL_EGL_IMAGE_CROP_TOP_ANDROID,
+ 0,
+ LOCAL_EGL_IMAGE_CROP_RIGHT_ANDROID,
+ aCropSize.width,
+ LOCAL_EGL_IMAGE_CROP_BOTTOM_ANDROID,
+ aCropSize.height,
+ LOCAL_EGL_NONE,
+ LOCAL_EGL_NONE,
+ };
+
+ auto* egl = gl::GLLibraryEGL::Get();
+ bool hasCropRect = (aCropSize.width != 0 && aCropSize.height != 0);
+ EGLint* usedAttrs = attrs;
+ if (hasCropRect &&
+ egl->IsExtensionSupported(GLLibraryEGL::EGL_ANDROID_image_crop)) {
+ usedAttrs = cropAttrs;
+ }
+
+ return egl->fCreateImage(egl->Display(), EGL_NO_CONTEXT,
+ LOCAL_EGL_NATIVE_BUFFER_ANDROID, aBuffer, usedAttrs);
+}
+
+void EGLImageDestroy(GLContext* aGL, EGLImage aImage) {
+ auto* egl = gl::GLLibraryEGL::Get();
+ egl->fDestroyImage(egl->Display(), aImage);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/EGLImageHelpers.h b/gfx/layers/opengl/EGLImageHelpers.h
new file mode 100644
index 0000000000..59eb944c0d
--- /dev/null
+++ b/gfx/layers/opengl/EGLImageHelpers.h
@@ -0,0 +1,28 @@
+/* -*- 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/. */
+
+#ifndef EGLIMAGEHELPERS_H_
+#define EGLIMAGEHELPERS_H_
+
+#include "mozilla/gfx/Point.h"
+
+typedef void* EGLImage;
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+}
+
+namespace layers {
+
+EGLImage EGLImageCreateFromNativeBuffer(gl::GLContext* aGL, void* aBuffer,
+ const gfx::IntSize& aCropSize);
+void EGLImageDestroy(gl::GLContext* aGL, EGLImage aImage);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // EGLIMAGEHELPERS_H_
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp
new file mode 100644
index 0000000000..9a14daacc7
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp
@@ -0,0 +1,120 @@
+/* -*- 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 "MacIOSurfaceTextureClientOGL.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "MacIOSurfaceHelpers.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+MacIOSurfaceTextureData::MacIOSurfaceTextureData(MacIOSurface* aSurface,
+ BackendType aBackend)
+ : mSurface(aSurface), mBackend(aBackend) {
+ MOZ_ASSERT(mSurface);
+}
+
+MacIOSurfaceTextureData::~MacIOSurfaceTextureData() = default;
+
+// static
+MacIOSurfaceTextureData* MacIOSurfaceTextureData::Create(MacIOSurface* aSurface,
+ BackendType aBackend) {
+ MOZ_ASSERT(aSurface);
+ if (!aSurface) {
+ return nullptr;
+ }
+ return new MacIOSurfaceTextureData(aSurface, aBackend);
+}
+
+MacIOSurfaceTextureData* MacIOSurfaceTextureData::Create(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ BackendType aBackend) {
+ if (aFormat != SurfaceFormat::B8G8R8A8 &&
+ aFormat != SurfaceFormat::B8G8R8X8) {
+ return nullptr;
+ }
+
+ RefPtr<MacIOSurface> surf = MacIOSurface::CreateIOSurface(
+ aSize.width, aSize.height, aFormat == SurfaceFormat::B8G8R8A8);
+ if (!surf) {
+ return nullptr;
+ }
+
+ return Create(surf, aBackend);
+}
+
+bool MacIOSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
+ aOutDescriptor = SurfaceDescriptorMacIOSurface(mSurface->GetIOSurfaceID(),
+ !mSurface->HasAlpha(),
+ mSurface->GetYUVColorSpace());
+ return true;
+}
+
+void MacIOSurfaceTextureData::GetSubDescriptor(
+ RemoteDecoderVideoSubDescriptor* const aOutDesc) {
+ *aOutDesc = SurfaceDescriptorMacIOSurface(mSurface->GetIOSurfaceID(),
+ !mSurface->HasAlpha(),
+ mSurface->GetYUVColorSpace());
+}
+
+void MacIOSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+ aInfo.format =
+ mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+bool MacIOSurfaceTextureData::Lock(OpenMode) {
+ mSurface->Lock(false);
+ return true;
+}
+
+void MacIOSurfaceTextureData::Unlock() { mSurface->Unlock(false); }
+
+already_AddRefed<DataSourceSurface> MacIOSurfaceTextureData::GetAsSurface() {
+ RefPtr<SourceSurface> surf = CreateSourceSurfaceFromMacIOSurface(mSurface);
+ return surf->GetDataSurface();
+}
+
+already_AddRefed<DrawTarget> MacIOSurfaceTextureData::BorrowDrawTarget() {
+ MOZ_ASSERT(mBackend != BackendType::NONE);
+ if (mBackend == BackendType::NONE) {
+ // shouldn't happen, but degrade gracefully
+ return nullptr;
+ }
+ return Factory::CreateDrawTargetForData(
+ mBackend, (unsigned char*)mSurface->GetBaseAddress(),
+ IntSize(mSurface->GetWidth(), mSurface->GetHeight()),
+ mSurface->GetBytesPerRow(),
+ mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8,
+ true);
+}
+
+void MacIOSurfaceTextureData::Deallocate(LayersIPCChannel*) {
+ mSurface = nullptr;
+}
+
+void MacIOSurfaceTextureData::Forget(LayersIPCChannel*) { mSurface = nullptr; }
+
+bool MacIOSurfaceTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()),
+ IntPoint());
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
new file mode 100644
index 0000000000..b5e39d8a63
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
@@ -0,0 +1,64 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+
+#include "mozilla/layers/TextureClientOGL.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+class MacIOSurfaceTextureData : public TextureData {
+ public:
+ static MacIOSurfaceTextureData* Create(MacIOSurface* aSurface,
+ gfx::BackendType aBackend);
+
+ static MacIOSurfaceTextureData* Create(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::BackendType aBackend);
+
+ ~MacIOSurfaceTextureData();
+
+ TextureType GetTextureType() const override {
+ return TextureType::MacIOSurface;
+ }
+
+ void FillInfo(TextureData::Info& aInfo) const override;
+
+ bool Lock(OpenMode) override;
+
+ void Unlock() override;
+
+ already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ void GetSubDescriptor(
+ RemoteDecoderVideoSubDescriptor* const aOutDesc) override;
+
+ void Deallocate(LayersIPCChannel*) override;
+
+ void Forget(LayersIPCChannel*) override;
+
+ bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ // For debugging purposes only.
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+ protected:
+ MacIOSurfaceTextureData(MacIOSurface* aSurface, gfx::BackendType aBackend);
+
+ RefPtr<MacIOSurface> mSurface;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
new file mode 100644
index 0000000000..ee61045599
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -0,0 +1,242 @@
+/* -*- 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 "MacIOSurfaceTextureHostOGL.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/webrender/RenderMacIOSurfaceTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "GLContextCGL.h"
+
+namespace mozilla {
+namespace layers {
+
+MacIOSurfaceTextureHostOGL::MacIOSurfaceTextureHostOGL(
+ TextureFlags aFlags, const SurfaceDescriptorMacIOSurface& aDescriptor)
+ : TextureHost(TextureHostType::MacIOSurface, aFlags) {
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureHostOGL);
+ mSurface = MacIOSurface::LookupSurface(aDescriptor.surfaceId(),
+ !aDescriptor.isOpaque(),
+ aDescriptor.yUVColorSpace());
+ if (!mSurface) {
+ gfxCriticalNote << "Failed to look up MacIOSurface";
+ }
+}
+
+MacIOSurfaceTextureHostOGL::~MacIOSurfaceTextureHostOGL() {
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureHostOGL);
+}
+
+gfx::SurfaceFormat MacIOSurfaceTextureHostOGL::GetFormat() const {
+ if (!mSurface) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mSurface->GetFormat();
+}
+
+gfx::SurfaceFormat MacIOSurfaceTextureHostOGL::GetReadFormat() const {
+ if (!mSurface) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mSurface->GetReadFormat();
+}
+
+gfx::IntSize MacIOSurfaceTextureHostOGL::GetSize() const {
+ if (!mSurface) {
+ return gfx::IntSize();
+ }
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gl::GLContext* MacIOSurfaceTextureHostOGL::gl() const { return nullptr; }
+
+gfx::YUVColorSpace MacIOSurfaceTextureHostOGL::GetYUVColorSpace() const {
+ if (!mSurface) {
+ return gfx::YUVColorSpace::Identity;
+ }
+ return mSurface->GetYUVColorSpace();
+}
+
+gfx::ColorRange MacIOSurfaceTextureHostOGL::GetColorRange() const {
+ if (!mSurface) {
+ return gfx::ColorRange::LIMITED;
+ }
+ return mSurface->IsFullRange() ? gfx::ColorRange::FULL
+ : gfx::ColorRange::LIMITED;
+}
+
+void MacIOSurfaceTextureHostOGL::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+
+ RefPtr<wr::RenderTextureHost> texture =
+ new wr::RenderMacIOSurfaceTextureHost(GetMacIOSurface());
+
+ bool isDRM = (bool)(mFlags & TextureFlags::DRM_SOURCE);
+ texture->SetIsFromDRMSource(isDRM);
+
+ wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId,
+ texture.forget());
+}
+
+uint32_t MacIOSurfaceTextureHostOGL::NumSubTextures() {
+ if (!mSurface) {
+ return 0;
+ }
+
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ case gfx::SurfaceFormat::YUV422: {
+ return 1;
+ }
+ case gfx::SurfaceFormat::NV12:
+ case gfx::SurfaceFormat::P010: {
+ return 2;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected format");
+ return 1;
+ }
+ }
+}
+
+void MacIOSurfaceTextureHostOGL::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ MOZ_ASSERT(mSurface);
+
+ auto method = aOp == TextureHost::ADD_IMAGE
+ ? &wr::TransactionBuilder::AddExternalImage
+ : &wr::TransactionBuilder::UpdateExternalImage;
+ auto imageType =
+ wr::ExternalImageType::TextureHandle(wr::ImageBufferKind::TextureRect);
+
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8: {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
+ // The internal pixel format of MacIOSurface is always BGRX or BGRA
+ // format.
+ auto format = GetFormat() == gfx::SurfaceFormat::B8G8R8A8
+ ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+ wr::ImageDescriptor descriptor(GetSize(), format);
+ (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0);
+ break;
+ }
+ case gfx::SurfaceFormat::YUV422: {
+ // This is the special buffer format. The buffer contents could be a
+ // converted RGB interleaving data or a YCbCr interleaving data depending
+ // on the different platform setting. (e.g. It will be RGB at OpenGL 2.1
+ // and YCbCr at OpenGL 3.1)
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
+ wr::ImageDescriptor descriptor(GetSize(), gfx::SurfaceFormat::B8G8R8X8);
+ (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0);
+ break;
+ }
+ case gfx::SurfaceFormat::NV12: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 2);
+ wr::ImageDescriptor descriptor0(
+ gfx::IntSize(mSurface->GetDevicePixelWidth(0),
+ mSurface->GetDevicePixelHeight(0)),
+ gfx::SurfaceFormat::A8);
+ wr::ImageDescriptor descriptor1(
+ gfx::IntSize(mSurface->GetDevicePixelWidth(1),
+ mSurface->GetDevicePixelHeight(1)),
+ gfx::SurfaceFormat::R8G8);
+ (aResources.*method)(aImageKeys[0], descriptor0, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], descriptor1, aExtID, imageType, 1);
+ break;
+ }
+ case gfx::SurfaceFormat::P010: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 2);
+ wr::ImageDescriptor descriptor0(
+ gfx::IntSize(mSurface->GetDevicePixelWidth(0),
+ mSurface->GetDevicePixelHeight(0)),
+ gfx::SurfaceFormat::A16);
+ wr::ImageDescriptor descriptor1(
+ gfx::IntSize(mSurface->GetDevicePixelWidth(1),
+ mSurface->GetDevicePixelHeight(1)),
+ gfx::SurfaceFormat::R16G16);
+ (aResources.*method)(aImageKeys[0], descriptor0, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], descriptor1, aExtID, imageType, 1);
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ }
+}
+
+void MacIOSurfaceTextureHostOGL::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);
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8: {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
+ // We disable external compositing for RGB surfaces for now until
+ // we've tested support more thoroughly. Bug 1667917.
+ aBuilder.PushImage(aBounds, aClip, true, false, aFilter, aImageKeys[0],
+ !(mFlags & TextureFlags::NON_PREMULTIPLIED),
+ wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
+ preferCompositorSurface,
+ /* aSupportsExternalCompositing */ true);
+ break;
+ }
+ case gfx::SurfaceFormat::YUV422: {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
+ // Those images can only be generated at present by the Apple H264 decoder
+ // which only supports 8 bits color depth.
+ aBuilder.PushYCbCrInterleavedImage(
+ aBounds, aClip, true, aImageKeys[0], wr::ColorDepth::Color8,
+ wr::ToWrYuvColorSpace(GetYUVColorSpace()),
+ wr::ToWrColorRange(GetColorRange()), aFilter, preferCompositorSurface,
+ /* aSupportsExternalCompositing */ true);
+ break;
+ }
+ case gfx::SurfaceFormat::NV12: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 2);
+ aBuilder.PushNV12Image(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1],
+ wr::ColorDepth::Color8, wr::ToWrYuvColorSpace(GetYUVColorSpace()),
+ wr::ToWrColorRange(GetColorRange()), aFilter, preferCompositorSurface,
+ /* aSupportsExternalCompositing */ true);
+ break;
+ }
+ case gfx::SurfaceFormat::P010: {
+ MOZ_ASSERT(aImageKeys.length() == 2);
+ MOZ_ASSERT(mSurface->GetPlaneCount() == 2);
+ aBuilder.PushP010Image(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1],
+ wr::ColorDepth::Color10, wr::ToWrYuvColorSpace(GetYUVColorSpace()),
+ wr::ToWrColorRange(GetColorRange()), aFilter, preferCompositorSurface,
+ /* aSupportsExternalCompositing */ true);
+ break;
+ }
+ default: {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
new file mode 100644
index 0000000000..d4aaa630de
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -0,0 +1,84 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
+
+#include "MacIOSurfaceHelpers.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A TextureHost for shared MacIOSurface
+ *
+ * Most of the logic actually happens in MacIOSurfaceTextureSourceOGL.
+ */
+class MacIOSurfaceTextureHostOGL : public TextureHost {
+ public:
+ MacIOSurfaceTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor);
+ virtual ~MacIOSurfaceTextureHostOGL();
+
+ // MacIOSurfaceTextureSourceOGL doesn't own any GL texture
+ void DeallocateDeviceData() override {}
+
+ gfx::SurfaceFormat GetFormat() const override;
+ gfx::SurfaceFormat GetReadFormat() const override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ RefPtr<gfx::SourceSurface> surf =
+ CreateSourceSurfaceFromMacIOSurface(GetMacIOSurface());
+ return surf->GetDataSurface();
+ }
+
+ gl::GLContext* gl() const;
+
+ gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ const char* Name() override { return "MacIOSurfaceTextureHostOGL"; }
+#endif
+
+ MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() override {
+ return this;
+ }
+
+ MacIOSurface* GetMacIOSurface() override { return mSurface; }
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ uint32_t NumSubTextures() override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ gfx::YUVColorSpace GetYUVColorSpace() const override;
+ gfx::ColorRange GetColorRange() const override;
+
+ protected:
+ RefPtr<GLTextureSource> mTextureSource;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
diff --git a/gfx/layers/opengl/OGLShaderConfig.h b/gfx/layers/opengl/OGLShaderConfig.h
new file mode 100644
index 0000000000..f7c939aa98
--- /dev/null
+++ b/gfx/layers/opengl/OGLShaderConfig.h
@@ -0,0 +1,255 @@
+/* -*- 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/. */
+
+#ifndef GFX_OGLSHADERCONFIG_H
+#define GFX_OGLSHADERCONFIG_H
+
+#include "gfxTypes.h"
+#include "ImageTypes.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h"
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsPoint.h" // for nsIntPoint
+#include "nsTArray.h" // for nsTArray
+#include "mozilla/layers/CompositorTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+enum ShaderFeatures {
+ ENABLE_RENDER_COLOR = 0x01,
+ ENABLE_TEXTURE_RECT = 0x02,
+ ENABLE_TEXTURE_EXTERNAL = 0x04,
+ ENABLE_TEXTURE_YCBCR = 0x08,
+ ENABLE_TEXTURE_NV12 = 0x10,
+ ENABLE_TEXTURE_COMPONENT_ALPHA = 0x20,
+ ENABLE_TEXTURE_NO_ALPHA = 0x40,
+ ENABLE_TEXTURE_RB_SWAP = 0x80,
+ ENABLE_OPACITY = 0x100,
+ ENABLE_BLUR = 0x200,
+ ENABLE_COLOR_MATRIX = 0x400,
+ ENABLE_MASK = 0x800,
+ ENABLE_NO_PREMUL_ALPHA = 0x1000,
+ ENABLE_DEAA = 0x2000,
+ ENABLE_DYNAMIC_GEOMETRY = 0x4000,
+ ENABLE_MASK_TEXTURE_RECT = 0x8000,
+ ENABLE_TEXTURE_NV12_GA_SWITCH = 0x10000,
+};
+
+class KnownUniform {
+ public:
+ // this needs to be kept in sync with strings in 'AddUniforms'
+ enum KnownUniformName {
+ NotAKnownUniform = -1,
+
+ LayerTransform = 0,
+ LayerTransformInverse,
+ MaskTransform,
+ BackdropTransform,
+ LayerRects,
+ MatrixProj,
+ TextureTransform,
+ TextureRects,
+ RenderTargetOffset,
+ LayerOpacity,
+ Texture,
+ YTexture,
+ CbTexture,
+ CrTexture,
+ RenderColor,
+ TexCoordMultiplier,
+ CbCrTexCoordMultiplier,
+ SSEdges,
+ ViewportSize,
+ VisibleCenter,
+ YuvColorMatrix,
+ YuvOffsetVector,
+
+ KnownUniformCount
+ };
+
+ KnownUniform() {
+ mName = NotAKnownUniform;
+ mNameString = nullptr;
+ mLocation = -1;
+ memset(&mValue, 0, sizeof(mValue));
+ }
+
+ bool UpdateUniform(int32_t i1) {
+ if (mLocation == -1) return false;
+ if (mValue.i1 != i1) {
+ mValue.i1 = i1;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1) {
+ if (mLocation == -1) return false;
+ if (mValue.f1 != f1) {
+ mValue.f1 = f1;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1, float f2) {
+ if (mLocation == -1) return false;
+ if (mValue.f16v[0] != f1 || mValue.f16v[1] != f2) {
+ mValue.f16v[0] = f1;
+ mValue.f16v[1] = f2;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(float f1, float f2, float f3, float f4) {
+ if (mLocation == -1) return false;
+ if (mValue.f16v[0] != f1 || mValue.f16v[1] != f2 || mValue.f16v[2] != f3 ||
+ mValue.f16v[3] != f4) {
+ mValue.f16v[0] = f1;
+ mValue.f16v[1] = f2;
+ mValue.f16v[2] = f3;
+ mValue.f16v[3] = f4;
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateUniform(int cnt, const float* fp) {
+ if (mLocation == -1) return false;
+ switch (cnt) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 9:
+ case 16:
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt);
+ return true;
+ }
+ return false;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("cnt must be 1 2 3 4 9 or 16");
+ return false;
+ }
+
+ bool UpdateArrayUniform(int cnt, const float* fp) {
+ if (mLocation == -1) return false;
+ if (cnt > 16) {
+ return false;
+ }
+
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt);
+ return true;
+ }
+ return false;
+ }
+
+ bool UpdateArrayUniform(int cnt, const gfx::Point3D* points) {
+ if (mLocation == -1) return false;
+ if (cnt > 4) {
+ return false;
+ }
+
+ float fp[12];
+ float* d = fp;
+ for (int i = 0; i < cnt; i++) {
+ // Note: Do not want to make assumptions about .x, .y, .z member packing.
+ // If gfx::Point3D is updated to make this guarantee, SIMD optimizations
+ // may be possible
+ *d++ = points[i].x;
+ *d++ = points[i].y;
+ *d++ = points[i].z;
+ }
+
+ if (memcmp(mValue.f16v, fp, sizeof(float) * cnt * 3) != 0) {
+ memcpy(mValue.f16v, fp, sizeof(float) * cnt * 3);
+ return true;
+ }
+ return false;
+ }
+
+ KnownUniformName mName;
+ const char* mNameString;
+ int32_t mLocation;
+
+ union {
+ int i1;
+ float f1;
+ float f16v[16];
+ } mValue;
+};
+
+class ShaderConfigOGL {
+ public:
+ ShaderConfigOGL()
+ : mFeatures(0),
+ mMultiplier(1),
+ mCompositionOp(gfx::CompositionOp::OP_OVER) {}
+
+ void SetRenderColor(bool aEnabled);
+ void SetTextureTarget(GLenum aTarget);
+ void SetMaskTextureTarget(GLenum aTarget);
+ void SetRBSwap(bool aEnabled);
+ void SetNoAlpha(bool aEnabled);
+ void SetOpacity(bool aEnabled);
+ void SetYCbCr(bool aEnabled);
+ void SetNV12(bool aEnabled);
+ void SetComponentAlpha(bool aEnabled);
+ void SetColorMatrix(bool aEnabled);
+ void SetBlur(bool aEnabled);
+ void SetMask(bool aEnabled);
+ void SetDEAA(bool aEnabled);
+ void SetCompositionOp(gfx::CompositionOp aOp);
+ void SetNoPremultipliedAlpha();
+ void SetDynamicGeometry(bool aEnabled);
+ void SetColorMultiplier(uint32_t aMultiplier);
+
+ bool operator<(const ShaderConfigOGL& other) const {
+ return mFeatures < other.mFeatures ||
+ (mFeatures == other.mFeatures &&
+ (int)mCompositionOp < (int)other.mCompositionOp) ||
+ (mFeatures == other.mFeatures &&
+ (int)mCompositionOp == (int)other.mCompositionOp &&
+ mMultiplier < other.mMultiplier);
+ }
+
+ public:
+ void SetFeature(int aBitmask, bool aState) {
+ if (aState)
+ mFeatures |= aBitmask;
+ else
+ mFeatures &= (~aBitmask);
+ }
+
+ int mFeatures;
+ uint32_t mMultiplier;
+ gfx::CompositionOp mCompositionOp;
+};
+
+static inline ShaderConfigOGL ShaderConfigFromTargetAndFormat(
+ GLenum aTarget, gfx::SurfaceFormat aFormat) {
+ ShaderConfigOGL config;
+ config.SetTextureTarget(aTarget);
+ config.SetRBSwap(aFormat == gfx::SurfaceFormat::B8G8R8A8 ||
+ aFormat == gfx::SurfaceFormat::B8G8R8X8);
+ config.SetNoAlpha(aFormat == gfx::SurfaceFormat::B8G8R8X8 ||
+ aFormat == gfx::SurfaceFormat::R8G8B8X8 ||
+ aFormat == gfx::SurfaceFormat::R5G6B5_UINT16);
+ return config;
+}
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_OGLSHADERCONFIG_H
diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp
new file mode 100644
index 0000000000..8a570ef201
--- /dev/null
+++ b/gfx/layers/opengl/OGLShaderProgram.cpp
@@ -0,0 +1,1058 @@
+/* -*- 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 "OGLShaderProgram.h"
+
+#include <stdint.h> // for uint32_t
+
+#include <sstream> // for std::ostringstream
+
+#include "GLContext.h"
+#include "gfxEnv.h"
+#include "gfxRect.h" // for gfxRect
+#include "gfxUtils.h"
+#include "mozilla/DebugOnly.h" // for DebugOnly
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/Compositor.h" // for BlendOpIsMixBlendMode
+#include "nsAString.h"
+#include "nsString.h" // for nsAutoCString
+
+namespace mozilla {
+namespace layers {
+
+using std::endl;
+
+#define GAUSSIAN_KERNEL_HALF_WIDTH 11
+#define GAUSSIAN_KERNEL_STEP 0.2
+
+static void AddUniforms(ProgramProfileOGL& aProfile) {
+ // This needs to be kept in sync with the KnownUniformName enum
+ static const char* sKnownUniformNames[] = {"uLayerTransform",
+ "uLayerTransformInverse",
+ "uMaskTransform",
+ "uBackdropTransform",
+ "uLayerRects",
+ "uMatrixProj",
+ "uTextureTransform",
+ "uTextureRects",
+ "uRenderTargetOffset",
+ "uLayerOpacity",
+ "uTexture",
+ "uYTexture",
+ "uCbTexture",
+ "uCrTexture",
+ "uRenderColor",
+ "uTexCoordMultiplier",
+ "uCbCrTexCoordMultiplier",
+ "uSSEdges",
+ "uViewportSize",
+ "uVisibleCenter",
+ "uYuvColorMatrix",
+ "uYuvOffsetVector",
+ nullptr};
+
+ for (int i = 0; sKnownUniformNames[i] != nullptr; ++i) {
+ aProfile.mUniforms[i].mNameString = sKnownUniformNames[i];
+ aProfile.mUniforms[i].mName = (KnownUniform::KnownUniformName)i;
+ }
+}
+
+void ShaderConfigOGL::SetRenderColor(bool aEnabled) {
+ SetFeature(ENABLE_RENDER_COLOR, aEnabled);
+}
+
+void ShaderConfigOGL::SetTextureTarget(GLenum aTarget) {
+ SetFeature(ENABLE_TEXTURE_EXTERNAL | ENABLE_TEXTURE_RECT, false);
+ switch (aTarget) {
+ case LOCAL_GL_TEXTURE_EXTERNAL:
+ SetFeature(ENABLE_TEXTURE_EXTERNAL, true);
+ break;
+ case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+ SetFeature(ENABLE_TEXTURE_RECT, true);
+ break;
+ }
+}
+
+void ShaderConfigOGL::SetMaskTextureTarget(GLenum aTarget) {
+ if (aTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) {
+ SetFeature(ENABLE_MASK_TEXTURE_RECT, true);
+ } else {
+ MOZ_ASSERT(aTarget == LOCAL_GL_TEXTURE_2D);
+ SetFeature(ENABLE_MASK_TEXTURE_RECT, false);
+ }
+}
+
+void ShaderConfigOGL::SetRBSwap(bool aEnabled) {
+ SetFeature(ENABLE_TEXTURE_RB_SWAP, aEnabled);
+}
+
+void ShaderConfigOGL::SetNoAlpha(bool aEnabled) {
+ SetFeature(ENABLE_TEXTURE_NO_ALPHA, aEnabled);
+}
+
+void ShaderConfigOGL::SetOpacity(bool aEnabled) {
+ SetFeature(ENABLE_OPACITY, aEnabled);
+}
+
+void ShaderConfigOGL::SetYCbCr(bool aEnabled) {
+ SetFeature(ENABLE_TEXTURE_YCBCR, aEnabled);
+ MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_NV12));
+}
+
+void ShaderConfigOGL::SetColorMultiplier(uint32_t aMultiplier) {
+ MOZ_ASSERT(mFeatures & ENABLE_TEXTURE_YCBCR,
+ "Multiplier only supported with YCbCr!");
+ mMultiplier = aMultiplier;
+}
+
+void ShaderConfigOGL::SetNV12(bool aEnabled) {
+ SetFeature(ENABLE_TEXTURE_NV12, aEnabled);
+ MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_YCBCR));
+#ifdef MOZ_WIDGET_GTK
+ SetFeature(ENABLE_TEXTURE_NV12_GA_SWITCH, aEnabled);
+#endif
+}
+
+void ShaderConfigOGL::SetComponentAlpha(bool aEnabled) {
+ SetFeature(ENABLE_TEXTURE_COMPONENT_ALPHA, aEnabled);
+}
+
+void ShaderConfigOGL::SetColorMatrix(bool aEnabled) {
+ SetFeature(ENABLE_COLOR_MATRIX, aEnabled);
+}
+
+void ShaderConfigOGL::SetBlur(bool aEnabled) {
+ SetFeature(ENABLE_BLUR, aEnabled);
+}
+
+void ShaderConfigOGL::SetMask(bool aEnabled) {
+ SetFeature(ENABLE_MASK, aEnabled);
+}
+
+void ShaderConfigOGL::SetNoPremultipliedAlpha() {
+ SetFeature(ENABLE_NO_PREMUL_ALPHA, true);
+}
+
+void ShaderConfigOGL::SetDEAA(bool aEnabled) {
+ SetFeature(ENABLE_DEAA, aEnabled);
+}
+
+void ShaderConfigOGL::SetCompositionOp(gfx::CompositionOp aOp) {
+ mCompositionOp = aOp;
+}
+
+void ShaderConfigOGL::SetDynamicGeometry(bool aEnabled) {
+ SetFeature(ENABLE_DYNAMIC_GEOMETRY, aEnabled);
+}
+
+/* static */
+ProgramProfileOGL ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig) {
+ ProgramProfileOGL result;
+ std::ostringstream fs, vs;
+
+ AddUniforms(result);
+
+ gfx::CompositionOp blendOp = aConfig.mCompositionOp;
+
+ vs << "#ifdef GL_ES" << endl;
+ vs << "#define EDGE_PRECISION mediump" << endl;
+ vs << "#else" << endl;
+ vs << "#define EDGE_PRECISION" << endl;
+ vs << "#endif" << endl;
+ vs << "uniform mat4 uMatrixProj;" << endl;
+ vs << "uniform vec4 uLayerRects[4];" << endl;
+ vs << "uniform mat4 uLayerTransform;" << endl;
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ vs << "uniform mat4 uLayerTransformInverse;" << endl;
+ vs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
+ vs << "uniform vec2 uVisibleCenter;" << endl;
+ vs << "uniform vec2 uViewportSize;" << endl;
+ }
+ vs << "uniform vec2 uRenderTargetOffset;" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY)) {
+ vs << "attribute vec4 aCoord;" << endl;
+ } else {
+ vs << "attribute vec2 aCoord;" << endl;
+ }
+
+ result.mAttributes.AppendElement(std::pair<nsCString, GLuint>{"aCoord", 0});
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ vs << "uniform mat4 uTextureTransform;" << endl;
+ vs << "uniform vec4 uTextureRects[4];" << endl;
+ vs << "varying vec2 vTexCoord;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << "attribute vec2 aTexCoord;" << endl;
+ result.mAttributes.AppendElement(
+ std::pair<nsCString, GLuint>{"aTexCoord", 1});
+ }
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ vs << "uniform mat4 uBackdropTransform;" << endl;
+ vs << "varying vec2 vBackdropCoord;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ vs << "uniform mat4 uMaskTransform;" << endl;
+ vs << "varying vec3 vMaskCoord;" << endl;
+ }
+
+ vs << "void main() {" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vec4 finalPosition = vec4(aCoord.xy, 0.0, 1.0);" << endl;
+ } else {
+ vs << " int vertexID = int(aCoord.w);" << endl;
+ vs << " vec4 layerRect = uLayerRects[vertexID];" << endl;
+ vs << " vec4 finalPosition = vec4(aCoord.xy * layerRect.zw + "
+ "layerRect.xy, 0.0, 1.0);"
+ << endl;
+ }
+
+ vs << " finalPosition = uLayerTransform * finalPosition;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ // XXX kip - The DEAA shader could be made simpler if we switch to
+ // using dynamic vertex buffers instead of sending everything
+ // in through uniforms. This would enable passing information
+ // about how to dilate each vertex explicitly and eliminate the
+ // need to extrapolate this with the sub-pixel coverage
+ // calculation in the vertex shader.
+
+ // Calculate the screen space position of this vertex, in screen pixels
+ vs << " vec4 ssPos = finalPosition;" << endl;
+ vs << " ssPos.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
+ vs << " ssPos = uMatrixProj * ssPos;" << endl;
+ vs << " ssPos.xy = ((ssPos.xy/ssPos.w)*0.5+0.5)*uViewportSize;" << endl;
+
+ if (aConfig.mFeatures & ENABLE_MASK ||
+ !(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ vs << " vec4 coordAdjusted;" << endl;
+ vs << " coordAdjusted.xy = aCoord.xy;" << endl;
+ }
+
+ // It is necessary to dilate edges away from uVisibleCenter to ensure that
+ // fragments with less than 50% sub-pixel coverage will be shaded.
+ // This offset is applied when the sub-pixel coverage of the vertex is
+ // less than 100%. Expanding by 0.5 pixels in screen space is sufficient
+ // to include these pixels.
+ vs << " if (dot(uSSEdges[0], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[1], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[2], vec3(ssPos.xy, 1.0)) < 1.5 ||" << endl;
+ vs << " dot(uSSEdges[3], vec3(ssPos.xy, 1.0)) < 1.5) {" << endl;
+ // If the shader reaches this branch, then this vertex is on the edge of
+ // the layer's visible rect and should be dilated away from the center of
+ // the visible rect. We don't want to hit this for inner facing
+ // edges between tiles, as the pixels may be covered twice without clipping
+ // against uSSEdges. If all edges were dilated, it would result in
+ // artifacts visible within semi-transparent layers with multiple tiles.
+ vs << " vec4 visibleCenter = uLayerTransform * vec4(uVisibleCenter, "
+ "0.0, 1.0);"
+ << endl;
+ vs << " vec2 dilateDir = finalPosition.xy / finalPosition.w - "
+ "visibleCenter.xy / visibleCenter.w;"
+ << endl;
+ vs << " vec2 offset = sign(dilateDir) * 0.5;" << endl;
+ vs << " finalPosition.xy += offset * finalPosition.w;" << endl;
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ // We must adjust the texture coordinates to compensate for the dilation
+ vs << " coordAdjusted = uLayerTransformInverse * finalPosition;"
+ << endl;
+ vs << " coordAdjusted /= coordAdjusted.w;" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY)) {
+ vs << " coordAdjusted.xy -= layerRect.xy;" << endl;
+ vs << " coordAdjusted.xy /= layerRect.zw;" << endl;
+ }
+ }
+ vs << " }" << endl;
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, "
+ "1.0)).xy;"
+ << endl;
+ } else {
+ vs << " vec4 textureRect = uTextureRects[vertexID];" << endl;
+ vs << " vec2 texCoord = coordAdjusted.xy * textureRect.zw + "
+ "textureRect.xy;"
+ << endl;
+ vs << " vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;"
+ << endl;
+ }
+ }
+ } else if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ if (aConfig.mFeatures & ENABLE_DYNAMIC_GEOMETRY) {
+ vs << " vTexCoord = (uTextureTransform * vec4(aTexCoord, 0.0, 1.0)).xy;"
+ << endl;
+ } else {
+ vs << " vec4 textureRect = uTextureRects[vertexID];" << endl;
+ vs << " vec2 texCoord = aCoord.xy * textureRect.zw + textureRect.xy;"
+ << endl;
+ vs << " vTexCoord = (uTextureTransform * vec4(texCoord, 0.0, 1.0)).xy;"
+ << endl;
+ }
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ vs << " vMaskCoord.xy = (uMaskTransform * (finalPosition / "
+ "finalPosition.w)).xy;"
+ << endl;
+ // correct for perspective correct interpolation, see comment in D3D11
+ // shader
+ vs << " vMaskCoord.z = 1.0;" << endl;
+ vs << " vMaskCoord *= finalPosition.w;" << endl;
+ }
+ vs << " finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
+ vs << " finalPosition = uMatrixProj * finalPosition;" << endl;
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ // Translate from clip space (-1, 1) to (0..1), apply the backdrop
+ // transform, then invert the y-axis.
+ vs << " vBackdropCoord.x = (finalPosition.x + 1.0) / 2.0;" << endl;
+ vs << " vBackdropCoord.y = 1.0 - (finalPosition.y + 1.0) / 2.0;" << endl;
+ vs << " vBackdropCoord = (uBackdropTransform * vec4(vBackdropCoord.xy, "
+ "0.0, 1.0)).xy;"
+ << endl;
+ vs << " vBackdropCoord.y = 1.0 - vBackdropCoord.y;" << endl;
+ }
+ vs << " gl_Position = finalPosition;" << endl;
+ vs << "}" << endl;
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << "#extension GL_ARB_texture_rectangle : require" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
+ fs << "#extension GL_OES_EGL_image_external : require" << endl;
+ }
+ fs << "#ifdef GL_ES" << endl;
+ fs << "precision mediump float;" << endl;
+ fs << "#define COLOR_PRECISION lowp" << endl;
+ fs << "#define EDGE_PRECISION mediump" << endl;
+ fs << "#else" << endl;
+ fs << "#define COLOR_PRECISION" << endl;
+ fs << "#define EDGE_PRECISION" << endl;
+ fs << "#endif" << endl;
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ fs << "uniform COLOR_PRECISION vec4 uRenderColor;" << endl;
+ } else {
+ // for tiling, texcoord can be greater than the lowfp range
+ fs << "varying vec2 vTexCoord;" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << "uniform bool uBlurAlpha;" << endl;
+ fs << "uniform vec2 uBlurRadius;" << endl;
+ fs << "uniform vec2 uBlurOffset;" << endl;
+ fs << "uniform float uBlurGaussianKernel[" << GAUSSIAN_KERNEL_HALF_WIDTH
+ << "];" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
+ fs << "uniform mat4 uColorMatrix;" << endl;
+ fs << "uniform vec4 uColorMatrixVector;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_OPACITY) {
+ fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
+ }
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ fs << "varying vec2 vBackdropCoord;" << endl;
+ }
+
+ const char* sampler2D = "sampler2D";
+ const char* texture2D = "texture2D";
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << "uniform vec2 uTexCoordMultiplier;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+ aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ fs << "uniform vec2 uCbCrTexCoordMultiplier;" << endl;
+ }
+ sampler2D = "sampler2DRect";
+ texture2D = "texture2DRect";
+ }
+
+ const char* maskSampler2D = "sampler2D";
+ const char* maskTexture2D = "texture2D";
+
+ if (aConfig.mFeatures & ENABLE_MASK &&
+ aConfig.mFeatures & ENABLE_MASK_TEXTURE_RECT) {
+ fs << "uniform vec2 uMaskCoordMultiplier;" << endl;
+ maskSampler2D = "sampler2DRect";
+ maskTexture2D = "texture2DRect";
+ }
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
+ sampler2D = "samplerExternalOES";
+ }
+
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ fs << "uniform " << sampler2D << " uYTexture;" << endl;
+ fs << "uniform " << sampler2D << " uCbTexture;" << endl;
+ fs << "uniform " << sampler2D << " uCrTexture;" << endl;
+ fs << "uniform mat3 uYuvColorMatrix;" << endl;
+ fs << "uniform vec3 uYuvOffsetVector;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ fs << "uniform " << sampler2D << " uYTexture;" << endl;
+ fs << "uniform " << sampler2D << " uCbTexture;" << endl;
+ fs << "uniform mat3 uYuvColorMatrix;" << endl;
+ fs << "uniform vec3 uYuvOffsetVector;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ fs << "uniform " << sampler2D << " uBlackTexture;" << endl;
+ fs << "uniform " << sampler2D << " uWhiteTexture;" << endl;
+ fs << "uniform bool uTexturePass2;" << endl;
+ } else {
+ fs << "uniform " << sampler2D << " uTexture;" << endl;
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ // Component alpha should be flattened away inside blend containers.
+ MOZ_ASSERT(!(aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA));
+
+ fs << "uniform sampler2D uBackdropTexture;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ fs << "varying vec3 vMaskCoord;" << endl;
+ fs << "uniform " << maskSampler2D << " uMaskTexture;" << endl;
+ }
+
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ fs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
+ }
+
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ BuildMixBlender(aConfig, fs);
+ }
+
+ if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
+ fs << "vec4 sample(vec2 coord) {" << endl;
+ fs << " vec4 color;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR ||
+ aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float y = " << texture2D
+ << "(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D
+ << "(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cr = " << texture2D
+ << "(uCrTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float y = " << texture2D
+ << "(uYTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D
+ << "(uCbTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cr = " << texture2D
+ << "(uCrTexture, coord).r;" << endl;
+ }
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float y = " << texture2D
+ << "(uYTexture, coord * uTexCoordMultiplier).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D
+ << "(uCbTexture, coord * uCbCrTexCoordMultiplier).r;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_NV12_GA_SWITCH) {
+ fs << " COLOR_PRECISION float cr = " << texture2D
+ << "(uCbTexture, coord * uCbCrTexCoordMultiplier).g;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float cr = " << texture2D
+ << "(uCbTexture, coord * uCbCrTexCoordMultiplier).a;" << endl;
+ }
+ } else {
+ fs << " COLOR_PRECISION float y = " << texture2D
+ << "(uYTexture, coord).r;" << endl;
+ fs << " COLOR_PRECISION float cb = " << texture2D
+ << "(uCbTexture, coord).r;" << endl;
+ if (aConfig.mFeatures & ENABLE_TEXTURE_NV12_GA_SWITCH) {
+ fs << " COLOR_PRECISION float cr = " << texture2D
+ << "(uCbTexture, coord).g;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float cr = " << texture2D
+ << "(uCbTexture, coord).a;" << endl;
+ }
+ }
+ }
+ fs << " vec3 yuv = vec3(y, cb, cr);" << endl;
+ if (aConfig.mMultiplier != 1) {
+ fs << " yuv *= " << aConfig.mMultiplier << ".0;" << endl;
+ }
+ fs << " yuv -= uYuvOffsetVector;" << endl;
+ fs << " color.rgb = uYuvColorMatrix * yuv;" << endl;
+ fs << " color.a = 1.0;" << endl;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION vec3 onBlack = " << texture2D
+ << "(uBlackTexture, coord * uTexCoordMultiplier).rgb;" << endl;
+ fs << " COLOR_PRECISION vec3 onWhite = " << texture2D
+ << "(uWhiteTexture, coord * uTexCoordMultiplier).rgb;" << endl;
+ } else {
+ fs << " COLOR_PRECISION vec3 onBlack = " << texture2D
+ << "(uBlackTexture, coord).rgb;" << endl;
+ fs << " COLOR_PRECISION vec3 onWhite = " << texture2D
+ << "(uWhiteTexture, coord).rgb;" << endl;
+ }
+ fs << " COLOR_PRECISION vec4 alphas = (1.0 - onWhite + onBlack).rgbg;"
+ << endl;
+ fs << " if (uTexturePass2)" << endl;
+ fs << " color = vec4(onBlack, alphas.a);" << endl;
+ fs << " else" << endl;
+ fs << " color = alphas;" << endl;
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
+ fs << " color = " << texture2D
+ << "(uTexture, coord * uTexCoordMultiplier);" << endl;
+ } else {
+ fs << " color = " << texture2D << "(uTexture, coord);" << endl;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_RB_SWAP) {
+ fs << " color = color.bgra;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_TEXTURE_NO_ALPHA) {
+ fs << " color = vec4(color.rgb, 1.0);" << endl;
+ }
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << "vec4 sampleAtRadius(vec2 coord, float radius) {" << endl;
+ fs << " coord += uBlurOffset;" << endl;
+ fs << " coord += radius * uBlurRadius;" << endl;
+ fs << " if (coord.x < 0. || coord.y < 0. || coord.x > 1. || coord.y > "
+ "1.)"
+ << endl;
+ fs << " return vec4(0, 0, 0, 0);" << endl;
+ fs << " return sample(coord);" << endl;
+ fs << "}" << endl;
+ fs << "vec4 blur(vec4 color, vec2 coord) {" << endl;
+ fs << " vec4 total = color * uBlurGaussianKernel[0];" << endl;
+ fs << " for (int i = 1; i < " << GAUSSIAN_KERNEL_HALF_WIDTH << "; ++i) {"
+ << endl;
+ fs << " float r = float(i) * " << GAUSSIAN_KERNEL_STEP << ";" << endl;
+ fs << " float k = uBlurGaussianKernel[i];" << endl;
+ fs << " total += sampleAtRadius(coord, r) * k;" << endl;
+ fs << " total += sampleAtRadius(coord, -r) * k;" << endl;
+ fs << " }" << endl;
+ fs << " if (uBlurAlpha) {" << endl;
+ fs << " color *= total.a;" << endl;
+ fs << " } else {" << endl;
+ fs << " color = total;" << endl;
+ fs << " }" << endl;
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+ }
+ }
+ fs << "void main() {" << endl;
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ fs << " vec4 color = uRenderColor;" << endl;
+ } else {
+ fs << " vec4 color = sample(vTexCoord);" << endl;
+ if (aConfig.mFeatures & ENABLE_BLUR) {
+ fs << " color = blur(color, vTexCoord);" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
+ fs << " color = uColorMatrix * vec4(color.rgb / color.a, color.a) + "
+ "uColorMatrixVector;"
+ << endl;
+ fs << " color.rgb *= color.a;" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_OPACITY) {
+ fs << " color *= uLayerOpacity;" << endl;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_DEAA) {
+ // Calculate the sub-pixel coverage of the pixel and modulate its opacity
+ // by that amount to perform DEAA.
+ fs << " vec3 ssPos = vec3(gl_FragCoord.xy, 1.0);" << endl;
+ fs << " float deaaCoverage = clamp(dot(uSSEdges[0], ssPos), 0.0, 1.0);"
+ << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[1], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[2], ssPos), 0.0, 1.0);" << endl;
+ fs << " deaaCoverage *= clamp(dot(uSSEdges[3], ssPos), 0.0, 1.0);" << endl;
+ fs << " color *= deaaCoverage;" << endl;
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ fs << " vec4 backdrop = texture2D(uBackdropTexture, vBackdropCoord);"
+ << endl;
+ fs << " color = mixAndBlend(backdrop, color);" << endl;
+ }
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
+ if (aConfig.mFeatures & ENABLE_MASK_TEXTURE_RECT) {
+ fs << " COLOR_PRECISION float mask = " << maskTexture2D
+ << "(uMaskTexture, maskCoords * uMaskCoordMultiplier).r;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float mask = " << maskTexture2D
+ << "(uMaskTexture, maskCoords).r;" << endl;
+ }
+ fs << " color *= mask;" << endl;
+ } else {
+ fs << " COLOR_PRECISION float mask = 1.0;" << endl;
+ fs << " color *= mask;" << endl;
+ }
+ fs << " gl_FragColor = color;" << endl;
+ fs << "}" << endl;
+
+ result.mVertexShaderString = vs.str();
+ result.mFragmentShaderString = fs.str();
+
+ if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
+ result.mTextureCount = 0;
+ } else {
+ if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
+ result.mTextureCount = 3;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) {
+ result.mTextureCount = 2;
+ } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
+ result.mTextureCount = 2;
+ } else {
+ result.mTextureCount = 1;
+ }
+ }
+ if (aConfig.mFeatures & ENABLE_MASK) {
+ result.mTextureCount = 1;
+ }
+ if (BlendOpIsMixBlendMode(blendOp)) {
+ result.mTextureCount += 1;
+ }
+
+ return result;
+}
+
+void ProgramProfileOGL::BuildMixBlender(const ShaderConfigOGL& aConfig,
+ std::ostringstream& fs) {
+ // From the "Compositing and Blending Level 1" spec.
+ // Generate helper functions first.
+ switch (aConfig.mCompositionOp) {
+ case gfx::CompositionOp::OP_OVERLAY:
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ // Note: we substitute (2*src-1) into the screen formula below.
+ fs << "float hardlight(float dest, float src) {" << endl;
+ fs << " if (src <= 0.5) {" << endl;
+ fs << " return dest * (2.0 * src);" << endl;
+ fs << " } else {" << endl;
+ fs << " return 2.0*dest + 2.0*src - 1.0 - 2.0*dest*src;" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ fs << "float dodge(float dest, float src) {" << endl;
+ fs << " if (dest == 0.0) {" << endl;
+ fs << " return 0.0;" << endl;
+ fs << " } else if (src == 1.0) {" << endl;
+ fs << " return 1.0;" << endl;
+ fs << " } else {" << endl;
+ fs << " return min(1.0, dest / (1.0 - src));" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ fs << "float burn(float dest, float src) {" << endl;
+ fs << " if (dest == 1.0) {" << endl;
+ fs << " return 1.0;" << endl;
+ fs << " } else if (src == 0.0) {" << endl;
+ fs << " return 0.0;" << endl;
+ fs << " } else {" << endl;
+ fs << " return 1.0 - min(1.0, (1.0 - dest) / src);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ fs << "float darken(float dest) {" << endl;
+ fs << " if (dest <= 0.25) {" << endl;
+ fs << " return ((16.0 * dest - 12.0) * dest + 4.0) * dest;" << endl;
+ fs << " } else {" << endl;
+ fs << " return sqrt(dest);" << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ fs << "float softlight(float dest, float src) {" << endl;
+ fs << " if (src <= 0.5) {" << endl;
+ fs << " return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);"
+ << endl;
+ fs << " } else {" << endl;
+ fs << " return dest + (2.0 * src - 1.0) * (darken(dest) - dest);"
+ << endl;
+ fs << " }" << endl;
+ fs << "}" << endl;
+ break;
+ case gfx::CompositionOp::OP_HUE:
+ case gfx::CompositionOp::OP_SATURATION:
+ case gfx::CompositionOp::OP_COLOR:
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ fs << "float Lum(vec3 c) {" << endl;
+ fs << " return dot(vec3(0.3, 0.59, 0.11), c);" << endl;
+ fs << "}" << endl;
+ fs << "vec3 ClipColor(vec3 c) {" << endl;
+ fs << " float L = Lum(c);" << endl;
+ fs << " float n = min(min(c.r, c.g), c.b);" << endl;
+ fs << " float x = max(max(c.r, c.g), c.b);" << endl;
+ fs << " if (n < 0.0) {" << endl;
+ fs << " c = L + (((c - L) * L) / (L - n));" << endl;
+ fs << " }" << endl;
+ fs << " if (x > 1.0) {" << endl;
+ fs << " c = L + (((c - L) * (1.0 - L)) / (x - L));" << endl;
+ fs << " }" << endl;
+ fs << " return c;" << endl;
+ fs << "}" << endl;
+ fs << "vec3 SetLum(vec3 c, float L) {" << endl;
+ fs << " float d = L - Lum(c);" << endl;
+ fs << " return ClipColor(vec3(" << endl;
+ fs << " c.r + d," << endl;
+ fs << " c.g + d," << endl;
+ fs << " c.b + d));" << endl;
+ fs << "}" << endl;
+ fs << "float Sat(vec3 c) {" << endl;
+ fs << " return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);"
+ << endl;
+ fs << "}" << endl;
+
+ // To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
+ fs << "vec3 SetSatInner(vec3 c, float s) {" << endl;
+ fs << " if (c.b > c.r) {" << endl;
+ fs << " c.g = (((c.g - c.r) * s) / (c.b - c.r));" << endl;
+ fs << " c.b = s;" << endl;
+ fs << " } else {" << endl;
+ fs << " c.gb = vec2(0.0, 0.0);" << endl;
+ fs << " }" << endl;
+ fs << " return vec3(0.0, c.gb);" << endl;
+ fs << "}" << endl;
+
+ fs << "vec3 SetSat(vec3 c, float s) {" << endl;
+ fs << " if (c.r <= c.g) {" << endl;
+ fs << " if (c.g <= c.b) {" << endl;
+ fs << " c.rgb = SetSatInner(c.rgb, s);" << endl;
+ fs << " } else if (c.r <= c.b) {" << endl;
+ fs << " c.rbg = SetSatInner(c.rbg, s);" << endl;
+ fs << " } else {" << endl;
+ fs << " c.brg = SetSatInner(c.brg, s);" << endl;
+ fs << " }" << endl;
+ fs << " } else if (c.r <= c.b) {" << endl;
+ fs << " c.grb = SetSatInner(c.grb, s);" << endl;
+ fs << " } else if (c.g <= c.b) {" << endl;
+ fs << " c.gbr = SetSatInner(c.gbr, s);" << endl;
+ fs << " } else {" << endl;
+ fs << " c.bgr = SetSatInner(c.bgr, s);" << endl;
+ fs << " }" << endl;
+ fs << " return c;" << endl;
+ fs << "}" << endl;
+ break;
+ default:
+ break;
+ }
+
+ // Generate the main blending helper.
+ fs << "vec3 blend(vec3 dest, vec3 src) {" << endl;
+ switch (aConfig.mCompositionOp) {
+ case gfx::CompositionOp::OP_MULTIPLY:
+ fs << " return dest * src;" << endl;
+ break;
+ case gfx::CompositionOp::OP_SCREEN:
+ fs << " return dest + src - (dest * src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_OVERLAY:
+ fs << " return vec3(" << endl;
+ fs << " hardlight(src.r, dest.r)," << endl;
+ fs << " hardlight(src.g, dest.g)," << endl;
+ fs << " hardlight(src.b, dest.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_DARKEN:
+ fs << " return min(dest, src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_LIGHTEN:
+ fs << " return max(dest, src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ fs << " return vec3(" << endl;
+ fs << " dodge(dest.r, src.r)," << endl;
+ fs << " dodge(dest.g, src.g)," << endl;
+ fs << " dodge(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ fs << " return vec3(" << endl;
+ fs << " burn(dest.r, src.r)," << endl;
+ fs << " burn(dest.g, src.g)," << endl;
+ fs << " burn(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ fs << " return vec3(" << endl;
+ fs << " hardlight(dest.r, src.r)," << endl;
+ fs << " hardlight(dest.g, src.g)," << endl;
+ fs << " hardlight(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ fs << " return vec3(" << endl;
+ fs << " softlight(dest.r, src.r)," << endl;
+ fs << " softlight(dest.g, src.g)," << endl;
+ fs << " softlight(dest.b, src.b));" << endl;
+ break;
+ case gfx::CompositionOp::OP_DIFFERENCE:
+ fs << " return abs(dest - src);" << endl;
+ break;
+ case gfx::CompositionOp::OP_EXCLUSION:
+ fs << " return dest + src - 2.0*dest*src;" << endl;
+ break;
+ case gfx::CompositionOp::OP_HUE:
+ fs << " return SetLum(SetSat(src, Sat(dest)), Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_SATURATION:
+ fs << " return SetLum(SetSat(dest, Sat(src)), Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_COLOR:
+ fs << " return SetLum(src, Lum(dest));" << endl;
+ break;
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ fs << " return SetLum(dest, Lum(src));" << endl;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown blend mode");
+ }
+ fs << "}" << endl;
+
+ // Generate the mix-blend function the fragment shader will call.
+ fs << "vec4 mixAndBlend(vec4 backdrop, vec4 color) {" << endl;
+
+ // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
+ // Infinity into the blend function and return incorrect results.
+ fs << " if (backdrop.a == 0.0) {" << endl;
+ fs << " return color;" << endl;
+ fs << " }" << endl;
+ fs << " if (color.a == 0.0) {" << endl;
+ fs << " return vec4(0.0, 0.0, 0.0, 0.0);" << endl;
+ fs << " }" << endl;
+
+ // The spec assumes there is no premultiplied alpha. The backdrop is always
+ // premultiplied, so undo the premultiply. If the source is premultiplied we
+ // must fix that as well.
+ fs << " backdrop.rgb /= backdrop.a;" << endl;
+ if (!(aConfig.mFeatures & ENABLE_NO_PREMUL_ALPHA)) {
+ fs << " color.rgb /= color.a;" << endl;
+ }
+ fs << " vec3 blended = blend(backdrop.rgb, color.rgb);" << endl;
+ fs << " color.rgb = (1.0 - backdrop.a) * color.rgb + backdrop.a * "
+ "blended.rgb;"
+ << endl;
+ fs << " color.rgb *= color.a;" << endl;
+ fs << " return color;" << endl;
+ fs << "}" << endl;
+}
+
+ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL,
+ const ProgramProfileOGL& aProfile)
+ : mGL(aGL), mProgram(0), mProfile(aProfile), mProgramState(STATE_NEW) {}
+
+ShaderProgramOGL::~ShaderProgramOGL() {
+ if (mProgram <= 0) {
+ return;
+ }
+
+ RefPtr<GLContext> ctx = mGL->GetSharedContext();
+ if (!ctx) {
+ ctx = mGL;
+ }
+ ctx->MakeCurrent();
+ ctx->fDeleteProgram(mProgram);
+}
+
+bool ShaderProgramOGL::Initialize() {
+ NS_ASSERTION(mProgramState == STATE_NEW,
+ "Shader program has already been initialised");
+
+ std::ostringstream vs, fs;
+ for (uint32_t i = 0; i < mProfile.mDefines.Length(); ++i) {
+ vs << mProfile.mDefines[i] << endl;
+ fs << mProfile.mDefines[i] << endl;
+ }
+ vs << mProfile.mVertexShaderString << endl;
+ fs << mProfile.mFragmentShaderString << endl;
+
+ if (!CreateProgram(vs.str().c_str(), fs.str().c_str())) {
+ mProgramState = STATE_ERROR;
+ return false;
+ }
+
+ mProgramState = STATE_OK;
+
+ for (uint32_t i = 0; i < KnownUniform::KnownUniformCount; ++i) {
+ mProfile.mUniforms[i].mLocation =
+ mGL->fGetUniformLocation(mProgram, mProfile.mUniforms[i].mNameString);
+ }
+
+ return true;
+}
+
+GLint ShaderProgramOGL::CreateShader(GLenum aShaderType,
+ const char* aShaderSource) {
+ GLint success, len = 0;
+
+ GLint sh = mGL->fCreateShader(aShaderType);
+ mGL->fShaderSource(sh, 1, (const GLchar**)&aShaderSource, nullptr);
+ mGL->fCompileShader(sh);
+ mGL->fGetShaderiv(sh, LOCAL_GL_COMPILE_STATUS, &success);
+ mGL->fGetShaderiv(sh, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&len);
+ /* Even if compiling is successful, there may still be warnings. Print them
+ * in a debug build. The > 10 is to catch silly compilers that might put
+ * some whitespace in the log but otherwise leave it empty.
+ */
+ if (!success
+#ifdef DEBUG
+ || (len > 10 && gfxEnv::MOZ_DEBUG_SHADERS())
+#endif
+ ) {
+ nsAutoCString log;
+ log.SetLength(len);
+ mGL->fGetShaderInfoLog(sh, len, (GLint*)&len, (char*)log.BeginWriting());
+ log.Truncate(len);
+
+ if (!success) {
+ printf_stderr("=== SHADER COMPILATION FAILED ===\n");
+ } else {
+ printf_stderr("=== SHADER COMPILATION WARNINGS ===\n");
+ }
+
+ printf_stderr("=== Source:\n%s\n", aShaderSource);
+ printf_stderr("=== Log:\n%s\n", log.get());
+ printf_stderr("============\n");
+
+ if (!success) {
+ mGL->fDeleteShader(sh);
+ return 0;
+ }
+ }
+
+ return sh;
+}
+
+bool ShaderProgramOGL::CreateProgram(const char* aVertexShaderString,
+ const char* aFragmentShaderString) {
+ GLuint vertexShader =
+ CreateShader(LOCAL_GL_VERTEX_SHADER, aVertexShaderString);
+ GLuint fragmentShader =
+ CreateShader(LOCAL_GL_FRAGMENT_SHADER, aFragmentShaderString);
+
+ if (!vertexShader || !fragmentShader) return false;
+
+ GLint result = mGL->fCreateProgram();
+ mGL->fAttachShader(result, vertexShader);
+ mGL->fAttachShader(result, fragmentShader);
+
+ for (std::pair<nsCString, GLuint>& attribute : mProfile.mAttributes) {
+ mGL->fBindAttribLocation(result, attribute.second, attribute.first.get());
+ }
+
+ mGL->fLinkProgram(result);
+
+ GLint success, len;
+ mGL->fGetProgramiv(result, LOCAL_GL_LINK_STATUS, &success);
+ mGL->fGetProgramiv(result, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&len);
+ /* Even if linking is successful, there may still be warnings. Print them
+ * in a debug build. The > 10 is to catch silly compilers that might put
+ * some whitespace in the log but otherwise leave it empty.
+ */
+ if (!success
+#ifdef DEBUG
+ || (len > 10 && gfxEnv::MOZ_DEBUG_SHADERS())
+#endif
+ ) {
+ nsAutoCString log;
+ log.SetLength(len);
+ mGL->fGetProgramInfoLog(result, len, (GLint*)&len,
+ (char*)log.BeginWriting());
+
+ if (!success) {
+ printf_stderr("=== PROGRAM LINKING FAILED ===\n");
+ } else {
+ printf_stderr("=== PROGRAM LINKING WARNINGS ===\n");
+ }
+ printf_stderr("=== Log:\n%s\n", log.get());
+ printf_stderr("============\n");
+ }
+
+ // We can mark the shaders for deletion; they're attached to the program
+ // and will remain attached.
+ mGL->fDeleteShader(vertexShader);
+ mGL->fDeleteShader(fragmentShader);
+
+ if (!success) {
+ mGL->fDeleteProgram(result);
+ return false;
+ }
+
+ mProgram = result;
+ return true;
+}
+
+GLuint ShaderProgramOGL::GetProgram() {
+ if (mProgramState == STATE_NEW) {
+ if (!Initialize()) {
+ NS_WARNING("Shader could not be initialised");
+ }
+ }
+ MOZ_ASSERT(HasInitialized(),
+ "Attempting to get a program that's not been initialized!");
+ return mProgram;
+}
+
+void ShaderProgramOGL::SetYUVColorSpace(gfx::YUVColorSpace aYUVColorSpace) {
+ const float* yuvToRgb =
+ gfxUtils::YuvToRgbMatrix3x3ColumnMajor(aYUVColorSpace);
+ SetMatrix3fvUniform(KnownUniform::YuvColorMatrix, yuvToRgb);
+ if (aYUVColorSpace == gfx::YUVColorSpace::Identity) {
+ const float identity[] = {0.0, 0.0, 0.0};
+ SetVec3fvUniform(KnownUniform::YuvOffsetVector, identity);
+ } else {
+ const float offset[] = {0.06275, 0.50196, 0.50196};
+ SetVec3fvUniform(KnownUniform::YuvOffsetVector, offset);
+ }
+}
+
+ShaderProgramOGLsHolder::ShaderProgramOGLsHolder(gl::GLContext* aGL)
+ : mGL(aGL) {}
+
+ShaderProgramOGLsHolder::~ShaderProgramOGLsHolder() { Clear(); }
+
+ShaderProgramOGL* ShaderProgramOGLsHolder::GetShaderProgramFor(
+ const ShaderConfigOGL& aConfig) {
+ auto iter = mPrograms.find(aConfig);
+ if (iter != mPrograms.end()) {
+ return iter->second.get();
+ }
+
+ ProgramProfileOGL profile = ProgramProfileOGL::GetProfileFor(aConfig);
+ auto shader = MakeUnique<ShaderProgramOGL>(mGL, profile);
+ if (!shader->Initialize()) {
+ gfxCriticalError() << "Shader compilation failure, cfg:"
+ << " features: " << gfx::hexa(aConfig.mFeatures)
+ << " multiplier: " << aConfig.mMultiplier
+ << " op: " << aConfig.mCompositionOp;
+ return nullptr;
+ }
+
+ mPrograms.emplace(aConfig, std::move(shader));
+ return mPrograms[aConfig].get();
+}
+
+void ShaderProgramOGLsHolder::Clear() { mPrograms.clear(); }
+
+ShaderProgramOGL* ShaderProgramOGLsHolder::ActivateProgram(
+ const ShaderConfigOGL& aConfig) {
+ ShaderProgramOGL* program = GetShaderProgramFor(aConfig);
+ MOZ_DIAGNOSTIC_ASSERT(program);
+ if (!program) {
+ return nullptr;
+ }
+ if (mCurrentProgram != program) {
+ mGL->fUseProgram(program->GetProgram());
+ mCurrentProgram = program;
+ }
+ return program;
+}
+
+void ShaderProgramOGLsHolder::ResetCurrentProgram() {
+ mCurrentProgram = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h
new file mode 100644
index 0000000000..cfeb7dd23e
--- /dev/null
+++ b/gfx/layers/opengl/OGLShaderProgram.h
@@ -0,0 +1,377 @@
+/* -*- 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/. */
+
+#ifndef GFX_OGLSHADERPROGRAM_H
+#define GFX_OGLSHADERPROGRAM_H
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "GLContext.h" // for fast inlines of glUniform*
+#include "mozilla/UniquePtr.h"
+#include "OGLShaderConfig.h"
+
+namespace mozilla {
+namespace layers {
+
+#if defined(DEBUG)
+# define CHECK_CURRENT_PROGRAM 1
+# define ASSERT_THIS_PROGRAM \
+ do { \
+ GLuint currentProgram; \
+ mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram); \
+ MOZ_ASSERT(currentProgram == mProgram, \
+ "SetUniform with wrong program active!"); \
+ } while (0)
+#else
+# define ASSERT_THIS_PROGRAM \
+ do { \
+ } while (0)
+#endif
+
+/**
+ * This struct represents the shaders that make up a program and the uniform
+ * and attribute parmeters that those shaders take.
+ * It is used by ShaderProgramOGL.
+ * Use the factory method GetProfileFor to create instances.
+ */
+struct ProgramProfileOGL {
+ /**
+ * Factory method; creates an instance of this class for the given
+ * ShaderConfigOGL
+ */
+ static ProgramProfileOGL GetProfileFor(ShaderConfigOGL aConfig);
+
+ // the source code for the program's shaders
+ std::string mVertexShaderString;
+ std::string mFragmentShaderString;
+
+ // the vertex attributes
+ CopyableTArray<std::pair<nsCString, GLuint>> mAttributes;
+
+ KnownUniform mUniforms[KnownUniform::KnownUniformCount];
+ CopyableTArray<const char*> mDefines;
+ size_t mTextureCount;
+
+ ProgramProfileOGL() : mTextureCount(0) {}
+
+ private:
+ static void BuildMixBlender(const ShaderConfigOGL& aConfig,
+ std::ostringstream& fs);
+};
+
+/**
+ * Represents an OGL shader program. The details of a program are represented
+ * by a ProgramProfileOGL
+ */
+class ShaderProgramOGL {
+ public:
+ typedef mozilla::gl::GLContext GLContext;
+
+ ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile);
+
+ ~ShaderProgramOGL();
+
+ bool HasInitialized() {
+ NS_ASSERTION(mProgramState != STATE_OK || mProgram > 0,
+ "Inconsistent program state");
+ return mProgramState == STATE_OK;
+ }
+
+ GLuint GetProgram();
+
+ bool Initialize();
+
+ GLint CreateShader(GLenum aShaderType, const char* aShaderSource);
+
+ /**
+ * Creates a program and stores its id.
+ */
+ bool CreateProgram(const char* aVertexShaderString,
+ const char* aFragmentShaderString);
+
+ /**
+ * The following set of methods set a uniform argument to the shader program.
+ * Not all uniforms may be set for all programs, and such uses will throw
+ * an assertion.
+ */
+ void SetLayerTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::LayerTransform, aMatrix);
+ }
+
+ void SetLayerTransformInverse(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::LayerTransformInverse, aMatrix);
+ }
+
+ void SetDEAAEdges(const gfx::Point3D* aEdges) {
+ SetArrayUniform(KnownUniform::SSEdges, 4, aEdges);
+ }
+
+ void SetViewportSize(const gfx::IntSize& aSize) {
+ float vals[2] = {(float)aSize.width, (float)aSize.height};
+ SetUniform(KnownUniform::ViewportSize, 2, vals);
+ }
+
+ void SetVisibleCenter(const gfx::Point& aVisibleCenter) {
+ float vals[2] = {aVisibleCenter.x, aVisibleCenter.y};
+ SetUniform(KnownUniform::VisibleCenter, 2, vals);
+ }
+
+ void SetLayerRects(const gfx::Rect* aRects) {
+ float vals[16] = {
+ aRects[0].X(), aRects[0].Y(), aRects[0].Width(), aRects[0].Height(),
+ aRects[1].X(), aRects[1].Y(), aRects[1].Width(), aRects[1].Height(),
+ aRects[2].X(), aRects[2].Y(), aRects[2].Width(), aRects[2].Height(),
+ aRects[3].X(), aRects[3].Y(), aRects[3].Width(), aRects[3].Height()};
+ SetUniform(KnownUniform::LayerRects, 16, vals);
+ }
+
+ void SetProjectionMatrix(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::MatrixProj, aMatrix);
+ }
+
+ // sets this program's texture transform, if it uses one
+ void SetTextureTransform(const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(KnownUniform::TextureTransform, aMatrix);
+ }
+
+ void SetTextureRects(const gfx::Rect* aRects) {
+ float vals[16] = {
+ aRects[0].X(), aRects[0].Y(), aRects[0].Width(), aRects[0].Height(),
+ aRects[1].X(), aRects[1].Y(), aRects[1].Width(), aRects[1].Height(),
+ aRects[2].X(), aRects[2].Y(), aRects[2].Width(), aRects[2].Height(),
+ aRects[3].X(), aRects[3].Y(), aRects[3].Width(), aRects[3].Height()};
+ SetUniform(KnownUniform::TextureRects, 16, vals);
+ }
+
+ void SetRenderOffset(const nsIntPoint& aOffset) {
+ float vals[4] = {float(aOffset.x), float(aOffset.y)};
+ SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
+ }
+
+ void SetRenderOffset(float aX, float aY) {
+ float vals[2] = {aX, aY};
+ SetUniform(KnownUniform::RenderTargetOffset, 2, vals);
+ }
+
+ void SetLayerOpacity(float aOpacity) {
+ SetUniform(KnownUniform::LayerOpacity, aOpacity);
+ }
+
+ void SetTextureUnit(GLint aUnit) { SetUniform(KnownUniform::Texture, aUnit); }
+ void SetYTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::YTexture, aUnit);
+ }
+
+ void SetCbTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::CbTexture, aUnit);
+ }
+
+ void SetCrTextureUnit(GLint aUnit) {
+ SetUniform(KnownUniform::CrTexture, aUnit);
+ }
+
+ void SetYCbCrTextureUnits(GLint aYUnit, GLint aCbUnit, GLint aCrUnit) {
+ SetUniform(KnownUniform::YTexture, aYUnit);
+ SetUniform(KnownUniform::CbTexture, aCbUnit);
+ SetUniform(KnownUniform::CrTexture, aCrUnit);
+ }
+
+ void SetNV12TextureUnits(GLint aYUnit, GLint aCbCrUnit) {
+ SetUniform(KnownUniform::YTexture, aYUnit);
+ SetUniform(KnownUniform::CbTexture, aCbCrUnit);
+ }
+
+ void SetTexCoordMultiplier(float aWidth, float aHeight) {
+ float f[] = {aWidth, aHeight};
+ SetUniform(KnownUniform::TexCoordMultiplier, 2, f);
+ }
+
+ void SetCbCrTexCoordMultiplier(float aWidth, float aHeight) {
+ float f[] = {aWidth, aHeight};
+ SetUniform(KnownUniform::CbCrTexCoordMultiplier, 2, f);
+ }
+
+ void SetYUVColorSpace(gfx::YUVColorSpace aYUVColorSpace);
+
+ size_t GetTextureCount() const { return mProfile.mTextureCount; }
+
+ protected:
+ RefPtr<GLContext> mGL;
+ // the OpenGL id of the program
+ GLuint mProgram;
+ ProgramProfileOGL mProfile;
+ enum { STATE_NEW, STATE_OK, STATE_ERROR } mProgramState;
+
+#ifdef CHECK_CURRENT_PROGRAM
+ static int sCurrentProgramKey;
+#endif
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform,
+ float aFloatValue) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aFloatValue)) {
+ mGL->fUniform1f(ku.mLocation, aFloatValue);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform,
+ const gfx::DeviceColor& aColor) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) {
+ mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength,
+ const float* aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aLength, aFloatValues)) {
+ switch (aLength) {
+ case 1:
+ mGL->fUniform1fv(ku.mLocation, 1, ku.mValue.f16v);
+ break;
+ case 2:
+ mGL->fUniform2fv(ku.mLocation, 1, ku.mValue.f16v);
+ break;
+ case 3:
+ mGL->fUniform3fv(ku.mLocation, 1, ku.mValue.f16v);
+ break;
+ case 4:
+ mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v);
+ break;
+ case 16:
+ mGL->fUniform4fv(ku.mLocation, 4, ku.mValue.f16v);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Bogus aLength param");
+ }
+ }
+ }
+
+ void SetArrayUniform(KnownUniform::KnownUniformName aKnownUniform,
+ int aLength, float* aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateArrayUniform(aLength, aFloatValues)) {
+ mGL->fUniform1fv(ku.mLocation, aLength, ku.mValue.f16v);
+ }
+ }
+
+ void SetArrayUniform(KnownUniform::KnownUniformName aKnownUniform,
+ int aLength, const gfx::Point3D* aPointValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateArrayUniform(aLength, aPointValues)) {
+ mGL->fUniform3fv(ku.mLocation, aLength, ku.mValue.f16v);
+ }
+ }
+
+ void SetUniform(KnownUniform::KnownUniformName aKnownUniform,
+ GLint aIntValue) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(aIntValue)) {
+ mGL->fUniform1i(ku.mLocation, aIntValue);
+ }
+ }
+
+ void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform,
+ const float* aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(16, aFloatValues)) {
+ mGL->fUniformMatrix4fv(ku.mLocation, 1, false, ku.mValue.f16v);
+ }
+ }
+
+ void SetMatrix3fvUniform(KnownUniform::KnownUniformName aKnownUniform,
+ const float* aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(9, aFloatValues)) {
+ mGL->fUniformMatrix3fv(ku.mLocation, 1, false, ku.mValue.f16v);
+ }
+ }
+
+ void SetVec3fvUniform(KnownUniform::KnownUniformName aKnownUniform,
+ const float* aFloatValues) {
+ ASSERT_THIS_PROGRAM;
+ NS_ASSERTION(
+ aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount,
+ "Invalid known uniform");
+
+ KnownUniform& ku(mProfile.mUniforms[aKnownUniform]);
+ if (ku.UpdateUniform(3, aFloatValues)) {
+ mGL->fUniform3fv(ku.mLocation, 1, ku.mValue.f16v);
+ }
+ }
+
+ void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform,
+ const gfx::Matrix4x4& aMatrix) {
+ SetMatrixUniform(aKnownUniform, &aMatrix._11);
+ }
+};
+
+class ShaderProgramOGLsHolder final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(ShaderProgramOGLsHolder)
+
+ explicit ShaderProgramOGLsHolder(gl::GLContext* aGL);
+
+ ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL& aConfig);
+ void Clear();
+ ShaderProgramOGL* ActivateProgram(const ShaderConfigOGL& aConfig);
+ void ResetCurrentProgram();
+
+ protected:
+ ~ShaderProgramOGLsHolder();
+
+ const RefPtr<gl::GLContext> mGL;
+ std::map<ShaderConfigOGL, UniquePtr<ShaderProgramOGL>> mPrograms;
+ ShaderProgramOGL* mCurrentProgram = nullptr;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_OGLSHADERPROGRAM_H
diff --git a/gfx/layers/opengl/TextureClientOGL.cpp b/gfx/layers/opengl/TextureClientOGL.cpp
new file mode 100644
index 0000000000..6035f0d2bf
--- /dev/null
+++ b/gfx/layers/opengl/TextureClientOGL.cpp
@@ -0,0 +1,331 @@
+/* -*- 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 "GLContext.h" // for GLContext, etc
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/gfx/2D.h" // for Factory
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "GLLibraryEGL.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+# include <jni.h>
+# include <android/native_window.h>
+# include <android/native_window_jni.h>
+# include <sys/socket.h>
+# include "mozilla/ipc/FileDescriptor.h"
+# include "mozilla/java/GeckoSurfaceWrappers.h"
+# include "mozilla/java/SurfaceAllocatorWrappers.h"
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+# include "mozilla/UniquePtrExtensions.h"
+#endif
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+class CompositableForwarder;
+
+////////////////////////////////////////////////////////////////////////
+// AndroidSurface
+
+#ifdef MOZ_WIDGET_ANDROID
+
+already_AddRefed<TextureClient> AndroidSurfaceTextureData::CreateTextureClient(
+ AndroidSurfaceTextureHandle aHandle, gfx::IntSize aSize, bool aContinuous,
+ gl::OriginPos aOriginPos, bool aHasAlpha, bool aForceBT709ColorSpace,
+ Maybe<gfx::Matrix4x4> aTransformOverride, LayersIPCChannel* aAllocator,
+ TextureFlags aFlags) {
+ if (aOriginPos == gl::OriginPos::BottomLeft) {
+ aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ return TextureClient::CreateWithData(
+ new AndroidSurfaceTextureData(aHandle, aSize, aContinuous, aHasAlpha,
+ aForceBT709ColorSpace, aTransformOverride),
+ aFlags, aAllocator);
+}
+
+AndroidSurfaceTextureData::AndroidSurfaceTextureData(
+ AndroidSurfaceTextureHandle aHandle, gfx::IntSize aSize, bool aContinuous,
+ bool aHasAlpha, bool aForceBT709ColorSpace,
+ Maybe<gfx::Matrix4x4> aTransformOverride)
+ : mHandle(aHandle),
+ mSize(aSize),
+ mContinuous(aContinuous),
+ mHasAlpha(aHasAlpha),
+ mForceBT709ColorSpace(aForceBT709ColorSpace),
+ mTransformOverride(aTransformOverride) {
+ MOZ_ASSERT(mHandle);
+}
+
+AndroidSurfaceTextureData::~AndroidSurfaceTextureData() {}
+
+void AndroidSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool AndroidSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
+ aOutDescriptor = SurfaceTextureDescriptor(
+ mHandle, mSize,
+ mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8,
+ mContinuous, mForceBT709ColorSpace, mTransformOverride);
+ return true;
+}
+
+#endif // MOZ_WIDGET_ANDROID
+
+////////////////////////////////////////////////////////////////////////
+// AndroidNativeWindow
+
+#ifdef MOZ_WIDGET_ANDROID
+
+AndroidNativeWindowTextureData* AndroidNativeWindowTextureData::Create(
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
+ if (aFormat != gfx::SurfaceFormat::R8G8B8A8 &&
+ aFormat != gfx::SurfaceFormat::R8G8B8X8 &&
+ aFormat != gfx::SurfaceFormat::B8G8R8A8 &&
+ aFormat != gfx::SurfaceFormat::B8G8R8X8 &&
+ aFormat != gfx::SurfaceFormat::R5G6B5_UINT16) {
+ return nullptr;
+ }
+
+ auto surface =
+ java::GeckoSurface::LocalRef(java::SurfaceAllocator::AcquireSurface(
+ aSize.width, aSize.height, true /* single-buffer mode */));
+ if (surface) {
+ return new AndroidNativeWindowTextureData(surface, aSize, aFormat);
+ }
+
+ return nullptr;
+}
+
+AndroidNativeWindowTextureData::AndroidNativeWindowTextureData(
+ java::GeckoSurface::Param aSurface, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat)
+ : mSurface(aSurface), mIsLocked(false), mSize(aSize), mFormat(aFormat) {
+ mNativeWindow = ANativeWindow_fromSurface(jni::GetEnvForThread(),
+ mSurface->GetSurface().Get());
+ MOZ_ASSERT(mNativeWindow, "Failed to create NativeWindow.");
+
+ // SurfaceTextures don't technically support BGR, but we can just pretend to
+ // be RGB.
+ int32_t format = WINDOW_FORMAT_RGBA_8888;
+ switch (aFormat) {
+ case gfx::SurfaceFormat::R8G8B8A8:
+ case gfx::SurfaceFormat::B8G8R8A8:
+ format = WINDOW_FORMAT_RGBA_8888;
+ break;
+
+ case gfx::SurfaceFormat::R8G8B8X8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ format = WINDOW_FORMAT_RGBX_8888;
+ break;
+
+ case gfx::SurfaceFormat::R5G6B5_UINT16:
+ format = WINDOW_FORMAT_RGB_565;
+ break;
+
+ default:
+ MOZ_ASSERT(false, "Unsupported AndroidNativeWindowTextureData format.");
+ }
+
+ DebugOnly<int32_t> r = ANativeWindow_setBuffersGeometry(
+ mNativeWindow, mSize.width, mSize.height, format);
+ MOZ_ASSERT(r == 0, "ANativeWindow_setBuffersGeometry failed.");
+
+ // Ideally here we'd call ANativeWindow_setBuffersTransform() with the
+ // identity transform, but that is only available on api level >= 26.
+ // Instead use SurfaceDescriptor's transformOverride flag when serializing.
+}
+
+void AndroidNativeWindowTextureData::FillInfo(TextureData::Info& aInfo) const {
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+ aInfo.canConcurrentlyReadLock = false;
+}
+
+bool AndroidNativeWindowTextureData::Serialize(
+ SurfaceDescriptor& aOutDescriptor) {
+ aOutDescriptor = SurfaceTextureDescriptor(
+ mSurface->GetHandle(), mSize, mFormat, false /* not continuous */,
+ false /* do not override colorspace */,
+ Some(gfx::Matrix4x4()) /* always use identity transform */);
+ return true;
+}
+
+bool AndroidNativeWindowTextureData::Lock(OpenMode) {
+ // ANativeWindows can only be locked and unlocked a single time, after which
+ // we must wait until they receive ownership back from the host.
+ // Therefore we must only actually call ANativeWindow_lock() once per cycle.
+ if (!mIsLocked) {
+ int32_t r = ANativeWindow_lock(mNativeWindow, &mBuffer, nullptr);
+ if (r == -ENOMEM) {
+ return false;
+ } else if (r < 0) {
+ MOZ_CRASH("ANativeWindow_lock failed.");
+ }
+ mIsLocked = true;
+ }
+ return true;
+}
+
+void AndroidNativeWindowTextureData::Unlock() {
+ // The TextureClient may want to call Lock again before handing ownership
+ // to the host, so we cannot call ANativeWindow_unlockAndPost yet.
+}
+
+void AndroidNativeWindowTextureData::Forget(LayersIPCChannel*) {
+ MOZ_ASSERT(!mIsLocked,
+ "ANativeWindow should not be released while locked.\n");
+ ANativeWindow_release(mNativeWindow);
+ mNativeWindow = nullptr;
+ java::SurfaceAllocator::DisposeSurface(mSurface);
+ mSurface = nullptr;
+}
+
+already_AddRefed<gfx::DrawTarget>
+AndroidNativeWindowTextureData::BorrowDrawTarget() {
+ const int bpp = (mFormat == gfx::SurfaceFormat::R5G6B5_UINT16) ? 2 : 4;
+
+ return gfx::Factory::CreateDrawTargetForData(
+ gfx::BackendType::SKIA, static_cast<unsigned char*>(mBuffer.bits),
+ gfx::IntSize(mBuffer.width, mBuffer.height), mBuffer.stride * bpp,
+ mFormat, true);
+}
+
+void AndroidNativeWindowTextureData::OnForwardedToHost() {
+ if (mIsLocked) {
+ int32_t r = ANativeWindow_unlockAndPost(mNativeWindow);
+ if (r < 0) {
+ MOZ_CRASH("ANativeWindow_unlockAndPost failed\n.");
+ }
+ mIsLocked = false;
+ }
+}
+
+AndroidHardwareBufferTextureData* AndroidHardwareBufferTextureData::Create(
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
+ RefPtr<AndroidHardwareBuffer> buffer =
+ AndroidHardwareBuffer::Create(aSize, aFormat);
+ if (!buffer) {
+ return nullptr;
+ }
+ return new AndroidHardwareBufferTextureData(buffer, aSize, aFormat);
+}
+
+AndroidHardwareBufferTextureData::AndroidHardwareBufferTextureData(
+ AndroidHardwareBuffer* aAndroidHardwareBuffer, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat)
+ : mAndroidHardwareBuffer(aAndroidHardwareBuffer),
+ mSize(aSize),
+ mFormat(aFormat),
+ mAddress(nullptr),
+ mIsLocked(false) {}
+
+AndroidHardwareBufferTextureData::~AndroidHardwareBufferTextureData() {}
+
+void AndroidHardwareBufferTextureData::FillInfo(
+ TextureData::Info& aInfo) const {
+ aInfo.size = mSize;
+ aInfo.format = mFormat;
+ aInfo.hasSynchronization = true;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+ aInfo.canConcurrentlyReadLock = true;
+}
+
+bool AndroidHardwareBufferTextureData::Serialize(
+ SurfaceDescriptor& aOutDescriptor) {
+ aOutDescriptor = SurfaceDescriptorAndroidHardwareBuffer(
+ mAndroidHardwareBuffer->mId, mSize, mFormat);
+ return true;
+}
+
+bool AndroidHardwareBufferTextureData::Lock(OpenMode aMode) {
+ if (!mIsLocked) {
+ MOZ_ASSERT(!mAddress);
+
+ uint64_t usage = 0;
+ if (aMode & OpenMode::OPEN_READ) {
+ usage |= AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
+ }
+ if (aMode & OpenMode::OPEN_WRITE) {
+ usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+ }
+
+ int ret = mAndroidHardwareBuffer->Lock(usage, 0, &mAddress);
+ if (ret) {
+ mAddress = nullptr;
+ return false;
+ }
+ mIsLocked = true;
+ }
+ return true;
+}
+
+void AndroidHardwareBufferTextureData::Unlock() {
+ // The TextureClient may want to call Lock again before handing ownership
+ // to the host, so we cannot call AHardwareBuffer_unlock yet.
+}
+
+void AndroidHardwareBufferTextureData::Forget(LayersIPCChannel*) {
+ MOZ_ASSERT(!mIsLocked);
+ mAndroidHardwareBuffer = nullptr;
+ mAddress = nullptr;
+}
+
+already_AddRefed<gfx::DrawTarget>
+AndroidHardwareBufferTextureData::BorrowDrawTarget() {
+ MOZ_ASSERT(mIsLocked);
+
+ const int bpp = (mFormat == gfx::SurfaceFormat::R5G6B5_UINT16) ? 2 : 4;
+
+ return gfx::Factory::CreateDrawTargetForData(
+ gfx::BackendType::SKIA, static_cast<unsigned char*>(mAddress),
+ gfx::IntSize(mAndroidHardwareBuffer->mSize.width,
+ mAndroidHardwareBuffer->mSize.height),
+ mAndroidHardwareBuffer->mStride * bpp, mFormat, true);
+}
+
+void AndroidHardwareBufferTextureData::OnForwardedToHost() {
+ if (mIsLocked) {
+ mAndroidHardwareBuffer->Unlock();
+ mAddress = nullptr;
+ mIsLocked = false;
+ }
+}
+
+TextureFlags AndroidHardwareBufferTextureData::GetTextureFlags() const {
+ return TextureFlags::WAIT_HOST_USAGE_END;
+}
+
+Maybe<uint64_t> AndroidHardwareBufferTextureData::GetBufferId() const {
+ return Some(mAndroidHardwareBuffer->mId);
+}
+
+mozilla::ipc::FileDescriptor
+AndroidHardwareBufferTextureData::GetAcquireFence() {
+ if (!mAndroidHardwareBuffer) {
+ return ipc::FileDescriptor();
+ }
+
+ return mAndroidHardwareBuffer->GetAcquireFence();
+}
+
+#endif // MOZ_WIDGET_ANDROID
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/TextureClientOGL.h b/gfx/layers/opengl/TextureClientOGL.h
new file mode 100644
index 0000000000..cb902ab5d2
--- /dev/null
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -0,0 +1,169 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENTOGL_H
+#define MOZILLA_GFX_TEXTURECLIENTOGL_H
+
+#include "GLContextTypes.h" // for SharedTextureHandle, etc
+#include "GLImages.h"
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidSurfaceTexture.h"
+# include "AndroidNativeWindow.h"
+# include "mozilla/java/GeckoSurfaceWrappers.h"
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+#endif
+
+namespace mozilla {
+
+namespace gfx {
+
+class DrawTarget;
+
+} // namespace gfx
+
+namespace layers {
+
+#ifdef MOZ_WIDGET_ANDROID
+
+class AndroidHardwareBuffer;
+
+class AndroidSurfaceTextureData : public TextureData {
+ public:
+ static already_AddRefed<TextureClient> CreateTextureClient(
+ AndroidSurfaceTextureHandle aHandle, gfx::IntSize aSize, bool aContinuous,
+ gl::OriginPos aOriginPos, bool aHasAlpha, bool aForceBT709ColorSpace,
+ Maybe<gfx::Matrix4x4> aTransformOverride, LayersIPCChannel* aAllocator,
+ TextureFlags aFlags);
+
+ virtual ~AndroidSurfaceTextureData();
+
+ void FillInfo(TextureData::Info& aInfo) const override;
+
+ bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ // Useless functions.
+ bool Lock(OpenMode) override { return true; }
+
+ void Unlock() override {}
+
+ // Our data is always owned externally.
+ void Deallocate(LayersIPCChannel*) override {}
+
+ protected:
+ AndroidSurfaceTextureData(AndroidSurfaceTextureHandle aHandle,
+ gfx::IntSize aSize, bool aContinuous,
+ bool aHasAlpha, bool aForceBT709ColorSpace,
+ Maybe<gfx::Matrix4x4> aTransformOverride);
+
+ const AndroidSurfaceTextureHandle mHandle;
+ const gfx::IntSize mSize;
+ const bool mContinuous;
+ const bool mHasAlpha;
+ const bool mForceBT709ColorSpace;
+ const Maybe<gfx::Matrix4x4> mTransformOverride;
+};
+
+class AndroidNativeWindowTextureData : public TextureData {
+ public:
+ static AndroidNativeWindowTextureData* Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat);
+
+ TextureType GetTextureType() const override {
+ return TextureType::AndroidNativeWindow;
+ }
+
+ void FillInfo(TextureData::Info& aInfo) const override;
+
+ bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ bool Lock(OpenMode) override;
+ void Unlock() override;
+
+ void Forget(LayersIPCChannel*) override;
+ void Deallocate(LayersIPCChannel*) override {}
+
+ already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ void OnForwardedToHost() override;
+
+ protected:
+ AndroidNativeWindowTextureData(java::GeckoSurface::Param aSurface,
+ gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat);
+
+ private:
+ java::GeckoSurface::GlobalRef mSurface;
+ ANativeWindow* mNativeWindow;
+ ANativeWindow_Buffer mBuffer;
+ // Keeps track of whether the underlying NativeWindow is actually locked.
+ bool mIsLocked;
+
+ const gfx::IntSize mSize;
+ const gfx::SurfaceFormat mFormat;
+};
+
+class AndroidHardwareBufferTextureData : public TextureData {
+ public:
+ static AndroidHardwareBufferTextureData* Create(gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat);
+
+ virtual ~AndroidHardwareBufferTextureData();
+
+ void FillInfo(TextureData::Info& aInfo) const override;
+
+ bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ bool Lock(OpenMode aMode) override;
+ void Unlock() override;
+
+ void Forget(LayersIPCChannel*) override;
+ void Deallocate(LayersIPCChannel*) override {}
+
+ already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ void OnForwardedToHost() override;
+
+ TextureFlags GetTextureFlags() const override;
+
+ Maybe<uint64_t> GetBufferId() const override;
+
+ mozilla::ipc::FileDescriptor GetAcquireFence() override;
+
+ AndroidHardwareBufferTextureData* AsAndroidHardwareBufferTextureData()
+ override {
+ return this;
+ }
+
+ AndroidHardwareBuffer* GetBuffer() { return mAndroidHardwareBuffer; }
+
+ protected:
+ AndroidHardwareBufferTextureData(
+ AndroidHardwareBuffer* aAndroidHardwareBuffer, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat);
+
+ RefPtr<AndroidHardwareBuffer> mAndroidHardwareBuffer;
+ const gfx::IntSize mSize;
+ const gfx::SurfaceFormat mFormat;
+
+ void* mAddress;
+
+ // Keeps track of whether the underlying NativeWindow is actually locked.
+ bool mIsLocked;
+};
+
+#endif // MOZ_WIDGET_ANDROID
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/opengl/TextureHostOGL.cpp b/gfx/layers/opengl/TextureHostOGL.cpp
new file mode 100644
index 0000000000..79d5d746bb
--- /dev/null
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -0,0 +1,1064 @@
+/* -*- 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_WIDGET_GTK
+# 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.forceBT709ColorSpace(), 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_WIDGET_GTK
+ case SurfaceDescriptor::TSurfaceDescriptorDMABuf: {
+ result = new DMABUFTextureHostOGL(aFlags, aDesc);
+ if (!result->IsValid()) {
+ gfxCriticalError() << "DMABuf surface import failed!";
+ result = nullptr;
+ }
+ 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,
+ bool aForceBT709ColorSpace, Maybe<Matrix4x4> aTransformOverride)
+ : TextureHost(TextureHostType::AndroidSurfaceTexture, aFlags),
+ mSurfTex(aSurfTex),
+ mSize(aSize),
+ mFormat(aFormat),
+ mContinuousUpdate(aContinuousUpdate),
+ mForceBT709ColorSpace(aForceBT709ColorSpace),
+ 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) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+
+ 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 = wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureExternal);
+ if (policy == TextureHost::NativeTexturePolicy::REQUIRE) {
+ imageType =
+ wr::ExternalImageType::TextureHandle(wr::ImageBufferKind::TextureRect);
+ } else if (mForceBT709ColorSpace) {
+ imageType = wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureExternalBT709);
+ }
+
+ 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) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+
+ 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) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+
+ 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
diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h
new file mode 100644
index 0000000000..8f4ca18768
--- /dev/null
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -0,0 +1,653 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTUREOGL_H
+#define MOZILLA_GFX_TEXTUREOGL_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint64_t
+#include "CompositableHost.h"
+#include "GLContextTypes.h" // for GLContext
+#include "GLDefs.h" // for GLenum, LOCAL_GL_CLAMP_TO_EDGE, etc
+#include "GLTextureImage.h" // for TextureImage
+#include "gfxTypes.h"
+#include "mozilla/GfxMessageUtils.h" // for gfxContentType
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/gfx/Point.h" // for IntSize, IntPoint
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureHost.h" // for TextureHost, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/webrender/RenderThread.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING
+#include "nsISupportsImpl.h" // for TextureImage::Release, etc
+#include "nsRegionFwd.h" // for nsIntRegion
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidSurfaceTexture.h"
+# include "mozilla/java/GeckoSurfaceTextureWrappers.h"
+#endif
+
+namespace mozilla {
+namespace gfx {
+class DataSourceSurface;
+} // namespace gfx
+
+namespace layers {
+
+class Compositor;
+class CompositorOGL;
+class AndroidHardwareBuffer;
+class SurfaceDescriptorAndroidHardwareBuffer;
+class TextureImageTextureSourceOGL;
+class GLTextureSource;
+
+void ApplySamplingFilterToBoundTexture(gl::GLContext* aGL,
+ gfx::SamplingFilter aSamplingFilter,
+ GLuint aTarget = LOCAL_GL_TEXTURE_2D);
+
+already_AddRefed<TextureHost> CreateTextureHostOGL(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags);
+
+/*
+ * TextureHost implementations for the OpenGL backend.
+ *
+ * Note that it is important to be careful about the ownership model with
+ * the OpenGL backend, due to some widget limitation on Linux: before
+ * the nsBaseWidget associated with our OpenGL context has been completely
+ * deleted, every resource belonging to the OpenGL context MUST have been
+ * released. At the moment the teardown sequence happens in the middle of
+ * the nsBaseWidget's destructor, meaning that at a given moment we must be
+ * able to easily find and release all the GL resources.
+ * The point is: be careful about the ownership model and limit the number
+ * of objects sharing references to GL resources to make the tear down
+ * sequence as simple as possible.
+ */
+
+/**
+ * TextureSourceOGL provides the necessary API for CompositorOGL to composite
+ * a TextureSource.
+ */
+class TextureSourceOGL {
+ public:
+ TextureSourceOGL()
+ : mCachedSamplingFilter(gfx::SamplingFilter::GOOD),
+ mHasCachedSamplingFilter(false) {}
+
+ virtual bool IsValid() const = 0;
+
+ virtual void BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter) = 0;
+
+ // To be overridden in textures that need this. This method will be called
+ // when the compositor has used the texture to draw. This allows us to set
+ // a fence with glFenceSync which we can wait on later to ensure the GPU
+ // is done with the draw calls using that texture. We would like to be able
+ // to simply use glFinishObjectAPPLE, but this returns earlier than
+ // expected with nvidia drivers.
+ virtual void MaybeFenceTexture() {}
+
+ virtual gfx::IntSize GetSize() const = 0;
+
+ virtual GLenum GetTextureTarget() const { return LOCAL_GL_TEXTURE_2D; }
+
+ virtual gfx::SurfaceFormat GetFormat() const = 0;
+
+ virtual GLenum GetWrapMode() const = 0;
+
+ virtual gfx::Matrix4x4 GetTextureTransform() { return gfx::Matrix4x4(); }
+
+ virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() {
+ return nullptr;
+ }
+
+ virtual GLTextureSource* AsGLTextureSource() { return nullptr; }
+
+ void SetSamplingFilter(gl::GLContext* aGL,
+ gfx::SamplingFilter aSamplingFilter) {
+ if (mHasCachedSamplingFilter && mCachedSamplingFilter == aSamplingFilter) {
+ return;
+ }
+ mHasCachedSamplingFilter = true;
+ mCachedSamplingFilter = aSamplingFilter;
+ ApplySamplingFilterToBoundTexture(aGL, aSamplingFilter, GetTextureTarget());
+ }
+
+ void ClearCachedFilter() { mHasCachedSamplingFilter = false; }
+
+ private:
+ gfx::SamplingFilter mCachedSamplingFilter;
+ bool mHasCachedSamplingFilter;
+};
+
+/**
+ * A TextureSource backed by a TextureImage.
+ *
+ * Depending on the underlying TextureImage, may support texture tiling, so
+ * make sure to check AsBigImageIterator() and use the texture accordingly.
+ *
+ * This TextureSource can be used without a TextureHost and manage it's own
+ * GL texture(s).
+ */
+class TextureImageTextureSourceOGL final : public DataTextureSource,
+ public TextureSourceOGL,
+ public BigImageIterator {
+ public:
+ explicit TextureImageTextureSourceOGL(
+ CompositorOGL* aCompositor, TextureFlags aFlags = TextureFlags::DEFAULT);
+
+ const char* Name() const override { return "TextureImageTextureSourceOGL"; }
+ // DataTextureSource
+
+ bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr,
+ gfx::IntPoint* aDstOffset = nullptr) override;
+
+ void EnsureBuffer(const gfx::IntSize& aSize, gfxContentType aContentType);
+
+ TextureImageTextureSourceOGL* AsTextureImageTextureSource() override {
+ return this;
+ }
+
+ // TextureSource
+
+ void DeallocateDeviceData() override;
+
+ TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ void BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ gfx::IntSize GetSize() const override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ bool IsValid() const override { return !!mTexImage; }
+
+ GLenum GetWrapMode() const override { return mTexImage->GetWrapMode(); }
+
+ // BigImageIterator
+
+ BigImageIterator* AsBigImageIterator() override { return this; }
+
+ void BeginBigImageIteration() override {
+ mTexImage->BeginBigImageIteration();
+ mIterating = true;
+ }
+
+ void EndBigImageIteration() override { mIterating = false; }
+
+ gfx::IntRect GetTileRect() override;
+
+ size_t GetTileCount() override { return mTexImage->GetTileCount(); }
+
+ bool NextTile() override { return mTexImage->NextTile(); }
+
+ gl::GLContext* gl() const { return mGL; }
+
+ protected:
+ ~TextureImageTextureSourceOGL();
+
+ RefPtr<gl::TextureImage> mTexImage;
+ RefPtr<gl::GLContext> mGL;
+ RefPtr<CompositorOGL> mCompositor;
+ TextureFlags mFlags;
+ bool mIterating;
+};
+
+/**
+ * A texture source for GL textures.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ *
+ * The shared texture handle is owned by the TextureHost.
+ */
+class GLTextureSource : public DataTextureSource, public TextureSourceOGL {
+ public:
+ GLTextureSource(TextureSourceProvider* aProvider, GLuint aTextureHandle,
+ GLenum aTarget, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat);
+
+ GLTextureSource(gl::GLContext* aGL, GLuint aTextureHandle, GLenum aTarget,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
+
+ virtual ~GLTextureSource();
+
+ const char* Name() const override { return "GLTextureSource"; }
+
+ GLTextureSource* AsGLTextureSource() override { return this; }
+
+ 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 LOCAL_GL_CLAMP_TO_EDGE; }
+
+ void DeallocateDeviceData() override;
+
+ void SetSize(gfx::IntSize aSize) { mSize = aSize; }
+
+ void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; }
+
+ GLuint GetTextureHandle() const { return mTextureHandle; }
+
+ gl::GLContext* gl() const { return mGL; }
+
+ bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr,
+ gfx::IntPoint* aDstOffset = nullptr) override {
+ return false;
+ }
+
+ protected:
+ void DeleteTextureHandle();
+
+ RefPtr<gl::GLContext> mGL;
+ RefPtr<CompositorOGL> mCompositor;
+ GLuint mTextureHandle;
+ GLenum mTextureTarget;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+};
+
+// This texture source try to wrap "aSurface" in ctor for compositor direct
+// access. Since we can't know the timing for gpu buffer access, the surface
+// should be alive until the ~ClientStorageTextureSource(). And if we try to
+// update the surface we mapped before, we need to call Sync() to make sure
+// the surface is not used by compositor.
+class DirectMapTextureSource : public GLTextureSource {
+ public:
+ DirectMapTextureSource(gl::GLContext* aContext,
+ gfx::DataSourceSurface* aSurface);
+ DirectMapTextureSource(TextureSourceProvider* aProvider,
+ gfx::DataSourceSurface* aSurface);
+ ~DirectMapTextureSource();
+
+ bool Update(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion = nullptr,
+ gfx::IntPoint* aSrcOffset = nullptr,
+ gfx::IntPoint* aDstOffset = nullptr) override;
+
+ // If aBlocking is false, check if this texture is no longer being used
+ // by the GPU - if aBlocking is true, this will block until the GPU is
+ // done with it.
+ bool Sync(bool aBlocking) override;
+
+ void MaybeFenceTexture() override;
+
+ private:
+ bool UpdateInternal(gfx::DataSourceSurface* aSurface,
+ nsIntRegion* aDestRegion, gfx::IntPoint* aSrcOffset,
+ bool aInit);
+
+ GLsync mSync;
+};
+
+class GLTextureHost : public TextureHost {
+ public:
+ GLTextureHost(TextureFlags aFlags, GLuint aTextureHandle, GLenum aTarget,
+ GLsync aSync, gfx::IntSize aSize, bool aHasAlpha);
+
+ virtual ~GLTextureHost();
+
+ // We don't own anything.
+ void DeallocateDeviceData() override {}
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ gfx::IntSize GetSize() const override { return mSize; }
+
+ const char* Name() override { return "GLTextureHost"; }
+
+ protected:
+ const GLuint mTexture;
+ const GLenum mTarget;
+ GLsync mSync;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ RefPtr<GLTextureSource> mTextureSource;
+};
+
+////////////////////////////////////////////////////////////////////////
+// SurfaceTexture
+
+#ifdef MOZ_WIDGET_ANDROID
+
+class SurfaceTextureSource : public TextureSource, public TextureSourceOGL {
+ public:
+ SurfaceTextureSource(TextureSourceProvider* aProvider,
+ java::GeckoSurfaceTexture::Ref& aSurfTex,
+ gfx::SurfaceFormat aFormat, GLenum aTarget,
+ GLenum aWrapMode, gfx::IntSize aSize,
+ Maybe<gfx::Matrix4x4> aTransformOverride);
+
+ 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; }
+
+ gfx::Matrix4x4 GetTextureTransform() override;
+
+ GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ GLenum GetWrapMode() const override { return mWrapMode; }
+
+ void DeallocateDeviceData() override;
+
+ gl::GLContext* gl() const { return mGL; }
+
+ protected:
+ RefPtr<gl::GLContext> mGL;
+ mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
+ const gfx::SurfaceFormat mFormat;
+ const GLenum mTextureTarget;
+ const GLenum mWrapMode;
+ const gfx::IntSize mSize;
+ const Maybe<gfx::Matrix4x4> mTransformOverride;
+};
+
+class SurfaceTextureHost : public TextureHost {
+ public:
+ SurfaceTextureHost(TextureFlags aFlags,
+ mozilla::java::GeckoSurfaceTexture::Ref& aSurfTex,
+ gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+ bool aContinuousUpdate, bool aForceBT709ColorSpace,
+ Maybe<gfx::Matrix4x4> aTransformOverride);
+
+ virtual ~SurfaceTextureHost();
+
+ void DeallocateDeviceData() override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ gfx::IntSize GetSize() const override { return mSize; }
+
+ const char* Name() override { return "SurfaceTextureHost"; }
+
+ SurfaceTextureHost* AsSurfaceTextureHost() override { return this; }
+
+ bool IsWrappingSurfaceTextureHost() override { return true; }
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ uint32_t NumSubTextures() override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ bool SupportsExternalCompositing(WebRenderBackend aBackend) override;
+
+ // gecko does not need deferred deletion with WebRender
+ // GPU/hardware task end could be checked by android fence.
+ // SurfaceTexture uses android fence internally,
+ bool NeedsDeferredDeletion() const override { return false; }
+
+ protected:
+ mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
+ const gfx::IntSize mSize;
+ const gfx::SurfaceFormat mFormat;
+ bool mContinuousUpdate;
+ const bool mForceBT709ColorSpace;
+ const Maybe<gfx::Matrix4x4> mTransformOverride;
+ RefPtr<CompositorOGL> mCompositor;
+ 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(
+ TextureFlags aFlags, const SurfaceDescriptorAndroidHardwareBuffer& aDesc);
+
+ AndroidHardwareBufferTextureHost(
+ TextureFlags aFlags, AndroidHardwareBuffer* aAndroidHardwareBuffer);
+
+ virtual ~AndroidHardwareBufferTextureHost();
+
+ void DeallocateDeviceData() override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ gfx::IntSize GetSize() const override;
+
+ void NotifyNotUsed() override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ const char* Name() override { return "AndroidHardwareBufferTextureHost"; }
+
+ AndroidHardwareBufferTextureHost* AsAndroidHardwareBufferTextureHost()
+ override {
+ return this;
+ }
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ uint32_t NumSubTextures() override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ void SetAcquireFence(mozilla::ipc::FileDescriptor&& aFenceFd) override;
+
+ void SetReleaseFence(mozilla::ipc::FileDescriptor&& aFenceFd) override;
+
+ mozilla::ipc::FileDescriptor GetAndResetReleaseFence() override;
+
+ AndroidHardwareBuffer* GetAndroidHardwareBuffer() const override {
+ 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; }
+
+ protected:
+ RefPtr<AndroidHardwareBuffer> mAndroidHardwareBuffer;
+};
+
+#endif // MOZ_WIDGET_ANDROID
+
+////////////////////////////////////////////////////////////////////////
+// EGLImage
+
+class EGLImageTextureSource : public TextureSource, public TextureSourceOGL {
+ public:
+ EGLImageTextureSource(TextureSourceProvider* aProvider, EGLImage aImage,
+ gfx::SurfaceFormat aFormat, GLenum aTarget,
+ GLenum aWrapMode, gfx::IntSize aSize);
+
+ const char* Name() const override { return "EGLImageTextureSource"; }
+
+ 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; }
+
+ gfx::Matrix4x4 GetTextureTransform() override;
+
+ GLenum GetTextureTarget() const override { return mTextureTarget; }
+
+ GLenum GetWrapMode() const override { return mWrapMode; }
+
+ // We don't own anything.
+ void DeallocateDeviceData() override {}
+
+ gl::GLContext* gl() const { return mGL; }
+
+ protected:
+ RefPtr<gl::GLContext> mGL;
+ RefPtr<CompositorOGL> mCompositor;
+ const EGLImage mImage;
+ const gfx::SurfaceFormat mFormat;
+ const GLenum mTextureTarget;
+ const GLenum mWrapMode;
+ const gfx::IntSize mSize;
+};
+
+class EGLImageTextureHost final : public TextureHost {
+ public:
+ EGLImageTextureHost(TextureFlags aFlags, EGLImage aImage, EGLSync aSync,
+ gfx::IntSize aSize, bool hasAlpha);
+
+ virtual ~EGLImageTextureHost();
+
+ // We don't own anything.
+ void DeallocateDeviceData() override {}
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ gfx::IntSize GetSize() const override { return mSize; }
+
+ const char* Name() override { return "EGLImageTextureHost"; }
+
+ void CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) override;
+
+ void PushResourceUpdates(wr::TransactionBuilder& aResources,
+ ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys,
+ const wr::ExternalImageId& aExtID) override;
+
+ void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) override;
+
+ protected:
+ const EGLImage mImage;
+ const EGLSync mSync;
+ const gfx::IntSize mSize;
+ const bool mHasAlpha;
+ RefPtr<EGLImageTextureSource> mTextureSource;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTUREOGL_H */