opengl: "-vvvv" to dump shaders
[vlc.git] / modules / video_output / opengl / vout_helper.c
blob52a07cb8c60774afb2940fde6e478ed1869b6de1
1 /*****************************************************************************
2 * vout_helper.c: OpenGL and OpenGL ES output common code
3 *****************************************************************************
4 * Copyright (C) 2004-2016 VLC authors and VideoLAN
5 * Copyright (C) 2009, 2011 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8 * Ilkka Ollakka <ileoo@videolan.org>
9 * Rémi Denis-Courmont
10 * Adrien Maglo <magsoft at videolan dot org>
11 * Felix Paul Kühne <fkuehne at videolan dot org>
12 * Pierre d'Herbemont <pdherbemont at videolan dot org>
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <assert.h>
33 #include <math.h>
35 #include <vlc_common.h>
36 #include <vlc_picture_pool.h>
37 #include <vlc_subpicture.h>
38 #include <vlc_opengl.h>
39 #include <vlc_memory.h>
40 #include <vlc_modules.h>
41 #include <vlc_vout.h>
42 #include <vlc_viewpoint.h>
44 #include "internal.h"
46 #ifndef GL_CLAMP_TO_EDGE
47 # define GL_CLAMP_TO_EDGE 0x812F
48 #endif
50 #define SPHERE_RADIUS 1.f
52 /* FIXME: GL_ASSERT_NOERROR disabled for now because:
53 * Proper GL error handling need to be implemented
54 * glClear(GL_COLOR_BUFFER_BIT) throws a GL_INVALID_FRAMEBUFFER_OPERATION on macOS
55 * assert fails on vout_display_opengl_Delete on iOS
57 #if 0
58 # define HAVE_GL_ASSERT_NOERROR
59 #endif
61 #ifdef HAVE_GL_ASSERT_NOERROR
62 # define GL_ASSERT_NOERROR() do { \
63 GLenum glError = glGetError(); \
64 switch (glError) \
65 { \
66 case GL_NO_ERROR: break; \
67 case GL_INVALID_ENUM: assert(!"GL_INVALID_ENUM"); \
68 case GL_INVALID_VALUE: assert(!"GL_INVALID_VALUE"); \
69 case GL_INVALID_OPERATION: assert(!"GL_INVALID_OPERATION"); \
70 case GL_INVALID_FRAMEBUFFER_OPERATION: assert(!"GL_INVALID_FRAMEBUFFER_OPERATION"); \
71 case GL_OUT_OF_MEMORY: assert(!"GL_OUT_OF_MEMORY"); \
72 default: assert(!"GL_UNKNOWN_ERROR"); \
73 } \
74 } while(0)
75 #else
76 # define GL_ASSERT_NOERROR()
77 #endif
79 typedef struct {
80 GLuint texture;
81 GLsizei width;
82 GLsizei height;
84 float alpha;
86 float top;
87 float left;
88 float bottom;
89 float right;
91 float tex_width;
92 float tex_height;
93 } gl_region_t;
95 struct prgm
97 GLuint id;
98 opengl_tex_converter_t *tc;
100 struct {
101 GLfloat OrientationMatrix[16];
102 GLfloat ProjectionMatrix[16];
103 GLfloat ZRotMatrix[16];
104 GLfloat YRotMatrix[16];
105 GLfloat XRotMatrix[16];
106 GLfloat ZoomMatrix[16];
107 } var;
109 struct { /* UniformLocation */
110 GLint OrientationMatrix;
111 GLint ProjectionMatrix;
112 GLint ZRotMatrix;
113 GLint YRotMatrix;
114 GLint XRotMatrix;
115 GLint ZoomMatrix;
116 } uloc;
117 struct { /* AttribLocation */
118 GLint MultiTexCoord[3];
119 GLint VertexPosition;
120 } aloc;
123 struct vout_display_opengl_t {
125 vlc_gl_t *gl;
126 opengl_vtable_t vt;
128 video_format_t fmt;
130 GLsizei tex_width[PICTURE_PLANE_MAX];
131 GLsizei tex_height[PICTURE_PLANE_MAX];
133 GLuint texture[PICTURE_PLANE_MAX];
135 int region_count;
136 gl_region_t *region;
139 picture_pool_t *pool;
141 /* One YUV program and one RGBA program (for subpics) */
142 struct prgm prgms[2];
143 struct prgm *prgm; /* Main program */
144 struct prgm *sub_prgm; /* Subpicture program */
146 unsigned nb_indices;
147 GLuint vertex_buffer_object;
148 GLuint index_buffer_object;
149 GLuint texture_buffer_object[PICTURE_PLANE_MAX];
151 GLuint *subpicture_buffer_object;
152 int subpicture_buffer_object_count;
154 struct {
155 unsigned int i_x_offset;
156 unsigned int i_y_offset;
157 unsigned int i_visible_width;
158 unsigned int i_visible_height;
159 } last_source;
161 /* Non-power-of-2 texture size support */
162 bool supports_npot;
164 /* View point */
165 float f_teta;
166 float f_phi;
167 float f_roll;
168 float f_fovx; /* f_fovx and f_fovy are linked but we keep both */
169 float f_fovy; /* to avoid recalculating them when needed. */
170 float f_z; /* Position of the camera on the shpere radius vector */
171 float f_z_min;
172 float f_sar;
175 static const GLfloat identity[] = {
176 1.0f, 0.0f, 0.0f, 0.0f,
177 0.0f, 1.0f, 0.0f, 0.0f,
178 0.0f, 0.0f, 1.0f, 0.0f,
179 0.0f, 0.0f, 0.0f, 1.0f
182 /* rotation around the Z axis */
183 static void getZRotMatrix(float theta, GLfloat matrix[static 16])
185 float st, ct;
187 sincosf(theta, &st, &ct);
189 const GLfloat m[] = {
190 /* x y z w */
191 ct, -st, 0.f, 0.f,
192 st, ct, 0.f, 0.f,
193 0.f, 0.f, 1.f, 0.f,
194 0.f, 0.f, 0.f, 1.f
197 memcpy(matrix, m, sizeof(m));
200 /* rotation around the Y axis */
201 static void getYRotMatrix(float theta, GLfloat matrix[static 16])
203 float st, ct;
205 sincosf(theta, &st, &ct);
207 const GLfloat m[] = {
208 /* x y z w */
209 ct, 0.f, -st, 0.f,
210 0.f, 1.f, 0.f, 0.f,
211 st, 0.f, ct, 0.f,
212 0.f, 0.f, 0.f, 1.f
215 memcpy(matrix, m, sizeof(m));
218 /* rotation around the X axis */
219 static void getXRotMatrix(float phi, GLfloat matrix[static 16])
221 float sp, cp;
223 sincosf(phi, &sp, &cp);
225 const GLfloat m[] = {
226 /* x y z w */
227 1.f, 0.f, 0.f, 0.f,
228 0.f, cp, sp, 0.f,
229 0.f, -sp, cp, 0.f,
230 0.f, 0.f, 0.f, 1.f
233 memcpy(matrix, m, sizeof(m));
236 static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {
238 const GLfloat m[] = {
239 /* x y z w */
240 1.0f, 0.0f, 0.0f, 0.0f,
241 0.0f, 1.0f, 0.0f, 0.0f,
242 0.0f, 0.0f, 1.0f, 0.0f,
243 0.0f, 0.0f, zoom, 1.0f
246 memcpy(matrix, m, sizeof(m));
249 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
250 static void getProjectionMatrix(float sar, float fovy, GLfloat matrix[static 16]) {
252 float zFar = 1000;
253 float zNear = 0.01;
255 float f = 1.f / tanf(fovy / 2.f);
257 const GLfloat m[] = {
258 f / sar, 0.f, 0.f, 0.f,
259 0.f, f, 0.f, 0.f,
260 0.f, 0.f, (zNear + zFar) / (zNear - zFar), -1.f,
261 0.f, 0.f, (2 * zNear * zFar) / (zNear - zFar), 0.f};
263 memcpy(matrix, m, sizeof(m));
266 static void getViewpointMatrixes(vout_display_opengl_t *vgl,
267 video_projection_mode_t projection_mode,
268 struct prgm *prgm)
270 if (projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
271 || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
273 float sar = (float) vgl->f_sar;
274 getProjectionMatrix(sar, vgl->f_fovy, prgm->var.ProjectionMatrix);
275 getYRotMatrix(vgl->f_teta, prgm->var.YRotMatrix);
276 getXRotMatrix(vgl->f_phi, prgm->var.XRotMatrix);
277 getZRotMatrix(vgl->f_roll, prgm->var.ZRotMatrix);
278 getZoomMatrix(vgl->f_z, prgm->var.ZoomMatrix);
280 else
282 memcpy(prgm->var.ProjectionMatrix, identity, sizeof(identity));
283 memcpy(prgm->var.ZRotMatrix, identity, sizeof(identity));
284 memcpy(prgm->var.YRotMatrix, identity, sizeof(identity));
285 memcpy(prgm->var.XRotMatrix, identity, sizeof(identity));
286 memcpy(prgm->var.ZoomMatrix, identity, sizeof(identity));
290 static void getOrientationTransformMatrix(video_orientation_t orientation,
291 GLfloat matrix[static 16])
293 memcpy(matrix, identity, sizeof(identity));
295 const int k_cos_pi = -1;
296 const int k_cos_pi_2 = 0;
297 const int k_cos_n_pi_2 = 0;
299 const int k_sin_pi = 0;
300 const int k_sin_pi_2 = 1;
301 const int k_sin_n_pi_2 = -1;
303 switch (orientation) {
305 case ORIENT_ROTATED_90:
306 matrix[0 * 4 + 0] = k_cos_pi_2;
307 matrix[0 * 4 + 1] = -k_sin_pi_2;
308 matrix[1 * 4 + 0] = k_sin_pi_2;
309 matrix[1 * 4 + 1] = k_cos_pi_2;
310 matrix[3 * 4 + 1] = 1;
311 break;
312 case ORIENT_ROTATED_180:
313 matrix[0 * 4 + 0] = k_cos_pi;
314 matrix[0 * 4 + 1] = -k_sin_pi;
315 matrix[1 * 4 + 0] = k_sin_pi;
316 matrix[1 * 4 + 1] = k_cos_pi;
317 matrix[3 * 4 + 0] = 1;
318 matrix[3 * 4 + 1] = 1;
319 break;
320 case ORIENT_ROTATED_270:
321 matrix[0 * 4 + 0] = k_cos_n_pi_2;
322 matrix[0 * 4 + 1] = -k_sin_n_pi_2;
323 matrix[1 * 4 + 0] = k_sin_n_pi_2;
324 matrix[1 * 4 + 1] = k_cos_n_pi_2;
325 matrix[3 * 4 + 0] = 1;
326 break;
327 case ORIENT_HFLIPPED:
328 matrix[0 * 4 + 0] = -1;
329 matrix[3 * 4 + 0] = 1;
330 break;
331 case ORIENT_VFLIPPED:
332 matrix[1 * 4 + 1] = -1;
333 matrix[3 * 4 + 1] = 1;
334 break;
335 case ORIENT_TRANSPOSED:
336 matrix[0 * 4 + 0] = 0;
337 matrix[1 * 4 + 1] = 0;
338 matrix[2 * 4 + 2] = -1;
339 matrix[0 * 4 + 1] = 1;
340 matrix[1 * 4 + 0] = 1;
341 break;
342 case ORIENT_ANTI_TRANSPOSED:
343 matrix[0 * 4 + 0] = 0;
344 matrix[1 * 4 + 1] = 0;
345 matrix[2 * 4 + 2] = -1;
346 matrix[0 * 4 + 1] = -1;
347 matrix[1 * 4 + 0] = -1;
348 matrix[3 * 4 + 0] = 1;
349 matrix[3 * 4 + 1] = 1;
350 break;
351 default:
352 break;
356 static inline GLsizei GetAlignedSize(unsigned size)
358 /* Return the smallest larger or equal power of 2 */
359 unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
360 return ((align >> 1) == size) ? size : align;
363 static GLuint BuildVertexShader(const opengl_tex_converter_t *tc,
364 unsigned plane_count)
366 /* Basic vertex shader */
367 static const char *template =
368 "#version %u\n"
369 "varying vec2 TexCoord0;\n"
370 "attribute vec4 MultiTexCoord0;\n"
371 "%s%s"
372 "attribute vec3 VertexPosition;\n"
373 "uniform mat4 OrientationMatrix;\n"
374 "uniform mat4 ProjectionMatrix;\n"
375 "uniform mat4 XRotMatrix;\n"
376 "uniform mat4 YRotMatrix;\n"
377 "uniform mat4 ZRotMatrix;\n"
378 "uniform mat4 ZoomMatrix;\n"
379 "void main() {\n"
380 " TexCoord0 = vec4(OrientationMatrix * MultiTexCoord0).st;\n"
381 "%s%s"
382 " gl_Position = ProjectionMatrix * ZoomMatrix * ZRotMatrix * XRotMatrix * YRotMatrix * vec4(VertexPosition, 1.0);\n"
383 "}";
385 const char *coord1_header = plane_count > 1 ?
386 "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
387 const char *coord1_code = plane_count > 1 ?
388 " TexCoord1 = vec4(OrientationMatrix * MultiTexCoord1).st;\n" : "";
389 const char *coord2_header = plane_count > 2 ?
390 "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
391 const char *coord2_code = plane_count > 2 ?
392 " TexCoord2 = vec4(OrientationMatrix * MultiTexCoord2).st;\n" : "";
394 char *code;
395 if (asprintf(&code, template, tc->glsl_version, coord1_header, coord2_header,
396 coord1_code, coord2_code) < 0)
397 return 0;
399 GLuint shader = tc->vt->CreateShader(GL_VERTEX_SHADER);
400 tc->vt->ShaderSource(shader, 1, (const char **) &code, NULL);
401 if (tc->b_dump_shaders)
402 fprintf(stderr, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
403 (const char *)&tc->fmt.i_chroma, code);
404 tc->vt->CompileShader(shader);
405 free(code);
406 return shader;
409 static int
410 GenTextures(const opengl_tex_converter_t *tc,
411 const GLsizei *tex_width, const GLsizei *tex_height,
412 GLuint *textures)
414 glGenTextures(tc->tex_count, textures);
416 for (unsigned i = 0; i < tc->tex_count; i++)
418 tc->vt->BindTexture(tc->tex_target, textures[i]);
420 #if !defined(USE_OPENGL_ES2)
421 /* Set the texture parameters */
422 glTexParameterf(tc->tex_target, GL_TEXTURE_PRIORITY, 1.0);
423 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
424 #endif
426 glTexParameteri(tc->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
427 glTexParameteri(tc->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
428 glTexParameteri(tc->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
429 glTexParameteri(tc->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
432 if (tc->pf_allocate_textures != NULL)
434 int ret = tc->pf_allocate_textures(tc, textures, tex_width, tex_height);
435 if (ret != VLC_SUCCESS)
437 glDeleteTextures(tc->tex_count, textures);
438 memset(textures, 0, tc->tex_count * sizeof(GLuint));
439 return ret;
442 return VLC_SUCCESS;
445 static void
446 DelTextures(const opengl_tex_converter_t *tc, GLuint *textures)
448 glDeleteTextures(tc->tex_count, textures);
449 memset(textures, 0, tc->tex_count * sizeof(GLuint));
452 static int
453 opengl_link_program(struct prgm *prgm)
455 opengl_tex_converter_t *tc = prgm->tc;
457 GLuint vertex_shader = BuildVertexShader(tc, tc->tex_count);
458 GLuint shaders[] = { tc->fshader, vertex_shader };
460 /* Check shaders messages */
461 for (unsigned i = 0; i < 2; i++) {
462 int infoLength;
463 tc->vt->GetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLength);
464 if (infoLength <= 1)
465 continue;
467 char *infolog = malloc(infoLength);
468 if (infolog != NULL)
470 int charsWritten;
471 tc->vt->GetShaderInfoLog(shaders[i], infoLength, &charsWritten,
472 infolog);
473 msg_Err(tc->gl, "shader %d: %s", i, infolog);
474 free(infolog);
478 prgm->id = tc->vt->CreateProgram();
479 tc->vt->AttachShader(prgm->id, tc->fshader);
480 tc->vt->AttachShader(prgm->id, vertex_shader);
481 tc->vt->LinkProgram(prgm->id);
483 tc->vt->DeleteShader(vertex_shader);
484 tc->vt->DeleteShader(tc->fshader);
486 /* Check program messages */
487 int infoLength = 0;
488 tc->vt->GetProgramiv(prgm->id, GL_INFO_LOG_LENGTH, &infoLength);
489 if (infoLength > 1)
491 char *infolog = malloc(infoLength);
492 if (infolog != NULL)
494 int charsWritten;
495 tc->vt->GetProgramInfoLog(prgm->id, infoLength, &charsWritten,
496 infolog);
497 msg_Err(tc->gl, "shader program: %s", infolog);
498 free(infolog);
501 /* If there is some message, better to check linking is ok */
502 GLint link_status = GL_TRUE;
503 tc->vt->GetProgramiv(prgm->id, GL_LINK_STATUS, &link_status);
504 if (link_status == GL_FALSE)
506 msg_Err(tc->gl, "Unable to use program");
507 goto error;
511 /* Fetch UniformLocations and AttribLocations */
512 #define GET_LOC(type, x, str) do { \
513 x = tc->vt->Get##type##Location(prgm->id, str); \
514 assert(x != -1); \
515 if (x == -1) { \
516 msg_Err(tc->gl, "Unable to Get"#type"Location(%s)\n", str); \
517 goto error; \
519 } while (0)
520 #define GET_ULOC(x, str) GET_LOC(Uniform, prgm->uloc.x, str)
521 #define GET_ALOC(x, str) GET_LOC(Attrib, prgm->aloc.x, str)
522 GET_ULOC(OrientationMatrix, "OrientationMatrix");
523 GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
524 GET_ULOC(ZRotMatrix, "ZRotMatrix");
525 GET_ULOC(YRotMatrix, "YRotMatrix");
526 GET_ULOC(XRotMatrix, "XRotMatrix");
527 GET_ULOC(ZoomMatrix, "ZoomMatrix");
529 GET_ALOC(VertexPosition, "VertexPosition");
530 GET_ALOC(MultiTexCoord[0], "MultiTexCoord0");
531 /* MultiTexCoord 1 and 2 can be optimized out if not used */
532 if (prgm->tc->tex_count > 1)
533 GET_ALOC(MultiTexCoord[1], "MultiTexCoord1");
534 else
535 prgm->aloc.MultiTexCoord[1] = -1;
536 if (prgm->tc->tex_count > 2)
537 GET_ALOC(MultiTexCoord[2], "MultiTexCoord2");
538 else
539 prgm->aloc.MultiTexCoord[2] = -1;
540 #undef GET_LOC
541 #undef GET_ULOC
542 #undef GET_ALOC
543 int ret = prgm->tc->pf_fetch_locations(prgm->tc, prgm->id);
544 assert(ret == VLC_SUCCESS);
545 if (ret != VLC_SUCCESS)
547 msg_Err(tc->gl, "Unable to get locations from tex_conv");
548 goto error;
551 return VLC_SUCCESS;
553 error:
554 tc->vt->DeleteProgram(prgm->id);
555 prgm->id = 0;
556 return VLC_EGENERIC;
559 static void
560 opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm)
562 opengl_tex_converter_t *tc = prgm->tc;
563 if (tc->p_module != NULL)
564 module_unneed(tc, tc->p_module);
565 else if (tc->priv != NULL)
566 opengl_tex_converter_generic_deinit(tc);
567 if (prgm->id != 0)
568 vgl->vt.DeleteProgram(prgm->id);
570 #ifdef HAVE_LIBPLACEBO
571 FREENULL(tc->uloc.pl_vars);
572 if (tc->pl_ctx)
573 pl_context_destroy(&tc->pl_ctx);
574 #endif
576 vlc_object_release(tc);
579 #ifdef HAVE_LIBPLACEBO
580 static void
581 log_cb(void *priv, enum pl_log_level level, const char *msg)
583 opengl_tex_converter_t *tc = priv;
584 switch (level) {
585 case PL_LOG_FATAL: // fall through
586 case PL_LOG_ERR: msg_Err(tc->gl, "%s", msg); break;
587 case PL_LOG_WARN: msg_Warn(tc->gl,"%s", msg); break;
588 case PL_LOG_INFO: msg_Info(tc->gl,"%s", msg); break;
589 default: break;
592 #endif
594 static int
595 opengl_init_program(vout_display_opengl_t *vgl, struct prgm *prgm,
596 const char *glexts, const video_format_t *fmt, bool subpics,
597 bool b_dump_shaders)
599 opengl_tex_converter_t *tc =
600 vlc_object_create(vgl->gl, sizeof(opengl_tex_converter_t));
601 if (tc == NULL)
602 return VLC_ENOMEM;
604 tc->gl = vgl->gl;
605 tc->vt = &vgl->vt;
606 tc->b_dump_shaders = b_dump_shaders;
607 tc->pf_fragment_shader_init = opengl_fragment_shader_init_impl;
608 tc->glexts = glexts;
609 #if defined(USE_OPENGL_ES2)
610 tc->is_gles = true;
611 tc->glsl_version = 100;
612 tc->glsl_precision_header = "precision highp float;\n";
613 #else
614 tc->is_gles = false;
615 tc->glsl_version = 120;
616 tc->glsl_precision_header = "";
617 #endif
618 tc->fmt = *fmt;
620 #ifdef HAVE_LIBPLACEBO
621 // create the main libplacebo context
622 if (!subpics)
624 tc->pl_ctx = pl_context_create(PL_API_VER, &(struct pl_context_params) {
625 .log_cb = log_cb,
626 .log_priv = tc,
627 .log_level = PL_LOG_INFO,
629 if (tc->pl_ctx)
630 tc->pl_sh = pl_shader_alloc(tc->pl_ctx, NULL, 0);
632 #endif
634 int ret;
635 if (subpics)
637 tc->fmt.i_chroma = VLC_CODEC_RGB32;
638 /* Normal orientation and no projection for subtitles */
639 tc->fmt.orientation = ORIENT_NORMAL;
640 tc->fmt.projection_mode = PROJECTION_MODE_RECTANGULAR;
641 tc->fmt.primaries = COLOR_PRIMARIES_UNDEF;
642 tc->fmt.transfer = TRANSFER_FUNC_UNDEF;
643 tc->fmt.space = COLOR_SPACE_UNDEF;
645 ret = opengl_tex_converter_generic_init(tc, false);
647 else
649 const vlc_chroma_description_t *desc =
650 vlc_fourcc_GetChromaDescription(fmt->i_chroma);
652 if (desc == NULL)
653 return VLC_EGENERIC;
654 if (desc->plane_count == 0)
656 /* Opaque chroma: load a module to handle it */
657 tc->p_module = module_need(tc, "glconv", "$glconv", true);
660 if (tc->p_module != NULL)
661 ret = VLC_SUCCESS;
662 else
664 /* Software chroma or gl hw converter failed: use a generic
665 * converter */
666 ret = opengl_tex_converter_generic_init(tc, true);
670 if (ret != VLC_SUCCESS)
672 vlc_object_release(tc);
673 return VLC_EGENERIC;
676 assert(tc->fshader != 0 && tc->tex_target != 0 && tc->tex_count > 0 &&
677 tc->pf_update != NULL && tc->pf_fetch_locations != NULL &&
678 tc->pf_prepare_shader != NULL);
680 prgm->tc = tc;
682 ret = opengl_link_program(prgm);
683 if (ret != VLC_SUCCESS)
685 opengl_deinit_program(vgl, prgm);
686 return VLC_EGENERIC;
689 getOrientationTransformMatrix(tc->fmt.orientation,
690 prgm->var.OrientationMatrix);
691 getViewpointMatrixes(vgl, tc->fmt.projection_mode, prgm);
693 return VLC_SUCCESS;
696 static void
697 ResizeFormatToGLMaxTexSize(video_format_t *fmt, unsigned int max_tex_size)
699 if (fmt->i_width > fmt->i_height)
701 unsigned int const vis_w = fmt->i_visible_width;
702 unsigned int const vis_h = fmt->i_visible_height;
703 unsigned int const nw_w = max_tex_size;
704 unsigned int const nw_vis_w = nw_w * vis_w / fmt->i_width;
706 fmt->i_height = nw_w * fmt->i_height / fmt->i_width;
707 fmt->i_width = nw_w;
708 fmt->i_visible_height = nw_vis_w * vis_h / vis_w;
709 fmt->i_visible_width = nw_vis_w;
711 else
713 unsigned int const vis_w = fmt->i_visible_width;
714 unsigned int const vis_h = fmt->i_visible_height;
715 unsigned int const nw_h = max_tex_size;
716 unsigned int const nw_vis_h = nw_h * vis_h / fmt->i_height;
718 fmt->i_width = nw_h * fmt->i_width / fmt->i_height;
719 fmt->i_height = nw_h;
720 fmt->i_visible_width = nw_vis_h * vis_w / vis_h;
721 fmt->i_visible_height = nw_vis_h;
725 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
726 const vlc_fourcc_t **subpicture_chromas,
727 vlc_gl_t *gl,
728 const vlc_viewpoint_t *viewpoint)
730 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
731 if (!vgl)
732 return NULL;
734 GL_ASSERT_NOERROR();
735 vgl->gl = gl;
737 if (gl->getProcAddress == NULL) {
738 msg_Err(gl, "getProcAddress not implemented, bailing out\n");
739 free(vgl);
740 return NULL;
743 const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
744 #if !defined(USE_OPENGL_ES2)
745 const unsigned char *ogl_version = glGetString(GL_VERSION);
746 bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
747 if (!supports_shaders)
749 msg_Err(gl, "shaders not supported, bailing out\n");
750 free(vgl);
751 return NULL;
753 #endif
755 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
756 #define GET_PROC_ADDR_EXT(name, critical) do { \
757 vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
758 if (vgl->vt.name == NULL && critical) { \
759 msg_Err(gl, "gl"#name" symbol not found, bailing out\n"); \
760 free(vgl); \
761 return NULL; \
763 } while(0)
764 #if defined(USE_OPENGL_ES2)
765 #define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
766 #define GET_PROC_ADDR_CORE_GL(name) vgl->vt.name = NULL /* GL only functions (not GLES) */
767 #else
768 #define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
769 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
770 #endif
771 #define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
773 GET_PROC_ADDR_CORE(GetError);
774 GET_PROC_ADDR_CORE(GetString);
775 GET_PROC_ADDR_CORE(GetIntegerv);
776 GET_PROC_ADDR_CORE(BindTexture);
777 GET_PROC_ADDR_CORE(TexParameteri);
778 GET_PROC_ADDR_CORE(TexParameterf);
779 GET_PROC_ADDR_CORE(PixelStorei);
780 GET_PROC_ADDR_CORE(GenTextures);
781 GET_PROC_ADDR_CORE(DeleteTextures);
782 GET_PROC_ADDR_CORE(TexImage2D);
783 GET_PROC_ADDR_CORE(TexSubImage2D);
785 GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv);
787 GET_PROC_ADDR(CreateShader);
788 GET_PROC_ADDR(ShaderSource);
789 GET_PROC_ADDR(CompileShader);
790 GET_PROC_ADDR(AttachShader);
791 GET_PROC_ADDR(DeleteShader);
793 GET_PROC_ADDR(GetProgramiv);
794 GET_PROC_ADDR(GetShaderiv);
795 GET_PROC_ADDR(GetProgramInfoLog);
796 GET_PROC_ADDR(GetShaderInfoLog);
798 GET_PROC_ADDR(GetUniformLocation);
799 GET_PROC_ADDR(GetAttribLocation);
800 GET_PROC_ADDR(VertexAttribPointer);
801 GET_PROC_ADDR(EnableVertexAttribArray);
802 GET_PROC_ADDR(UniformMatrix4fv);
803 GET_PROC_ADDR(UniformMatrix3fv);
804 GET_PROC_ADDR(UniformMatrix2fv);
805 GET_PROC_ADDR(Uniform4fv);
806 GET_PROC_ADDR(Uniform4f);
807 GET_PROC_ADDR(Uniform3f);
808 GET_PROC_ADDR(Uniform2f);
809 GET_PROC_ADDR(Uniform1f);
810 GET_PROC_ADDR(Uniform1i);
812 GET_PROC_ADDR(CreateProgram);
813 GET_PROC_ADDR(LinkProgram);
814 GET_PROC_ADDR(UseProgram);
815 GET_PROC_ADDR(DeleteProgram);
817 GET_PROC_ADDR(ActiveTexture);
819 GET_PROC_ADDR(GenBuffers);
820 GET_PROC_ADDR(BindBuffer);
821 GET_PROC_ADDR(BufferData);
822 GET_PROC_ADDR(DeleteBuffers);
824 GET_PROC_ADDR_OPTIONAL(BufferSubData);
825 GET_PROC_ADDR_OPTIONAL(BufferStorage);
826 GET_PROC_ADDR_OPTIONAL(MapBufferRange);
827 GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange);
828 GET_PROC_ADDR_OPTIONAL(UnmapBuffer);
829 GET_PROC_ADDR_OPTIONAL(FenceSync);
830 GET_PROC_ADDR_OPTIONAL(DeleteSync);
831 GET_PROC_ADDR_OPTIONAL(ClientWaitSync);
832 #undef GET_PROC_ADDR
834 /* Resize the format if it is greater than the maximum texture size
835 * supported by the hardware */
836 GLint max_tex_size;
837 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
839 if ((GLint)fmt->i_width > max_tex_size ||
840 (GLint)fmt->i_height > max_tex_size)
841 ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
843 #if defined(USE_OPENGL_ES2)
844 /* OpenGL ES 2 includes support for non-power of 2 textures by specification
845 * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
846 vgl->supports_npot = true;
847 #else
848 vgl->supports_npot = HasExtension(extensions, "GL_ARB_texture_non_power_of_two") ||
849 HasExtension(extensions, "GL_APPLE_texture_2D_limited_npot");
850 #endif
852 bool b_dump_shaders = var_InheritInteger(gl, "verbose") >= 4;
854 vgl->prgm = &vgl->prgms[0];
855 vgl->sub_prgm = &vgl->prgms[1];
857 GL_ASSERT_NOERROR();
858 int ret;
859 ret = opengl_init_program(vgl, vgl->prgm, extensions, fmt, false,
860 b_dump_shaders);
861 if (ret != VLC_SUCCESS)
863 msg_Warn(gl, "could not init tex converter for %4.4s",
864 (const char *) &fmt->i_chroma);
865 free(vgl);
866 return NULL;
869 GL_ASSERT_NOERROR();
870 ret = opengl_init_program(vgl, vgl->sub_prgm, extensions, fmt, true,
871 b_dump_shaders);
872 if (ret != VLC_SUCCESS)
874 msg_Warn(gl, "could not init subpictures tex converter for %4.4s",
875 (const char *) &fmt->i_chroma);
876 opengl_deinit_program(vgl, vgl->prgm);
877 free(vgl);
878 return NULL;
880 GL_ASSERT_NOERROR();
881 /* Update the fmt to main program one */
882 vgl->fmt = vgl->prgm->tc->fmt;
883 /* The orientation is handled by the orientation matrix */
884 vgl->fmt.orientation = fmt->orientation;
886 /* Texture size */
887 const opengl_tex_converter_t *tc = vgl->prgm->tc;
888 for (unsigned j = 0; j < tc->tex_count; j++) {
889 const GLsizei w = vgl->fmt.i_visible_width * tc->texs[j].w.num
890 / tc->texs[j].w.den;
891 const GLsizei h = vgl->fmt.i_visible_height * tc->texs[j].h.num
892 / tc->texs[j].h.den;
893 if (vgl->supports_npot) {
894 vgl->tex_width[j] = w;
895 vgl->tex_height[j] = h;
896 } else {
897 vgl->tex_width[j] = GetAlignedSize(w);
898 vgl->tex_height[j] = GetAlignedSize(h);
902 /* Allocates our textures */
903 assert(!vgl->sub_prgm->tc->handle_texs_gen);
905 if (!vgl->prgm->tc->handle_texs_gen)
907 ret = GenTextures(vgl->prgm->tc, vgl->tex_width, vgl->tex_height,
908 vgl->texture);
909 if (ret != VLC_SUCCESS)
911 vout_display_opengl_Delete(vgl);
912 return NULL;
916 /* */
917 glDisable(GL_BLEND);
918 glDisable(GL_DEPTH_TEST);
919 glDepthMask(GL_FALSE);
920 glEnable(GL_CULL_FACE);
921 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
922 glClear(GL_COLOR_BUFFER_BIT);
924 vgl->vt.GenBuffers(1, &vgl->vertex_buffer_object);
925 vgl->vt.GenBuffers(1, &vgl->index_buffer_object);
926 vgl->vt.GenBuffers(vgl->prgm->tc->tex_count, vgl->texture_buffer_object);
928 /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
929 int subpicture_buffer_object_count = 8;
930 vgl->subpicture_buffer_object = vlc_alloc(subpicture_buffer_object_count, sizeof(GLuint));
931 if (!vgl->subpicture_buffer_object) {
932 vout_display_opengl_Delete(vgl);
933 return NULL;
935 vgl->subpicture_buffer_object_count = subpicture_buffer_object_count;
936 vgl->vt.GenBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
938 /* */
939 vgl->region_count = 0;
940 vgl->region = NULL;
941 vgl->pool = NULL;
943 if (vgl->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
944 && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
946 vout_display_opengl_Delete(vgl);
947 return NULL;
950 *fmt = vgl->fmt;
951 if (subpicture_chromas) {
952 *subpicture_chromas = gl_subpicture_chromas;
955 GL_ASSERT_NOERROR();
956 return vgl;
959 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
961 GL_ASSERT_NOERROR();
963 /* */
964 glFinish();
965 glFlush();
967 opengl_tex_converter_t *tc = vgl->prgm->tc;
968 if (!tc->handle_texs_gen)
969 DelTextures(tc, vgl->texture);
971 tc = vgl->sub_prgm->tc;
972 for (int i = 0; i < vgl->region_count; i++)
974 if (vgl->region[i].texture)
975 DelTextures(tc, &vgl->region[i].texture);
977 free(vgl->region);
979 vgl->vt.DeleteBuffers(1, &vgl->vertex_buffer_object);
980 vgl->vt.DeleteBuffers(1, &vgl->index_buffer_object);
981 vgl->vt.DeleteBuffers(vgl->prgm->tc->tex_count, vgl->texture_buffer_object);
983 if (vgl->subpicture_buffer_object_count > 0)
984 vgl->vt.DeleteBuffers(vgl->subpicture_buffer_object_count,
985 vgl->subpicture_buffer_object);
986 free(vgl->subpicture_buffer_object);
988 if (vgl->pool)
989 picture_pool_Release(vgl->pool);
990 opengl_deinit_program(vgl, vgl->prgm);
991 opengl_deinit_program(vgl, vgl->sub_prgm);
993 free(vgl);
995 GL_ASSERT_NOERROR();
998 static void UpdateZ(vout_display_opengl_t *vgl)
1000 /* Do trigonometry to calculate the minimal z value
1001 * that will allow us to zoom out without seeing the outside of the
1002 * sphere (black borders). */
1003 float tan_fovx_2 = tanf(vgl->f_fovx / 2);
1004 float tan_fovy_2 = tanf(vgl->f_fovy / 2);
1005 float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
1006 tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
1008 /* The FOV value above which z is dynamically calculated. */
1009 const float z_thresh = 90.f;
1011 if (vgl->f_fovx <= z_thresh * M_PI / 180)
1012 vgl->f_z = 0;
1013 else
1015 float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
1016 vgl->f_z = f * vgl->f_fovx - f * z_thresh * M_PI / 180;
1017 if (vgl->f_z < z_min)
1018 vgl->f_z = z_min;
1022 static void UpdateFOVy(vout_display_opengl_t *vgl)
1024 vgl->f_fovy = 2 * atanf(tanf(vgl->f_fovx / 2) / vgl->f_sar);
1027 int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
1028 const vlc_viewpoint_t *p_vp)
1030 #define RAD(d) ((float) ((d) * M_PI / 180.f))
1031 float f_fovx = RAD(p_vp->fov);
1032 if (f_fovx > FIELD_OF_VIEW_DEGREES_MAX * M_PI / 180 + 0.001f
1033 || f_fovx < -0.001f)
1034 return VLC_EBADVAR;
1036 vgl->f_teta = RAD(p_vp->yaw) - (float) M_PI_2;
1037 vgl->f_phi = RAD(p_vp->pitch);
1038 vgl->f_roll = RAD(p_vp->roll);
1041 if (fabsf(f_fovx - vgl->f_fovx) >= 0.001f)
1043 /* FOVx has changed. */
1044 vgl->f_fovx = f_fovx;
1045 UpdateFOVy(vgl);
1046 UpdateZ(vgl);
1048 getViewpointMatrixes(vgl, vgl->fmt.projection_mode, vgl->prgm);
1050 return VLC_SUCCESS;
1051 #undef RAD
1055 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t *vgl,
1056 float f_sar)
1058 /* Each time the window size changes, we must recompute the minimum zoom
1059 * since the aspect ration changes.
1060 * We must also set the new current zoom value. */
1061 vgl->f_sar = f_sar;
1062 UpdateFOVy(vgl);
1063 UpdateZ(vgl);
1064 getViewpointMatrixes(vgl, vgl->fmt.projection_mode, vgl->prgm);
1067 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
1069 GL_ASSERT_NOERROR();
1071 if (vgl->pool)
1072 return vgl->pool;
1074 opengl_tex_converter_t *tc = vgl->prgm->tc;
1075 requested_count = __MIN(VLCGL_PICTURE_MAX, requested_count);
1076 /* Allocate with tex converter pool callback if it exists */
1077 if (tc->pf_get_pool != NULL)
1079 vgl->pool = tc->pf_get_pool(tc, requested_count);
1080 if (!vgl->pool)
1081 goto error;
1082 return vgl->pool;
1085 /* Allocate our pictures */
1086 picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
1087 unsigned count;
1088 for (count = 0; count < requested_count; count++)
1090 picture[count] = picture_NewFromFormat(&vgl->fmt);
1091 if (!picture[count])
1092 break;
1094 if (count <= 0)
1095 goto error;
1097 /* Wrap the pictures into a pool */
1098 vgl->pool = picture_pool_New(count, picture);
1099 if (!vgl->pool)
1101 for (unsigned i = 0; i < count; i++)
1102 picture_Release(picture[i]);
1103 goto error;
1106 GL_ASSERT_NOERROR();
1107 return vgl->pool;
1109 error:
1110 DelTextures(tc, vgl->texture);
1111 return NULL;
1114 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
1115 picture_t *picture, subpicture_t *subpicture)
1117 GL_ASSERT_NOERROR();
1119 opengl_tex_converter_t *tc = vgl->prgm->tc;
1121 /* Update the texture */
1122 int ret = tc->pf_update(tc, vgl->texture, vgl->tex_width, vgl->tex_height,
1123 picture, NULL);
1124 if (ret != VLC_SUCCESS)
1125 return ret;
1127 int last_count = vgl->region_count;
1128 gl_region_t *last = vgl->region;
1130 vgl->region_count = 0;
1131 vgl->region = NULL;
1133 tc = vgl->sub_prgm->tc;
1134 if (subpicture) {
1136 int count = 0;
1137 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1138 count++;
1140 vgl->region_count = count;
1141 vgl->region = calloc(count, sizeof(*vgl->region));
1143 int i = 0;
1144 for (subpicture_region_t *r = subpicture->p_region;
1145 r && ret == VLC_SUCCESS; r = r->p_next, i++) {
1146 gl_region_t *glr = &vgl->region[i];
1148 glr->width = r->fmt.i_visible_width;
1149 glr->height = r->fmt.i_visible_height;
1150 if (!vgl->supports_npot) {
1151 glr->width = GetAlignedSize(glr->width);
1152 glr->height = GetAlignedSize(glr->height);
1153 glr->tex_width = (float) r->fmt.i_visible_width / glr->width;
1154 glr->tex_height = (float) r->fmt.i_visible_height / glr->height;
1155 } else {
1156 glr->tex_width = 1.0;
1157 glr->tex_height = 1.0;
1159 glr->alpha = (float)subpicture->i_alpha * r->i_alpha / 255 / 255;
1160 glr->left = 2.0 * (r->i_x ) / subpicture->i_original_picture_width - 1.0;
1161 glr->top = -2.0 * (r->i_y ) / subpicture->i_original_picture_height + 1.0;
1162 glr->right = 2.0 * (r->i_x + r->fmt.i_visible_width ) / subpicture->i_original_picture_width - 1.0;
1163 glr->bottom = -2.0 * (r->i_y + r->fmt.i_visible_height) / subpicture->i_original_picture_height + 1.0;
1165 glr->texture = 0;
1166 /* Try to recycle the textures allocated by the previous
1167 call to this function. */
1168 for (int j = 0; j < last_count; j++) {
1169 if (last[j].texture &&
1170 last[j].width == glr->width &&
1171 last[j].height == glr->height) {
1172 glr->texture = last[j].texture;
1173 memset(&last[j], 0, sizeof(last[j]));
1174 break;
1178 const size_t pixels_offset =
1179 r->fmt.i_y_offset * r->p_picture->p->i_pitch +
1180 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
1181 if (!glr->texture)
1183 /* Could not recycle a previous texture, generate a new one. */
1184 ret = GenTextures(tc, &glr->width, &glr->height, &glr->texture);
1185 if (ret != VLC_SUCCESS)
1186 continue;
1188 ret = tc->pf_update(tc, &glr->texture, &glr->width, &glr->height,
1189 r->p_picture, &pixels_offset);
1192 for (int i = 0; i < last_count; i++) {
1193 if (last[i].texture)
1194 DelTextures(tc, &last[i].texture);
1196 free(last);
1198 VLC_UNUSED(subpicture);
1200 GL_ASSERT_NOERROR();
1201 return ret;
1204 static int BuildSphere(unsigned nbPlanes,
1205 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1206 GLushort **indices, unsigned *nbIndices,
1207 const float *left, const float *top,
1208 const float *right, const float *bottom)
1210 unsigned nbLatBands = 128;
1211 unsigned nbLonBands = 128;
1213 *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
1214 *nbIndices = nbLatBands * nbLonBands * 3 * 2;
1216 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1217 if (*vertexCoord == NULL)
1218 return VLC_ENOMEM;
1219 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1220 if (*textureCoord == NULL)
1222 free(*vertexCoord);
1223 return VLC_ENOMEM;
1225 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1226 if (*indices == NULL)
1228 free(*textureCoord);
1229 free(*vertexCoord);
1230 return VLC_ENOMEM;
1233 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
1234 float theta = lat * (float) M_PI / nbLatBands;
1235 float sinTheta, cosTheta;
1237 sincosf(theta, &sinTheta, &cosTheta);
1239 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
1240 float phi = lon * 2 * (float) M_PI / nbLonBands;
1241 float sinPhi, cosPhi;
1243 sincosf(phi, &sinPhi, &cosPhi);
1245 float x = cosPhi * sinTheta;
1246 float y = cosTheta;
1247 float z = sinPhi * sinTheta;
1249 unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
1250 (*vertexCoord)[off1] = SPHERE_RADIUS * x;
1251 (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
1252 (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
1254 for (unsigned p = 0; p < nbPlanes; ++p)
1256 unsigned off2 = (p * (nbLatBands + 1) * (nbLonBands + 1)
1257 + lat * (nbLonBands + 1) + lon) * 2;
1258 float width = right[p] - left[p];
1259 float height = bottom[p] - top[p];
1260 float u = (float)lon / nbLonBands * width;
1261 float v = (float)lat / nbLatBands * height;
1262 (*textureCoord)[off2] = u;
1263 (*textureCoord)[off2 + 1] = v;
1268 for (unsigned lat = 0; lat < nbLatBands; lat++) {
1269 for (unsigned lon = 0; lon < nbLonBands; lon++) {
1270 unsigned first = (lat * (nbLonBands + 1)) + lon;
1271 unsigned second = first + nbLonBands + 1;
1273 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
1275 (*indices)[off] = first;
1276 (*indices)[off + 1] = second;
1277 (*indices)[off + 2] = first + 1;
1279 (*indices)[off + 3] = second;
1280 (*indices)[off + 4] = second + 1;
1281 (*indices)[off + 5] = first + 1;
1285 return VLC_SUCCESS;
1289 static int BuildCube(unsigned nbPlanes,
1290 float padW, float padH,
1291 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1292 GLushort **indices, unsigned *nbIndices,
1293 const float *left, const float *top,
1294 const float *right, const float *bottom)
1296 *nbVertices = 4 * 6;
1297 *nbIndices = 6 * 6;
1299 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1300 if (*vertexCoord == NULL)
1301 return VLC_ENOMEM;
1302 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1303 if (*textureCoord == NULL)
1305 free(*vertexCoord);
1306 return VLC_ENOMEM;
1308 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1309 if (*indices == NULL)
1311 free(*textureCoord);
1312 free(*vertexCoord);
1313 return VLC_ENOMEM;
1316 static const GLfloat coord[] = {
1317 -1.0, 1.0, -1.0f, // front
1318 -1.0, -1.0, -1.0f,
1319 1.0, 1.0, -1.0f,
1320 1.0, -1.0, -1.0f,
1322 -1.0, 1.0, 1.0f, // back
1323 -1.0, -1.0, 1.0f,
1324 1.0, 1.0, 1.0f,
1325 1.0, -1.0, 1.0f,
1327 -1.0, 1.0, -1.0f, // left
1328 -1.0, -1.0, -1.0f,
1329 -1.0, 1.0, 1.0f,
1330 -1.0, -1.0, 1.0f,
1332 1.0f, 1.0, -1.0f, // right
1333 1.0f, -1.0, -1.0f,
1334 1.0f, 1.0, 1.0f,
1335 1.0f, -1.0, 1.0f,
1337 -1.0, -1.0, 1.0f, // bottom
1338 -1.0, -1.0, -1.0f,
1339 1.0, -1.0, 1.0f,
1340 1.0, -1.0, -1.0f,
1342 -1.0, 1.0, 1.0f, // top
1343 -1.0, 1.0, -1.0f,
1344 1.0, 1.0, 1.0f,
1345 1.0, 1.0, -1.0f,
1348 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1350 for (unsigned p = 0; p < nbPlanes; ++p)
1352 float width = right[p] - left[p];
1353 float height = bottom[p] - top[p];
1355 float col[] = {left[p],
1356 left[p] + width * 1.f/3,
1357 left[p] + width * 2.f/3,
1358 left[p] + width};
1360 float row[] = {top[p],
1361 top[p] + height * 1.f/2,
1362 top[p] + height};
1364 const GLfloat tex[] = {
1365 col[1] + padW, row[1] + padH, // front
1366 col[1] + padW, row[2] - padH,
1367 col[2] - padW, row[1] + padH,
1368 col[2] - padW, row[2] - padH,
1370 col[3] - padW, row[1] + padH, // back
1371 col[3] - padW, row[2] - padH,
1372 col[2] + padW, row[1] + padH,
1373 col[2] + padW, row[2] - padH,
1375 col[2] - padW, row[0] + padH, // left
1376 col[2] - padW, row[1] - padH,
1377 col[1] + padW, row[0] + padH,
1378 col[1] + padW, row[1] - padH,
1380 col[0] + padW, row[0] + padH, // right
1381 col[0] + padW, row[1] - padH,
1382 col[1] - padW, row[0] + padH,
1383 col[1] - padW, row[1] - padH,
1385 col[0] + padW, row[2] - padH, // bottom
1386 col[0] + padW, row[1] + padH,
1387 col[1] - padW, row[2] - padH,
1388 col[1] - padW, row[1] + padH,
1390 col[2] + padW, row[0] + padH, // top
1391 col[2] + padW, row[1] - padH,
1392 col[3] - padW, row[0] + padH,
1393 col[3] - padW, row[1] - padH,
1396 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1397 *nbVertices * 2 * sizeof(GLfloat));
1400 const GLushort ind[] = {
1401 0, 1, 2, 2, 1, 3, // front
1402 6, 7, 4, 4, 7, 5, // back
1403 10, 11, 8, 8, 11, 9, // left
1404 12, 13, 14, 14, 13, 15, // right
1405 18, 19, 16, 16, 19, 17, // bottom
1406 20, 21, 22, 22, 21, 23, // top
1409 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1411 return VLC_SUCCESS;
1414 static int BuildRectangle(unsigned nbPlanes,
1415 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1416 GLushort **indices, unsigned *nbIndices,
1417 const float *left, const float *top,
1418 const float *right, const float *bottom)
1420 *nbVertices = 4;
1421 *nbIndices = 6;
1423 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1424 if (*vertexCoord == NULL)
1425 return VLC_ENOMEM;
1426 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1427 if (*textureCoord == NULL)
1429 free(*vertexCoord);
1430 return VLC_ENOMEM;
1432 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1433 if (*indices == NULL)
1435 free(*textureCoord);
1436 free(*vertexCoord);
1437 return VLC_ENOMEM;
1440 static const GLfloat coord[] = {
1441 -1.0, 1.0, -1.0f,
1442 -1.0, -1.0, -1.0f,
1443 1.0, 1.0, -1.0f,
1444 1.0, -1.0, -1.0f
1447 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1449 for (unsigned p = 0; p < nbPlanes; ++p)
1451 const GLfloat tex[] = {
1452 left[p], top[p],
1453 left[p], bottom[p],
1454 right[p], top[p],
1455 right[p], bottom[p]
1458 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1459 *nbVertices * 2 * sizeof(GLfloat));
1462 const GLushort ind[] = {
1463 0, 1, 2,
1464 2, 1, 3
1467 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1469 return VLC_SUCCESS;
1472 static int SetupCoords(vout_display_opengl_t *vgl,
1473 const float *left, const float *top,
1474 const float *right, const float *bottom)
1476 GLfloat *vertexCoord, *textureCoord;
1477 GLushort *indices;
1478 unsigned nbVertices, nbIndices;
1480 int i_ret;
1481 switch (vgl->fmt.projection_mode)
1483 case PROJECTION_MODE_RECTANGULAR:
1484 i_ret = BuildRectangle(vgl->prgm->tc->tex_count,
1485 &vertexCoord, &textureCoord, &nbVertices,
1486 &indices, &nbIndices,
1487 left, top, right, bottom);
1488 break;
1489 case PROJECTION_MODE_EQUIRECTANGULAR:
1490 i_ret = BuildSphere(vgl->prgm->tc->tex_count,
1491 &vertexCoord, &textureCoord, &nbVertices,
1492 &indices, &nbIndices,
1493 left, top, right, bottom);
1494 break;
1495 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
1496 i_ret = BuildCube(vgl->prgm->tc->tex_count,
1497 (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_width,
1498 (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_height,
1499 &vertexCoord, &textureCoord, &nbVertices,
1500 &indices, &nbIndices,
1501 left, top, right, bottom);
1502 break;
1503 default:
1504 i_ret = VLC_EGENERIC;
1505 break;
1508 if (i_ret != VLC_SUCCESS)
1509 return i_ret;
1511 for (unsigned j = 0; j < vgl->prgm->tc->tex_count; j++)
1513 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
1514 vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
1515 textureCoord + j * nbVertices * 2, GL_STATIC_DRAW);
1518 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
1519 vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
1520 vertexCoord, GL_STATIC_DRAW);
1522 vgl->vt.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, vgl->index_buffer_object);
1523 vgl->vt.BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
1524 indices, GL_STATIC_DRAW);
1526 free(textureCoord);
1527 free(vertexCoord);
1528 free(indices);
1530 vgl->nb_indices = nbIndices;
1532 return VLC_SUCCESS;
1535 static void DrawWithShaders(vout_display_opengl_t *vgl, struct prgm *prgm)
1537 opengl_tex_converter_t *tc = prgm->tc;
1538 tc->pf_prepare_shader(tc, vgl->tex_width, vgl->tex_height, 1.0f);
1540 for (unsigned j = 0; j < vgl->prgm->tc->tex_count; j++) {
1541 assert(vgl->texture[j] != 0);
1542 vgl->vt.ActiveTexture(GL_TEXTURE0+j);
1543 vgl->vt.BindTexture(tc->tex_target, vgl->texture[j]);
1545 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
1547 assert(prgm->aloc.MultiTexCoord[j] != -1);
1548 vgl->vt.EnableVertexAttribArray(prgm->aloc.MultiTexCoord[j]);
1549 vgl->vt.VertexAttribPointer(prgm->aloc.MultiTexCoord[j], 2, GL_FLOAT,
1550 0, 0, 0);
1553 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
1554 vgl->vt.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, vgl->index_buffer_object);
1555 vgl->vt.EnableVertexAttribArray(prgm->aloc.VertexPosition);
1556 vgl->vt.VertexAttribPointer(prgm->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
1558 vgl->vt.UniformMatrix4fv(prgm->uloc.OrientationMatrix, 1, GL_FALSE,
1559 prgm->var.OrientationMatrix);
1560 vgl->vt.UniformMatrix4fv(prgm->uloc.ProjectionMatrix, 1, GL_FALSE,
1561 prgm->var.ProjectionMatrix);
1562 vgl->vt.UniformMatrix4fv(prgm->uloc.ZRotMatrix, 1, GL_FALSE,
1563 prgm->var.ZRotMatrix);
1564 vgl->vt.UniformMatrix4fv(prgm->uloc.YRotMatrix, 1, GL_FALSE,
1565 prgm->var.YRotMatrix);
1566 vgl->vt.UniformMatrix4fv(prgm->uloc.XRotMatrix, 1, GL_FALSE,
1567 prgm->var.XRotMatrix);
1568 vgl->vt.UniformMatrix4fv(prgm->uloc.ZoomMatrix, 1, GL_FALSE,
1569 prgm->var.ZoomMatrix);
1571 glDrawElements(GL_TRIANGLES, vgl->nb_indices, GL_UNSIGNED_SHORT, 0);
1575 static void GetTextureCropParamsForStereo(unsigned i_nbTextures,
1576 const float *stereoCoefs,
1577 const float *stereoOffsets,
1578 float *left, float *top,
1579 float *right, float *bottom)
1581 for (unsigned i = 0; i < i_nbTextures; ++i)
1583 float f_2eyesWidth = right[i] - left[i];
1584 left[i] = left[i] + f_2eyesWidth * stereoOffsets[0];
1585 right[i] = left[i] + f_2eyesWidth * stereoCoefs[0];
1587 float f_2eyesHeight = bottom[i] - top[i];
1588 top[i] = top[i] + f_2eyesHeight * stereoOffsets[1];
1589 bottom[i] = top[i] + f_2eyesHeight * stereoCoefs[1];
1593 static void TextureCropForStereo(vout_display_opengl_t *vgl,
1594 float *left, float *top,
1595 float *right, float *bottom)
1597 float stereoCoefs[2];
1598 float stereoOffsets[2];
1600 switch (vgl->fmt.multiview_mode)
1602 case MULTIVIEW_STEREO_TB:
1603 // Display only the left eye.
1604 stereoCoefs[0] = 1; stereoCoefs[1] = 0.5;
1605 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1606 GetTextureCropParamsForStereo(vgl->prgm->tc->tex_count,
1607 stereoCoefs, stereoOffsets,
1608 left, top, right, bottom);
1609 break;
1610 case MULTIVIEW_STEREO_SBS:
1611 // Display only the left eye.
1612 stereoCoefs[0] = 0.5; stereoCoefs[1] = 1;
1613 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1614 GetTextureCropParamsForStereo(vgl->prgm->tc->tex_count,
1615 stereoCoefs, stereoOffsets,
1616 left, top, right, bottom);
1617 break;
1618 default:
1619 break;
1623 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
1624 const video_format_t *source)
1626 GL_ASSERT_NOERROR();
1628 /* Why drawing here and not in Render()? Because this way, the
1629 OpenGL providers can call vout_display_opengl_Display to force redraw.
1630 Currently, the OS X provider uses it to get a smooth window resizing */
1631 glClear(GL_COLOR_BUFFER_BIT);
1633 vgl->vt.UseProgram(vgl->prgm->id);
1635 if (source->i_x_offset != vgl->last_source.i_x_offset
1636 || source->i_y_offset != vgl->last_source.i_y_offset
1637 || source->i_visible_width != vgl->last_source.i_visible_width
1638 || source->i_visible_height != vgl->last_source.i_visible_height)
1640 float left[PICTURE_PLANE_MAX];
1641 float top[PICTURE_PLANE_MAX];
1642 float right[PICTURE_PLANE_MAX];
1643 float bottom[PICTURE_PLANE_MAX];
1644 const opengl_tex_converter_t *tc = vgl->prgm->tc;
1645 for (unsigned j = 0; j < tc->tex_count; j++)
1647 float scale_w = (float)tc->texs[j].w.num / tc->texs[j].w.den
1648 / vgl->tex_width[j];
1649 float scale_h = (float)tc->texs[j].h.num / tc->texs[j].h.den
1650 / vgl->tex_height[j];
1652 /* Warning: if NPOT is not supported a larger texture is
1653 allocated. This will cause right and bottom coordinates to
1654 land on the edge of two texels with the texels to the
1655 right/bottom uninitialized by the call to
1656 glTexSubImage2D. This might cause a green line to appear on
1657 the right/bottom of the display.
1658 There are two possible solutions:
1659 - Manually mirror the edges of the texture.
1660 - Add a "-1" when computing right and bottom, however the
1661 last row/column might not be displayed at all.
1663 left[j] = (source->i_x_offset + 0 ) * scale_w;
1664 top[j] = (source->i_y_offset + 0 ) * scale_h;
1665 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
1666 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
1669 TextureCropForStereo(vgl, left, top, right, bottom);
1670 int ret = SetupCoords(vgl, left, top, right, bottom);
1671 if (ret != VLC_SUCCESS)
1672 return ret;
1674 vgl->last_source.i_x_offset = source->i_x_offset;
1675 vgl->last_source.i_y_offset = source->i_y_offset;
1676 vgl->last_source.i_visible_width = source->i_visible_width;
1677 vgl->last_source.i_visible_height = source->i_visible_height;
1679 DrawWithShaders(vgl, vgl->prgm);
1681 /* Draw the subpictures */
1682 // Change the program for overlays
1683 struct prgm *prgm = vgl->sub_prgm;
1684 GLuint program = prgm->id;
1685 opengl_tex_converter_t *tc = prgm->tc;
1686 vgl->vt.UseProgram(program);
1688 glEnable(GL_BLEND);
1689 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1691 /* We need two buffer objects for each region: for vertex and texture coordinates. */
1692 if (2 * vgl->region_count > vgl->subpicture_buffer_object_count) {
1693 if (vgl->subpicture_buffer_object_count > 0)
1694 vgl->vt.DeleteBuffers(vgl->subpicture_buffer_object_count,
1695 vgl->subpicture_buffer_object);
1696 vgl->subpicture_buffer_object_count = 0;
1698 int new_count = 2 * vgl->region_count;
1699 vgl->subpicture_buffer_object = realloc_or_free(vgl->subpicture_buffer_object, new_count * sizeof(GLuint));
1700 if (!vgl->subpicture_buffer_object)
1701 return VLC_ENOMEM;
1703 vgl->subpicture_buffer_object_count = new_count;
1704 vgl->vt.GenBuffers(vgl->subpicture_buffer_object_count,
1705 vgl->subpicture_buffer_object);
1708 vgl->vt.ActiveTexture(GL_TEXTURE0 + 0);
1709 for (int i = 0; i < vgl->region_count; i++) {
1710 gl_region_t *glr = &vgl->region[i];
1711 const GLfloat vertexCoord[] = {
1712 glr->left, glr->top,
1713 glr->left, glr->bottom,
1714 glr->right, glr->top,
1715 glr->right, glr->bottom,
1717 const GLfloat textureCoord[] = {
1718 0.0, 0.0,
1719 0.0, glr->tex_height,
1720 glr->tex_width, 0.0,
1721 glr->tex_width, glr->tex_height,
1724 assert(glr->texture != 0);
1725 vgl->vt.BindTexture(tc->tex_target, glr->texture);
1727 tc->pf_prepare_shader(tc, &glr->width, &glr->height, glr->alpha);
1729 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->subpicture_buffer_object[2 * i]);
1730 vgl->vt.BufferData(GL_ARRAY_BUFFER, sizeof(textureCoord), textureCoord, GL_STATIC_DRAW);
1731 vgl->vt.EnableVertexAttribArray(prgm->aloc.MultiTexCoord[0]);
1732 vgl->vt.VertexAttribPointer(prgm->aloc.MultiTexCoord[0], 2, GL_FLOAT,
1733 0, 0, 0);
1735 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->subpicture_buffer_object[2 * i + 1]);
1736 vgl->vt.BufferData(GL_ARRAY_BUFFER, sizeof(vertexCoord), vertexCoord, GL_STATIC_DRAW);
1737 vgl->vt.EnableVertexAttribArray(prgm->aloc.VertexPosition);
1738 vgl->vt.VertexAttribPointer(prgm->aloc.VertexPosition, 2, GL_FLOAT,
1739 0, 0, 0);
1741 vgl->vt.UniformMatrix4fv(prgm->uloc.OrientationMatrix, 1, GL_FALSE,
1742 prgm->var.OrientationMatrix);
1743 vgl->vt.UniformMatrix4fv(prgm->uloc.ProjectionMatrix, 1, GL_FALSE,
1744 prgm->var.ProjectionMatrix);
1745 vgl->vt.UniformMatrix4fv(prgm->uloc.ZRotMatrix, 1, GL_FALSE,
1746 prgm->var.ZRotMatrix);
1747 vgl->vt.UniformMatrix4fv(prgm->uloc.YRotMatrix, 1, GL_FALSE,
1748 prgm->var.YRotMatrix);
1749 vgl->vt.UniformMatrix4fv(prgm->uloc.XRotMatrix, 1, GL_FALSE,
1750 prgm->var.XRotMatrix);
1751 vgl->vt.UniformMatrix4fv(prgm->uloc.ZoomMatrix, 1, GL_FALSE,
1752 prgm->var.ZoomMatrix);
1754 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1756 glDisable(GL_BLEND);
1758 /* Display */
1759 vlc_gl_Swap(vgl->gl);
1761 GL_ASSERT_NOERROR();
1763 return VLC_SUCCESS;