blob: 9c97118d5805f3bcf5909e4cf2c8db4403b30fe1 [file] [log] [blame]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01001/*
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
30namespace android {
31namespace companion {
32namespace virtualcamera {
33
34namespace {
35
36constexpr char kGlExtYuvTarget[] = "GL_EXT_YUV_target";
37
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020038constexpr char kJuliaFractalVertexShader[] = R"(#version 300 es
39 in vec4 aPosition;
40 in vec2 aTextureCoord;
41 out vec2 vFractalCoord;
42 out vec2 vUVCoord;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010043 void main() {
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020044 gl_Position = aPosition;
45 vUVCoord = aTextureCoord;
46 vFractalCoord = vec2(aTextureCoord.x - 0.5, aTextureCoord.y - 0.5) * 4.0;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010047 })";
48
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020049constexpr char kJuliaFractalFragmentShader[] = R"(#version 300 es
50 #extension GL_EXT_YUV_target : require
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010051 precision mediump float;
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020052
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010053 const float kIter = 64.0;
54
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020055 in vec2 vFractalCoord;
56 in vec2 vUVCoord;
57 out vec4 fragColor;
58 uniform vec2 uC;
59
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010060 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() {
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020074 float juliaVal = julia(vFractalCoord, uC);
75 fragColor = vec4(yuv_2_rgb(vec3(juliaVal, vUVCoord.x, vUVCoord.y), itu_601_full_range), 0.0);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010076 })";
77
78constexpr char kExternalTextureVertexShader[] = R"(#version 300 es
Jan Sebechlebsky99492e32023-12-20 09:49:45 +010079 uniform mat4 aTextureTransformMatrix; // Transform matrix given by surface texture.
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010080 in vec4 aPosition;
81 in vec2 aTextureCoord;
82 out vec2 vTextureCoord;
83 void main() {
84 gl_Position = aPosition;
Jan Sebechlebsky99492e32023-12-20 09:49:45 +010085 vTextureCoord = (aTextureTransformMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010086 })";
87
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +010088constexpr char kExternalYuvTextureFragmentShader[] = R"(#version 300 es
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010089 #extension GL_OES_EGL_image_external_essl3 : require
90 #extension GL_EXT_YUV_target : require
91 precision mediump float;
92 in vec2 vTextureCoord;
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +010093 layout (yuv) out vec4 fragColor;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010094 uniform __samplerExternal2DY2YEXT uTexture;
95 void main() {
96 fragColor = texture(uTexture, vTextureCoord);
97 })";
98
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +010099constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
Jan Sebechlebskyc06aeb22024-05-28 13:15:12 +0200100 #extension GL_OES_EGL_image_external_essl3 : require
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100101 #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
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100111constexpr int kCoordsPerVertex = 3;
Jan Sebechlebsky99492e32023-12-20 09:49:45 +0100112
113constexpr 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
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100118
119constexpr 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
124constexpr std::array<uint8_t, 6> kDrawOrder{0, 1, 2, 0, 2, 3};
125
126GLuint 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
159EglProgram::~EglProgram() {
160 if (mProgram) {
161 glDeleteProgram(mProgram);
162 }
163}
164
165bool 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
206bool EglProgram::isInitialized() const {
207 return mIsInitialized;
208}
209
210EglTestPatternProgram::EglTestPatternProgram() {
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200211 if (initialize(kJuliaFractalVertexShader, kJuliaFractalFragmentShader)) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100212 ALOGV("Successfully initialized EGL shaders for test pattern program.");
213 } else {
214 ALOGE("Test pattern EGL shader program initialization failed.");
215 }
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200216
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());
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100230}
231
Jan Sebechlebsky58a7ac42024-06-03 16:41:03 +0200232bool EglTestPatternProgram::draw(const std::chrono::nanoseconds timestamp) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100233 // Load compiled shader.
234 glUseProgram(mProgram);
235 checkEglError("glUseProgram");
236
Jan Sebechlebsky58a7ac42024-06-03 16:41:03 +0200237 float time = float(timestamp.count() / 1e9) / 10;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100238 const std::complex<float> c(std::sin(time) * 0.78f, std::cos(time) * 0.78f);
239
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100240 // Pass "C" constant value determining the Julia set to the shader.
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200241 glUniform2f(mCHandle, c.imag(), c.real());
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100242
243 // Draw triangle strip forming a square filling the viewport.
244 glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
245 kDrawOrder.data());
246 if (checkEglError("glDrawElements")) {
247 return false;
248 }
249
250 return true;
251}
252
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100253EglTextureProgram::EglTextureProgram(const TextureFormat textureFormat) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100254 if (!isGlExtensionSupported(kGlExtYuvTarget)) {
255 ALOGE(
256 "Cannot initialize external texture program due to missing "
257 "GL_EXT_YUV_target extension");
258 return;
259 }
260
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100261 const char* fragmentShaderSrc = textureFormat == TextureFormat::YUV
262 ? kExternalYuvTextureFragmentShader
263 : kExternalRgbaTextureFragmentShader;
264 if (initialize(kExternalTextureVertexShader, fragmentShaderSrc)) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100265 ALOGV("Successfully initialized EGL shaders for external texture program.");
266 } else {
267 ALOGE("External texture EGL shader program initialization failed.");
268 }
Jan Sebechlebskyca2d7972023-12-22 13:02:51 +0100269
270 // Lookup and cache handles to uniforms & attributes.
271 mPositionHandle = glGetAttribLocation(mProgram, "aPosition");
272 mTextureCoordHandle = glGetAttribLocation(mProgram, "aTextureCoord");
273 mTransformMatrixHandle =
274 glGetUniformLocation(mProgram, "aTextureTransformMatrix");
275 mTextureHandle = glGetUniformLocation(mProgram, "uTexture");
276
277 // Pass vertex array to the shader.
278 glEnableVertexAttribArray(mPositionHandle);
279 glVertexAttribPointer(mPositionHandle, kCoordsPerVertex, GL_FLOAT, false,
280 kSquareCoords.size(), kSquareCoords.data());
281
282 // Pass texture coordinates corresponding to vertex array to the shader.
283 glEnableVertexAttribArray(mTextureCoordHandle);
284 glVertexAttribPointer(mTextureCoordHandle, 2, GL_FLOAT, false,
285 kTextureCoords.size(), kTextureCoords.data());
286}
287
288EglTextureProgram::~EglTextureProgram() {
289 if (mPositionHandle != -1) {
290 glDisableVertexAttribArray(mPositionHandle);
291 }
292 if (mTextureCoordHandle != -1) {
293 glDisableVertexAttribArray(mTextureCoordHandle);
294 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100295}
296
Jan Sebechlebsky99492e32023-12-20 09:49:45 +0100297bool EglTextureProgram::draw(GLuint textureId,
298 const std::array<float, 16>& transformMatrix) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100299 // Load compiled shader.
300 glUseProgram(mProgram);
301 if (checkEglError("glUseProgram")) {
302 return false;
303 }
304
Jan Sebechlebsky99492e32023-12-20 09:49:45 +0100305 // Pass transformation matrix for the texture coordinates.
Jan Sebechlebsky7a309b22023-12-27 15:25:57 +0100306 glUniformMatrix4fv(mTransformMatrixHandle, 1, /*transpose=*/GL_FALSE,
Jan Sebechlebsky99492e32023-12-20 09:49:45 +0100307 transformMatrix.data());
308
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100309 // Configure texture for the shader.
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100310 glActiveTexture(GL_TEXTURE0);
311 glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId);
Jan Sebechlebskyca2d7972023-12-22 13:02:51 +0100312 glUniform1i(mTextureHandle, 0);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100313
314 // Draw triangle strip forming a square filling the viewport.
315 glDrawElements(GL_TRIANGLES, kDrawOrder.size(), GL_UNSIGNED_BYTE,
316 kDrawOrder.data());
317 if (checkEglError("glDrawElements")) {
318 return false;
319 }
320
321 return true;
322}
323
324} // namespace virtualcamera
325} // namespace companion
326} // namespace android