Home | History | Annotate | Download | only in filterfw
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.media.filterfw;
     18 
     19 import android.graphics.RectF;
     20 import android.opengl.GLES20;
     21 import android.util.Log;
     22 
     23 import androidx.media.filterfw.geometry.Quad;
     24 
     25 import java.nio.ByteBuffer;
     26 import java.nio.ByteOrder;
     27 import java.nio.FloatBuffer;
     28 import java.util.Arrays;
     29 import java.util.HashMap;
     30 
     31 /**
     32  * Convenience class to perform GL shader operations on image data.
     33  * <p>
     34  * The ImageShader class greatly simplifies the task of running GL shader language kernels over
     35  * Frame data buffers that contain RGBA image data.
     36  * </p><p>
     37  * TODO: More documentation
     38  * </p>
     39  */
     40 public class ImageShader {
     41 
     42     private int mProgram = 0;
     43     private boolean mClearsOutput = false;
     44     private float[] mClearColor = { 0f, 0f, 0f, 0f };
     45     private boolean mBlendEnabled = false;
     46     private int mSFactor = GLES20.GL_SRC_ALPHA;
     47     private int mDFactor = GLES20.GL_ONE_MINUS_SRC_ALPHA;
     48     private int mDrawMode = GLES20.GL_TRIANGLE_STRIP;
     49     private int mVertexCount = 4;
     50     private int mBaseTexUnit = GLES20.GL_TEXTURE0;
     51     private int mClearBuffers = GLES20.GL_COLOR_BUFFER_BIT;
     52     private float[] mSourceCoords = new float[] { 0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f };
     53     private float[] mTargetCoords = new float[] { -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f };
     54 
     55     private HashMap<String, ProgramUniform> mUniforms;
     56     private HashMap<String, VertexAttribute> mAttributes = new HashMap<String, VertexAttribute>();
     57 
     58     private final static int FLOAT_SIZE = 4;
     59 
     60     private final static String mDefaultVertexShader =
     61         "attribute vec4 a_position;\n" +
     62         "attribute vec2 a_texcoord;\n" +
     63         "varying vec2 v_texcoord;\n" +
     64         "void main() {\n" +
     65         "  gl_Position = a_position;\n" +
     66         "  v_texcoord = a_texcoord;\n" +
     67         "}\n";
     68 
     69     private final static String mIdentityShader =
     70         "precision mediump float;\n" +
     71         "uniform sampler2D tex_sampler_0;\n" +
     72         "varying vec2 v_texcoord;\n" +
     73         "void main() {\n" +
     74         "  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
     75         "}\n";
     76 
     77     private static class VertexAttribute {
     78         private String mName;
     79         private boolean mIsConst;
     80         private int mIndex;
     81         private boolean mShouldNormalize;
     82         private int mOffset;
     83         private int mStride;
     84         private int mComponents;
     85         private int mType;
     86         private int mVbo;
     87         private int mLength;
     88         private FloatBuffer mValues;
     89 
     90         public VertexAttribute(String name, int index) {
     91             mName = name;
     92             mIndex = index;
     93             mLength = -1;
     94         }
     95 
     96         public void set(boolean normalize, int stride, int components, int type, float[] values) {
     97             mIsConst = false;
     98             mShouldNormalize = normalize;
     99             mStride = stride;
    100             mComponents = components;
    101             mType = type;
    102             mVbo = 0;
    103             if (mLength != values.length){
    104                 initBuffer(values);
    105                 mLength = values.length;
    106             }
    107             copyValues(values);
    108         }
    109 
    110         public void set(boolean normalize, int offset, int stride, int components, int type,
    111                 int vbo){
    112             mIsConst = false;
    113             mShouldNormalize = normalize;
    114             mOffset = offset;
    115             mStride = stride;
    116             mComponents = components;
    117             mType = type;
    118             mVbo = vbo;
    119             mValues = null;
    120         }
    121 
    122         public boolean push() {
    123             if (mIsConst) {
    124                 switch (mComponents) {
    125                     case 1:
    126                         GLES20.glVertexAttrib1fv(mIndex, mValues);
    127                         break;
    128                     case 2:
    129                         GLES20.glVertexAttrib2fv(mIndex, mValues);
    130                         break;
    131                     case 3:
    132                         GLES20.glVertexAttrib3fv(mIndex, mValues);
    133                         break;
    134                     case 4:
    135                         GLES20.glVertexAttrib4fv(mIndex, mValues);
    136                         break;
    137                     default:
    138                         return false;
    139                 }
    140                 GLES20.glDisableVertexAttribArray(mIndex);
    141             } else {
    142                 if (mValues != null) {
    143                     // Note that we cannot do any size checking here, as the correct component
    144                     // count depends on the drawing step. GL should catch such errors then, and
    145                     // we will report them to the user.
    146                     GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    147                     GLES20.glVertexAttribPointer(mIndex,
    148                                                  mComponents,
    149                                                  mType,
    150                                                  mShouldNormalize,
    151                                                  mStride,
    152                                                  mValues);
    153                 } else {
    154                     GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVbo);
    155                     GLES20.glVertexAttribPointer(mIndex,
    156                                                  mComponents,
    157                                                  mType,
    158                                                  mShouldNormalize,
    159                                                  mStride,
    160                                                  mOffset);
    161                 }
    162                 GLES20.glEnableVertexAttribArray(mIndex);
    163             }
    164             GLToolbox.checkGlError("Set vertex-attribute values");
    165             return true;
    166         }
    167 
    168         @Override
    169         public String toString() {
    170             return mName;
    171         }
    172 
    173         private void initBuffer(float[] values) {
    174             mValues = ByteBuffer.allocateDirect(values.length * FLOAT_SIZE)
    175                 .order(ByteOrder.nativeOrder()).asFloatBuffer();
    176         }
    177 
    178         private void copyValues(float[] values) {
    179             mValues.put(values).position(0);
    180         }
    181 
    182     }
    183 
    184     private static final class ProgramUniform {
    185         private String mName;
    186         private int mLocation;
    187         private int mType;
    188         private int mSize;
    189 
    190         public ProgramUniform(int program, int index) {
    191             int[] len = new int[1];
    192             GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, len, 0);
    193 
    194             int[] type = new int[1];
    195             int[] size = new int[1];
    196             byte[] name = new byte[len[0]];
    197             int[] ignore = new int[1];
    198 
    199             GLES20.glGetActiveUniform(program, index, len[0], ignore, 0, size, 0, type, 0, name, 0);
    200             mName = new String(name, 0, strlen(name));
    201             mLocation = GLES20.glGetUniformLocation(program, mName);
    202             mType = type[0];
    203             mSize = size[0];
    204             GLToolbox.checkGlError("Initializing uniform");
    205         }
    206 
    207         public String getName() {
    208             return mName;
    209         }
    210 
    211         public int getType() {
    212             return mType;
    213         }
    214 
    215         public int getLocation() {
    216             return mLocation;
    217         }
    218 
    219         public int getSize() {
    220             return mSize;
    221         }
    222     }
    223 
    224     public ImageShader(String fragmentShader) {
    225         mProgram = createProgram(mDefaultVertexShader, fragmentShader);
    226         scanUniforms();
    227     }
    228 
    229     public ImageShader(String vertexShader, String fragmentShader) {
    230         mProgram = createProgram(vertexShader, fragmentShader);
    231         scanUniforms();
    232     }
    233 
    234     public static ImageShader createIdentity() {
    235         return new ImageShader(mIdentityShader);
    236     }
    237 
    238     public static ImageShader createIdentity(String vertexShader) {
    239         return new ImageShader(vertexShader, mIdentityShader);
    240     }
    241 
    242     public static void renderTextureToTarget(TextureSource texture,
    243                                              RenderTarget target,
    244                                              int width,
    245                                              int height) {
    246         ImageShader shader = RenderTarget.currentTarget().getIdentityShader();
    247         shader.process(texture, target, width, height);
    248     }
    249 
    250     public void process(FrameImage2D input, FrameImage2D output) {
    251         TextureSource texSource = input.lockTextureSource();
    252         RenderTarget renderTarget = output.lockRenderTarget();
    253         processMulti(new TextureSource[] { texSource },
    254                      renderTarget,
    255                      output.getWidth(),
    256                      output.getHeight());
    257         input.unlock();
    258         output.unlock();
    259     }
    260 
    261     public void processMulti(FrameImage2D[] inputs, FrameImage2D output) {
    262         TextureSource[] texSources = new TextureSource[inputs.length];
    263         for (int i = 0; i < inputs.length; ++i) {
    264             texSources[i] = inputs[i].lockTextureSource();
    265         }
    266         RenderTarget renderTarget = output.lockRenderTarget();
    267         processMulti(texSources,
    268                      renderTarget,
    269                      output.getWidth(),
    270                      output.getHeight());
    271         for (FrameImage2D input : inputs) {
    272             input.unlock();
    273         }
    274         output.unlock();
    275     }
    276 
    277     public void process(TextureSource texture, RenderTarget target, int width, int height) {
    278         processMulti(new TextureSource[] { texture }, target, width, height);
    279     }
    280 
    281     public void processMulti(TextureSource[] sources, RenderTarget target, int width, int height) {
    282         GLToolbox.checkGlError("Unknown Operation");
    283         checkExecutable();
    284         checkTexCount(sources.length);
    285         focusTarget(target, width, height);
    286         pushShaderState();
    287         bindInputTextures(sources);
    288         render();
    289     }
    290 
    291     public void processNoInput(FrameImage2D output) {
    292         RenderTarget renderTarget = output.lockRenderTarget();
    293         processNoInput(renderTarget, output.getWidth(), output.getHeight());
    294         output.unlock();
    295     }
    296 
    297     public void processNoInput(RenderTarget target, int width, int height) {
    298         processMulti(new TextureSource[] {}, target, width, height);
    299     }
    300 
    301     public int getUniformLocation(String name) {
    302         return getProgramUniform(name, true).getLocation();
    303     }
    304 
    305     public int getAttributeLocation(String name) {
    306         if (name.equals(positionAttributeName()) || name.equals(texCoordAttributeName())) {
    307             Log.w("ImageShader", "Attempting to access internal attribute '" + name
    308                 + "' directly!");
    309         }
    310         int loc = GLES20.glGetAttribLocation(mProgram, name);
    311         if (loc < 0) {
    312             throw new RuntimeException("Unknown attribute '" + name + "' in shader program!");
    313         }
    314         return loc;
    315     }
    316 
    317     public void setUniformValue(String uniformName, int value) {
    318         useProgram();
    319         int uniformHandle = getUniformLocation(uniformName);
    320         GLES20.glUniform1i(uniformHandle, value);
    321         GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
    322     }
    323 
    324     public void setUniformValue(String uniformName, float value) {
    325         useProgram();
    326         int uniformHandle = getUniformLocation(uniformName);
    327         GLES20.glUniform1f(uniformHandle, value);
    328         GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
    329     }
    330 
    331     public void setUniformValue(String uniformName, int[] values) {
    332         ProgramUniform uniform = getProgramUniform(uniformName, true);
    333         useProgram();
    334         int len = values.length;
    335         switch (uniform.getType()) {
    336             case GLES20.GL_INT:
    337                 checkUniformAssignment(uniform, len, 1);
    338                 GLES20.glUniform1iv(uniform.getLocation(), len, values, 0);
    339                 break;
    340             case GLES20.GL_INT_VEC2:
    341                 checkUniformAssignment(uniform, len, 2);
    342                 GLES20.glUniform2iv(uniform.getLocation(), len / 2, values, 0);
    343                 break;
    344             case GLES20.GL_INT_VEC3:
    345                 checkUniformAssignment(uniform, len, 3);
    346                 GLES20.glUniform2iv(uniform.getLocation(), len / 3, values, 0);
    347                 break;
    348             case GLES20.GL_INT_VEC4:
    349                 checkUniformAssignment(uniform, len, 4);
    350                 GLES20.glUniform2iv(uniform.getLocation(), len / 4, values, 0);
    351                 break;
    352             default:
    353                 throw new RuntimeException("Cannot assign int-array to incompatible uniform type "
    354                     + "for uniform '" + uniformName + "'!");
    355         }
    356         GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
    357     }
    358 
    359 
    360     public void setUniformValue(String uniformName, float[] values) {
    361         ProgramUniform uniform = getProgramUniform(uniformName, true);
    362         useProgram();
    363         int len = values.length;
    364         switch (uniform.getType()) {
    365             case GLES20.GL_FLOAT:
    366                 checkUniformAssignment(uniform, len, 1);
    367                 GLES20.glUniform1fv(uniform.getLocation(), len, values, 0);
    368                 break;
    369             case GLES20.GL_FLOAT_VEC2:
    370                 checkUniformAssignment(uniform, len, 2);
    371                 GLES20.glUniform2fv(uniform.getLocation(), len / 2, values, 0);
    372                 break;
    373             case GLES20.GL_FLOAT_VEC3:
    374                 checkUniformAssignment(uniform, len, 3);
    375                 GLES20.glUniform3fv(uniform.getLocation(), len / 3, values, 0);
    376                 break;
    377             case GLES20.GL_FLOAT_VEC4:
    378                 checkUniformAssignment(uniform, len, 4);
    379                 GLES20.glUniform4fv(uniform.getLocation(), len / 4, values, 0);
    380                 break;
    381             case GLES20.GL_FLOAT_MAT2:
    382                 checkUniformAssignment(uniform, len, 4);
    383                 GLES20.glUniformMatrix2fv(uniform.getLocation(), len / 4, false, values, 0);
    384                 break;
    385             case GLES20.GL_FLOAT_MAT3:
    386                 checkUniformAssignment(uniform, len, 9);
    387                 GLES20.glUniformMatrix3fv(uniform.getLocation(), len / 9, false, values, 0);
    388                 break;
    389             case GLES20.GL_FLOAT_MAT4:
    390                 checkUniformAssignment(uniform, len, 16);
    391                 GLES20.glUniformMatrix4fv(uniform.getLocation(), len / 16, false, values, 0);
    392                 break;
    393             default:
    394                 throw new RuntimeException("Cannot assign float-array to incompatible uniform type "
    395                     + "for uniform '" + uniformName + "'!");
    396         }
    397         GLToolbox.checkGlError("Set uniform value (" + uniformName + ")");
    398     }
    399 
    400     public void setAttributeValues(String attributeName, float[] data, int components) {
    401         VertexAttribute attr = getProgramAttribute(attributeName, true);
    402         attr.set(false, FLOAT_SIZE * components, components, GLES20.GL_FLOAT, data);
    403     }
    404 
    405     public void setAttributeValues(String attributeName, int vbo, int type, int components,
    406                                    int stride, int offset, boolean normalize) {
    407         VertexAttribute attr = getProgramAttribute(attributeName, true);
    408         attr.set(normalize, offset, stride, components, type, vbo);
    409     }
    410 
    411     public void setSourceRect(float x, float y, float width, float height) {
    412         setSourceCoords(new float[] { x, y, x + width, y, x, y + height, x + width, y + height });
    413     }
    414 
    415     public void setSourceRect(RectF rect) {
    416         setSourceRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
    417     }
    418 
    419     public void setSourceQuad(Quad quad) {
    420         setSourceCoords(new float[] { quad.topLeft().x,     quad.topLeft().y,
    421                                       quad.topRight().x,    quad.topRight().y,
    422                                       quad.bottomLeft().x,  quad.bottomLeft().y,
    423                                       quad.bottomRight().x, quad.bottomRight().y });
    424     }
    425 
    426     public void setSourceCoords(float[] coords) {
    427         if (coords.length != 8) {
    428             throw new IllegalArgumentException("Expected 8 coordinates as source coordinates but "
    429                 + "got " + coords.length + " coordinates!");
    430         }
    431         mSourceCoords = Arrays.copyOf(coords, 8);
    432     }
    433 
    434     public void setSourceTransform(float[] matrix) {
    435         if (matrix.length != 16) {
    436             throw new IllegalArgumentException("Expected 4x4 matrix for source transform!");
    437         }
    438         setSourceCoords(new float[] {
    439             matrix[12],
    440             matrix[13],
    441 
    442             matrix[0] + matrix[12],
    443             matrix[1] + matrix[13],
    444 
    445             matrix[4] + matrix[12],
    446             matrix[5] + matrix[13],
    447 
    448             matrix[0] + matrix[4] + matrix[12],
    449             matrix[1] + matrix[5] + matrix[13],
    450         });
    451     }
    452 
    453     public void setTargetRect(float x, float y, float width, float height) {
    454         setTargetCoords(new float[] { x, y,
    455                                       x + width, y,
    456                                       x, y + height,
    457                                       x + width, y + height });
    458     }
    459 
    460     public void setTargetRect(RectF rect) {
    461         setTargetCoords(new float[] { rect.left,    rect.top,
    462                                       rect.right,   rect.top,
    463                                       rect.left,    rect.bottom,
    464                                       rect.right,   rect.bottom });
    465     }
    466 
    467     public void setTargetQuad(Quad quad) {
    468         setTargetCoords(new float[] { quad.topLeft().x,     quad.topLeft().y,
    469                                       quad.topRight().x,    quad.topRight().y,
    470                                       quad.bottomLeft().x,  quad.bottomLeft().y,
    471                                       quad.bottomRight().x, quad.bottomRight().y });
    472     }
    473 
    474     public void setTargetCoords(float[] coords) {
    475         if (coords.length != 8) {
    476             throw new IllegalArgumentException("Expected 8 coordinates as target coordinates but "
    477                 + "got " + coords.length + " coordinates!");
    478         }
    479         mTargetCoords = new float[8];
    480         for (int i = 0; i < 8; ++i) {
    481             mTargetCoords[i] = coords[i] * 2f - 1f;
    482         }
    483     }
    484 
    485     public void setTargetTransform(float[] matrix) {
    486         if (matrix.length != 16) {
    487             throw new IllegalArgumentException("Expected 4x4 matrix for target transform!");
    488         }
    489         setTargetCoords(new float[] {
    490             matrix[12],
    491             matrix[13],
    492 
    493             matrix[0] + matrix[12],
    494             matrix[1] + matrix[13],
    495 
    496             matrix[4] + matrix[12],
    497             matrix[5] + matrix[13],
    498 
    499             matrix[0] + matrix[4] + matrix[12],
    500             matrix[1] + matrix[5] + matrix[13],
    501         });
    502     }
    503 
    504     public void setClearsOutput(boolean clears) {
    505         mClearsOutput = clears;
    506     }
    507 
    508     public boolean getClearsOutput() {
    509         return mClearsOutput;
    510     }
    511 
    512     public void setClearColor(float[] rgba) {
    513         mClearColor = rgba;
    514     }
    515 
    516     public float[] getClearColor() {
    517         return mClearColor;
    518     }
    519 
    520     public void setClearBufferMask(int bufferMask) {
    521         mClearBuffers = bufferMask;
    522     }
    523 
    524     public int getClearBufferMask() {
    525         return mClearBuffers;
    526     }
    527 
    528     public void setBlendEnabled(boolean enable) {
    529         mBlendEnabled = enable;
    530     }
    531 
    532     public boolean getBlendEnabled() {
    533         return mBlendEnabled;
    534     }
    535 
    536     public void setBlendFunc(int sFactor, int dFactor) {
    537         mSFactor = sFactor;
    538         mDFactor = dFactor;
    539     }
    540 
    541     public void setDrawMode(int drawMode) {
    542         mDrawMode = drawMode;
    543     }
    544 
    545     public int getDrawMode() {
    546         return mDrawMode;
    547     }
    548 
    549     public void setVertexCount(int count) {
    550         mVertexCount = count;
    551     }
    552 
    553     public int getVertexCount() {
    554         return mVertexCount;
    555     }
    556 
    557     public void setBaseTextureUnit(int baseTexUnit) {
    558         mBaseTexUnit = baseTexUnit;
    559     }
    560 
    561     public int baseTextureUnit() {
    562         return mBaseTexUnit;
    563     }
    564 
    565     public String texCoordAttributeName() {
    566         return "a_texcoord";
    567     }
    568 
    569     public String positionAttributeName() {
    570         return "a_position";
    571     }
    572 
    573     public String inputTextureUniformName(int index) {
    574         return "tex_sampler_" + index;
    575     }
    576 
    577     public static int maxTextureUnits() {
    578         return GLES20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
    579     }
    580 
    581     @Override
    582     protected void finalize() throws Throwable {
    583         GLES20.glDeleteProgram(mProgram);
    584     }
    585 
    586     protected void pushShaderState() {
    587         useProgram();
    588         updateSourceCoordAttribute();
    589         updateTargetCoordAttribute();
    590         pushAttributes();
    591         if (mClearsOutput) {
    592             GLES20.glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]);
    593             GLES20.glClear(mClearBuffers);
    594         }
    595         if (mBlendEnabled) {
    596             GLES20.glEnable(GLES20.GL_BLEND);
    597             GLES20.glBlendFunc(mSFactor, mDFactor);
    598         } else {
    599             GLES20.glDisable(GLES20.GL_BLEND);
    600         }
    601         GLToolbox.checkGlError("Set render variables");
    602     }
    603 
    604     private void focusTarget(RenderTarget target, int width, int height) {
    605         target.focus();
    606         GLES20.glViewport(0, 0, width, height);
    607         GLToolbox.checkGlError("glViewport");
    608     }
    609 
    610     private void bindInputTextures(TextureSource[] sources) {
    611         for (int i = 0; i < sources.length; ++i) {
    612             // Activate texture unit i
    613             GLES20.glActiveTexture(baseTextureUnit() + i);
    614 
    615             // Bind texture
    616             sources[i].bind();
    617 
    618             // Assign the texture uniform in the shader to unit i
    619             int texUniform = GLES20.glGetUniformLocation(mProgram, inputTextureUniformName(i));
    620             if (texUniform >= 0) {
    621                 GLES20.glUniform1i(texUniform, i);
    622             } else {
    623                 throw new RuntimeException("Shader does not seem to support " + sources.length
    624                     + " number of input textures! Missing uniform " + inputTextureUniformName(i)
    625                     + "!");
    626             }
    627             GLToolbox.checkGlError("Binding input texture " + i);
    628         }
    629     }
    630 
    631     private void pushAttributes() {
    632         for (VertexAttribute attr : mAttributes.values()) {
    633             if (!attr.push()) {
    634                 throw new RuntimeException("Unable to assign attribute value '" + attr + "'!");
    635             }
    636         }
    637         GLToolbox.checkGlError("Push Attributes");
    638     }
    639 
    640     private void updateSourceCoordAttribute() {
    641         // If attribute does not exist, simply do nothing (may be custom shader).
    642         VertexAttribute attr = getProgramAttribute(texCoordAttributeName(), false);
    643         // A non-null value of mSourceCoords indicates new values to be set.
    644         if (mSourceCoords != null && attr != null) {
    645             // Upload new source coordinates to GPU
    646             attr.set(false, FLOAT_SIZE * 2, 2, GLES20.GL_FLOAT, mSourceCoords);
    647         }
    648         // Do not set again (even if failed, to not cause endless attempts)
    649         mSourceCoords = null;
    650     }
    651 
    652     private void updateTargetCoordAttribute() {
    653         // If attribute does not exist, simply do nothing (may be custom shader).
    654         VertexAttribute attr = getProgramAttribute(positionAttributeName(), false);
    655         // A non-null value of mTargetCoords indicates new values to be set.
    656         if (mTargetCoords != null && attr != null) {
    657             // Upload new target coordinates to GPU
    658             attr.set(false, FLOAT_SIZE * 2, 2, GLES20.GL_FLOAT, mTargetCoords);
    659         }
    660         // Do not set again (even if failed, to not cause endless attempts)
    661         mTargetCoords = null;
    662     }
    663 
    664     private void render() {
    665         GLES20.glDrawArrays(mDrawMode, 0, mVertexCount);
    666         GLToolbox.checkGlError("glDrawArrays");
    667     }
    668 
    669     private void checkExecutable() {
    670         if (mProgram == 0) {
    671             throw new RuntimeException("Attempting to execute invalid shader-program!");
    672         }
    673     }
    674 
    675     private void useProgram() {
    676         GLES20.glUseProgram(mProgram);
    677         GLToolbox.checkGlError("glUseProgram");
    678     }
    679 
    680     private static void checkTexCount(int count) {
    681         if (count > maxTextureUnits()) {
    682             throw new RuntimeException("Number of textures passed (" + count + ") exceeds the "
    683                 + "maximum number of allowed texture units (" + maxTextureUnits() + ")!");
    684         }
    685     }
    686 
    687     private static int loadShader(int shaderType, String source) {
    688         int shader = GLES20.glCreateShader(shaderType);
    689         if (shader != 0) {
    690             GLES20.glShaderSource(shader, source);
    691             GLES20.glCompileShader(shader);
    692             int[] compiled = new int[1];
    693             GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
    694             if (compiled[0] == 0) {
    695                 String info = GLES20.glGetShaderInfoLog(shader);
    696                 GLES20.glDeleteShader(shader);
    697                 shader = 0;
    698                 throw new RuntimeException("Could not compile shader " + shaderType + ":" + info);
    699             }
    700         }
    701         return shader;
    702     }
    703 
    704     private static int createProgram(String vertexSource, String fragmentSource) {
    705         int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
    706         if (vertexShader == 0) {
    707             throw new RuntimeException("Could not create shader-program as vertex shader "
    708                 + "could not be compiled!");
    709         }
    710         int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
    711         if (pixelShader == 0) {
    712             throw new RuntimeException("Could not create shader-program as fragment shader "
    713                 + "could not be compiled!");
    714         }
    715 
    716         int program = GLES20.glCreateProgram();
    717         if (program != 0) {
    718             GLES20.glAttachShader(program, vertexShader);
    719             GLToolbox.checkGlError("glAttachShader");
    720             GLES20.glAttachShader(program, pixelShader);
    721             GLToolbox.checkGlError("glAttachShader");
    722             GLES20.glLinkProgram(program);
    723             int[] linkStatus = new int[1];
    724             GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
    725             if (linkStatus[0] != GLES20.GL_TRUE) {
    726                 String info = GLES20.glGetProgramInfoLog(program);
    727                 GLES20.glDeleteProgram(program);
    728                 program = 0;
    729                 throw new RuntimeException("Could not link program: " + info);
    730             }
    731         }
    732 
    733         GLES20.glDeleteShader(vertexShader);
    734         GLES20.glDeleteShader(pixelShader);
    735 
    736         return program;
    737     }
    738 
    739     private void scanUniforms() {
    740         int uniformCount[] = new int [1];
    741         GLES20.glGetProgramiv(mProgram, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, 0);
    742         if (uniformCount[0] > 0) {
    743             mUniforms = new HashMap<String, ProgramUniform>(uniformCount[0]);
    744             for (int i = 0; i < uniformCount[0]; ++i) {
    745                 ProgramUniform uniform = new ProgramUniform(mProgram, i);
    746                 mUniforms.put(uniform.getName(), uniform);
    747             }
    748         }
    749     }
    750 
    751     private ProgramUniform getProgramUniform(String name, boolean required) {
    752         ProgramUniform result = mUniforms.get(name);
    753         if (result == null && required) {
    754             throw new IllegalArgumentException("Unknown uniform '" + name + "'!");
    755         }
    756         return result;
    757     }
    758 
    759     private VertexAttribute getProgramAttribute(String name, boolean required) {
    760         VertexAttribute result = mAttributes.get(name);
    761         if (result == null) {
    762             int handle = GLES20.glGetAttribLocation(mProgram, name);
    763             if (handle >= 0) {
    764                 result = new VertexAttribute(name, handle);
    765                 mAttributes.put(name, result);
    766             } else if (required) {
    767                 throw new IllegalArgumentException("Unknown attribute '" + name + "'!");
    768             }
    769         }
    770         return result;
    771     }
    772 
    773     private void checkUniformAssignment(ProgramUniform uniform, int values, int components) {
    774         if (values % components != 0) {
    775             throw new RuntimeException("Size mismatch: Attempting to assign values of size "
    776                 + values + " to uniform '" + uniform.getName() + "' (must be multiple of "
    777                 + components + ")!");
    778         } else if (uniform.getSize() != values / components) {
    779             throw new RuntimeException("Size mismatch: Cannot assign " + values + " values to "
    780                 + "uniform '" + uniform.getName() + "'!");
    781         }
    782     }
    783 
    784     private static int strlen(byte[] strVal) {
    785         for (int i = 0; i < strVal.length; ++i) {
    786             if (strVal[i] == '\0') {
    787                 return i;
    788             }
    789         }
    790         return strVal.length;
    791     }
    792 }
    793 
    794