summaryrefslogtreecommitdiffstats
path: root/gfx/layers/opengl/CompositorOGL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/opengl/CompositorOGL.cpp')
-rw-r--r--gfx/layers/opengl/CompositorOGL.cpp1725
1 files changed, 1725 insertions, 0 deletions
diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp
new file mode 100644
index 0000000000..4cafbea88a
--- /dev/null
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -0,0 +1,1725 @@
+/* -*- 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_WAYLAND
+# 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 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<EffectNV12> effect =
+ new EffectNV12(nullptr, YUVColorSpace::BT601, ColorRange::LIMITED,
+ ColorDepth::COLOR_8, 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_WAYLAND
+ if (mWidget && mWidget->AsGTK()) {
+ mWidget->AsGTK()->SetEGLNativeWindowSize(mWidgetSize);
+ }
+#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) {
+ nsCString string(aObj->Name());
+ string.Append('-');
+ string.AppendInt((uint64_t)aObj);
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, R"(array["%s"]=")",
+ string.BeginReading());
+ }
+ gfxUtils::DumpAsDataURI(aSurf, gfxUtils::sDumpPaintFile);
+ if (gfxUtils::sDumpPaintFile != stderr) {
+ fprintf_stderr(gfxUtils::sDumpPaintFile, R"(";)");
+ }
+}
+
+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_WAYLAND)
+ // ReleaseSurface internally calls MakeCurrent
+ gl()->ReleaseSurface();
+#endif
+}
+
+bool CompositorOGL::Resume() {
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT) || \
+ defined(MOZ_WAYLAND)
+ 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