opengl: use all GL symbols from the vtable
[vlc.git] / modules / video_output / opengl / vout_helper.c
blobc220e42c443776341f8c8fcdcfdfc153eff20122
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 = vgl->vt.GetError(); \
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 msg_Dbg(tc->gl, "\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 tc->vt->GenTextures(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 tc->vt->TexParameterf(tc->tex_target, GL_TEXTURE_PRIORITY, 1.0);
423 tc->vt->TexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
424 #endif
426 tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
427 tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
428 tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
429 tc->vt->TexParameteri(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 tc->vt->DeleteTextures(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 tc->vt->DeleteTextures(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 if (gl->getProcAddress == NULL) {
731 msg_Err(gl, "getProcAddress not implemented, bailing out\n");
732 return NULL;
735 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
736 if (!vgl)
737 return NULL;
739 vgl->gl = gl;
741 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
742 #define GET_PROC_ADDR_EXT(name, critical) do { \
743 vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
744 if (vgl->vt.name == NULL && critical) { \
745 msg_Err(gl, "gl"#name" symbol not found, bailing out\n"); \
746 free(vgl); \
747 return NULL; \
749 } while(0)
750 #if defined(USE_OPENGL_ES2)
751 #define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
752 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_EXT(name, false) /* optional for GLES */
753 #else
754 #define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
755 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
756 #endif
757 #define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
759 GET_PROC_ADDR_CORE(BindTexture);
760 GET_PROC_ADDR_CORE(BlendFunc);
761 GET_PROC_ADDR_CORE(Clear);
762 GET_PROC_ADDR_CORE(ClearColor);
763 GET_PROC_ADDR_CORE(DeleteTextures);
764 GET_PROC_ADDR_CORE(DepthMask);
765 GET_PROC_ADDR_CORE(Disable);
766 GET_PROC_ADDR_CORE(DrawArrays);
767 GET_PROC_ADDR_CORE(DrawElements);
768 GET_PROC_ADDR_CORE(Enable);
769 GET_PROC_ADDR_CORE(Finish);
770 GET_PROC_ADDR_CORE(Flush);
771 GET_PROC_ADDR_CORE(GenTextures);
772 GET_PROC_ADDR_CORE(GetError);
773 GET_PROC_ADDR_CORE(GetIntegerv);
774 GET_PROC_ADDR_CORE(GetString);
775 GET_PROC_ADDR_CORE(PixelStorei);
776 GET_PROC_ADDR_CORE(TexImage2D);
777 GET_PROC_ADDR_CORE(TexParameterf);
778 GET_PROC_ADDR_CORE(TexParameteri);
779 GET_PROC_ADDR_CORE(TexSubImage2D);
780 GET_PROC_ADDR_CORE(Viewport);
782 GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv);
783 GET_PROC_ADDR_CORE_GL(TexEnvf);
785 GET_PROC_ADDR(CreateShader);
786 GET_PROC_ADDR(ShaderSource);
787 GET_PROC_ADDR(CompileShader);
788 GET_PROC_ADDR(AttachShader);
789 GET_PROC_ADDR(DeleteShader);
791 GET_PROC_ADDR(GetProgramiv);
792 GET_PROC_ADDR(GetShaderiv);
793 GET_PROC_ADDR(GetProgramInfoLog);
794 GET_PROC_ADDR(GetShaderInfoLog);
796 GET_PROC_ADDR(GetUniformLocation);
797 GET_PROC_ADDR(GetAttribLocation);
798 GET_PROC_ADDR(VertexAttribPointer);
799 GET_PROC_ADDR(EnableVertexAttribArray);
800 GET_PROC_ADDR(UniformMatrix4fv);
801 GET_PROC_ADDR(UniformMatrix3fv);
802 GET_PROC_ADDR(UniformMatrix2fv);
803 GET_PROC_ADDR(Uniform4fv);
804 GET_PROC_ADDR(Uniform4f);
805 GET_PROC_ADDR(Uniform3f);
806 GET_PROC_ADDR(Uniform2f);
807 GET_PROC_ADDR(Uniform1f);
808 GET_PROC_ADDR(Uniform1i);
810 GET_PROC_ADDR(CreateProgram);
811 GET_PROC_ADDR(LinkProgram);
812 GET_PROC_ADDR(UseProgram);
813 GET_PROC_ADDR(DeleteProgram);
815 GET_PROC_ADDR(ActiveTexture);
817 GET_PROC_ADDR(GenBuffers);
818 GET_PROC_ADDR(BindBuffer);
819 GET_PROC_ADDR(BufferData);
820 GET_PROC_ADDR(DeleteBuffers);
822 GET_PROC_ADDR_OPTIONAL(BufferSubData);
823 GET_PROC_ADDR_OPTIONAL(BufferStorage);
824 GET_PROC_ADDR_OPTIONAL(MapBufferRange);
825 GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange);
826 GET_PROC_ADDR_OPTIONAL(UnmapBuffer);
827 GET_PROC_ADDR_OPTIONAL(FenceSync);
828 GET_PROC_ADDR_OPTIONAL(DeleteSync);
829 GET_PROC_ADDR_OPTIONAL(ClientWaitSync);
830 #undef GET_PROC_ADDR
832 GL_ASSERT_NOERROR();
834 const char *extensions = (const char *)vgl->vt.GetString(GL_EXTENSIONS);
835 #if !defined(USE_OPENGL_ES2)
836 const unsigned char *ogl_version = vgl->vt.GetString(GL_VERSION);
837 bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
838 if (!supports_shaders)
840 msg_Err(gl, "shaders not supported, bailing out\n");
841 free(vgl);
842 return NULL;
844 #endif
846 /* Resize the format if it is greater than the maximum texture size
847 * supported by the hardware */
848 GLint max_tex_size;
849 vgl->vt.GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
851 if ((GLint)fmt->i_width > max_tex_size ||
852 (GLint)fmt->i_height > max_tex_size)
853 ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
855 #if defined(USE_OPENGL_ES2)
856 /* OpenGL ES 2 includes support for non-power of 2 textures by specification
857 * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
858 vgl->supports_npot = true;
859 #else
860 vgl->supports_npot = HasExtension(extensions, "GL_ARB_texture_non_power_of_two") ||
861 HasExtension(extensions, "GL_APPLE_texture_2D_limited_npot");
862 #endif
864 bool b_dump_shaders = var_InheritInteger(gl, "verbose") >= 4;
866 vgl->prgm = &vgl->prgms[0];
867 vgl->sub_prgm = &vgl->prgms[1];
869 GL_ASSERT_NOERROR();
870 int ret;
871 ret = opengl_init_program(vgl, vgl->prgm, extensions, fmt, false,
872 b_dump_shaders);
873 if (ret != VLC_SUCCESS)
875 msg_Warn(gl, "could not init tex converter for %4.4s",
876 (const char *) &fmt->i_chroma);
877 free(vgl);
878 return NULL;
881 GL_ASSERT_NOERROR();
882 ret = opengl_init_program(vgl, vgl->sub_prgm, extensions, fmt, true,
883 b_dump_shaders);
884 if (ret != VLC_SUCCESS)
886 msg_Warn(gl, "could not init subpictures tex converter for %4.4s",
887 (const char *) &fmt->i_chroma);
888 opengl_deinit_program(vgl, vgl->prgm);
889 free(vgl);
890 return NULL;
892 GL_ASSERT_NOERROR();
893 /* Update the fmt to main program one */
894 vgl->fmt = vgl->prgm->tc->fmt;
895 /* The orientation is handled by the orientation matrix */
896 vgl->fmt.orientation = fmt->orientation;
898 /* Texture size */
899 const opengl_tex_converter_t *tc = vgl->prgm->tc;
900 for (unsigned j = 0; j < tc->tex_count; j++) {
901 const GLsizei w = vgl->fmt.i_visible_width * tc->texs[j].w.num
902 / tc->texs[j].w.den;
903 const GLsizei h = vgl->fmt.i_visible_height * tc->texs[j].h.num
904 / tc->texs[j].h.den;
905 if (vgl->supports_npot) {
906 vgl->tex_width[j] = w;
907 vgl->tex_height[j] = h;
908 } else {
909 vgl->tex_width[j] = GetAlignedSize(w);
910 vgl->tex_height[j] = GetAlignedSize(h);
914 /* Allocates our textures */
915 assert(!vgl->sub_prgm->tc->handle_texs_gen);
917 if (!vgl->prgm->tc->handle_texs_gen)
919 ret = GenTextures(vgl->prgm->tc, vgl->tex_width, vgl->tex_height,
920 vgl->texture);
921 if (ret != VLC_SUCCESS)
923 vout_display_opengl_Delete(vgl);
924 return NULL;
928 /* */
929 vgl->vt.Disable(GL_BLEND);
930 vgl->vt.Disable(GL_DEPTH_TEST);
931 vgl->vt.DepthMask(GL_FALSE);
932 vgl->vt.Enable(GL_CULL_FACE);
933 vgl->vt.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
934 vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
936 vgl->vt.GenBuffers(1, &vgl->vertex_buffer_object);
937 vgl->vt.GenBuffers(1, &vgl->index_buffer_object);
938 vgl->vt.GenBuffers(vgl->prgm->tc->tex_count, vgl->texture_buffer_object);
940 /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
941 int subpicture_buffer_object_count = 8;
942 vgl->subpicture_buffer_object = vlc_alloc(subpicture_buffer_object_count, sizeof(GLuint));
943 if (!vgl->subpicture_buffer_object) {
944 vout_display_opengl_Delete(vgl);
945 return NULL;
947 vgl->subpicture_buffer_object_count = subpicture_buffer_object_count;
948 vgl->vt.GenBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
950 /* */
951 vgl->region_count = 0;
952 vgl->region = NULL;
953 vgl->pool = NULL;
955 if (vgl->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
956 && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
958 vout_display_opengl_Delete(vgl);
959 return NULL;
962 *fmt = vgl->fmt;
963 if (subpicture_chromas) {
964 *subpicture_chromas = gl_subpicture_chromas;
967 GL_ASSERT_NOERROR();
968 return vgl;
971 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
973 GL_ASSERT_NOERROR();
975 /* */
976 vgl->vt.Finish();
977 vgl->vt.Flush();
979 opengl_tex_converter_t *tc = vgl->prgm->tc;
980 if (!tc->handle_texs_gen)
981 DelTextures(tc, vgl->texture);
983 tc = vgl->sub_prgm->tc;
984 for (int i = 0; i < vgl->region_count; i++)
986 if (vgl->region[i].texture)
987 DelTextures(tc, &vgl->region[i].texture);
989 free(vgl->region);
991 vgl->vt.DeleteBuffers(1, &vgl->vertex_buffer_object);
992 vgl->vt.DeleteBuffers(1, &vgl->index_buffer_object);
993 vgl->vt.DeleteBuffers(vgl->prgm->tc->tex_count, vgl->texture_buffer_object);
995 if (vgl->subpicture_buffer_object_count > 0)
996 vgl->vt.DeleteBuffers(vgl->subpicture_buffer_object_count,
997 vgl->subpicture_buffer_object);
998 free(vgl->subpicture_buffer_object);
1000 if (vgl->pool)
1001 picture_pool_Release(vgl->pool);
1002 opengl_deinit_program(vgl, vgl->prgm);
1003 opengl_deinit_program(vgl, vgl->sub_prgm);
1005 GL_ASSERT_NOERROR();
1007 free(vgl);
1010 static void UpdateZ(vout_display_opengl_t *vgl)
1012 /* Do trigonometry to calculate the minimal z value
1013 * that will allow us to zoom out without seeing the outside of the
1014 * sphere (black borders). */
1015 float tan_fovx_2 = tanf(vgl->f_fovx / 2);
1016 float tan_fovy_2 = tanf(vgl->f_fovy / 2);
1017 float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
1018 tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
1020 /* The FOV value above which z is dynamically calculated. */
1021 const float z_thresh = 90.f;
1023 if (vgl->f_fovx <= z_thresh * M_PI / 180)
1024 vgl->f_z = 0;
1025 else
1027 float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
1028 vgl->f_z = f * vgl->f_fovx - f * z_thresh * M_PI / 180;
1029 if (vgl->f_z < z_min)
1030 vgl->f_z = z_min;
1034 static void UpdateFOVy(vout_display_opengl_t *vgl)
1036 vgl->f_fovy = 2 * atanf(tanf(vgl->f_fovx / 2) / vgl->f_sar);
1039 int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
1040 const vlc_viewpoint_t *p_vp)
1042 #define RAD(d) ((float) ((d) * M_PI / 180.f))
1043 float f_fovx = RAD(p_vp->fov);
1044 if (f_fovx > FIELD_OF_VIEW_DEGREES_MAX * M_PI / 180 + 0.001f
1045 || f_fovx < -0.001f)
1046 return VLC_EBADVAR;
1048 vgl->f_teta = RAD(p_vp->yaw) - (float) M_PI_2;
1049 vgl->f_phi = RAD(p_vp->pitch);
1050 vgl->f_roll = RAD(p_vp->roll);
1053 if (fabsf(f_fovx - vgl->f_fovx) >= 0.001f)
1055 /* FOVx has changed. */
1056 vgl->f_fovx = f_fovx;
1057 UpdateFOVy(vgl);
1058 UpdateZ(vgl);
1060 getViewpointMatrixes(vgl, vgl->fmt.projection_mode, vgl->prgm);
1062 return VLC_SUCCESS;
1063 #undef RAD
1067 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t *vgl,
1068 float f_sar)
1070 /* Each time the window size changes, we must recompute the minimum zoom
1071 * since the aspect ration changes.
1072 * We must also set the new current zoom value. */
1073 vgl->f_sar = f_sar;
1074 UpdateFOVy(vgl);
1075 UpdateZ(vgl);
1076 getViewpointMatrixes(vgl, vgl->fmt.projection_mode, vgl->prgm);
1079 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
1081 GL_ASSERT_NOERROR();
1083 if (vgl->pool)
1084 return vgl->pool;
1086 opengl_tex_converter_t *tc = vgl->prgm->tc;
1087 requested_count = __MIN(VLCGL_PICTURE_MAX, requested_count);
1088 /* Allocate with tex converter pool callback if it exists */
1089 if (tc->pf_get_pool != NULL)
1091 vgl->pool = tc->pf_get_pool(tc, requested_count);
1092 if (!vgl->pool)
1093 goto error;
1094 return vgl->pool;
1097 /* Allocate our pictures */
1098 picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
1099 unsigned count;
1100 for (count = 0; count < requested_count; count++)
1102 picture[count] = picture_NewFromFormat(&vgl->fmt);
1103 if (!picture[count])
1104 break;
1106 if (count <= 0)
1107 goto error;
1109 /* Wrap the pictures into a pool */
1110 vgl->pool = picture_pool_New(count, picture);
1111 if (!vgl->pool)
1113 for (unsigned i = 0; i < count; i++)
1114 picture_Release(picture[i]);
1115 goto error;
1118 GL_ASSERT_NOERROR();
1119 return vgl->pool;
1121 error:
1122 DelTextures(tc, vgl->texture);
1123 return NULL;
1126 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
1127 picture_t *picture, subpicture_t *subpicture)
1129 GL_ASSERT_NOERROR();
1131 opengl_tex_converter_t *tc = vgl->prgm->tc;
1133 /* Update the texture */
1134 int ret = tc->pf_update(tc, vgl->texture, vgl->tex_width, vgl->tex_height,
1135 picture, NULL);
1136 if (ret != VLC_SUCCESS)
1137 return ret;
1139 int last_count = vgl->region_count;
1140 gl_region_t *last = vgl->region;
1142 vgl->region_count = 0;
1143 vgl->region = NULL;
1145 tc = vgl->sub_prgm->tc;
1146 if (subpicture) {
1148 int count = 0;
1149 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1150 count++;
1152 vgl->region_count = count;
1153 vgl->region = calloc(count, sizeof(*vgl->region));
1155 int i = 0;
1156 for (subpicture_region_t *r = subpicture->p_region;
1157 r && ret == VLC_SUCCESS; r = r->p_next, i++) {
1158 gl_region_t *glr = &vgl->region[i];
1160 glr->width = r->fmt.i_visible_width;
1161 glr->height = r->fmt.i_visible_height;
1162 if (!vgl->supports_npot) {
1163 glr->width = GetAlignedSize(glr->width);
1164 glr->height = GetAlignedSize(glr->height);
1165 glr->tex_width = (float) r->fmt.i_visible_width / glr->width;
1166 glr->tex_height = (float) r->fmt.i_visible_height / glr->height;
1167 } else {
1168 glr->tex_width = 1.0;
1169 glr->tex_height = 1.0;
1171 glr->alpha = (float)subpicture->i_alpha * r->i_alpha / 255 / 255;
1172 glr->left = 2.0 * (r->i_x ) / subpicture->i_original_picture_width - 1.0;
1173 glr->top = -2.0 * (r->i_y ) / subpicture->i_original_picture_height + 1.0;
1174 glr->right = 2.0 * (r->i_x + r->fmt.i_visible_width ) / subpicture->i_original_picture_width - 1.0;
1175 glr->bottom = -2.0 * (r->i_y + r->fmt.i_visible_height) / subpicture->i_original_picture_height + 1.0;
1177 glr->texture = 0;
1178 /* Try to recycle the textures allocated by the previous
1179 call to this function. */
1180 for (int j = 0; j < last_count; j++) {
1181 if (last[j].texture &&
1182 last[j].width == glr->width &&
1183 last[j].height == glr->height) {
1184 glr->texture = last[j].texture;
1185 memset(&last[j], 0, sizeof(last[j]));
1186 break;
1190 const size_t pixels_offset =
1191 r->fmt.i_y_offset * r->p_picture->p->i_pitch +
1192 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
1193 if (!glr->texture)
1195 /* Could not recycle a previous texture, generate a new one. */
1196 ret = GenTextures(tc, &glr->width, &glr->height, &glr->texture);
1197 if (ret != VLC_SUCCESS)
1198 continue;
1200 ret = tc->pf_update(tc, &glr->texture, &glr->width, &glr->height,
1201 r->p_picture, &pixels_offset);
1204 for (int i = 0; i < last_count; i++) {
1205 if (last[i].texture)
1206 DelTextures(tc, &last[i].texture);
1208 free(last);
1210 VLC_UNUSED(subpicture);
1212 GL_ASSERT_NOERROR();
1213 return ret;
1216 static int BuildSphere(unsigned nbPlanes,
1217 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1218 GLushort **indices, unsigned *nbIndices,
1219 const float *left, const float *top,
1220 const float *right, const float *bottom)
1222 unsigned nbLatBands = 128;
1223 unsigned nbLonBands = 128;
1225 *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
1226 *nbIndices = nbLatBands * nbLonBands * 3 * 2;
1228 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1229 if (*vertexCoord == NULL)
1230 return VLC_ENOMEM;
1231 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1232 if (*textureCoord == NULL)
1234 free(*vertexCoord);
1235 return VLC_ENOMEM;
1237 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1238 if (*indices == NULL)
1240 free(*textureCoord);
1241 free(*vertexCoord);
1242 return VLC_ENOMEM;
1245 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
1246 float theta = lat * (float) M_PI / nbLatBands;
1247 float sinTheta, cosTheta;
1249 sincosf(theta, &sinTheta, &cosTheta);
1251 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
1252 float phi = lon * 2 * (float) M_PI / nbLonBands;
1253 float sinPhi, cosPhi;
1255 sincosf(phi, &sinPhi, &cosPhi);
1257 float x = cosPhi * sinTheta;
1258 float y = cosTheta;
1259 float z = sinPhi * sinTheta;
1261 unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
1262 (*vertexCoord)[off1] = SPHERE_RADIUS * x;
1263 (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
1264 (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
1266 for (unsigned p = 0; p < nbPlanes; ++p)
1268 unsigned off2 = (p * (nbLatBands + 1) * (nbLonBands + 1)
1269 + lat * (nbLonBands + 1) + lon) * 2;
1270 float width = right[p] - left[p];
1271 float height = bottom[p] - top[p];
1272 float u = (float)lon / nbLonBands * width;
1273 float v = (float)lat / nbLatBands * height;
1274 (*textureCoord)[off2] = u;
1275 (*textureCoord)[off2 + 1] = v;
1280 for (unsigned lat = 0; lat < nbLatBands; lat++) {
1281 for (unsigned lon = 0; lon < nbLonBands; lon++) {
1282 unsigned first = (lat * (nbLonBands + 1)) + lon;
1283 unsigned second = first + nbLonBands + 1;
1285 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
1287 (*indices)[off] = first;
1288 (*indices)[off + 1] = second;
1289 (*indices)[off + 2] = first + 1;
1291 (*indices)[off + 3] = second;
1292 (*indices)[off + 4] = second + 1;
1293 (*indices)[off + 5] = first + 1;
1297 return VLC_SUCCESS;
1301 static int BuildCube(unsigned nbPlanes,
1302 float padW, float padH,
1303 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1304 GLushort **indices, unsigned *nbIndices,
1305 const float *left, const float *top,
1306 const float *right, const float *bottom)
1308 *nbVertices = 4 * 6;
1309 *nbIndices = 6 * 6;
1311 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1312 if (*vertexCoord == NULL)
1313 return VLC_ENOMEM;
1314 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1315 if (*textureCoord == NULL)
1317 free(*vertexCoord);
1318 return VLC_ENOMEM;
1320 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1321 if (*indices == NULL)
1323 free(*textureCoord);
1324 free(*vertexCoord);
1325 return VLC_ENOMEM;
1328 static const GLfloat coord[] = {
1329 -1.0, 1.0, -1.0f, // front
1330 -1.0, -1.0, -1.0f,
1331 1.0, 1.0, -1.0f,
1332 1.0, -1.0, -1.0f,
1334 -1.0, 1.0, 1.0f, // back
1335 -1.0, -1.0, 1.0f,
1336 1.0, 1.0, 1.0f,
1337 1.0, -1.0, 1.0f,
1339 -1.0, 1.0, -1.0f, // left
1340 -1.0, -1.0, -1.0f,
1341 -1.0, 1.0, 1.0f,
1342 -1.0, -1.0, 1.0f,
1344 1.0f, 1.0, -1.0f, // right
1345 1.0f, -1.0, -1.0f,
1346 1.0f, 1.0, 1.0f,
1347 1.0f, -1.0, 1.0f,
1349 -1.0, -1.0, 1.0f, // bottom
1350 -1.0, -1.0, -1.0f,
1351 1.0, -1.0, 1.0f,
1352 1.0, -1.0, -1.0f,
1354 -1.0, 1.0, 1.0f, // top
1355 -1.0, 1.0, -1.0f,
1356 1.0, 1.0, 1.0f,
1357 1.0, 1.0, -1.0f,
1360 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1362 for (unsigned p = 0; p < nbPlanes; ++p)
1364 float width = right[p] - left[p];
1365 float height = bottom[p] - top[p];
1367 float col[] = {left[p],
1368 left[p] + width * 1.f/3,
1369 left[p] + width * 2.f/3,
1370 left[p] + width};
1372 float row[] = {top[p],
1373 top[p] + height * 1.f/2,
1374 top[p] + height};
1376 const GLfloat tex[] = {
1377 col[1] + padW, row[1] + padH, // front
1378 col[1] + padW, row[2] - padH,
1379 col[2] - padW, row[1] + padH,
1380 col[2] - padW, row[2] - padH,
1382 col[3] - padW, row[1] + padH, // back
1383 col[3] - padW, row[2] - padH,
1384 col[2] + padW, row[1] + padH,
1385 col[2] + padW, row[2] - padH,
1387 col[2] - padW, row[0] + padH, // left
1388 col[2] - padW, row[1] - padH,
1389 col[1] + padW, row[0] + padH,
1390 col[1] + padW, row[1] - padH,
1392 col[0] + padW, row[0] + padH, // right
1393 col[0] + padW, row[1] - padH,
1394 col[1] - padW, row[0] + padH,
1395 col[1] - padW, row[1] - padH,
1397 col[0] + padW, row[2] - padH, // bottom
1398 col[0] + padW, row[1] + padH,
1399 col[1] - padW, row[2] - padH,
1400 col[1] - padW, row[1] + padH,
1402 col[2] + padW, row[0] + padH, // top
1403 col[2] + padW, row[1] - padH,
1404 col[3] - padW, row[0] + padH,
1405 col[3] - padW, row[1] - padH,
1408 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1409 *nbVertices * 2 * sizeof(GLfloat));
1412 const GLushort ind[] = {
1413 0, 1, 2, 2, 1, 3, // front
1414 6, 7, 4, 4, 7, 5, // back
1415 10, 11, 8, 8, 11, 9, // left
1416 12, 13, 14, 14, 13, 15, // right
1417 18, 19, 16, 16, 19, 17, // bottom
1418 20, 21, 22, 22, 21, 23, // top
1421 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1423 return VLC_SUCCESS;
1426 static int BuildRectangle(unsigned nbPlanes,
1427 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1428 GLushort **indices, unsigned *nbIndices,
1429 const float *left, const float *top,
1430 const float *right, const float *bottom)
1432 *nbVertices = 4;
1433 *nbIndices = 6;
1435 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1436 if (*vertexCoord == NULL)
1437 return VLC_ENOMEM;
1438 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1439 if (*textureCoord == NULL)
1441 free(*vertexCoord);
1442 return VLC_ENOMEM;
1444 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1445 if (*indices == NULL)
1447 free(*textureCoord);
1448 free(*vertexCoord);
1449 return VLC_ENOMEM;
1452 static const GLfloat coord[] = {
1453 -1.0, 1.0, -1.0f,
1454 -1.0, -1.0, -1.0f,
1455 1.0, 1.0, -1.0f,
1456 1.0, -1.0, -1.0f
1459 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1461 for (unsigned p = 0; p < nbPlanes; ++p)
1463 const GLfloat tex[] = {
1464 left[p], top[p],
1465 left[p], bottom[p],
1466 right[p], top[p],
1467 right[p], bottom[p]
1470 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1471 *nbVertices * 2 * sizeof(GLfloat));
1474 const GLushort ind[] = {
1475 0, 1, 2,
1476 2, 1, 3
1479 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1481 return VLC_SUCCESS;
1484 static int SetupCoords(vout_display_opengl_t *vgl,
1485 const float *left, const float *top,
1486 const float *right, const float *bottom)
1488 GLfloat *vertexCoord, *textureCoord;
1489 GLushort *indices;
1490 unsigned nbVertices, nbIndices;
1492 int i_ret;
1493 switch (vgl->fmt.projection_mode)
1495 case PROJECTION_MODE_RECTANGULAR:
1496 i_ret = BuildRectangle(vgl->prgm->tc->tex_count,
1497 &vertexCoord, &textureCoord, &nbVertices,
1498 &indices, &nbIndices,
1499 left, top, right, bottom);
1500 break;
1501 case PROJECTION_MODE_EQUIRECTANGULAR:
1502 i_ret = BuildSphere(vgl->prgm->tc->tex_count,
1503 &vertexCoord, &textureCoord, &nbVertices,
1504 &indices, &nbIndices,
1505 left, top, right, bottom);
1506 break;
1507 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
1508 i_ret = BuildCube(vgl->prgm->tc->tex_count,
1509 (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_width,
1510 (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_height,
1511 &vertexCoord, &textureCoord, &nbVertices,
1512 &indices, &nbIndices,
1513 left, top, right, bottom);
1514 break;
1515 default:
1516 i_ret = VLC_EGENERIC;
1517 break;
1520 if (i_ret != VLC_SUCCESS)
1521 return i_ret;
1523 for (unsigned j = 0; j < vgl->prgm->tc->tex_count; j++)
1525 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
1526 vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
1527 textureCoord + j * nbVertices * 2, GL_STATIC_DRAW);
1530 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
1531 vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
1532 vertexCoord, GL_STATIC_DRAW);
1534 vgl->vt.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, vgl->index_buffer_object);
1535 vgl->vt.BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
1536 indices, GL_STATIC_DRAW);
1538 free(textureCoord);
1539 free(vertexCoord);
1540 free(indices);
1542 vgl->nb_indices = nbIndices;
1544 return VLC_SUCCESS;
1547 static void DrawWithShaders(vout_display_opengl_t *vgl, struct prgm *prgm)
1549 opengl_tex_converter_t *tc = prgm->tc;
1550 tc->pf_prepare_shader(tc, vgl->tex_width, vgl->tex_height, 1.0f);
1552 for (unsigned j = 0; j < vgl->prgm->tc->tex_count; j++) {
1553 assert(vgl->texture[j] != 0);
1554 vgl->vt.ActiveTexture(GL_TEXTURE0+j);
1555 vgl->vt.BindTexture(tc->tex_target, vgl->texture[j]);
1557 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
1559 assert(prgm->aloc.MultiTexCoord[j] != -1);
1560 vgl->vt.EnableVertexAttribArray(prgm->aloc.MultiTexCoord[j]);
1561 vgl->vt.VertexAttribPointer(prgm->aloc.MultiTexCoord[j], 2, GL_FLOAT,
1562 0, 0, 0);
1565 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
1566 vgl->vt.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, vgl->index_buffer_object);
1567 vgl->vt.EnableVertexAttribArray(prgm->aloc.VertexPosition);
1568 vgl->vt.VertexAttribPointer(prgm->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
1570 vgl->vt.UniformMatrix4fv(prgm->uloc.OrientationMatrix, 1, GL_FALSE,
1571 prgm->var.OrientationMatrix);
1572 vgl->vt.UniformMatrix4fv(prgm->uloc.ProjectionMatrix, 1, GL_FALSE,
1573 prgm->var.ProjectionMatrix);
1574 vgl->vt.UniformMatrix4fv(prgm->uloc.ZRotMatrix, 1, GL_FALSE,
1575 prgm->var.ZRotMatrix);
1576 vgl->vt.UniformMatrix4fv(prgm->uloc.YRotMatrix, 1, GL_FALSE,
1577 prgm->var.YRotMatrix);
1578 vgl->vt.UniformMatrix4fv(prgm->uloc.XRotMatrix, 1, GL_FALSE,
1579 prgm->var.XRotMatrix);
1580 vgl->vt.UniformMatrix4fv(prgm->uloc.ZoomMatrix, 1, GL_FALSE,
1581 prgm->var.ZoomMatrix);
1583 vgl->vt.DrawElements(GL_TRIANGLES, vgl->nb_indices, GL_UNSIGNED_SHORT, 0);
1587 static void GetTextureCropParamsForStereo(unsigned i_nbTextures,
1588 const float *stereoCoefs,
1589 const float *stereoOffsets,
1590 float *left, float *top,
1591 float *right, float *bottom)
1593 for (unsigned i = 0; i < i_nbTextures; ++i)
1595 float f_2eyesWidth = right[i] - left[i];
1596 left[i] = left[i] + f_2eyesWidth * stereoOffsets[0];
1597 right[i] = left[i] + f_2eyesWidth * stereoCoefs[0];
1599 float f_2eyesHeight = bottom[i] - top[i];
1600 top[i] = top[i] + f_2eyesHeight * stereoOffsets[1];
1601 bottom[i] = top[i] + f_2eyesHeight * stereoCoefs[1];
1605 static void TextureCropForStereo(vout_display_opengl_t *vgl,
1606 float *left, float *top,
1607 float *right, float *bottom)
1609 float stereoCoefs[2];
1610 float stereoOffsets[2];
1612 switch (vgl->fmt.multiview_mode)
1614 case MULTIVIEW_STEREO_TB:
1615 // Display only the left eye.
1616 stereoCoefs[0] = 1; stereoCoefs[1] = 0.5;
1617 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1618 GetTextureCropParamsForStereo(vgl->prgm->tc->tex_count,
1619 stereoCoefs, stereoOffsets,
1620 left, top, right, bottom);
1621 break;
1622 case MULTIVIEW_STEREO_SBS:
1623 // Display only the left eye.
1624 stereoCoefs[0] = 0.5; stereoCoefs[1] = 1;
1625 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1626 GetTextureCropParamsForStereo(vgl->prgm->tc->tex_count,
1627 stereoCoefs, stereoOffsets,
1628 left, top, right, bottom);
1629 break;
1630 default:
1631 break;
1635 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
1636 const video_format_t *source)
1638 GL_ASSERT_NOERROR();
1640 /* Why drawing here and not in Render()? Because this way, the
1641 OpenGL providers can call vout_display_opengl_Display to force redraw.
1642 Currently, the OS X provider uses it to get a smooth window resizing */
1643 vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
1645 vgl->vt.UseProgram(vgl->prgm->id);
1647 if (source->i_x_offset != vgl->last_source.i_x_offset
1648 || source->i_y_offset != vgl->last_source.i_y_offset
1649 || source->i_visible_width != vgl->last_source.i_visible_width
1650 || source->i_visible_height != vgl->last_source.i_visible_height)
1652 float left[PICTURE_PLANE_MAX];
1653 float top[PICTURE_PLANE_MAX];
1654 float right[PICTURE_PLANE_MAX];
1655 float bottom[PICTURE_PLANE_MAX];
1656 const opengl_tex_converter_t *tc = vgl->prgm->tc;
1657 for (unsigned j = 0; j < tc->tex_count; j++)
1659 float scale_w = (float)tc->texs[j].w.num / tc->texs[j].w.den
1660 / vgl->tex_width[j];
1661 float scale_h = (float)tc->texs[j].h.num / tc->texs[j].h.den
1662 / vgl->tex_height[j];
1664 /* Warning: if NPOT is not supported a larger texture is
1665 allocated. This will cause right and bottom coordinates to
1666 land on the edge of two texels with the texels to the
1667 right/bottom uninitialized by the call to
1668 glTexSubImage2D. This might cause a green line to appear on
1669 the right/bottom of the display.
1670 There are two possible solutions:
1671 - Manually mirror the edges of the texture.
1672 - Add a "-1" when computing right and bottom, however the
1673 last row/column might not be displayed at all.
1675 left[j] = (source->i_x_offset + 0 ) * scale_w;
1676 top[j] = (source->i_y_offset + 0 ) * scale_h;
1677 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
1678 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
1681 TextureCropForStereo(vgl, left, top, right, bottom);
1682 int ret = SetupCoords(vgl, left, top, right, bottom);
1683 if (ret != VLC_SUCCESS)
1684 return ret;
1686 vgl->last_source.i_x_offset = source->i_x_offset;
1687 vgl->last_source.i_y_offset = source->i_y_offset;
1688 vgl->last_source.i_visible_width = source->i_visible_width;
1689 vgl->last_source.i_visible_height = source->i_visible_height;
1691 DrawWithShaders(vgl, vgl->prgm);
1693 /* Draw the subpictures */
1694 // Change the program for overlays
1695 struct prgm *prgm = vgl->sub_prgm;
1696 GLuint program = prgm->id;
1697 opengl_tex_converter_t *tc = prgm->tc;
1698 vgl->vt.UseProgram(program);
1700 vgl->vt.Enable(GL_BLEND);
1701 vgl->vt.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1703 /* We need two buffer objects for each region: for vertex and texture coordinates. */
1704 if (2 * vgl->region_count > vgl->subpicture_buffer_object_count) {
1705 if (vgl->subpicture_buffer_object_count > 0)
1706 vgl->vt.DeleteBuffers(vgl->subpicture_buffer_object_count,
1707 vgl->subpicture_buffer_object);
1708 vgl->subpicture_buffer_object_count = 0;
1710 int new_count = 2 * vgl->region_count;
1711 vgl->subpicture_buffer_object = realloc_or_free(vgl->subpicture_buffer_object, new_count * sizeof(GLuint));
1712 if (!vgl->subpicture_buffer_object)
1713 return VLC_ENOMEM;
1715 vgl->subpicture_buffer_object_count = new_count;
1716 vgl->vt.GenBuffers(vgl->subpicture_buffer_object_count,
1717 vgl->subpicture_buffer_object);
1720 vgl->vt.ActiveTexture(GL_TEXTURE0 + 0);
1721 for (int i = 0; i < vgl->region_count; i++) {
1722 gl_region_t *glr = &vgl->region[i];
1723 const GLfloat vertexCoord[] = {
1724 glr->left, glr->top,
1725 glr->left, glr->bottom,
1726 glr->right, glr->top,
1727 glr->right, glr->bottom,
1729 const GLfloat textureCoord[] = {
1730 0.0, 0.0,
1731 0.0, glr->tex_height,
1732 glr->tex_width, 0.0,
1733 glr->tex_width, glr->tex_height,
1736 assert(glr->texture != 0);
1737 vgl->vt.BindTexture(tc->tex_target, glr->texture);
1739 tc->pf_prepare_shader(tc, &glr->width, &glr->height, glr->alpha);
1741 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->subpicture_buffer_object[2 * i]);
1742 vgl->vt.BufferData(GL_ARRAY_BUFFER, sizeof(textureCoord), textureCoord, GL_STATIC_DRAW);
1743 vgl->vt.EnableVertexAttribArray(prgm->aloc.MultiTexCoord[0]);
1744 vgl->vt.VertexAttribPointer(prgm->aloc.MultiTexCoord[0], 2, GL_FLOAT,
1745 0, 0, 0);
1747 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->subpicture_buffer_object[2 * i + 1]);
1748 vgl->vt.BufferData(GL_ARRAY_BUFFER, sizeof(vertexCoord), vertexCoord, GL_STATIC_DRAW);
1749 vgl->vt.EnableVertexAttribArray(prgm->aloc.VertexPosition);
1750 vgl->vt.VertexAttribPointer(prgm->aloc.VertexPosition, 2, GL_FLOAT,
1751 0, 0, 0);
1753 vgl->vt.UniformMatrix4fv(prgm->uloc.OrientationMatrix, 1, GL_FALSE,
1754 prgm->var.OrientationMatrix);
1755 vgl->vt.UniformMatrix4fv(prgm->uloc.ProjectionMatrix, 1, GL_FALSE,
1756 prgm->var.ProjectionMatrix);
1757 vgl->vt.UniformMatrix4fv(prgm->uloc.ZRotMatrix, 1, GL_FALSE,
1758 prgm->var.ZRotMatrix);
1759 vgl->vt.UniformMatrix4fv(prgm->uloc.YRotMatrix, 1, GL_FALSE,
1760 prgm->var.YRotMatrix);
1761 vgl->vt.UniformMatrix4fv(prgm->uloc.XRotMatrix, 1, GL_FALSE,
1762 prgm->var.XRotMatrix);
1763 vgl->vt.UniformMatrix4fv(prgm->uloc.ZoomMatrix, 1, GL_FALSE,
1764 prgm->var.ZoomMatrix);
1766 vgl->vt.DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1768 vgl->vt.Disable(GL_BLEND);
1770 /* Display */
1771 vlc_gl_Swap(vgl->gl);
1773 GL_ASSERT_NOERROR();
1775 return VLC_SUCCESS;