1/*
2Copyright (c) 2013, Broadcom Europe Ltd
3Copyright (c) 2013, Tim Gover
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 * Neither the name of the copyright holder nor the
14 names of its contributors may be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
21DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include "RaspiTexUtil.h"
30#include "RaspiTex.h"
31#include <bcm_host.h>
32#include <GLES2/gl2.h>
33
34VCOS_LOG_CAT_T raspitex_log_category;
35
36/**
37 * \file RaspiTexUtil.c
38 *
39 * Provides default implementations for the raspitex_scene_ops functions
40 * and general utility functions.
41 */
42
43/**
44 * Deletes textures and EGL surfaces and context.
45 * @param raspitex_state Pointer to the Raspi
46 */
47void raspitexutil_gl_term(RASPITEX_STATE *raspitex_state)
48{
49 vcos_log_trace("%s", VCOS_FUNCTION);
50
51 /* Delete OES textures */
52 glDeleteTextures(1, &raspitex_state->texture);
53 eglDestroyImageKHR(raspitex_state->display, raspitex_state->egl_image);
54 raspitex_state->egl_image = EGL_NO_IMAGE_KHR;
55
56 glDeleteTextures(1, &raspitex_state->y_texture);
57 eglDestroyImageKHR(raspitex_state->display, raspitex_state->y_egl_image);
58 raspitex_state->y_egl_image = EGL_NO_IMAGE_KHR;
59
60 glDeleteTextures(1, &raspitex_state->u_texture);
61 eglDestroyImageKHR(raspitex_state->display, raspitex_state->u_egl_image);
62 raspitex_state->u_egl_image = EGL_NO_IMAGE_KHR;
63
64 glDeleteTextures(1, &raspitex_state->v_texture);
65 eglDestroyImageKHR(raspitex_state->display, raspitex_state->v_egl_image);
66 raspitex_state->v_egl_image = EGL_NO_IMAGE_KHR;
67
68 /* Terminate EGL */
69 eglMakeCurrent(raspitex_state->display, EGL_NO_SURFACE,
70 EGL_NO_SURFACE, EGL_NO_CONTEXT);
71 eglDestroyContext(raspitex_state->display, raspitex_state->context);
72 eglDestroySurface(raspitex_state->display, raspitex_state->surface);
73 eglTerminate(raspitex_state->display);
74}
75
76/** Creates a native window for the GL surface using dispmanx
77 * @param raspitex_state A pointer to the GL preview state.
78 * @return Zero if successful, otherwise, -1 is returned.
79 */
80int raspitexutil_create_native_window(RASPITEX_STATE *raspitex_state)
81{
82 VC_DISPMANX_ALPHA_T alpha = {DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, 255, 0};
83 VC_RECT_T src_rect = {0};
84 VC_RECT_T dest_rect = {0};
85 uint32_t disp_num = 0; // Primary
86 uint32_t layer_num = 0;
87 DISPMANX_ELEMENT_HANDLE_T elem;
88 DISPMANX_UPDATE_HANDLE_T update;
89
90 alpha.opacity = raspitex_state->opacity;
91 dest_rect.x = raspitex_state->x;
92 dest_rect.y = raspitex_state->y;
93 dest_rect.width = raspitex_state->width;
94 dest_rect.height = raspitex_state->height;
95
96 vcos_log_trace("%s: %d,%d,%d,%d %d,%d,0x%x,0x%x", VCOS_FUNCTION,
97 src_rect.x, src_rect.y, src_rect.width, src_rect.height,
98 dest_rect.x, dest_rect.y, dest_rect.width, dest_rect.height);
99
100 src_rect.width = dest_rect.width << 16;
101 src_rect.height = dest_rect.height << 16;
102
103 raspitex_state->disp = vc_dispmanx_display_open(disp_num);
104 if (raspitex_state->disp == DISPMANX_NO_HANDLE)
105 {
106 vcos_log_error("Failed to open display handle");
107 goto error;
108 }
109
110 update = vc_dispmanx_update_start(0);
111 if (update == DISPMANX_NO_HANDLE)
112 {
113 vcos_log_error("Failed to open update handle");
114 goto error;
115 }
116
117 elem = vc_dispmanx_element_add(update, raspitex_state->disp, layer_num,
118 &dest_rect, 0, &src_rect, DISPMANX_PROTECTION_NONE, &alpha, NULL,
119 DISPMANX_NO_ROTATE);
120 if (elem == DISPMANX_NO_HANDLE)
121 {
122 vcos_log_error("Failed to create element handle");
123 goto error;
124 }
125
126 raspitex_state->win.element = elem;
127 raspitex_state->win.width = raspitex_state->width;
128 raspitex_state->win.height = raspitex_state->height;
129 vc_dispmanx_update_submit_sync(update);
130
131 raspitex_state->native_window = (EGLNativeWindowType*) &raspitex_state->win;
132
133 return 0;
134error:
135 return -1;
136}
137
138/** Destroys the pools of buffers used by the GL renderer.
139 * @param raspitex_state A pointer to the GL preview state.
140 */
141void raspitexutil_destroy_native_window(RASPITEX_STATE *raspitex_state)
142{
143 vcos_log_trace("%s", VCOS_FUNCTION);
144 if (raspitex_state->disp != DISPMANX_NO_HANDLE)
145 {
146 vc_dispmanx_display_close(raspitex_state->disp);
147 raspitex_state->disp = DISPMANX_NO_HANDLE;
148 }
149}
150
151/** Creates the EGL context and window surface for the native window
152 * using specified arguments.
153 * @param raspitex_state A pointer to the GL preview state. This contains
154 * the native_window pointer.
155 * @param attribs The config attributes.
156 * @param context_attribs The context attributes.
157 * @return Zero if successful.
158 */
159static int raspitexutil_gl_common(RASPITEX_STATE *raspitex_state,
160 const EGLint attribs[], const EGLint context_attribs[])
161{
162 EGLConfig config;
163 EGLint num_configs;
164
165 vcos_log_trace("%s", VCOS_FUNCTION);
166
167 if (raspitex_state->native_window == NULL)
168 {
169 vcos_log_error("%s: No native window", VCOS_FUNCTION);
170 goto error;
171 }
172
173 raspitex_state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
174 if (raspitex_state->display == EGL_NO_DISPLAY)
175 {
176 vcos_log_error("%s: Failed to get EGL display", VCOS_FUNCTION);
177 goto error;
178 }
179
180 if (! eglInitialize(raspitex_state->display, 0, 0))
181 {
182 vcos_log_error("%s: eglInitialize failed", VCOS_FUNCTION);
183 goto error;
184 }
185
186 if (! eglChooseConfig(raspitex_state->display, attribs, &config,
187 1, &num_configs))
188 {
189 vcos_log_error("%s: eglChooseConfig failed", VCOS_FUNCTION);
190 goto error;
191 }
192
193 raspitex_state->surface = eglCreateWindowSurface(raspitex_state->display,
194 config, raspitex_state->native_window, NULL);
195 if (raspitex_state->surface == EGL_NO_SURFACE)
196 {
197 vcos_log_error("%s: eglCreateWindowSurface failed", VCOS_FUNCTION);
198 goto error;
199 }
200
201 raspitex_state->context = eglCreateContext(raspitex_state->display,
202 config, EGL_NO_CONTEXT, context_attribs);
203 if (raspitex_state->context == EGL_NO_CONTEXT)
204 {
205 vcos_log_error("%s: eglCreateContext failed", VCOS_FUNCTION);
206 goto error;
207 }
208
209 if (!eglMakeCurrent(raspitex_state->display, raspitex_state->surface,
210 raspitex_state->surface, raspitex_state->context))
211 {
212 vcos_log_error("%s: Failed to activate EGL context", VCOS_FUNCTION);
213 goto error;
214 }
215
216 return 0;
217
218error:
219 vcos_log_error("%s: EGL error 0x%08x", VCOS_FUNCTION, eglGetError());
220 raspitex_state->ops.gl_term(raspitex_state);
221 return -1;
222}
223
224/* Creates the RGBA and luma textures with some default parameters
225 * @param raspitex_state A pointer to the GL preview state.
226 * @return Zero if successful.
227 */
228int raspitexutil_create_textures(RASPITEX_STATE *raspitex_state)
229{
230 GLCHK(glGenTextures(1, &raspitex_state->texture));
231 GLCHK(glGenTextures(1, &raspitex_state->y_texture));
232 GLCHK(glGenTextures(1, &raspitex_state->u_texture));
233 GLCHK(glGenTextures(1, &raspitex_state->v_texture));
234 return 0;
235}
236
237/**
238 * Creates an OpenGL ES 1.X context.
239 * @param raspitex_state A pointer to the GL preview state.
240 * @return Zero if successful.
241 */
242int raspitexutil_gl_init_1_0(RASPITEX_STATE *raspitex_state)
243{
244 int rc;
245 const EGLint* attribs = raspitex_state->egl_config_attribs;
246
247 const EGLint default_attribs[] =
248 {
249 EGL_RED_SIZE, 8,
250 EGL_GREEN_SIZE, 8,
251 EGL_BLUE_SIZE, 8,
252 EGL_ALPHA_SIZE, 8,
253 EGL_DEPTH_SIZE, 16,
254 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
255 EGL_NONE
256 };
257
258 const EGLint context_attribs[] =
259 {
260 EGL_CONTEXT_CLIENT_VERSION, 1,
261 EGL_NONE
262 };
263
264 if (! attribs)
265 attribs = default_attribs;
266
267 rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs);
268 if (rc != 0)
269 goto end;
270
271 GLCHK(glEnable(GL_TEXTURE_EXTERNAL_OES));
272 rc = raspitexutil_create_textures(raspitex_state);
273
274end:
275 return rc;
276}
277
278/**
279 * Creates an OpenGL ES 2.X context.
280 * @param raspitex_state A pointer to the GL preview state.
281 * @return Zero if successful.
282 */
283int raspitexutil_gl_init_2_0(RASPITEX_STATE *raspitex_state)
284{
285 int rc;
286 const EGLint* attribs = raspitex_state->egl_config_attribs;;
287
288 const EGLint default_attribs[] =
289 {
290 EGL_RED_SIZE, 8,
291 EGL_GREEN_SIZE, 8,
292 EGL_BLUE_SIZE, 8,
293 EGL_ALPHA_SIZE, 8,
294 EGL_DEPTH_SIZE, 16,
295 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
296 EGL_NONE
297 };
298
299 const EGLint context_attribs[] =
300 {
301 EGL_CONTEXT_CLIENT_VERSION, 2,
302 EGL_NONE
303 };
304
305 if (! attribs)
306 attribs = default_attribs;
307
308 vcos_log_trace("%s", VCOS_FUNCTION);
309 rc = raspitexutil_gl_common(raspitex_state, attribs, context_attribs);
310 if (rc != 0)
311 goto end;
312
313 rc = raspitexutil_create_textures(raspitex_state);
314end:
315 return rc;
316}
317
318/**
319 * Advances the texture and EGL image to the next MMAL buffer.
320 *
321 * @param display The EGL display.
322 * @param target The EGL image target e.g. EGL_IMAGE_BRCM_MULTIMEDIA
323 * @param mm_buf The EGL client buffer (mmal opaque buffer) that is used to
324 * create the EGL Image for the preview texture.
325 * @param egl_image Pointer to the EGL image to update with mm_buf.
326 * @param texture Pointer to the texture to update from EGL image.
327 * @return Zero if successful.
328 */
329int raspitexutil_do_update_texture(EGLDisplay display, EGLenum target,
330 EGLClientBuffer mm_buf, GLuint *texture, EGLImageKHR *egl_image)
331{
332 vcos_log_trace("%s: mm_buf %u", VCOS_FUNCTION, (unsigned) mm_buf);
333 GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, *texture));
334 if (*egl_image != EGL_NO_IMAGE_KHR)
335 {
336 /* Discard the EGL image for the preview frame */
337 eglDestroyImageKHR(display, *egl_image);
338 *egl_image = EGL_NO_IMAGE_KHR;
339 }
340
341 *egl_image = eglCreateImageKHR(display, EGL_NO_CONTEXT, target, mm_buf, NULL);
342 GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, *egl_image));
343
344 return 0;
345}
346
347/**
348 * Updates the RGBX texture to the specified MMAL buffer.
349 * @param raspitex_state A pointer to the GL preview state.
350 * @param mm_buf The MMAL buffer.
351 * @return Zero if successful.
352 */
353int raspitexutil_update_texture(RASPITEX_STATE *raspitex_state,
354 EGLClientBuffer mm_buf)
355{
356 return raspitexutil_do_update_texture(raspitex_state->display,
357 EGL_IMAGE_BRCM_MULTIMEDIA, mm_buf,
358 &raspitex_state->texture, &raspitex_state->egl_image);
359}
360
361/**
362 * Updates the Y plane texture to the specified MMAL buffer.
363 * @param raspitex_state A pointer to the GL preview state.
364 * @param mm_buf The MMAL buffer.
365 * @return Zero if successful.
366 */
367int raspitexutil_update_y_texture(RASPITEX_STATE *raspitex_state,
368 EGLClientBuffer mm_buf)
369{
370 return raspitexutil_do_update_texture(raspitex_state->display,
371 EGL_IMAGE_BRCM_MULTIMEDIA_Y, mm_buf,
372 &raspitex_state->y_texture, &raspitex_state->y_egl_image);
373}
374
375/**
376 * Updates the U plane texture to the specified MMAL buffer.
377 * @param raspitex_state A pointer to the GL preview state.
378 * @param mm_buf The MMAL buffer.
379 * @return Zero if successful.
380 */
381int raspitexutil_update_u_texture(RASPITEX_STATE *raspitex_state,
382 EGLClientBuffer mm_buf)
383{
384 return raspitexutil_do_update_texture(raspitex_state->display,
385 EGL_IMAGE_BRCM_MULTIMEDIA_U, mm_buf,
386 &raspitex_state->u_texture, &raspitex_state->u_egl_image);
387}
388
389/**
390 * Updates the V plane texture to the specified MMAL buffer.
391 * @param raspitex_state A pointer to the GL preview state.
392 * @param mm_buf The MMAL buffer.
393 * @return Zero if successful.
394 */
395int raspitexutil_update_v_texture(RASPITEX_STATE *raspitex_state,
396 EGLClientBuffer mm_buf)
397{
398 return raspitexutil_do_update_texture(raspitex_state->display,
399 EGL_IMAGE_BRCM_MULTIMEDIA_V, mm_buf,
400 &raspitex_state->v_texture, &raspitex_state->v_egl_image);
401}
402
403/**
404 * Default is a no-op
405 * @param raspitex_state A pointer to the GL preview state.
406 * @return Zero.
407 */
408int raspitexutil_update_model(RASPITEX_STATE* raspitex_state)
409{
410 (void) raspitex_state;
411 return 0;
412}
413
414/**
415 * Default is a no-op
416 * @param raspitex_state A pointer to the GL preview state.
417 * @return Zero.
418 */
419int raspitexutil_redraw(RASPITEX_STATE* raspitex_state)
420{
421 (void) raspitex_state;
422 return 0;
423}
424
425/**
426 * Default is a no-op
427 * @param raspitex_state A pointer to the GL preview state.
428 */
429void raspitexutil_close(RASPITEX_STATE* raspitex_state)
430{
431 (void) raspitex_state;
432}
433
434/**
435 * Performs an in-place byte swap from BGRA to RGBA.
436 * @param buffer The buffer to modify.
437 * @param size Size of the buffer in bytes.
438 */
439void raspitexutil_brga_to_rgba(uint8_t *buffer, size_t size)
440{
441 uint8_t* out = buffer;
442 uint8_t* end = buffer + size;
443
444 while (out < end)
445 {
446 uint8_t tmp = out[0];
447 out[0] = out[2];
448 out[2] = tmp;
449 out += 4;
450 }
451}
452
453/**
454 * Uses glReadPixels to grab the current frame-buffer contents
455 * and returns the result in a newly allocate buffer along with
456 * the its size.
457 * Data is returned in BGRA format for TGA output. PPM output doesn't
458 * require the channel order swap but would require a vflip. The TGA
459 * format also supports alpha. The byte swap is not done in this function
460 * to avoid blocking the GL rendering thread.
461 * @param state Pointer to the GL preview state.
462 * @param buffer Address of pointer to set to pointer to new buffer.
463 * @param buffer_size The size of the new buffer in bytes (out param)
464 * @return Zero if successful.
465 */
466int raspitexutil_capture_bgra(RASPITEX_STATE *state,
467 uint8_t **buffer, size_t *buffer_size)
468{
469 const int bytes_per_pixel = 4;
470
471 vcos_log_trace("%s: %dx%d %d", VCOS_FUNCTION,
472 state->width, state->height, bytes_per_pixel);
473
474 *buffer_size = state->width * state->height * bytes_per_pixel;
475 *buffer = calloc(*buffer_size, 1);
476 if (! *buffer)
477 goto error;
478
479 glReadPixels(0, 0, state->width, state->height, GL_RGBA,
480 GL_UNSIGNED_BYTE, *buffer);
481 if (glGetError() != GL_NO_ERROR)
482 goto error;
483
484 return 0;
485
486error:
487 *buffer_size = 0;
488 if (*buffer)
489 free(*buffer);
490 *buffer = NULL;
491 return -1;
492}
493
494
495/**
496 * Takes a description of shader program, compiles it and gets the locations
497 * of uniforms and attributes.
498 *
499 * @param p The shader program state.
500 * @return Zero if successful.
501 */
502int raspitexutil_build_shader_program(RASPITEXUTIL_SHADER_PROGRAM_T *p)
503{
504 GLint status;
505 int i = 0;
506 char log[1024];
507 int logLen = 0;
508 vcos_assert(p);
509 vcos_assert(p->vertex_source);
510 vcos_assert(p->fragment_source);
511
512 if (! (p && p->vertex_source && p->fragment_source))
513 goto fail;
514
515 p->vs = p->fs = 0;
516
517 p->vs = glCreateShader(GL_VERTEX_SHADER);
518 glShaderSource(p->vs, 1, &p->vertex_source, NULL);
519 glCompileShader(p->vs);
520 glGetShaderiv(p->vs, GL_COMPILE_STATUS, &status);
521 if (! status)
522 {
523 glGetShaderInfoLog(p->vs, sizeof(log), &logLen, log);
524 vcos_log_error("Program info log %s", log);
525 goto fail;
526 }
527
528 p->fs = glCreateShader(GL_FRAGMENT_SHADER);
529 glShaderSource(p->fs, 1, &p->fragment_source, NULL);
530 glCompileShader(p->fs);
531
532 glGetShaderiv(p->fs, GL_COMPILE_STATUS, &status);
533 if (! status)
534 {
535 glGetShaderInfoLog(p->fs, sizeof(log), &logLen, log);
536 vcos_log_error("Program info log %s", log);
537 goto fail;
538 }
539
540 p->program = glCreateProgram();
541 glAttachShader(p->program, p->vs);
542 glAttachShader(p->program, p->fs);
543 glLinkProgram(p->program);
544 glGetProgramiv(p->program, GL_LINK_STATUS, &status);
545 if (! status)
546 {
547 vcos_log_error("Failed to link shader program");
548 glGetProgramInfoLog(p->program, sizeof(log), &logLen, log);
549 vcos_log_error("Program info log %s", log);
550 goto fail;
551 }
552
553 for (i = 0; i < SHADER_MAX_ATTRIBUTES; ++i)
554 {
555 if (! p->attribute_names[i])
556 break;
557 p->attribute_locations[i] = glGetAttribLocation(p->program, p->attribute_names[i]);
558 if (p->attribute_locations[i] == -1)
559 {
560 vcos_log_error("Failed to get location for attribute %s",
561 p->attribute_names[i]);
562 goto fail;
563 }
564 else
565 {
566 vcos_log_trace("Attribute for %s is %d",
567 p->attribute_names[i], p->attribute_locations[i]);
568 }
569 }
570
571 for (i = 0; i < SHADER_MAX_UNIFORMS; ++i)
572 {
573 if (! p->uniform_names[i])
574 break;
575 p->uniform_locations[i] = glGetUniformLocation(p->program, p->uniform_names[i]);
576 if (p->uniform_locations[i] == -1)
577 {
578 vcos_log_error("Failed to get location for uniform %s",
579 p->uniform_names[i]);
580 goto fail;
581 }
582 else
583 {
584 vcos_log_trace("Uniform for %s is %d",
585 p->uniform_names[i], p->uniform_locations[i]);
586 }
587 }
588
589 return 0;
590
591fail:
592 vcos_log_error("%s: Failed to build shader program", VCOS_FUNCTION);
593 if (p)
594 {
595 glDeleteProgram(p->program);
596 glDeleteShader(p->fs);
597 glDeleteShader(p->vs);
598 }
599 return -1;
600}
601
602