/packages/WallpaperCropper/src/com/android/photos/views/BlockingGLTextureView.java

https://github.com/aizuzi/platform_frameworks_base · Java · 438 lines · 307 code · 57 blank · 74 comment · 56 complexity · 5f4d53cae875fb59b8cc450f3813479b MD5 · raw file

  1. /*
  2. * Copyright (C) 2013 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. package com.android.photos.views;
  17. import android.content.Context;
  18. import android.graphics.SurfaceTexture;
  19. import android.opengl.GLSurfaceView.Renderer;
  20. import android.opengl.GLUtils;
  21. import android.util.Log;
  22. import android.view.TextureView;
  23. import android.view.TextureView.SurfaceTextureListener;
  24. import javax.microedition.khronos.egl.EGL10;
  25. import javax.microedition.khronos.egl.EGLConfig;
  26. import javax.microedition.khronos.egl.EGLContext;
  27. import javax.microedition.khronos.egl.EGLDisplay;
  28. import javax.microedition.khronos.egl.EGLSurface;
  29. import javax.microedition.khronos.opengles.GL10;
  30. /**
  31. * A TextureView that supports blocking rendering for synchronous drawing
  32. */
  33. public class BlockingGLTextureView extends TextureView
  34. implements SurfaceTextureListener {
  35. private RenderThread mRenderThread;
  36. public BlockingGLTextureView(Context context) {
  37. super(context);
  38. setSurfaceTextureListener(this);
  39. }
  40. public void setRenderer(Renderer renderer) {
  41. if (mRenderThread != null) {
  42. throw new IllegalArgumentException("Renderer already set");
  43. }
  44. mRenderThread = new RenderThread(renderer);
  45. }
  46. public void render() {
  47. mRenderThread.render();
  48. }
  49. public void destroy() {
  50. if (mRenderThread != null) {
  51. mRenderThread.finish();
  52. mRenderThread = null;
  53. }
  54. }
  55. @Override
  56. public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
  57. int height) {
  58. mRenderThread.setSurface(surface);
  59. mRenderThread.setSize(width, height);
  60. }
  61. @Override
  62. public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
  63. int height) {
  64. mRenderThread.setSize(width, height);
  65. }
  66. @Override
  67. public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
  68. if (mRenderThread != null) {
  69. mRenderThread.setSurface(null);
  70. }
  71. return false;
  72. }
  73. @Override
  74. public void onSurfaceTextureUpdated(SurfaceTexture surface) {
  75. }
  76. @Override
  77. protected void finalize() throws Throwable {
  78. try {
  79. destroy();
  80. } catch (Throwable t) {
  81. // Ignore
  82. }
  83. super.finalize();
  84. }
  85. /**
  86. * An EGL helper class.
  87. */
  88. private static class EglHelper {
  89. private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
  90. private static final int EGL_OPENGL_ES2_BIT = 4;
  91. EGL10 mEgl;
  92. EGLDisplay mEglDisplay;
  93. EGLSurface mEglSurface;
  94. EGLConfig mEglConfig;
  95. EGLContext mEglContext;
  96. private EGLConfig chooseEglConfig() {
  97. int[] configsCount = new int[1];
  98. EGLConfig[] configs = new EGLConfig[1];
  99. int[] configSpec = getConfig();
  100. if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
  101. throw new IllegalArgumentException("eglChooseConfig failed " +
  102. GLUtils.getEGLErrorString(mEgl.eglGetError()));
  103. } else if (configsCount[0] > 0) {
  104. return configs[0];
  105. }
  106. return null;
  107. }
  108. private static int[] getConfig() {
  109. return new int[] {
  110. EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
  111. EGL10.EGL_RED_SIZE, 8,
  112. EGL10.EGL_GREEN_SIZE, 8,
  113. EGL10.EGL_BLUE_SIZE, 8,
  114. EGL10.EGL_ALPHA_SIZE, 8,
  115. EGL10.EGL_DEPTH_SIZE, 0,
  116. EGL10.EGL_STENCIL_SIZE, 0,
  117. EGL10.EGL_NONE
  118. };
  119. }
  120. EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
  121. int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
  122. return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attribList);
  123. }
  124. /**
  125. * Initialize EGL for a given configuration spec.
  126. */
  127. public void start() {
  128. /*
  129. * Get an EGL instance
  130. */
  131. mEgl = (EGL10) EGLContext.getEGL();
  132. /*
  133. * Get to the default display.
  134. */
  135. mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
  136. if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
  137. throw new RuntimeException("eglGetDisplay failed");
  138. }
  139. /*
  140. * We can now initialize EGL for that display
  141. */
  142. int[] version = new int[2];
  143. if (!mEgl.eglInitialize(mEglDisplay, version)) {
  144. throw new RuntimeException("eglInitialize failed");
  145. }
  146. mEglConfig = chooseEglConfig();
  147. /*
  148. * Create an EGL context. We want to do this as rarely as we can, because an
  149. * EGL context is a somewhat heavy object.
  150. */
  151. mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
  152. if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
  153. mEglContext = null;
  154. throwEglException("createContext");
  155. }
  156. mEglSurface = null;
  157. }
  158. /**
  159. * Create an egl surface for the current SurfaceTexture surface. If a surface
  160. * already exists, destroy it before creating the new surface.
  161. *
  162. * @return true if the surface was created successfully.
  163. */
  164. public boolean createSurface(SurfaceTexture surface) {
  165. /*
  166. * Check preconditions.
  167. */
  168. if (mEgl == null) {
  169. throw new RuntimeException("egl not initialized");
  170. }
  171. if (mEglDisplay == null) {
  172. throw new RuntimeException("eglDisplay not initialized");
  173. }
  174. if (mEglConfig == null) {
  175. throw new RuntimeException("mEglConfig not initialized");
  176. }
  177. /*
  178. * The window size has changed, so we need to create a new
  179. * surface.
  180. */
  181. destroySurfaceImp();
  182. /*
  183. * Create an EGL surface we can render into.
  184. */
  185. if (surface != null) {
  186. mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, null);
  187. } else {
  188. mEglSurface = null;
  189. }
  190. if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
  191. int error = mEgl.eglGetError();
  192. if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
  193. Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
  194. }
  195. return false;
  196. }
  197. /*
  198. * Before we can issue GL commands, we need to make sure
  199. * the context is current and bound to a surface.
  200. */
  201. if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
  202. /*
  203. * Could not make the context current, probably because the underlying
  204. * SurfaceView surface has been destroyed.
  205. */
  206. logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
  207. return false;
  208. }
  209. return true;
  210. }
  211. /**
  212. * Create a GL object for the current EGL context.
  213. */
  214. public GL10 createGL() {
  215. return (GL10) mEglContext.getGL();
  216. }
  217. /**
  218. * Display the current render surface.
  219. * @return the EGL error code from eglSwapBuffers.
  220. */
  221. public int swap() {
  222. if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
  223. return mEgl.eglGetError();
  224. }
  225. return EGL10.EGL_SUCCESS;
  226. }
  227. public void destroySurface() {
  228. destroySurfaceImp();
  229. }
  230. private void destroySurfaceImp() {
  231. if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
  232. mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
  233. EGL10.EGL_NO_SURFACE,
  234. EGL10.EGL_NO_CONTEXT);
  235. mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
  236. mEglSurface = null;
  237. }
  238. }
  239. public void finish() {
  240. if (mEglContext != null) {
  241. mEgl.eglDestroyContext(mEglDisplay, mEglContext);
  242. mEglContext = null;
  243. }
  244. if (mEglDisplay != null) {
  245. mEgl.eglTerminate(mEglDisplay);
  246. mEglDisplay = null;
  247. }
  248. }
  249. private void throwEglException(String function) {
  250. throwEglException(function, mEgl.eglGetError());
  251. }
  252. public static void throwEglException(String function, int error) {
  253. String message = formatEglError(function, error);
  254. throw new RuntimeException(message);
  255. }
  256. public static void logEglErrorAsWarning(String tag, String function, int error) {
  257. Log.w(tag, formatEglError(function, error));
  258. }
  259. public static String formatEglError(String function, int error) {
  260. return function + " failed: " + error;
  261. }
  262. }
  263. private static class RenderThread extends Thread {
  264. private static final int INVALID = -1;
  265. private static final int RENDER = 1;
  266. private static final int CHANGE_SURFACE = 2;
  267. private static final int RESIZE_SURFACE = 3;
  268. private static final int FINISH = 4;
  269. private EglHelper mEglHelper = new EglHelper();
  270. private Object mLock = new Object();
  271. private int mExecMsgId = INVALID;
  272. private SurfaceTexture mSurface;
  273. private Renderer mRenderer;
  274. private int mWidth, mHeight;
  275. private boolean mFinished = false;
  276. private GL10 mGL;
  277. public RenderThread(Renderer renderer) {
  278. super("RenderThread");
  279. mRenderer = renderer;
  280. start();
  281. }
  282. private void checkRenderer() {
  283. if (mRenderer == null) {
  284. throw new IllegalArgumentException("Renderer is null!");
  285. }
  286. }
  287. private void checkSurface() {
  288. if (mSurface == null) {
  289. throw new IllegalArgumentException("surface is null!");
  290. }
  291. }
  292. public void setSurface(SurfaceTexture surface) {
  293. // If the surface is null we're being torn down, don't need a
  294. // renderer then
  295. if (surface != null) {
  296. checkRenderer();
  297. }
  298. mSurface = surface;
  299. exec(CHANGE_SURFACE);
  300. }
  301. public void setSize(int width, int height) {
  302. checkRenderer();
  303. checkSurface();
  304. mWidth = width;
  305. mHeight = height;
  306. exec(RESIZE_SURFACE);
  307. }
  308. public void render() {
  309. checkRenderer();
  310. if (mSurface != null) {
  311. exec(RENDER);
  312. mSurface.updateTexImage();
  313. }
  314. }
  315. public void finish() {
  316. mSurface = null;
  317. exec(FINISH);
  318. try {
  319. join();
  320. } catch (InterruptedException e) {
  321. // Ignore
  322. }
  323. }
  324. private void exec(int msgid) {
  325. synchronized (mLock) {
  326. if (mExecMsgId != INVALID) {
  327. throw new IllegalArgumentException(
  328. "Message already set - multithreaded access?");
  329. }
  330. mExecMsgId = msgid;
  331. mLock.notify();
  332. try {
  333. mLock.wait();
  334. } catch (InterruptedException e) {
  335. // Ignore
  336. }
  337. }
  338. }
  339. private void handleMessageLocked(int what) {
  340. switch (what) {
  341. case CHANGE_SURFACE:
  342. if (mEglHelper.createSurface(mSurface)) {
  343. mGL = mEglHelper.createGL();
  344. mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
  345. }
  346. break;
  347. case RESIZE_SURFACE:
  348. mRenderer.onSurfaceChanged(mGL, mWidth, mHeight);
  349. break;
  350. case RENDER:
  351. mRenderer.onDrawFrame(mGL);
  352. mEglHelper.swap();
  353. break;
  354. case FINISH:
  355. mEglHelper.destroySurface();
  356. mEglHelper.finish();
  357. mFinished = true;
  358. break;
  359. }
  360. }
  361. @Override
  362. public void run() {
  363. synchronized (mLock) {
  364. mEglHelper.start();
  365. while (!mFinished) {
  366. while (mExecMsgId == INVALID) {
  367. try {
  368. mLock.wait();
  369. } catch (InterruptedException e) {
  370. // Ignore
  371. }
  372. }
  373. handleMessageLocked(mExecMsgId);
  374. mExecMsgId = INVALID;
  375. mLock.notify();
  376. }
  377. mExecMsgId = FINISH;
  378. }
  379. }
  380. }
  381. }