opengl: rename tex converter to renderer
[vlc.git] / modules / video_output / opengl / vout_helper.c
blob1abac866f66df58728857d68cda335464ba0bd39
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_subpicture.h>
37 #include <vlc_opengl.h>
38 #include <vlc_modules.h>
39 #include <vlc_vout.h>
40 #include <vlc_viewpoint.h>
42 #include "gl_util.h"
43 #include "vout_helper.h"
44 #include "internal.h"
45 #include "sub_renderer.h"
47 #define SPHERE_RADIUS 1.f
49 struct prgm
51 GLuint id;
52 struct vlc_gl_renderer *renderer;
54 struct {
55 GLfloat OrientationMatrix[16];
56 GLfloat ProjectionMatrix[16];
57 GLfloat ZoomMatrix[16];
58 GLfloat ViewMatrix[16];
59 } var;
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;
74 struct vout_display_opengl_t {
76 vlc_gl_t *gl;
77 opengl_vtable_t vt;
79 video_format_t fmt;
81 GLsizei tex_width[PICTURE_PLANE_MAX];
82 GLsizei tex_height[PICTURE_PLANE_MAX];
84 GLuint texture[PICTURE_PLANE_MAX];
86 struct prgm prgm;
88 unsigned nb_indices;
89 GLuint vertex_buffer_object;
90 GLuint index_buffer_object;
91 GLuint texture_buffer_object[PICTURE_PLANE_MAX];
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;
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;
110 struct vlc_gl_sub_renderer *sub_renderer;
113 static const vlc_fourcc_t gl_subpicture_chromas[] = {
114 VLC_CODEC_RGBA,
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
125 static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {
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
135 memcpy(matrix, m, sizeof(m));
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]) {
141 float zFar = 1000;
142 float zNear = 0.01;
144 float f = 1.f / tanf(fovy / 2.f);
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};
152 memcpy(matrix, m, sizeof(m));
155 static void getViewpointMatrixes(vout_display_opengl_t *vgl,
156 video_projection_mode_t projection_mode,
157 struct prgm *prgm)
159 if (projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
160 || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
162 getProjectionMatrix(vgl->f_sar, vgl->f_fovy, prgm->var.ProjectionMatrix);
163 getZoomMatrix(vgl->f_z, prgm->var.ZoomMatrix);
165 /* vgl->vp has been reversed and is a world transform */
166 vlc_viewpoint_to_4x4(&vgl->vp, prgm->var.ViewMatrix);
168 else
170 memcpy(prgm->var.ProjectionMatrix, identity, sizeof(identity));
171 memcpy(prgm->var.ZoomMatrix, identity, sizeof(identity));
172 memcpy(prgm->var.ViewMatrix, identity, sizeof(identity));
177 static void getOrientationTransformMatrix(video_orientation_t orientation,
178 GLfloat matrix[static 16])
180 memcpy(matrix, identity, sizeof(identity));
182 const int k_cos_pi = -1;
183 const int k_cos_pi_2 = 0;
184 const int k_cos_n_pi_2 = 0;
186 const int k_sin_pi = 0;
187 const int k_sin_pi_2 = 1;
188 const int k_sin_n_pi_2 = -1;
190 switch (orientation) {
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;
243 static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer,
244 unsigned plane_count)
246 const opengl_vtable_t *vt = renderer->vt;
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 "}";
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" : "";
276 char *code;
277 if (asprintf(&code, template, renderer->glsl_version, coord1_header,
278 coord2_header, coord1_code, coord2_code) < 0)
279 return 0;
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;
291 static int
292 opengl_link_program(struct prgm *prgm)
294 struct vlc_gl_renderer *renderer = prgm->renderer;
295 struct vlc_gl_interop *interop = renderer->interop;
296 const opengl_vtable_t *vt = renderer->vt;
298 GLuint vertex_shader = BuildVertexShader(renderer, interop->tex_count);
299 if (!vertex_shader)
300 return VLC_EGENERIC;
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;
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);
315 GLuint shaders[] = { fragment_shader, vertex_shader };
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;
324 char *infolog = malloc(infoLength);
325 if (infolog != NULL)
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);
335 prgm->id = vt->CreateProgram();
336 vt->AttachShader(prgm->id, fragment_shader);
337 vt->AttachShader(prgm->id, vertex_shader);
338 vt->LinkProgram(prgm->id);
340 vt->DeleteShader(vertex_shader);
341 vt->DeleteShader(fragment_shader);
343 /* Check program messages */
344 int infoLength = 0;
345 vt->GetProgramiv(prgm->id, GL_INFO_LOG_LENGTH, &infoLength);
346 if (infoLength > 1)
348 char *infolog = malloc(infoLength);
349 if (infolog != NULL)
351 int charsWritten;
352 vt->GetProgramInfoLog(prgm->id, infoLength, &charsWritten,
353 infolog);
354 msg_Err(renderer->gl, "shader program: %s", infolog);
355 free(infolog);
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)
363 msg_Err(renderer->gl, "Unable to use program");
364 goto error;
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; \
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");
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)
403 msg_Err(renderer->gl, "Unable to get locations from tex_conv");
404 goto error;
407 return VLC_SUCCESS;
409 error:
410 vt->DeleteProgram(prgm->id);
411 prgm->id = 0;
412 return VLC_EGENERIC;
415 static void
416 opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm)
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);
423 #ifdef HAVE_LIBPLACEBO
424 FREENULL(renderer->uloc.pl_vars);
425 if (renderer->pl_ctx)
426 pl_context_destroy(&renderer->pl_ctx);
427 #endif
429 free(renderer);
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)
437 struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
438 if (!renderer)
439 return VLC_ENOMEM;
441 struct vlc_gl_interop *interop =
442 vlc_gl_interop_New(vgl->gl, &vgl->vt, context, fmt, false);
443 if (!interop)
445 free(renderer);
446 return VLC_ENOMEM;
449 renderer->interop = interop;
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
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
474 #endif
476 prgm->renderer = renderer;
478 int ret = opengl_link_program(prgm);
479 if (ret != VLC_SUCCESS)
481 opengl_deinit_program(vgl, prgm);
482 return VLC_EGENERIC;
485 getOrientationTransformMatrix(interop->fmt.orientation,
486 prgm->var.OrientationMatrix);
487 getViewpointMatrixes(vgl, interop->fmt.projection_mode, prgm);
489 return VLC_SUCCESS;
492 static void
493 ResizeFormatToGLMaxTexSize(video_format_t *fmt, unsigned int max_tex_size)
495 if (fmt->i_width > fmt->i_height)
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;
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;
507 else
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;
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;
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)
527 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
528 if (!vgl)
529 return NULL;
531 vgl->gl = gl;
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; \
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 */
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);
578 GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv);
579 GET_PROC_ADDR_CORE_GL(TexEnvf);
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);
587 GET_PROC_ADDR(GetProgramiv);
588 GET_PROC_ADDR(GetShaderiv);
589 GET_PROC_ADDR(GetProgramInfoLog);
590 GET_PROC_ADDR(GetShaderInfoLog);
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);
606 GET_PROC_ADDR(CreateProgram);
607 GET_PROC_ADDR(LinkProgram);
608 GET_PROC_ADDR(UseProgram);
609 GET_PROC_ADDR(DeleteProgram);
611 GET_PROC_ADDR(ActiveTexture);
613 GET_PROC_ADDR(GenBuffers);
614 GET_PROC_ADDR(BindBuffer);
615 GET_PROC_ADDR(BufferData);
616 GET_PROC_ADDR(DeleteBuffers);
618 GET_PROC_ADDR_OPTIONAL(GetFramebufferAttachmentParameteriv);
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
630 GL_ASSERT_NOERROR();
632 const char *extensions = (const char *)vgl->vt.GetString(GL_EXTENSIONS);
633 assert(extensions);
634 if (!extensions)
636 msg_Err(gl, "glGetString returned NULL");
637 free(vgl);
638 return NULL;
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)
645 msg_Err(gl, "shaders not supported, bailing out");
646 free(vgl);
647 return NULL;
649 #endif
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);
656 if ((GLint)fmt->i_width > max_tex_size ||
657 (GLint)fmt->i_height > max_tex_size)
658 ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
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
671 bool b_dump_shaders = var_InheritInteger(gl, "verbose") >= 4;
673 vgl->sub_renderer =
674 vlc_gl_sub_renderer_New(gl, &vgl->vt, supports_npot);
675 if (!vgl->sub_renderer)
677 msg_Err(gl, "Could not create sub renderer");
678 free(vgl);
679 return NULL;
682 GL_ASSERT_NOERROR();
683 int ret = opengl_init_program(vgl, context, &vgl->prgm, fmt,
684 b_dump_shaders);
685 if (ret != VLC_SUCCESS)
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;
694 GL_ASSERT_NOERROR();
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;
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);
717 if (!interop->handle_texs_gen)
719 ret = vlc_gl_interop_GenerateTextures(interop,
720 vgl->tex_width, vgl->tex_height,
721 vgl->texture);
722 if (ret != VLC_SUCCESS)
724 vout_display_opengl_Delete(vgl);
725 return NULL;
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);
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);
741 if (vgl->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
742 && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
744 vout_display_opengl_Delete(vgl);
745 return NULL;
748 *fmt = vgl->fmt;
749 if (subpicture_chromas) {
750 *subpicture_chromas = gl_subpicture_chromas;
753 GL_ASSERT_NOERROR();
754 return vgl;
757 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
759 GL_ASSERT_NOERROR();
761 /* */
762 vgl->vt.Finish();
763 vgl->vt.Flush();
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;
769 vlc_gl_sub_renderer_Delete(vgl->sub_renderer);
771 opengl_deinit_program(vgl, &vgl->prgm);
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);
777 if (main_del_texs)
778 vgl->vt.DeleteTextures(main_tex_count, vgl->texture);
780 GL_ASSERT_NOERROR();
782 free(vgl);
785 static void UpdateZ(vout_display_opengl_t *vgl)
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)));
795 /* The FOV value above which z is dynamically calculated. */
796 const float z_thresh = 90.f;
798 if (vgl->f_fovx <= z_thresh * M_PI / 180)
799 vgl->f_z = 0;
800 else
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;
809 static void UpdateFOVy(vout_display_opengl_t *vgl)
811 vgl->f_fovy = 2 * atanf(tanf(vgl->f_fovx / 2) / vgl->f_sar);
814 int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
815 const vlc_viewpoint_t *p_vp)
817 if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
818 || p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
819 return VLC_EBADVAR;
821 // Convert degree into radian
822 float f_fovx = p_vp->fov * (float)M_PI / 180.f;
824 /* vgl->vp needs to be converted into world transform */
825 vlc_viewpoint_reverse(&vgl->vp, p_vp);
827 if (fabsf(f_fovx - vgl->f_fovx) >= 0.001f)
829 /* FOVx has changed. */
830 vgl->f_fovx = f_fovx;
831 UpdateFOVy(vgl);
832 UpdateZ(vgl);
834 getViewpointMatrixes(vgl, vgl->fmt.projection_mode, &vgl->prgm);
836 return VLC_SUCCESS;
840 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t *vgl,
841 float f_sar)
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);
852 void vout_display_opengl_Viewport(vout_display_opengl_t *vgl, int x, int y,
853 unsigned width, unsigned height)
855 vgl->vt.Viewport(x, y, width, height);
858 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
859 picture_t *picture, subpicture_t *subpicture)
861 GL_ASSERT_NOERROR();
863 struct vlc_gl_renderer *renderer = vgl->prgm.renderer;
864 const struct vlc_gl_interop *interop = renderer->interop;
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;
872 ret = vlc_gl_sub_renderer_Prepare(vgl->sub_renderer, subpicture);
873 GL_ASSERT_NOERROR();
874 return ret;
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)
883 unsigned nbLatBands = 128;
884 unsigned nbLonBands = 128;
886 *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
887 *nbIndices = nbLatBands * nbLonBands * 3 * 2;
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)
895 free(*vertexCoord);
896 return VLC_ENOMEM;
898 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
899 if (*indices == NULL)
901 free(*textureCoord);
902 free(*vertexCoord);
903 return VLC_ENOMEM;
906 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
907 float theta = lat * (float) M_PI / nbLatBands;
908 float sinTheta, cosTheta;
910 sincosf(theta, &sinTheta, &cosTheta);
912 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
913 float phi = lon * 2 * (float) M_PI / nbLonBands;
914 float sinPhi, cosPhi;
916 sincosf(phi, &sinPhi, &cosPhi);
918 float x = cosPhi * sinTheta;
919 float y = cosTheta;
920 float z = sinPhi * sinTheta;
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;
927 for (unsigned p = 0; p < nbPlanes; ++p)
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;
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;
946 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
948 (*indices)[off] = first;
949 (*indices)[off + 1] = second;
950 (*indices)[off + 2] = first + 1;
952 (*indices)[off + 3] = second;
953 (*indices)[off + 4] = second + 1;
954 (*indices)[off + 5] = first + 1;
958 return VLC_SUCCESS;
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)
969 *nbVertices = 4 * 6;
970 *nbIndices = 6 * 6;
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)
978 free(*vertexCoord);
979 return VLC_ENOMEM;
981 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
982 if (*indices == NULL)
984 free(*textureCoord);
985 free(*vertexCoord);
986 return VLC_ENOMEM;
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,
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,
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,
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,
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,
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,
1021 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1023 for (unsigned p = 0; p < nbPlanes; ++p)
1025 float width = right[p] - left[p];
1026 float height = bottom[p] - top[p];
1028 float col[] = {left[p],
1029 left[p] + width * 1.f/3,
1030 left[p] + width * 2.f/3,
1031 left[p] + width};
1033 float row[] = {top[p],
1034 top[p] + height * 1.f/2,
1035 top[p] + height};
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,
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,
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,
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,
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,
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,
1069 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1070 *nbVertices * 2 * sizeof(GLfloat));
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
1082 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1084 return VLC_SUCCESS;
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)
1093 *nbVertices = 4;
1094 *nbIndices = 6;
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)
1102 free(*vertexCoord);
1103 return VLC_ENOMEM;
1105 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1106 if (*indices == NULL)
1108 free(*textureCoord);
1109 free(*vertexCoord);
1110 return VLC_ENOMEM;
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
1120 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1122 for (unsigned p = 0; p < nbPlanes; ++p)
1124 const GLfloat tex[] = {
1125 left[p], top[p],
1126 left[p], bottom[p],
1127 right[p], top[p],
1128 right[p], bottom[p]
1131 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1132 *nbVertices * 2 * sizeof(GLfloat));
1135 const GLushort ind[] = {
1136 0, 1, 2,
1137 2, 1, 3
1140 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1142 return VLC_SUCCESS;
1145 static int SetupCoords(vout_display_opengl_t *vgl,
1146 const float *left, const float *top,
1147 const float *right, const float *bottom)
1149 const struct vlc_gl_interop *interop = vgl->prgm.renderer->interop;
1151 GLfloat *vertexCoord, *textureCoord;
1152 GLushort *indices;
1153 unsigned nbVertices, nbIndices;
1155 int i_ret;
1156 switch (vgl->fmt.projection_mode)
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;
1183 if (i_ret != VLC_SUCCESS)
1184 return i_ret;
1186 for (unsigned j = 0; j < interop->tex_count; j++)
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);
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);
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);
1201 free(textureCoord);
1202 free(vertexCoord);
1203 free(indices);
1205 vgl->nb_indices = nbIndices;
1207 return VLC_SUCCESS;
1210 static void DrawWithShaders(vout_display_opengl_t *vgl, struct prgm *prgm)
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);
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]);
1221 vgl->vt.BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
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);
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);
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;
1240 vgl->vt.UniformMatrix4fv(prgm->uloc.TransformMatrix, 1, GL_FALSE, tm);
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);
1251 vgl->vt.DrawElements(GL_TRIANGLES, vgl->nb_indices, GL_UNSIGNED_SHORT, 0);
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)
1261 for (unsigned i = 0; i < i_nbTextures; ++i)
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];
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];
1273 static void TextureCropForStereo(vout_display_opengl_t *vgl,
1274 float *left, float *top,
1275 float *right, float *bottom)
1277 const struct vlc_gl_interop *interop = vgl->prgm.renderer->interop;
1279 float stereoCoefs[2];
1280 float stereoOffsets[2];
1282 switch (vgl->fmt.multiview_mode)
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;
1305 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
1306 const video_format_t *source)
1308 GL_ASSERT_NOERROR();
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);
1315 vgl->vt.UseProgram(vgl->prgm.id);
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)
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++)
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];
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.
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;
1352 TextureCropForStereo(vgl, left, top, right, bottom);
1353 int ret = SetupCoords(vgl, left, top, right, bottom);
1354 if (ret != VLC_SUCCESS)
1355 return ret;
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;
1362 DrawWithShaders(vgl, &vgl->prgm);
1364 int ret = vlc_gl_sub_renderer_Draw(vgl->sub_renderer);
1365 if (ret != VLC_SUCCESS)
1366 return ret;
1368 /* Display */
1369 vlc_gl_Swap(vgl->gl);
1371 GL_ASSERT_NOERROR();
1373 return VLC_SUCCESS;