1 /* 2 * Copyright (C) 2019 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 com.android.systemui.glwallpaper; 18 19 import static android.opengl.EGL14.EGL_ALPHA_SIZE; 20 import static android.opengl.EGL14.EGL_BLUE_SIZE; 21 import static android.opengl.EGL14.EGL_CONFIG_CAVEAT; 22 import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION; 23 import static android.opengl.EGL14.EGL_DEFAULT_DISPLAY; 24 import static android.opengl.EGL14.EGL_DEPTH_SIZE; 25 import static android.opengl.EGL14.EGL_EXTENSIONS; 26 import static android.opengl.EGL14.EGL_GREEN_SIZE; 27 import static android.opengl.EGL14.EGL_NONE; 28 import static android.opengl.EGL14.EGL_NO_CONTEXT; 29 import static android.opengl.EGL14.EGL_NO_DISPLAY; 30 import static android.opengl.EGL14.EGL_NO_SURFACE; 31 import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT; 32 import static android.opengl.EGL14.EGL_RED_SIZE; 33 import static android.opengl.EGL14.EGL_RENDERABLE_TYPE; 34 import static android.opengl.EGL14.EGL_STENCIL_SIZE; 35 import static android.opengl.EGL14.EGL_SUCCESS; 36 import static android.opengl.EGL14.eglChooseConfig; 37 import static android.opengl.EGL14.eglCreateContext; 38 import static android.opengl.EGL14.eglCreateWindowSurface; 39 import static android.opengl.EGL14.eglDestroyContext; 40 import static android.opengl.EGL14.eglDestroySurface; 41 import static android.opengl.EGL14.eglGetDisplay; 42 import static android.opengl.EGL14.eglGetError; 43 import static android.opengl.EGL14.eglInitialize; 44 import static android.opengl.EGL14.eglMakeCurrent; 45 import static android.opengl.EGL14.eglQueryString; 46 import static android.opengl.EGL14.eglSwapBuffers; 47 import static android.opengl.EGL14.eglTerminate; 48 49 import android.opengl.EGLConfig; 50 import android.opengl.EGLContext; 51 import android.opengl.EGLDisplay; 52 import android.opengl.EGLSurface; 53 import android.opengl.GLUtils; 54 import android.text.TextUtils; 55 import android.util.Log; 56 import android.view.SurfaceHolder; 57 58 import java.io.FileDescriptor; 59 import java.io.PrintWriter; 60 import java.util.Collections; 61 import java.util.HashSet; 62 import java.util.Set; 63 64 /** 65 * A helper class to handle EGL management. 66 */ 67 public class EglHelper { 68 private static final String TAG = EglHelper.class.getSimpleName(); 69 private static final int OPENGLES_VERSION = 2; 70 // Below two constants make drawing at low priority, so other things can preempt our drawing. 71 private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100; 72 private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103; 73 private static final boolean DEBUG = true; 74 75 private static final int EGL_GL_COLORSPACE_KHR = 0x309D; 76 private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490; 77 78 private static final String EGL_IMG_CONTEXT_PRIORITY = "EGL_IMG_context_priority"; 79 80 /** 81 * https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt 82 */ 83 private static final String KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace"; 84 85 /** 86 * https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3_passthrough.txt 87 */ 88 private static final String EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH = 89 "EGL_EXT_gl_colorspace_display_p3_passthrough"; 90 91 private EGLDisplay mEglDisplay; 92 private EGLConfig mEglConfig; 93 private EGLContext mEglContext; 94 private EGLSurface mEglSurface; 95 private final int[] mEglVersion = new int[2]; 96 private boolean mEglReady; 97 private final Set<String> mExts; 98 EglHelper()99 public EglHelper() { 100 mExts = new HashSet<>(); 101 connectDisplay(); 102 } 103 104 /** 105 * Initialize render context. 106 * @param surfaceHolder surface holder. 107 * @param wideColorGamut claim if a wcg surface is necessary. 108 * @return true if the render context is ready. 109 */ init(SurfaceHolder surfaceHolder, boolean wideColorGamut)110 public boolean init(SurfaceHolder surfaceHolder, boolean wideColorGamut) { 111 if (!hasEglDisplay() && !connectDisplay()) { 112 Log.w(TAG, "Can not connect display, abort!"); 113 return false; 114 } 115 116 if (!eglInitialize(mEglDisplay, mEglVersion, 0 /* majorOffset */, 117 mEglVersion, 1 /* minorOffset */)) { 118 Log.w(TAG, "eglInitialize failed: " + GLUtils.getEGLErrorString(eglGetError())); 119 return false; 120 } 121 122 mEglConfig = chooseEglConfig(); 123 if (mEglConfig == null) { 124 Log.w(TAG, "eglConfig not initialized!"); 125 return false; 126 } 127 128 if (!createEglContext()) { 129 Log.w(TAG, "Can't create EGLContext!"); 130 return false; 131 } 132 133 if (!createEglSurface(surfaceHolder, wideColorGamut)) { 134 Log.w(TAG, "Can't create EGLSurface!"); 135 return false; 136 } 137 138 mEglReady = true; 139 return true; 140 } 141 connectDisplay()142 private boolean connectDisplay() { 143 mExts.clear(); 144 mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 145 if (!hasEglDisplay()) { 146 Log.w(TAG, "eglGetDisplay failed: " + GLUtils.getEGLErrorString(eglGetError())); 147 return false; 148 } 149 String queryString = eglQueryString(mEglDisplay, EGL_EXTENSIONS); 150 if (!TextUtils.isEmpty(queryString)) { 151 Collections.addAll(mExts, queryString.split(" ")); 152 } 153 return true; 154 } 155 checkExtensionCapability(String extName)156 boolean checkExtensionCapability(String extName) { 157 return mExts.contains(extName); 158 } 159 getWcgCapability()160 int getWcgCapability() { 161 if (checkExtensionCapability(EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH)) { 162 return EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; 163 } 164 return 0; 165 } 166 chooseEglConfig()167 private EGLConfig chooseEglConfig() { 168 int[] configsCount = new int[1]; 169 EGLConfig[] configs = new EGLConfig[1]; 170 int[] configSpec = getConfig(); 171 if (!eglChooseConfig(mEglDisplay, configSpec, 0, configs, 0, 1, configsCount, 0)) { 172 Log.w(TAG, "eglChooseConfig failed: " + GLUtils.getEGLErrorString(eglGetError())); 173 return null; 174 } else { 175 if (configsCount[0] <= 0) { 176 Log.w(TAG, "eglChooseConfig failed, invalid configs count: " + configsCount[0]); 177 return null; 178 } else { 179 return configs[0]; 180 } 181 } 182 } 183 getConfig()184 private int[] getConfig() { 185 return new int[] { 186 EGL_RED_SIZE, 8, 187 EGL_GREEN_SIZE, 8, 188 EGL_BLUE_SIZE, 8, 189 EGL_ALPHA_SIZE, 0, 190 EGL_DEPTH_SIZE, 0, 191 EGL_STENCIL_SIZE, 0, 192 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 193 EGL_CONFIG_CAVEAT, EGL_NONE, 194 EGL_NONE 195 }; 196 } 197 198 /** 199 * Prepare an EglSurface. 200 * @param surfaceHolder surface holder. 201 * @param wcg if need to support wcg. 202 * @return true if EglSurface is ready. 203 */ createEglSurface(SurfaceHolder surfaceHolder, boolean wcg)204 public boolean createEglSurface(SurfaceHolder surfaceHolder, boolean wcg) { 205 if (DEBUG) { 206 Log.d(TAG, "createEglSurface start"); 207 } 208 209 if (hasEglDisplay() && surfaceHolder.getSurface().isValid()) { 210 int[] attrs = null; 211 int wcgCapability = getWcgCapability(); 212 if (wcg && checkExtensionCapability(KHR_GL_COLOR_SPACE) && wcgCapability > 0) { 213 attrs = new int[] {EGL_GL_COLORSPACE_KHR, wcgCapability, EGL_NONE}; 214 } 215 mEglSurface = askCreatingEglWindowSurface(surfaceHolder, attrs, 0 /* offset */); 216 } else { 217 Log.w(TAG, "Create EglSurface failed: hasEglDisplay=" + hasEglDisplay() 218 + ", has valid surface=" + surfaceHolder.getSurface().isValid()); 219 return false; 220 } 221 222 if (!hasEglSurface()) { 223 Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError())); 224 return false; 225 } 226 227 if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 228 Log.w(TAG, "eglMakeCurrent failed: " + GLUtils.getEGLErrorString(eglGetError())); 229 return false; 230 } 231 232 if (DEBUG) { 233 Log.d(TAG, "createEglSurface done"); 234 } 235 return true; 236 } 237 askCreatingEglWindowSurface(SurfaceHolder holder, int[] attrs, int offset)238 EGLSurface askCreatingEglWindowSurface(SurfaceHolder holder, int[] attrs, int offset) { 239 return eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, attrs, offset); 240 } 241 242 /** 243 * Destroy EglSurface. 244 */ destroyEglSurface()245 public void destroyEglSurface() { 246 if (hasEglSurface()) { 247 eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 248 eglDestroySurface(mEglDisplay, mEglSurface); 249 mEglSurface = EGL_NO_SURFACE; 250 } 251 } 252 253 /** 254 * Check if we have a valid EglSurface. 255 * @return true if EglSurface is ready. 256 */ hasEglSurface()257 public boolean hasEglSurface() { 258 return mEglSurface != null && mEglSurface != EGL_NO_SURFACE; 259 } 260 261 /** 262 * Prepare EglContext. 263 * @return true if EglContext is ready. 264 */ createEglContext()265 public boolean createEglContext() { 266 if (DEBUG) { 267 Log.d(TAG, "createEglContext start"); 268 } 269 270 int[] attrib_list = new int[5]; 271 int idx = 0; 272 attrib_list[idx++] = EGL_CONTEXT_CLIENT_VERSION; 273 attrib_list[idx++] = OPENGLES_VERSION; 274 if (checkExtensionCapability(EGL_IMG_CONTEXT_PRIORITY)) { 275 attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; 276 attrib_list[idx++] = EGL_CONTEXT_PRIORITY_LOW_IMG; 277 } 278 attrib_list[idx] = EGL_NONE; 279 if (hasEglDisplay()) { 280 mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0); 281 } else { 282 Log.w(TAG, "mEglDisplay is null"); 283 return false; 284 } 285 286 if (!hasEglContext()) { 287 Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError())); 288 return false; 289 } 290 291 if (DEBUG) { 292 Log.d(TAG, "createEglContext done"); 293 } 294 return true; 295 } 296 297 /** 298 * Destroy EglContext. 299 */ destroyEglContext()300 public void destroyEglContext() { 301 if (hasEglContext()) { 302 eglDestroyContext(mEglDisplay, mEglContext); 303 mEglContext = EGL_NO_CONTEXT; 304 } 305 } 306 307 /** 308 * Check if we have EglContext. 309 * @return true if EglContext is ready. 310 */ hasEglContext()311 public boolean hasEglContext() { 312 return mEglContext != null && mEglContext != EGL_NO_CONTEXT; 313 } 314 315 /** 316 * Check if we have EglDisplay. 317 * @return true if EglDisplay is ready. 318 */ hasEglDisplay()319 public boolean hasEglDisplay() { 320 return mEglDisplay != null && mEglDisplay != EGL_NO_DISPLAY; 321 } 322 323 /** 324 * Swap buffer to display. 325 * @return true if swap successfully. 326 */ swapBuffer()327 public boolean swapBuffer() { 328 boolean status = eglSwapBuffers(mEglDisplay, mEglSurface); 329 int error = eglGetError(); 330 if (error != EGL_SUCCESS) { 331 Log.w(TAG, "eglSwapBuffers failed: " + GLUtils.getEGLErrorString(error)); 332 } 333 return status; 334 } 335 336 /** 337 * Destroy EglSurface and EglContext, then terminate EGL. 338 */ finish()339 public void finish() { 340 if (hasEglSurface()) { 341 destroyEglSurface(); 342 } 343 if (hasEglContext()) { 344 destroyEglContext(); 345 } 346 if (hasEglDisplay()) { 347 terminateEglDisplay(); 348 } 349 mEglReady = false; 350 } 351 terminateEglDisplay()352 void terminateEglDisplay() { 353 eglTerminate(mEglDisplay); 354 mEglDisplay = EGL_NO_DISPLAY; 355 } 356 357 /** 358 * Called to dump current state. 359 * @param prefix prefix. 360 * @param fd fd. 361 * @param out out. 362 * @param args args. 363 */ dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)364 public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 365 String eglVersion = mEglVersion[0] + "." + mEglVersion[1]; 366 out.print(prefix); out.print("EGL version="); out.print(eglVersion); 367 out.print(", "); out.print("EGL ready="); out.print(mEglReady); 368 out.print(", "); out.print("has EglContext="); out.print(hasEglContext()); 369 out.print(", "); out.print("has EglSurface="); out.println(hasEglSurface()); 370 371 int[] configs = getConfig(); 372 StringBuilder sb = new StringBuilder(); 373 sb.append('{'); 374 for (int egl : configs) { 375 sb.append("0x").append(Integer.toHexString(egl)).append(","); 376 } 377 sb.setCharAt(sb.length() - 1, '}'); 378 out.print(prefix); out.print("EglConfig="); out.println(sb.toString()); 379 } 380 } 381