diff options
Diffstat (limited to 'gfx/layers/opengl')
25 files changed, 7307 insertions, 0 deletions
diff --git a/gfx/layers/opengl/Composer2D.h b/gfx/layers/opengl/Composer2D.h new file mode 100644 index 0000000000..a7cdcea360 --- /dev/null +++ b/gfx/layers/opengl/Composer2D.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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_layers_Composer2D_h +#define mozilla_layers_Composer2D_h + +#include "gfxTypes.h" +#include "nsISupportsImpl.h" + +/** + * Many platforms have dedicated hardware for simple composition. + * This hardware is usually faster or more power efficient than the + * GPU. However, in exchange for this better performance, generality + * has to be sacrificed: no 3d transforms, no intermediate surfaces, + * no special shader effects, loss of other goodies depending on the + * platform. + * + * Composer2D is a very simple interface to this class of hardware + * that allows an implementation to "try rendering" with the fast + * path. If the given layer tree requires more generality than the + * hardware provides, the implementation should bail and have the + * layer manager fall back on full GPU composition. + */ + +class nsIWidget; + +namespace mozilla { +namespace layers { + +class Layer; + +class Composer2D { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Composer2D) + +protected: + // Protected destructor, to discourage deletion outside of Release(): + virtual ~Composer2D() {} + +public: + /** + * Return true if |aRoot| met the implementation's criteria for fast + * composition and the render was successful. Return false to fall + * back on the GPU. + * + * Currently, when TryRender() returns true, the entire framebuffer + * must have been rendered. + */ + virtual bool TryRenderWithHwc(Layer* aRoot, + nsIWidget* aWidget, + bool aGeometryChanged, + bool aHasImageHostOverlays) = 0; + + /** + * Return true if Composer2D does composition. Return false if Composer2D + * failed the composition. + */ + virtual bool Render(nsIWidget* aWidget) = 0; + + /** + * Return true if Composer2D has a fast composition hardware. + * Return false if Composer2D does not have a fast composition hardware. + */ + virtual bool HasHwc() = 0; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_layers_Composer2D_h diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp new file mode 100644 index 0000000000..c05b8edfd3 --- /dev/null +++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 (mGL && mGL->MakeCurrent()) { + mGL->fDeleteTextures(1, &mTextureHandle); + mGL->fDeleteFramebuffers(1, &mFBO); + } +} + +void +CompositingRenderTargetOGL::BindTexture(GLenum aTextureUnit, GLenum aTextureTarget) +{ + MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); + MOZ_ASSERT(mTextureHandle != 0); + mGL->fActiveTexture(aTextureUnit); + mGL->fBindTexture(aTextureTarget, mTextureHandle); +} + +void +CompositingRenderTargetOGL::BindRenderTarget() +{ + bool needsClear = false; + + if (mInitParams.mStatus != InitParams::INITIALIZED) { + InitializeImpl(); + if (mInitParams.mInit == INIT_MODE_CLEAR) { + needsClear = true; + mClearOnBind = false; + } + } else { + MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); + GLuint fbo = mFBO == 0 ? mGL->GetDefaultFramebuffer() : mFBO; + 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()->RealWidget()); + 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, aFBOTextureTarget=0x%x, " + "aRect.width=%d, aRect.height=%d", + result, mGL, mGL->IsOffscreen(), mFBO, mInitParams.mFBOTextureTarget, + mInitParams.mSize.width, mInitParams.mSize.height); + NS_WARNING(msg.get()); + } + } + + needsClear = mClearOnBind; + } + + if (needsClear) { + ScopedGLState scopedScissorTestState(mGL, LOCAL_GL_SCISSOR_TEST, true); + ScopedScissorRect autoScissorRect(mGL, 0, 0, mInitParams.mSize.width, + mInitParams.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); + } +} + +#ifdef MOZ_DUMP_PAINTING +already_AddRefed<DataSourceSurface> +CompositingRenderTargetOGL::Dump(Compositor* aCompositor) +{ + MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); + CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL(); + return ReadBackSurface(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat()); +} +#endif + +void +CompositingRenderTargetOGL::InitializeImpl() +{ + MOZ_ASSERT(mInitParams.mStatus == InitParams::READY); + + //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, + mInitParams.mFBOTextureTarget, + 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, mInitParams.mFBOTextureTarget, mFBO, mTextureHandle, mInitParams.mSize.width, mInitParams.mSize.height); + NS_ERROR(msg.get()); + } + + mInitParams.mStatus = InitParams::INITIALIZED; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/opengl/CompositingRenderTargetOGL.h b/gfx/layers/opengl/CompositingRenderTargetOGL.h new file mode 100644 index 0000000000..501701d6f1 --- /dev/null +++ b/gfx/layers/opengl/CompositingRenderTargetOGL.h @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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; + + // For lazy initialisation of the GL stuff + struct InitParams + { + InitParams() : mStatus(NO_PARAMS) {} + InitParams(const gfx::IntSize& aSize, + const gfx::IntSize& aPhySize, + GLenum aFBOTextureTarget, + SurfaceInitMode aInit) + : mStatus(READY) + , mSize(aSize) + , mPhySize(aPhySize) + , mFBOTextureTarget(aFBOTextureTarget) + , mInit(aInit) + {} + + enum { + NO_PARAMS, + READY, + INITIALIZED + } mStatus; + /* + * 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. + GLenum mFBOTextureTarget; + SurfaceInitMode mInit; + }; + +public: + CompositingRenderTargetOGL(CompositorOGL* aCompositor, const gfx::IntPoint& aOrigin, + GLuint aTexure, GLuint aFBO) + : CompositingRenderTarget(aOrigin) + , mInitParams() + , mCompositor(aCompositor) + , mGL(aCompositor->gl()) + , mTextureHandle(aTexure) + , mFBO(aFBO) + { + MOZ_ASSERT(mGL); + } + + ~CompositingRenderTargetOGL(); + + virtual 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> + RenderTargetForWindow(CompositorOGL* aCompositor, + const gfx::IntSize& aSize) + { + RefPtr<CompositingRenderTargetOGL> result + = new CompositingRenderTargetOGL(aCompositor, gfx::IntPoint(), 0, 0); + result->mInitParams = InitParams(aSize, aSize, 0, INIT_MODE_NONE); + result->mInitParams.mStatus = InitParams::INITIALIZED; + return result.forget(); + } + + /** + * Some initialisation work on the backing FBO and texture. + * 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. + */ + void Initialize(const gfx::IntSize& aSize, + const gfx::IntSize& aPhySize, + GLenum aFBOTextureTarget, + SurfaceInitMode aInit) + { + MOZ_ASSERT(mInitParams.mStatus == InitParams::NO_PARAMS, "Initialized twice?"); + // postpone initialization until we actually want to use this render target + mInitParams = InitParams(aSize, aPhySize, aFBOTextureTarget, aInit); + } + + void BindTexture(GLenum aTextureUnit, GLenum aTextureTarget); + + /** + * Call when we want to draw into our FBO + */ + void BindRenderTarget(); + + bool IsWindow() { return GetFBO() == 0; } + + GLuint GetFBO() const + { + MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); + return mFBO; + } + + GLuint GetTextureHandle() const + { + MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED); + 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 mInitParams.mSize; + } + + gfx::SurfaceFormat GetFormat() const override + { + // XXX - Should it be implemented ? is the above assert true ? + MOZ_ASSERT(false, "Not implemented"); + return gfx::SurfaceFormat::UNKNOWN; + } + +#ifdef MOZ_DUMP_PAINTING + virtual already_AddRefed<gfx::DataSourceSurface> Dump(Compositor* aCompositor) override; +#endif + + const gfx::IntSize& GetInitSize() const { + return mInitParams.mSize; + } + +private: + /** + * Actually do the initialisation. Note that we leave our FBO bound, and so + * calling this method is only suitable when about to use this render target. + */ + void InitializeImpl(); + + InitParams mInitParams; + /** + * 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; + GLContext* mGL; + 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..bbe1b46578 --- /dev/null +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -0,0 +1,1916 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 "Layers.h" // for WriteSnapshotToDumpFile +#include "LayerScope.h" // for LayerScope +#include "gfxCrashReporterUtils.h" // for ScopedGfxFeatureReporter +#include "gfxEnv.h" // for gfxEnv +#include "gfxPlatform.h" // for gfxPlatform +#include "gfxPrefs.h" // for gfxPrefs +#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/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/LayerManagerComposite.h" // for LayerComposite, etc +#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/mozalloc.h" // for operator delete, etc +#include "nsAppRunner.h" +#include "nsAString.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 "ScopedGLHelpers.h" +#include "GLReadTexImageHelper.h" +#include "GLBlitTextureImageHelper.h" +#include "HeapCopyOfStackArray.h" + +#if MOZ_WIDGET_ANDROID +#include "TexturePoolOGL.h" +#endif + +#include "GeckoProfiler.h" + +namespace mozilla { + +using namespace std; +using namespace gfx; + +namespace layers { + +using namespace mozilla::gl; + +static const GLuint kCoordinateAttributeIndex = 0; +static const GLuint kTexCoordinateAttributeIndex = 1; + +static void +BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask, + GLenum aTexUnit, const gfx::Matrix4x4& aTransform) +{ + MOZ_ASSERT(LOCAL_GL_TEXTURE0 <= aTexUnit && aTexUnit <= LOCAL_GL_TEXTURE31); + aSourceMask->BindTexture(aTexUnit, gfx::SamplingFilter::LINEAR); + aProgram->SetMaskTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0); + aProgram->SetMaskLayerTransform(aTransform); +} + +void +CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit) +{ + MOZ_ASSERT(aBackdrop); + + mGLContext->fActiveTexture(aTexUnit); + mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop); + mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); + mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); + aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0); +} + +CompositorOGL::CompositorOGL(CompositorBridgeParent* aParent, + widget::CompositorWidget* aWidget, + int aSurfaceWidth, int aSurfaceHeight, + bool aUseExternalSurfaceSize) + : Compositor(aWidget, aParent) + , mWidgetSize(-1, -1) + , mSurfaceSize(aSurfaceWidth, aSurfaceHeight) + , mHasBGRA(0) + , mUseExternalSurfaceSize(aUseExternalSurfaceSize) + , mFrameInProgress(false) + , mDestroyed(false) + , mViewportSize(0, 0) + , mCurrentProgram(nullptr) +{ + MOZ_COUNT_CTOR(CompositorOGL); +} + +CompositorOGL::~CompositorOGL() +{ + MOZ_COUNT_DTOR(CompositorOGL); + Destroy(); +} + +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::LayersPreferEGL()) { + printf_stderr("Trying GL layers...\n"); + context = gl::GLContextProviderEGL::CreateForCompositorWidget(mWidget, false); + } +#endif + + // Allow to create offscreen GL context for main Layer Manager + if (!context && gfxEnv::LayersPreferOffscreen()) { + SurfaceCaps caps = SurfaceCaps::ForRGB(); + caps.preserve = false; + caps.bpp16 = gfxVars::OffscreenFormat() == SurfaceFormat::R5G6B5_UINT16; + + nsCString discardFailureId; + context = GLContextProvider::CreateOffscreen(mSurfaceSize, + caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE, + &discardFailureId); + } + + if (!context) { + context = gl::GLContextProvider::CreateForCompositorWidget(mWidget, + 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; + + RefPtr<GLContext> ctx = mGLContext->GetSharedContext(); + if (!ctx) { + ctx = mGLContext; + } + + if (!ctx->MakeCurrent()) { + // Leak resources! + mQuadVBO = 0; + mTriangleVBO = 0; + mGLContext = nullptr; + mPrograms.clear(); + return; + } + + for (std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.begin(); + iter != mPrograms.end(); + iter++) { + delete iter->second; + } + mPrograms.clear(); + + ctx->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); + + if (mQuadVBO) { + ctx->fDeleteBuffers(1, &mQuadVBO); + mQuadVBO = 0; + } + + if (mTriangleVBO) { + ctx->fDeleteBuffers(1, &mTriangleVBO); + mTriangleVBO = 0; + } + + mGLContext->MakeCurrent(); + + mBlitTextureImageHelper = nullptr; + + mContextStateTracker.DestroyOGL(mGLContext); + + // 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(nsCString* const out_failureReason) +{ + ScopedGfxFeatureReporter reporter("GL Layers"); + + // Do not allow double initialization + MOZ_ASSERT(mGLContext == nullptr, "Don't reinitialize CompositorOGL"); + + mGLContext = CreateContext(); + +#ifdef MOZ_WIDGET_ANDROID + if (!mGLContext){ + *out_failureReason = "FEATURE_FAILURE_OPENGL_NO_ANDROID_CONTEXT"; + NS_RUNTIMEABORT("We need a context on Android"); + } +#endif + + if (!mGLContext){ + *out_failureReason = "FEATURE_FAILURE_OPENGL_CREATE_CONTEXT"; + return false; + } + + 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<EffectSolidColor> effect = new EffectSolidColor(Color(0, 0, 0, 0)); + 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 += + NS_LITERAL_STRING("OpenGL compositor Initialized Succesfully.\nVersion: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VERSION))); + msg += NS_LITERAL_STRING("\nVendor: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_VENDOR))); + msg += NS_LITERAL_STRING("\nRenderer: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)mGLContext->fGetString(LOCAL_GL_RENDERER))); + msg += NS_LITERAL_STRING("\nFBO Texture Target: "); + if (mFBOTextureTarget == LOCAL_GL_TEXTURE_2D) + msg += NS_LITERAL_STRING("TEXTURE_2D"); + else + msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE"); + 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->mInitParams.mSize; + // Physical surface size. + const gfx::IntSize& phySize = aRenderTarget->mInitParams.mPhySize; + + // Set the viewport correctly. + mGLContext->fViewport(0, 0, 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; + if (mGLContext->IsOffscreen() && !gIsGtest) { + // In case of rendering via GL Offscreen context, disable Y-Flipping + viewMatrix.PreTranslate(-1.0, -1.0); + viewMatrix.PreScale(2.0f / float(size.width), 2.0f / float(size.height)); + } else { + 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"); + // If we're drawing directly to the window then we want to offset + // drawing by the render offset. + if (!mTarget && mCurrentRenderTarget->IsWindow()) { + viewMatrix.PreTranslate(mRenderOffset.x, mRenderOffset.y); + } + + Matrix4x4 matrix3d = Matrix4x4::From2D(viewMatrix); + matrix3d._33 = 0.0f; + mProjMatrix = matrix3d; + mGLContext->fDepthRange(0.0f, 1.0f); + } else { + // XXX take into account mRenderOffset + 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.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + 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); + RefPtr<CompositingRenderTargetOGL> surface + = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo); + surface->Initialize(aRect.Size(), FBOSize, mFBOTextureTarget, aInit); + return surface.forget(); +} + +already_AddRefed<CompositingRenderTarget> +CompositorOGL::CreateRenderTargetFromSource(const IntRect &aRect, + const CompositingRenderTarget *aSource, + const IntPoint &aSourcePoint) +{ + MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size"); + + if (aRect.width * aRect.height == 0) { + return nullptr; + } + + if (!gl()) { + return nullptr; + } + + GLuint tex = 0; + GLuint fbo = 0; + const CompositingRenderTargetOGL* sourceSurface + = static_cast<const CompositingRenderTargetOGL*>(aSource); + IntRect sourceRect(aSourcePoint, aRect.Size()); + if (aSource) { + CreateFBOWithTexture(sourceRect, true, sourceSurface->GetFBO(), + &fbo, &tex); + } else { + CreateFBOWithTexture(sourceRect, true, 0, + &fbo, &tex); + } + + RefPtr<CompositingRenderTargetOGL> surface + = new CompositingRenderTargetOGL(this, aRect.TopLeft(), tex, fbo); + surface->Initialize(aRect.Size(), + sourceRect.Size(), + mFBOTextureTarget, + INIT_MODE_NONE); + return surface.forget(); +} + +void +CompositorOGL::SetRenderTarget(CompositingRenderTarget *aSurface) +{ + MOZ_ASSERT(aSurface); + CompositingRenderTargetOGL* surface + = static_cast<CompositingRenderTargetOGL*>(aSurface); + if (mCurrentRenderTarget != surface) { + mCurrentRenderTarget = surface; + if (mCurrentRenderTarget) { + mContextStateTracker.PopOGLSection(gl(), "Frame"); + } + mContextStateTracker.PushOGLSection(gl(), "Frame"); + surface->BindRenderTarget(); + } + + PrepareViewport(mCurrentRenderTarget); +} + +CompositingRenderTarget* +CompositorOGL::GetCurrentRenderTarget() const +{ + return mCurrentRenderTarget; +} + +static GLenum +GetFrameBufferInternalFormat(GLContext* gl, + GLuint aFrameBuffer, + mozilla::widget::CompositorWidget* aWidget) +{ + if (aFrameBuffer == 0) { // default framebuffer + return aWidget->GetGLFrameBufferFormat(); + } + return LOCAL_GL_RGBA; +} + +void +CompositorOGL::ClearRect(const gfx::Rect& aRect) +{ + // Map aRect to OGL coordinates, origin:bottom-left + GLint y = mViewportSize.height - (aRect.y + aRect.height); + + ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true); + ScopedScissorRect autoScissorRect(mGLContext, aRect.x, y, aRect.width, aRect.height); + mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0); + mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT); +} + +void +CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion, + const IntRect *aClipRectIn, + const IntRect& aRenderBounds, + const nsIntRegion& aOpaqueRegion, + IntRect *aClipRectOut, + IntRect *aRenderBoundsOut) +{ + PROFILER_LABEL("CompositorOGL", "BeginFrame", + js::ProfileEntry::Category::GRAPHICS); + + MOZ_ASSERT(!mFrameInProgress, "frame still in progress (should have called EndFrame"); + + gfx::IntRect rect; + if (mUseExternalSurfaceSize) { + rect = gfx::IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height); + } else { + rect = gfx::IntRect(aRenderBounds.x, aRenderBounds.y, aRenderBounds.width, aRenderBounds.height); + } + + if (aRenderBoundsOut) { + *aRenderBoundsOut = rect; + } + + GLint width = rect.width; + GLint height = rect.height; + + // We can't draw anything to something with no area + // so just return + if (width == 0 || height == 0) + return; + + // We're about to actually draw a frame. + mFrameInProgress = true; + + // If the widget size changed, we have to force a MakeCurrent + // to make sure that GL sees the updated widget size. + if (mWidgetSize.width != width || + mWidgetSize.height != height) + { + MakeCurrent(ForceMakeCurrent); + + mWidgetSize.width = width; + mWidgetSize.height = height; + } else { + MakeCurrent(); + } + + mPixelsPerFrame = width * height; + mPixelsFilled = 0; + +#ifdef MOZ_WIDGET_ANDROID + TexturePoolOGL::Fill(gl()); +#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<CompositingRenderTargetOGL> rt = + CompositingRenderTargetOGL::RenderTargetForWindow(this, + IntSize(width, height)); + SetRenderTarget(rt); + +#ifdef DEBUG + mWindowRenderTarget = mCurrentRenderTarget; +#endif + + if (aClipRectOut && !aClipRectIn) { + aClipRectOut->SetRect(0, 0, width, height); + } + + mGLContext->fClearColor(mClearColor.r, mClearColor.g, mClearColor.b, mClearColor.a); + mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT); +} + +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); +} + +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.width = std::min(clampedRect.width, maxTexSize); + clampedRect.height = std::min(clampedRect.height, maxTexSize); + + 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.y + clampedRect.height), + clampedRect.width, clampedRect.height, + 0); + } else { + // Curses, incompatible formats. Take a slow path. + + // RGBA + size_t bufferSize = clampedRect.width * clampedRect.height * 4; + auto buf = MakeUnique<uint8_t[]>(bufferSize); + + mGLContext->fReadPixels(clampedRect.x, clampedRect.y, + clampedRect.width, clampedRect.height, + LOCAL_GL_RGBA, + LOCAL_GL_UNSIGNED_BYTE, + buf.get()); + mGLContext->fTexImage2D(mFBOTextureTarget, + 0, + LOCAL_GL_RGBA, + clampedRect.width, clampedRect.height, + 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, + clampedRect.width, clampedRect.height, + 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 = clampedRect.width; + aAllocSize->height = clampedRect.height; + } + + return tex; +} + +ShaderConfigOGL +CompositorOGL::GetShaderConfigFor(Effect *aEffect, + MaskType aMask, + gfx::CompositionOp aOp, + bool aColorMatrix, + bool aDEAAEnabled) const +{ + ShaderConfigOGL config; + + switch(aEffect->mType) { + case EffectTypes::SOLID_COLOR: + config.SetRenderColor(true); + break; + case EffectTypes::YCBCR: + config.SetYCbCr(true); + break; + case EffectTypes::NV12: + config.SetNV12(true); + config.SetTextureTarget(LOCAL_GL_TEXTURE_RECTANGLE_ARB); + break; + case EffectTypes::COMPONENT_ALPHA: + { + config.SetComponentAlpha(true); + EffectComponentAlpha* effectComponentAlpha = + static_cast<EffectComponentAlpha*>(aEffect); + gfx::SurfaceFormat format = effectComponentAlpha->mOnWhite->GetFormat(); + config.SetRBSwap(format == gfx::SurfaceFormat::B8G8R8A8 || + format == gfx::SurfaceFormat::B8G8R8X8); + TextureSourceOGL* source = effectComponentAlpha->mOnWhite->AsSourceOGL(); + config.SetTextureTarget(source->GetTextureTarget()); + break; + } + case EffectTypes::RENDER_TARGET: + config.SetTextureTarget(mFBOTextureTarget); + 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); + 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.SetColorMatrix(aColorMatrix); + config.SetMask(aMask == MaskType::Mask); + config.SetDEAA(aDEAAEnabled); + config.SetCompositionOp(aOp); + return config; +} + +ShaderProgramOGL* +CompositorOGL::GetShaderProgramFor(const ShaderConfigOGL &aConfig) +{ + std::map<ShaderConfigOGL, ShaderProgramOGL *>::iterator iter = mPrograms.find(aConfig); + if (iter != mPrograms.end()) + return iter->second; + + ProgramProfileOGL profile = ProgramProfileOGL::GetProfileFor(aConfig); + ShaderProgramOGL *shader = new ShaderProgramOGL(gl(), profile); + if (!shader->Initialize()) { + delete shader; + return nullptr; + } + + mPrograms[aConfig] = shader; + return shader; +} + +void +CompositorOGL::ActivateProgram(ShaderProgramOGL* aProg) +{ + if (mCurrentProgram != aProg) { + gl()->fUseProgram(aProg->GetProgram()); + mCurrentProgram = aProg; + } +} + +void +CompositorOGL::ResetProgram() +{ + mCurrentProgram = nullptr; +} + +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 * aPoint2.y - aPoint2.x * aPoint1.y; + + 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) +{ + PROFILER_LABEL("CompositorOGL", "DrawQuad", + js::ProfileEntry::Category::GRAPHICS); + + DrawGeometry(aRect, aClipRect, aEffectChain, + aOpacity, aTransform, aVisibleRect); +} + +void +CompositorOGL::DrawTriangle(const gfx::TexturedTriangle& aTriangle, + const gfx::IntRect& aClipRect, + const EffectChain& aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) +{ + PROFILER_LABEL("CompositorOGL", "DrawTriangle", + js::ProfileEntry::Category::GRAPHICS); + + DrawGeometry(aTriangle, aClipRect, aEffectChain, + aOpacity, aTransform, aVisibleRect); +} + +template<typename Geometry> +void +CompositorOGL::DrawGeometry(const Geometry& aGeometry, + const IntRect& aClipRect, + const EffectChain &aEffectChain, + Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) +{ + MOZ_ASSERT(mFrameInProgress, "frame not started"); + MOZ_ASSERT(mCurrentRenderTarget, "No destination"); + + MakeCurrent(); + + IntPoint offset = mCurrentRenderTarget->GetOrigin(); + IntSize size = mCurrentRenderTarget->GetSize(); + + Rect renderBound(0, 0, size.width, size.height); + renderBound.IntersectRect(renderBound, Rect(aClipRect)); + renderBound.MoveBy(offset); + + Rect destRect = aTransform.TransformAndClipBounds(aGeometry, renderBound); + + // XXX: This doesn't handle 3D transforms. It also doesn't handled rotated + // quads. Fix me. + mPixelsFilled += destRect.width * destRect.height; + + // Do a simple culling if this rect is out of target buffer. + // Inflate a small size to avoid some numerical imprecision issue. + destRect.Inflate(1, 1); + destRect.MoveBy(-offset); + renderBound = Rect(0, 0, size.width, size.height); + if (!renderBound.Intersects(destRect)) { + return; + } + + LayerScope::DrawBegin(); + + IntRect clipRect = aClipRect; + // aClipRect is in destination coordinate space (after all + // transforms and offsets have been applied) so if our + // drawing is going to be shifted by mRenderOffset then we need + // to shift the clip rect by the same amount. + if (!mTarget && mCurrentRenderTarget->IsWindow()) { + clipRect.MoveBy(mRenderOffset.x, mRenderOffset.y); + } + + ScopedGLState scopedScissorTestState(mGLContext, LOCAL_GL_SCISSOR_TEST, true); + ScopedScissorRect autoScissorRect(mGLContext, clipRect.x, FlipY(clipRect.y + clipRect.height), + clipRect.width, clipRect.height); + + MaskType maskType; + EffectMask* effectMask; + TextureSourceOGL* sourceMask = nullptr; + gfx::Matrix4x4 maskQuadTransform; + if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) { + effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get()); + sourceMask = effectMask->mMaskTexture->AsSourceOGL(); + + // NS_ASSERTION(textureMask->IsAlpha(), + // "OpenGL mask layers must be backed by alpha surfaces"); + + // We're assuming that the gl backend won't cheat and use NPOT + // textures when glContext says it can't (which seems to happen + // on a mac when you force POT textures) + IntSize maskSize = CalculatePOTSize(effectMask->mSize, mGLContext); + + const gfx::Matrix4x4& maskTransform = effectMask->mMaskTransform; + NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!"); + Rect bounds = Rect(Point(), Size(maskSize)); + bounds = maskTransform.As2D().TransformBounds(bounds); + + maskQuadTransform._11 = 1.0f/bounds.width; + maskQuadTransform._22 = 1.0f/bounds.height; + maskQuadTransform._41 = float(-bounds.x)/bounds.width; + maskQuadTransform._42 = float(-bounds.y)/bounds.height; + + maskType = MaskType::Mask; + } else { + maskType = MaskType::MaskNone; + } + + // Determine the color if this is a color shader and fold the opacity into + // the color since color shaders don't have an opacity uniform. + Color color; + if (aEffectChain.mPrimaryEffect->mType == EffectTypes::SOLID_COLOR) { + EffectSolidColor* effectSolidColor = + static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get()); + color = effectSolidColor->mColor; + + Float opacity = aOpacity * color.a; + color.r *= opacity; + color.g *= opacity; + color.b *= opacity; + color.a = opacity; + + // We can fold opacity into the color, so no need to consider it further. + aOpacity = 1.f; + } + + bool createdMixBlendBackdropTexture = false; + GLuint mixBlendBackdrop = 0; + gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER; + + if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) { + EffectBlendMode *blendEffect = + static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get()); + blendMode = blendEffect->mBlendMode; + } + + // Only apply DEAA to quads that have been transformed such that aliasing + // could be visible + bool bEnableAA = gfxPrefs::LayersDEAAEnabled() && + !aTransform.Is2DIntegerTranslation(); + + bool colorMatrix = aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX]; + ShaderConfigOGL config = GetShaderConfigFor(aEffectChain.mPrimaryEffect, + maskType, blendMode, colorMatrix, + bEnableAA); + + config.SetOpacity(aOpacity != 1.f); + ApplyPrimitiveConfig(config, aGeometry); + + ShaderProgramOGL *program = GetShaderProgramFor(config); + ActivateProgram(program); + program->SetProjectionMatrix(mProjMatrix); + program->SetLayerTransform(aTransform); + LayerScope::SetLayerTransform(aTransform); + + if (colorMatrix) { + EffectColorMatrix* effectColorMatrix = + static_cast<EffectColorMatrix*>(aEffectChain.mSecondaryEffects[EffectTypes::COLOR_MATRIX].get()); + program->SetColorMatrix(effectColorMatrix->mColorMatrix); + } + + if (BlendOpIsMixBlendMode(blendMode)) { + gfx::Matrix4x4 backdropTransform; + + if (gl()->IsExtensionSupported(GLContext::NV_texture_barrier)) { + // The NV_texture_barrier extension lets us read directly from the + // backbuffer. Let's do that. + // We need to tell OpenGL about this, so that it can make sure everything + // on the GPU is happening in the right order. + gl()->fTextureBarrier(); + mixBlendBackdrop = mCurrentRenderTarget->GetTextureHandle(); + } else { + gfx::IntRect rect = ComputeBackdropCopyRect(aGeometry, aClipRect, + aTransform, &backdropTransform); + mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO()); + createdMixBlendBackdropTexture = true; + } + program->SetBackdropTransform(backdropTransform); + } + + program->SetRenderOffset(offset.x, offset.y); + LayerScope::SetRenderOffset(offset.x, offset.y); + + if (aOpacity != 1.f) + program->SetLayerOpacity(aOpacity); + + if (config.mFeatures & ENABLE_TEXTURE_RECT) { + TextureSourceOGL* source = nullptr; + if (aEffectChain.mPrimaryEffect->mType == EffectTypes::COMPONENT_ALPHA) { + EffectComponentAlpha* effectComponentAlpha = + static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get()); + source = effectComponentAlpha->mOnWhite->AsSourceOGL(); + } else { + 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) * (points[wp1].y + points[wp].y) + + (points[wp2].x - points[wp1].x) * (points[wp2].y + points[wp1].y) + + (points[wp].x - points[wp2].x) * (points[wp].y + points[wp2].y); + 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::SOLID_COLOR: { + program->SetRenderColor(color); + + if (maskType != MaskType::MaskNone) { + BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform); + } + if (mixBlendBackdrop) { + BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1); + } + + didSetBlendMode = SetBlendMode(gl(), blendMode); + + BindAndDrawGeometry(program, aGeometry); + } + break; + + case EffectTypes::RGB: { + TexturedEffect* texturedEffect = + static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get()); + TextureSource *source = texturedEffect->mTexture; + + didSetBlendMode = SetBlendMode(gl(), blendMode, 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); + + if (maskType != MaskType::MaskNone) { + BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform); + } + if (mixBlendBackdrop) { + BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2); + } + + BindAndDrawGeometryWithTextureRect(program, aGeometry, + texturedEffect->mTextureCoords, source); + } + 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); + + program->SetYCbCrTextureUnits(Y, Cb, Cr); + program->SetTextureTransform(Matrix4x4()); + program->SetYUVColorSpace(effectYCbCr->mYUVColorSpace); + + if (maskType != MaskType::MaskNone) { + BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform); + } + if (mixBlendBackdrop) { + BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4); + } + didSetBlendMode = SetBlendMode(gl(), blendMode); + BindAndDrawGeometryWithTextureRect(program, + aGeometry, + effectYCbCr->mTextureCoords, + sourceYCbCr->GetSubSource(Y)); + } + 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()); + + if (maskType != MaskType::MaskNone) { + BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform); + } + if (mixBlendBackdrop) { + BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3); + } + didSetBlendMode = SetBlendMode(gl(), blendMode); + BindAndDrawGeometryWithTextureRect(program, + aGeometry, + effectNV12->mTextureCoords, + sourceNV12->GetSubSource(Y)); + } + break; + case EffectTypes::RENDER_TARGET: { + EffectRenderTarget* effectRenderTarget = + static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get()); + RefPtr<CompositingRenderTargetOGL> surface + = static_cast<CompositingRenderTargetOGL*>(effectRenderTarget->mRenderTarget.get()); + + surface->BindTexture(LOCAL_GL_TEXTURE0, mFBOTextureTarget); + + // Drawing is always flipped, but when copying between surfaces we want to avoid + // this, so apply a flip here to cancel the other one out. + Matrix transform; + transform.PreTranslate(0.0, 1.0); + transform.PreScale(1.0f, -1.0f); + program->SetTextureTransform(Matrix4x4::From2D(transform)); + program->SetTextureUnit(0); + + if (maskType != MaskType::MaskNone) { + BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform); + } + if (mixBlendBackdrop) { + BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2); + } + + if (config.mFeatures & ENABLE_TEXTURE_RECT) { + // 2DRect case, get the multiplier right for a sampler2DRect + program->SetTexCoordMultiplier(surface->GetSize().width, + surface->GetSize().height); + } + + // Drawing is always flipped, but when copying between surfaces we want to avoid + // this. Pass true for the flip parameter to introduce a second flip + // that cancels the other one out. + didSetBlendMode = SetBlendMode(gl(), blendMode); + BindAndDrawGeometry(program, aGeometry); + } + break; + case EffectTypes::COMPONENT_ALPHA: { + MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled()); + MOZ_ASSERT(blendMode == gfx::CompositionOp::OP_OVER, "Can't support blend modes with component alpha!"); + EffectComponentAlpha* effectComponentAlpha = + static_cast<EffectComponentAlpha*>(aEffectChain.mPrimaryEffect.get()); + TextureSourceOGL* sourceOnWhite = effectComponentAlpha->mOnWhite->AsSourceOGL(); + TextureSourceOGL* sourceOnBlack = effectComponentAlpha->mOnBlack->AsSourceOGL(); + + if (!sourceOnBlack->IsValid() || + !sourceOnWhite->IsValid()) { + NS_WARNING("Invalid layer texture for component alpha"); + return; + } + + sourceOnBlack->BindTexture(LOCAL_GL_TEXTURE0, effectComponentAlpha->mSamplingFilter); + sourceOnWhite->BindTexture(LOCAL_GL_TEXTURE1, effectComponentAlpha->mSamplingFilter); + + program->SetBlackTextureUnit(0); + program->SetWhiteTextureUnit(1); + program->SetTextureTransform(Matrix4x4()); + + if (maskType != MaskType::MaskNone) { + BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform); + } + // Pass 1. + gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_COLOR, + LOCAL_GL_ONE, LOCAL_GL_ONE); + program->SetTexturePass2(false); + BindAndDrawGeometryWithTextureRect(program, + aGeometry, + effectComponentAlpha->mTextureCoords, + effectComponentAlpha->mOnBlack); + + // Pass 2. + gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE, + LOCAL_GL_ONE, LOCAL_GL_ONE); + program->SetTexturePass2(true); + BindAndDrawGeometryWithTextureRect(program, + aGeometry, + effectComponentAlpha->mTextureCoords, + effectComponentAlpha->mOnBlack); + + mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA, + LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA); + } + 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); + } + if (createdMixBlendBackdropTexture) { + gl()->fDeleteTextures(1, &mixBlendBackdrop); + } + + // in case rendering has used some other GL context + MakeCurrent(); + + LayerScope::DrawEnd(mGLContext, aEffectChain, + aGeometry.width, aGeometry.height); +} + +void +CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram, + const gfx::Rect& aRect, + const gfx::Rect& aTextureRect) +{ + BindAndDrawQuad(aProgram, aRect, aTextureRect); +} + +void +CompositorOGL::BindAndDrawGeometry(ShaderProgramOGL* aProgram, + const gfx::TexturedTriangle& aTriangle, + const gfx::Rect& aTextureRect) +{ + NS_ASSERTION(aProgram->HasInitialized(), "Shader program not correctly initialized"); + + const gfx::TexturedTriangle& t = aTriangle; + const gfx::Triangle& tex = t.textureCoords; + + GLfloat vertices[] = { + t.p1.x, t.p1.y, 0.0f, 1.0f, tex.p1.x, tex.p1.y, + t.p2.x, t.p2.y, 0.0f, 1.0f, tex.p2.x, tex.p2.y, + t.p3.x, t.p3.y, 0.0f, 1.0f, tex.p3.x, tex.p3.y + }; + + HeapCopyOfStackArray<GLfloat> verticesOnHeap(vertices); + mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTriangleVBO); + mGLContext->fBufferData(LOCAL_GL_ARRAY_BUFFER, + verticesOnHeap.ByteLength(), + verticesOnHeap.Data(), + LOCAL_GL_STREAM_DRAW); + + const GLsizei stride = 6 * sizeof(GLfloat); + InitializeVAO(kCoordinateAttributeIndex, 4, stride, 0); + InitializeVAO(kTexCoordinateAttributeIndex, 2, stride, 4 * sizeof(GLfloat)); + + mGLContext->fDrawArrays(LOCAL_GL_TRIANGLES, 0, 3); + + 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 gfx::TexturedTriangle& aTriangle, + const gfx::Rect& aTexCoordRect, + TextureSource *aTexture) +{ + BindAndDrawGeometry(aProg, aTriangle, + GetTextureCoordinates(aTexCoordRect, aTexture)); +} + +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); + LayerScope::SetDrawRects(aQuads, aLayerRects, aTextureRects); +} + +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); +} + +void +CompositorOGL::EndFrame() +{ + PROFILER_LABEL("CompositorOGL", "EndFrame", + js::ProfileEntry::Category::GRAPHICS); + + MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored"); + +#ifdef MOZ_DUMP_PAINTING + if (gfxEnv::DumpCompositorTextures()) { + 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 + + mContextStateTracker.PopOGLSection(gl(), "Frame"); + + mFrameInProgress = false; + + if (mTarget) { + CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix()); + mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); + mCurrentRenderTarget = nullptr; + Compositor::EndFrame(); + return; + } + + mCurrentRenderTarget = nullptr; + + if (mTexturePool) { + mTexturePool->EndFrame(); + } + + 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); + } + } + + Compositor::EndFrame(); +} + +void +CompositorOGL::EndFrameForExternalComposition(const gfx::Matrix& aTransform) +{ + MOZ_ASSERT(!mTarget); + if (mTexturePool) { + mTexturePool->EndFrame(); + } +} + +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; + } + + mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); + + if (!mGLContext->IsGLES()) { + // GLES2 promises that binding to any custom FBO will attach + // to GL_COLOR_ATTACHMENT0 attachment point. + mGLContext->fReadBuffer(LOCAL_GL_BACK); + } + + 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, rect.width, rect.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(); +#endif +} + +bool +CompositorOGL::Resume() +{ +#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT) + if (!gl() || gl()->IsDestroyed()) + return false; + + // RenewSurface internally calls MakeCurrent. + return gl()->RenewSurface(GetWidget()->RealWidget()); +#endif + return true; +} + +already_AddRefed<DataTextureSource> +CompositorOGL::CreateDataTextureSource(TextureFlags aFlags) +{ + return MakeAndAddRef<TextureImageTextureSourceOGL>(this, aFlags); +} + +bool +CompositorOGL::SupportsPartialTextureUpdate() +{ + return CanUploadSubTextures(mGLContext); +} + +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); +} + +GLBlitTextureImageHelper* +CompositorOGL::BlitTextureImageHelper() +{ + if (!mBlitTextureImageHelper) { + mBlitTextureImageHelper = MakeUnique<GLBlitTextureImageHelper>(this); + } + + return mBlitTextureImageHelper.get(); +} + + + +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 +PerFrameTexturePoolOGL::DestroyTextures() +{ + if (!mGL->MakeCurrent()) { + return; + } + + if (mUnusedTextures.Length() > 0) { + mGL->fDeleteTextures(mUnusedTextures.Length(), &mUnusedTextures[0]); + mUnusedTextures.Clear(); + } + + if (mCreatedTextures.Length() > 0) { + mGL->fDeleteTextures(mCreatedTextures.Length(), &mCreatedTextures[0]); + mCreatedTextures.Clear(); + } +} + +GLuint +PerFrameTexturePoolOGL::GetTexture(GLenum aTarget, GLenum) +{ + if (mTextureTarget == 0) { + mTextureTarget = aTarget; + } + + // The pool should always use the same texture target because it is illegal + // to change the target of an already exisiting gl texture. + // If we need to use several targets, a pool with several sub-pools (one per + // target) will have to be implemented. + // At the moment this pool is only used with tiling on b2g so we always need + // the same target. + MOZ_ASSERT(mTextureTarget == aTarget); + + GLuint texture = 0; + + if (!mUnusedTextures.IsEmpty()) { + // Try to reuse one from the unused pile first + texture = mUnusedTextures[0]; + mUnusedTextures.RemoveElementAt(0); + } else if (mGL->MakeCurrent()) { + // There isn't one to reuse, create one. + mGL->fGenTextures(1, &texture); + mGL->fBindTexture(aTarget, texture); + 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); + } + + if (texture) { + mCreatedTextures.AppendElement(texture); + } + + return texture; +} + +void +PerFrameTexturePoolOGL::EndFrame() +{ + if (!mGL->MakeCurrent()) { + // this means the context got destroyed underneith us somehow, and the driver + // already has destroyed the textures. + mCreatedTextures.Clear(); + mUnusedTextures.Clear(); + return; + } + + // Some platforms have issues unlocking Gralloc buffers even when they're + // rebound. + if (gfxPrefs::OverzealousGrallocUnlocking()) { + mUnusedTextures.AppendElements(mCreatedTextures); + mCreatedTextures.Clear(); + } + + // Delete unused textures + for (size_t i = 0; i < mUnusedTextures.Length(); i++) { + GLuint texture = mUnusedTextures[i]; + mGL->fDeleteTextures(1, &texture); + } + mUnusedTextures.Clear(); + + // Move all created textures into the unused pile + mUnusedTextures.AppendElements(mCreatedTextures); + mCreatedTextures.Clear(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/opengl/CompositorOGL.h b/gfx/layers/opengl/CompositorOGL.h new file mode 100644 index 0000000000..da204b12e6 --- /dev/null +++ b/gfx/layers/opengl/CompositorOGL.h @@ -0,0 +1,502 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 "ContextStateTracker.h" +#include "gfx2DGlue.h" +#include "GLContextTypes.h" // for GLContext, etc +#include "GLDefs.h" // for GLuint, LOCAL_GL_TEXTURE_2D, etc +#include "OGLShaderProgram.h" // for ShaderProgramOGL, etc +#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/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 GLManagerCompositor; +class TextureSource; +struct Effect; +struct EffectChain; +class GLBlitTextureImageHelper; + +/** + * 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() {} + +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) + : mTextureTarget(0) // zero is never a valid texture target + , mGL(aGL) + {} + + virtual ~PerUnitTexturePoolOGL() + { + DestroyTextures(); + } + + virtual void Clear() override + { + DestroyTextures(); + } + + virtual GLuint GetTexture(GLenum aTarget, GLenum aUnit) override; + + virtual void EndFrame() override {} + +protected: + void DestroyTextures(); + + GLenum mTextureTarget; + nsTArray<GLuint> mTextures; + RefPtr<gl::GLContext> mGL; +}; + +/** + * Reuse gl textures from a pool of textures that haven't yet been + * used during the current frame. + * All the textures that are not used at the end of a frame are + * deleted. + * This strategy seems to work well with gralloc textures because destroying + * unused textures which are bound to gralloc buffers let drivers know that it + * can unlock the gralloc buffers. + */ +class PerFrameTexturePoolOGL : public CompositorTexturePoolOGL +{ +public: + explicit PerFrameTexturePoolOGL(gl::GLContext* aGL) + : mTextureTarget(0) // zero is never a valid texture target + , mGL(aGL) + {} + + virtual ~PerFrameTexturePoolOGL() + { + DestroyTextures(); + } + + virtual void Clear() override + { + DestroyTextures(); + } + + virtual GLuint GetTexture(GLenum aTarget, GLenum aUnit) override; + + virtual void EndFrame() override; + +protected: + void DestroyTextures(); + + GLenum mTextureTarget; + RefPtr<gl::GLContext> mGL; + nsTArray<GLuint> mCreatedTextures; + nsTArray<GLuint> mUnusedTextures; +}; + +// 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 GLManagerCompositor; + friend class CompositingRenderTargetOGL; + + std::map<ShaderConfigOGL, ShaderProgramOGL*> mPrograms; +public: + explicit CompositorOGL(CompositorBridgeParent* aParent, + widget::CompositorWidget* aWidget, + int aSurfaceWidth = -1, int aSurfaceHeight = -1, + bool aUseExternalSurfaceSize = false); + +protected: + virtual ~CompositorOGL(); + +public: + virtual CompositorOGL* AsCompositorOGL() override { return this; } + + virtual already_AddRefed<DataTextureSource> + CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override; + + virtual bool Initialize(nsCString* const out_failureReason) override; + + virtual void Destroy() override; + + virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override + { + TextureFactoryIdentifier result = + TextureFactoryIdentifier(LayersBackend::LAYERS_OPENGL, + XRE_GetProcessType(), + GetMaxTextureSize(), + mFBOTextureTarget == LOCAL_GL_TEXTURE_2D, + SupportsPartialTextureUpdate()); + return result; + } + + virtual already_AddRefed<CompositingRenderTarget> + CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override; + + virtual already_AddRefed<CompositingRenderTarget> + CreateRenderTargetFromSource(const gfx::IntRect &aRect, + const CompositingRenderTarget *aSource, + const gfx::IntPoint &aSourcePoint) override; + + virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override; + virtual CompositingRenderTarget* GetCurrentRenderTarget() const override; + + virtual 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; + + virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle, + const gfx::IntRect& aClipRect, + const EffectChain& aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect) override; + + virtual void EndFrame() override; + virtual void EndFrameForExternalComposition(const gfx::Matrix& aTransform) override; + + virtual bool SupportsPartialTextureUpdate() override; + + virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override + { + if (!mGLContext) + return false; + int32_t maxSize = GetMaxTextureSize(); + return aSize <= gfx::IntSize(maxSize, maxSize); + } + + virtual int32_t GetMaxTextureSize() const override; + + /** + * Set the size of the EGL surface we're rendering to, if we're rendering to + * an EGL surface. + */ + virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override; + + virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override { + mRenderOffset = aOffset; + } + + virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override; + +#ifdef MOZ_DUMP_PAINTING + virtual const char* Name() const override { return "OGL"; } +#endif // MOZ_DUMP_PAINTING + + virtual LayersBackend GetBackendType() const override { + return LayersBackend::LAYERS_OPENGL; + } + + virtual void Pause() override; + virtual bool Resume() override; + + virtual bool HasImageHostOverlays() override + { + return false; + } + + virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) override + { + } + + virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) override + { + } + + GLContext* gl() const { 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; + } + + GLBlitTextureImageHelper* BlitTextureImageHelper(); + + /** + * The compositor provides with temporary textures for use with direct + * textruing like gralloc texture. + * Doing so lets us use gralloc the way it has been designed to be used + * (see https://wiki.mozilla.org/Platform/GFX/Gralloc) + */ + GLuint GetTemporaryTexture(GLenum aTarget, GLenum aUnit); + + const gfx::Matrix4x4& GetProjMatrix() const { + return mProjMatrix; + } + + void SetProjMatrix(const gfx::Matrix4x4& aProjMatrix) { + mProjMatrix = aProjMatrix; + } + + const gfx::IntSize GetDestinationSurfaceSize() const { + return gfx::IntSize (mSurfaceSize.width, mSurfaceSize.height); + } + + const ScreenPoint& GetScreenRenderOffset() const { + return mRenderOffset; + } + +private: + template<typename Geometry> + void DrawGeometry(const Geometry& aGeometry, + const gfx::IntRect& aClipRect, + const EffectChain &aEffectChain, + gfx::Float aOpacity, + const gfx::Matrix4x4& aTransform, + const gfx::Rect& aVisibleRect); + + void PrepareViewport(CompositingRenderTargetOGL *aRenderTarget); + + /** Widget associated with this compositor */ + LayoutDeviceIntSize mWidgetSize; + RefPtr<GLContext> mGLContext; + UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper; + gfx::Matrix4x4 mProjMatrix; + + /** The size of the surface we are rendering to */ + gfx::IntSize mSurfaceSize; + + ScreenPoint mRenderOffset; + + already_AddRefed<mozilla::gl::GLContext> CreateContext(); + + /** Texture target to use for FBOs */ + GLenum mFBOTextureTarget; + + /** Currently bound render target */ + RefPtr<CompositingRenderTargetOGL> mCurrentRenderTarget; +#ifdef DEBUG + CompositingRenderTargetOGL* mWindowRenderTarget; +#endif + + /** + * 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; + + 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; + + /* + * Clear aRect on current render target. + */ + virtual void ClearRect(const gfx::Rect& aRect) override; + + /* Start a new frame. If aClipRectIn is null and aClipRectOut is non-null, + * sets *aClipRectOut to the screen dimensions. + */ + virtual void BeginFrame(const nsIntRegion& aInvalidRegion, + const gfx::IntRect *aClipRectIn, + const gfx::IntRect& aRenderBounds, + const nsIntRegion& aOpaqueRegion, + gfx::IntRect *aClipRectOut = nullptr, + gfx::IntRect *aRenderBoundsOut = nullptr) override; + + ShaderConfigOGL GetShaderConfigFor(Effect *aEffect, + MaskType aMask = MaskType::MaskNone, + gfx::CompositionOp aOp = gfx::CompositionOp::OP_OVER, + bool aColorMatrix = false, + bool aDEAAEnabled = false) const; + + ShaderProgramOGL* GetShaderProgramFor(const ShaderConfigOGL &aConfig); + + void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig, + const gfx::Rect&) + { + aConfig.SetDynamicGeometry(false); + } + + void ApplyPrimitiveConfig(ShaderConfigOGL& aConfig, + const 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 ActivateProgram(ShaderProgramOGL *aProg); + + 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, + const gfx::Rect& aTextureRect = + gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f)); + + void BindAndDrawGeometry(ShaderProgramOGL* aProgram, + const gfx::TexturedTriangle& aTriangle, + const gfx::Rect& aTextureRect = + gfx::Rect(0.0f, 0.0f, 1.0f, 1.0f)); + + void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg, + const gfx::Rect& aRect, + const gfx::Rect& aTexCoordRect, + TextureSource *aTexture); + + void BindAndDrawGeometryWithTextureRect(ShaderProgramOGL *aProg, + const gfx::TexturedTriangle& aTriangle, + 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); + + /** + * Bind the texture behind the current render target as the backdrop for a + * mix-blend shader. + */ + void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit); + + /** + * Copies the content of our backbuffer to the set transaction target. + * Does not restore the target FBO, so only call from EndFrame. + */ + 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; } + + RefPtr<CompositorTexturePoolOGL> mTexturePool; + + ContextStateTrackerOGL mContextStateTracker; + + 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; + + ShaderProgramOGL *mCurrentProgram; +}; + +} // namespace layers +} // namespace mozilla + +#endif /* MOZILLA_GFX_COMPOSITOROGL_H */ diff --git a/gfx/layers/opengl/EGLImageHelpers.cpp b/gfx/layers/opengl/EGLImageHelpers.cpp new file mode 100644 index 0000000000..4a720302f0 --- /dev/null +++ b/gfx/layers/opengl/EGLImageHelpers.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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, + }; + + bool hasCropRect = (aCropSize.width != 0 && aCropSize.height != 0); + EGLint* usedAttrs = attrs; + if (hasCropRect && sEGLLibrary.IsExtensionSupported(GLLibraryEGL::EGL_ANDROID_image_crop)) { + usedAttrs = cropAttrs; + } + + return sEGLLibrary.fCreateImage(sEGLLibrary.Display(), + EGL_NO_CONTEXT, + LOCAL_EGL_NATIVE_BUFFER_ANDROID, + aBuffer, usedAttrs); +} + +void +EGLImageDestroy(GLContext* aGL, EGLImage aImage) +{ + sEGLLibrary.fDestroyImage(sEGLLibrary.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..3e03a499ca --- /dev/null +++ b/gfx/layers/opengl/EGLImageHelpers.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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/GLBlitTextureImageHelper.cpp b/gfx/layers/opengl/GLBlitTextureImageHelper.cpp new file mode 100644 index 0000000000..8c6ac17031 --- /dev/null +++ b/gfx/layers/opengl/GLBlitTextureImageHelper.cpp @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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 "GLBlitTextureImageHelper.h" +#include "GLUploadHelpers.h" +#include "DecomposeIntoNoRepeatTriangles.h" +#include "GLContext.h" +#include "GLTextureImage.h" +#include "ScopedGLHelpers.h" +#include "nsRect.h" +#include "gfx2DGlue.h" +#include "gfxUtils.h" +#include "CompositorOGL.h" +#include "mozilla/gfx/Point.h" + +using namespace mozilla::gl; + +namespace mozilla { +namespace layers { + +GLBlitTextureImageHelper::GLBlitTextureImageHelper(CompositorOGL* aCompositor) + : mCompositor(aCompositor) + , mBlitProgram(0) + , mBlitFramebuffer(0) + +{ +} + +GLBlitTextureImageHelper::~GLBlitTextureImageHelper() +{ + GLContext *gl = mCompositor->gl(); + // Likely used by OGL Layers. + gl->fDeleteProgram(mBlitProgram); + gl->fDeleteFramebuffers(1, &mBlitFramebuffer); +} + +void +GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const gfx::IntRect& aSrcRect, + TextureImage *aDst, const gfx::IntRect& aDstRect) +{ + GLContext *gl = mCompositor->gl(); + + if (!aSrc || !aDst || aSrcRect.IsEmpty() || aDstRect.IsEmpty()) + return; + + int savedFb = 0; + gl->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb); + + ScopedGLState scopedScissorTestState(gl, LOCAL_GL_SCISSOR_TEST, false); + ScopedGLState scopedBlendState(gl, LOCAL_GL_BLEND, false); + + // 2.0 means scale up by two + float blitScaleX = float(aDstRect.width) / float(aSrcRect.width); + float blitScaleY = float(aDstRect.height) / float(aSrcRect.height); + + // We start iterating over all destination tiles + aDst->BeginBigImageIteration(); + do { + // calculate portion of the tile that is going to be painted to + gfx::IntRect dstSubRect; + gfx::IntRect dstTextureRect = aDst->GetTileRect(); + dstSubRect.IntersectRect(aDstRect, dstTextureRect); + + // this tile is not part of the destination rectangle aDstRect + if (dstSubRect.IsEmpty()) + continue; + + // (*) transform the rect of this tile into the rectangle defined by aSrcRect... + gfx::IntRect dstInSrcRect(dstSubRect); + dstInSrcRect.MoveBy(-aDstRect.TopLeft()); + // ...which might be of different size, hence scale accordingly + dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY); + dstInSrcRect.MoveBy(aSrcRect.TopLeft()); + + SetBlitFramebufferForDestTexture(aDst->GetTextureID()); + UseBlitProgram(); + + aSrc->BeginBigImageIteration(); + // now iterate over all tiles in the source Image... + do { + // calculate portion of the source tile that is in the source rect + gfx::IntRect srcSubRect; + gfx::IntRect srcTextureRect = aSrc->GetTileRect(); + srcSubRect.IntersectRect(aSrcRect, srcTextureRect); + + // this tile is not part of the source rect + if (srcSubRect.IsEmpty()) { + continue; + } + // calculate intersection of source rect with destination rect + srcSubRect.IntersectRect(srcSubRect, dstInSrcRect); + // this tile does not overlap the current destination tile + if (srcSubRect.IsEmpty()) { + continue; + } + // We now have the intersection of + // the current source tile + // and the desired source rectangle + // and the destination tile + // and the desired destination rectange + // in destination space. + // We need to transform this back into destination space, inverting the transform from (*) + gfx::IntRect srcSubInDstRect(srcSubRect); + srcSubInDstRect.MoveBy(-aSrcRect.TopLeft()); + srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY); + srcSubInDstRect.MoveBy(aDstRect.TopLeft()); + + // we transform these rectangles to be relative to the current src and dst tiles, respectively + gfx::IntSize srcSize = srcTextureRect.Size(); + gfx::IntSize dstSize = dstTextureRect.Size(); + srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y); + srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y); + + float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f; + float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f; + float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f; + float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f; + ScopedViewportRect autoViewportRect(gl, 0, 0, dstSize.width, dstSize.height); + + RectTriangles rects; + + gfx::IntSize realTexSize = srcSize; + if (!CanUploadNonPowerOfTwo(gl)) { + realTexSize = gfx::IntSize(RoundUpPow2(srcSize.width), + RoundUpPow2(srcSize.height)); + } + + if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) { + rects.addRect(/* dest rectangle */ + dx0, dy0, dx1, dy1, + /* tex coords */ + srcSubRect.x / float(realTexSize.width), + srcSubRect.y / float(realTexSize.height), + srcSubRect.XMost() / float(realTexSize.width), + srcSubRect.YMost() / float(realTexSize.height)); + } else { + DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects); + + // now put the coords into the d[xy]0 .. d[xy]1 coordinate space + // from the 0..1 that it comes out of decompose + InfallibleTArray<RectTriangles::coord>& coords = rects.vertCoords(); + + for (unsigned int i = 0; i < coords.Length(); ++i) { + coords[i].x = (coords[i].x * (dx1 - dx0)) + dx0; + coords[i].y = (coords[i].y * (dy1 - dy0)) + dy0; + } + } + + ScopedBindTextureUnit autoTexUnit(gl, LOCAL_GL_TEXTURE0); + ScopedBindTexture autoTex(gl, aSrc->GetTextureID()); + ScopedVertexAttribPointer autoAttrib0(gl, 0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.vertCoords().Elements()); + ScopedVertexAttribPointer autoAttrib1(gl, 1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0, rects.texCoords().Elements()); + + gl->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements()); + + } while (aSrc->NextTile()); + } while (aDst->NextTile()); + + // unbind the previous texture from the framebuffer + SetBlitFramebufferForDestTexture(0); + + gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb); +} + +void +GLBlitTextureImageHelper::SetBlitFramebufferForDestTexture(GLuint aTexture) +{ + GLContext *gl = mCompositor->gl(); + if (!mBlitFramebuffer) { + gl->fGenFramebuffers(1, &mBlitFramebuffer); + } + + gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer); + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_COLOR_ATTACHMENT0, + LOCAL_GL_TEXTURE_2D, + aTexture, + 0); + + GLenum result = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) { + nsAutoCString msg; + msg.AppendLiteral("Framebuffer not complete -- error 0x"); + msg.AppendInt(result, 16); + // Note: if you are hitting this, it is likely that + // your texture is not texture complete -- that is, you + // allocated a texture name, but didn't actually define its + // size via a call to TexImage2D. + NS_RUNTIMEABORT(msg.get()); + } +} + +void +GLBlitTextureImageHelper::UseBlitProgram() +{ + // XXX: GLBlitTextureImageHelper doesn't use ShaderProgramOGL + // so we need to Reset the program + mCompositor->ResetProgram(); + + GLContext *gl = mCompositor->gl(); + if (mBlitProgram) { + gl->fUseProgram(mBlitProgram); + return; + } + + mBlitProgram = gl->fCreateProgram(); + + GLuint shaders[2]; + shaders[0] = gl->fCreateShader(LOCAL_GL_VERTEX_SHADER); + shaders[1] = gl->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); + + const char *blitVSSrc = + "attribute vec2 aVertex;" + "attribute vec2 aTexCoord;" + "varying vec2 vTexCoord;" + "void main() {" + " vTexCoord = aTexCoord;" + " gl_Position = vec4(aVertex, 0.0, 1.0);" + "}"; + const char *blitFSSrc = "#ifdef GL_ES\nprecision mediump float;\n#endif\n" + "uniform sampler2D uSrcTexture;" + "varying vec2 vTexCoord;" + "void main() {" + " gl_FragColor = texture2D(uSrcTexture, vTexCoord);" + "}"; + + gl->fShaderSource(shaders[0], 1, (const GLchar**) &blitVSSrc, nullptr); + gl->fShaderSource(shaders[1], 1, (const GLchar**) &blitFSSrc, nullptr); + + for (int i = 0; i < 2; ++i) { + GLint success, len = 0; + + gl->fCompileShader(shaders[i]); + gl->fGetShaderiv(shaders[i], LOCAL_GL_COMPILE_STATUS, &success); + NS_ASSERTION(success, "Shader compilation failed!"); + + if (!success) { + nsAutoCString log; + gl->fGetShaderiv(shaders[i], LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len); + log.SetCapacity(len); + gl->fGetShaderInfoLog(shaders[i], len, (GLint*) &len, (char*) log.BeginWriting()); + log.SetLength(len); + + printf_stderr("Shader %d compilation failed:\n%s\n", i, log.get()); + return; + } + + gl->fAttachShader(mBlitProgram, shaders[i]); + gl->fDeleteShader(shaders[i]); + } + + gl->fBindAttribLocation(mBlitProgram, 0, "aVertex"); + gl->fBindAttribLocation(mBlitProgram, 1, "aTexCoord"); + + gl->fLinkProgram(mBlitProgram); + + GLint success, len = 0; + gl->fGetProgramiv(mBlitProgram, LOCAL_GL_LINK_STATUS, &success); + NS_ASSERTION(success, "Shader linking failed!"); + + if (!success) { + nsAutoCString log; + gl->fGetProgramiv(mBlitProgram, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len); + log.SetCapacity(len); + gl->fGetProgramInfoLog(mBlitProgram, len, (GLint*) &len, (char*) log.BeginWriting()); + log.SetLength(len); + + printf_stderr("Program linking failed:\n%s\n", log.get()); + return; + } + + gl->fUseProgram(mBlitProgram); + gl->fUniform1i(gl->fGetUniformLocation(mBlitProgram, "uSrcTexture"), 0); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/opengl/GLBlitTextureImageHelper.h b/gfx/layers/opengl/GLBlitTextureImageHelper.h new file mode 100644 index 0000000000..91cb1bf857 --- /dev/null +++ b/gfx/layers/opengl/GLBlitTextureImageHelper.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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 GLBLITTEXTUREIMAGEHELPER_H_ +#define GLBLITTEXTUREIMAGEHELPER_H_ + +#include "mozilla/Attributes.h" +#include "GLContextTypes.h" +#include "GLConsts.h" +#include "mozilla/gfx/Rect.h" + +namespace mozilla { +namespace gl { + class GLContext; + class TextureImage; +} // namespace gl +namespace layers { + +class CompositorOGL; + +class GLBlitTextureImageHelper final +{ + // The GLContext is the sole owner of the GLBlitTextureImageHelper. + CompositorOGL* mCompositor; + + // lazy-initialized things + GLuint mBlitProgram, mBlitFramebuffer; + void UseBlitProgram(); + void SetBlitFramebufferForDestTexture(GLuint aTexture); + +public: + + explicit GLBlitTextureImageHelper(CompositorOGL *gl); + ~GLBlitTextureImageHelper(); + + /** + * Copy a rectangle from one TextureImage into another. The + * source and destination are given in integer coordinates, and + * will be converted to texture coordinates. + * + * For the source texture, the wrap modes DO apply -- it's valid + * to use REPEAT or PAD and expect appropriate behaviour if the source + * rectangle extends beyond its bounds. + * + * For the destination texture, the wrap modes DO NOT apply -- the + * destination will be clipped by the bounds of the texture. + * + * Note: calling this function will cause the following OpenGL state + * to be changed: + * + * - current program + * - framebuffer binding + * - viewport + * - blend state (will be enabled at end) + * - scissor state (will be enabled at end) + * - vertex attrib 0 and 1 (pointer and enable state [enable state will be disabled at exit]) + * - array buffer binding (will be 0) + * - active texture (will be 0) + * - texture 0 binding + */ + void BlitTextureImage(gl::TextureImage *aSrc, const gfx::IntRect& aSrcRect, + gl::TextureImage *aDst, const gfx::IntRect& aDstRect); +}; + +} // namespace layers +} // namespace mozilla + +#endif // GLBLITTEXTUREIMAGEHELPER_H_ diff --git a/gfx/layers/opengl/GLManager.cpp b/gfx/layers/opengl/GLManager.cpp new file mode 100644 index 0000000000..e4c0b361fb --- /dev/null +++ b/gfx/layers/opengl/GLManager.cpp @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 "GLManager.h" +#include "CompositorOGL.h" // for CompositorOGL +#include "GLContext.h" // for GLContext +#include "mozilla/Attributes.h" // for override +#include "mozilla/RefPtr.h" // for RefPtr +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/LayersTypes.h" +#include "mozilla/mozalloc.h" // for operator new, etc + +using namespace mozilla::gl; + +namespace mozilla { +namespace layers { + +class GLManagerCompositor : public GLManager +{ +public: + explicit GLManagerCompositor(CompositorOGL* aCompositor) + : mImpl(aCompositor) + {} + + virtual GLContext* gl() const override + { + return mImpl->gl(); + } + + virtual void ActivateProgram(ShaderProgramOGL *aProg) override + { + mImpl->ActivateProgram(aProg); + } + + virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) override + { + ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(aTarget, aFormat); + return mImpl->GetShaderProgramFor(config); + } + + virtual const gfx::Matrix4x4& GetProjMatrix() const override + { + return mImpl->GetProjMatrix(); + } + + virtual void BindAndDrawQuad(ShaderProgramOGL *aProg, + const gfx::Rect& aLayerRect, + const gfx::Rect& aTextureRect) override + { + mImpl->BindAndDrawQuad(aProg, aLayerRect, aTextureRect); + } + +private: + RefPtr<CompositorOGL> mImpl; +}; + +/* static */ GLManager* +GLManager::CreateGLManager(LayerManagerComposite* aManager) +{ + if (aManager && aManager->GetCompositor()->GetBackendType() == LayersBackend::LAYERS_OPENGL) { + return new GLManagerCompositor(aManager->GetCompositor()->AsCompositorOGL()); + } + return nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/opengl/GLManager.h b/gfx/layers/opengl/GLManager.h new file mode 100644 index 0000000000..7c872758e9 --- /dev/null +++ b/gfx/layers/opengl/GLManager.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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_GLMANAGER_H +#define MOZILLA_GFX_GLMANAGER_H + +#include "mozilla/gfx/Types.h" // for SurfaceFormat +#include "OGLShaderProgram.h" + +namespace mozilla { +namespace gl { +class GLContext; +} // namespace gl + +namespace layers { + +class LayerManagerComposite; + +/** + * Minimal interface to allow widgets to draw using OpenGL. Abstracts + * CompositorOGL. Call CreateGLManager with a LayerManagerComposite + * backed by a CompositorOGL. + */ +class GLManager +{ +public: + static GLManager* CreateGLManager(LayerManagerComposite* aManager); + + virtual ~GLManager() {} + + virtual gl::GLContext* gl() const = 0; + virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) = 0; + virtual void ActivateProgram(ShaderProgramOGL* aPrg) = 0; + virtual const gfx::Matrix4x4& GetProjMatrix() const = 0; + virtual void BindAndDrawQuad(ShaderProgramOGL *aProg, const gfx::Rect& aLayerRect, + const gfx::Rect& aTextureRect) = 0; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp new file mode 100644 index 0000000000..dd522e6503 --- /dev/null +++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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() +{} + +// 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, + 1.0, + aFormat == SurfaceFormat::B8G8R8A8); + if (!surf) { + return nullptr; + } + + return Create(surf, aBackend); +} + +bool +MacIOSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) +{ + aOutDescriptor = SurfaceDescriptorMacIOSurface(mSurface->GetIOSurfaceID(), + mSurface->GetContentsScaleFactor(), + !mSurface->HasAlpha()); + return true; +} + +void +MacIOSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const +{ + aInfo.size = gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight()); + aInfo.format = mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8; + aInfo.hasIntermediateBuffer = false; + 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..21e4459537 --- /dev/null +++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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(); + + virtual void FillInfo(TextureData::Info& aInfo) const override; + + virtual bool Lock(OpenMode) override; + + virtual void Unlock() override; + + virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override; + + virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override; + + virtual void Deallocate(LayersIPCChannel*) override; + + virtual void Forget(LayersIPCChannel*) override; + + virtual 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..05f8cf38f1 --- /dev/null +++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/MacIOSurface.h" +#include "GLContextCGL.h" + +namespace mozilla { +namespace layers { + +MacIOSurfaceTextureHostOGL::MacIOSurfaceTextureHostOGL(TextureFlags aFlags, + const SurfaceDescriptorMacIOSurface& aDescriptor) + : TextureHost(aFlags) +{ + MOZ_COUNT_CTOR(MacIOSurfaceTextureHostOGL); + mSurface = MacIOSurface::LookupSurface(aDescriptor.surfaceId(), + aDescriptor.scaleFactor(), + !aDescriptor.isOpaque()); +} + +MacIOSurfaceTextureHostOGL::~MacIOSurfaceTextureHostOGL() +{ + MOZ_COUNT_DTOR(MacIOSurfaceTextureHostOGL); +} + +GLTextureSource* +MacIOSurfaceTextureHostOGL::CreateTextureSourceForPlane(size_t aPlane) +{ + GLuint textureHandle; + gl::GLContext* gl = mCompositor->gl(); + gl->fGenTextures(1, &textureHandle); + gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle); + gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + + mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext(), aPlane); + + return new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB, + gfx::IntSize(mSurface->GetDevicePixelWidth(aPlane), + mSurface->GetDevicePixelHeight(aPlane)), + // XXX: This isn't really correct (but isn't used), we should be using the + // format of the individual plane, not of the whole buffer. + mSurface->GetFormat()); +} + +bool +MacIOSurfaceTextureHostOGL::Lock() +{ + if (!gl() || !gl()->MakeCurrent() || !mSurface) { + return false; + } + + if (!mTextureSource) { + mTextureSource = CreateTextureSourceForPlane(0); + + RefPtr<TextureSource> prev = mTextureSource; + for (size_t i = 1; i < mSurface->GetPlaneCount(); i++) { + RefPtr<TextureSource> next = CreateTextureSourceForPlane(i); + prev->SetNextSibling(next); + prev = next; + } + } + return true; +} + +void +MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + mTextureSource = nullptr; + mCompositor = nullptr; + return; + } + + if (mCompositor != glCompositor) { + // Cannot share GL texture identifiers across compositors. + mTextureSource = nullptr; + } + mCompositor = glCompositor; +} + +gfx::SurfaceFormat +MacIOSurfaceTextureHostOGL::GetFormat() const { + return mSurface->GetFormat(); +} + +gfx::SurfaceFormat +MacIOSurfaceTextureHostOGL::GetReadFormat() const { + 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 mCompositor ? mCompositor->gl() : nullptr; +} + +MacIOSurfaceTextureSourceOGL::MacIOSurfaceTextureSourceOGL( + CompositorOGL* aCompositor, + MacIOSurface* aSurface) + : mCompositor(aCompositor) + , mSurface(aSurface) +{ + MOZ_ASSERT(aCompositor); + MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceOGL); +} + +MacIOSurfaceTextureSourceOGL::~MacIOSurfaceTextureSourceOGL() +{ + MOZ_COUNT_DTOR(MacIOSurfaceTextureSourceOGL); +} + +gfx::IntSize +MacIOSurfaceTextureSourceOGL::GetSize() const +{ + return gfx::IntSize(mSurface->GetDevicePixelWidth(), + mSurface->GetDevicePixelHeight()); +} + +gfx::SurfaceFormat +MacIOSurfaceTextureSourceOGL::GetFormat() const +{ + return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8 + : gfx::SurfaceFormat::R8G8B8X8; +} + +void +MacIOSurfaceTextureSourceOGL::BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) +{ + gl::GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + NS_WARNING("Trying to bind a texture without a working GLContext"); + return; + } + GLuint tex = mCompositor->GetTemporaryTexture(GetTextureTarget(), aTextureUnit); + + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex); + mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext()); + ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB); +} + +void +MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertGLCompositor(aCompositor); + if (mCompositor && mNextSibling) { + mNextSibling->SetCompositor(aCompositor); + } +} + +gl::GLContext* +MacIOSurfaceTextureSourceOGL::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h new file mode 100644 index 0000000000..55e2f5019a --- /dev/null +++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 "mozilla/layers/CompositorOGL.h" +#include "mozilla/layers/TextureHostOGL.h" + +class MacIOSurface; + +namespace mozilla { +namespace layers { + +/** + * A texture source meant for use with MacIOSurfaceTextureHostOGL. + * + * It does not own any GL texture, and attaches its shared handle to one of + * the compositor's temporary textures when binding. + */ +class MacIOSurfaceTextureSourceOGL : public TextureSource + , public TextureSourceOGL +{ +public: + MacIOSurfaceTextureSourceOGL(CompositorOGL* aCompositor, + MacIOSurface* aSurface); + virtual ~MacIOSurfaceTextureSourceOGL(); + + virtual const char* Name() const override { return "MacIOSurfaceTextureSourceOGL"; } + + virtual TextureSourceOGL* AsSourceOGL() override { return this; } + + virtual void BindTexture(GLenum activetex, + gfx::SamplingFilter aSamplingFilter) override; + + virtual bool IsValid() const override { return !!gl(); } + + virtual gfx::IntSize GetSize() const override; + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual GLenum GetTextureTarget() const override { return LOCAL_GL_TEXTURE_RECTANGLE_ARB; } + + virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; } + + // MacIOSurfaceTextureSourceOGL doesn't own any gl texture + virtual void DeallocateDeviceData() override {} + + virtual void SetCompositor(Compositor* aCompositor) override; + + gl::GLContext* gl() const; + +protected: + RefPtr<CompositorOGL> mCompositor; + RefPtr<MacIOSurface> mSurface; +}; + +/** + * 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 + virtual void DeallocateDeviceData() override {} + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual Compositor* GetCompositor() override { return mCompositor; } + + virtual bool Lock() override; + + virtual gfx::SurfaceFormat GetFormat() const override; + virtual gfx::SurfaceFormat GetReadFormat() const override; + + virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override + { + aTexture = mTextureSource; + return !!aTexture; + } + + virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override + { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + gl::GLContext* gl() const; + + virtual gfx::IntSize GetSize() const override; + +#ifdef MOZ_LAYERS_HAVE_LOG + virtual const char* Name() override { return "MacIOSurfaceTextureHostOGL"; } +#endif + +protected: + GLTextureSource* CreateTextureSourceForPlane(size_t aPlane); + + RefPtr<CompositorOGL> mCompositor; + RefPtr<GLTextureSource> mTextureSource; + RefPtr<MacIOSurface> mSurface; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H diff --git a/gfx/layers/opengl/OGLShaderProgram.cpp b/gfx/layers/opengl/OGLShaderProgram.cpp new file mode 100644 index 0000000000..c06dc52ddd --- /dev/null +++ b/gfx/layers/opengl/OGLShaderProgram.cpp @@ -0,0 +1,974 @@ +/* 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 ostringstream +#include "gfxEnv.h" +#include "gfxRect.h" // for gfxRect +#include "gfxUtils.h" +#include "mozilla/DebugOnly.h" // for DebugOnly +#include "mozilla/layers/Compositor.h" // for BlendOpIsMixBlendMode +#include "nsAString.h" +#include "nsString.h" // for nsAutoCString +#include "Layers.h" +#include "GLContext.h" + +namespace mozilla { +namespace layers { + +using namespace std; + +#define GAUSSIAN_KERNEL_HALF_WIDTH 11 +#define GAUSSIAN_KERNEL_STEP 0.2 + +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", + "uBlackTexture", + "uWhiteTexture", + "uMaskTexture", + "uBackdropTexture", + "uRenderColor", + "uTexCoordMultiplier", + "uCbCrTexCoordMultiplier", + "uTexturePass2", + "uColorMatrix", + "uColorMatrixVector", + "uBlurRadius", + "uBlurOffset", + "uBlurAlpha", + "uBlurGaussianKernel", + "uSSEdges", + "uViewportSize", + "uVisibleCenter", + "uYuvColorMatrix", + 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::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::SetNV12(bool aEnabled) +{ + SetFeature(ENABLE_TEXTURE_NV12, aEnabled); + MOZ_ASSERT(!(mFeatures & ENABLE_TEXTURE_YCBCR)); +} + +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; + 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; + vs << "attribute vec4 aCoord;" << endl; + result.mAttributes.AppendElement(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(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"; + } + + 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; + } else if (aConfig.mFeatures & ENABLE_TEXTURE_NV12) { + fs << "uniform " << sampler2D << " uYTexture;" << endl; + fs << "uniform " << sampler2D << " uCbTexture;" << 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 sampler2D 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; + 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; + fs << " COLOR_PRECISION float cr = " << texture2D << "(uCbTexture, coord).a;" << endl; + } + } + + fs << " y = y - 0.06275;" << endl; + fs << " cb = cb - 0.50196;" << endl; + fs << " cr = cr - 0.50196;" << endl; + fs << " vec3 yuv = vec3(y, cb, cr);" << 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; + fs << " COLOR_PRECISION float mask = texture2D(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"); + + 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::DebugShaders()) +#endif + ) + { + nsAutoCString log; + log.SetCapacity(len); + mGL->fGetShaderInfoLog(sh, len, (GLint*) &len, (char*) log.BeginWriting()); + log.SetLength(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 (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::DebugShaders()) +#endif + ) + { + nsAutoCString log; + log.SetCapacity(len); + mGL->fGetProgramInfoLog(result, len, (GLint*) &len, (char*) log.BeginWriting()); + log.SetLength(len); + + 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::SetBlurRadius(float aRX, float aRY) +{ + float f[] = {aRX, aRY}; + SetUniform(KnownUniform::BlurRadius, 2, f); + + float gaussianKernel[GAUSSIAN_KERNEL_HALF_WIDTH]; + float sum = 0.0f; + for (int i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) { + float x = i * GAUSSIAN_KERNEL_STEP; + float sigma = 1.0f; + gaussianKernel[i] = exp(-x * x / (2 * sigma * sigma)) / sqrt(2 * M_PI * sigma * sigma); + sum += gaussianKernel[i] * (i == 0 ? 1 : 2); + } + for (int i = 0; i < GAUSSIAN_KERNEL_HALF_WIDTH; i++) { + gaussianKernel[i] /= sum; + } + SetArrayUniform(KnownUniform::BlurGaussianKernel, GAUSSIAN_KERNEL_HALF_WIDTH, gaussianKernel); +} + +void +ShaderProgramOGL::SetYUVColorSpace(YUVColorSpace aYUVColorSpace) +{ + float* yuvToRgb = gfxUtils::Get3x3YuvColorMatrix(aYUVColorSpace); + SetMatrix3fvUniform(KnownUniform::YuvColorMatrix, yuvToRgb); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/opengl/OGLShaderProgram.h b/gfx/layers/opengl/OGLShaderProgram.h new file mode 100644 index 0000000000..ff4fb825f5 --- /dev/null +++ b/gfx/layers/opengl/OGLShaderProgram.h @@ -0,0 +1,621 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "GLContext.h" // for fast inlines of glUniform* +#include "gfxTypes.h" +#include "ImageTypes.h" +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/Pair.h" // for Pair +#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" + +#include <string> + +namespace mozilla { +namespace layers { + +class Layer; + +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 +}; + +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, + BlackTexture, + WhiteTexture, + MaskTexture, + BackdropTexture, + RenderColor, + TexCoordMultiplier, + CbCrTexCoordMultiplier, + TexturePass2, + ColorMatrix, + ColorMatrixVector, + BlurRadius, + BlurOffset, + BlurAlpha, + BlurGaussianKernel, + SSEdges, + ViewportSize, + VisibleCenter, + YuvColorMatrix, + + 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; + } + + NS_NOTREACHED("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), + mCompositionOp(gfx::CompositionOp::OP_OVER) + {} + + void SetRenderColor(bool aEnabled); + void SetTextureTarget(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); + + bool operator< (const ShaderConfigOGL& other) const { + return mFeatures < other.mFeatures || + (mFeatures == other.mFeatures && + (int)mCompositionOp < (int)other.mCompositionOp); + } + +public: + void SetFeature(int aBitmask, bool aState) { + if (aState) + mFeatures |= aBitmask; + else + mFeatures &= (~aBitmask); + } + + int mFeatures; + 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; +} + +/** + * 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 + nsTArray<Pair<nsCString, GLuint>> mAttributes; + + KnownUniform mUniforms[KnownUniform::KnownUniformCount]; + nsTArray<const char *> mDefines; + size_t mTextureCount; + + ProgramProfileOGL() : + mTextureCount(0) + {} + + private: + static void BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs); +}; + + +#if defined(DEBUG) +#define CHECK_CURRENT_PROGRAM 1 +#define ASSERT_THIS_PROGRAM \ + do { \ + GLuint currentProgram; \ + mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram); \ + MOZ_ASSERT(currentProgram == mProgram, \ + "SetUniform with wrong program active!"); \ + } while (0) +#else +#define ASSERT_THIS_PROGRAM \ + do { } while (0) +#endif + +/** + * 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 SetMaskLayerTransform(const gfx::Matrix4x4& aMatrix) { + SetMatrixUniform(KnownUniform::MaskTransform, aMatrix); + } + + void SetBackdropTransform(const gfx::Matrix4x4& aMatrix) { + SetMatrixUniform(KnownUniform::BackdropTransform, 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 SetBlackTextureUnit(GLint aUnit) { + SetUniform(KnownUniform::BlackTexture, aUnit); + } + + void SetWhiteTextureUnit(GLint aUnit) { + SetUniform(KnownUniform::WhiteTexture, aUnit); + } + + void SetMaskTextureUnit(GLint aUnit) { + SetUniform(KnownUniform::MaskTexture, aUnit); + } + + void SetBackdropTextureUnit(GLint aUnit) { + SetUniform(KnownUniform::BackdropTexture, aUnit); + } + + void SetRenderColor(const gfx::Color& aColor) { + SetUniform(KnownUniform::RenderColor, aColor); + } + + void SetColorMatrix(const gfx::Matrix5x4& aColorMatrix) + { + SetMatrixUniform(KnownUniform::ColorMatrix, &aColorMatrix._11); + SetUniform(KnownUniform::ColorMatrixVector, 4, &aColorMatrix._51); + } + + 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(YUVColorSpace aYUVColorSpace); + + // Set whether we want the component alpha shader to return the color + // vector (pass 1, false) or the alpha vector (pass2, true). With support + // for multiple render targets we wouldn't need two passes here. + void SetTexturePass2(bool aFlag) { + SetUniform(KnownUniform::TexturePass2, aFlag ? 1 : 0); + } + + void SetBlurRadius(float aRX, float aRY); + + void SetBlurAlpha(float aAlpha) { + SetUniform(KnownUniform::BlurAlpha, aAlpha); + } + + void SetBlurOffset(float aOffsetX, float aOffsetY) { + float f[] = {aOffsetX, aOffsetY}; + SetUniform(KnownUniform::BlurOffset, 2, f); + } + + 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::Color& 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: + NS_NOTREACHED("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 SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Matrix4x4& aMatrix) { + SetMatrixUniform(aKnownUniform, &aMatrix._11); + } +}; + +} // 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..78d4e6d9c9 --- /dev/null +++ b/gfx/layers/opengl/TextureClientOGL.cpp @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/Point.h" // for IntSize +#include "GLLibraryEGL.h" + +using namespace mozilla::gl; + +namespace mozilla { +namespace layers { + +class CompositableForwarder; + +//////////////////////////////////////////////////////////////////////// +// EGLImage + +EGLImageTextureData::EGLImageTextureData(EGLImageImage* aImage, gfx::IntSize aSize) +: mImage(aImage) +, mSize(aSize) +{ + MOZ_ASSERT(aImage); +} + +already_AddRefed<TextureClient> +EGLImageTextureData::CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize, + LayersIPCChannel* aAllocator, TextureFlags aFlags) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Can't pass an `EGLImage` between processes."); + + if (!aImage || !XRE_IsParentProcess()) { + return nullptr; + } + + // XXX - This is quite sad and slow. + aFlags |= TextureFlags::DEALLOCATE_CLIENT; + + if (aImage->GetOriginPos() == gl::OriginPos::BottomLeft) { + aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT; + } + + return TextureClient::CreateWithData( + new EGLImageTextureData(aImage, aSize), + aFlags, aAllocator + ); +} + +void +EGLImageTextureData::FillInfo(TextureData::Info& aInfo) const +{ + aInfo.size = mSize; + aInfo.format = gfx::SurfaceFormat::UNKNOWN; + aInfo.hasIntermediateBuffer = false; + aInfo.hasSynchronization = false; + aInfo.supportsMoz2D = false; + aInfo.canExposeMappedData = false; +} + +bool +EGLImageTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) +{ + const bool hasAlpha = true; + aOutDescriptor = + EGLImageDescriptor((uintptr_t)mImage->GetImage(), + (uintptr_t)mImage->GetSync(), + mImage->GetSize(), hasAlpha); + return true; +} + +//////////////////////////////////////////////////////////////////////// +// AndroidSurface + +#ifdef MOZ_WIDGET_ANDROID + +already_AddRefed<TextureClient> +AndroidSurfaceTextureData::CreateTextureClient(AndroidSurfaceTexture* aSurfTex, + gfx::IntSize aSize, + gl::OriginPos aOriginPos, + LayersIPCChannel* aAllocator, + TextureFlags aFlags) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Can't pass an android surfaces between processes."); + + if (!aSurfTex || !XRE_IsParentProcess()) { + return nullptr; + } + + if (aOriginPos == gl::OriginPos::BottomLeft) { + aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT; + } + + return TextureClient::CreateWithData( + new AndroidSurfaceTextureData(aSurfTex, aSize), + aFlags, aAllocator + ); +} + +AndroidSurfaceTextureData::AndroidSurfaceTextureData(AndroidSurfaceTexture* aSurfTex, + gfx::IntSize aSize) + : mSurfTex(aSurfTex) + , mSize(aSize) +{} + +AndroidSurfaceTextureData::~AndroidSurfaceTextureData() +{} + +void +AndroidSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const +{ + aInfo.size = mSize; + aInfo.format = gfx::SurfaceFormat::UNKNOWN; + aInfo.hasIntermediateBuffer = false; + aInfo.hasSynchronization = false; + aInfo.supportsMoz2D = false; + aInfo.canExposeMappedData = false; +} + +bool +AndroidSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) +{ + aOutDescriptor = SurfaceTextureDescriptor((uintptr_t)mSurfTex.get(), + mSize); + return true; +} + +#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..6555f138ad --- /dev/null +++ b/gfx/layers/opengl/TextureClientOGL.h @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// * 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/layers/CompositorTypes.h" +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor +#include "mozilla/layers/TextureClient.h" // for TextureClient, etc +#include "AndroidSurfaceTexture.h" + +namespace mozilla { + +namespace layers { + +class EGLImageTextureData : public TextureData +{ +public: + + static already_AddRefed<TextureClient> + CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize, + LayersIPCChannel* aAllocator, TextureFlags aFlags); + + virtual void FillInfo(TextureData::Info& aInfo) const override; + + virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override; + + virtual void Deallocate(LayersIPCChannel*) override { mImage = nullptr; } + + virtual void Forget(LayersIPCChannel*) override { mImage = nullptr; } + + // Unused functions. + virtual bool Lock(OpenMode) override { return true; } + + virtual void Unlock() override {} + +protected: + EGLImageTextureData(EGLImageImage* aImage, gfx::IntSize aSize); + + RefPtr<EGLImageImage> mImage; + const gfx::IntSize mSize; +}; + +#ifdef MOZ_WIDGET_ANDROID + +class AndroidSurfaceTextureData : public TextureData +{ +public: + static already_AddRefed<TextureClient> + CreateTextureClient(gl::AndroidSurfaceTexture* aSurfTex, + gfx::IntSize aSize, + gl::OriginPos aOriginPos, + LayersIPCChannel* aAllocator, + TextureFlags aFlags); + + ~AndroidSurfaceTextureData(); + + virtual void FillInfo(TextureData::Info& aInfo) const override; + + virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override; + + // Useless functions. + virtual bool Lock(OpenMode) override { return true; } + + virtual void Unlock() override {} + + // Our data is always owned externally. + virtual void Deallocate(LayersIPCChannel*) override {} + +protected: + AndroidSurfaceTextureData(gl::AndroidSurfaceTexture* aSurfTex, gfx::IntSize aSize); + + const RefPtr<gl::AndroidSurfaceTexture> mSurfTex; + const gfx::IntSize mSize; +}; + +#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..854160bc67 --- /dev/null +++ b/gfx/layers/opengl/TextureHostOGL.cpp @@ -0,0 +1,771 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- +* 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 "EGLUtils.h" +#include "GLContext.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/Logging.h" // for gfxCriticalError +#include "mozilla/layers/ISurfaceAllocator.h" +#include "nsRegion.h" // for nsIntRegion +#include "AndroidSurfaceTexture.h" +#include "GfxTexturesReporter.h" // for GfxTexturesReporter +#include "GLBlitTextureImageHelper.h" +#include "GeckoProfiler.h" + +#ifdef XP_MACOSX +#include "mozilla/layers/MacIOSurfaceTextureHostOGL.h" +#endif + +#ifdef GL_PROVIDER_GLX +#include "mozilla/layers/X11TextureHost.h" +#endif + +using namespace mozilla::gl; +using namespace mozilla::gfx; + +namespace mozilla { +namespace layers { + +class Compositor; + +already_AddRefed<TextureHost> +CreateTextureHostOGL(const SurfaceDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) +{ + RefPtr<TextureHost> result; + switch (aDesc.type()) { + case SurfaceDescriptor::TSurfaceDescriptorBuffer: { + result = CreateBackendIndependentTextureHost(aDesc, + aDeallocator, aFlags); + break; + } + +#ifdef MOZ_WIDGET_ANDROID + case SurfaceDescriptor::TSurfaceTextureDescriptor: { + const SurfaceTextureDescriptor& desc = aDesc.get_SurfaceTextureDescriptor(); + result = new SurfaceTextureHost(aFlags, + (AndroidSurfaceTexture*)desc.surfTex(), + desc.size()); + 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 XP_MACOSX + case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: { + const SurfaceDescriptorMacIOSurface& desc = + aDesc.get_SurfaceDescriptorMacIOSurface(); + result = new MacIOSurfaceTextureHostOGL(aFlags, desc); + break; + } +#endif + +#ifdef GL_PROVIDER_GLX + case SurfaceDescriptor::TSurfaceDescriptorX11: { + const auto& desc = aDesc.get_SurfaceDescriptorX11(); + result = new X11TextureHost(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: return nullptr; + } + 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); +} + +bool +TextureImageTextureSourceOGL::Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion, + gfx::IntPoint* aSrcOffset) +{ + GLContext *gl = mCompositor->gl(); + 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); + } + } + + mTexImage->UpdateFromDataSource(aSurface, aDestRegion, aSrcOffset); + + return true; +} + +void +TextureImageTextureSourceOGL::EnsureBuffer(const IntSize& aSize, + gfxContentType aContentType) +{ + if (!mTexImage || + mTexImage->GetSize() != aSize || + mTexImage->GetContentType() != aContentType) { + mTexImage = CreateTextureImage(mCompositor->gl(), + aSize, + aContentType, + LOCAL_GL_CLAMP_TO_EDGE, + FlagsToGLFlags(mFlags)); + } + mTexImage->Resize(aSize); +} + +void +TextureImageTextureSourceOGL::CopyTo(const gfx::IntRect& aSourceRect, + DataTextureSource *aDest, + const gfx::IntRect& aDestRect) +{ + MOZ_ASSERT(aDest->AsSourceOGL(), "Incompatible destination type!"); + TextureImageTextureSourceOGL *dest = + aDest->AsSourceOGL()->AsTextureImageTextureSource(); + MOZ_ASSERT(dest, "Incompatible destination type!"); + + mCompositor->BlitTextureImageHelper()->BlitTextureImage(mTexImage, aSourceRect, + dest->mTexImage, aDestRect); + dest->mTexImage->MarkValid(); +} + +CompositorOGL* AssertGLCompositor(Compositor* aCompositor) +{ + CompositorOGL* compositor = aCompositor ? aCompositor->AsCompositorOGL() + : nullptr; + MOZ_ASSERT(!!compositor); + return compositor; +} + +void +TextureImageTextureSourceOGL::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + DeallocateDeviceData(); + return; + } + if (mCompositor != glCompositor) { + DeallocateDeviceData(); + mCompositor = glCompositor; + } +} + +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(mCompositor->gl(), aSamplingFilter); +} + +//////////////////////////////////////////////////////////////////////// +// GLTextureSource + +GLTextureSource::GLTextureSource(CompositorOGL* aCompositor, + GLuint aTextureHandle, + GLenum aTarget, + gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + bool aExternallyOwned) + : mCompositor(aCompositor) + , mTextureHandle(aTextureHandle) + , mTextureTarget(aTarget) + , mSize(aSize) + , mFormat(aFormat) + , mExternallyOwned(aExternallyOwned) +{ + MOZ_COUNT_CTOR(GLTextureSource); +} + +GLTextureSource::~GLTextureSource() +{ + MOZ_COUNT_DTOR(GLTextureSource); + if (!mExternallyOwned) { + DeleteTextureHandle(); + } +} + +void +GLTextureSource::DeallocateDeviceData() +{ + if (!mExternallyOwned) { + 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); +} + +void +GLTextureSource::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + return; + } + + if (mCompositor && mCompositor != glCompositor) { + gfxCriticalError() << "GLTextureSource does not support changing compositors"; + } + mCompositor = glCompositor; + + if (mNextSibling) { + mNextSibling->SetCompositor(aCompositor); + } +} + +bool +GLTextureSource::IsValid() const +{ + return !!gl() && mTextureHandle != 0; +} + +gl::GLContext* +GLTextureSource::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// SurfaceTextureHost + +#ifdef MOZ_WIDGET_ANDROID + +SurfaceTextureSource::SurfaceTextureSource(CompositorOGL* aCompositor, + AndroidSurfaceTexture* aSurfTex, + gfx::SurfaceFormat aFormat, + GLenum aTarget, + GLenum aWrapMode, + gfx::IntSize aSize) + : mCompositor(aCompositor) + , mSurfTex(aSurfTex) + , mFormat(aFormat) + , mTextureTarget(aTarget) + , mWrapMode(aWrapMode) + , mSize(aSize) +{ +} + +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); + + // SurfaceTexture spams us if there are any existing GL errors, so + // we'll clear them here in order to avoid that. + gl->FlushErrors(); + + mSurfTex->UpdateTexImage(); + + ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget); +} + +void +SurfaceTextureSource::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + DeallocateDeviceData(); + return; + } + if (mCompositor != glCompositor) { + DeallocateDeviceData(); + } + + mCompositor = glCompositor; +} + +bool +SurfaceTextureSource::IsValid() const +{ + return !!gl(); +} + +gl::GLContext* +SurfaceTextureSource::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +gfx::Matrix4x4 +SurfaceTextureSource::GetTextureTransform() +{ + MOZ_ASSERT(mSurfTex); + + gfx::Matrix4x4 ret; + mSurfTex->GetTransformMatrix(ret); + + return ret; +} + +void +SurfaceTextureSource::DeallocateDeviceData() +{ + mSurfTex = nullptr; +} + +//////////////////////////////////////////////////////////////////////// + +SurfaceTextureHost::SurfaceTextureHost(TextureFlags aFlags, + AndroidSurfaceTexture* aSurfTex, + gfx::IntSize aSize) + : TextureHost(aFlags) + , mSurfTex(aSurfTex) + , mSize(aSize) + , mCompositor(nullptr) +{ +} + +SurfaceTextureHost::~SurfaceTextureHost() +{ +} + +gl::GLContext* +SurfaceTextureHost::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +bool +SurfaceTextureHost::Lock() +{ + MOZ_ASSERT(mSurfTex); + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + return false; + } + + if (!mTextureSource) { + gfx::SurfaceFormat format = gfx::SurfaceFormat::R8G8B8A8; + GLenum target = LOCAL_GL_TEXTURE_EXTERNAL; + GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE; + mTextureSource = new SurfaceTextureSource(mCompositor, + mSurfTex, + format, + target, + wrapMode, + mSize); + } + + return NS_SUCCEEDED(mSurfTex->Attach(gl)); +} + +void +SurfaceTextureHost::Unlock() +{ + MOZ_ASSERT(mSurfTex); + mSurfTex->Detach(); +} + +void +SurfaceTextureHost::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + DeallocateDeviceData(); + return; + } + mCompositor = glCompositor; + if (mTextureSource) { + mTextureSource->SetCompositor(glCompositor); + } +} + +gfx::SurfaceFormat +SurfaceTextureHost::GetFormat() const +{ + return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN; +} + +void +SurfaceTextureHost::DeallocateDeviceData() +{ + if (mTextureSource) { + mTextureSource->DeallocateDeviceData(); + } + mSurfTex = nullptr; +} + +#endif // MOZ_WIDGET_ANDROID + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// EGLImage + +EGLImageTextureSource::EGLImageTextureSource(CompositorOGL* aCompositor, + EGLImage aImage, + gfx::SurfaceFormat aFormat, + GLenum aTarget, + GLenum aWrapMode, + gfx::IntSize aSize) + : mCompositor(aCompositor) + , 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; + } + + MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl), + "EGLImage not supported or disabled in runtime"); + + GLuint tex = mCompositor->GetTemporaryTexture(mTextureTarget, aTextureUnit); + + gl->fActiveTexture(aTextureUnit); + gl->fBindTexture(mTextureTarget, tex); + + gl->fEGLImageTargetTexture2D(mTextureTarget, mImage); + + ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, mTextureTarget); +} + +void +EGLImageTextureSource::SetCompositor(Compositor* aCompositor) +{ + mCompositor = AssertGLCompositor(aCompositor); +} + +bool +EGLImageTextureSource::IsValid() const +{ + return !!gl(); +} + +gl::GLContext* +EGLImageTextureSource::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +gfx::Matrix4x4 +EGLImageTextureSource::GetTextureTransform() +{ + gfx::Matrix4x4 ret; + return ret; +} + +//////////////////////////////////////////////////////////////////////// + +EGLImageTextureHost::EGLImageTextureHost(TextureFlags aFlags, + EGLImage aImage, + EGLSync aSync, + gfx::IntSize aSize, + bool hasAlpha) + : TextureHost(aFlags) + , mImage(aImage) + , mSync(aSync) + , mSize(aSize) + , mHasAlpha(hasAlpha) + , mCompositor(nullptr) +{} + +EGLImageTextureHost::~EGLImageTextureHost() +{} + +gl::GLContext* +EGLImageTextureHost::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +bool +EGLImageTextureHost::Lock() +{ + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + return false; + } + + EGLint status = LOCAL_EGL_CONDITION_SATISFIED; + + if (mSync) { + MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)); + status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), mSync, 0, LOCAL_EGL_FOREVER); + } + + if (status != LOCAL_EGL_CONDITION_SATISFIED) { + MOZ_ASSERT(status != 0, + "ClientWaitSync generated an error. Has mSync already been destroyed?"); + return false; + } + + if (!mTextureSource) { + gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 + : gfx::SurfaceFormat::R8G8B8X8; + GLenum target = gl->GetPreferredEGLImageTextureTarget(); + GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE; + mTextureSource = new EGLImageTextureSource(mCompositor, + mImage, + format, + target, + wrapMode, + mSize); + } + + return true; +} + +void +EGLImageTextureHost::Unlock() +{ +} + +void +EGLImageTextureHost::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + mCompositor = nullptr; + mTextureSource = nullptr; + return; + } + mCompositor = glCompositor; + if (mTextureSource) { + mTextureSource->SetCompositor(glCompositor); + } +} + +gfx::SurfaceFormat +EGLImageTextureHost::GetFormat() const +{ + MOZ_ASSERT(mTextureSource); + return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN; +} + +// + +GLTextureHost::GLTextureHost(TextureFlags aFlags, + GLuint aTextureHandle, + GLenum aTarget, + GLsync aSync, + gfx::IntSize aSize, + bool aHasAlpha) + : TextureHost(aFlags) + , mTexture(aTextureHandle) + , mTarget(aTarget) + , mSync(aSync) + , mSize(aSize) + , mHasAlpha(aHasAlpha) + , mCompositor(nullptr) +{} + +GLTextureHost::~GLTextureHost() +{} + +gl::GLContext* +GLTextureHost::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +bool +GLTextureHost::Lock() +{ + GLContext* gl = this->gl(); + if (!gl || !gl->MakeCurrent()) { + return false; + } + + if (mSync) { + if (!gl->MakeCurrent()) { + return false; + } + gl->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED); + gl->fDeleteSync(mSync); + mSync = 0; + } + + if (!mTextureSource) { + gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 + : gfx::SurfaceFormat::R8G8B8X8; + mTextureSource = new GLTextureSource(mCompositor, + mTexture, + mTarget, + mSize, + format, + false /* owned by the client */); + } + + return true; +} +void +GLTextureHost::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (!glCompositor) { + mCompositor = nullptr; + mTextureSource = nullptr; + return; + } + mCompositor = glCompositor; + if (mTextureSource) { + mTextureSource->SetCompositor(glCompositor); + } +} + +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..dd425e768c --- /dev/null +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -0,0 +1,539 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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 "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_WARNING +#include "nsISupportsImpl.h" // for TextureImage::Release, etc +#include "nsRegionFwd.h" // for nsIntRegion +#include "OGLShaderProgram.h" // for ShaderProgramType, etc + +namespace mozilla { +namespace gfx { +class DataSourceSurface; +} // namespace gfx + +namespace gl { +class AndroidSurfaceTexture; +} // namespace gl + +namespace layers { + +class Compositor; +class CompositorOGL; +class TextureImageTextureSourceOGL; +class GLTextureSource; + +inline void ApplySamplingFilterToBoundTexture(gl::GLContext* aGL, + gfx::SamplingFilter aSamplingFilter, + GLuint aTarget = LOCAL_GL_TEXTURE_2D) +{ + 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); +} + +/* + * 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() + : mHasCachedSamplingFilter(false) + {} + + virtual bool IsValid() const = 0; + + virtual void BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) = 0; + + 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) + : mCompositor(aCompositor) + , mFlags(aFlags) + , mIterating(false) + {} + + virtual const char* Name() const override { return "TextureImageTextureSourceOGL"; } + // DataTextureSource + + virtual bool Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion = nullptr, + gfx::IntPoint* aSrcOffset = nullptr) override; + + void EnsureBuffer(const gfx::IntSize& aSize, + gfxContentType aContentType); + + void CopyTo(const gfx::IntRect& aSourceRect, + DataTextureSource* aDest, + const gfx::IntRect& aDestRect); + + virtual TextureImageTextureSourceOGL* AsTextureImageTextureSource() override { return this; } + + // TextureSource + + virtual void DeallocateDeviceData() override + { + mTexImage = nullptr; + SetUpdateSerial(0); + } + + virtual TextureSourceOGL* AsSourceOGL() override { return this; } + + virtual void BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) override; + + virtual gfx::IntSize GetSize() const override; + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual bool IsValid() const override { return !!mTexImage; } + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual GLenum GetWrapMode() const override + { + return mTexImage->GetWrapMode(); + } + + // BigImageIterator + + virtual BigImageIterator* AsBigImageIterator() override { return this; } + + virtual void BeginBigImageIteration() override + { + mTexImage->BeginBigImageIteration(); + mIterating = true; + } + + virtual void EndBigImageIteration() override + { + mIterating = false; + } + + virtual gfx::IntRect GetTileRect() override; + + virtual size_t GetTileCount() override + { + return mTexImage->GetTileCount(); + } + + virtual bool NextTile() override + { + return mTexImage->NextTile(); + } + +protected: + RefPtr<gl::TextureImage> mTexImage; + 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 TextureSource + , public TextureSourceOGL +{ +public: + GLTextureSource(CompositorOGL* aCompositor, + GLuint aTextureHandle, + GLenum aTarget, + gfx::IntSize aSize, + gfx::SurfaceFormat aFormat, + bool aExternallyOwned = false); + + ~GLTextureSource(); + + virtual const char* Name() const override { return "GLTextureSource"; } + + virtual GLTextureSource* AsGLTextureSource() override { return this; } + + virtual TextureSourceOGL* AsSourceOGL() override { return this; } + + virtual void BindTexture(GLenum activetex, + gfx::SamplingFilter aSamplingFilter) override; + + virtual bool IsValid() const override; + + virtual gfx::IntSize GetSize() const override { return mSize; } + + virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; } + + virtual GLenum GetTextureTarget() const override { return mTextureTarget; } + + virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; } + + virtual void DeallocateDeviceData() override; + + virtual void SetCompositor(Compositor* aCompositor) override; + + void SetSize(gfx::IntSize aSize) { mSize = aSize; } + + void SetFormat(gfx::SurfaceFormat aFormat) { mFormat = aFormat; } + + GLuint GetTextureHandle() const { return mTextureHandle; } + + gl::GLContext* gl() const; + +protected: + void DeleteTextureHandle(); + + RefPtr<CompositorOGL> mCompositor; + GLuint mTextureHandle; + GLenum mTextureTarget; + gfx::IntSize mSize; + gfx::SurfaceFormat mFormat; + // If the texture is externally owned, the gl handle will not be deleted + // in the destructor. + bool mExternallyOwned; +}; + +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. + virtual void DeallocateDeviceData() override {} + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual Compositor* GetCompositor() override { return mCompositor; } + + virtual bool Lock() override; + + virtual void Unlock() override {} + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override + { + aTexture = mTextureSource; + return !!aTexture; + } + + virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override + { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + gl::GLContext* gl() const; + + virtual gfx::IntSize GetSize() const override { return mSize; } + + virtual const char* Name() override { return "GLTextureHost"; } + +protected: + const GLuint mTexture; + const GLenum mTarget; + GLsync mSync; + const gfx::IntSize mSize; + const bool mHasAlpha; + RefPtr<CompositorOGL> mCompositor; + RefPtr<GLTextureSource> mTextureSource; +}; + +//////////////////////////////////////////////////////////////////////// +// SurfaceTexture + +#ifdef MOZ_WIDGET_ANDROID + +class SurfaceTextureSource : public TextureSource + , public TextureSourceOGL +{ +public: + SurfaceTextureSource(CompositorOGL* aCompositor, + mozilla::gl::AndroidSurfaceTexture* aSurfTex, + gfx::SurfaceFormat aFormat, + GLenum aTarget, + GLenum aWrapMode, + gfx::IntSize aSize); + + virtual const char* Name() const override { return "SurfaceTextureSource"; } + + virtual TextureSourceOGL* AsSourceOGL() override { return this; } + + virtual void BindTexture(GLenum activetex, + gfx::SamplingFilter aSamplingFilter) override; + + virtual bool IsValid() const override; + + virtual gfx::IntSize GetSize() const override { return mSize; } + + virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; } + + virtual gfx::Matrix4x4 GetTextureTransform() override; + + virtual GLenum GetTextureTarget() const override { return mTextureTarget; } + + virtual GLenum GetWrapMode() const override { return mWrapMode; } + + virtual void DeallocateDeviceData() override; + + virtual void SetCompositor(Compositor* aCompositor) override; + + gl::GLContext* gl() const; + +protected: + RefPtr<CompositorOGL> mCompositor; + RefPtr<gl::AndroidSurfaceTexture> mSurfTex; + const gfx::SurfaceFormat mFormat; + const GLenum mTextureTarget; + const GLenum mWrapMode; + const gfx::IntSize mSize; +}; + +class SurfaceTextureHost : public TextureHost +{ +public: + SurfaceTextureHost(TextureFlags aFlags, + mozilla::gl::AndroidSurfaceTexture* aSurfTex, + gfx::IntSize aSize); + + virtual ~SurfaceTextureHost(); + + virtual void DeallocateDeviceData() override; + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual Compositor* GetCompositor() override { return mCompositor; } + + virtual bool Lock() override; + + virtual void Unlock() override; + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override + { + aTexture = mTextureSource; + return !!aTexture; + } + + virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override + { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + gl::GLContext* gl() const; + + virtual gfx::IntSize GetSize() const override { return mSize; } + + virtual const char* Name() override { return "SurfaceTextureHost"; } + +protected: + RefPtr<gl::AndroidSurfaceTexture> mSurfTex; + const gfx::IntSize mSize; + RefPtr<CompositorOGL> mCompositor; + RefPtr<SurfaceTextureSource> mTextureSource; +}; + +#endif // MOZ_WIDGET_ANDROID + +//////////////////////////////////////////////////////////////////////// +// EGLImage + +class EGLImageTextureSource : public TextureSource + , public TextureSourceOGL +{ +public: + EGLImageTextureSource(CompositorOGL* aCompositor, + EGLImage aImage, + gfx::SurfaceFormat aFormat, + GLenum aTarget, + GLenum aWrapMode, + gfx::IntSize aSize); + + virtual const char* Name() const override { return "EGLImageTextureSource"; } + + virtual TextureSourceOGL* AsSourceOGL() override { return this; } + + virtual void BindTexture(GLenum activetex, + gfx::SamplingFilter aSamplingFilter) override; + + virtual bool IsValid() const override; + + virtual gfx::IntSize GetSize() const override { return mSize; } + + virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; } + + virtual gfx::Matrix4x4 GetTextureTransform() override; + + virtual GLenum GetTextureTarget() const override { return mTextureTarget; } + + virtual GLenum GetWrapMode() const override { return mWrapMode; } + + // We don't own anything. + virtual void DeallocateDeviceData() override {} + + virtual void SetCompositor(Compositor* aCompositor) override; + + gl::GLContext* gl() const; + +protected: + RefPtr<CompositorOGL> mCompositor; + const EGLImage mImage; + const gfx::SurfaceFormat mFormat; + const GLenum mTextureTarget; + const GLenum mWrapMode; + const gfx::IntSize mSize; +}; + +class EGLImageTextureHost : public TextureHost +{ +public: + EGLImageTextureHost(TextureFlags aFlags, + EGLImage aImage, + EGLSync aSync, + gfx::IntSize aSize, + bool hasAlpha); + + virtual ~EGLImageTextureHost(); + + // We don't own anything. + virtual void DeallocateDeviceData() override {} + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual Compositor* GetCompositor() override { return mCompositor; } + + virtual bool Lock() override; + + virtual void Unlock() override; + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override + { + aTexture = mTextureSource; + return !!aTexture; + } + + virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override + { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + gl::GLContext* gl() const; + + virtual gfx::IntSize GetSize() const override { return mSize; } + + virtual const char* Name() override { return "EGLImageTextureHost"; } + +protected: + const EGLImage mImage; + const EGLSync mSync; + const gfx::IntSize mSize; + const bool mHasAlpha; + RefPtr<CompositorOGL> mCompositor; + RefPtr<EGLImageTextureSource> mTextureSource; +}; + +CompositorOGL* AssertGLCompositor(Compositor* aCompositor); + +} // namespace layers +} // namespace mozilla + +#endif /* MOZILLA_GFX_TEXTUREOGL_H */ diff --git a/gfx/layers/opengl/TexturePoolOGL.cpp b/gfx/layers/opengl/TexturePoolOGL.cpp new file mode 100644 index 0000000000..8ee3b4cbbc --- /dev/null +++ b/gfx/layers/opengl/TexturePoolOGL.cpp @@ -0,0 +1,123 @@ +/* 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 "TexturePoolOGL.h" +#include <stdlib.h> // for malloc +#include "GLContext.h" // for GLContext +#include "mozilla/Monitor.h" // for Monitor, MonitorAutoLock +#include "mozilla/mozalloc.h" // for operator delete, etc +#include "nsDebug.h" // for NS_ASSERTION, NS_ERROR, etc +#include "nsDeque.h" // for nsDeque + +#define TEXTURE_POOL_SIZE 10 + +namespace mozilla { +namespace gl { + +static GLContext* sActiveContext = nullptr; + +static Monitor* sMonitor = nullptr; +static nsDeque* sTextures = nullptr; + +GLuint TexturePoolOGL::AcquireTexture() +{ + NS_ASSERTION(sMonitor, "not initialized"); + + MonitorAutoLock lock(*sMonitor); + + if (!sActiveContext) { + // Wait for a context + sMonitor->Wait(); + + if (!sActiveContext) + return 0; + } + + GLuint texture = 0; + if (sActiveContext->IsOwningThreadCurrent()) { + sActiveContext->MakeCurrent(); + + sActiveContext->fGenTextures(1, &texture); + } else { + while (sTextures->GetSize() == 0) { + NS_WARNING("Waiting for texture"); + sMonitor->Wait(); + } + + GLuint* popped = (GLuint*) sTextures->Pop(); + if (!popped) { + NS_ERROR("Failed to pop texture pool item"); + return 0; + } + + texture = *popped; + delete popped; + + NS_ASSERTION(texture, "Failed to retrieve texture from pool"); + } + + return texture; +} + +static void Clear() +{ + if (!sActiveContext) + return; + + sActiveContext->MakeCurrent(); + + GLuint* item; + while (sTextures->GetSize()) { + item = (GLuint*)sTextures->Pop(); + sActiveContext->fDeleteTextures(1, item); + delete item; + } +} + +void TexturePoolOGL::Fill(GLContext* aContext) +{ + NS_ASSERTION(aContext, "NULL GLContext"); + NS_ASSERTION(sMonitor, "not initialized"); + + MonitorAutoLock lock(*sMonitor); + + if (sActiveContext != aContext) { + Clear(); + sActiveContext = aContext; + } + + if (sTextures->GetSize() == TEXTURE_POOL_SIZE) + return; + + sActiveContext->MakeCurrent(); + + GLuint* texture = nullptr; + while (sTextures->GetSize() < TEXTURE_POOL_SIZE) { + texture = (GLuint*)malloc(sizeof(GLuint)); + sActiveContext->fGenTextures(1, texture); + sTextures->Push((void*) texture); + } + + sMonitor->NotifyAll(); +} + +GLContext* TexturePoolOGL::GetGLContext() +{ + return sActiveContext; +} + +void TexturePoolOGL::Init() +{ + sMonitor = new Monitor("TexturePoolOGL.sMonitor"); + sTextures = new nsDeque(); +} + +void TexturePoolOGL::Shutdown() +{ + delete sMonitor; + delete sTextures; +} + +} // namespace gl +} // namespace mozilla diff --git a/gfx/layers/opengl/TexturePoolOGL.h b/gfx/layers/opengl/TexturePoolOGL.h new file mode 100644 index 0000000000..136364e8c9 --- /dev/null +++ b/gfx/layers/opengl/TexturePoolOGL.h @@ -0,0 +1,40 @@ +/* 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_TEXTUREPOOLOGL_H +#define GFX_TEXTUREPOOLOGL_H + +#include "GLContextTypes.h" // for GLContext, GLuint + +namespace mozilla { +namespace gl { + +// A texture pool for for the on-screen GLContext. The main purpose of this class +// is to provide the ability to easily allocate an on-screen texture from the +// content thread. The unfortunate nature of the SurfaceTexture API (see AndroidSurfaceTexture) +// necessitates this. +class TexturePoolOGL +{ +public: + // Get a new texture from the pool. Will block + // and wait for one to be created if necessary + static GLuint AcquireTexture(); + + // Called by the active LayerManagerOGL to fill + // the pool + static void Fill(GLContext* aContext); + + static GLContext* GetGLContext(); + + // Initializes the pool, but does not fill it. Called by gfxPlatform init. + static void Init(); + + // Clears all internal data structures in preparation for shutdown + static void Shutdown(); +}; + +} // namespace gl +} // namespace mozilla + +#endif // GFX_TEXTUREPOOLOGL_H diff --git a/gfx/layers/opengl/X11TextureSourceOGL.cpp b/gfx/layers/opengl/X11TextureSourceOGL.cpp new file mode 100644 index 0000000000..dbed66b61b --- /dev/null +++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +#ifdef GL_PROVIDER_GLX + +#include "X11TextureSourceOGL.h" +#include "gfxXlibSurface.h" +#include "gfx2DGlue.h" + +namespace mozilla { +namespace layers { + +using namespace mozilla::gfx; + +X11TextureSourceOGL::X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface) + : mCompositor(aCompositor) + , mSurface(aSurface) + , mTexture(0) + , mUpdated(false) +{ +} + +X11TextureSourceOGL::~X11TextureSourceOGL() +{ + DeallocateDeviceData(); +} + +void +X11TextureSourceOGL::DeallocateDeviceData() +{ + if (mTexture) { + if (gl() && gl()->MakeCurrent()) { + gl::sGLXLibrary.ReleaseTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap()); + gl()->fDeleteTextures(1, &mTexture); + mTexture = 0; + } + } +} + +void +X11TextureSourceOGL::BindTexture(GLenum aTextureUnit, + gfx::SamplingFilter aSamplingFilter) +{ + gl()->fActiveTexture(aTextureUnit); + + if (!mTexture) { + gl()->fGenTextures(1, &mTexture); + + gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); + + gl::sGLXLibrary.BindTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap()); + } else { + gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); + if (mUpdated) { + gl::sGLXLibrary.UpdateTexImage(mSurface->XDisplay(), mSurface->GetGLXPixmap()); + mUpdated = false; + } + } + + ApplySamplingFilterToBoundTexture(gl(), aSamplingFilter, LOCAL_GL_TEXTURE_2D); +} + +IntSize +X11TextureSourceOGL::GetSize() const +{ + return mSurface->GetSize(); +} + +SurfaceFormat +X11TextureSourceOGL::GetFormat() const { + gfxContentType type = mSurface->GetContentType(); + return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type); +} + +void +X11TextureSourceOGL::SetCompositor(Compositor* aCompositor) +{ + CompositorOGL* glCompositor = AssertGLCompositor(aCompositor); + if (mCompositor == glCompositor) { + return; + } + DeallocateDeviceData(); + if (glCompositor) { + mCompositor = glCompositor; + } +} + +gl::GLContext* +X11TextureSourceOGL::gl() const +{ + return mCompositor ? mCompositor->gl() : nullptr; +} + +SurfaceFormat +X11TextureSourceOGL::ContentTypeToSurfaceFormat(gfxContentType aType) +{ + // X11 uses a switched format and the OGL compositor + // doesn't support ALPHA / A8. + switch (aType) { + case gfxContentType::COLOR: + return SurfaceFormat::R8G8B8X8; + case gfxContentType::COLOR_ALPHA: + return SurfaceFormat::R8G8B8A8; + default: + return SurfaceFormat::UNKNOWN; + } +} + +} +} + +#endif diff --git a/gfx/layers/opengl/X11TextureSourceOGL.h b/gfx/layers/opengl/X11TextureSourceOGL.h new file mode 100644 index 0000000000..5058474030 --- /dev/null +++ b/gfx/layers/opengl/X11TextureSourceOGL.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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_X11TEXTURESOURCEOGL__H +#define MOZILLA_GFX_X11TEXTURESOURCEOGL__H + +#ifdef GL_PROVIDER_GLX + +#include "mozilla/layers/CompositorOGL.h" +#include "mozilla/layers/TextureHostOGL.h" +#include "mozilla/layers/X11TextureHost.h" +#include "mozilla/gfx/2D.h" + +namespace mozilla { +namespace layers { + +// TextureSource for Xlib-backed surfaces. +class X11TextureSourceOGL + : public TextureSourceOGL + , public X11TextureSource +{ +public: + X11TextureSourceOGL(CompositorOGL* aCompositor, gfxXlibSurface* aSurface); + ~X11TextureSourceOGL(); + + virtual X11TextureSourceOGL* AsSourceOGL() override { return this; } + + virtual bool IsValid() const override { return !!gl(); } ; + + virtual void BindTexture(GLenum aTextureUnit, gfx::SamplingFilter aSamplingFilter) override; + + virtual gfx::IntSize GetSize() const override; + + virtual GLenum GetTextureTarget() const override { return LOCAL_GL_TEXTURE_2D; } + + virtual gfx::SurfaceFormat GetFormat() const override; + + virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; } + + virtual void DeallocateDeviceData() override; + + virtual void SetCompositor(Compositor* aCompositor) override; + + virtual void Updated() override { mUpdated = true; } + + gl::GLContext* gl() const; + + static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType); + +protected: + RefPtr<CompositorOGL> mCompositor; + RefPtr<gfxXlibSurface> mSurface; + RefPtr<gfx::SourceSurface> mSourceSurface; + GLuint mTexture; + bool mUpdated; +}; + +} // namespace layers +} // namespace mozilla + +#endif + +#endif // MOZILLA_GFX_X11TEXTURESOURCEOGL__H |