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