Home | History | Annotate | Download | only in cts
      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.stub.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 
     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
     69     public void onResume() {
     70         super.onResume();
     71     }
     72 
     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 
    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 
    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 
    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, 3, 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 
    196         private int getUnsignedByte(byte val) {
    197             return 0xFF & ((int)val);
    198         }
    199 
    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 
    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 
    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 
    265         public void onSurfaceChanged(GL10 glUnused, int width, int height) {
    266             mWidth = width;
    267             mHeight = height;
    268         }
    269 
    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 
    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 
    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 
    363         synchronized public void onFrameAvailable(SurfaceTexture surface) {
    364             updateSurface = true;
    365         }
    366 
    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 
    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 
    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