1 /* 2 * Copyright (C) 2016 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.view.cts; 18 19 import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT; 20 import static android.opengl.GLES20.glClear; 21 import static android.opengl.GLES20.glClearColor; 22 23 import android.app.Activity; 24 import android.graphics.Color; 25 import android.graphics.SurfaceTexture; 26 import android.opengl.GLUtils; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.HandlerThread; 30 import android.view.TextureView; 31 import android.view.TextureView.SurfaceTextureListener; 32 import android.view.View; 33 import android.view.ViewGroup.LayoutParams; 34 import android.widget.FrameLayout; 35 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.TimeUnit; 38 39 import javax.microedition.khronos.egl.EGL10; 40 import javax.microedition.khronos.egl.EGLConfig; 41 import javax.microedition.khronos.egl.EGLContext; 42 import javax.microedition.khronos.egl.EGLDisplay; 43 import javax.microedition.khronos.egl.EGLSurface; 44 45 public class TextureViewCtsActivity extends Activity implements SurfaceTextureListener { 46 private final static long TIME_OUT_MS = 10000; 47 private Object mLock = new Object(); 48 49 private View mPreview; 50 private TextureView mTextureView; 51 private HandlerThread mGLThreadLooper; 52 private Handler mGLThread; 53 54 private SurfaceTexture mSurface; 55 private int mSurfaceUpdatedCount; 56 57 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 58 static final int EGL_OPENGL_ES2_BIT = 4; 59 60 private EGL10 mEgl; 61 private EGLDisplay mEglDisplay; 62 private EGLConfig mEglConfig; 63 private EGLContext mEglContext; 64 private EGLSurface mEglSurface; 65 66 @Override onCreate(Bundle savedInstanceState)67 protected void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 if (mGLThreadLooper == null) { 70 mGLThreadLooper = new HandlerThread("GLThread"); 71 mGLThreadLooper.start(); 72 mGLThread = new Handler(mGLThreadLooper.getLooper()); 73 } 74 75 View preview = new View(this); 76 preview.setBackgroundColor(Color.WHITE); 77 mPreview = preview; 78 mTextureView = new TextureView(this); 79 mTextureView.setSurfaceTextureListener(this); 80 81 FrameLayout content = new FrameLayout(this); 82 content.setBackgroundColor(Color.BLACK); 83 content.addView(mTextureView, 84 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 85 content.addView(mPreview, 86 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 87 88 setContentView(content); 89 } 90 91 private class RunSignalAndCatch implements Runnable { 92 public Throwable error; 93 private Runnable mRunnable; 94 private CountDownLatch mFence; 95 RunSignalAndCatch(Runnable run, CountDownLatch fence)96 RunSignalAndCatch(Runnable run, CountDownLatch fence) { 97 mRunnable = run; 98 mFence = fence; 99 } 100 101 @Override run()102 public void run() { 103 try { 104 mRunnable.run(); 105 } catch (Throwable t) { 106 error = t; 107 } finally { 108 mFence.countDown(); 109 } 110 } 111 } 112 runOnGLThread(Runnable r)113 private void runOnGLThread(Runnable r) throws Throwable { 114 CountDownLatch fence = new CountDownLatch(1); 115 RunSignalAndCatch wrapper = new RunSignalAndCatch(r, fence); 116 mGLThread.post(wrapper); 117 fence.await(TIME_OUT_MS, TimeUnit.MILLISECONDS); 118 if (wrapper.error != null) { 119 throw wrapper.error; 120 } 121 } 122 waitForSurface()123 public void waitForSurface() throws InterruptedException { 124 synchronized (mLock) { 125 while (mSurface == null) { 126 mLock.wait(TIME_OUT_MS); 127 } 128 } 129 } 130 initGl()131 public void initGl() throws Throwable { 132 if (mEglSurface != null) return; 133 runOnGLThread(mDoInitGL); 134 } 135 drawColor(int color)136 public void drawColor(int color) throws Throwable { 137 runOnGLThread(() -> { 138 glClearColor(Color.red(color) / 255.0f, 139 Color.green(color) / 255.0f, 140 Color.blue(color) / 255.0f, 141 Color.alpha(color) / 255.0f); 142 glClear(GL_COLOR_BUFFER_BIT); 143 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 144 throw new RuntimeException("Cannot swap buffers"); 145 } 146 }); 147 } 148 finishGL()149 public void finishGL() throws Throwable { 150 if (mEglSurface == null) return; 151 runOnGLThread(() -> doFinishGL()); 152 } 153 waitForSurfaceUpdateCount(int updateCount)154 public int waitForSurfaceUpdateCount(int updateCount) throws InterruptedException { 155 synchronized (mLock) { 156 while (updateCount > mSurfaceUpdatedCount) { 157 mLock.wait(TIME_OUT_MS); 158 } 159 return mSurfaceUpdatedCount; 160 } 161 } 162 removeCover()163 public void removeCover() { 164 mPreview.setVisibility(View.GONE); 165 } 166 doFinishGL()167 private void doFinishGL() { 168 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 169 mEglContext = null; 170 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 171 mEglSurface = null; 172 mEgl.eglTerminate(mEglDisplay); 173 } 174 175 @Override onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)176 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 177 synchronized (mLock) { 178 mSurface = surface; 179 mLock.notifyAll(); 180 } 181 } 182 183 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)184 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 185 } 186 187 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)188 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 189 synchronized (mLock) { 190 mSurface = null; 191 mLock.notifyAll(); 192 } 193 return true; 194 } 195 196 @Override onSurfaceTextureUpdated(SurfaceTexture surface)197 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 198 synchronized (mLock) { 199 mSurfaceUpdatedCount++; 200 mLock.notifyAll(); 201 } 202 } 203 204 private Runnable mDoInitGL = new Runnable() { 205 @Override 206 public void run() { 207 mEgl = (EGL10) EGLContext.getEGL(); 208 209 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 210 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 211 throw new RuntimeException("eglGetDisplay failed " 212 + GLUtils.getEGLErrorString(mEgl.eglGetError())); 213 } 214 215 int[] version = new int[2]; 216 if (!mEgl.eglInitialize(mEglDisplay, version)) { 217 throw new RuntimeException("eglInitialize failed " + 218 GLUtils.getEGLErrorString(mEgl.eglGetError())); 219 } 220 221 mEglConfig = chooseEglConfig(); 222 if (mEglConfig == null) { 223 throw new RuntimeException("eglConfig not initialized"); 224 } 225 226 mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); 227 228 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, 229 mSurface, null); 230 231 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 232 int error = mEgl.eglGetError(); 233 throw new RuntimeException("createWindowSurface failed " 234 + GLUtils.getEGLErrorString(error)); 235 } 236 237 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 238 throw new RuntimeException("eglMakeCurrent failed " 239 + GLUtils.getEGLErrorString(mEgl.eglGetError())); 240 } 241 } 242 }; 243 createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig)244 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { 245 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 246 return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); 247 } 248 chooseEglConfig()249 private EGLConfig chooseEglConfig() { 250 int[] configsCount = new int[1]; 251 EGLConfig[] configs = new EGLConfig[1]; 252 int[] configSpec = getConfig(); 253 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) { 254 throw new IllegalArgumentException("eglChooseConfig failed " + 255 GLUtils.getEGLErrorString(mEgl.eglGetError())); 256 } else if (configsCount[0] > 0) { 257 return configs[0]; 258 } 259 return null; 260 } 261 getConfig()262 private int[] getConfig() { 263 return new int[] { 264 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 265 EGL10.EGL_RED_SIZE, 8, 266 EGL10.EGL_GREEN_SIZE, 8, 267 EGL10.EGL_BLUE_SIZE, 8, 268 EGL10.EGL_ALPHA_SIZE, 8, 269 EGL10.EGL_DEPTH_SIZE, 0, 270 EGL10.EGL_STENCIL_SIZE, 0, 271 EGL10.EGL_NONE 272 }; 273 } 274 } 275