1 /* 2 * Copyright (C) 2011 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 android.opengl.cts; 18 19 import java.io.IOException; 20 import java.nio.Buffer; 21 import java.nio.ByteBuffer; 22 import java.nio.ByteOrder; 23 import java.nio.FloatBuffer; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 27 import javax.microedition.khronos.egl.EGLConfig; 28 import javax.microedition.khronos.opengles.GL10; 29 30 import com.android.cts.graphics.R; 31 32 import android.content.Context; 33 import android.content.res.Resources; 34 import android.graphics.Bitmap; 35 import android.graphics.Bitmap.Config; 36 import android.graphics.BitmapFactory; 37 import android.graphics.Color; 38 import android.graphics.SurfaceTexture; 39 import android.opengl.ETC1; 40 import android.opengl.ETC1Util; 41 import android.opengl.GLES20; 42 import android.opengl.GLSurfaceView; 43 import android.opengl.GLUtils; 44 import android.opengl.Matrix; 45 import android.util.Log; 46 import android.view.Surface; 47 48 import java.util.concurrent.CountDownLatch; 49 import java.util.concurrent.TimeUnit; 50 51 class CompressedTextureSurfaceView extends GLSurfaceView { 52 private static final String TAG = "CompressedTextureSurfaceView"; 53 private static final int SLEEP_TIME_MS = 1000; 54 55 CompressedTextureRender mRenderer; 56 CompressedTextureSurfaceView(Context context, Bitmap base, CompressedTextureLoader.Texture compressed)57 public CompressedTextureSurfaceView(Context context, 58 Bitmap base, 59 CompressedTextureLoader.Texture compressed) { 60 super(context); 61 62 setEGLContextClientVersion(2); 63 mRenderer = new CompressedTextureRender(context, base, compressed); 64 setRenderer(mRenderer); 65 setRenderMode(RENDERMODE_WHEN_DIRTY); 66 } 67 68 @Override onResume()69 public void onResume() { 70 super.onResume(); 71 } 72 getTestPassed()73 public boolean getTestPassed() throws InterruptedException { 74 return mRenderer.getTestPassed(); 75 } 76 77 private static class CompressedTextureRender implements GLSurfaceView.Renderer { 78 private static String TAG = "CompressedTextureRender"; 79 80 private static final int ALLOWED_DELTA = 25; 81 private static final int FBO_PIXEL_SIZE_BYTES = 4; 82 private static final int FLOAT_SIZE_BYTES = 4; 83 private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; 84 private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; 85 private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; 86 private final float[] mTriangleVerticesData = { 87 // X, Y, Z, U, V 88 -1.0f, -1.0f, 0, 0.f, 0.f, 89 1.0f, -1.0f, 0, 1.f, 0.f, 90 -1.0f, 1.0f, 0, 0.f, 1.f, 91 1.0f, 1.0f, 0, 1.f, 1.f, 92 }; 93 94 private FloatBuffer mTriangleVertices; 95 96 private final String mVertexShader = 97 "uniform mat4 uMVPMatrix;\n" + 98 "attribute vec4 aPosition;\n" + 99 "attribute vec4 aTextureCoord;\n" + 100 "varying vec2 vTextureCoord;\n" + 101 "void main() {\n" + 102 " gl_Position = uMVPMatrix * aPosition;\n" + 103 " vTextureCoord = aTextureCoord.xy;\n" + 104 "}\n"; 105 106 private final String mFragmentShader = 107 "precision mediump float;\n" + 108 "varying vec2 vTextureCoord;\n" + 109 "uniform sampler2D sTexture;\n" + 110 "void main() {\n" + 111 " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + 112 "}\n"; 113 114 private float[] mMVPMatrix = new float[16]; 115 private float[] mSTMatrix = new float[16]; 116 117 private int mProgram; 118 private int mTextureID; 119 private int muMVPMatrixHandle; 120 private int maPositionHandle; 121 private int maTextureHandle; 122 private int msTextureHandle; 123 124 private int mColorTargetID; 125 private int mFrameBufferObjectID; 126 127 private boolean updateSurface = false; 128 129 private boolean mTestPassed; 130 private CountDownLatch mDoneSignal; 131 132 Bitmap mBaseTexture; 133 CompressedTextureLoader.Texture mCompressedTexture; 134 135 int mWidth; 136 int mHeight; 137 138 ByteBuffer mReadBackBuffer; 139 getTestPassed()140 boolean getTestPassed() throws InterruptedException { 141 if (!mDoneSignal.await(2000L, TimeUnit.MILLISECONDS)) { 142 throw new IllegalStateException("Coudn't finish drawing frames!"); 143 } 144 145 return mTestPassed; 146 } 147 CompressedTextureRender(Context context, Bitmap base, CompressedTextureLoader.Texture compressed)148 public CompressedTextureRender(Context context, 149 Bitmap base, 150 CompressedTextureLoader.Texture compressed) { 151 mBaseTexture = base; 152 mCompressedTexture = compressed; 153 mTriangleVertices = ByteBuffer.allocateDirect( 154 mTriangleVerticesData.length * FLOAT_SIZE_BYTES) 155 .order(ByteOrder.nativeOrder()).asFloatBuffer(); 156 mTriangleVertices.put(mTriangleVerticesData).position(0); 157 158 Matrix.setIdentityM(mSTMatrix, 0); 159 160 int byteBufferSize = mBaseTexture.getWidth() * 161 mBaseTexture.getHeight() * 162 FBO_PIXEL_SIZE_BYTES; 163 mReadBackBuffer = ByteBuffer.allocateDirect(byteBufferSize); 164 165 mDoneSignal = new CountDownLatch(1); 166 } 167 renderQuad(int textureID)168 private void renderQuad(int textureID) { 169 GLES20.glUseProgram(mProgram); 170 checkGlError("glUseProgram"); 171 172 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 173 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); 174 175 mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); 176 GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 177 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); 178 checkGlError("glVertexAttribPointer maPosition"); 179 GLES20.glEnableVertexAttribArray(maPositionHandle); 180 checkGlError("glEnableVertexAttribArray maPositionHandle"); 181 182 mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); 183 GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, 184 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); 185 checkGlError("glVertexAttribPointer maTextureHandle"); 186 GLES20.glEnableVertexAttribArray(maTextureHandle); 187 checkGlError("glEnableVertexAttribArray maTextureHandle"); 188 189 Matrix.setIdentityM(mMVPMatrix, 0); 190 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); 191 192 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); 193 checkGlError("glDrawArrays"); 194 } 195 getUnsignedByte(byte val)196 private int getUnsignedByte(byte val) { 197 return 0xFF & ((int)val); 198 } 199 comparePixel(int x, int y)200 private boolean comparePixel(int x, int y) { 201 int w = mBaseTexture.getWidth(); 202 int sampleStart = (y * w + x) * FBO_PIXEL_SIZE_BYTES; 203 204 int R = getUnsignedByte(mReadBackBuffer.get(sampleStart)); 205 int G = getUnsignedByte(mReadBackBuffer.get(sampleStart + 1)); 206 int B = getUnsignedByte(mReadBackBuffer.get(sampleStart + 2)); 207 208 int original = mBaseTexture.getPixel(x, y); 209 210 int deltaR = Math.abs(R - Color.red(original)); 211 int deltaG = Math.abs(G - Color.green(original)); 212 int deltaB = Math.abs(B - Color.blue(original)); 213 214 if (deltaR <= ALLOWED_DELTA && 215 deltaG <= ALLOWED_DELTA && 216 deltaB <= ALLOWED_DELTA) { 217 return true; 218 } 219 220 Log.i("PIXEL DELTA", "R: " + deltaR + " G: " + deltaG + " B: " + deltaB); 221 222 return false; 223 } 224 comparePixels()225 private void comparePixels() { 226 int w = mBaseTexture.getWidth(); 227 int h = mBaseTexture.getWidth(); 228 int wOver4 = w / 4; 229 int hOver4 = h / 4; 230 231 // Sample 4 points in the image. Test is designed so that 232 // sample areas are low frequency and easy to compare 233 boolean sample1Matches = comparePixel(wOver4, hOver4); 234 boolean sample2Matches = comparePixel(wOver4 * 3, hOver4); 235 boolean sample3Matches = comparePixel(wOver4, hOver4 * 3); 236 boolean sample4Matches = comparePixel(wOver4 * 3, hOver4 * 3); 237 238 mTestPassed = sample1Matches && sample2Matches && sample3Matches && sample4Matches; 239 mDoneSignal.countDown(); 240 } 241 onDrawFrame(GL10 glUnused)242 public void onDrawFrame(GL10 glUnused) { 243 if (mProgram == 0) { 244 return; 245 } 246 247 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBufferObjectID); 248 GLES20.glViewport(0, 0, mBaseTexture.getWidth(), mBaseTexture.getHeight()); 249 GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 250 renderQuad(mTextureID); 251 GLES20.glReadPixels(0, 0, mBaseTexture.getWidth(), mBaseTexture.getHeight(), 252 GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mReadBackBuffer); 253 comparePixels(); 254 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 255 256 GLES20.glViewport(0, 0, mWidth, mHeight); 257 GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 258 GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); 259 260 renderQuad(mColorTargetID); 261 262 GLES20.glFinish(); 263 } 264 onSurfaceChanged(GL10 glUnused, int width, int height)265 public void onSurfaceChanged(GL10 glUnused, int width, int height) { 266 mWidth = width; 267 mHeight = height; 268 } 269 setupSamplers()270 private void setupSamplers() { 271 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, 272 GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 273 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, 274 GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 275 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, 276 GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); 277 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, 278 GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); 279 } 280 initFBO()281 private void initFBO() { 282 int[] textures = new int[1]; 283 GLES20.glGenTextures(1, textures, 0); 284 285 mColorTargetID = textures[0]; 286 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mColorTargetID); 287 checkGlError("glBindTexture mColorTargetID"); 288 setupSamplers(); 289 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 290 mBaseTexture.getWidth(), mBaseTexture.getHeight(), 0, 291 GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); 292 checkGlError("glTexImage2D mColorTargetID"); 293 294 GLES20.glGenFramebuffers(1, textures, 0); 295 mFrameBufferObjectID = textures[0]; 296 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBufferObjectID); 297 298 GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, 299 GLES20.GL_TEXTURE_2D, mColorTargetID, 0); 300 301 int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); 302 if(status != GLES20.GL_FRAMEBUFFER_COMPLETE) { 303 throw new RuntimeException("Failed to initialize framebuffer object"); 304 } 305 306 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 307 } 308 onSurfaceCreated(GL10 glUnused, EGLConfig config)309 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { 310 if (mCompressedTexture != null && !mCompressedTexture.isSupported()) { 311 mTestPassed = true; 312 mDoneSignal.countDown(); 313 return; 314 } 315 316 initFBO(); 317 318 mProgram = createProgram(mVertexShader, mFragmentShader); 319 if (mProgram == 0) { 320 return; 321 } 322 maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); 323 checkGlError("glGetAttribLocation aPosition"); 324 if (maPositionHandle == -1) { 325 throw new RuntimeException("Could not get attrib location for aPosition"); 326 } 327 maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); 328 checkGlError("glGetAttribLocation aTextureCoord"); 329 if (maTextureHandle == -1) { 330 throw new RuntimeException("Could not get attrib location for aTextureCoord"); 331 } 332 333 muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 334 checkGlError("glGetUniformLocation uMVPMatrix"); 335 if (muMVPMatrixHandle == -1) { 336 throw new RuntimeException("Could not get attrib location for uMVPMatrix"); 337 } 338 339 int[] textures = new int[1]; 340 GLES20.glGenTextures(1, textures, 0); 341 342 mTextureID = textures[0]; 343 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID); 344 checkGlError("glBindTexture mTextureID"); 345 setupSamplers(); 346 347 if (mCompressedTexture == null) { 348 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBaseTexture, 0); 349 checkGlError("texImage2D mBaseTexture"); 350 } else { 351 GLES20.glCompressedTexImage2D(GLES20.GL_TEXTURE_2D, 352 0, 353 mCompressedTexture.getFormat(), 354 mCompressedTexture.getWidth(), 355 mCompressedTexture.getHeight(), 356 0, 357 mCompressedTexture.getData().remaining(), 358 mCompressedTexture.getData()); 359 checkGlError("glCompressedTexImage2D mTextureID"); 360 } 361 } 362 onFrameAvailable(SurfaceTexture surface)363 synchronized public void onFrameAvailable(SurfaceTexture surface) { 364 updateSurface = true; 365 } 366 loadShader(int shaderType, String source)367 private int loadShader(int shaderType, String source) { 368 int shader = GLES20.glCreateShader(shaderType); 369 if (shader != 0) { 370 GLES20.glShaderSource(shader, source); 371 GLES20.glCompileShader(shader); 372 int[] compiled = new int[1]; 373 GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 374 if (compiled[0] == 0) { 375 Log.e(TAG, "Could not compile shader " + shaderType + ":"); 376 Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); 377 GLES20.glDeleteShader(shader); 378 shader = 0; 379 } 380 } 381 return shader; 382 } 383 createProgram(String vertexSource, String fragmentSource)384 private int createProgram(String vertexSource, String fragmentSource) { 385 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 386 if (vertexShader == 0) { 387 return 0; 388 } 389 int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 390 if (pixelShader == 0) { 391 return 0; 392 } 393 394 int program = GLES20.glCreateProgram(); 395 if (program != 0) { 396 GLES20.glAttachShader(program, vertexShader); 397 checkGlError("glAttachShader"); 398 GLES20.glAttachShader(program, pixelShader); 399 checkGlError("glAttachShader"); 400 GLES20.glLinkProgram(program); 401 int[] linkStatus = new int[1]; 402 GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 403 if (linkStatus[0] != GLES20.GL_TRUE) { 404 Log.e(TAG, "Could not link program: "); 405 Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 406 GLES20.glDeleteProgram(program); 407 program = 0; 408 } 409 } 410 return program; 411 } 412 checkGlError(String op)413 private void checkGlError(String op) { 414 int error; 415 while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 416 Log.e(TAG, op + ": glError " + error); 417 throw new RuntimeException(op + ": glError " + error); 418 } 419 } 420 421 } // End of class CompressedTextureRender. 422 423 } // End of class CompressedTextureSurfaceView. 424