1 /*
2 * Copyright 2024 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 #include "ultrahdr/ultrahdrcommon.h"
18
19 namespace ultrahdr {
20
uhdr_opengl_ctxt()21 uhdr_opengl_ctxt::uhdr_opengl_ctxt() {
22 mEGLDisplay = EGL_NO_DISPLAY;
23 mEGLContext = EGL_NO_CONTEXT;
24 mEGLSurface = EGL_NO_SURFACE;
25 mEGLConfig = 0;
26 mQuadVAO = 0;
27 mQuadVBO = 0;
28 mQuadEBO = 0;
29 mErrorStatus = g_no_error;
30 mDecodedImgTexture = 0;
31 mGainmapImgTexture = 0;
32 for (int i = 0; i < UHDR_RESIZE + 1; i++) {
33 mShaderProgram[i] = 0;
34 }
35 }
36
~uhdr_opengl_ctxt()37 uhdr_opengl_ctxt::~uhdr_opengl_ctxt() { delete_opengl_ctxt(); }
38
init_opengl_ctxt()39 void uhdr_opengl_ctxt::init_opengl_ctxt() {
40 #define RET_IF_TRUE(cond, msg) \
41 { \
42 if (cond) { \
43 mErrorStatus.error_code = UHDR_CODEC_ERROR; \
44 mErrorStatus.has_detail = 1; \
45 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail, \
46 "%s, received egl error code 0x%x", msg, eglGetError()); \
47 return; \
48 } \
49 }
50
51 mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
52 RET_IF_TRUE(mEGLDisplay == EGL_NO_DISPLAY, "eglGetDisplay() failed")
53
54 RET_IF_TRUE(!eglInitialize(mEGLDisplay, NULL, NULL), "eglInitialize() failed")
55
56 EGLint num_config;
57 EGLint attribs[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
58 EGL_NONE};
59 RET_IF_TRUE(!eglChooseConfig(mEGLDisplay, attribs, &mEGLConfig, 1, &num_config) || num_config < 1,
60 "eglChooseConfig() failed")
61
62 EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
63 mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, EGL_NO_CONTEXT, context_attribs);
64 RET_IF_TRUE(mEGLContext == EGL_NO_CONTEXT, "eglCreateContext() failed")
65
66 EGLint pbuffer_attribs[] = {
67 EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE,
68 };
69 mEGLSurface = eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, pbuffer_attribs);
70 RET_IF_TRUE(mEGLSurface == EGL_NO_SURFACE, "eglCreatePbufferSurface() failed")
71
72 RET_IF_TRUE(!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext),
73 "eglMakeCurrent() failed")
74 #undef RET_IF_TRUE
75
76 setup_quad();
77 }
78
compile_shader(GLenum type,const char * source)79 GLuint uhdr_opengl_ctxt::compile_shader(GLenum type, const char* source) {
80 GLuint shader = glCreateShader(type);
81 if (!shader) {
82 mErrorStatus.error_code = UHDR_CODEC_ERROR;
83 mErrorStatus.has_detail = 1;
84 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
85 "glCreateShader() failed, received gl error code 0x%x", glGetError());
86 return 0;
87 }
88 glShaderSource(shader, 1, &source, nullptr);
89 glCompileShader(shader);
90 GLint compileStatus;
91 glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
92 if (compileStatus != GL_TRUE) {
93 GLint logLength;
94 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
95 // Info log length includes the null terminator, so 1 means that the info log is an empty
96 // string.
97 if (logLength > 1) {
98 std::vector<char> log(logLength);
99 glGetShaderInfoLog(shader, logLength, nullptr, log.data());
100 mErrorStatus.error_code = UHDR_CODEC_ERROR;
101 mErrorStatus.has_detail = 1;
102 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
103 "Unable to compile shader, error log: %s", log.data());
104 } else {
105 mErrorStatus.error_code = UHDR_CODEC_ERROR;
106 mErrorStatus.has_detail = 1;
107 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
108 "Unable to compile shader, <empty log message>");
109 }
110 glDeleteShader(shader);
111 return 0;
112 }
113 return shader;
114 }
115
create_shader_program(const char * vertex_source,const char * fragment_source)116 GLuint uhdr_opengl_ctxt::create_shader_program(const char* vertex_source,
117 const char* fragment_source) {
118 if (vertex_source == nullptr || *vertex_source == '\0') {
119 mErrorStatus.error_code = UHDR_CODEC_INVALID_PARAM;
120 mErrorStatus.has_detail = 1;
121 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail, "empty vertex source shader");
122 return 0;
123 }
124
125 if (fragment_source == nullptr || *fragment_source == '\0') {
126 mErrorStatus.error_code = UHDR_CODEC_INVALID_PARAM;
127 mErrorStatus.has_detail = 1;
128 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail, "empty fragment source shader");
129 return 0;
130 }
131
132 GLuint program = glCreateProgram();
133 if (!program) {
134 mErrorStatus.error_code = UHDR_CODEC_ERROR;
135 mErrorStatus.has_detail = 1;
136 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
137 "glCreateProgram() failed, received gl error code 0x%x", glGetError());
138 return 0;
139 }
140
141 GLuint vertexShader = compile_shader(GL_VERTEX_SHADER, vertex_source);
142 GLuint fragmentShader = compile_shader(GL_FRAGMENT_SHADER, fragment_source);
143 if (vertexShader == 0 || fragmentShader == 0) {
144 glDeleteShader(vertexShader);
145 glDeleteShader(fragmentShader);
146 glDeleteProgram(program);
147 return 0;
148 }
149
150 glAttachShader(program, vertexShader);
151 glDeleteShader(vertexShader);
152
153 glAttachShader(program, fragmentShader);
154 glDeleteShader(fragmentShader);
155
156 glLinkProgram(program);
157 GLint linkStatus;
158 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
159 if (linkStatus != GL_TRUE) {
160 GLint logLength;
161 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
162 // Info log length includes the null terminator, so 1 means that the info log is an empty
163 // string.
164 if (logLength > 1) {
165 std::vector<char> log(logLength);
166 glGetProgramInfoLog(program, logLength, nullptr, log.data());
167 mErrorStatus.error_code = UHDR_CODEC_ERROR;
168 mErrorStatus.has_detail = 1;
169 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
170 "Unable to link shader program, error log: %s", log.data());
171 } else {
172 mErrorStatus.error_code = UHDR_CODEC_ERROR;
173 mErrorStatus.has_detail = 1;
174 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
175 "Unable to link shader program, <empty log message>");
176 }
177 glDeleteProgram(program);
178 return 0;
179 }
180 return program;
181 }
182
create_texture(uhdr_img_fmt_t fmt,int w,int h,void * data)183 GLuint uhdr_opengl_ctxt::create_texture(uhdr_img_fmt_t fmt, int w, int h, void* data) {
184 GLuint textureID;
185
186 glGenTextures(1, &textureID);
187 glBindTexture(GL_TEXTURE_2D, textureID);
188 switch (fmt) {
189 case UHDR_IMG_FMT_12bppYCbCr420:
190 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h * 3 / 2, 0, GL_RED, GL_UNSIGNED_BYTE, data);
191 break;
192 case UHDR_IMG_FMT_8bppYCbCr400:
193 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
194 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
195 glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
196 break;
197 case UHDR_IMG_FMT_32bppRGBA8888:
198 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
199 break;
200 case UHDR_IMG_FMT_64bppRGBAHalfFloat:
201 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_HALF_FLOAT, data);
202 break;
203 case UHDR_IMG_FMT_32bppRGBA1010102:
204 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, w, h, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV,
205 data);
206 break;
207 case UHDR_IMG_FMT_24bppRGB888:
208 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
209 break;
210 case UHDR_IMG_FMT_24bppYCbCr444:
211 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h * 3, 0, GL_RED, GL_UNSIGNED_BYTE, data);
212 break;
213 case UHDR_IMG_FMT_16bppYCbCr422:
214 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h * 2, 0, GL_RED, GL_UNSIGNED_BYTE, data);
215 break;
216 case UHDR_IMG_FMT_16bppYCbCr440:
217 [[fallthrough]];
218 case UHDR_IMG_FMT_12bppYCbCr411:
219 [[fallthrough]];
220 case UHDR_IMG_FMT_10bppYCbCr410:
221 [[fallthrough]];
222 case UHDR_IMG_FMT_30bppYCbCr444:
223 [[fallthrough]];
224 default:
225 mErrorStatus.error_code = UHDR_CODEC_INVALID_PARAM;
226 mErrorStatus.has_detail = 1;
227 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
228 "unsupported color format option in create_texture(), color format %d", fmt);
229 glDeleteTextures(1, &textureID);
230 return 0;
231 }
232 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
233 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
234 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
235 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
236
237 check_gl_errors("create_texture()");
238 if (mErrorStatus.error_code != UHDR_CODEC_OK) {
239 glDeleteTextures(1, &textureID);
240 return 0;
241 }
242
243 return textureID;
244 }
245
setup_quad()246 void uhdr_opengl_ctxt::setup_quad() {
247 const float quadVertices[] = { // Positions // TexCoords
248 -1.0f, 1.0f, 0.0f, 1.0f,
249 -1.0f, -1.0f, 0.0f, 0.0f,
250 1.0f, -1.0f, 1.0f, 0.0f,
251 1.0f, 1.0f, 1.0f, 1.0f};
252 const unsigned int quadIndices[] = {0, 1, 2, 0, 2, 3};
253
254 glGenVertexArrays(1, &mQuadVAO);
255 glGenBuffers(1, &mQuadVBO);
256 glGenBuffers(1, &mQuadEBO);
257 glBindVertexArray(mQuadVAO);
258 glBindBuffer(GL_ARRAY_BUFFER, mQuadVBO);
259 glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
260 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadEBO);
261 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quadIndices), quadIndices, GL_STATIC_DRAW);
262 glEnableVertexAttribArray(0);
263 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
264 glEnableVertexAttribArray(1);
265 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
266
267 check_gl_errors("setup_quad()");
268 if (mErrorStatus.error_code != UHDR_CODEC_OK) {
269 if (mQuadVAO) {
270 glDeleteVertexArrays(1, &mQuadVAO);
271 mQuadVAO = 0;
272 }
273 if (mQuadVBO) {
274 glDeleteBuffers(1, &mQuadVBO);
275 mQuadVBO = 0;
276 }
277 if (mQuadEBO) {
278 glDeleteBuffers(1, &mQuadEBO);
279 mQuadEBO = 0;
280 }
281 }
282 }
283
setup_framebuffer(GLuint & texture)284 GLuint uhdr_opengl_ctxt::setup_framebuffer(GLuint& texture) {
285 GLuint frameBufferID;
286
287 glGenFramebuffers(1, &frameBufferID);
288 glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID);
289 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
290 GLenum err;
291 if ((err = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) {
292 mErrorStatus.error_code = UHDR_CODEC_ERROR;
293 mErrorStatus.has_detail = 1;
294 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
295 "glCheckFramebufferStatus() returned with error code : 0x%x", err);
296 glDeleteFramebuffers(1, &frameBufferID);
297 return 0;
298 }
299
300 check_gl_errors("setup_framebuffer()");
301 if (mErrorStatus.error_code != UHDR_CODEC_OK) {
302 glDeleteFramebuffers(1, &frameBufferID);
303 return 0;
304 }
305 return frameBufferID;
306 }
307
check_gl_errors(const char * msg)308 void uhdr_opengl_ctxt::check_gl_errors(const char* msg) {
309 GLenum err;
310 if ((err = glGetError()) != GL_NO_ERROR) {
311 mErrorStatus.error_code = UHDR_CODEC_ERROR;
312 mErrorStatus.has_detail = 1;
313 const char* err_str;
314 switch (err) {
315 case GL_INVALID_ENUM:
316 err_str = "GL_INVALID_ENUM";
317 break;
318 case GL_INVALID_VALUE:
319 err_str = "GL_INVALID_VALUE";
320 break;
321 case GL_INVALID_OPERATION:
322 err_str = "GL_INVALID_OPERATION";
323 break;
324 case GL_INVALID_FRAMEBUFFER_OPERATION:
325 err_str = "GL_INVALID_FRAMEBUFFER_OPERATION";
326 break;
327 case GL_OUT_OF_MEMORY:
328 err_str = "GL_OUT_OF_MEMORY";
329 break;
330 default:
331 err_str = "Unknown";
332 break;
333 }
334 snprintf(mErrorStatus.detail, sizeof mErrorStatus.detail,
335 "call to %s has raised one or more error flags, value of one error flag : %s", msg,
336 err_str);
337 }
338 }
339
read_texture(GLuint * texture,uhdr_img_fmt_t fmt,int w,int h,void * data)340 void uhdr_opengl_ctxt::read_texture(GLuint* texture, uhdr_img_fmt_t fmt, int w, int h, void* data) {
341 GLuint frm_buffer;
342 glGenFramebuffers(1, &frm_buffer);
343 glBindFramebuffer(GL_FRAMEBUFFER, frm_buffer);
344 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0);
345 if (fmt == UHDR_IMG_FMT_32bppRGBA8888) {
346 glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
347 } else if (fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
348 glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, data);
349 } else if (fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
350 glReadPixels(0, 0, w, h, GL_RGBA, GL_HALF_FLOAT, data);
351 } else if (fmt == UHDR_IMG_FMT_8bppYCbCr400) {
352 glPixelStorei(GL_PACK_ALIGNMENT, 1);
353 glReadPixels(0, 0, w, h, GL_RED, GL_UNSIGNED_BYTE, data);
354 glPixelStorei(GL_PACK_ALIGNMENT, 4);
355 }
356 glBindFramebuffer(GL_FRAMEBUFFER, 0);
357 glDeleteFramebuffers(1, &frm_buffer);
358 }
359
reset_opengl_ctxt()360 void uhdr_opengl_ctxt::reset_opengl_ctxt() {
361 delete_opengl_ctxt();
362 mErrorStatus = g_no_error;
363 }
364
delete_opengl_ctxt()365 void uhdr_opengl_ctxt::delete_opengl_ctxt() {
366 if (mQuadVAO) {
367 glDeleteVertexArrays(1, &mQuadVAO);
368 mQuadVAO = 0;
369 }
370 if (mQuadVBO) {
371 glDeleteBuffers(1, &mQuadVBO);
372 mQuadVBO = 0;
373 }
374 if (mQuadEBO) {
375 glDeleteBuffers(1, &mQuadEBO);
376 mQuadEBO = 0;
377 }
378 if (mEGLSurface != EGL_NO_SURFACE) {
379 eglDestroySurface(mEGLDisplay, mEGLSurface);
380 mEGLSurface = EGL_NO_SURFACE;
381 }
382 if (mEGLContext != EGL_NO_CONTEXT) {
383 eglDestroyContext(mEGLDisplay, mEGLContext);
384 mEGLContext = EGL_NO_CONTEXT;
385 }
386 mEGLConfig = 0;
387 if (mEGLDisplay != EGL_NO_DISPLAY) {
388 eglTerminate(mEGLDisplay);
389 mEGLDisplay = EGL_NO_DISPLAY;
390 }
391 if (mDecodedImgTexture) {
392 glDeleteTextures(1, &mDecodedImgTexture);
393 mDecodedImgTexture = 0;
394 }
395 if (mGainmapImgTexture) {
396 glDeleteTextures(1, &mGainmapImgTexture);
397 mGainmapImgTexture = 0;
398 }
399 for (int i = 0; i < UHDR_RESIZE + 1; i++) {
400 if (mShaderProgram[i]) {
401 glDeleteProgram(mShaderProgram[i]);
402 mShaderProgram[i] = 0;
403 }
404 }
405 }
406 } // namespace ultrahdr
407