1abac866f66df58728857d68cda335464ba0bd39
[vlc.git] / modules / video_output / opengl / vout_helper.c
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
6  *
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>
13  *
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.
18  *
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.
23  *
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
31
32 #include <assert.h>
33 #include <math.h>
34
35 #include <vlc_common.h>
36 #include <vlc_subpicture.h>
37 #include <vlc_opengl.h>
38 #include <vlc_modules.h>
39 #include <vlc_vout.h>
40 #include <vlc_viewpoint.h>
41
42 #include "gl_util.h"
43 #include "vout_helper.h"
44 #include "internal.h"
45 #include "sub_renderer.h"
46
47 #define SPHERE_RADIUS 1.f
48
49 struct prgm
50 {
51     GLuint id;
52     struct vlc_gl_renderer *renderer;
53
54     struct {
55         GLfloat OrientationMatrix[16];
56         GLfloat ProjectionMatrix[16];
57         GLfloat ZoomMatrix[16];
58         GLfloat ViewMatrix[16];
59     } var;
60
61     struct { /* UniformLocation */
62         GLint TransformMatrix;
63         GLint OrientationMatrix;
64         GLint ProjectionMatrix;
65         GLint ViewMatrix;
66         GLint ZoomMatrix;
67     } uloc;
68     struct { /* AttribLocation */
69         GLint MultiTexCoord[3];
70         GLint VertexPosition;
71     } aloc;
72 };
73
74 struct vout_display_opengl_t {
75
76     vlc_gl_t   *gl;
77     opengl_vtable_t vt;
78
79     video_format_t fmt;
80
81     GLsizei    tex_width[PICTURE_PLANE_MAX];
82     GLsizei    tex_height[PICTURE_PLANE_MAX];
83
84     GLuint     texture[PICTURE_PLANE_MAX];
85
86     struct prgm prgm;
87
88     unsigned nb_indices;
89     GLuint vertex_buffer_object;
90     GLuint index_buffer_object;
91     GLuint texture_buffer_object[PICTURE_PLANE_MAX];
92
93     struct {
94         unsigned int i_x_offset;
95         unsigned int i_y_offset;
96         unsigned int i_visible_width;
97         unsigned int i_visible_height;
98     } last_source;
99
100     /* View point */
101     vlc_viewpoint_t vp;
102     float f_teta;
103     float f_phi;
104     float f_roll;
105     float f_fovx; /* f_fovx and f_fovy are linked but we keep both */
106     float f_fovy; /* to avoid recalculating them when needed.      */
107     float f_z;    /* Position of the camera on the shpere radius vector */
108     float f_sar;
109
110     struct vlc_gl_sub_renderer *sub_renderer;
111 };
112
113 static const vlc_fourcc_t gl_subpicture_chromas[] = {
114     VLC_CODEC_RGBA,
115     0
116 };
117
118 static const GLfloat identity[] = {
119     1.0f, 0.0f, 0.0f, 0.0f,
120     0.0f, 1.0f, 0.0f, 0.0f,
121     0.0f, 0.0f, 1.0f, 0.0f,
122     0.0f, 0.0f, 0.0f, 1.0f
123 };
124
125 static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {
126
127     const GLfloat m[] = {
128         /* x   y     z     w */
129         1.0f, 0.0f, 0.0f, 0.0f,
130         0.0f, 1.0f, 0.0f, 0.0f,
131         0.0f, 0.0f, 1.0f, 0.0f,
132         0.0f, 0.0f, zoom, 1.0f
133     };
134
135     memcpy(matrix, m, sizeof(m));
136 }
137
138 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
139 static void getProjectionMatrix(float sar, float fovy, GLfloat matrix[static 16]) {
140
141     float zFar  = 1000;
142     float zNear = 0.01;
143
144     float f = 1.f / tanf(fovy / 2.f);
145
146     const GLfloat m[] = {
147         f / sar, 0.f,                   0.f,                0.f,
148         0.f,     f,                     0.f,                0.f,
149         0.f,     0.f,     (zNear + zFar) / (zNear - zFar), -1.f,
150         0.f,     0.f, (2 * zNear * zFar) / (zNear - zFar),  0.f};
151
152      memcpy(matrix, m, sizeof(m));
153 }
154
155 static void getViewpointMatrixes(vout_display_opengl_t *vgl,
156                                  video_projection_mode_t projection_mode,
157                                  struct prgm *prgm)
158 {
159     if (projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
160         || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
161     {
162         getProjectionMatrix(vgl->f_sar, vgl->f_fovy, prgm->var.ProjectionMatrix);
163         getZoomMatrix(vgl->f_z, prgm->var.ZoomMatrix);
164
165         /* vgl->vp has been reversed and is a world transform */
166         vlc_viewpoint_to_4x4(&vgl->vp, prgm->var.ViewMatrix);
167     }
168     else
169     {
170         memcpy(prgm->var.ProjectionMatrix, identity, sizeof(identity));
171         memcpy(prgm->var.ZoomMatrix, identity, sizeof(identity));
172         memcpy(prgm->var.ViewMatrix, identity, sizeof(identity));
173     }
174
175 }
176
177 static void getOrientationTransformMatrix(video_orientation_t orientation,
178                                           GLfloat matrix[static 16])
179 {
180     memcpy(matrix, identity, sizeof(identity));
181
182     const int k_cos_pi = -1;
183     const int k_cos_pi_2 = 0;
184     const int k_cos_n_pi_2 = 0;
185
186     const int k_sin_pi = 0;
187     const int k_sin_pi_2 = 1;
188     const int k_sin_n_pi_2 = -1;
189
190     switch (orientation) {
191
192         case ORIENT_ROTATED_90:
193             matrix[0 * 4 + 0] = k_cos_pi_2;
194             matrix[0 * 4 + 1] = -k_sin_pi_2;
195             matrix[1 * 4 + 0] = k_sin_pi_2;
196             matrix[1 * 4 + 1] = k_cos_pi_2;
197             matrix[3 * 4 + 1] = 1;
198             break;
199         case ORIENT_ROTATED_180:
200             matrix[0 * 4 + 0] = k_cos_pi;
201             matrix[0 * 4 + 1] = -k_sin_pi;
202             matrix[1 * 4 + 0] = k_sin_pi;
203             matrix[1 * 4 + 1] = k_cos_pi;
204             matrix[3 * 4 + 0] = 1;
205             matrix[3 * 4 + 1] = 1;
206             break;
207         case ORIENT_ROTATED_270:
208             matrix[0 * 4 + 0] = k_cos_n_pi_2;
209             matrix[0 * 4 + 1] = -k_sin_n_pi_2;
210             matrix[1 * 4 + 0] = k_sin_n_pi_2;
211             matrix[1 * 4 + 1] = k_cos_n_pi_2;
212             matrix[3 * 4 + 0] = 1;
213             break;
214         case ORIENT_HFLIPPED:
215             matrix[0 * 4 + 0] = -1;
216             matrix[3 * 4 + 0] = 1;
217             break;
218         case ORIENT_VFLIPPED:
219             matrix[1 * 4 + 1] = -1;
220             matrix[3 * 4 + 1] = 1;
221             break;
222         case ORIENT_TRANSPOSED:
223             matrix[0 * 4 + 0] = 0;
224             matrix[1 * 4 + 1] = 0;
225             matrix[2 * 4 + 2] = -1;
226             matrix[0 * 4 + 1] = 1;
227             matrix[1 * 4 + 0] = 1;
228             break;
229         case ORIENT_ANTI_TRANSPOSED:
230             matrix[0 * 4 + 0] = 0;
231             matrix[1 * 4 + 1] = 0;
232             matrix[2 * 4 + 2] = -1;
233             matrix[0 * 4 + 1] = -1;
234             matrix[1 * 4 + 0] = -1;
235             matrix[3 * 4 + 0] = 1;
236             matrix[3 * 4 + 1] = 1;
237             break;
238         default:
239             break;
240     }
241 }
242
243 static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer,
244                                 unsigned plane_count)
245 {
246     const opengl_vtable_t *vt = renderer->vt;
247
248     /* Basic vertex shader */
249     static const char *template =
250         "#version %u\n"
251         "varying vec2 TexCoord0;\n"
252         "attribute vec4 MultiTexCoord0;\n"
253         "%s%s"
254         "attribute vec3 VertexPosition;\n"
255         "uniform mat4 TransformMatrix;\n"
256         "uniform mat4 OrientationMatrix;\n"
257         "uniform mat4 ProjectionMatrix;\n"
258         "uniform mat4 ZoomMatrix;\n"
259         "uniform mat4 ViewMatrix;\n"
260         "void main() {\n"
261         " TexCoord0 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord0).st;\n"
262         "%s%s"
263         " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
264         "               * vec4(VertexPosition, 1.0);\n"
265         "}";
266
267     const char *coord1_header = plane_count > 1 ?
268         "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
269     const char *coord1_code = plane_count > 1 ?
270         " TexCoord1 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord1).st;\n" : "";
271     const char *coord2_header = plane_count > 2 ?
272         "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
273     const char *coord2_code = plane_count > 2 ?
274         " TexCoord2 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord2).st;\n" : "";
275
276     char *code;
277     if (asprintf(&code, template, renderer->glsl_version, coord1_header,
278                  coord2_header, coord1_code, coord2_code) < 0)
279         return 0;
280
281     GLuint shader = vt->CreateShader(GL_VERTEX_SHADER);
282     vt->ShaderSource(shader, 1, (const char **) &code, NULL);
283     if (renderer->b_dump_shaders)
284         msg_Dbg(renderer->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
285                 (const char *) &renderer->interop->fmt.i_chroma, code);
286     vt->CompileShader(shader);
287     free(code);
288     return shader;
289 }
290
291 static int
292 opengl_link_program(struct prgm *prgm)
293 {
294     struct vlc_gl_renderer *renderer = prgm->renderer;
295     struct vlc_gl_interop *interop = renderer->interop;
296     const opengl_vtable_t *vt = renderer->vt;
297
298     GLuint vertex_shader = BuildVertexShader(renderer, interop->tex_count);
299     if (!vertex_shader)
300         return VLC_EGENERIC;
301
302     GLuint fragment_shader =
303         opengl_fragment_shader_init(renderer, interop->tex_target,
304                                     interop->sw_fmt.i_chroma,
305                                     interop->sw_fmt.space);
306     if (!fragment_shader)
307         return VLC_EGENERIC;
308
309     assert(interop->tex_target != 0 &&
310            interop->tex_count > 0 &&
311            interop->ops->update_textures != NULL &&
312            renderer->pf_fetch_locations != NULL &&
313            renderer->pf_prepare_shader != NULL);
314
315     GLuint shaders[] = { fragment_shader, vertex_shader };
316
317     /* Check shaders messages */
318     for (unsigned i = 0; i < 2; i++) {
319         int infoLength;
320         vt->GetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLength);
321         if (infoLength <= 1)
322             continue;
323
324         char *infolog = malloc(infoLength);
325         if (infolog != NULL)
326         {
327             int charsWritten;
328             vt->GetShaderInfoLog(shaders[i], infoLength, &charsWritten,
329                                  infolog);
330             msg_Err(renderer->gl, "shader %u: %s", i, infolog);
331             free(infolog);
332         }
333     }
334
335     prgm->id = vt->CreateProgram();
336     vt->AttachShader(prgm->id, fragment_shader);
337     vt->AttachShader(prgm->id, vertex_shader);
338     vt->LinkProgram(prgm->id);
339
340     vt->DeleteShader(vertex_shader);
341     vt->DeleteShader(fragment_shader);
342
343     /* Check program messages */
344     int infoLength = 0;
345     vt->GetProgramiv(prgm->id, GL_INFO_LOG_LENGTH, &infoLength);
346     if (infoLength > 1)
347     {
348         char *infolog = malloc(infoLength);
349         if (infolog != NULL)
350         {
351             int charsWritten;
352             vt->GetProgramInfoLog(prgm->id, infoLength, &charsWritten,
353                                   infolog);
354             msg_Err(renderer->gl, "shader program: %s", infolog);
355             free(infolog);
356         }
357
358         /* If there is some message, better to check linking is ok */
359         GLint link_status = GL_TRUE;
360         vt->GetProgramiv(prgm->id, GL_LINK_STATUS, &link_status);
361         if (link_status == GL_FALSE)
362         {
363             msg_Err(renderer->gl, "Unable to use program");
364             goto error;
365         }
366     }
367
368     /* Fetch UniformLocations and AttribLocations */
369 #define GET_LOC(type, x, str) do { \
370     x = vt->Get##type##Location(prgm->id, str); \
371     assert(x != -1); \
372     if (x == -1) { \
373         msg_Err(renderer->gl, "Unable to Get"#type"Location(%s)", str); \
374         goto error; \
375     } \
376 } while (0)
377 #define GET_ULOC(x, str) GET_LOC(Uniform, prgm->uloc.x, str)
378 #define GET_ALOC(x, str) GET_LOC(Attrib, prgm->aloc.x, str)
379     GET_ULOC(TransformMatrix, "TransformMatrix");
380     GET_ULOC(OrientationMatrix, "OrientationMatrix");
381     GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
382     GET_ULOC(ViewMatrix, "ViewMatrix");
383     GET_ULOC(ZoomMatrix, "ZoomMatrix");
384
385     GET_ALOC(VertexPosition, "VertexPosition");
386     GET_ALOC(MultiTexCoord[0], "MultiTexCoord0");
387     /* MultiTexCoord 1 and 2 can be optimized out if not used */
388     if (interop->tex_count > 1)
389         GET_ALOC(MultiTexCoord[1], "MultiTexCoord1");
390     else
391         prgm->aloc.MultiTexCoord[1] = -1;
392     if (interop->tex_count > 2)
393         GET_ALOC(MultiTexCoord[2], "MultiTexCoord2");
394     else
395         prgm->aloc.MultiTexCoord[2] = -1;
396 #undef GET_LOC
397 #undef GET_ULOC
398 #undef GET_ALOC
399     int ret = prgm->renderer->pf_fetch_locations(prgm->renderer, prgm->id);
400     assert(ret == VLC_SUCCESS);
401     if (ret != VLC_SUCCESS)
402     {
403         msg_Err(renderer->gl, "Unable to get locations from tex_conv");
404         goto error;
405     }
406
407     return VLC_SUCCESS;
408
409 error:
410     vt->DeleteProgram(prgm->id);
411     prgm->id = 0;
412     return VLC_EGENERIC;
413 }
414
415 static void
416 opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm)
417 {
418     struct vlc_gl_renderer *renderer = prgm->renderer;
419     vlc_gl_interop_Delete(renderer->interop);
420     if (prgm->id != 0)
421         vgl->vt.DeleteProgram(prgm->id);
422
423 #ifdef HAVE_LIBPLACEBO
424     FREENULL(renderer->uloc.pl_vars);
425     if (renderer->pl_ctx)
426         pl_context_destroy(&renderer->pl_ctx);
427 #endif
428
429     free(renderer);
430 }
431
432 static int
433 opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
434                     struct prgm *prgm, const video_format_t *fmt,
435                     bool b_dump_shaders)
436 {
437     struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
438     if (!renderer)
439         return VLC_ENOMEM;
440
441     struct vlc_gl_interop *interop =
442         vlc_gl_interop_New(vgl->gl, &vgl->vt, context, fmt, false);
443     if (!interop)
444     {
445         free(renderer);
446         return VLC_ENOMEM;
447     }
448
449     renderer->interop = interop;
450
451     renderer->gl = vgl->gl;
452     renderer->vt = &vgl->vt;
453     renderer->b_dump_shaders = b_dump_shaders;
454 #if defined(USE_OPENGL_ES2)
455     renderer->glsl_version = 100;
456     renderer->glsl_precision_header = "precision highp float;\n";
457 #else
458     renderer->glsl_version = 120;
459     renderer->glsl_precision_header = "";
460 #endif
461
462 #ifdef HAVE_LIBPLACEBO
463     // Create the main libplacebo context
464     renderer->pl_ctx = vlc_placebo_Create(VLC_OBJECT(vgl->gl));
465     if (renderer->pl_ctx) {
466 #   if PL_API_VER >= 20
467         renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL);
468 #   elif PL_API_VER >= 6
469         renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL, 0);
470 #   else
471         renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL, 0, 0);
472 #   endif
473     }
474 #endif
475
476     prgm->renderer = renderer;
477
478     int ret = opengl_link_program(prgm);
479     if (ret != VLC_SUCCESS)
480     {
481         opengl_deinit_program(vgl, prgm);
482         return VLC_EGENERIC;
483     }
484
485     getOrientationTransformMatrix(interop->fmt.orientation,
486                                   prgm->var.OrientationMatrix);
487     getViewpointMatrixes(vgl, interop->fmt.projection_mode, prgm);
488
489     return VLC_SUCCESS;
490 }
491
492 static void
493 ResizeFormatToGLMaxTexSize(video_format_t *fmt, unsigned int max_tex_size)
494 {
495     if (fmt->i_width > fmt->i_height)
496     {
497         unsigned int const  vis_w = fmt->i_visible_width;
498         unsigned int const  vis_h = fmt->i_visible_height;
499         unsigned int const  nw_w = max_tex_size;
500         unsigned int const  nw_vis_w = nw_w * vis_w / fmt->i_width;
501
502         fmt->i_height = nw_w * fmt->i_height / fmt->i_width;
503         fmt->i_width = nw_w;
504         fmt->i_visible_height = nw_vis_w * vis_h / vis_w;
505         fmt->i_visible_width = nw_vis_w;
506     }
507     else
508     {
509         unsigned int const  vis_w = fmt->i_visible_width;
510         unsigned int const  vis_h = fmt->i_visible_height;
511         unsigned int const  nw_h = max_tex_size;
512         unsigned int const  nw_vis_h = nw_h * vis_h / fmt->i_height;
513
514         fmt->i_width = nw_h * fmt->i_width / fmt->i_height;
515         fmt->i_height = nw_h;
516         fmt->i_visible_width = nw_vis_h * vis_w / vis_h;
517         fmt->i_visible_height = nw_vis_h;
518     }
519 }
520
521 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
522                                                const vlc_fourcc_t **subpicture_chromas,
523                                                vlc_gl_t *gl,
524                                                const vlc_viewpoint_t *viewpoint,
525                                                vlc_video_context *context)
526 {
527     vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
528     if (!vgl)
529         return NULL;
530
531     vgl->gl = gl;
532
533 #if defined(USE_OPENGL_ES2) || defined(HAVE_GL_CORE_SYMBOLS)
534 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
535 #else
536 #define GET_PROC_ADDR_CORE(name) GET_PROC_ADDR_EXT(name, true)
537 #endif
538 #define GET_PROC_ADDR_EXT(name, critical) do { \
539     vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
540     if (vgl->vt.name == NULL && critical) { \
541         msg_Err(gl, "gl"#name" symbol not found, bailing out"); \
542         free(vgl); \
543         return NULL; \
544     } \
545 } while(0)
546 #if defined(USE_OPENGL_ES2)
547 #define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
548 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_EXT(name, false) /* optional for GLES */
549 #else
550 #define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
551 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
552 #endif
553 #define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
554
555     GET_PROC_ADDR_CORE(BindTexture);
556     GET_PROC_ADDR_CORE(BlendFunc);
557     GET_PROC_ADDR_CORE(Clear);
558     GET_PROC_ADDR_CORE(ClearColor);
559     GET_PROC_ADDR_CORE(DeleteTextures);
560     GET_PROC_ADDR_CORE(DepthMask);
561     GET_PROC_ADDR_CORE(Disable);
562     GET_PROC_ADDR_CORE(DrawArrays);
563     GET_PROC_ADDR_CORE(DrawElements);
564     GET_PROC_ADDR_CORE(Enable);
565     GET_PROC_ADDR_CORE(Finish);
566     GET_PROC_ADDR_CORE(Flush);
567     GET_PROC_ADDR_CORE(GenTextures);
568     GET_PROC_ADDR_CORE(GetError);
569     GET_PROC_ADDR_CORE(GetIntegerv);
570     GET_PROC_ADDR_CORE(GetString);
571     GET_PROC_ADDR_CORE(PixelStorei);
572     GET_PROC_ADDR_CORE(TexImage2D);
573     GET_PROC_ADDR_CORE(TexParameterf);
574     GET_PROC_ADDR_CORE(TexParameteri);
575     GET_PROC_ADDR_CORE(TexSubImage2D);
576     GET_PROC_ADDR_CORE(Viewport);
577
578     GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv);
579     GET_PROC_ADDR_CORE_GL(TexEnvf);
580
581     GET_PROC_ADDR(CreateShader);
582     GET_PROC_ADDR(ShaderSource);
583     GET_PROC_ADDR(CompileShader);
584     GET_PROC_ADDR(AttachShader);
585     GET_PROC_ADDR(DeleteShader);
586
587     GET_PROC_ADDR(GetProgramiv);
588     GET_PROC_ADDR(GetShaderiv);
589     GET_PROC_ADDR(GetProgramInfoLog);
590     GET_PROC_ADDR(GetShaderInfoLog);
591
592     GET_PROC_ADDR(GetUniformLocation);
593     GET_PROC_ADDR(GetAttribLocation);
594     GET_PROC_ADDR(VertexAttribPointer);
595     GET_PROC_ADDR(EnableVertexAttribArray);
596     GET_PROC_ADDR(UniformMatrix4fv);
597     GET_PROC_ADDR(UniformMatrix3fv);
598     GET_PROC_ADDR(UniformMatrix2fv);
599     GET_PROC_ADDR(Uniform4fv);
600     GET_PROC_ADDR(Uniform4f);
601     GET_PROC_ADDR(Uniform3f);
602     GET_PROC_ADDR(Uniform2f);
603     GET_PROC_ADDR(Uniform1f);
604     GET_PROC_ADDR(Uniform1i);
605
606     GET_PROC_ADDR(CreateProgram);
607     GET_PROC_ADDR(LinkProgram);
608     GET_PROC_ADDR(UseProgram);
609     GET_PROC_ADDR(DeleteProgram);
610
611     GET_PROC_ADDR(ActiveTexture);
612
613     GET_PROC_ADDR(GenBuffers);
614     GET_PROC_ADDR(BindBuffer);
615     GET_PROC_ADDR(BufferData);
616     GET_PROC_ADDR(DeleteBuffers);
617
618     GET_PROC_ADDR_OPTIONAL(GetFramebufferAttachmentParameteriv);
619
620     GET_PROC_ADDR_OPTIONAL(BufferSubData);
621     GET_PROC_ADDR_OPTIONAL(BufferStorage);
622     GET_PROC_ADDR_OPTIONAL(MapBufferRange);
623     GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange);
624     GET_PROC_ADDR_OPTIONAL(UnmapBuffer);
625     GET_PROC_ADDR_OPTIONAL(FenceSync);
626     GET_PROC_ADDR_OPTIONAL(DeleteSync);
627     GET_PROC_ADDR_OPTIONAL(ClientWaitSync);
628 #undef GET_PROC_ADDR
629
630     GL_ASSERT_NOERROR();
631
632     const char *extensions = (const char *)vgl->vt.GetString(GL_EXTENSIONS);
633     assert(extensions);
634     if (!extensions)
635     {
636         msg_Err(gl, "glGetString returned NULL");
637         free(vgl);
638         return NULL;
639     }
640 #if !defined(USE_OPENGL_ES2)
641     const unsigned char *ogl_version = vgl->vt.GetString(GL_VERSION);
642     bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
643     if (!supports_shaders)
644     {
645         msg_Err(gl, "shaders not supported, bailing out");
646         free(vgl);
647         return NULL;
648     }
649 #endif
650
651     /* Resize the format if it is greater than the maximum texture size
652      * supported by the hardware */
653     GLint       max_tex_size;
654     vgl->vt.GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
655
656     if ((GLint)fmt->i_width > max_tex_size ||
657         (GLint)fmt->i_height > max_tex_size)
658         ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
659
660     /* Non-power-of-2 texture size support */
661     bool supports_npot;
662 #if defined(USE_OPENGL_ES2)
663     /* OpenGL ES 2 includes support for non-power of 2 textures by specification
664      * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
665     supports_npot = true;
666 #else
667     supports_npot = vlc_gl_StrHasToken(extensions, "GL_ARB_texture_non_power_of_two") ||
668                     vlc_gl_StrHasToken(extensions, "GL_APPLE_texture_2D_limited_npot");
669 #endif
670
671     bool b_dump_shaders = var_InheritInteger(gl, "verbose") >= 4;
672
673     vgl->sub_renderer =
674         vlc_gl_sub_renderer_New(gl, &vgl->vt, supports_npot);
675     if (!vgl->sub_renderer)
676     {
677         msg_Err(gl, "Could not create sub renderer");
678         free(vgl);
679         return NULL;
680     }
681
682     GL_ASSERT_NOERROR();
683     int ret = opengl_init_program(vgl, context, &vgl->prgm, fmt,
684                                   b_dump_shaders);
685     if (ret != VLC_SUCCESS)
686     {
687         msg_Warn(gl, "could not init tex converter for %4.4s",
688                  (const char *) &fmt->i_chroma);
689         vlc_gl_sub_renderer_Delete(vgl->sub_renderer);
690         free(vgl);
691         return NULL;
692     }
693
694     GL_ASSERT_NOERROR();
695
696     const struct vlc_gl_interop *interop = vgl->prgm.renderer->interop;
697     /* Update the fmt to main program one */
698     vgl->fmt = interop->fmt;
699     /* The orientation is handled by the orientation matrix */
700     vgl->fmt.orientation = fmt->orientation;
701
702     /* Texture size */
703     for (unsigned j = 0; j < interop->tex_count; j++) {
704         const GLsizei w = vgl->fmt.i_visible_width  * interop->texs[j].w.num
705                         / interop->texs[j].w.den;
706         const GLsizei h = vgl->fmt.i_visible_height * interop->texs[j].h.num
707                         / interop->texs[j].h.den;
708         if (supports_npot) {
709             vgl->tex_width[j]  = w;
710             vgl->tex_height[j] = h;
711         } else {
712             vgl->tex_width[j]  = vlc_align_pot(w);
713             vgl->tex_height[j] = vlc_align_pot(h);
714         }
715     }
716
717     if (!interop->handle_texs_gen)
718     {
719         ret = vlc_gl_interop_GenerateTextures(interop,
720                                               vgl->tex_width, vgl->tex_height,
721                                               vgl->texture);
722         if (ret != VLC_SUCCESS)
723         {
724             vout_display_opengl_Delete(vgl);
725             return NULL;
726         }
727     }
728
729     /* */
730     vgl->vt.Disable(GL_BLEND);
731     vgl->vt.Disable(GL_DEPTH_TEST);
732     vgl->vt.DepthMask(GL_FALSE);
733     vgl->vt.Enable(GL_CULL_FACE);
734     vgl->vt.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
735     vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
736
737     vgl->vt.GenBuffers(1, &vgl->vertex_buffer_object);
738     vgl->vt.GenBuffers(1, &vgl->index_buffer_object);
739     vgl->vt.GenBuffers(interop->tex_count, vgl->texture_buffer_object);
740
741     if (vgl->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
742      && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
743     {
744         vout_display_opengl_Delete(vgl);
745         return NULL;
746     }
747
748     *fmt = vgl->fmt;
749     if (subpicture_chromas) {
750         *subpicture_chromas = gl_subpicture_chromas;
751     }
752
753     GL_ASSERT_NOERROR();
754     return vgl;
755 }
756
757 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
758 {
759     GL_ASSERT_NOERROR();
760
761     /* */
762     vgl->vt.Finish();
763     vgl->vt.Flush();
764
765     const struct vlc_gl_interop *interop = vgl->prgm.renderer->interop;
766     const size_t main_tex_count = interop->tex_count;
767     const bool main_del_texs = !interop->handle_texs_gen;
768
769     vlc_gl_sub_renderer_Delete(vgl->sub_renderer);
770
771     opengl_deinit_program(vgl, &vgl->prgm);
772
773     vgl->vt.DeleteBuffers(1, &vgl->vertex_buffer_object);
774     vgl->vt.DeleteBuffers(1, &vgl->index_buffer_object);
775     vgl->vt.DeleteBuffers(main_tex_count, vgl->texture_buffer_object);
776
777     if (main_del_texs)
778         vgl->vt.DeleteTextures(main_tex_count, vgl->texture);
779
780     GL_ASSERT_NOERROR();
781
782     free(vgl);
783 }
784
785 static void UpdateZ(vout_display_opengl_t *vgl)
786 {
787     /* Do trigonometry to calculate the minimal z value
788      * that will allow us to zoom out without seeing the outside of the
789      * sphere (black borders). */
790     float tan_fovx_2 = tanf(vgl->f_fovx / 2);
791     float tan_fovy_2 = tanf(vgl->f_fovy / 2);
792     float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
793                     tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
794
795     /* The FOV value above which z is dynamically calculated. */
796     const float z_thresh = 90.f;
797
798     if (vgl->f_fovx <= z_thresh * M_PI / 180)
799         vgl->f_z = 0;
800     else
801     {
802         float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
803         vgl->f_z = f * vgl->f_fovx - f * z_thresh * M_PI / 180;
804         if (vgl->f_z < z_min)
805             vgl->f_z = z_min;
806     }
807 }
808
809 static void UpdateFOVy(vout_display_opengl_t *vgl)
810 {
811     vgl->f_fovy = 2 * atanf(tanf(vgl->f_fovx / 2) / vgl->f_sar);
812 }
813
814 int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
815                                      const vlc_viewpoint_t *p_vp)
816 {
817     if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
818             || p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
819         return VLC_EBADVAR;
820
821     // Convert degree into radian
822     float f_fovx = p_vp->fov * (float)M_PI / 180.f;
823
824     /* vgl->vp needs to be converted into world transform */
825     vlc_viewpoint_reverse(&vgl->vp, p_vp);
826
827     if (fabsf(f_fovx - vgl->f_fovx) >= 0.001f)
828     {
829         /* FOVx has changed. */
830         vgl->f_fovx = f_fovx;
831         UpdateFOVy(vgl);
832         UpdateZ(vgl);
833     }
834     getViewpointMatrixes(vgl, vgl->fmt.projection_mode, &vgl->prgm);
835
836     return VLC_SUCCESS;
837 }
838
839
840 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t *vgl,
841                                               float f_sar)
842 {
843     /* Each time the window size changes, we must recompute the minimum zoom
844      * since the aspect ration changes.
845      * We must also set the new current zoom value. */
846     vgl->f_sar = f_sar;
847     UpdateFOVy(vgl);
848     UpdateZ(vgl);
849     getViewpointMatrixes(vgl, vgl->fmt.projection_mode, &vgl->prgm);
850 }
851
852 void vout_display_opengl_Viewport(vout_display_opengl_t *vgl, int x, int y,
853                                   unsigned width, unsigned height)
854 {
855     vgl->vt.Viewport(x, y, width, height);
856 }
857
858 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
859                                 picture_t *picture, subpicture_t *subpicture)
860 {
861     GL_ASSERT_NOERROR();
862
863     struct vlc_gl_renderer *renderer = vgl->prgm.renderer;
864     const struct vlc_gl_interop *interop = renderer->interop;
865
866     /* Update the texture */
867     int ret = interop->ops->update_textures(interop, vgl->texture, vgl->tex_width, vgl->tex_height,
868                                             picture, NULL);
869     if (ret != VLC_SUCCESS)
870         return ret;
871
872     ret = vlc_gl_sub_renderer_Prepare(vgl->sub_renderer, subpicture);
873     GL_ASSERT_NOERROR();
874     return ret;
875 }
876
877 static int BuildSphere(unsigned nbPlanes,
878                         GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
879                         GLushort **indices, unsigned *nbIndices,
880                         const float *left, const float *top,
881                         const float *right, const float *bottom)
882 {
883     unsigned nbLatBands = 128;
884     unsigned nbLonBands = 128;
885
886     *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
887     *nbIndices = nbLatBands * nbLonBands * 3 * 2;
888
889     *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
890     if (*vertexCoord == NULL)
891         return VLC_ENOMEM;
892     *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
893     if (*textureCoord == NULL)
894     {
895         free(*vertexCoord);
896         return VLC_ENOMEM;
897     }
898     *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
899     if (*indices == NULL)
900     {
901         free(*textureCoord);
902         free(*vertexCoord);
903         return VLC_ENOMEM;
904     }
905
906     for (unsigned lat = 0; lat <= nbLatBands; lat++) {
907         float theta = lat * (float) M_PI / nbLatBands;
908         float sinTheta, cosTheta;
909
910         sincosf(theta, &sinTheta, &cosTheta);
911
912         for (unsigned lon = 0; lon <= nbLonBands; lon++) {
913             float phi = lon * 2 * (float) M_PI / nbLonBands;
914             float sinPhi, cosPhi;
915
916             sincosf(phi, &sinPhi, &cosPhi);
917
918             float x = cosPhi * sinTheta;
919             float y = cosTheta;
920             float z = sinPhi * sinTheta;
921
922             unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
923             (*vertexCoord)[off1] = SPHERE_RADIUS * x;
924             (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
925             (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
926
927             for (unsigned p = 0; p < nbPlanes; ++p)
928             {
929                 unsigned off2 = (p * (nbLatBands + 1) * (nbLonBands + 1)
930                                 + lat * (nbLonBands + 1) + lon) * 2;
931                 float width = right[p] - left[p];
932                 float height = bottom[p] - top[p];
933                 float u = (float)lon / nbLonBands * width;
934                 float v = (float)lat / nbLatBands * height;
935                 (*textureCoord)[off2] = u;
936                 (*textureCoord)[off2 + 1] = v;
937             }
938         }
939     }
940
941     for (unsigned lat = 0; lat < nbLatBands; lat++) {
942         for (unsigned lon = 0; lon < nbLonBands; lon++) {
943             unsigned first = (lat * (nbLonBands + 1)) + lon;
944             unsigned second = first + nbLonBands + 1;
945
946             unsigned off = (lat * nbLatBands + lon) * 3 * 2;
947
948             (*indices)[off] = first;
949             (*indices)[off + 1] = second;
950             (*indices)[off + 2] = first + 1;
951
952             (*indices)[off + 3] = second;
953             (*indices)[off + 4] = second + 1;
954             (*indices)[off + 5] = first + 1;
955         }
956     }
957
958     return VLC_SUCCESS;
959 }
960
961
962 static int BuildCube(unsigned nbPlanes,
963                      float padW, float padH,
964                      GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
965                      GLushort **indices, unsigned *nbIndices,
966                      const float *left, const float *top,
967                      const float *right, const float *bottom)
968 {
969     *nbVertices = 4 * 6;
970     *nbIndices = 6 * 6;
971
972     *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
973     if (*vertexCoord == NULL)
974         return VLC_ENOMEM;
975     *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
976     if (*textureCoord == NULL)
977     {
978         free(*vertexCoord);
979         return VLC_ENOMEM;
980     }
981     *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
982     if (*indices == NULL)
983     {
984         free(*textureCoord);
985         free(*vertexCoord);
986         return VLC_ENOMEM;
987     }
988
989     static const GLfloat coord[] = {
990         -1.0,    1.0,    -1.0f, // front
991         -1.0,    -1.0,   -1.0f,
992         1.0,     1.0,    -1.0f,
993         1.0,     -1.0,   -1.0f,
994
995         -1.0,    1.0,    1.0f, // back
996         -1.0,    -1.0,   1.0f,
997         1.0,     1.0,    1.0f,
998         1.0,     -1.0,   1.0f,
999
1000         -1.0,    1.0,    -1.0f, // left
1001         -1.0,    -1.0,   -1.0f,
1002         -1.0,     1.0,    1.0f,
1003         -1.0,     -1.0,   1.0f,
1004
1005         1.0f,    1.0,    -1.0f, // right
1006         1.0f,   -1.0,    -1.0f,
1007         1.0f,   1.0,     1.0f,
1008         1.0f,   -1.0,    1.0f,
1009
1010         -1.0,    -1.0,    1.0f, // bottom
1011         -1.0,    -1.0,   -1.0f,
1012         1.0,     -1.0,    1.0f,
1013         1.0,     -1.0,   -1.0f,
1014
1015         -1.0,    1.0,    1.0f, // top
1016         -1.0,    1.0,   -1.0f,
1017         1.0,     1.0,    1.0f,
1018         1.0,     1.0,   -1.0f,
1019     };
1020
1021     memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1022
1023     for (unsigned p = 0; p < nbPlanes; ++p)
1024     {
1025         float width = right[p] - left[p];
1026         float height = bottom[p] - top[p];
1027
1028         float col[] = {left[p],
1029                        left[p] + width * 1.f/3,
1030                        left[p] + width * 2.f/3,
1031                        left[p] + width};
1032
1033         float row[] = {top[p],
1034                        top[p] + height * 1.f/2,
1035                        top[p] + height};
1036
1037         const GLfloat tex[] = {
1038             col[1] + padW, row[1] + padH, // front
1039             col[1] + padW, row[2] - padH,
1040             col[2] - padW, row[1] + padH,
1041             col[2] - padW, row[2] - padH,
1042
1043             col[3] - padW, row[1] + padH, // back
1044             col[3] - padW, row[2] - padH,
1045             col[2] + padW, row[1] + padH,
1046             col[2] + padW, row[2] - padH,
1047
1048             col[2] - padW, row[0] + padH, // left
1049             col[2] - padW, row[1] - padH,
1050             col[1] + padW, row[0] + padH,
1051             col[1] + padW, row[1] - padH,
1052
1053             col[0] + padW, row[0] + padH, // right
1054             col[0] + padW, row[1] - padH,
1055             col[1] - padW, row[0] + padH,
1056             col[1] - padW, row[1] - padH,
1057
1058             col[0] + padW, row[2] - padH, // bottom
1059             col[0] + padW, row[1] + padH,
1060             col[1] - padW, row[2] - padH,
1061             col[1] - padW, row[1] + padH,
1062
1063             col[2] + padW, row[0] + padH, // top
1064             col[2] + padW, row[1] - padH,
1065             col[3] - padW, row[0] + padH,
1066             col[3] - padW, row[1] - padH,
1067         };
1068
1069         memcpy(*textureCoord + p * *nbVertices * 2, tex,
1070                *nbVertices * 2 * sizeof(GLfloat));
1071     }
1072
1073     const GLushort ind[] = {
1074         0, 1, 2,       2, 1, 3, // front
1075         6, 7, 4,       4, 7, 5, // back
1076         10, 11, 8,     8, 11, 9, // left
1077         12, 13, 14,    14, 13, 15, // right
1078         18, 19, 16,    16, 19, 17, // bottom
1079         20, 21, 22,    22, 21, 23, // top
1080     };
1081
1082     memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1083
1084     return VLC_SUCCESS;
1085 }
1086
1087 static int BuildRectangle(unsigned nbPlanes,
1088                           GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1089                           GLushort **indices, unsigned *nbIndices,
1090                           const float *left, const float *top,
1091                           const float *right, const float *bottom)
1092 {
1093     *nbVertices = 4;
1094     *nbIndices = 6;
1095
1096     *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1097     if (*vertexCoord == NULL)
1098         return VLC_ENOMEM;
1099     *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1100     if (*textureCoord == NULL)
1101     {
1102         free(*vertexCoord);
1103         return VLC_ENOMEM;
1104     }
1105     *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1106     if (*indices == NULL)
1107     {
1108         free(*textureCoord);
1109         free(*vertexCoord);
1110         return VLC_ENOMEM;
1111     }
1112
1113     static const GLfloat coord[] = {
1114        -1.0,    1.0,    -1.0f,
1115        -1.0,    -1.0,   -1.0f,
1116        1.0,     1.0,    -1.0f,
1117        1.0,     -1.0,   -1.0f
1118     };
1119
1120     memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1121
1122     for (unsigned p = 0; p < nbPlanes; ++p)
1123     {
1124         const GLfloat tex[] = {
1125             left[p],  top[p],
1126             left[p],  bottom[p],
1127             right[p], top[p],
1128             right[p], bottom[p]
1129         };
1130
1131         memcpy(*textureCoord + p * *nbVertices * 2, tex,
1132                *nbVertices * 2 * sizeof(GLfloat));
1133     }
1134
1135     const GLushort ind[] = {
1136         0, 1, 2,
1137         2, 1, 3
1138     };
1139
1140     memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1141
1142     return VLC_SUCCESS;
1143 }
1144
1145 static int SetupCoords(vout_display_opengl_t *vgl,
1146                        const float *left, const float *top,
1147                        const float *right, const float *bottom)
1148 {
1149     const struct vlc_gl_interop *interop = vgl->prgm.renderer->interop;
1150
1151     GLfloat *vertexCoord, *textureCoord;
1152     GLushort *indices;
1153     unsigned nbVertices, nbIndices;
1154
1155     int i_ret;
1156     switch (vgl->fmt.projection_mode)
1157     {
1158     case PROJECTION_MODE_RECTANGULAR:
1159         i_ret = BuildRectangle(interop->tex_count,
1160                                &vertexCoord, &textureCoord, &nbVertices,
1161                                &indices, &nbIndices,
1162                                left, top, right, bottom);
1163         break;
1164     case PROJECTION_MODE_EQUIRECTANGULAR:
1165         i_ret = BuildSphere(interop->tex_count,
1166                             &vertexCoord, &textureCoord, &nbVertices,
1167                             &indices, &nbIndices,
1168                             left, top, right, bottom);
1169         break;
1170     case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
1171         i_ret = BuildCube(interop->tex_count,
1172                           (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_width,
1173                           (float)vgl->fmt.i_cubemap_padding / vgl->fmt.i_height,
1174                           &vertexCoord, &textureCoord, &nbVertices,
1175                           &indices, &nbIndices,
1176                           left, top, right, bottom);
1177         break;
1178     default:
1179         i_ret = VLC_EGENERIC;
1180         break;
1181     }
1182
1183     if (i_ret != VLC_SUCCESS)
1184         return i_ret;
1185
1186     for (unsigned j = 0; j < interop->tex_count; j++)
1187     {
1188         vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
1189         vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
1190                            textureCoord + j * nbVertices * 2, GL_STATIC_DRAW);
1191     }
1192
1193     vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
1194     vgl->vt.BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
1195                        vertexCoord, GL_STATIC_DRAW);
1196
1197     vgl->vt.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, vgl->index_buffer_object);
1198     vgl->vt.BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
1199                        indices, GL_STATIC_DRAW);
1200
1201     free(textureCoord);
1202     free(vertexCoord);
1203     free(indices);
1204
1205     vgl->nb_indices = nbIndices;
1206
1207     return VLC_SUCCESS;
1208 }
1209
1210 static void DrawWithShaders(vout_display_opengl_t *vgl, struct prgm *prgm)
1211 {
1212     struct vlc_gl_renderer *renderer = prgm->renderer;
1213     const struct vlc_gl_interop *interop = renderer->interop;
1214     renderer->pf_prepare_shader(renderer, vgl->tex_width, vgl->tex_height, 1.0f);
1215
1216     for (unsigned j = 0; j < interop->tex_count; j++) {
1217         assert(vgl->texture[j] != 0);
1218         vgl->vt.ActiveTexture(GL_TEXTURE0+j);
1219         vgl->vt.BindTexture(interop->tex_target, vgl->texture[j]);
1220
1221         vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
1222
1223         assert(prgm->aloc.MultiTexCoord[j] != -1);
1224         vgl->vt.EnableVertexAttribArray(prgm->aloc.MultiTexCoord[j]);
1225         vgl->vt.VertexAttribPointer(prgm->aloc.MultiTexCoord[j], 2, GL_FLOAT,
1226                                      0, 0, 0);
1227     }
1228
1229     vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
1230     vgl->vt.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, vgl->index_buffer_object);
1231     vgl->vt.EnableVertexAttribArray(prgm->aloc.VertexPosition);
1232     vgl->vt.VertexAttribPointer(prgm->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
1233
1234     const GLfloat *tm = NULL;
1235     if (interop->ops && interop->ops->get_transform_matrix)
1236         tm = interop->ops->get_transform_matrix(interop);
1237     if (!tm)
1238         tm = identity;
1239
1240     vgl->vt.UniformMatrix4fv(prgm->uloc.TransformMatrix, 1, GL_FALSE, tm);
1241
1242     vgl->vt.UniformMatrix4fv(prgm->uloc.OrientationMatrix, 1, GL_FALSE,
1243                              prgm->var.OrientationMatrix);
1244     vgl->vt.UniformMatrix4fv(prgm->uloc.ProjectionMatrix, 1, GL_FALSE,
1245                              prgm->var.ProjectionMatrix);
1246     vgl->vt.UniformMatrix4fv(prgm->uloc.ViewMatrix, 1, GL_FALSE,
1247                              prgm->var.ViewMatrix);
1248     vgl->vt.UniformMatrix4fv(prgm->uloc.ZoomMatrix, 1, GL_FALSE,
1249                              prgm->var.ZoomMatrix);
1250
1251     vgl->vt.DrawElements(GL_TRIANGLES, vgl->nb_indices, GL_UNSIGNED_SHORT, 0);
1252 }
1253
1254
1255 static void GetTextureCropParamsForStereo(unsigned i_nbTextures,
1256                                           const float *stereoCoefs,
1257                                           const float *stereoOffsets,
1258                                           float *left, float *top,
1259                                           float *right, float *bottom)
1260 {
1261     for (unsigned i = 0; i < i_nbTextures; ++i)
1262     {
1263         float f_2eyesWidth = right[i] - left[i];
1264         left[i] = left[i] + f_2eyesWidth * stereoOffsets[0];
1265         right[i] = left[i] + f_2eyesWidth * stereoCoefs[0];
1266
1267         float f_2eyesHeight = bottom[i] - top[i];
1268         top[i] = top[i] + f_2eyesHeight * stereoOffsets[1];
1269         bottom[i] = top[i] + f_2eyesHeight * stereoCoefs[1];
1270     }
1271 }
1272
1273 static void TextureCropForStereo(vout_display_opengl_t *vgl,
1274                                  float *left, float *top,
1275                                  float *right, float *bottom)
1276 {
1277     const struct vlc_gl_interop *interop = vgl->prgm.renderer->interop;
1278
1279     float stereoCoefs[2];
1280     float stereoOffsets[2];
1281
1282     switch (vgl->fmt.multiview_mode)
1283     {
1284     case MULTIVIEW_STEREO_TB:
1285         // Display only the left eye.
1286         stereoCoefs[0] = 1; stereoCoefs[1] = 0.5;
1287         stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1288         GetTextureCropParamsForStereo(interop->tex_count,
1289                                       stereoCoefs, stereoOffsets,
1290                                       left, top, right, bottom);
1291         break;
1292     case MULTIVIEW_STEREO_SBS:
1293         // Display only the left eye.
1294         stereoCoefs[0] = 0.5; stereoCoefs[1] = 1;
1295         stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1296         GetTextureCropParamsForStereo(interop->tex_count,
1297                                       stereoCoefs, stereoOffsets,
1298                                       left, top, right, bottom);
1299         break;
1300     default:
1301         break;
1302     }
1303 }
1304
1305 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
1306                                 const video_format_t *source)
1307 {
1308     GL_ASSERT_NOERROR();
1309
1310     /* Why drawing here and not in Render()? Because this way, the
1311        OpenGL providers can call vout_display_opengl_Display to force redraw.
1312        Currently, the OS X provider uses it to get a smooth window resizing */
1313     vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
1314
1315     vgl->vt.UseProgram(vgl->prgm.id);
1316
1317     if (source->i_x_offset != vgl->last_source.i_x_offset
1318      || source->i_y_offset != vgl->last_source.i_y_offset
1319      || source->i_visible_width != vgl->last_source.i_visible_width
1320      || source->i_visible_height != vgl->last_source.i_visible_height)
1321     {
1322         float left[PICTURE_PLANE_MAX];
1323         float top[PICTURE_PLANE_MAX];
1324         float right[PICTURE_PLANE_MAX];
1325         float bottom[PICTURE_PLANE_MAX];
1326         const struct vlc_gl_renderer *renderer = vgl->prgm.renderer;
1327         const struct vlc_gl_interop *interop = renderer->interop;
1328         for (unsigned j = 0; j < interop->tex_count; j++)
1329         {
1330             float scale_w = (float)interop->texs[j].w.num / interop->texs[j].w.den
1331                           / vgl->tex_width[j];
1332             float scale_h = (float)interop->texs[j].h.num / interop->texs[j].h.den
1333                           / vgl->tex_height[j];
1334
1335             /* Warning: if NPOT is not supported a larger texture is
1336                allocated. This will cause right and bottom coordinates to
1337                land on the edge of two texels with the texels to the
1338                right/bottom uninitialized by the call to
1339                glTexSubImage2D. This might cause a green line to appear on
1340                the right/bottom of the display.
1341                There are two possible solutions:
1342                - Manually mirror the edges of the texture.
1343                - Add a "-1" when computing right and bottom, however the
1344                last row/column might not be displayed at all.
1345             */
1346             left[j]   = (source->i_x_offset +                       0 ) * scale_w;
1347             top[j]    = (source->i_y_offset +                       0 ) * scale_h;
1348             right[j]  = (source->i_x_offset + source->i_visible_width ) * scale_w;
1349             bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
1350         }
1351
1352         TextureCropForStereo(vgl, left, top, right, bottom);
1353         int ret = SetupCoords(vgl, left, top, right, bottom);
1354         if (ret != VLC_SUCCESS)
1355             return ret;
1356
1357         vgl->last_source.i_x_offset = source->i_x_offset;
1358         vgl->last_source.i_y_offset = source->i_y_offset;
1359         vgl->last_source.i_visible_width = source->i_visible_width;
1360         vgl->last_source.i_visible_height = source->i_visible_height;
1361     }
1362     DrawWithShaders(vgl, &vgl->prgm);
1363
1364     int ret = vlc_gl_sub_renderer_Draw(vgl->sub_renderer);
1365     if (ret != VLC_SUCCESS)
1366         return ret;
1367
1368     /* Display */
1369     vlc_gl_Swap(vgl->gl);
1370
1371     GL_ASSERT_NOERROR();
1372
1373     return VLC_SUCCESS;
1374 }
1375