gfx/layers/opengl/OGLShaderProgram.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "OGLShaderProgram.h"
     6 #include <stdint.h>                     // for uint32_t
     7 #include <sstream>                      // for ostringstream
     8 #include "gfxRect.h"                    // for gfxRect
     9 #include "mozilla/DebugOnly.h"          // for DebugOnly
    10 #include "nsAString.h"
    11 #include "nsAutoPtr.h"                  // for nsRefPtr
    12 #include "nsString.h"                   // for nsAutoCString
    13 #include "prenv.h"                      // for PR_GetEnv
    14 #include "Layers.h"
    15 #include "GLContext.h"
    17 struct gfxRGBA;
    19 namespace mozilla {
    20 namespace layers {
    22 using namespace std;
    24 typedef ProgramProfileOGL::Argument Argument;
    26 #define GAUSSIAN_KERNEL_HALF_WIDTH 11
    27 #define GAUSSIAN_KERNEL_STEP 0.2
    29 void
    30 AddUniforms(ProgramProfileOGL& aProfile)
    31 {
    32     static const char *sKnownUniformNames[] = {
    33         "uLayerTransform",
    34         "uMaskQuadTransform",
    35         "uLayerQuadTransform",
    36         "uMatrixProj",
    37         "uTextureTransform",
    38         "uRenderTargetOffset",
    39         "uLayerOpacity",
    40         "uTexture",
    41         "uYTexture",
    42         "uCbTexture",
    43         "uCrTexture",
    44         "uBlackTexture",
    45         "uWhiteTexture",
    46         "uMaskTexture",
    47         "uRenderColor",
    48         "uTexCoordMultiplier",
    49         "uTexturePass2",
    50         nullptr
    51     };
    53     for (int i = 0; sKnownUniformNames[i] != nullptr; ++i) {
    54         aProfile.mUniforms[i].mNameString = sKnownUniformNames[i];
    55         aProfile.mUniforms[i].mName = (KnownUniform::KnownUniformName) i;
    56     }
    57 }
    59 void
    60 ShaderConfigOGL::SetRenderColor(bool aEnabled)
    61 {
    62   SetFeature(ENABLE_RENDER_COLOR, aEnabled);
    63 }
    65 void
    66 ShaderConfigOGL::SetTextureTarget(GLenum aTarget)
    67 {
    68   SetFeature(ENABLE_TEXTURE_EXTERNAL | ENABLE_TEXTURE_RECT, false);
    69   switch (aTarget) {
    70   case LOCAL_GL_TEXTURE_EXTERNAL:
    71     SetFeature(ENABLE_TEXTURE_EXTERNAL, true);
    72     break;
    73   case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
    74     SetFeature(ENABLE_TEXTURE_RECT, true);
    75     break;
    76   }
    77 }
    79 void
    80 ShaderConfigOGL::SetRBSwap(bool aEnabled)
    81 {
    82   SetFeature(ENABLE_TEXTURE_RB_SWAP, aEnabled);
    83 }
    85 void
    86 ShaderConfigOGL::SetNoAlpha(bool aEnabled)
    87 {
    88   SetFeature(ENABLE_TEXTURE_NO_ALPHA, aEnabled);
    89 }
    91 void
    92 ShaderConfigOGL::SetOpacity(bool aEnabled)
    93 {
    94   SetFeature(ENABLE_OPACITY, aEnabled);
    95 }
    97 void
    98 ShaderConfigOGL::SetYCbCr(bool aEnabled)
    99 {
   100   SetFeature(ENABLE_TEXTURE_YCBCR, aEnabled);
   101 }
   103 void
   104 ShaderConfigOGL::SetComponentAlpha(bool aEnabled)
   105 {
   106   SetFeature(ENABLE_TEXTURE_COMPONENT_ALPHA, aEnabled);
   107 }
   109 void
   110 ShaderConfigOGL::SetColorMatrix(bool aEnabled)
   111 {
   112   SetFeature(ENABLE_COLOR_MATRIX, aEnabled);
   113 }
   115 void
   116 ShaderConfigOGL::SetBlur(bool aEnabled)
   117 {
   118   SetFeature(ENABLE_BLUR, aEnabled);
   119 }
   121 void
   122 ShaderConfigOGL::SetMask2D(bool aEnabled)
   123 {
   124   SetFeature(ENABLE_MASK_2D, aEnabled);
   125 }
   127 void
   128 ShaderConfigOGL::SetMask3D(bool aEnabled)
   129 {
   130   SetFeature(ENABLE_MASK_3D, aEnabled);
   131 }
   133 /* static */ ProgramProfileOGL
   134 ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
   135 {
   136   ProgramProfileOGL result;
   137   ostringstream fs, vs;
   139   AddUniforms(result);
   141   vs << "uniform mat4 uMatrixProj;" << endl;
   142   vs << "uniform mat4 uLayerQuadTransform;" << endl;
   143   vs << "uniform mat4 uLayerTransform;" << endl;
   144   vs << "uniform vec4 uRenderTargetOffset;" << endl;
   146   vs << "attribute vec4 aVertexCoord;" << endl;
   148   if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
   149     vs << "uniform mat4 uTextureTransform;" << endl;
   150     vs << "attribute vec2 aTexCoord;" << endl;
   151     vs << "varying vec2 vTexCoord;" << endl;
   152   }
   154   if (aConfig.mFeatures & ENABLE_MASK_2D ||
   155       aConfig.mFeatures & ENABLE_MASK_3D) {
   156     vs << "uniform mat4 uMaskQuadTransform;" << endl;
   157     vs << "varying vec3 vMaskCoord;" << endl;
   158   }
   160   vs << "void main() {" << endl;
   161   vs << "  vec4 finalPosition = aVertexCoord;" << endl;
   162   vs << "  finalPosition = uLayerQuadTransform * finalPosition;" << endl;
   163   vs << "  finalPosition = uLayerTransform * finalPosition;" << endl;
   164   vs << "  finalPosition.xyz /= finalPosition.w;" << endl;
   166   if (aConfig.mFeatures & ENABLE_MASK_3D) {
   167     vs << "  vMaskCoord.xy = (uMaskQuadTransform * vec4(finalPosition.xyz, 1.0)).xy;" << endl;
   168     // correct for perspective correct interpolation, see comment in D3D10 shader
   169     vs << "  vMaskCoord.z = 1.0;" << endl;
   170     vs << "  vMaskCoord *= finalPosition.w;" << endl;
   171   } else if (aConfig.mFeatures & ENABLE_MASK_2D) {
   172     vs << "  vMaskCoord.xy = (uMaskQuadTransform * finalPosition).xy;" << endl;
   173   }
   175   vs << "  finalPosition = finalPosition - uRenderTargetOffset;" << endl;
   176   vs << "  finalPosition.xyz *= finalPosition.w;" << endl;
   177   vs << "  finalPosition = uMatrixProj * finalPosition;" << endl;
   179   if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
   180     vs << "  vTexCoord = (uTextureTransform * vec4(aTexCoord.x, aTexCoord.y, 0.0, 1.0)).xy;" << endl;
   181   }
   183   vs << "  gl_Position = finalPosition;" << endl;
   184   vs << "}" << endl;
   186   if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
   187     fs << "#extension GL_ARB_texture_rectangle : require" << endl;
   188   }
   189   if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
   190     fs << "#extension GL_OES_EGL_image_external : require" << endl;
   191   }
   192   fs << "#ifdef GL_ES" << endl;
   193   fs << "precision mediump float;" << endl;
   194   fs << "#define COLOR_PRECISION lowp" << endl;
   195   fs << "#else" << endl;
   196   fs << "#define COLOR_PRECISION" << endl;
   197   fs << "#endif" << endl;
   198   if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
   199     fs << "uniform COLOR_PRECISION vec4 uRenderColor;" << endl;
   200   } else {
   201     // for tiling, texcoord can be greater than the lowfp range
   202     fs << "varying vec2 vTexCoord;" << endl;
   203     if (aConfig.mFeatures & ENABLE_BLUR) {
   204       fs << "uniform bool uBlurAlpha;" << endl;
   205       fs << "uniform vec2 uBlurRadius;" << endl;
   206       fs << "uniform vec2 uBlurOffset;" << endl;
   207       fs << "uniform float uBlurGaussianKernel[" << GAUSSIAN_KERNEL_HALF_WIDTH << "];" << endl;
   208     }
   209     if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
   210       fs << "uniform mat4 uColorMatrix;" << endl;
   211       fs << "uniform vec4 uColorMatrixVector;" << endl;
   212     }
   213     if (aConfig.mFeatures & ENABLE_OPACITY) {
   214       fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
   215     }
   216   }
   218   const char *sampler2D = "sampler2D";
   219   const char *texture2D = "texture2D";
   221   if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
   222     fs << "uniform vec2 uTexCoordMultiplier;" << endl;
   223     sampler2D = "sampler2DRect";
   224     texture2D = "texture2DRect";
   225   }
   227   if (aConfig.mFeatures & ENABLE_TEXTURE_EXTERNAL) {
   228     sampler2D = "samplerExternalOES";
   229   }
   231   if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
   232     fs << "uniform sampler2D uYTexture;" << endl;
   233     fs << "uniform sampler2D uCbTexture;" << endl;
   234     fs << "uniform sampler2D uCrTexture;" << endl;
   235   } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
   236     fs << "uniform sampler2D uBlackTexture;" << endl;
   237     fs << "uniform sampler2D uWhiteTexture;" << endl;
   238     fs << "uniform bool uTexturePass2;" << endl;
   239   } else {
   240     fs << "uniform " << sampler2D << " uTexture;" << endl;
   241   }
   243   if (aConfig.mFeatures & ENABLE_MASK_2D ||
   244       aConfig.mFeatures & ENABLE_MASK_3D) {
   245     fs << "varying vec3 vMaskCoord;" << endl;
   246     fs << "uniform sampler2D uMaskTexture;" << endl;
   247   }
   249   if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
   250     fs << "vec4 sample(vec2 coord) {" << endl;
   251     fs << "  vec4 color;" << endl;
   252     if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
   253       fs << "  COLOR_PRECISION float y = texture2D(uYTexture, coord).r;" << endl;
   254       fs << "  COLOR_PRECISION float cb = texture2D(uCbTexture, coord).r;" << endl;
   255       fs << "  COLOR_PRECISION float cr = texture2D(uCrTexture, coord).r;" << endl;
   256       fs << "  y = (y - 0.0625) * 1.164;" << endl;
   257       fs << "  cb = cb - 0.5;" << endl;
   258       fs << "  cr = cr - 0.5;" << endl;
   259       fs << "  color.r = y + cr * 1.596;" << endl;
   260       fs << "  color.g = y - 0.813 * cr - 0.391 * cb;" << endl;
   261       fs << "  color.b = y + cb * 2.018;" << endl;
   262       fs << "  color.a = 1.0;" << endl;
   263     } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
   264       fs << "  COLOR_PRECISION vec3 onBlack = texture2D(uBlackTexture, coord).rgb;" << endl;
   265       fs << "  COLOR_PRECISION vec3 onWhite = texture2D(uWhiteTexture, coord).rgb;" << endl;
   266       fs << "  COLOR_PRECISION vec4 alphas = (1.0 - onWhite + onBlack).rgbg;" << endl;
   267       fs << "  if (uTexturePass2)" << endl;
   268       fs << "    color = vec4(onBlack, alphas.a);" << endl;
   269       fs << "  else" << endl;
   270       fs << "    color = alphas;" << endl;
   271     } else {
   272       fs << "  color = " << texture2D << "(uTexture, coord);" << endl;
   273     }
   274     if (aConfig.mFeatures & ENABLE_TEXTURE_RB_SWAP) {
   275       fs << "  color = color.bgra;" << endl;
   276     }
   277     if (aConfig.mFeatures & ENABLE_TEXTURE_NO_ALPHA) {
   278       fs << "  color = vec4(color.rgb, 1.0);" << endl;
   279     }
   280     fs << "  return color;" << endl;
   281     fs << "}" << endl;
   282     if (aConfig.mFeatures & ENABLE_BLUR) {
   283       fs << "vec4 sampleAtRadius(vec2 coord, float radius) {" << endl;
   284       fs << "  coord += uBlurOffset;" << endl;
   285       fs << "  coord += radius * uBlurRadius;" << endl;
   286       fs << "  if (coord.x < 0. || coord.y < 0. || coord.x > 1. || coord.y > 1.)" << endl;
   287       fs << "    return vec4(0, 0, 0, 0);" << endl;
   288       fs << "  return sample(coord);" << endl;
   289       fs << "}" << endl;
   290       fs << "vec4 blur(vec4 color, vec2 coord) {" << endl;
   291       fs << "  vec4 total = color * uBlurGaussianKernel[0];" << endl;
   292       fs << "  for (int i = 1; i < " << GAUSSIAN_KERNEL_HALF_WIDTH << "; ++i) {" << endl;
   293       fs << "    float r = float(i) * " << GAUSSIAN_KERNEL_STEP << " << endl;" << endl;
   294       fs << "    float k = uBlurGaussianKernel[i];" << endl;
   295       fs << "    total += sampleAtRadius(coord, r) * k;" << endl;
   296       fs << "    total += sampleAtRadius(coord, -r) * k;" << endl;
   297       fs << "  }" << endl;
   298       fs << "  if (uBlurAlpha) {" << endl;
   299       fs << "    color *= total.a;" << endl;
   300       fs << "  } else {" << endl;
   301       fs << "    color = total;" << endl;
   302       fs << "  }" << endl;
   303       fs << "  return color;" << endl;
   304       fs << "}" << endl;
   305     }
   306   }
   307   fs << "void main() {" << endl;
   308   if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
   309     fs << "  vec4 color = uRenderColor;" << endl;
   310   } else {
   311     if (aConfig.mFeatures & ENABLE_TEXTURE_RECT) {
   312       fs << "  vec4 color = sample(vTexCoord * uTexCoordMultiplier);" << endl;
   313     } else {
   314       fs << "  vec4 color = sample(vTexCoord);" << endl;
   315     }
   316     if (aConfig.mFeatures & ENABLE_BLUR) {
   317       fs << "  color = blur(color, vTexCoord);" << endl;
   318     }
   319     if (aConfig.mFeatures & ENABLE_COLOR_MATRIX) {
   320       fs << "  color = uColorMatrix * vec4(color.rgb / color.a, color.a) + uColorMatrixVector;" << endl;
   321       fs << "  color.rgb *= color.a;" << endl;
   322     }
   323     if (aConfig.mFeatures & ENABLE_OPACITY) {
   324       fs << "  color *= uLayerOpacity;" << endl;
   325     }
   326   }
   327   if (aConfig.mFeatures & ENABLE_MASK_3D) {
   328     fs << "  vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
   329     fs << "  COLOR_PRECISION float mask = texture2D(uMaskTexture, maskCoords).r;" << endl;
   330     fs << "  color *= mask;" << endl;
   331   } else if (aConfig.mFeatures & ENABLE_MASK_2D) {
   332     fs << "  COLOR_PRECISION float mask = texture2D(uMaskTexture, vMaskCoord.xy).r;" << endl;
   333     fs << "  color *= mask;" << endl;
   334   } else {
   335     fs << "  COLOR_PRECISION float mask = 1.0;" << endl;
   336     fs << "  color *= mask;" << endl;
   337   }
   338   fs << "  gl_FragColor = color;" << endl;
   339   fs << "}" << endl;
   341   result.mVertexShaderString = vs.str();
   342   result.mFragmentShaderString = fs.str();
   344   result.mAttributes.AppendElement(Argument("aVertexCoord"));
   345   if (aConfig.mFeatures & ENABLE_RENDER_COLOR) {
   346     result.mTextureCount = 0;
   347   } else {
   348     result.mAttributes.AppendElement(Argument("aTexCoord"));
   349     if (aConfig.mFeatures & ENABLE_TEXTURE_YCBCR) {
   350       result.mTextureCount = 3;
   351     } else if (aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA) {
   352       result.mTextureCount = 2;
   353     } else {
   354       result.mTextureCount = 1;
   355     }
   356   }
   357   if (aConfig.mFeatures & ENABLE_MASK_2D ||
   358       aConfig.mFeatures & ENABLE_MASK_3D) {
   359     result.mTextureCount = 1;
   360   }
   362   return result;
   363 }
   365 const char* const ShaderProgramOGL::VertexCoordAttrib = "aVertexCoord";
   366 const char* const ShaderProgramOGL::TexCoordAttrib = "aTexCoord";
   368 ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile)
   369   : mGL(aGL)
   370   , mProgram(0)
   371   , mProfile(aProfile)
   372   , mProgramState(STATE_NEW)
   373 {
   374 }
   376 ShaderProgramOGL::~ShaderProgramOGL()
   377 {
   378   if (mProgram <= 0) {
   379     return;
   380   }
   382   nsRefPtr<GLContext> ctx = mGL->GetSharedContext();
   383   if (!ctx) {
   384     ctx = mGL;
   385   }
   386   ctx->MakeCurrent();
   387   ctx->fDeleteProgram(mProgram);
   388 }
   390 bool
   391 ShaderProgramOGL::Initialize()
   392 {
   393   NS_ASSERTION(mProgramState == STATE_NEW, "Shader program has already been initialised");
   395   ostringstream vs, fs;
   396   for (uint32_t i = 0; i < mProfile.mDefines.Length(); ++i) {
   397     vs << mProfile.mDefines[i] << endl;
   398     fs << mProfile.mDefines[i] << endl;
   399   }
   400   vs << mProfile.mVertexShaderString << endl;
   401   fs << mProfile.mFragmentShaderString << endl;
   403   if (!CreateProgram(vs.str().c_str(), fs.str().c_str())) {
   404     mProgramState = STATE_ERROR;
   405     return false;
   406   }
   408   mProgramState = STATE_OK;
   410   for (uint32_t i = 0; i < KnownUniform::KnownUniformCount; ++i) {
   411     mProfile.mUniforms[i].mLocation =
   412       mGL->fGetUniformLocation(mProgram, mProfile.mUniforms[i].mNameString);
   413   }
   415   for (uint32_t i = 0; i < mProfile.mAttributes.Length(); ++i) {
   416     mProfile.mAttributes[i].mLocation =
   417       mGL->fGetAttribLocation(mProgram, mProfile.mAttributes[i].mName);
   418     NS_ASSERTION(mProfile.mAttributes[i].mLocation >= 0, "Bad attribute location.");
   419   }
   421   //mProfile.mHasMatrixProj = mProfile.mUniforms[KnownUniform::MatrixProj].mLocation != -1;
   423   return true;
   424 }
   426 GLint
   427 ShaderProgramOGL::CreateShader(GLenum aShaderType, const char *aShaderSource)
   428 {
   429   GLint success, len = 0;
   431   GLint sh = mGL->fCreateShader(aShaderType);
   432   mGL->fShaderSource(sh, 1, (const GLchar**)&aShaderSource, nullptr);
   433   mGL->fCompileShader(sh);
   434   mGL->fGetShaderiv(sh, LOCAL_GL_COMPILE_STATUS, &success);
   435   mGL->fGetShaderiv(sh, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
   436   /* Even if compiling is successful, there may still be warnings.  Print them
   437    * in a debug build.  The > 10 is to catch silly compilers that might put
   438    * some whitespace in the log but otherwise leave it empty.
   439    */
   440   if (!success
   441 #ifdef DEBUG
   442       || (len > 10 && PR_GetEnv("MOZ_DEBUG_SHADERS"))
   443 #endif
   444       )
   445   {
   446     nsAutoCString log;
   447     log.SetCapacity(len);
   448     mGL->fGetShaderInfoLog(sh, len, (GLint*) &len, (char*) log.BeginWriting());
   449     log.SetLength(len);
   451     if (!success) {
   452       printf_stderr("=== SHADER COMPILATION FAILED ===\n");
   453     } else {
   454       printf_stderr("=== SHADER COMPILATION WARNINGS ===\n");
   455     }
   457       printf_stderr("=== Source:\n%s\n", aShaderSource);
   458       printf_stderr("=== Log:\n%s\n", log.get());
   459       printf_stderr("============\n");
   461     if (!success) {
   462       mGL->fDeleteShader(sh);
   463       return 0;
   464     }
   465   }
   467   return sh;
   468 }
   470 bool
   471 ShaderProgramOGL::CreateProgram(const char *aVertexShaderString,
   472                                 const char *aFragmentShaderString)
   473 {
   474   GLuint vertexShader = CreateShader(LOCAL_GL_VERTEX_SHADER, aVertexShaderString);
   475   GLuint fragmentShader = CreateShader(LOCAL_GL_FRAGMENT_SHADER, aFragmentShaderString);
   477   if (!vertexShader || !fragmentShader)
   478     return false;
   480   GLint result = mGL->fCreateProgram();
   481   mGL->fAttachShader(result, vertexShader);
   482   mGL->fAttachShader(result, fragmentShader);
   484   mGL->fLinkProgram(result);
   486   GLint success, len;
   487   mGL->fGetProgramiv(result, LOCAL_GL_LINK_STATUS, &success);
   488   mGL->fGetProgramiv(result, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
   489   /* Even if linking is successful, there may still be warnings.  Print them
   490    * in a debug build.  The > 10 is to catch silly compilers that might put
   491    * some whitespace in the log but otherwise leave it empty.
   492    */
   493   if (!success
   494 #ifdef DEBUG
   495       || (len > 10 && PR_GetEnv("MOZ_DEBUG_SHADERS"))
   496 #endif
   497       )
   498   {
   499     nsAutoCString log;
   500     log.SetCapacity(len);
   501     mGL->fGetProgramInfoLog(result, len, (GLint*) &len, (char*) log.BeginWriting());
   502     log.SetLength(len);
   504     if (!success) {
   505       printf_stderr("=== PROGRAM LINKING FAILED ===\n");
   506     } else {
   507       printf_stderr("=== PROGRAM LINKING WARNINGS ===\n");
   508     }
   509     printf_stderr("=== Log:\n%s\n", log.get());
   510     printf_stderr("============\n");
   511   }
   513   // We can mark the shaders for deletion; they're attached to the program
   514   // and will remain attached.
   515   mGL->fDeleteShader(vertexShader);
   516   mGL->fDeleteShader(fragmentShader);
   518   if (!success) {
   519     mGL->fDeleteProgram(result);
   520     return false;
   521   }
   523   mProgram = result;
   524   return true;
   525 }
   527 void
   528 ShaderProgramOGL::Activate()
   529 {
   530   if (mProgramState == STATE_NEW) {
   531     if (!Initialize()) {
   532       NS_WARNING("Shader could not be initialised");
   533       return;
   534     }
   535   }
   536   NS_ASSERTION(HasInitialized(), "Attempting to activate a program that's not in use!");
   537   mGL->fUseProgram(mProgram);
   538 }
   540 } /* layers */
   541 } /* mozilla */

mercurial