• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 // #define LOG_NDEBUG 0
18 #define LOG_TAG "EglProgram"
19 #include "EglProgram.h"
20 
21 #include <array>
22 #include <complex>
23 
24 #include "EglUtil.h"
25 #include "GLES/gl.h"
26 #include "GLES2/gl2.h"
27 #include "GLES2/gl2ext.h"
28 #include "log/log.h"
29 
30 namespace android {
31 namespace companion {
32 namespace virtualcamera {
33 
34 namespace {
35 
36 constexpr char kGlExtYuvTarget[] = "GL_EXT_YUV_target";
37 
38 constexpr char kJuliaFractalVertexShader[] = R"(#version 300 es
39     in vec4 aPosition;
40     in vec2 aTextureCoord;
41     out vec2 vFractalCoord;
42     out vec2 vUVCoord;
43     void main() {
44       gl_Position = aPosition;
45       vUVCoord = aTextureCoord;
46       vFractalCoord = vec2(aTextureCoord.x - 0.5, aTextureCoord.y - 0.5) * 4.0;
47     })";
48 
49 constexpr char kJuliaFractalFragmentShader[] = R"(#version 300 es
50     #extension GL_EXT_YUV_target : require
51     precision mediump float;
52 
53     const float kIter = 64.0;
54 
55     in vec2 vFractalCoord;
56     in vec2 vUVCoord;
57     out vec4 fragColor;
58     uniform vec2 uC;
59 
60     vec2 imSq(vec2 n){
61       return vec2(pow(n.x,2.0)-pow(n.y,2.0), 2.0*n.x*n.y);
62     }
63 
64     float julia(vec2 n, vec2 c) {
65       vec2 z = n;
66       for (float i=0.0;i<kIter; i+=1.0) {
67         z = imSq(z) + c;
68         if (length(z) > 2.0) return i/kIter;
69       }
70       return kIter;
71     }
72 
73     void main() {
74       float juliaVal = julia(vFractalCoord, uC);
75       fragColor = vec4(yuv_2_rgb(vec3(juliaVal, vUVCoord.x, vUVCoord.y), itu_601_full_range), 0.0);
76     })";
77 
78 constexpr char kExternalTextureVertexShader[] = R"(#version 300 es
79   uniform mat4 aTextureTransformMatrix; // Transform matrix given by surface texture.
80   in vec4 aPosition;
81   in vec2 aTextureCoord;
82   out vec2 vTextureCoord;
83   void main() {
84     gl_Position = aPosition;
85     vTextureCoord = (aTextureTransformMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy;
86   })";
87 
88 constexpr char kExternalYuvTextureFragmentShader[] = R"(#version 300 es
89     #extension GL_OES_EGL_image_external_essl3 : require
90     #extension GL_EXT_YUV_target : require
91     precision mediump float;
92     in vec2 vTextureCoord;
93     layout (yuv) out vec4 fragColor;
94     uniform __samplerExternal2DY2YEXT uTexture;
95     void main() {
96       fragColor = texture(uTexture, vTextureCoord);
97     })";
98 
99 constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
100     #extension GL_OES_EGL_image_external_essl3 : require
101     #extension GL_EXT_YUV_target : require
102     precision mediump float;
103     in vec2 vTextureCoord;
104     layout (yuv) out vec4 fragColor;
105     uniform samplerExternalOES uTexture;
106     void main() {
107       vec4 rgbaColor = texture(uTexture, vTextureCoord);
108       fragColor = vec4(rgb_2_yuv(rgbaColor.xyz, itu_601_full_range), 0.0);
109     })";
110 
111 constexpr int kCoordsPerVertex = 3;
112 
113 constexpr std::array<float, 12> kSquareCoords{
114     -1.f, -1.0f, 0.0f,   // top left
115     -1.f, 1.f,   0.0f,   // bottom left
116     1.0f, 1.f,   0.0f,   // bottom right
117     1.0f, -1.0f, 0.0f};  // top right
118 
119 constexpr std::array<float, 8> kTextureCoords{0.0f, 1.0f,   // top left
120                                               0.0f, 0.0f,   // bottom left
121                                               1.0f, 0.0f,   // bottom right
122                                               1.0f, 1.0f};  // top right
123 
124 constexpr std::array<uint8_t, 6> kDrawOrder{0, 1, 2, 0, 2, 3};
125 
compileShader(GLenum shaderType,const char * src)126 GLuint compileShader(GLenum shaderType, const char* src) {
127   GLuint shader = glCreateShader(shaderType);
128   if (shader == 0) {
129     ALOGE("glCreateShader(shaderType=%x) error: %#x",
130           static_cast<unsigned int>(shaderType), glGetError());
131     return 0;
132   }
133 
134   glShaderSource(shader, 1, &src, NULL);
135   glCompileShader(shader);
136 
137   GLint compiled = 0;
138   glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
139   if (!compiled) {
140     ALOGE("Compile of shader type %d failed", shaderType);
141     GLint infoLen = 0;
142     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
143     if (infoLen) {
144       char* buf = new char[infoLen];
145       if (buf) {
146         glGetShaderInfoLog(shader, infoLen, NULL, buf);
147         ALOGE("Compile log: %s", buf);
148         delete[] buf;
149       }
150     }
151     glDeleteShader(shader);
152     return 0;
153   }
154   return shader;
155 }
156 
157 }  // namespace
158 
~EglProgram()159 EglProgram::~EglProgram() {
160   if (mProgram) {
161     glDeleteProgram(mProgram);
162   }
163 }
164 
initialize(const char * vertexShaderSrc,const char * fragmentShaderSrc)165 bool EglProgram::initialize(const char* vertexShaderSrc,
166                             const char* fragmentShaderSrc) {
167   GLuint vertexShaderId = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
168   if (checkEglError("compileShader(vertex)")) {
169     return false;
170   }
171   GLuint fragmentShaderId = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
172   if (checkEglError("compileShader(fragment)")) {
173     return false;
174   }
175 
176   GLuint programId = glCreateProgram();
177 
178   glAttachShader(programId, vertexShaderId);
179   glAttachShader(programId, fragmentShaderId);
180   glLinkProgram(programId);
181 
182   GLint linkStatus = GL_FALSE;
183   glGetProgramiv(programId, GL_LINK_STATUS, &linkStatus);
184   if (linkStatus != GL_TRUE) {
185     ALOGE("glLinkProgram failed");
186     GLint bufLength = 0;
187     glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &bufLength);
188     if (bufLength) {
189       char* buf = new char[bufLength];
190       if (buf) {
191         glGetProgramInfoLog(programId, bufLength, NULL, buf);
192         ALOGE("Link log: %s", buf);
193         delete[] buf;
194       }
195     }
196     glDeleteProgram(programId);
197     return false;
198   }
199 
200   mProgram = programId;
201 
202   mIsInitialized = true;
203   return mIsInitialized;
204 }
205 
isInitialized() const206 bool EglProgram::isInitialized() const {
207   return mIsInitialized;
208 }
209 
EglTestPatternProgram()210 EglTestPatternProgram::EglTestPatternProgram() {
211   if (initialize(kJuliaFractalVertexShader, kJuliaFractalFragmentShader)) {
212     ALOGV("Successfully initialized EGL shaders for test pattern program.");
213   } else {
214     ALOGE("Test pattern EGL shader program initialization failed.");
215   }
216 
217   mCHandle = glGetUniformLocation(mProgram, "uC");
218   mPositionHandle = glGetAttribLocation(mProgram, "aPosition");
219   mTextureCoordHandle = glGetAttribLocation(mProgram, "aTextureCoord");
220 
221   // Pass vertex array to draw.
222   glEnableVertexAttribArray(mPositionHandle);
223   // Prepare the triangle coordinate data.
224   glVertexAttribPointer(mPositionHandle, kCoordsPerVertex, GL_FLOAT, false,
225                         kSquareCoords.size(), kSquareCoords.data());
226 
227   glEnableVertexAttribArray(mTextureCoordHandle);
228   glVertexAttribPointer(mTextureCoordHandle, 2, GL_FLOAT, false,
229                         kTextureCoords.size(), kTextureCoords.data());
230 }
231 
~EglTestPatternProgram()232 EglTestPatternProgram::~EglTestPatternProgram() {
233   if (mPositionHandle != -1) {
234     glDisableVertexAttribArray(mPositionHandle);
235   }
236   if (mTextureCoordHandle != -1) {
237     glDisableVertexAttribArray(mTextureCoordHandle);
238   }
239 }
240 
draw(const std::chrono::nanoseconds timestamp)241 bool EglTestPatternProgram::draw(const std::chrono::nanoseconds timestamp) {
242   // Load compiled shader.
243   glUseProgram(mProgram);
244   checkEglError("glUseProgram");
245 
246   float time = float(timestamp.count() / 1e9) / 10;
247   const std::complex<float> c(std::sin(time) * 0.78f, std::cos(time) * 0.78f);
248 
249   // Pass "C" constant value determining the Julia set to the shader.
250   glUniform2f(mCHandle, c.imag(), c.real());
251 
252   // Draw triangle strip forming a square filling the viewport.
253   glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
254                  kDrawOrder.data());
255   if (checkEglError("glDrawElements")) {
256     return false;
257   }
258 
259   return true;
260 }
261 
EglTextureProgram(const TextureFormat textureFormat)262 EglTextureProgram::EglTextureProgram(const TextureFormat textureFormat) {
263   if (!isGlExtensionSupported(kGlExtYuvTarget)) {
264     ALOGE(
265         "Cannot initialize external texture program due to missing "
266         "GL_EXT_YUV_target extension");
267     return;
268   }
269 
270   const char* fragmentShaderSrc = textureFormat == TextureFormat::YUV
271                                       ? kExternalYuvTextureFragmentShader
272                                       : kExternalRgbaTextureFragmentShader;
273   if (initialize(kExternalTextureVertexShader, fragmentShaderSrc)) {
274     ALOGV("Successfully initialized EGL shaders for external texture program.");
275   } else {
276     ALOGE("External texture EGL shader program initialization failed.");
277   }
278 
279   // Lookup and cache handles to uniforms & attributes.
280   mPositionHandle = glGetAttribLocation(mProgram, "aPosition");
281   mTextureCoordHandle = glGetAttribLocation(mProgram, "aTextureCoord");
282   mTransformMatrixHandle =
283       glGetUniformLocation(mProgram, "aTextureTransformMatrix");
284   mTextureHandle = glGetUniformLocation(mProgram, "uTexture");
285 
286   // Pass vertex array to the shader.
287   glEnableVertexAttribArray(mPositionHandle);
288   glVertexAttribPointer(mPositionHandle, kCoordsPerVertex, GL_FLOAT, false,
289                         kSquareCoords.size(), kSquareCoords.data());
290 
291   // Pass texture coordinates corresponding to vertex array to the shader.
292   glEnableVertexAttribArray(mTextureCoordHandle);
293   glVertexAttribPointer(mTextureCoordHandle, 2, GL_FLOAT, false,
294                         kTextureCoords.size(), kTextureCoords.data());
295 }
296 
~EglTextureProgram()297 EglTextureProgram::~EglTextureProgram() {
298   if (mPositionHandle != -1) {
299     glDisableVertexAttribArray(mPositionHandle);
300   }
301   if (mTextureCoordHandle != -1) {
302     glDisableVertexAttribArray(mTextureCoordHandle);
303   }
304 }
305 
draw(GLuint textureId,const std::array<float,16> & transformMatrix)306 bool EglTextureProgram::draw(GLuint textureId,
307                              const std::array<float, 16>& transformMatrix) {
308   // Load compiled shader.
309   glUseProgram(mProgram);
310   if (checkEglError("glUseProgram")) {
311     return false;
312   }
313 
314   // Pass transformation matrix for the texture coordinates.
315   glUniformMatrix4fv(mTransformMatrixHandle, 1, /*transpose=*/GL_FALSE,
316                      transformMatrix.data());
317 
318   // Configure texture for the shader.
319   glActiveTexture(GL_TEXTURE0);
320   glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
321   glUniform1i(mTextureHandle, 0);
322 
323   // Draw triangle strip forming a square filling the viewport.
324   glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
325                  kDrawOrder.data());
326   if (checkEglError("glDrawElements")) {
327     return false;
328   }
329 
330   return true;
331 }
332 
333 }  // namespace virtualcamera
334 }  // namespace companion
335 }  // namespace android
336