/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Corporation code. * * The Initial Developer of the Original Code is Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Vladimir Vukicevic * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef GFX_LAYERMANAGEROGLPROGRAM_H #define GFX_LAYERMANAGEROGLPROGRAM_H #include #include "prenv.h" #include "nsString.h" #include "GLContext.h" namespace mozilla { namespace layers { #define ASSERT_THIS_PROGRAM \ do { \ NS_ASSERTION(mGL->GetUserData(&sCurrentProgramKey) == this, \ "SetUniform with wrong program active!"); \ } while (0) struct UniformValue { UniformValue() { memset(this, 0, sizeof(UniformValue)); } void setInt(const int i) { value.i[0] = i; } void setFloat(const float f) { value.f[0] = f; } void setFloatN(const float *f, const int n) { memcpy(value.f, f, sizeof(float)*n); } void setColor(const gfxRGBA& c) { value.f[0] = float(c.r); value.f[1] = float(c.g); value.f[2] = float(c.b); value.f[3] = float(c.a); } bool equalsInt(const int i) { return i == value.i[0]; } bool equalsFloat(const float f) { return f == value.f[0]; } bool equalsFloatN(const float *f, const int n) { return memcmp(f, value.f, sizeof(float)*n) == 0; } bool equalsColor(const gfxRGBA& c) { return value.f[0] == float(c.r) && value.f[1] == float(c.g) && value.f[2] == float(c.b) && value.f[3] == float(c.a); } union { int i[1]; float f[16]; } value; }; class LayerManagerOGLProgram { protected: static int sCurrentProgramKey; public: typedef mozilla::gl::GLContext GLContext; // common attrib locations enum { VertexAttrib = 0, TexCoordAttrib = 1 }; LayerManagerOGLProgram(GLContext *aGL) : mGL(aGL), mProgram(0) { } virtual ~LayerManagerOGLProgram() { nsRefPtr ctx = mGL->GetSharedContext(); if (!ctx) { ctx = mGL; } ctx->MakeCurrent(); ctx->fDeleteProgram(mProgram); } void Activate() { NS_ASSERTION(mProgram != 0, "Attempting to activate a program that's not in use!"); mGL->fUseProgram(mProgram); mGL->SetUserData(&sCurrentProgramKey, this); } void SetUniform(GLuint aUniform, float aFloatValue) { ASSERT_THIS_PROGRAM; if (aUniform == GLuint(-1)) return; if (!mUniformValues[aUniform].equalsFloat(aFloatValue)) { mGL->fUniform1f(aUniform, aFloatValue); mUniformValues[aUniform].setFloat(aFloatValue); } } void SetUniform(GLuint aUniform, const gfxRGBA& aColor) { ASSERT_THIS_PROGRAM; if (aUniform == GLuint(-1)) return; if (!mUniformValues[aUniform].equalsColor(aColor)) { mGL->fUniform4f(aUniform, float(aColor.r), float(aColor.g), float(aColor.b), float(aColor.a)); mUniformValues[aUniform].setColor(aColor); } } void SetUniform(GLuint aUniform, int aLength, float *aFloatValues) { ASSERT_THIS_PROGRAM; if (aUniform == GLuint(-1)) return; if (!mUniformValues[aUniform].equalsFloatN(aFloatValues, aLength)) { if (aLength == 1) { mGL->fUniform1fv(aUniform, 1, aFloatValues); } else if (aLength == 2) { mGL->fUniform2fv(aUniform, 1, aFloatValues); } else if (aLength == 3) { mGL->fUniform3fv(aUniform, 1, aFloatValues); } else if (aLength == 4) { mGL->fUniform4fv(aUniform, 1, aFloatValues); } else { NS_NOTREACHED("Bogus aLength param"); } mUniformValues[aUniform].setFloatN(aFloatValues, aLength); } } void SetUniform(GLuint aUniform, GLint aIntValue) { ASSERT_THIS_PROGRAM; if (aUniform == GLuint(-1)) return; if (!mUniformValues[aUniform].equalsInt(aIntValue)) { mGL->fUniform1i(aUniform, aIntValue); mUniformValues[aUniform].setInt(aIntValue); } } void SetMatrixUniform(GLuint aUniform, const float *aFloatValues) { ASSERT_THIS_PROGRAM; if (aUniform == GLuint(-1)) return; if (!mUniformValues[aUniform].equalsFloatN(aFloatValues, 16)) { mGL->fUniformMatrix4fv(aUniform, 1, false, aFloatValues); mUniformValues[aUniform].setFloatN(aFloatValues, 16); } } protected: nsRefPtr mGL; GLuint mProgram; nsTArray mUniformValues; GLint CreateShader(GLenum aShaderType, const char *aShaderSource) { GLint success, len = 0; GLint sh = mGL->fCreateShader(aShaderType); mGL->fShaderSource(sh, 1, (const GLchar**)&aShaderSource, NULL); 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 && PR_GetEnv("MOZ_DEBUG_SHADERS")) #endif ) { nsCAutoString log; log.SetCapacity(len); mGL->fGetShaderInfoLog(sh, len, (GLint*) &len, (char*) log.BeginWriting()); log.SetLength(len); if (!success) { fprintf (stderr, "=== SHADER COMPILATION FAILED ===\n"); } else { fprintf (stderr, "=== SHADER COMPILATION WARNINGS ===\n"); } fprintf (stderr, "=== Source:\n%s\n", aShaderSource); fprintf (stderr, "=== Log:\n%s\n", nsPromiseFlatCString(log).get()); fprintf (stderr, "============\n"); if (!success) { mGL->fDeleteShader(sh); return 0; } } return sh; } bool 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; mProgram = mGL->fCreateProgram(); mGL->fAttachShader(mProgram, vertexShader); mGL->fAttachShader(mProgram, fragmentShader); // bind common attribs to consistent indices mGL->fBindAttribLocation(mProgram, VertexAttrib, "aVertexCoord"); mGL->fBindAttribLocation(mProgram, TexCoordAttrib, "aTexCoord"); mGL->fLinkProgram(mProgram); GLint success, len; mGL->fGetProgramiv(mProgram, LOCAL_GL_LINK_STATUS, &success); mGL->fGetProgramiv(mProgram, 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 && PR_GetEnv("MOZ_DEBUG_SHADERS")) #endif ) { nsCAutoString log; log.SetCapacity(len); mGL->fGetProgramInfoLog(mProgram, len, (GLint*) &len, (char*) log.BeginWriting()); log.SetLength(len); if (!success) { fprintf (stderr, "=== PROGRAM LINKING FAILED ===\n"); } else { fprintf (stderr, "=== PROGRAM LINKING WARNINGS ===\n"); } fprintf (stderr, "=== Log:\n%s\n", nsPromiseFlatCString(log).get()); fprintf (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(mProgram); mProgram = 0; return false; } } // Now query uniforms, so that we can initialize mUniformValues // note that for simplicity, mUniformLocations is indexed by the // uniform -location-, and not the uniform -index-. This means // that it might have a bunch of unused space as locations dense // like indices are; however, there are unlikely to be enough for // our shaders for this to become a significant memory issue. GLint count, maxnamelen; nsCAutoString uname; GLint maxloc = 0; mGL->fGetProgramiv(mProgram, LOCAL_GL_ACTIVE_UNIFORMS, &count); mGL->fGetProgramiv(mProgram, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxnamelen); uname.SetCapacity(maxnamelen); for (int i = 0; i < count; ++i) { GLsizei namelen; GLint usize; GLenum utype; mGL->fGetActiveUniform(mProgram, i, maxnamelen, &namelen, &usize, &utype, uname.BeginWriting()); uname.SetLength(namelen); GLint uloc = mGL->fGetUniformLocation(mProgram, uname.BeginReading()); if (maxloc < uloc) maxloc = uloc; } // Note +1: the last valid index needs to be 'maxloc', so we need // to set the array length to 1 more than that. mUniformValues.SetLength(maxloc+1); return true; } void GetAttribLocations(const char **aAttribNames, GLint *aAttribLocations) { NS_ASSERTION(mProgram != 0, "GetAttribLocations called with no program!"); for (int i = 0; aAttribNames[i] != nsnull; ++i) { aAttribLocations[i] = mGL->fGetAttribLocation(mProgram, aAttribNames[i]); } } void GetUniformLocations(const char **aUniformNames, GLint *aUniformLocations) { NS_ASSERTION(mProgram != 0, "GetUniformLocations called with no program!"); for (int i = 0; aUniformNames[i] != nsnull; ++i) { aUniformLocations[i] = mGL->fGetUniformLocation(mProgram, aUniformNames[i]); } } }; /* * A LayerProgram is the base of all further LayerPrograms. * * It has a number of attributes and uniforms common to all layer programs. * * Attribute inputs: * aVertexCoord - vertex coordinate * * Uniforms: * uTransformMatrix - a transform matrix * uQuadTransform * uProjMatrix - projection matrix * uOffset - a vec4 offset to apply to the transformed coordinates * uLayerOpacity - a float, the layer opacity (final colors will be multiplied by this) */ class LayerProgram : public LayerManagerOGLProgram { public: enum { TransformMatrixUniform = 0, QuadTransformUniform, ProjectionMatrixUniform, OffsetUniform, LayerOpacityUniform, NumLayerUniforms }; enum { VertexCoordAttrib = 0, NumLayerAttribs }; LayerProgram(GLContext *aGL) : LayerManagerOGLProgram(aGL) { } bool Initialize(const char *aVertexShaderString, const char *aFragmentShaderString) { if (!CreateProgram(aVertexShaderString, aFragmentShaderString)) return false; const char *uniformNames[] = { "uLayerTransform", "uLayerQuadTransform", "uMatrixProj", "uRenderTargetOffset", "uLayerOpacity", NULL }; mUniformLocations.SetLength(NumLayerUniforms); GetUniformLocations(uniformNames, &mUniformLocations[0]); const char *attribNames[] = { "aVertexCoord", NULL }; mAttribLocations.SetLength(NumLayerAttribs); GetAttribLocations(attribNames, &mAttribLocations[0]); return true; } GLint AttribLocation(int aWhich) { if (aWhich < 0 || aWhich >= int(mAttribLocations.Length())) return -1; return mAttribLocations[aWhich]; } void SetLayerTransform(const gfx3DMatrix& aMatrix) { SetLayerTransform(&aMatrix._11); } void SetLayerTransform(const float *aMatrix) { SetMatrixUniform(mUniformLocations[TransformMatrixUniform], aMatrix); } void SetLayerQuadRect(const nsIntRect& aRect) { gfx3DMatrix m; m._11 = float(aRect.width); m._22 = float(aRect.height); m._41 = float(aRect.x); m._42 = float(aRect.y); SetMatrixUniform(mUniformLocations[QuadTransformUniform], &m._11); } void SetProjectionMatrix(const gfx3DMatrix& aMatrix) { SetProjectionMatrix(&aMatrix._11); } void SetProjectionMatrix(const float *aMatrix) { SetMatrixUniform(mUniformLocations[ProjectionMatrixUniform], aMatrix); } void SetRenderOffset(const nsIntPoint& aOffset) { float vals[4] = { float(aOffset.x), float(aOffset.y), 0.0f, 0.0f }; SetUniform(mUniformLocations[OffsetUniform], 4, vals); } void SetRenderOffset(float aX, float aY) { float vals[4] = { aX, aY, 0.0f, 0.0f }; SetUniform(mUniformLocations[OffsetUniform], 4, vals); } void SetLayerOpacity(float aOpacity) { SetUniform(mUniformLocations[LayerOpacityUniform], aOpacity); } protected: nsTArray mUniformLocations; nsTArray mAttribLocations; }; /* * A ColorTextureLayerProgram is a LayerProgram that renders * a single texture. It adds the following attributes and uniforms: * * Attribute inputs: * aTexCoord - texture coordinate * * Uniforms: * uTexture - 2D texture unit which to sample */ class ColorTextureLayerProgram : public LayerProgram { public: enum { TextureUniform = NumLayerUniforms, NumUniforms }; enum { TexCoordAttrib = NumLayerAttribs, NumAttribs }; ColorTextureLayerProgram(GLContext *aGL) : LayerProgram(aGL) { } bool Initialize(const char *aVertexShaderString, const char *aFragmentShaderString) { if (!LayerProgram::Initialize(aVertexShaderString, aFragmentShaderString)) return false; const char *uniformNames[] = { "uTexture", NULL }; mUniformLocations.SetLength(NumUniforms); GetUniformLocations(uniformNames, &mUniformLocations[NumLayerUniforms]); const char *attribNames[] = { "aTexCoord", NULL }; mAttribLocations.SetLength(NumAttribs); GetAttribLocations(attribNames, &mAttribLocations[NumLayerAttribs]); // this is a one-off that's present in the 2DRect versions of some shaders. mTexCoordMultiplierUniformLocation = mGL->fGetUniformLocation(mProgram, "uTexCoordMultiplier"); return true; } void SetTextureUnit(GLint aUnit) { SetUniform(mUniformLocations[TextureUniform], aUnit); } GLint GetTexCoordMultiplierUniformLocation() { return mTexCoordMultiplierUniformLocation; } protected: GLint mTexCoordMultiplierUniformLocation; }; /* * A YCbCrTextureLayerProgram is a LayerProgram that renders a YCbCr * image, reading from three texture units. The textures are assumed * to be single-channel textures. * * Attribute inputs: * aTexCoord - texture coordinate * * Uniforms: * uYTexture - 2D texture unit which to sample Y * uCbTexture - 2D texture unit which to sample Cb * uCrTexture - 2D texture unit which to sample Cr */ class YCbCrTextureLayerProgram : public LayerProgram { public: enum { YTextureUniform = NumLayerUniforms, CbTextureUniform, CrTextureUniform, NumUniforms }; enum { TexCoordAttrib = NumLayerAttribs, NumAttribs }; YCbCrTextureLayerProgram(GLContext *aGL) : LayerProgram(aGL) { } bool Initialize(const char *aVertexShaderString, const char *aFragmentShaderString) { if (!LayerProgram::Initialize(aVertexShaderString, aFragmentShaderString)) return false; const char *uniformNames[] = { "uYTexture", "uCbTexture", "uCrTexture", NULL }; mUniformLocations.SetLength(NumUniforms); GetUniformLocations(uniformNames, &mUniformLocations[NumLayerUniforms]); const char *attribNames[] = { "aTexCoord", NULL }; mAttribLocations.SetLength(NumAttribs); GetAttribLocations(attribNames, &mAttribLocations[NumLayerAttribs]); return true; } void SetYTextureUnit(GLint aUnit) { SetUniform(mUniformLocations[YTextureUniform], aUnit); } void SetCbTextureUnit(GLint aUnit) { SetUniform(mUniformLocations[CbTextureUniform], aUnit); } void SetCrTextureUnit(GLint aUnit) { SetUniform(mUniformLocations[CrTextureUniform], aUnit); } void SetYCbCrTextureUnits(GLint aYUnit, GLint aCbUnit, GLint aCrUnit) { SetUniform(mUniformLocations[YTextureUniform], aYUnit); SetUniform(mUniformLocations[CbTextureUniform], aCbUnit); SetUniform(mUniformLocations[CrTextureUniform], aCrUnit); } }; /* * A SolidColorLayerProgram is a LayerProgram that renders * a solid color. It adds the following attributes and uniforms: * * Uniforms: * uRenderColor - solid color to render; * This should be with premultiplied opacity, as it's written * to the color buffer directly. Layer Opacity is ignored. */ class SolidColorLayerProgram : public LayerProgram { public: enum { RenderColorUniform = NumLayerUniforms, NumUniforms }; enum { NumAttribs = NumLayerAttribs }; SolidColorLayerProgram(GLContext *aGL) : LayerProgram(aGL) { } bool Initialize(const char *aVertexShaderString, const char *aFragmentShaderString) { if (!LayerProgram::Initialize(aVertexShaderString, aFragmentShaderString)) return false; const char *uniformNames[] = { "uRenderColor", NULL }; mUniformLocations.SetLength(NumUniforms); GetUniformLocations(uniformNames, &mUniformLocations[NumLayerUniforms]); return true; } void SetRenderColor(const gfxRGBA& aColor) { SetUniform(mUniformLocations[RenderColorUniform], aColor); } }; /* * A CopyProgram is an OpenGL program that copies a 4-channel texture * to the destination, making no attempt to transform any incoming * vertices. It has the following attributes and uniforms: * * Attribute inputs: * aVertex - vertex coordinate * aTexCoord - texture coordinate * * Uniforms: * uTexture - 2D texture unit which to sample */ class CopyProgram : public LayerManagerOGLProgram { public: enum { TextureUniform = 0, NumUniforms }; enum { VertexCoordAttrib = 0, TexCoordAttrib, NumAttribs }; CopyProgram(GLContext *aGL) : LayerManagerOGLProgram(aGL) { } bool Initialize(const char *aVertexShaderString, const char *aFragmentShaderString) { if (!CreateProgram(aVertexShaderString, aFragmentShaderString)) return false; const char *uniformNames[] = { "uTexture", NULL }; mUniformLocations.SetLength(NumUniforms); GetUniformLocations(uniformNames, &mUniformLocations[0]); const char *attribNames[] = { "aVertexCoord", "aTexCoord", NULL }; mAttribLocations.SetLength(NumAttribs); GetAttribLocations(attribNames, &mAttribLocations[0]); // this is a one-off that's present in the 2DRect versions of some shaders. mTexCoordMultiplierUniformLocation = mGL->fGetUniformLocation(mProgram, "uTexCoordMultiplier"); return true; } GLint AttribLocation(int aWhich) { if (aWhich < 0 || aWhich >= int(mAttribLocations.Length())) return -1; return mAttribLocations[aWhich]; } void SetTextureUnit(GLint aUnit) { SetUniform(mUniformLocations[TextureUniform], aUnit); } GLint GetTexCoordMultiplierUniformLocation() { return mTexCoordMultiplierUniformLocation; } protected: nsTArray mUniformLocations; nsTArray mAttribLocations; GLint mTexCoordMultiplierUniformLocation; }; } /* layers */ } /* mozilla */ #endif /* GFX_LAYERMANAGEROGLPROGRAM_H */