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 VertexAttribute(String name, int index)90 public VertexAttribute(String name, int index) { 91 mName = name; 92 mIndex = index; 93 mLength = -1; 94 } 95 set(boolean normalize, int stride, int components, int type, float[] values)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 set(boolean normalize, int offset, int stride, int components, int type, int vbo)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 push()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 toString()169 public String toString() { 170 return mName; 171 } 172 initBuffer(float[] values)173 private void initBuffer(float[] values) { 174 mValues = ByteBuffer.allocateDirect(values.length * FLOAT_SIZE) 175 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 176 } 177 copyValues(float[] values)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 ProgramUniform(int program, int index)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 getName()207 public String getName() { 208 return mName; 209 } 210 getType()211 public int getType() { 212 return mType; 213 } 214 getLocation()215 public int getLocation() { 216 return mLocation; 217 } 218 getSize()219 public int getSize() { 220 return mSize; 221 } 222 } 223 ImageShader(String fragmentShader)224 public ImageShader(String fragmentShader) { 225 mProgram = createProgram(mDefaultVertexShader, fragmentShader); 226 scanUniforms(); 227 } 228 ImageShader(String vertexShader, String fragmentShader)229 public ImageShader(String vertexShader, String fragmentShader) { 230 mProgram = createProgram(vertexShader, fragmentShader); 231 scanUniforms(); 232 } 233 createIdentity()234 public static ImageShader createIdentity() { 235 return new ImageShader(mIdentityShader); 236 } 237 createIdentity(String vertexShader)238 public static ImageShader createIdentity(String vertexShader) { 239 return new ImageShader(vertexShader, mIdentityShader); 240 } 241 renderTextureToTarget(TextureSource texture, RenderTarget target, int width, int height)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 process(FrameImage2D input, FrameImage2D output)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 processMulti(FrameImage2D[] inputs, FrameImage2D output)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 process(TextureSource texture, RenderTarget target, int width, int height)277 public void process(TextureSource texture, RenderTarget target, int width, int height) { 278 processMulti(new TextureSource[] { texture }, target, width, height); 279 } 280 processMulti(TextureSource[] sources, RenderTarget target, int width, int height)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 processNoInput(FrameImage2D output)291 public void processNoInput(FrameImage2D output) { 292 RenderTarget renderTarget = output.lockRenderTarget(); 293 processNoInput(renderTarget, output.getWidth(), output.getHeight()); 294 output.unlock(); 295 } 296 processNoInput(RenderTarget target, int width, int height)297 public void processNoInput(RenderTarget target, int width, int height) { 298 processMulti(new TextureSource[] {}, target, width, height); 299 } 300 getUniformLocation(String name)301 public int getUniformLocation(String name) { 302 return getProgramUniform(name, true).getLocation(); 303 } 304 getAttributeLocation(String name)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 setUniformValue(String uniformName, int value)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 setUniformValue(String uniformName, float value)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 setUniformValue(String uniformName, int[] values)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 setUniformValue(String uniformName, float[] values)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 setAttributeValues(String attributeName, float[] data, int components)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 setAttributeValues(String attributeName, int vbo, int type, int components, int stride, int offset, boolean normalize)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 setSourceRect(float x, float y, float width, float height)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 setSourceRect(RectF rect)415 public void setSourceRect(RectF rect) { 416 setSourceRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); 417 } 418 setSourceQuad(Quad quad)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 setSourceCoords(float[] coords)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 setSourceTransform(float[] matrix)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 setTargetRect(float x, float y, float width, float height)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 setTargetRect(RectF rect)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 setTargetQuad(Quad quad)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 setTargetCoords(float[] coords)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 setTargetTransform(float[] matrix)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 setClearsOutput(boolean clears)504 public void setClearsOutput(boolean clears) { 505 mClearsOutput = clears; 506 } 507 getClearsOutput()508 public boolean getClearsOutput() { 509 return mClearsOutput; 510 } 511 setClearColor(float[] rgba)512 public void setClearColor(float[] rgba) { 513 mClearColor = rgba; 514 } 515 getClearColor()516 public float[] getClearColor() { 517 return mClearColor; 518 } 519 setClearBufferMask(int bufferMask)520 public void setClearBufferMask(int bufferMask) { 521 mClearBuffers = bufferMask; 522 } 523 getClearBufferMask()524 public int getClearBufferMask() { 525 return mClearBuffers; 526 } 527 setBlendEnabled(boolean enable)528 public void setBlendEnabled(boolean enable) { 529 mBlendEnabled = enable; 530 } 531 getBlendEnabled()532 public boolean getBlendEnabled() { 533 return mBlendEnabled; 534 } 535 setBlendFunc(int sFactor, int dFactor)536 public void setBlendFunc(int sFactor, int dFactor) { 537 mSFactor = sFactor; 538 mDFactor = dFactor; 539 } 540 setDrawMode(int drawMode)541 public void setDrawMode(int drawMode) { 542 mDrawMode = drawMode; 543 } 544 getDrawMode()545 public int getDrawMode() { 546 return mDrawMode; 547 } 548 setVertexCount(int count)549 public void setVertexCount(int count) { 550 mVertexCount = count; 551 } 552 getVertexCount()553 public int getVertexCount() { 554 return mVertexCount; 555 } 556 setBaseTextureUnit(int baseTexUnit)557 public void setBaseTextureUnit(int baseTexUnit) { 558 mBaseTexUnit = baseTexUnit; 559 } 560 baseTextureUnit()561 public int baseTextureUnit() { 562 return mBaseTexUnit; 563 } 564 texCoordAttributeName()565 public String texCoordAttributeName() { 566 return "a_texcoord"; 567 } 568 positionAttributeName()569 public String positionAttributeName() { 570 return "a_position"; 571 } 572 inputTextureUniformName(int index)573 public String inputTextureUniformName(int index) { 574 return "tex_sampler_" + index; 575 } 576 maxTextureUnits()577 public static int maxTextureUnits() { 578 return GLES20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; 579 } 580 581 @Override finalize()582 protected void finalize() throws Throwable { 583 GLES20.glDeleteProgram(mProgram); 584 } 585 pushShaderState()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 focusTarget(RenderTarget target, int width, int height)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 bindInputTextures(TextureSource[] sources)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 pushAttributes()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 updateSourceCoordAttribute()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 updateTargetCoordAttribute()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 render()664 private void render() { 665 GLES20.glDrawArrays(mDrawMode, 0, mVertexCount); 666 GLToolbox.checkGlError("glDrawArrays"); 667 } 668 checkExecutable()669 private void checkExecutable() { 670 if (mProgram == 0) { 671 throw new RuntimeException("Attempting to execute invalid shader-program!"); 672 } 673 } 674 useProgram()675 private void useProgram() { 676 GLES20.glUseProgram(mProgram); 677 GLToolbox.checkGlError("glUseProgram"); 678 } 679 checkTexCount(int count)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 loadShader(int shaderType, String source)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 createProgram(String vertexSource, String fragmentSource)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 scanUniforms()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 getProgramUniform(String name, boolean required)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 getProgramAttribute(String name, boolean required)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 checkUniformAssignment(ProgramUniform uniform, int values, int components)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 strlen(byte[] strVal)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