michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_OGLSHADERPROGRAM_H michael@0: #define GFX_OGLSHADERPROGRAM_H michael@0: michael@0: #include "GLContext.h" // for fast inlines of glUniform* michael@0: #include "gfx3DMatrix.h" // for gfx3DMatrix michael@0: #include "gfxTypes.h" michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/RefPtr.h" // for RefPtr michael@0: #include "mozilla/gfx/Matrix.h" // for Matrix4x4 michael@0: #include "mozilla/gfx/Rect.h" // for Rect michael@0: #include "mozilla/gfx/Types.h" michael@0: #include "nsDebug.h" // for NS_ASSERTION michael@0: #include "nsPoint.h" // for nsIntPoint michael@0: #include "nsTArray.h" // for nsTArray michael@0: #include "mozilla/layers/CompositorTypes.h" michael@0: michael@0: #include michael@0: michael@0: struct gfxRGBA; michael@0: struct nsIntRect; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: class Layer; michael@0: michael@0: enum ShaderFeatures { michael@0: ENABLE_RENDER_COLOR=0x01, michael@0: ENABLE_TEXTURE_RECT=0x02, michael@0: ENABLE_TEXTURE_EXTERNAL=0x04, michael@0: ENABLE_TEXTURE_YCBCR=0x08, michael@0: ENABLE_TEXTURE_COMPONENT_ALPHA=0x10, michael@0: ENABLE_TEXTURE_NO_ALPHA=0x20, michael@0: ENABLE_TEXTURE_RB_SWAP=0x40, michael@0: ENABLE_OPACITY=0x80, michael@0: ENABLE_BLUR=0x100, michael@0: ENABLE_COLOR_MATRIX=0x200, michael@0: ENABLE_MASK_2D=0x400, michael@0: ENABLE_MASK_3D=0x800 michael@0: }; michael@0: michael@0: class KnownUniform { michael@0: public: michael@0: enum KnownUniformName { michael@0: NotAKnownUniform = -1, michael@0: michael@0: LayerTransform = 0, michael@0: MaskQuadTransform, michael@0: LayerQuadTransform, michael@0: MatrixProj, michael@0: TextureTransform, michael@0: RenderTargetOffset, michael@0: LayerOpacity, michael@0: Texture, michael@0: YTexture, michael@0: CbTexture, michael@0: CrTexture, michael@0: BlackTexture, michael@0: WhiteTexture, michael@0: MaskTexture, michael@0: RenderColor, michael@0: TexCoordMultiplier, michael@0: TexturePass2, michael@0: michael@0: KnownUniformCount michael@0: }; michael@0: michael@0: KnownUniform() michael@0: { michael@0: mName = NotAKnownUniform; michael@0: mNameString = nullptr; michael@0: mLocation = -1; michael@0: memset(&mValue, 0, sizeof(mValue)); michael@0: } michael@0: michael@0: bool UpdateUniform(int32_t i1) { michael@0: if (mLocation == -1) return false; michael@0: if (mValue.i1 != i1) { michael@0: mValue.i1 = i1; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool UpdateUniform(float f1) { michael@0: if (mLocation == -1) return false; michael@0: if (mValue.f1 != f1) { michael@0: mValue.f1 = f1; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool UpdateUniform(float f1, float f2) { michael@0: if (mLocation == -1) return false; michael@0: if (mValue.f16v[0] != f1 || michael@0: mValue.f16v[1] != f2) michael@0: { michael@0: mValue.f16v[0] = f1; michael@0: mValue.f16v[1] = f2; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool UpdateUniform(float f1, float f2, float f3, float f4) { michael@0: if (mLocation == -1) return false; michael@0: if (mValue.f16v[0] != f1 || michael@0: mValue.f16v[1] != f2 || michael@0: mValue.f16v[2] != f3 || michael@0: mValue.f16v[3] != f4) michael@0: { michael@0: mValue.f16v[0] = f1; michael@0: mValue.f16v[1] = f2; michael@0: mValue.f16v[2] = f3; michael@0: mValue.f16v[3] = f4; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool UpdateUniform(int cnt, const float *fp) { michael@0: if (mLocation == -1) return false; michael@0: switch (cnt) { michael@0: case 1: michael@0: case 2: michael@0: case 3: michael@0: case 4: michael@0: case 16: michael@0: if (memcmp(mValue.f16v, fp, sizeof(float) * cnt) != 0) { michael@0: memcpy(mValue.f16v, fp, sizeof(float) * cnt); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NS_NOTREACHED("cnt must be 1 2 3 4 or 16"); michael@0: return false; michael@0: } michael@0: michael@0: KnownUniformName mName; michael@0: const char *mNameString; michael@0: int32_t mLocation; michael@0: michael@0: union { michael@0: int i1; michael@0: float f1; michael@0: float f16v[16]; michael@0: } mValue; michael@0: }; michael@0: michael@0: class ShaderConfigOGL michael@0: { michael@0: public: michael@0: ShaderConfigOGL() : michael@0: mFeatures(0) {} michael@0: michael@0: void SetRenderColor(bool aEnabled); michael@0: void SetTextureTarget(GLenum aTarget); michael@0: void SetRBSwap(bool aEnabled); michael@0: void SetNoAlpha(bool aEnabled); michael@0: void SetOpacity(bool aEnabled); michael@0: void SetYCbCr(bool aEnabled); michael@0: void SetComponentAlpha(bool aEnabled); michael@0: void SetColorMatrix(bool aEnabled); michael@0: void SetBlur(bool aEnabled); michael@0: void SetMask2D(bool aEnabled); michael@0: void SetMask3D(bool aEnabled); michael@0: michael@0: bool operator< (const ShaderConfigOGL& other) const { michael@0: return mFeatures < other.mFeatures; michael@0: } michael@0: michael@0: public: michael@0: void SetFeature(int aBitmask, bool aState) { michael@0: if (aState) michael@0: mFeatures |= aBitmask; michael@0: else michael@0: mFeatures &= (~aBitmask); michael@0: } michael@0: michael@0: int mFeatures; michael@0: }; michael@0: michael@0: static inline ShaderConfigOGL michael@0: ShaderConfigFromTargetAndFormat(GLenum aTarget, michael@0: gfx::SurfaceFormat aFormat) michael@0: { michael@0: ShaderConfigOGL config; michael@0: config.SetTextureTarget(aTarget); michael@0: config.SetRBSwap(aFormat == gfx::SurfaceFormat::B8G8R8A8 || michael@0: aFormat == gfx::SurfaceFormat::B8G8R8X8); michael@0: config.SetNoAlpha(aFormat == gfx::SurfaceFormat::B8G8R8X8 || michael@0: aFormat == gfx::SurfaceFormat::R8G8B8X8 || michael@0: aFormat == gfx::SurfaceFormat::R5G6B5); michael@0: return config; michael@0: } michael@0: michael@0: /** michael@0: * This struct represents the shaders that make up a program and the uniform michael@0: * and attribute parmeters that those shaders take. michael@0: * It is used by ShaderProgramOGL. michael@0: * Use the factory method GetProfileFor to create instances. michael@0: */ michael@0: struct ProgramProfileOGL michael@0: { michael@0: /** michael@0: * Factory method; creates an instance of this class for the given michael@0: * ShaderConfigOGL michael@0: */ michael@0: static ProgramProfileOGL GetProfileFor(ShaderConfigOGL aConfig); michael@0: michael@0: /** michael@0: * These two methods lookup the location of a uniform and attribute, michael@0: * respectively. Returns -1 if the named uniform/attribute does not michael@0: * have a location for the shaders represented by this profile. michael@0: */ michael@0: GLint LookupAttributeLocation(const char* aName) michael@0: { michael@0: for (uint32_t i = 0; i < mAttributes.Length(); ++i) { michael@0: if (strcmp(mAttributes[i].mName, aName) == 0) { michael@0: return mAttributes[i].mLocation; michael@0: } michael@0: } michael@0: michael@0: return -1; michael@0: } michael@0: michael@0: // represents the name and location of a uniform or attribute michael@0: struct Argument michael@0: { michael@0: Argument(const char* aName) : michael@0: mName(aName) {} michael@0: const char* mName; michael@0: GLint mLocation; michael@0: }; michael@0: michael@0: // the source code for the program's shaders michael@0: std::string mVertexShaderString; michael@0: std::string mFragmentShaderString; michael@0: michael@0: KnownUniform mUniforms[KnownUniform::KnownUniformCount]; michael@0: nsTArray mAttributes; michael@0: nsTArray mDefines; michael@0: uint32_t mTextureCount; michael@0: michael@0: ProgramProfileOGL() : michael@0: mTextureCount(0) michael@0: {} michael@0: }; michael@0: michael@0: michael@0: #if defined(DEBUG) michael@0: #define CHECK_CURRENT_PROGRAM 1 michael@0: #define ASSERT_THIS_PROGRAM \ michael@0: do { \ michael@0: GLuint currentProgram; \ michael@0: mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, ¤tProgram); \ michael@0: NS_ASSERTION(currentProgram == mProgram, \ michael@0: "SetUniform with wrong program active!"); \ michael@0: } while (0) michael@0: #else michael@0: #define ASSERT_THIS_PROGRAM \ michael@0: do { } while (0) michael@0: #endif michael@0: michael@0: /** michael@0: * Represents an OGL shader program. The details of a program are represented michael@0: * by a ProgramProfileOGL michael@0: */ michael@0: class ShaderProgramOGL michael@0: { michael@0: public: michael@0: typedef mozilla::gl::GLContext GLContext; michael@0: michael@0: ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile); michael@0: michael@0: ~ShaderProgramOGL(); michael@0: michael@0: bool HasInitialized() { michael@0: NS_ASSERTION(mProgramState != STATE_OK || mProgram > 0, "Inconsistent program state"); michael@0: return mProgramState == STATE_OK; michael@0: } michael@0: michael@0: void Activate(); michael@0: michael@0: bool Initialize(); michael@0: michael@0: GLint CreateShader(GLenum aShaderType, const char *aShaderSource); michael@0: michael@0: /** michael@0: * Creates a program and stores its id. michael@0: */ michael@0: bool CreateProgram(const char *aVertexShaderString, michael@0: const char *aFragmentShaderString); michael@0: michael@0: /** michael@0: * Lookup the location of an attribute michael@0: */ michael@0: GLint AttribLocation(const char* aName) { michael@0: return mProfile.LookupAttributeLocation(aName); michael@0: } michael@0: michael@0: /** michael@0: * The following set of methods set a uniform argument to the shader program. michael@0: * Not all uniforms may be set for all programs, and such uses will throw michael@0: * an assertion. michael@0: */ michael@0: void SetLayerTransform(const gfx::Matrix4x4& aMatrix) { michael@0: SetMatrixUniform(KnownUniform::LayerTransform, aMatrix); michael@0: } michael@0: michael@0: void SetMaskLayerTransform(const gfx::Matrix4x4& aMatrix) { michael@0: SetMatrixUniform(KnownUniform::MaskQuadTransform, aMatrix); michael@0: } michael@0: michael@0: void SetLayerQuadRect(const nsIntRect& aRect) { michael@0: gfx3DMatrix m; michael@0: m._11 = float(aRect.width); michael@0: m._22 = float(aRect.height); michael@0: m._41 = float(aRect.x); michael@0: m._42 = float(aRect.y); michael@0: SetMatrixUniform(KnownUniform::LayerQuadTransform, m); michael@0: } michael@0: michael@0: void SetLayerQuadRect(const gfx::Rect& aRect) { michael@0: gfx3DMatrix m; michael@0: m._11 = aRect.width; michael@0: m._22 = aRect.height; michael@0: m._41 = aRect.x; michael@0: m._42 = aRect.y; michael@0: SetMatrixUniform(KnownUniform::LayerQuadTransform, m); michael@0: } michael@0: michael@0: void SetProjectionMatrix(const gfx::Matrix4x4& aMatrix) { michael@0: SetMatrixUniform(KnownUniform::MatrixProj, aMatrix); michael@0: } michael@0: michael@0: // sets this program's texture transform, if it uses one michael@0: void SetTextureTransform(const gfx::Matrix4x4& aMatrix) { michael@0: SetMatrixUniform(KnownUniform::TextureTransform, aMatrix); michael@0: } michael@0: michael@0: void SetRenderOffset(const nsIntPoint& aOffset) { michael@0: float vals[4] = { float(aOffset.x), float(aOffset.y), 0.0f, 0.0f }; michael@0: SetUniform(KnownUniform::RenderTargetOffset, 4, vals); michael@0: } michael@0: michael@0: void SetRenderOffset(float aX, float aY) { michael@0: float vals[4] = { aX, aY, 0.0f, 0.0f }; michael@0: SetUniform(KnownUniform::RenderTargetOffset, 4, vals); michael@0: } michael@0: michael@0: void SetLayerOpacity(float aOpacity) { michael@0: SetUniform(KnownUniform::LayerOpacity, aOpacity); michael@0: } michael@0: michael@0: void SetTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::Texture, aUnit); michael@0: } michael@0: void SetYTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::YTexture, aUnit); michael@0: } michael@0: michael@0: void SetCbTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::CbTexture, aUnit); michael@0: } michael@0: michael@0: void SetCrTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::CrTexture, aUnit); michael@0: } michael@0: michael@0: void SetYCbCrTextureUnits(GLint aYUnit, GLint aCbUnit, GLint aCrUnit) { michael@0: SetUniform(KnownUniform::YTexture, aYUnit); michael@0: SetUniform(KnownUniform::CbTexture, aCbUnit); michael@0: SetUniform(KnownUniform::CrTexture, aCrUnit); michael@0: } michael@0: michael@0: void SetBlackTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::BlackTexture, aUnit); michael@0: } michael@0: michael@0: void SetWhiteTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::WhiteTexture, aUnit); michael@0: } michael@0: michael@0: void SetMaskTextureUnit(GLint aUnit) { michael@0: SetUniform(KnownUniform::MaskTexture, aUnit); michael@0: } michael@0: michael@0: void SetRenderColor(const gfxRGBA& aColor) { michael@0: SetUniform(KnownUniform::RenderColor, aColor); michael@0: } michael@0: michael@0: void SetRenderColor(const gfx::Color& aColor) { michael@0: SetUniform(KnownUniform::RenderColor, aColor); michael@0: } michael@0: michael@0: void SetTexCoordMultiplier(float aWidth, float aHeight) { michael@0: float f[] = {aWidth, aHeight}; michael@0: SetUniform(KnownUniform::TexCoordMultiplier, 2, f); michael@0: } michael@0: michael@0: // Set whether we want the component alpha shader to return the color michael@0: // vector (pass 1, false) or the alpha vector (pass2, true). With support michael@0: // for multiple render targets we wouldn't need two passes here. michael@0: void SetTexturePass2(bool aFlag) { michael@0: SetUniform(KnownUniform::TexturePass2, aFlag ? 1 : 0); michael@0: } michael@0: michael@0: // the names of attributes michael@0: static const char* const VertexCoordAttrib; michael@0: static const char* const TexCoordAttrib; michael@0: michael@0: protected: michael@0: RefPtr mGL; michael@0: // the OpenGL id of the program michael@0: GLuint mProgram; michael@0: ProgramProfileOGL mProfile; michael@0: enum { michael@0: STATE_NEW, michael@0: STATE_OK, michael@0: STATE_ERROR michael@0: } mProgramState; michael@0: michael@0: #ifdef CHECK_CURRENT_PROGRAM michael@0: static int sCurrentProgramKey; michael@0: #endif michael@0: michael@0: void SetUniform(KnownUniform::KnownUniformName aKnownUniform, float aFloatValue) michael@0: { michael@0: ASSERT_THIS_PROGRAM; michael@0: NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); michael@0: michael@0: KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); michael@0: if (ku.UpdateUniform(aFloatValue)) { michael@0: mGL->fUniform1f(ku.mLocation, aFloatValue); michael@0: } michael@0: } michael@0: michael@0: void SetUniform(KnownUniform::KnownUniformName aKnownUniform, const gfxRGBA& aColor) michael@0: { michael@0: ASSERT_THIS_PROGRAM; michael@0: NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); michael@0: michael@0: KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); michael@0: if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) { michael@0: mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v); michael@0: } michael@0: } michael@0: michael@0: void SetUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Color& aColor) { michael@0: ASSERT_THIS_PROGRAM; michael@0: NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); michael@0: michael@0: KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); michael@0: if (ku.UpdateUniform(aColor.r, aColor.g, aColor.b, aColor.a)) { michael@0: mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v); michael@0: } michael@0: } michael@0: michael@0: void SetUniform(KnownUniform::KnownUniformName aKnownUniform, int aLength, float *aFloatValues) michael@0: { michael@0: ASSERT_THIS_PROGRAM; michael@0: NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); michael@0: michael@0: KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); michael@0: if (ku.UpdateUniform(aLength, aFloatValues)) { michael@0: switch (aLength) { michael@0: case 1: mGL->fUniform1fv(ku.mLocation, 1, ku.mValue.f16v); break; michael@0: case 2: mGL->fUniform2fv(ku.mLocation, 1, ku.mValue.f16v); break; michael@0: case 3: mGL->fUniform3fv(ku.mLocation, 1, ku.mValue.f16v); break; michael@0: case 4: mGL->fUniform4fv(ku.mLocation, 1, ku.mValue.f16v); break; michael@0: default: michael@0: NS_NOTREACHED("Bogus aLength param"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void SetUniform(KnownUniform::KnownUniformName aKnownUniform, GLint aIntValue) { michael@0: ASSERT_THIS_PROGRAM; michael@0: NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); michael@0: michael@0: KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); michael@0: if (ku.UpdateUniform(aIntValue)) { michael@0: mGL->fUniform1i(ku.mLocation, aIntValue); michael@0: } michael@0: } michael@0: michael@0: void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const float *aFloatValues) { michael@0: ASSERT_THIS_PROGRAM; michael@0: NS_ASSERTION(aKnownUniform >= 0 && aKnownUniform < KnownUniform::KnownUniformCount, "Invalid known uniform"); michael@0: michael@0: KnownUniform& ku(mProfile.mUniforms[aKnownUniform]); michael@0: if (ku.UpdateUniform(16, aFloatValues)) { michael@0: mGL->fUniformMatrix4fv(ku.mLocation, 1, false, ku.mValue.f16v); michael@0: } michael@0: } michael@0: michael@0: void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx3DMatrix& aMatrix) { michael@0: SetMatrixUniform(aKnownUniform, &aMatrix._11); michael@0: } michael@0: michael@0: void SetMatrixUniform(KnownUniform::KnownUniformName aKnownUniform, const gfx::Matrix4x4& aMatrix) { michael@0: SetMatrixUniform(aKnownUniform, &aMatrix._11); michael@0: } michael@0: }; michael@0: michael@0: michael@0: } /* layers */ michael@0: } /* mozilla */ michael@0: michael@0: #endif /* GFX_OGLSHADERPROGRAM_H */