1 /*
2  * Copyright (C) 2020 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.car.pm.blurredbackground;
18 
19 import android.content.Context;
20 import android.opengl.GLES30;
21 import android.opengl.Matrix;
22 import android.os.Build;
23 import android.util.Log;
24 import android.util.Slog;
25 
26 import androidx.annotation.Nullable;
27 
28 import libcore.io.Streams;
29 
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.InputStreamReader;
33 import java.nio.ByteBuffer;
34 import java.nio.ByteOrder;
35 import java.nio.FloatBuffer;
36 import java.nio.IntBuffer;
37 
38 /**
39  * A helper class for simple OpenGL operations
40  */
41 public class GLHelper {
42 
43     private static final String TAG = "GLHelper";
44     private static final int SIZEOF_FLOAT = 4;
45 
46     /**
47      * Creates an OpenGL program that uses the provided shader sources
48      * and returns the id of the created program
49      *
50      * @param vertexShaderSource   The source for the vertex shader
51      * @param fragmentShaderSource The source for the fragment shader
52      * @return The id of the created program
53      */
createProgram(String vertexShaderSource, String fragmentShaderSource)54     public static int createProgram(String vertexShaderSource, String fragmentShaderSource) {
55         int vertexShader = compileShader(GLES30.GL_VERTEX_SHADER, vertexShaderSource);
56         int fragmentShader = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderSource);
57 
58         int programId = GLES30.glCreateProgram();
59         checkGlErrors("glCreateProgram");
60 
61         GLES30.glAttachShader(programId, vertexShader);
62         GLES30.glAttachShader(programId, fragmentShader);
63 
64         // glDeleteShader flags these shaders to be deleted, the shaders
65         // are not actually deleted until the program they are attached to are deleted
66         GLES30.glDeleteShader(vertexShader);
67         checkGlErrors("glDeleteShader");
68         GLES30.glDeleteShader(fragmentShader);
69         checkGlErrors("glDeleteShader");
70 
71         GLES30.glLinkProgram(programId);
72         checkGlErrors("glLinkProgram");
73 
74         return programId;
75     }
76 
77     /**
78      * Creates and binds a texture and returns the id of the created texture
79      *
80      * @param textureIdBuffer The IntBuffer that will contain the created texture id
81      * @param textureTarget   The texture target for the created texture
82      * @return The id of the created and bound texture
83      */
createAndBindTextureObject(IntBuffer textureIdBuffer, int textureTarget)84     public static int createAndBindTextureObject(IntBuffer textureIdBuffer, int textureTarget) {
85         GLES30.glGenTextures(1, textureIdBuffer);
86 
87         int textureId = textureIdBuffer.get(0);
88 
89         GLES30.glBindTexture(textureTarget, textureId);
90         checkGlErrors("glBindTexture");
91 
92         // We define the filters that will be applied to the textures if
93         // they get minified or magnified when they are sampled
94         GLES30.glTexParameterf(textureTarget, GLES30.GL_TEXTURE_MIN_FILTER,
95                 GLES30.GL_LINEAR);
96         GLES30.glTexParameterf(textureTarget, GLES30.GL_TEXTURE_MAG_FILTER,
97                 GLES30.GL_LINEAR);
98 
99         // Set the wrap parameters for if the edges of the texture do not fill the surface
100         GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_WRAP_S,
101                 GLES30.GL_CLAMP_TO_EDGE);
102         GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_WRAP_T,
103                 GLES30.GL_CLAMP_TO_EDGE);
104 
105         return textureId;
106     }
107 
108     /**
109      * Creates and binds a Framebuffer object
110      *
111      * @param frameBuffer the IntBuffer that will contain the created Framebuffer ID
112      * @return The id of the created and bound Framebuffer
113      */
createAndBindFramebuffer(IntBuffer frameBuffer)114     public static int createAndBindFramebuffer(IntBuffer frameBuffer) {
115         GLES30.glGenFramebuffers(1, frameBuffer);
116         checkGlErrors("glGenFramebuffers");
117 
118         int frameBufferId = frameBuffer.get(0);
119 
120         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferId);
121         checkGlErrors("glBindFramebuffer");
122 
123         return frameBufferId;
124     }
125 
126     /**
127      * Retrieves a string of an OpenGL shader
128      *
129      * @param id the ID of the raw shader resource
130      * @return The shader script, null if the shader failed to load
131      */
getShaderFromRaw(Context context, int id)132     public static @Nullable String getShaderFromRaw(Context context, int id) {
133         try {
134             InputStream stream = context.getResources().openRawResource(id);
135             return new String(Streams.readFully(new InputStreamReader(stream)));
136         } catch (IOException e) {
137             Log.e(TAG, "Failed to load shader");
138             return null;
139         }
140     }
141 
142     /**
143      * Creates a FloatBuffer to hold texture and vertex coordinates
144      *
145      * @param coords The coordinates that will be held in the FloatBuffer
146      * @return a FloatBuffer containing the provided coordinates
147      */
createFloatBuffer(float[] coords)148     public static FloatBuffer createFloatBuffer(float[] coords) {
149         ByteBuffer byteBuffer = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT);
150         byteBuffer.order(ByteOrder.nativeOrder());
151 
152         FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
153         floatBuffer.put(coords);
154         floatBuffer.position(0);
155         return floatBuffer;
156     }
157 
158     /**
159      * @return a float[] representing a 4x4 identity matrix
160      */
getIdentityMatrix()161     public static float[] getIdentityMatrix() {
162         float[] identityMatrix = new float[16];
163         Matrix.setIdentityM(identityMatrix, 0);
164         return identityMatrix;
165     }
166 
167     /**
168      * Checks for GL errors, logging any errors found
169      *
170      * @param func The name of the most recent GL function called
171      * @return a boolean representing if there was a GL error or not
172      */
checkGlErrors(String func)173     public static boolean checkGlErrors(String func) {
174         boolean hadError = false;
175         int error;
176 
177         while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {
178             if (Build.IS_ENG || Build.IS_USERDEBUG) {
179                 Slog.e(TAG, func + " failed: error " + error, new Throwable());
180             }
181             hadError = true;
182         }
183         return hadError;
184     }
185 
compileShader(int shaderType, String shaderSource)186     private static int compileShader(int shaderType, String shaderSource) {
187         int shader = GLES30.glCreateShader(shaderType);
188         GLES30.glShaderSource(shader, shaderSource);
189 
190         GLES30.glCompileShader(shader);
191         checkGlErrors("glCompileShader");
192 
193         return shader;
194     }
195 }
196