| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.display; |
| |
| import android.content.Context; |
| import android.graphics.SurfaceTexture; |
| import android.hardware.display.DisplayManagerInternal; |
| import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; |
| import android.opengl.EGL14; |
| import android.opengl.EGLConfig; |
| import android.opengl.EGLContext; |
| import android.opengl.EGLDisplay; |
| import android.opengl.EGLSurface; |
| import android.opengl.GLES11Ext; |
| import android.opengl.GLES20; |
| import android.os.IBinder; |
| import android.util.Slog; |
| import android.view.Display; |
| import android.view.DisplayInfo; |
| import android.view.Surface; |
| import android.view.Surface.OutOfResourcesException; |
| import android.view.SurfaceControl; |
| import android.view.SurfaceControl.Transaction; |
| import android.view.SurfaceSession; |
| |
| import com.android.server.LocalServices; |
| import com.android.server.policy.WindowManagerPolicy; |
| |
| import libcore.io.Streams; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.PrintWriter; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.FloatBuffer; |
| |
| /** |
| * <p> |
| * Animates a screen transition from on to off or off to on by applying |
| * some GL transformations to a screenshot. |
| * </p><p> |
| * This component must only be created or accessed by the {@link Looper} thread |
| * that belongs to the {@link DisplayPowerController}. |
| * </p> |
| */ |
| final class ColorFade { |
| private static final String TAG = "ColorFade"; |
| |
| private static final boolean DEBUG = false; |
| |
| // The layer for the electron beam surface. |
| // This is currently hardcoded to be one layer above the boot animation. |
| private static final int COLOR_FADE_LAYER = WindowManagerPolicy.COLOR_FADE_LAYER; |
| |
| // The number of frames to draw when preparing the animation so that it will |
| // be ready to run smoothly. We use 3 frames because we are triple-buffered. |
| // See code for details. |
| private static final int DEJANK_FRAMES = 3; |
| |
| private static final int EGL_GL_COLORSPACE_KHR = 0x309D; |
| private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490; |
| |
| private final int mDisplayId; |
| |
| // Set to true when the animation context has been fully prepared. |
| private boolean mPrepared; |
| private boolean mCreatedResources; |
| private int mMode; |
| |
| private final DisplayManagerInternal mDisplayManagerInternal; |
| private int mDisplayLayerStack; // layer stack associated with primary display |
| private int mDisplayWidth; // real width, not rotated |
| private int mDisplayHeight; // real height, not rotated |
| private SurfaceSession mSurfaceSession; |
| private SurfaceControl mSurfaceControl; |
| private Surface mSurface; |
| private NaturalSurfaceLayout mSurfaceLayout; |
| private EGLDisplay mEglDisplay; |
| private EGLConfig mEglConfig; |
| private EGLContext mEglContext; |
| private EGLSurface mEglSurface; |
| private boolean mSurfaceVisible; |
| private float mSurfaceAlpha; |
| private boolean mIsWideColor; |
| |
| // Texture names. We only use one texture, which contains the screenshot. |
| private final int[] mTexNames = new int[1]; |
| private boolean mTexNamesGenerated; |
| private final float mTexMatrix[] = new float[16]; |
| private final float mProjMatrix[] = new float[16]; |
| private final int[] mGLBuffers = new int[2]; |
| private int mTexCoordLoc, mVertexLoc, mTexUnitLoc, mProjMatrixLoc, mTexMatrixLoc; |
| private int mOpacityLoc, mGammaLoc; |
| private int mProgram; |
| |
| // Vertex and corresponding texture coordinates. |
| // We have 4 2D vertices, so 8 elements. The vertices form a quad. |
| private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8); |
| private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8); |
| |
| /** |
| * Animates an color fade warming up. |
| */ |
| public static final int MODE_WARM_UP = 0; |
| |
| /** |
| * Animates an color fade shutting off. |
| */ |
| public static final int MODE_COOL_DOWN = 1; |
| |
| /** |
| * Animates a simple dim layer to fade the contents of the screen in or out progressively. |
| */ |
| public static final int MODE_FADE = 2; |
| |
| public ColorFade(int displayId) { |
| mDisplayId = displayId; |
| mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); |
| } |
| |
| /** |
| * Warms up the color fade in preparation for turning on or off. |
| * This method prepares a GL context, and captures a screen shot. |
| * |
| * @param mode The desired mode for the upcoming animation. |
| * @return True if the color fade is ready, false if it is uncontrollable. |
| */ |
| public boolean prepare(Context context, int mode) { |
| if (DEBUG) { |
| Slog.d(TAG, "prepare: mode=" + mode); |
| } |
| |
| mMode = mode; |
| |
| // Get the display size and layer stack. |
| // This is not expected to change while the color fade surface is showing. |
| DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); |
| mDisplayLayerStack = displayInfo.layerStack; |
| mDisplayWidth = displayInfo.getNaturalWidth(); |
| mDisplayHeight = displayInfo.getNaturalHeight(); |
| |
| // Prepare the surface for drawing. |
| if (!(createSurface() && createEglContext() && createEglSurface() && |
| captureScreenshotTextureAndSetViewport())) { |
| dismiss(); |
| return false; |
| } |
| |
| // Init GL |
| if (!attachEglContext()) { |
| return false; |
| } |
| try { |
| if(!initGLShaders(context) || !initGLBuffers() || checkGlErrors("prepare")) { |
| detachEglContext(); |
| dismiss(); |
| return false; |
| } |
| } finally { |
| detachEglContext(); |
| } |
| |
| // Done. |
| mCreatedResources = true; |
| mPrepared = true; |
| |
| // Dejanking optimization. |
| // Some GL drivers can introduce a lot of lag in the first few frames as they |
| // initialize their state and allocate graphics buffers for rendering. |
| // Work around this problem by rendering the first frame of the animation a few |
| // times. The rest of the animation should run smoothly thereafter. |
| // The frames we draw here aren't visible because we are essentially just |
| // painting the screenshot as-is. |
| if (mode == MODE_COOL_DOWN) { |
| for (int i = 0; i < DEJANK_FRAMES; i++) { |
| draw(1.0f); |
| } |
| } |
| return true; |
| } |
| |
| private String readFile(Context context, int resourceId) { |
| try{ |
| InputStream stream = context.getResources().openRawResource(resourceId); |
| return new String(Streams.readFully(new InputStreamReader(stream))); |
| } |
| catch (IOException e) { |
| Slog.e(TAG, "Unrecognized shader " + Integer.toString(resourceId)); |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private int loadShader(Context context, int resourceId, int type) { |
| String source = readFile(context, resourceId); |
| |
| int shader = GLES20.glCreateShader(type); |
| |
| GLES20.glShaderSource(shader, source); |
| GLES20.glCompileShader(shader); |
| |
| int[] compiled = new int[1]; |
| GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); |
| if (compiled[0] == 0) { |
| Slog.e(TAG, "Could not compile shader " + shader + ", " + type + ":"); |
| Slog.e(TAG, GLES20.glGetShaderSource(shader)); |
| Slog.e(TAG, GLES20.glGetShaderInfoLog(shader)); |
| GLES20.glDeleteShader(shader); |
| shader = 0; |
| } |
| |
| return shader; |
| } |
| |
| private boolean initGLShaders(Context context) { |
| int vshader = loadShader(context, com.android.internal.R.raw.color_fade_vert, |
| GLES20.GL_VERTEX_SHADER); |
| int fshader = loadShader(context, com.android.internal.R.raw.color_fade_frag, |
| GLES20.GL_FRAGMENT_SHADER); |
| GLES20.glReleaseShaderCompiler(); |
| if (vshader == 0 || fshader == 0) return false; |
| |
| mProgram = GLES20.glCreateProgram(); |
| |
| GLES20.glAttachShader(mProgram, vshader); |
| GLES20.glAttachShader(mProgram, fshader); |
| GLES20.glDeleteShader(vshader); |
| GLES20.glDeleteShader(fshader); |
| |
| GLES20.glLinkProgram(mProgram); |
| |
| mVertexLoc = GLES20.glGetAttribLocation(mProgram, "position"); |
| mTexCoordLoc = GLES20.glGetAttribLocation(mProgram, "uv"); |
| |
| mProjMatrixLoc = GLES20.glGetUniformLocation(mProgram, "proj_matrix"); |
| mTexMatrixLoc = GLES20.glGetUniformLocation(mProgram, "tex_matrix"); |
| |
| mOpacityLoc = GLES20.glGetUniformLocation(mProgram, "opacity"); |
| mGammaLoc = GLES20.glGetUniformLocation(mProgram, "gamma"); |
| mTexUnitLoc = GLES20.glGetUniformLocation(mProgram, "texUnit"); |
| |
| GLES20.glUseProgram(mProgram); |
| GLES20.glUniform1i(mTexUnitLoc, 0); |
| GLES20.glUseProgram(0); |
| |
| return true; |
| } |
| |
| private void destroyGLShaders() { |
| GLES20.glDeleteProgram(mProgram); |
| checkGlErrors("glDeleteProgram"); |
| } |
| |
| private boolean initGLBuffers() { |
| //Fill vertices |
| setQuad(mVertexBuffer, 0, 0, mDisplayWidth, mDisplayHeight); |
| |
| // Setup GL Textures |
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]); |
| GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, |
| GLES20.GL_NEAREST); |
| GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, |
| GLES20.GL_NEAREST); |
| GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, |
| GLES20.GL_CLAMP_TO_EDGE); |
| GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, |
| GLES20.GL_CLAMP_TO_EDGE); |
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); |
| |
| // Setup GL Buffers |
| GLES20.glGenBuffers(2, mGLBuffers, 0); |
| |
| // fill vertex buffer |
| GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]); |
| GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVertexBuffer.capacity() * 4, |
| mVertexBuffer, GLES20.GL_STATIC_DRAW); |
| |
| // fill tex buffer |
| GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]); |
| GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mTexCoordBuffer.capacity() * 4, |
| mTexCoordBuffer, GLES20.GL_STATIC_DRAW); |
| |
| GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); |
| |
| return true; |
| } |
| |
| private void destroyGLBuffers() { |
| GLES20.glDeleteBuffers(2, mGLBuffers, 0); |
| checkGlErrors("glDeleteBuffers"); |
| } |
| |
| private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) { |
| if (DEBUG) { |
| Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h); |
| } |
| vtx.put(0, x); |
| vtx.put(1, y); |
| vtx.put(2, x); |
| vtx.put(3, y + h); |
| vtx.put(4, x + w); |
| vtx.put(5, y + h); |
| vtx.put(6, x + w); |
| vtx.put(7, y); |
| } |
| |
| /** |
| * Dismisses the color fade animation resources. |
| * |
| * This function destroys the resources that are created for the color fade |
| * animation but does not clean up the surface. |
| */ |
| public void dismissResources() { |
| if (DEBUG) { |
| Slog.d(TAG, "dismissResources"); |
| } |
| |
| if (mCreatedResources) { |
| attachEglContext(); |
| try { |
| destroyScreenshotTexture(); |
| destroyGLShaders(); |
| destroyGLBuffers(); |
| destroyEglSurface(); |
| } finally { |
| detachEglContext(); |
| } |
| // This is being called with no active context so shouldn't be |
| // needed but is safer to not change for now. |
| GLES20.glFlush(); |
| mCreatedResources = false; |
| } |
| } |
| |
| /** |
| * Dismisses the color fade animation surface and cleans up. |
| * |
| * To prevent stray photons from leaking out after the color fade has been |
| * turned off, it is a good idea to defer dismissing the animation until the |
| * color fade has been turned back on fully. |
| */ |
| public void dismiss() { |
| if (DEBUG) { |
| Slog.d(TAG, "dismiss"); |
| } |
| |
| if (mPrepared) { |
| dismissResources(); |
| destroySurface(); |
| mPrepared = false; |
| } |
| } |
| |
| /** |
| * Draws an animation frame showing the color fade activated at the |
| * specified level. |
| * |
| * @param level The color fade level. |
| * @return True if successful. |
| */ |
| public boolean draw(float level) { |
| if (DEBUG) { |
| Slog.d(TAG, "drawFrame: level=" + level); |
| } |
| |
| if (!mPrepared) { |
| return false; |
| } |
| |
| if (mMode == MODE_FADE) { |
| return showSurface(1.0f - level); |
| } |
| |
| if (!attachEglContext()) { |
| return false; |
| } |
| try { |
| // Clear frame to solid black. |
| GLES20.glClearColor(0f, 0f, 0f, 1f); |
| GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
| |
| // Draw the frame. |
| double one_minus_level = 1 - level; |
| double cos = Math.cos(Math.PI * one_minus_level); |
| double sign = cos < 0 ? -1 : 1; |
| float opacity = (float) -Math.pow(one_minus_level, 2) + 1; |
| float gamma = (float) ((0.5d * sign * Math.pow(cos, 2) + 0.5d) * 0.9d + 0.1d); |
| drawFaded(opacity, 1.f / gamma); |
| if (checkGlErrors("drawFrame")) { |
| return false; |
| } |
| |
| EGL14.eglSwapBuffers(mEglDisplay, mEglSurface); |
| } finally { |
| detachEglContext(); |
| } |
| return showSurface(1.0f); |
| } |
| |
| private void drawFaded(float opacity, float gamma) { |
| if (DEBUG) { |
| Slog.d(TAG, "drawFaded: opacity=" + opacity + ", gamma=" + gamma); |
| } |
| // Use shaders |
| GLES20.glUseProgram(mProgram); |
| |
| // Set Uniforms |
| GLES20.glUniformMatrix4fv(mProjMatrixLoc, 1, false, mProjMatrix, 0); |
| GLES20.glUniformMatrix4fv(mTexMatrixLoc, 1, false, mTexMatrix, 0); |
| GLES20.glUniform1f(mOpacityLoc, opacity); |
| GLES20.glUniform1f(mGammaLoc, gamma); |
| |
| // Use textures |
| GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]); |
| |
| // draw the plane |
| GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[0]); |
| GLES20.glEnableVertexAttribArray(mVertexLoc); |
| GLES20.glVertexAttribPointer(mVertexLoc, 2, GLES20.GL_FLOAT, false, 0, 0); |
| |
| GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLBuffers[1]); |
| GLES20.glEnableVertexAttribArray(mTexCoordLoc); |
| GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, 0); |
| |
| GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4); |
| |
| // clean up |
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); |
| GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); |
| } |
| |
| private void ortho(float left, float right, float bottom, float top, float znear, float zfar) { |
| mProjMatrix[0] = 2f / (right - left); |
| mProjMatrix[1] = 0; |
| mProjMatrix[2] = 0; |
| mProjMatrix[3] = 0; |
| mProjMatrix[4] = 0; |
| mProjMatrix[5] = 2f / (top - bottom); |
| mProjMatrix[6] = 0; |
| mProjMatrix[7] = 0; |
| mProjMatrix[8] = 0; |
| mProjMatrix[9] = 0; |
| mProjMatrix[10] = -2f / (zfar - znear); |
| mProjMatrix[11] = 0; |
| mProjMatrix[12] = -(right + left) / (right - left); |
| mProjMatrix[13] = -(top + bottom) / (top - bottom); |
| mProjMatrix[14] = -(zfar + znear) / (zfar - znear); |
| mProjMatrix[15] = 1f; |
| } |
| |
| private boolean captureScreenshotTextureAndSetViewport() { |
| if (!attachEglContext()) { |
| return false; |
| } |
| try { |
| if (!mTexNamesGenerated) { |
| GLES20.glGenTextures(1, mTexNames, 0); |
| if (checkGlErrors("glGenTextures")) { |
| return false; |
| } |
| mTexNamesGenerated = true; |
| } |
| |
| final SurfaceTexture st = new SurfaceTexture(mTexNames[0]); |
| final Surface s = new Surface(st); |
| try { |
| final IBinder token = SurfaceControl.getInternalDisplayToken(); |
| if (token == null) { |
| Slog.e(TAG, |
| "Failed to take screenshot because internal display is disconnected"); |
| return false; |
| } |
| |
| mIsWideColor = SurfaceControl.getActiveColorMode(token) |
| == Display.COLOR_MODE_DISPLAY_P3; |
| SurfaceControl.screenshot(token, s); |
| st.updateTexImage(); |
| st.getTransformMatrix(mTexMatrix); |
| } finally { |
| s.release(); |
| st.release(); |
| } |
| |
| // Set up texture coordinates for a quad. |
| // We might need to change this if the texture ends up being |
| // a different size from the display for some reason. |
| mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f); |
| mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f); |
| mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f); |
| mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f); |
| |
| // Set up our viewport. |
| GLES20.glViewport(0, 0, mDisplayWidth, mDisplayHeight); |
| ortho(0, mDisplayWidth, 0, mDisplayHeight, -1, 1); |
| } finally { |
| detachEglContext(); |
| } |
| return true; |
| } |
| |
| private void destroyScreenshotTexture() { |
| if (mTexNamesGenerated) { |
| mTexNamesGenerated = false; |
| GLES20.glDeleteTextures(1, mTexNames, 0); |
| checkGlErrors("glDeleteTextures"); |
| } |
| } |
| |
| private boolean createEglContext() { |
| if (mEglDisplay == null) { |
| mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); |
| if (mEglDisplay == EGL14.EGL_NO_DISPLAY) { |
| logEglError("eglGetDisplay"); |
| return false; |
| } |
| |
| int[] version = new int[2]; |
| if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) { |
| mEglDisplay = null; |
| logEglError("eglInitialize"); |
| return false; |
| } |
| } |
| |
| if (mEglConfig == null) { |
| int[] eglConfigAttribList = new int[] { |
| EGL14.EGL_RENDERABLE_TYPE, |
| EGL14.EGL_OPENGL_ES2_BIT, |
| EGL14.EGL_RED_SIZE, 8, |
| EGL14.EGL_GREEN_SIZE, 8, |
| EGL14.EGL_BLUE_SIZE, 8, |
| EGL14.EGL_ALPHA_SIZE, 8, |
| EGL14.EGL_NONE |
| }; |
| int[] numEglConfigs = new int[1]; |
| EGLConfig[] eglConfigs = new EGLConfig[1]; |
| if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0, |
| eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { |
| logEglError("eglChooseConfig"); |
| return false; |
| } |
| if (numEglConfigs[0] <= 0) { |
| Slog.e(TAG, "no valid config found"); |
| return false; |
| } |
| |
| mEglConfig = eglConfigs[0]; |
| } |
| |
| if (mEglContext == null) { |
| int[] eglContextAttribList = new int[] { |
| EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, |
| EGL14.EGL_NONE |
| }; |
| mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, |
| EGL14.EGL_NO_CONTEXT, eglContextAttribList, 0); |
| if (mEglContext == null) { |
| logEglError("eglCreateContext"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean createSurface() { |
| if (mSurfaceSession == null) { |
| mSurfaceSession = new SurfaceSession(); |
| } |
| |
| if (mSurfaceControl == null) { |
| Transaction t = new Transaction(); |
| try { |
| final SurfaceControl.Builder builder = |
| new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade"); |
| if (mMode == MODE_FADE) { |
| builder.setColorLayer(); |
| } else { |
| builder.setBufferSize(mDisplayWidth, mDisplayHeight); |
| } |
| mSurfaceControl = builder.build(); |
| } catch (OutOfResourcesException ex) { |
| Slog.e(TAG, "Unable to create surface.", ex); |
| return false; |
| } |
| |
| t.setLayerStack(mSurfaceControl, mDisplayLayerStack); |
| t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight); |
| mSurface = new Surface(); |
| mSurface.copyFrom(mSurfaceControl); |
| |
| mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, |
| mDisplayId, mSurfaceControl); |
| mSurfaceLayout.onDisplayTransaction(t); |
| t.apply(); |
| } |
| return true; |
| } |
| |
| private boolean createEglSurface() { |
| if (mEglSurface == null) { |
| int[] eglSurfaceAttribList = new int[] { |
| EGL14.EGL_NONE, |
| EGL14.EGL_NONE, |
| EGL14.EGL_NONE |
| }; |
| |
| // If the current display is in wide color, then so is the screenshot. |
| if (mIsWideColor) { |
| eglSurfaceAttribList[0] = EGL_GL_COLORSPACE_KHR; |
| eglSurfaceAttribList[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; |
| } |
| // turn our SurfaceControl into a Surface |
| mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, |
| eglSurfaceAttribList, 0); |
| if (mEglSurface == null) { |
| logEglError("eglCreateWindowSurface"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void destroyEglSurface() { |
| if (mEglSurface != null) { |
| if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) { |
| logEglError("eglDestroySurface"); |
| } |
| mEglSurface = null; |
| } |
| } |
| |
| private void destroySurface() { |
| if (mSurfaceControl != null) { |
| mSurfaceLayout.dispose(); |
| mSurfaceLayout = null; |
| new Transaction().remove(mSurfaceControl).apply(); |
| mSurface.release(); |
| mSurfaceControl = null; |
| mSurfaceVisible = false; |
| mSurfaceAlpha = 0f; |
| } |
| } |
| |
| private boolean showSurface(float alpha) { |
| if (!mSurfaceVisible || mSurfaceAlpha != alpha) { |
| SurfaceControl.openTransaction(); |
| try { |
| mSurfaceControl.setLayer(COLOR_FADE_LAYER); |
| mSurfaceControl.setAlpha(alpha); |
| mSurfaceControl.show(); |
| } finally { |
| SurfaceControl.closeTransaction(); |
| } |
| mSurfaceVisible = true; |
| mSurfaceAlpha = alpha; |
| } |
| return true; |
| } |
| |
| private boolean attachEglContext() { |
| if (mEglSurface == null) { |
| return false; |
| } |
| if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { |
| logEglError("eglMakeCurrent"); |
| return false; |
| } |
| return true; |
| } |
| |
| private void detachEglContext() { |
| if (mEglDisplay != null) { |
| EGL14.eglMakeCurrent(mEglDisplay, |
| EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); |
| } |
| } |
| |
| private static FloatBuffer createNativeFloatBuffer(int size) { |
| ByteBuffer bb = ByteBuffer.allocateDirect(size * 4); |
| bb.order(ByteOrder.nativeOrder()); |
| return bb.asFloatBuffer(); |
| } |
| |
| private static void logEglError(String func) { |
| Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable()); |
| } |
| |
| private static boolean checkGlErrors(String func) { |
| return checkGlErrors(func, true); |
| } |
| |
| private static boolean checkGlErrors(String func, boolean log) { |
| boolean hadError = false; |
| int error; |
| while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { |
| if (log) { |
| Slog.e(TAG, func + " failed: error " + error, new Throwable()); |
| } |
| hadError = true; |
| } |
| return hadError; |
| } |
| |
| public void dump(PrintWriter pw) { |
| pw.println(); |
| pw.println("Color Fade State:"); |
| pw.println(" mPrepared=" + mPrepared); |
| pw.println(" mMode=" + mMode); |
| pw.println(" mDisplayLayerStack=" + mDisplayLayerStack); |
| pw.println(" mDisplayWidth=" + mDisplayWidth); |
| pw.println(" mDisplayHeight=" + mDisplayHeight); |
| pw.println(" mSurfaceVisible=" + mSurfaceVisible); |
| pw.println(" mSurfaceAlpha=" + mSurfaceAlpha); |
| } |
| |
| /** |
| * Keeps a surface aligned with the natural orientation of the device. |
| * Updates the position and transformation of the matrix whenever the display |
| * is rotated. This is a little tricky because the display transaction |
| * callback can be invoked on any thread, not necessarily the thread that |
| * owns the color fade. |
| */ |
| private static final class NaturalSurfaceLayout implements DisplayTransactionListener { |
| private final DisplayManagerInternal mDisplayManagerInternal; |
| private final int mDisplayId; |
| private SurfaceControl mSurfaceControl; |
| |
| public NaturalSurfaceLayout(DisplayManagerInternal displayManagerInternal, |
| int displayId, SurfaceControl surfaceControl) { |
| mDisplayManagerInternal = displayManagerInternal; |
| mDisplayId = displayId; |
| mSurfaceControl = surfaceControl; |
| mDisplayManagerInternal.registerDisplayTransactionListener(this); |
| } |
| |
| public void dispose() { |
| synchronized (this) { |
| mSurfaceControl = null; |
| } |
| mDisplayManagerInternal.unregisterDisplayTransactionListener(this); |
| } |
| |
| @Override |
| public void onDisplayTransaction(Transaction t) { |
| synchronized (this) { |
| if (mSurfaceControl == null) { |
| return; |
| } |
| |
| DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); |
| switch (displayInfo.rotation) { |
| case Surface.ROTATION_0: |
| t.setPosition(mSurfaceControl, 0, 0); |
| t.setMatrix(mSurfaceControl, 1, 0, 0, 1); |
| break; |
| case Surface.ROTATION_90: |
| t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight); |
| t.setMatrix(mSurfaceControl, 0, -1, 1, 0); |
| break; |
| case Surface.ROTATION_180: |
| t.setPosition(mSurfaceControl, displayInfo.logicalWidth, |
| displayInfo.logicalHeight); |
| t.setMatrix(mSurfaceControl, -1, 0, 0, -1); |
| break; |
| case Surface.ROTATION_270: |
| t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0); |
| t.setMatrix(mSurfaceControl, 0, 1, -1, 0); |
| break; |
| } |
| } |
| } |
| } |
| } |