xref: /aosp_15_r20/frameworks/av/services/camera/virtualcamera/util/EglProgram.cc (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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 // Shader to render a RGBA texture into a YUV buffer.
100 constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
101     #extension GL_OES_EGL_image_external_essl3 : require
102     #extension GL_EXT_YUV_target : require
103     precision mediump float;
104     in vec2 vTextureCoord;
105     layout (yuv) out vec4 fragColor;
106     uniform samplerExternalOES uTexture;
107     void main() {
108       vec4 rgbaColor = texture(uTexture, vTextureCoord);
109       fragColor = vec4(rgb_2_yuv(rgbaColor.xyz, itu_601_full_range), 0.0);
110     })";
111 
112 constexpr int kCoordsPerVertex = 3;
113 
114 constexpr std::array<float, 12> kSquareCoords{
115     -1.f, -1.0f, 0.0f,   // top left
116     -1.f, 1.f,   0.0f,   // bottom left
117     1.0f, 1.f,   0.0f,   // bottom right
118     1.0f, -1.0f, 0.0f};  // top right
119 
120 constexpr std::array<float, 8> kTextureCoords{0.0f, 1.0f,   // top left
121                                               0.0f, 0.0f,   // bottom left
122                                               1.0f, 0.0f,   // bottom right
123                                               1.0f, 1.0f};  // top right
124 
125 constexpr std::array<uint8_t, 6> kDrawOrder{0, 1, 2, 0, 2, 3};
126 
compileShader(GLenum shaderType,const char * src)127 GLuint compileShader(GLenum shaderType, const char* src) {
128   GLuint shader = glCreateShader(shaderType);
129   if (shader == 0) {
130     ALOGE("glCreateShader(shaderType=%x) error: %#x",
131           static_cast<unsigned int>(shaderType), glGetError());
132     return 0;
133   }
134 
135   glShaderSource(shader, 1, &src, NULL);
136   glCompileShader(shader);
137 
138   GLint compiled = 0;
139   glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
140   if (!compiled) {
141     ALOGE("Compile of shader type %d failed", shaderType);
142     GLint infoLen = 0;
143     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
144     if (infoLen) {
145       char* buf = new char[infoLen];
146       if (buf) {
147         glGetShaderInfoLog(shader, infoLen, NULL, buf);
148         ALOGE("Compile log: %s", buf);
149         delete[] buf;
150       }
151     }
152     glDeleteShader(shader);
153     return 0;
154   }
155   return shader;
156 }
157 
158 }  // namespace
159 
~EglProgram()160 EglProgram::~EglProgram() {
161   if (mProgram) {
162     glDeleteProgram(mProgram);
163   }
164 }
165 
initialize(const char * vertexShaderSrc,const char * fragmentShaderSrc)166 bool EglProgram::initialize(const char* vertexShaderSrc,
167                             const char* fragmentShaderSrc) {
168   GLuint vertexShaderId = compileShader(GL_VERTEX_SHADER, vertexShaderSrc);
169   if (checkEglError("compileShader(vertex)")) {
170     return false;
171   }
172   GLuint fragmentShaderId = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSrc);
173   if (checkEglError("compileShader(fragment)")) {
174     return false;
175   }
176 
177   GLuint programId = glCreateProgram();
178 
179   glAttachShader(programId, vertexShaderId);
180   glAttachShader(programId, fragmentShaderId);
181   glLinkProgram(programId);
182 
183   GLint linkStatus = GL_FALSE;
184   glGetProgramiv(programId, GL_LINK_STATUS, &linkStatus);
185   if (linkStatus != GL_TRUE) {
186     ALOGE("glLinkProgram failed");
187     GLint bufLength = 0;
188     glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &bufLength);
189     if (bufLength) {
190       char* buf = new char[bufLength];
191       if (buf) {
192         glGetProgramInfoLog(programId, bufLength, NULL, buf);
193         ALOGE("Link log: %s", buf);
194         delete[] buf;
195       }
196     }
197     glDeleteProgram(programId);
198     return false;
199   }
200 
201   mProgram = programId;
202 
203   mIsInitialized = true;
204   return mIsInitialized;
205 }
206 
isInitialized() const207 bool EglProgram::isInitialized() const {
208   return mIsInitialized;
209 }
210 
EglTestPatternProgram()211 EglTestPatternProgram::EglTestPatternProgram() {
212   if (initialize(kJuliaFractalVertexShader, kJuliaFractalFragmentShader)) {
213     ALOGV("Successfully initialized EGL shaders for test pattern program.");
214   } else {
215     ALOGE("Test pattern EGL shader program initialization failed.");
216   }
217 
218   mCHandle = glGetUniformLocation(mProgram, "uC");
219   mPositionHandle = glGetAttribLocation(mProgram, "aPosition");
220   mTextureCoordHandle = glGetAttribLocation(mProgram, "aTextureCoord");
221 
222   // Pass vertex array to draw.
223   glEnableVertexAttribArray(mPositionHandle);
224   // Prepare the triangle coordinate data.
225   glVertexAttribPointer(mPositionHandle, kCoordsPerVertex, GL_FLOAT, false,
226                         kSquareCoords.size(), kSquareCoords.data());
227 
228   glEnableVertexAttribArray(mTextureCoordHandle);
229   glVertexAttribPointer(mTextureCoordHandle, 2, GL_FLOAT, false,
230                         kTextureCoords.size(), kTextureCoords.data());
231 }
232 
~EglTestPatternProgram()233 EglTestPatternProgram::~EglTestPatternProgram() {
234   if (mPositionHandle != -1) {
235     glDisableVertexAttribArray(mPositionHandle);
236   }
237   if (mTextureCoordHandle != -1) {
238     glDisableVertexAttribArray(mTextureCoordHandle);
239   }
240 }
241 
draw(const std::chrono::nanoseconds timestamp)242 bool EglTestPatternProgram::draw(const std::chrono::nanoseconds timestamp) {
243   // Load compiled shader.
244   glUseProgram(mProgram);
245   checkEglError("glUseProgram");
246 
247   float time = float(timestamp.count() / 1e9) / 10;
248   const std::complex<float> c(std::sin(time) * 0.78f, std::cos(time) * 0.78f);
249 
250   // Pass "C" constant value determining the Julia set to the shader.
251   glUniform2f(mCHandle, c.imag(), c.real());
252 
253   // Draw triangle strip forming a square filling the viewport.
254   glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
255                  kDrawOrder.data());
256   if (checkEglError("glDrawElements")) {
257     return false;
258   }
259 
260   return true;
261 }
262 
EglTextureProgram(const TextureFormat textureFormat)263 EglTextureProgram::EglTextureProgram(const TextureFormat textureFormat) {
264   if (!isGlExtensionSupported(kGlExtYuvTarget)) {
265     ALOGE(
266         "Cannot initialize external texture program due to missing "
267         "GL_EXT_YUV_target extension");
268     return;
269   }
270 
271   const char* fragmentShaderSrc = textureFormat == TextureFormat::YUV
272                                       ? kExternalYuvTextureFragmentShader
273                                       : kExternalRgbaTextureFragmentShader;
274   if (initialize(kExternalTextureVertexShader, fragmentShaderSrc)) {
275     ALOGV("Successfully initialized EGL shaders for external texture program.");
276   } else {
277     ALOGE("External texture EGL shader program initialization failed.");
278   }
279 
280   // Lookup and cache handles to uniforms & attributes.
281   mPositionHandle = glGetAttribLocation(mProgram, "aPosition");
282   mTextureCoordHandle = glGetAttribLocation(mProgram, "aTextureCoord");
283   mTransformMatrixHandle =
284       glGetUniformLocation(mProgram, "aTextureTransformMatrix");
285   mTextureHandle = glGetUniformLocation(mProgram, "uTexture");
286 
287   // Pass vertex array to the shader.
288   glEnableVertexAttribArray(mPositionHandle);
289   glVertexAttribPointer(mPositionHandle, kCoordsPerVertex, GL_FLOAT, false,
290                         kSquareCoords.size(), kSquareCoords.data());
291 
292   // Pass texture coordinates corresponding to vertex array to the shader.
293   glEnableVertexAttribArray(mTextureCoordHandle);
294   glVertexAttribPointer(mTextureCoordHandle, 2, GL_FLOAT, false,
295                         kTextureCoords.size(), kTextureCoords.data());
296 }
297 
~EglTextureProgram()298 EglTextureProgram::~EglTextureProgram() {
299   if (mPositionHandle != -1) {
300     glDisableVertexAttribArray(mPositionHandle);
301   }
302   if (mTextureCoordHandle != -1) {
303     glDisableVertexAttribArray(mTextureCoordHandle);
304   }
305 }
306 
draw(GLuint textureId,const std::array<float,16> & transformMatrix)307 bool EglTextureProgram::draw(GLuint textureId,
308                              const std::array<float, 16>& transformMatrix) {
309   // Load compiled shader.
310   glUseProgram(mProgram);
311   if (checkEglError("glUseProgram")) {
312     return false;
313   }
314 
315   // Pass transformation matrix for the texture coordinates.
316   glUniformMatrix4fv(mTransformMatrixHandle, 1, /*transpose=*/GL_FALSE,
317                      transformMatrix.data());
318 
319   // Configure texture for the shader.
320   glActiveTexture(GL_TEXTURE0);
321   glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
322   glUniform1i(mTextureHandle, 0);
323 
324   // Draw triangle strip forming a square filling the viewport.
325   glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
326                  kDrawOrder.data());
327   if (checkEglError("glDrawElements")) {
328     return false;
329   }
330 
331   return true;
332 }
333 
334 }  // namespace virtualcamera
335 }  // namespace companion
336 }  // namespace android
337