From 3526b0f9101e2deef42f149d433851a1389bbb8d Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 2 Jan 2014 10:17:22 -0500 Subject: [PATCH] Bug 945586 - Move GLReadTexImageHelper out of GLContext - r=jgilbert --- gfx/gl/GLContext.cpp | 356 ++------------------------------ gfx/gl/GLContext.h | 34 +-- gfx/gl/GLReadTexImageHelper.cpp | 347 +++++++++++++++++++++++++++++++ gfx/gl/GLReadTexImageHelper.h | 63 ++++++ gfx/gl/moz.build | 2 + gfx/layers/LayerScope.cpp | 7 +- 6 files changed, 431 insertions(+), 378 deletions(-) create mode 100644 gfx/gl/GLReadTexImageHelper.cpp create mode 100644 gfx/gl/GLReadTexImageHelper.h diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index a0d9437a9808..4175e328906f 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -12,6 +12,7 @@ #include "GLContext.h" #include "GLBlitHelper.h" #include "GLBlitTextureImageHelper.h" +#include "GLReadTexImageHelper.h" #include "gfxCrashReporterUtils.h" #include "gfxPlatform.h" @@ -275,11 +276,6 @@ GLContext::GLContext(const SurfaceCaps& caps, mWorkAroundDriverBugs(true) { mOwningThread = NS_GetCurrentThread(); - - mReadTextureImagePrograms[0] = 0; - mReadTextureImagePrograms[1] = 0; - mReadTextureImagePrograms[2] = 0; - mReadTextureImagePrograms[3] = 0; } GLContext::~GLContext() { @@ -1605,11 +1601,7 @@ GLContext::MarkDestroyed() mBlitHelper = nullptr; mBlitTextureImageHelper = nullptr; - - fDeleteProgram(mReadTextureImagePrograms[0]); - fDeleteProgram(mReadTextureImagePrograms[1]); - fDeleteProgram(mReadTextureImagePrograms[2]); - fDeleteProgram(mReadTextureImagePrograms[3]); + mReadTexImageHelper = nullptr; mTexGarbageBin->GLContextTeardown(); } else { @@ -1694,340 +1686,6 @@ GLContext::GetTexImage(GLuint aTexture, bool aYInvert, SurfaceFormat aFormat) return surf.forget(); } -static float -gReadTextureImageVerts[4*4] = { - -1.0f, -1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f -}; - -static float* -ReadTextureVertexArray() -{ - return gReadTextureImageVerts; -} - -static float -gReadTextureImageTexcoords[2*4] = { - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f -}; - -static float* -ReadTextureTexCoordArray(float aWidth, float aHeight, bool aFlip) -{ - const float u0 = 0.0f; - const float u1 = aWidth; - const float v0 = (aFlip) ? aHeight : 0.0f; - const float v1 = (aFlip) ? 0.0f : aHeight; - - float* uvs = gReadTextureImageTexcoords; - uvs[0] = u0; - uvs[1] = v0; - uvs[2] = u1; - uvs[3] = v0; - uvs[4] = u0; - uvs[5] = v1; - uvs[6] = u1; - uvs[7] = v1; - - return uvs; -} - -static const char* -gReadTextureImageVS = - "attribute vec4 aVertex;\n" - "attribute vec2 aTexCoord;\n" - "varying vec2 vTexCoord;\n" - "void main() { gl_Position = aVertex; vTexCoord = aTexCoord; }"; - -static const char* -gReadTextureImageFS[] = { - /* TEXTURE_2D */ - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 vTexCoord;\n" - "uniform sampler2D uTexture;\n" - "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }" - , - /* TEXTURE_2D with R/B swizzling */ - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 vTexCoord;\n" - "uniform sampler2D uTexture;\n" - "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }" - , - /* TEXTURE_EXTERNAL */ - "#extension GL_OES_EGL_image_external : require\n" - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 vTexCoord;\n" - "uniform samplerExternalOES uTexture;\n" - "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }" - , - /* TEXTURE_RECTANGLE */ - "#extension GL_ARB_texture_rectangle\n" - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "varying vec2 vTexCoord;\n" - "uniform sampler2DRect uTexture;\n" - "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }" -}; - -GLuint -GLContext::TextureImageProgramFor(GLenum aTextureTarget, int aShader) { - int variant = 0; - if (aTextureTarget == LOCAL_GL_TEXTURE_2D && - (aShader == layers::BGRALayerProgramType || - aShader == layers::BGRXLayerProgramType)) - { // Need to swizzle R/B. - variant = 1; - } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { - variant = 2; - } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { - variant = 3; - } - - /* This might be overkill, but assure that we don't access out-of-bounds */ - MOZ_ASSERT((size_t) variant < ArrayLength(mReadTextureImagePrograms)); - if (!mReadTextureImagePrograms[variant]) { - GLuint vs = fCreateShader(LOCAL_GL_VERTEX_SHADER); - fShaderSource(vs, 1, (const GLchar**) &gReadTextureImageVS, NULL); - fCompileShader(vs); - - GLuint fs = fCreateShader(LOCAL_GL_FRAGMENT_SHADER); - fShaderSource(fs, 1, (const GLchar**) &gReadTextureImageFS[variant], NULL); - fCompileShader(fs); - - GLuint program = fCreateProgram(); - fAttachShader(program, vs); - fAttachShader(program, fs); - fBindAttribLocation(program, 0, "aVertex"); - fBindAttribLocation(program, 1, "aTexCoord"); - fLinkProgram(program); - - GLint success; - fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success); - - if (!success) { - fDeleteProgram(program); - program = 0; - } - - fDeleteShader(vs); - fDeleteShader(fs); - - mReadTextureImagePrograms[variant] = program; - } - - return mReadTextureImagePrograms[variant]; -} - -static bool -DidGLErrorOccur(GLContext* aGL, const char* str) -{ - GLenum error = aGL->fGetError(); - if (error != LOCAL_GL_NO_ERROR) { - printf_stderr("GL ERROR: %s (0x%04x) %s\n", - aGL->GLErrorToString(error), error, str); - return true; - } - - return false; -} - -bool -GLContext::ReadBackPixelsIntoSurface(gfxImageSurface* aSurface, const gfxIntSize& aSize) { - GLint oldPackAlignment; - fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &oldPackAlignment); - - if (oldPackAlignment != 4) - fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); - - fReadPixels(0, 0, aSize.width, aSize.height, - LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, - aSurface->Data()); - - bool result = DidGLErrorOccur(this, "when reading pixels into surface"); - - if (oldPackAlignment != 4) - fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, oldPackAlignment); - - return result; -} - -#define CLEANUP_IF_GLERROR_OCCURRED(x) \ - if (DidGLErrorOccur(this, (x))) { \ - isurf = nullptr; \ - break; \ - } - -already_AddRefed -GLContext::ReadTextureImage(GLuint aTextureId, - GLenum aTextureTarget, - const gfxIntSize& aSize, - /* ShaderProgramType */ int aShaderProgram, - bool aYInvert) -{ - // Check aShaderProgram is in bounds for a layers::ShaderProgramType - MOZ_ASSERT(0 <= aShaderProgram && aShaderProgram < NumProgramTypes); - - if (aTextureTarget != LOCAL_GL_TEXTURE_2D && - aTextureTarget != LOCAL_GL_TEXTURE_EXTERNAL && - aTextureTarget != LOCAL_GL_TEXTURE_RECTANGLE_ARB) - { - printf_stderr("ReadTextureImage target is not TEXTURE_2D || " - "TEXTURE_EXTERNAL || TEXTURE_RECTANGLE\n"); - return nullptr; - } - - MakeCurrent(); - - /* Allocate resulting image surface */ - nsRefPtr isurf; - isurf = new gfxImageSurface(aSize, gfxImageFormatARGB32); - if (!isurf || isurf->CairoStatus()) { - isurf = nullptr; - return isurf.forget(); - } - - realGLboolean oldBlend, oldScissor; - GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex; - GLuint rb, fb; - - do { - /* Save current GL state */ - oldBlend = fIsEnabled(LOCAL_GL_BLEND); - oldScissor = fIsEnabled(LOCAL_GL_SCISSOR_TEST); - - fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb); - fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb); - fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog); - fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit); - fActiveTexture(LOCAL_GL_TEXTURE0); - switch (aTextureTarget) { - case LOCAL_GL_TEXTURE_2D: - fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex); - break; - case LOCAL_GL_TEXTURE_EXTERNAL: - fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex); - break; - case LOCAL_GL_TEXTURE_RECTANGLE: - fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex); - break; - default: /* Already checked above */ - break; - } - - /* Set required GL state */ - fDisable(LOCAL_GL_BLEND); - fDisable(LOCAL_GL_SCISSOR_TEST); - - PushViewportRect(nsIntRect(0, 0, aSize.width, aSize.height)); - - /* Setup renderbuffer */ - fGenRenderbuffers(1, &rb); - fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); - - GLenum rbInternalFormat = - IsGLES2() - ? (IsExtensionSupported(OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4) - : LOCAL_GL_RGBA; - fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height); - CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer"); - - /* Setup framebuffer */ - fGenFramebuffers(1, &fb); - fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb); - fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, - LOCAL_GL_RENDERBUFFER, rb); - CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer"); - - if (fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE) { - printf_stderr("framebuffer is incomplete\n"); - break; //goto cleanup; - } - - /* Setup vertex and fragment shader */ - layers::ShaderProgramType shaderProgram = (ShaderProgramType) aShaderProgram; - GLuint program = TextureImageProgramFor(aTextureTarget, shaderProgram); - if (!program) { - printf_stderr("failed to compile program for texture target %u and" - " shader program type %d\n", - aTextureTarget, aShaderProgram); - break; // goto cleanup; - } - - fUseProgram(program); - CLEANUP_IF_GLERROR_OCCURRED("when using program"); - fUniform1i(fGetUniformLocation(program, "uTexture"), 0); - CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location"); - - /* Setup quad geometry */ - fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); - fEnableVertexAttribArray(0); - fEnableVertexAttribArray(1); - - float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f; - float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f; - fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, ReadTextureVertexArray()); - fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, ReadTextureTexCoordArray(w, h, aYInvert)); - - /* Bind the texture */ - if (aTextureId) { - fBindTexture(aTextureTarget, aTextureId); - CLEANUP_IF_GLERROR_OCCURRED("when binding texture"); - } - - /* Draw quad */ - fClearColor(1.0f, 0.0f, 1.0f, 1.0f); - fClear(LOCAL_GL_COLOR_BUFFER_BIT); - CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer"); - - fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); - CLEANUP_IF_GLERROR_OCCURRED("when drawing texture"); - - fDisableVertexAttribArray(1); - fDisableVertexAttribArray(0); - - /* Read-back draw results */ - ReadBackPixelsIntoSurface(isurf, aSize); - CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface"); - } while (false); - - /* Restore GL state */ -//cleanup: - fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb); - fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb); - fUseProgram(oldprog); - - // note that deleting 0 has no effect in any of these calls - fDeleteRenderbuffers(1, &rb); - fDeleteFramebuffers(1, &fb); - - if (oldBlend) - fEnable(LOCAL_GL_BLEND); - - if (oldScissor) - fEnable(LOCAL_GL_SCISSOR_TEST); - - if (aTextureId) - fBindTexture(aTextureTarget, oldTex); - - if (oldTexUnit != LOCAL_GL_TEXTURE0) - fActiveTexture(oldTexUnit); - - PopViewportRect(); - - return isurf.forget(); -} - -#undef CLEANUP_IF_GLERROR_OCCURRED - static bool GetActualReadFormats(GLContext* gl, GLenum destFormat, GLenum destType, GLenum& readFormat, GLenum& readType) @@ -2579,6 +2237,16 @@ GLContext::BlitTextureImageHelper() return mBlitTextureImageHelper; } +GLReadTexImageHelper* +GLContext::ReadTexImageHelper() +{ + if (!mReadTexImageHelper) { + mReadTexImageHelper = new GLReadTexImageHelper(this); + } + + return mReadTexImageHelper; +} + bool DoesStringMatch(const char* aString, const char *aWantedString) { diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index dc177c025369..55fc95dcb7d4 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -70,6 +70,7 @@ namespace mozilla { class TextureGarbageBin; class GLBlitHelper; class GLBlitTextureImageHelper; + class GLReadTexImageHelper; } namespace layers { @@ -2581,35 +2582,6 @@ public: virtual bool RenewSurface() { return false; } -private: - /** - * Helpers for ReadTextureImage - */ - GLuint TextureImageProgramFor(GLenum aTextureTarget, int aShader); - bool ReadBackPixelsIntoSurface(gfxImageSurface* aSurface, const gfxIntSize& aSize); - -public: - /** - * Read the image data contained in aTexture, and return it as an ImageSurface. - * If GL_RGBA is given as the format, a gfxImageFormatARGB32 surface is returned. - * Not implemented yet: - * If GL_RGB is given as the format, a gfxImageFormatRGB24 surface is returned. - * If GL_LUMINANCE is given as the format, a gfxImageFormatA8 surface is returned. - * - * THIS IS EXPENSIVE. It is ridiculously expensive. Only do this - * if you absolutely positively must, and never in any performance - * critical path. - * - * NOTE: aShaderProgram is really mozilla::layers::ShaderProgramType. It is - * passed as int to eliminate including LayerManagerOGLProgram.h in this - * hub header. - */ - already_AddRefed ReadTextureImage(GLuint aTextureId, - GLenum aTextureTarget, - const gfxIntSize& aSize, - /* ShaderProgramType */ int aShaderProgram, - bool aYInvert = false); - already_AddRefed GetTexImage(GLuint aTexture, bool aYInvert, SurfaceFormat aFormat); @@ -2689,11 +2661,13 @@ protected: ScopedDeletePtr mBlitHelper; ScopedDeletePtr mBlitTextureImageHelper; + ScopedDeletePtr mReadTexImageHelper; public: GLBlitHelper* BlitHelper(); GLBlitTextureImageHelper* BlitTextureImageHelper(); + GLReadTexImageHelper* ReadTexImageHelper(); // Assumes shares are created by all sharing with the same global context. bool SharesWith(const GLContext* other) const { @@ -2861,8 +2835,6 @@ public: bool IsOffscreenSizeAllowed(const gfx::IntSize& aSize) const; protected: - GLuint mReadTextureImagePrograms[4]; - bool InitWithPrefix(const char *prefix, bool trygl); void InitExtensions(); diff --git a/gfx/gl/GLReadTexImageHelper.cpp b/gfx/gl/GLReadTexImageHelper.cpp new file mode 100644 index 000000000000..866ceedf945a --- /dev/null +++ b/gfx/gl/GLReadTexImageHelper.cpp @@ -0,0 +1,347 @@ +/* -*- 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 "GLReadTexImageHelper.h" +#include "GLContext.h" +#include "OGLShaderProgram.h" +#include "gfxTypes.h" + + +namespace mozilla { +namespace gl { + +GLReadTexImageHelper::GLReadTexImageHelper(GLContext* gl) + : mGL(gl) +{ + mPrograms[0] = 0; + mPrograms[1] = 0; + mPrograms[2] = 0; + mPrograms[3] = 0; +} + +GLReadTexImageHelper::~GLReadTexImageHelper() +{ + mGL->fDeleteProgram(mPrograms[0]); + mGL->fDeleteProgram(mPrograms[1]); + mGL->fDeleteProgram(mPrograms[2]); + mGL->fDeleteProgram(mPrograms[3]); +} + +static const char* +readTextureImageVS = + "attribute vec4 aVertex;\n" + "attribute vec2 aTexCoord;\n" + "varying vec2 vTexCoord;\n" + "void main() { gl_Position = aVertex; vTexCoord = aTexCoord; }"; + +static const char* +readTextureImageFS[] = { + /* TEXTURE_2D */ + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 vTexCoord;\n" + "uniform sampler2D uTexture;\n" + "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }" + , + /* TEXTURE_2D with R/B swizzling */ + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 vTexCoord;\n" + "uniform sampler2D uTexture;\n" + "void main() { gl_FragColor = texture2D(uTexture, vTexCoord).bgra; }" + , + /* TEXTURE_EXTERNAL */ + "#extension GL_OES_EGL_image_external : require\n" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 vTexCoord;\n" + "uniform samplerExternalOES uTexture;\n" + "void main() { gl_FragColor = texture2D(uTexture, vTexCoord); }" + , + /* TEXTURE_RECTANGLE */ + "#extension GL_ARB_texture_rectangle\n" + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec2 vTexCoord;\n" + "uniform sampler2DRect uTexture;\n" + "void main() { gl_FragColor = texture2DRect(uTexture, vTexCoord).bgra; }" +}; + +GLuint +GLReadTexImageHelper::TextureImageProgramFor(GLenum aTextureTarget, int aShader) { + int variant = 0; + if (aTextureTarget == LOCAL_GL_TEXTURE_2D && + (aShader == layers::BGRALayerProgramType || + aShader == layers::BGRXLayerProgramType)) + { // Need to swizzle R/B. + variant = 1; + } else if (aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL) { + variant = 2; + } else if (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) { + variant = 3; + } + + /* This might be overkill, but assure that we don't access out-of-bounds */ + MOZ_ASSERT((size_t) variant < ArrayLength(mPrograms)); + if (!mPrograms[variant]) { + GLuint vs = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER); + mGL->fShaderSource(vs, 1, (const GLchar**) &readTextureImageVS, NULL); + mGL->fCompileShader(vs); + + GLuint fs = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); + mGL->fShaderSource(fs, 1, (const GLchar**) &readTextureImageFS[variant], NULL); + mGL->fCompileShader(fs); + + GLuint program = mGL->fCreateProgram(); + mGL->fAttachShader(program, vs); + mGL->fAttachShader(program, fs); + mGL->fBindAttribLocation(program, 0, "aVertex"); + mGL->fBindAttribLocation(program, 1, "aTexCoord"); + mGL->fLinkProgram(program); + + GLint success; + mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &success); + + if (!success) { + mGL->fDeleteProgram(program); + program = 0; + } + + mGL->fDeleteShader(vs); + mGL->fDeleteShader(fs); + + mPrograms[variant] = program; + } + + return mPrograms[variant]; +} + +bool +GLReadTexImageHelper::DidGLErrorOccur(const char* str) +{ + GLenum error = mGL->fGetError(); + if (error != LOCAL_GL_NO_ERROR) { + printf_stderr("GL ERROR: %s (0x%04x) %s\n", + mGL->GLErrorToString(error), error, str); + return true; + } + + return false; +} + +bool +GLReadTexImageHelper::ReadBackPixelsIntoSurface(gfxImageSurface* aSurface, const gfxIntSize& aSize) { + GLint oldPackAlignment; + mGL->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &oldPackAlignment); + + if (oldPackAlignment != 4) + mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4); + + mGL->fReadPixels(0, 0, aSize.width, aSize.height, + LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, + aSurface->Data()); + + bool result = DidGLErrorOccur("when reading pixels into surface"); + + if (oldPackAlignment != 4) + mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, oldPackAlignment); + + return result; +} + +#define CLEANUP_IF_GLERROR_OCCURRED(x) \ + if (DidGLErrorOccur(x)) { \ + isurf = nullptr; \ + break; \ + } + +already_AddRefed +GLReadTexImageHelper::ReadTexImage(GLuint aTextureId, + GLenum aTextureTarget, + const gfxIntSize& aSize, + /* ShaderProgramType */ int aShaderProgram, + bool aYInvert) +{ + // Check aShaderProgram is in bounds for a layers::ShaderProgramType + MOZ_ASSERT(0 <= aShaderProgram && aShaderProgram < NumProgramTypes); + + if (aTextureTarget != LOCAL_GL_TEXTURE_2D && + aTextureTarget != LOCAL_GL_TEXTURE_EXTERNAL && + aTextureTarget != LOCAL_GL_TEXTURE_RECTANGLE_ARB) + { + printf_stderr("ReadTextureImage target is not TEXTURE_2D || " + "TEXTURE_EXTERNAL || TEXTURE_RECTANGLE\n"); + return nullptr; + } + + mGL->MakeCurrent(); + + /* Allocate resulting image surface */ + nsRefPtr isurf; + isurf = new gfxImageSurface(aSize, gfxImageFormatARGB32); + if (!isurf || isurf->CairoStatus()) { + isurf = nullptr; + return isurf.forget(); + } + + realGLboolean oldBlend, oldScissor; + GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex; + GLuint rb, fb; + + do { + /* Save current GL state */ + oldBlend = mGL->fIsEnabled(LOCAL_GL_BLEND); + oldScissor = mGL->fIsEnabled(LOCAL_GL_SCISSOR_TEST); + + mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb); + mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb); + mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog); + mGL->fGetIntegerv(LOCAL_GL_ACTIVE_TEXTURE, &oldTexUnit); + mGL->fActiveTexture(LOCAL_GL_TEXTURE0); + switch (aTextureTarget) { + case LOCAL_GL_TEXTURE_2D: + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex); + break; + case LOCAL_GL_TEXTURE_EXTERNAL: + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldTex); + break; + case LOCAL_GL_TEXTURE_RECTANGLE: + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &oldTex); + break; + default: /* Already checked above */ + break; + } + + /* Set required GL state */ + mGL->fDisable(LOCAL_GL_BLEND); + mGL->fDisable(LOCAL_GL_SCISSOR_TEST); + + mGL->PushViewportRect(nsIntRect(0, 0, aSize.width, aSize.height)); + + /* Setup renderbuffer */ + mGL->fGenRenderbuffers(1, &rb); + mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, rb); + + GLenum rbInternalFormat = + mGL->IsGLES2() + ? (mGL->IsExtensionSupported(GLContext::OES_rgb8_rgba8) ? LOCAL_GL_RGBA8 : LOCAL_GL_RGBA4) + : LOCAL_GL_RGBA; + mGL->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, rbInternalFormat, aSize.width, aSize.height); + CLEANUP_IF_GLERROR_OCCURRED("when binding and creating renderbuffer"); + + /* Setup framebuffer */ + mGL->fGenFramebuffers(1, &fb); + mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb); + mGL->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, + LOCAL_GL_RENDERBUFFER, rb); + CLEANUP_IF_GLERROR_OCCURRED("when binding and creating framebuffer"); + + if (mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE) { + printf_stderr("framebuffer is incomplete\n"); + break; //goto cleanup; + } + + /* Setup vertex and fragment shader */ + layers::ShaderProgramType shaderProgram = (layers::ShaderProgramType) aShaderProgram; + GLuint program = TextureImageProgramFor(aTextureTarget, shaderProgram); + if (!program) { + printf_stderr("failed to compile program for texture target %u and" + " shader program type %d\n", + aTextureTarget, aShaderProgram); + break; // goto cleanup; + } + + mGL->fUseProgram(program); + CLEANUP_IF_GLERROR_OCCURRED("when using program"); + mGL->fUniform1i(mGL->fGetUniformLocation(program, "uTexture"), 0); + CLEANUP_IF_GLERROR_OCCURRED("when setting uniform location"); + + /* Setup quad geometry */ + mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0); + mGL->fEnableVertexAttribArray(0); + mGL->fEnableVertexAttribArray(1); + + float w = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.width : 1.0f; + float h = (aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE) ? (float) aSize.height : 1.0f; + + + const float + vertexArray[4*4] = { + -1.0f, -1.0f, 0.0f, 1.0f, + 1.0f, -1.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f + }; + mGL->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, vertexArray); + + const float u0 = 0.0f; + const float u1 = w; + const float v0 = aYInvert ? h : 0.0f; + const float v1 = aYInvert ? 0.0f : h; + const float texCoordArray[8] = { u0, v0, + u1, v0, + u0, v1, + u1, v1 }; + mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, texCoordArray); + + /* Bind the texture */ + if (aTextureId) { + mGL->fBindTexture(aTextureTarget, aTextureId); + CLEANUP_IF_GLERROR_OCCURRED("when binding texture"); + } + + /* Draw quad */ + mGL->fClearColor(1.0f, 0.0f, 1.0f, 1.0f); + mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT); + CLEANUP_IF_GLERROR_OCCURRED("when clearing color buffer"); + + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); + CLEANUP_IF_GLERROR_OCCURRED("when drawing texture"); + + mGL->fDisableVertexAttribArray(1); + mGL->fDisableVertexAttribArray(0); + + /* Read-back draw results */ + ReadBackPixelsIntoSurface(isurf, aSize); + CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface"); + } while (false); + + /* Restore GL state */ +//cleanup: + mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb); + mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb); + mGL->fUseProgram(oldprog); + + // note that deleting 0 has no effect in any of these calls + mGL->fDeleteRenderbuffers(1, &rb); + mGL->fDeleteFramebuffers(1, &fb); + + if (oldBlend) + mGL->fEnable(LOCAL_GL_BLEND); + + if (oldScissor) + mGL->fEnable(LOCAL_GL_SCISSOR_TEST); + + if (aTextureId) + mGL->fBindTexture(aTextureTarget, oldTex); + + if (oldTexUnit != LOCAL_GL_TEXTURE0) + mGL->fActiveTexture(oldTexUnit); + + mGL->PopViewportRect(); + + return isurf.forget(); +} + +#undef CLEANUP_IF_GLERROR_OCCURRED + + +} +} diff --git a/gfx/gl/GLReadTexImageHelper.h b/gfx/gl/GLReadTexImageHelper.h new file mode 100644 index 000000000000..5643355262c4 --- /dev/null +++ b/gfx/gl/GLReadTexImageHelper.h @@ -0,0 +1,63 @@ +/* -*- 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 GLREADTEXIMAGEHELPER_H_ +#define GLREADTEXIMAGEHELPER_H_ + +#include "GLContextTypes.h" +#include "mozilla/Attributes.h" +#include "nsSize.h" +#include "nsAutoPtr.h" + +class gfxImageSurface; + +namespace mozilla { +namespace gl { + +class GLReadTexImageHelper MOZ_FINAL +{ + // The GLContext is the sole owner of the GLBlitHelper. + GLContext* mGL; + + GLuint mPrograms[4]; + + GLuint TextureImageProgramFor(GLenum aTextureTarget, int aShader); + bool ReadBackPixelsIntoSurface(gfxImageSurface* aSurface, const gfxIntSize& aSize); + + bool DidGLErrorOccur(const char* str); + +public: + + GLReadTexImageHelper(GLContext* gl); + ~GLReadTexImageHelper(); + + /** + * Read the image data contained in aTexture, and return it as an ImageSurface. + * If GL_RGBA is given as the format, a gfxImageFormatARGB32 surface is returned. + * Not implemented yet: + * If GL_RGB is given as the format, a gfxImageFormatRGB24 surface is returned. + * If GL_LUMINANCE is given as the format, a gfxImageFormatA8 surface is returned. + * + * THIS IS EXPENSIVE. It is ridiculously expensive. Only do this + * if you absolutely positively must, and never in any performance + * critical path. + * + * NOTE: aShaderProgram is really mozilla::layers::ShaderProgramType. It is + * passed as int to eliminate including LayerManagerOGLProgram.h here. + */ + already_AddRefed ReadTexImage(GLuint aTextureId, + GLenum aTextureTarget, + const gfxIntSize& aSize, + /* ShaderProgramType */ int aShaderProgram, + bool aYInvert = false); + + +}; + +} +} + +#endif diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build index fbb2e603695d..9a9982b0fa6b 100644 --- a/gfx/gl/moz.build +++ b/gfx/gl/moz.build @@ -40,6 +40,7 @@ EXPORTS += [ 'GLDefs.h', 'GLLibraryEGL.h', 'GLLibraryLoader.h', + 'GLReadTexImageHelper.h', 'GLScreenBuffer.h', 'GLSharedHandleHelpers.h', 'GLTextureImage.h', @@ -118,6 +119,7 @@ UNIFIED_SOURCES += [ 'GLContextUtils.cpp', 'GLLibraryEGL.cpp', 'GLLibraryLoader.cpp', + 'GLReadTexImageHelper.cpp', 'GLScreenBuffer.cpp', 'GLSharedHandleHelpers.cpp', 'GLTextureImage.cpp', diff --git a/gfx/layers/LayerScope.cpp b/gfx/layers/LayerScope.cpp index ca3f32172310..05415ea568b9 100644 --- a/gfx/layers/LayerScope.cpp +++ b/gfx/layers/LayerScope.cpp @@ -21,6 +21,7 @@ #include "GLContext.h" #include "GLContextProvider.h" +#include "GLReadTexImageHelper.h" #include "nsIServiceManager.h" #include "nsIConsoleService.h" @@ -495,9 +496,9 @@ SendTextureSource(GLContext* aGLContext, // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding // texture correctly. textureId is used for tracking in DebugGLTextureData. nsRefPtr img = - aGLContext->ReadTextureImage(0, textureTarget, - gfxIntSize(size.width, size.height), - shaderProgram, aFlipY); + aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget, + gfxIntSize(size.width, size.height), + shaderProgram, aFlipY); gCurrentSender->Append( new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,