1 /* 2 * Copyright 2017 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.graphics.cts; 18 19 import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; 20 import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY; 21 import static android.opengl.EGL14.EGL_HEIGHT; 22 import static android.opengl.EGL14.EGL_NONE; 23 import static android.opengl.EGL14.EGL_NO_CONTEXT; 24 import static android.opengl.EGL14.EGL_NO_DISPLAY; 25 import static android.opengl.EGL14.EGL_NO_SURFACE; 26 import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; 27 import static android.opengl.EGL14.EGL_PBUFFER_BIT; 28 import static android.opengl.EGL14.EGL_RENDERABLE_TYPE; 29 import static android.opengl.EGL14.EGL_SURFACE_TYPE; 30 import static android.opengl.EGL14.EGL_WIDTH; 31 import static android.system.OsConstants.EINVAL; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertNotEquals; 35 import static org.junit.Assert.assertTrue; 36 37 import android.graphics.SurfaceTexture; 38 import android.media.ImageReader; 39 import android.opengl.EGL14; 40 import android.opengl.EGLConfig; 41 import android.opengl.EGLContext; 42 import android.opengl.EGLDisplay; 43 import android.opengl.EGLSurface; 44 import android.opengl.GLES20; 45 import android.os.Parcel; 46 import android.util.Log; 47 import android.view.Surface; 48 49 import androidx.test.filters.SmallTest; 50 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.junit.runners.BlockJUnit4ClassRunner; 55 56 @SmallTest 57 @RunWith(BlockJUnit4ClassRunner.class) 58 public class ANativeWindowTest { 59 60 static { 61 System.loadLibrary("ctsgraphics_jni"); 62 } 63 64 private static final String TAG = ANativeWindowTest.class.getSimpleName(); 65 private static final boolean DEBUG = false; 66 67 private EGLDisplay mEglDisplay = EGL_NO_DISPLAY; 68 private EGLConfig mEglConfig = null; 69 private EGLSurface mEglPbuffer = EGL_NO_SURFACE; 70 private EGLContext mEglContext = EGL_NO_CONTEXT; 71 72 @Before setup()73 public void setup() throws Throwable { 74 mEglDisplay = EGL14.eglGetDisplay(EGL_DEFAULT_DISPLAY); 75 if (mEglDisplay == EGL_NO_DISPLAY) { 76 throw new RuntimeException("no EGL display"); 77 } 78 int[] major = new int[1]; 79 int[] minor = new int[1]; 80 if (!EGL14.eglInitialize(mEglDisplay, major, 0, minor, 0)) { 81 throw new RuntimeException("error in eglInitialize"); 82 } 83 84 // If we could rely on having EGL_KHR_surfaceless_context and EGL_KHR_context_no_config, we 85 // wouldn't have to create a config or pbuffer at all. 86 87 int[] numConfigs = new int[1]; 88 EGLConfig[] configs = new EGLConfig[1]; 89 if (!EGL14.eglChooseConfig(mEglDisplay, 90 new int[] { 91 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 92 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 93 EGL_NONE}, 94 0, configs, 0, 1, numConfigs, 0)) { 95 throw new RuntimeException("eglChooseConfig failed"); 96 } 97 mEglConfig = configs[0]; 98 99 mEglPbuffer = EGL14.eglCreatePbufferSurface(mEglDisplay, mEglConfig, 100 new int[] {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE}, 0); 101 if (mEglPbuffer == EGL_NO_SURFACE) { 102 throw new RuntimeException("eglCreatePbufferSurface failed"); 103 } 104 105 mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, 106 new int[] {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}, 0); 107 if (mEglContext == EGL_NO_CONTEXT) { 108 throw new RuntimeException("eglCreateContext failed"); 109 } 110 111 if (!EGL14.eglMakeCurrent(mEglDisplay, mEglPbuffer, mEglPbuffer, mEglContext)) { 112 throw new RuntimeException("eglMakeCurrent failed"); 113 } 114 } 115 116 @Test testSetBuffersTransform()117 public void testSetBuffersTransform() { 118 final int MIRROR_HORIZONTAL_BIT = 0x01; 119 final int MIRROR_VERTICAL_BIT = 0x02; 120 final int ROTATE_90_BIT = 0x04; 121 final int ALL_TRANSFORM_BITS = 122 MIRROR_HORIZONTAL_BIT | MIRROR_VERTICAL_BIT | ROTATE_90_BIT; 123 124 // 4x4 GL-style matrices, as returned by SurfaceTexture#getTransformMatrix(). Note they're 125 // transforming texture coordinates ([0,1]^2), so the origin for the transforms is 126 // (0.5, 0.5), not (0,0). 127 final float[] MIRROR_HORIZONTAL_MATRIX = new float[] { 128 -1.0f, 0.0f, 0.0f, 0.0f, 129 0.0f, 1.0f, 0.0f, 0.0f, 130 0.0f, 0.0f, 1.0f, 0.0f, 131 1.0f, 0.0f, 0.0f, 1.0f, 132 }; 133 final float[] MIRROR_VERTICAL_MATRIX = new float[] { 134 1.0f, 0.0f, 0.0f, 0.0f, 135 0.0f, -1.0f, 0.0f, 0.0f, 136 0.0f, 0.0f, 1.0f, 0.0f, 137 0.0f, 1.0f, 0.0f, 1.0f, 138 }; 139 final float[] ROTATE_90_MATRIX = new float[] { 140 0.0f, 1.0f, 0.0f, 0.0f, 141 -1.0f, 0.0f, 0.0f, 0.0f, 142 0.0f, 0.0f, 1.0f, 0.0f, 143 1.0f, 0.0f, 0.0f, 1.0f, 144 }; 145 146 int[] texId = new int[1]; 147 GLES20.glGenTextures(1, texId, 0); 148 149 SurfaceTexture consumer = new SurfaceTexture(texId[0]); 150 consumer.setDefaultBufferSize(16, 16); 151 Surface surface = new Surface(consumer); 152 153 float[] computedTransform = new float[16]; 154 float[] receivedTransform = new float[16]; 155 float[] tmp = new float[16]; 156 for (int transform = 0; transform <= ALL_TRANSFORM_BITS; transform++) { 157 nPushBufferWithTransform(surface, transform); 158 159 // The SurfaceTexture texture transform matrix first does a vertical flip so that 160 // "first row in memory" corresponds to "texture coordinate v=0". 161 System.arraycopy(MIRROR_VERTICAL_MATRIX, 0, computedTransform, 0, 16); 162 163 if ((transform & MIRROR_HORIZONTAL_BIT) != 0) { 164 matrixMultiply(computedTransform, computedTransform, MIRROR_HORIZONTAL_MATRIX, tmp); 165 } 166 if ((transform & MIRROR_VERTICAL_BIT) != 0) { 167 matrixMultiply(computedTransform, computedTransform, MIRROR_VERTICAL_MATRIX, tmp); 168 } 169 if ((transform & ROTATE_90_BIT) != 0) { 170 matrixMultiply(computedTransform, computedTransform, ROTATE_90_MATRIX, tmp); 171 } 172 173 consumer.updateTexImage(); 174 consumer.getTransformMatrix(receivedTransform); 175 176 if (DEBUG) { 177 Log.d(TAG, String.format( 178 "Transform 0x%x:\n" + 179 " expected: % 2.0f % 2.0f % 2.0f % 2.0f\n" + 180 " % 2.0f % 2.0f % 2.0f % 2.0f\n" + 181 " % 2.0f % 2.0f % 2.0f % 2.0f\n" + 182 " % 2.0f % 2.0f % 2.0f % 2.0f\n" + 183 " actual: % 2.0f % 2.0f % 2.0f % 2.0f\n" + 184 " % 2.0f % 2.0f % 2.0f % 2.0f\n" + 185 " % 2.0f % 2.0f % 2.0f % 2.0f\n" + 186 " % 2.0f % 2.0f % 2.0f % 2.0f\n", 187 transform, 188 computedTransform[ 0], computedTransform[ 1], 189 computedTransform[ 2], computedTransform[ 3], 190 computedTransform[ 4], computedTransform[ 5], 191 computedTransform[ 6], computedTransform[ 7], 192 computedTransform[ 8], computedTransform[ 9], 193 computedTransform[10], computedTransform[11], 194 computedTransform[12], computedTransform[13], 195 computedTransform[14], computedTransform[15], 196 receivedTransform[ 0], receivedTransform[ 1], 197 receivedTransform[ 2], receivedTransform[ 3], 198 receivedTransform[ 4], receivedTransform[ 5], 199 receivedTransform[ 6], receivedTransform[ 7], 200 receivedTransform[ 8], receivedTransform[ 9], 201 receivedTransform[10], receivedTransform[11], 202 receivedTransform[12], receivedTransform[13], 203 receivedTransform[14], receivedTransform[15])); 204 } 205 206 for (int i = 0; i < 16; i++) { 207 assertEquals(computedTransform[i], receivedTransform[i], 0.0f); 208 } 209 } 210 } 211 212 @Test testSetBuffersDataSpace()213 public void testSetBuffersDataSpace() { 214 int[] texId = new int[1]; 215 GLES20.glGenTextures(1, texId, 0); 216 217 SurfaceTexture consumer = new SurfaceTexture(texId[0]); 218 consumer.setDefaultBufferSize(16, 16); 219 Surface surface = new Surface(consumer); 220 221 assertEquals(nGetBuffersDataSpace(surface), 0); 222 assertEquals(nSetBuffersDataSpace(surface, DataSpace.ADATASPACE_SRGB), 0); 223 assertEquals(nGetBuffersDataSpace(surface), DataSpace.ADATASPACE_SRGB); 224 225 assertEquals(nSetBuffersDataSpace(null, DataSpace.ADATASPACE_SRGB), -EINVAL); 226 assertEquals(nGetBuffersDataSpace(null), -EINVAL); 227 assertEquals(nGetBuffersDataSpace(surface), DataSpace.ADATASPACE_SRGB); 228 } 229 230 @Test testGetBuffersDefaultDataspace()231 public void testGetBuffersDefaultDataspace() { 232 assertEquals(nGetBuffersDefaultDataSpace(null), -EINVAL); 233 234 ImageReader reader1 = new ImageReader.Builder(32, 32) 235 .setDefaultDataSpace(DataSpace.ADATASPACE_BT709) 236 .build(); 237 assertEquals(nGetBuffersDefaultDataSpace(reader1.getSurface()), DataSpace.ADATASPACE_BT709); 238 reader1.close(); 239 240 ImageReader reader2 = new ImageReader.Builder(32, 32) 241 .setDefaultDataSpace(DataSpace.ADATASPACE_BT2020) 242 .build(); 243 assertEquals(nGetBuffersDefaultDataSpace(reader2.getSurface()), 244 DataSpace.ADATASPACE_BT2020); 245 reader2.close(); 246 } 247 248 @Test testWriteToParcel()249 public void testWriteToParcel() { 250 ImageReader reader = new ImageReader.Builder(32, 32) 251 .setDefaultDataSpace(DataSpace.ADATASPACE_BT709) 252 .build(); 253 Parcel parcel = Parcel.obtain(); 254 assertEquals(0, parcel.dataPosition()); 255 assertEquals(0, parcel.dataAvail()); 256 nWriteToParcel(reader.getSurface(), parcel); 257 assertNotEquals(0, parcel.dataPosition()); 258 parcel.setDataPosition(0); 259 final Surface outSurface = Surface.CREATOR.createFromParcel(parcel); 260 parcel.recycle(); 261 assertTrue(outSurface.isValid()); 262 assertEquals(nGetBuffersDefaultDataSpace(outSurface), DataSpace.ADATASPACE_BT709); 263 reader.close(); 264 } 265 266 @Test testReadFromParcel()267 public void testReadFromParcel() { 268 ImageReader reader = new ImageReader.Builder(32, 32) 269 .setDefaultDataSpace(DataSpace.ADATASPACE_BT709) 270 .build(); 271 Parcel parcel = Parcel.obtain(); 272 assertEquals(0, parcel.dataPosition()); 273 assertEquals(0, parcel.dataAvail()); 274 reader.getSurface().writeToParcel(parcel, 0); 275 assertNotEquals(0, parcel.dataPosition()); 276 parcel.setDataPosition(0); 277 final Surface outSurface = nReadFromParcel(parcel); 278 parcel.recycle(); 279 assertTrue(outSurface.isValid()); 280 assertEquals(nGetBuffersDefaultDataSpace(outSurface), DataSpace.ADATASPACE_BT709); 281 reader.close(); 282 } 283 284 @Test testWriteReadFromParcel()285 public void testWriteReadFromParcel() { 286 ImageReader reader = new ImageReader.Builder(32, 32) 287 .setDefaultDataSpace(DataSpace.ADATASPACE_BT709) 288 .build(); 289 Parcel parcel = Parcel.obtain(); 290 assertEquals(0, parcel.dataPosition()); 291 assertEquals(0, parcel.dataAvail()); 292 nWriteToParcel(reader.getSurface(), parcel); 293 assertNotEquals(0, parcel.dataPosition()); 294 parcel.setDataPosition(0); 295 final Surface outSurface = nReadFromParcel(parcel); 296 parcel.recycle(); 297 assertTrue(outSurface.isValid()); 298 assertEquals(nGetBuffersDefaultDataSpace(outSurface), DataSpace.ADATASPACE_BT709); 299 reader.close(); 300 } 301 302 // Multiply 4x4 matrices result = a*b. result can be the same as either a or b, 303 // allowing for result *= b. Another 4x4 matrix tmp must be provided as scratch space. matrixMultiply(float[] result, float[] a, float[] b, float[] tmp)304 private void matrixMultiply(float[] result, float[] a, float[] b, float[] tmp) { 305 tmp[ 0] = a[ 0]*b[ 0] + a[ 4]*b[ 1] + a[ 8]*b[ 2] + a[12]*b[ 3]; 306 tmp[ 1] = a[ 1]*b[ 0] + a[ 5]*b[ 1] + a[ 9]*b[ 2] + a[13]*b[ 3]; 307 tmp[ 2] = a[ 2]*b[ 0] + a[ 6]*b[ 1] + a[10]*b[ 2] + a[14]*b[ 3]; 308 tmp[ 3] = a[ 3]*b[ 0] + a[ 7]*b[ 1] + a[11]*b[ 2] + a[15]*b[ 3]; 309 310 tmp[ 4] = a[ 0]*b[ 4] + a[ 4]*b[ 5] + a[ 8]*b[ 6] + a[12]*b[ 7]; 311 tmp[ 5] = a[ 1]*b[ 4] + a[ 5]*b[ 5] + a[ 9]*b[ 6] + a[13]*b[ 7]; 312 tmp[ 6] = a[ 2]*b[ 4] + a[ 6]*b[ 5] + a[10]*b[ 6] + a[14]*b[ 7]; 313 tmp[ 7] = a[ 3]*b[ 4] + a[ 7]*b[ 5] + a[11]*b[ 6] + a[15]*b[ 7]; 314 315 tmp[ 8] = a[ 0]*b[ 8] + a[ 4]*b[ 9] + a[ 8]*b[10] + a[12]*b[11]; 316 tmp[ 9] = a[ 1]*b[ 8] + a[ 5]*b[ 9] + a[ 9]*b[10] + a[13]*b[11]; 317 tmp[10] = a[ 2]*b[ 8] + a[ 6]*b[ 9] + a[10]*b[10] + a[14]*b[11]; 318 tmp[11] = a[ 3]*b[ 8] + a[ 7]*b[ 9] + a[11]*b[10] + a[15]*b[11]; 319 320 tmp[12] = a[ 0]*b[12] + a[ 4]*b[13] + a[ 8]*b[14] + a[12]*b[15]; 321 tmp[13] = a[ 1]*b[12] + a[ 5]*b[13] + a[ 9]*b[14] + a[13]*b[15]; 322 tmp[14] = a[ 2]*b[12] + a[ 6]*b[13] + a[10]*b[14] + a[14]*b[15]; 323 tmp[15] = a[ 3]*b[12] + a[ 7]*b[13] + a[11]*b[14] + a[15]*b[15]; 324 325 System.arraycopy(tmp, 0, result, 0, 16); 326 } 327 328 @Test testTryAllocateBuffersDoesNotCrash()329 public void testTryAllocateBuffersDoesNotCrash() { 330 int[] texId = new int[1]; 331 GLES20.glGenTextures(1, texId, 0); 332 333 SurfaceTexture consumer = new SurfaceTexture(texId[0]); 334 consumer.setDefaultBufferSize(16, 16); 335 Surface surface = new Surface(consumer); 336 337 nTryAllocateBuffers(surface); 338 nTryAllocateBuffers(null); 339 } 340 nPushBufferWithTransform(Surface surface, int transform)341 private static native void nPushBufferWithTransform(Surface surface, int transform); nSetBuffersDataSpace(Surface surface, int dataSpace)342 private static native int nSetBuffersDataSpace(Surface surface, int dataSpace); nGetBuffersDataSpace(Surface surface)343 private static native int nGetBuffersDataSpace(Surface surface); nGetBuffersDefaultDataSpace(Surface surface)344 private static native int nGetBuffersDefaultDataSpace(Surface surface); nTryAllocateBuffers(Surface surface)345 private static native void nTryAllocateBuffers(Surface surface); nReadFromParcel(Parcel parcel)346 private static native Surface nReadFromParcel(Parcel parcel); nWriteToParcel(Surface surface, Parcel parcel)347 private static native void nWriteToParcel(Surface surface, Parcel parcel); 348 } 349