1 | /* |
2 | Copyright (c) 2013, Broadcom Europe Ltd |
3 | Copyright (c) 2013, Tim Gover |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, 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 | |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
24 | ON 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 |
26 | SOFTWARE, 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 | |
34 | VCOS_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 | */ |
47 | void 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 | */ |
80 | int 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; |
134 | error: |
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 | */ |
141 | void 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 | */ |
159 | static 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 | |
218 | error: |
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 | */ |
228 | int 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 | */ |
242 | int 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 | |
274 | end: |
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 | */ |
283 | int 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); |
314 | end: |
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 | */ |
329 | int 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 | */ |
353 | int 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 | */ |
367 | int 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 | */ |
381 | int 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 | */ |
395 | int 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 | */ |
408 | int 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 | */ |
419 | int 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 | */ |
429 | void 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 | */ |
439 | void 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 | */ |
466 | int 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 | |
486 | error: |
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 | */ |
502 | int 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 | |
591 | fail: |
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 | |