xref: /aosp_15_r20/external/libultrahdr/lib/src/gpu/uhdr_gl_utils.cpp (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
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