opengl: use vlc_gl_renderer when possible
[vlc.git] / modules / video_output / opengl / vout_helper.c
blob6a502543b8fce227f00e5e7ff193558063bb3877
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 vout_display_opengl_t {
51 vlc_gl_t *gl;
52 opengl_vtable_t vt;
54 struct vlc_gl_renderer *renderer;
55 struct vlc_gl_sub_renderer *sub_renderer;
58 static const vlc_fourcc_t gl_subpicture_chromas[] = {
59 VLC_CODEC_RGBA,
63 static const GLfloat identity[] = {
64 1.0f, 0.0f, 0.0f, 0.0f,
65 0.0f, 1.0f, 0.0f, 0.0f,
66 0.0f, 0.0f, 1.0f, 0.0f,
67 0.0f, 0.0f, 0.0f, 1.0f
70 static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {
72 const GLfloat m[] = {
73 /* x y z w */
74 1.0f, 0.0f, 0.0f, 0.0f,
75 0.0f, 1.0f, 0.0f, 0.0f,
76 0.0f, 0.0f, 1.0f, 0.0f,
77 0.0f, 0.0f, zoom, 1.0f
80 memcpy(matrix, m, sizeof(m));
83 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
84 static void getProjectionMatrix(float sar, float fovy, GLfloat matrix[static 16]) {
86 float zFar = 1000;
87 float zNear = 0.01;
89 float f = 1.f / tanf(fovy / 2.f);
91 const GLfloat m[] = {
92 f / sar, 0.f, 0.f, 0.f,
93 0.f, f, 0.f, 0.f,
94 0.f, 0.f, (zNear + zFar) / (zNear - zFar), -1.f,
95 0.f, 0.f, (2 * zNear * zFar) / (zNear - zFar), 0.f};
97 memcpy(matrix, m, sizeof(m));
100 static void getViewpointMatrixes(struct vlc_gl_renderer *renderer,
101 video_projection_mode_t projection_mode)
103 if (projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
104 || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
106 getProjectionMatrix(renderer->f_sar, renderer->f_fovy,
107 renderer->var.ProjectionMatrix);
108 getZoomMatrix(renderer->f_z, renderer->var.ZoomMatrix);
110 /* renderer->vp has been reversed and is a world transform */
111 vlc_viewpoint_to_4x4(&renderer->vp, renderer->var.ViewMatrix);
113 else
115 memcpy(renderer->var.ProjectionMatrix, identity, sizeof(identity));
116 memcpy(renderer->var.ZoomMatrix, identity, sizeof(identity));
117 memcpy(renderer->var.ViewMatrix, identity, sizeof(identity));
122 static void getOrientationTransformMatrix(video_orientation_t orientation,
123 GLfloat matrix[static 16])
125 memcpy(matrix, identity, sizeof(identity));
127 const int k_cos_pi = -1;
128 const int k_cos_pi_2 = 0;
129 const int k_cos_n_pi_2 = 0;
131 const int k_sin_pi = 0;
132 const int k_sin_pi_2 = 1;
133 const int k_sin_n_pi_2 = -1;
135 switch (orientation) {
137 case ORIENT_ROTATED_90:
138 matrix[0 * 4 + 0] = k_cos_pi_2;
139 matrix[0 * 4 + 1] = -k_sin_pi_2;
140 matrix[1 * 4 + 0] = k_sin_pi_2;
141 matrix[1 * 4 + 1] = k_cos_pi_2;
142 matrix[3 * 4 + 1] = 1;
143 break;
144 case ORIENT_ROTATED_180:
145 matrix[0 * 4 + 0] = k_cos_pi;
146 matrix[0 * 4 + 1] = -k_sin_pi;
147 matrix[1 * 4 + 0] = k_sin_pi;
148 matrix[1 * 4 + 1] = k_cos_pi;
149 matrix[3 * 4 + 0] = 1;
150 matrix[3 * 4 + 1] = 1;
151 break;
152 case ORIENT_ROTATED_270:
153 matrix[0 * 4 + 0] = k_cos_n_pi_2;
154 matrix[0 * 4 + 1] = -k_sin_n_pi_2;
155 matrix[1 * 4 + 0] = k_sin_n_pi_2;
156 matrix[1 * 4 + 1] = k_cos_n_pi_2;
157 matrix[3 * 4 + 0] = 1;
158 break;
159 case ORIENT_HFLIPPED:
160 matrix[0 * 4 + 0] = -1;
161 matrix[3 * 4 + 0] = 1;
162 break;
163 case ORIENT_VFLIPPED:
164 matrix[1 * 4 + 1] = -1;
165 matrix[3 * 4 + 1] = 1;
166 break;
167 case ORIENT_TRANSPOSED:
168 matrix[0 * 4 + 0] = 0;
169 matrix[1 * 4 + 1] = 0;
170 matrix[2 * 4 + 2] = -1;
171 matrix[0 * 4 + 1] = 1;
172 matrix[1 * 4 + 0] = 1;
173 break;
174 case ORIENT_ANTI_TRANSPOSED:
175 matrix[0 * 4 + 0] = 0;
176 matrix[1 * 4 + 1] = 0;
177 matrix[2 * 4 + 2] = -1;
178 matrix[0 * 4 + 1] = -1;
179 matrix[1 * 4 + 0] = -1;
180 matrix[3 * 4 + 0] = 1;
181 matrix[3 * 4 + 1] = 1;
182 break;
183 default:
184 break;
188 static GLuint BuildVertexShader(const struct vlc_gl_renderer *renderer,
189 unsigned plane_count)
191 const opengl_vtable_t *vt = renderer->vt;
193 /* Basic vertex shader */
194 static const char *template =
195 "#version %u\n"
196 "varying vec2 TexCoord0;\n"
197 "attribute vec4 MultiTexCoord0;\n"
198 "%s%s"
199 "attribute vec3 VertexPosition;\n"
200 "uniform mat4 TransformMatrix;\n"
201 "uniform mat4 OrientationMatrix;\n"
202 "uniform mat4 ProjectionMatrix;\n"
203 "uniform mat4 ZoomMatrix;\n"
204 "uniform mat4 ViewMatrix;\n"
205 "void main() {\n"
206 " TexCoord0 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord0).st;\n"
207 "%s%s"
208 " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
209 " * vec4(VertexPosition, 1.0);\n"
210 "}";
212 const char *coord1_header = plane_count > 1 ?
213 "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
214 const char *coord1_code = plane_count > 1 ?
215 " TexCoord1 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord1).st;\n" : "";
216 const char *coord2_header = plane_count > 2 ?
217 "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
218 const char *coord2_code = plane_count > 2 ?
219 " TexCoord2 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord2).st;\n" : "";
221 char *code;
222 if (asprintf(&code, template, renderer->glsl_version, coord1_header,
223 coord2_header, coord1_code, coord2_code) < 0)
224 return 0;
226 GLuint shader = vt->CreateShader(GL_VERTEX_SHADER);
227 vt->ShaderSource(shader, 1, (const char **) &code, NULL);
228 if (renderer->b_dump_shaders)
229 msg_Dbg(renderer->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
230 (const char *) &renderer->interop->fmt.i_chroma, code);
231 vt->CompileShader(shader);
232 free(code);
233 return shader;
236 static int
237 opengl_link_program(struct vlc_gl_renderer *renderer)
239 struct vlc_gl_interop *interop = renderer->interop;
240 const opengl_vtable_t *vt = renderer->vt;
242 GLuint vertex_shader = BuildVertexShader(renderer, interop->tex_count);
243 if (!vertex_shader)
244 return VLC_EGENERIC;
246 GLuint fragment_shader =
247 opengl_fragment_shader_init(renderer, interop->tex_target,
248 interop->sw_fmt.i_chroma,
249 interop->sw_fmt.space);
250 if (!fragment_shader)
251 return VLC_EGENERIC;
253 assert(interop->tex_target != 0 &&
254 interop->tex_count > 0 &&
255 interop->ops->update_textures != NULL &&
256 renderer->pf_fetch_locations != NULL &&
257 renderer->pf_prepare_shader != NULL);
259 GLuint shaders[] = { fragment_shader, vertex_shader };
261 /* Check shaders messages */
262 for (unsigned i = 0; i < 2; i++) {
263 int infoLength;
264 vt->GetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLength);
265 if (infoLength <= 1)
266 continue;
268 char *infolog = malloc(infoLength);
269 if (infolog != NULL)
271 int charsWritten;
272 vt->GetShaderInfoLog(shaders[i], infoLength, &charsWritten,
273 infolog);
274 msg_Err(renderer->gl, "shader %u: %s", i, infolog);
275 free(infolog);
279 GLuint program_id = renderer->program_id = vt->CreateProgram();
280 vt->AttachShader(program_id, fragment_shader);
281 vt->AttachShader(program_id, vertex_shader);
282 vt->LinkProgram(program_id);
284 vt->DeleteShader(vertex_shader);
285 vt->DeleteShader(fragment_shader);
287 /* Check program messages */
288 int infoLength = 0;
289 vt->GetProgramiv(program_id, GL_INFO_LOG_LENGTH, &infoLength);
290 if (infoLength > 1)
292 char *infolog = malloc(infoLength);
293 if (infolog != NULL)
295 int charsWritten;
296 vt->GetProgramInfoLog(program_id, infoLength, &charsWritten,
297 infolog);
298 msg_Err(renderer->gl, "shader program: %s", infolog);
299 free(infolog);
302 /* If there is some message, better to check linking is ok */
303 GLint link_status = GL_TRUE;
304 vt->GetProgramiv(program_id, GL_LINK_STATUS, &link_status);
305 if (link_status == GL_FALSE)
307 msg_Err(renderer->gl, "Unable to use program");
308 goto error;
312 /* Fetch UniformLocations and AttribLocations */
313 #define GET_LOC(type, x, str) do { \
314 x = vt->Get##type##Location(program_id, str); \
315 assert(x != -1); \
316 if (x == -1) { \
317 msg_Err(renderer->gl, "Unable to Get"#type"Location(%s)", str); \
318 goto error; \
320 } while (0)
321 #define GET_ULOC(x, str) GET_LOC(Uniform, renderer->uloc.x, str)
322 #define GET_ALOC(x, str) GET_LOC(Attrib, renderer->aloc.x, str)
323 GET_ULOC(TransformMatrix, "TransformMatrix");
324 GET_ULOC(OrientationMatrix, "OrientationMatrix");
325 GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
326 GET_ULOC(ViewMatrix, "ViewMatrix");
327 GET_ULOC(ZoomMatrix, "ZoomMatrix");
329 GET_ALOC(VertexPosition, "VertexPosition");
330 GET_ALOC(MultiTexCoord[0], "MultiTexCoord0");
331 /* MultiTexCoord 1 and 2 can be optimized out if not used */
332 if (interop->tex_count > 1)
333 GET_ALOC(MultiTexCoord[1], "MultiTexCoord1");
334 else
335 renderer->aloc.MultiTexCoord[1] = -1;
336 if (interop->tex_count > 2)
337 GET_ALOC(MultiTexCoord[2], "MultiTexCoord2");
338 else
339 renderer->aloc.MultiTexCoord[2] = -1;
340 #undef GET_LOC
341 #undef GET_ULOC
342 #undef GET_ALOC
343 int ret = renderer->pf_fetch_locations(renderer, program_id);
344 assert(ret == VLC_SUCCESS);
345 if (ret != VLC_SUCCESS)
347 msg_Err(renderer->gl, "Unable to get locations from tex_conv");
348 goto error;
351 return VLC_SUCCESS;
353 error:
354 vt->DeleteProgram(program_id);
355 renderer->program_id = 0;
356 return VLC_EGENERIC;
359 static void
360 opengl_deinit_program(vout_display_opengl_t *vgl)
362 struct vlc_gl_renderer *renderer = vgl->renderer;
363 vlc_gl_interop_Delete(renderer->interop);
364 if (renderer->program_id != 0)
365 vgl->vt.DeleteProgram(renderer->program_id);
367 #ifdef HAVE_LIBPLACEBO
368 FREENULL(renderer->uloc.pl_vars);
369 if (renderer->pl_ctx)
370 pl_context_destroy(&renderer->pl_ctx);
371 #endif
373 free(renderer);
376 static int
377 opengl_init_program(vout_display_opengl_t *vgl, vlc_video_context *context,
378 const video_format_t *fmt, bool b_dump_shaders)
380 struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
381 if (!renderer)
382 return VLC_ENOMEM;
384 struct vlc_gl_interop *interop =
385 vlc_gl_interop_New(vgl->gl, &vgl->vt, context, fmt, false);
386 if (!interop)
388 free(renderer);
389 return VLC_ENOMEM;
392 renderer->interop = interop;
394 renderer->gl = vgl->gl;
395 renderer->vt = &vgl->vt;
396 renderer->b_dump_shaders = b_dump_shaders;
397 #if defined(USE_OPENGL_ES2)
398 renderer->glsl_version = 100;
399 renderer->glsl_precision_header = "precision highp float;\n";
400 #else
401 renderer->glsl_version = 120;
402 renderer->glsl_precision_header = "";
403 #endif
405 #ifdef HAVE_LIBPLACEBO
406 // Create the main libplacebo context
407 renderer->pl_ctx = vlc_placebo_Create(VLC_OBJECT(vgl->gl));
408 if (renderer->pl_ctx) {
409 # if PL_API_VER >= 20
410 renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL);
411 # elif PL_API_VER >= 6
412 renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL, 0);
413 # else
414 renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL, 0, 0);
415 # endif
417 #endif
419 vgl->renderer = renderer;
421 int ret = opengl_link_program(renderer);
422 if (ret != VLC_SUCCESS)
424 opengl_deinit_program(vgl);
425 return VLC_EGENERIC;
428 getOrientationTransformMatrix(interop->fmt.orientation,
429 renderer->var.OrientationMatrix);
430 getViewpointMatrixes(renderer, interop->fmt.projection_mode);
432 return VLC_SUCCESS;
435 static void
436 ResizeFormatToGLMaxTexSize(video_format_t *fmt, unsigned int max_tex_size)
438 if (fmt->i_width > fmt->i_height)
440 unsigned int const vis_w = fmt->i_visible_width;
441 unsigned int const vis_h = fmt->i_visible_height;
442 unsigned int const nw_w = max_tex_size;
443 unsigned int const nw_vis_w = nw_w * vis_w / fmt->i_width;
445 fmt->i_height = nw_w * fmt->i_height / fmt->i_width;
446 fmt->i_width = nw_w;
447 fmt->i_visible_height = nw_vis_w * vis_h / vis_w;
448 fmt->i_visible_width = nw_vis_w;
450 else
452 unsigned int const vis_w = fmt->i_visible_width;
453 unsigned int const vis_h = fmt->i_visible_height;
454 unsigned int const nw_h = max_tex_size;
455 unsigned int const nw_vis_h = nw_h * vis_h / fmt->i_height;
457 fmt->i_width = nw_h * fmt->i_width / fmt->i_height;
458 fmt->i_height = nw_h;
459 fmt->i_visible_width = nw_vis_h * vis_w / vis_h;
460 fmt->i_visible_height = nw_vis_h;
464 vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
465 const vlc_fourcc_t **subpicture_chromas,
466 vlc_gl_t *gl,
467 const vlc_viewpoint_t *viewpoint,
468 vlc_video_context *context)
470 vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
471 if (!vgl)
472 return NULL;
474 vgl->gl = gl;
476 #if defined(USE_OPENGL_ES2) || defined(HAVE_GL_CORE_SYMBOLS)
477 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
478 #else
479 #define GET_PROC_ADDR_CORE(name) GET_PROC_ADDR_EXT(name, true)
480 #endif
481 #define GET_PROC_ADDR_EXT(name, critical) do { \
482 vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
483 if (vgl->vt.name == NULL && critical) { \
484 msg_Err(gl, "gl"#name" symbol not found, bailing out"); \
485 free(vgl); \
486 return NULL; \
488 } while(0)
489 #if defined(USE_OPENGL_ES2)
490 #define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
491 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_EXT(name, false) /* optional for GLES */
492 #else
493 #define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
494 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
495 #endif
496 #define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
498 GET_PROC_ADDR_CORE(BindTexture);
499 GET_PROC_ADDR_CORE(BlendFunc);
500 GET_PROC_ADDR_CORE(Clear);
501 GET_PROC_ADDR_CORE(ClearColor);
502 GET_PROC_ADDR_CORE(DeleteTextures);
503 GET_PROC_ADDR_CORE(DepthMask);
504 GET_PROC_ADDR_CORE(Disable);
505 GET_PROC_ADDR_CORE(DrawArrays);
506 GET_PROC_ADDR_CORE(DrawElements);
507 GET_PROC_ADDR_CORE(Enable);
508 GET_PROC_ADDR_CORE(Finish);
509 GET_PROC_ADDR_CORE(Flush);
510 GET_PROC_ADDR_CORE(GenTextures);
511 GET_PROC_ADDR_CORE(GetError);
512 GET_PROC_ADDR_CORE(GetIntegerv);
513 GET_PROC_ADDR_CORE(GetString);
514 GET_PROC_ADDR_CORE(PixelStorei);
515 GET_PROC_ADDR_CORE(TexImage2D);
516 GET_PROC_ADDR_CORE(TexParameterf);
517 GET_PROC_ADDR_CORE(TexParameteri);
518 GET_PROC_ADDR_CORE(TexSubImage2D);
519 GET_PROC_ADDR_CORE(Viewport);
521 GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv);
522 GET_PROC_ADDR_CORE_GL(TexEnvf);
524 GET_PROC_ADDR(CreateShader);
525 GET_PROC_ADDR(ShaderSource);
526 GET_PROC_ADDR(CompileShader);
527 GET_PROC_ADDR(AttachShader);
528 GET_PROC_ADDR(DeleteShader);
530 GET_PROC_ADDR(GetProgramiv);
531 GET_PROC_ADDR(GetShaderiv);
532 GET_PROC_ADDR(GetProgramInfoLog);
533 GET_PROC_ADDR(GetShaderInfoLog);
535 GET_PROC_ADDR(GetUniformLocation);
536 GET_PROC_ADDR(GetAttribLocation);
537 GET_PROC_ADDR(VertexAttribPointer);
538 GET_PROC_ADDR(EnableVertexAttribArray);
539 GET_PROC_ADDR(UniformMatrix4fv);
540 GET_PROC_ADDR(UniformMatrix3fv);
541 GET_PROC_ADDR(UniformMatrix2fv);
542 GET_PROC_ADDR(Uniform4fv);
543 GET_PROC_ADDR(Uniform4f);
544 GET_PROC_ADDR(Uniform3f);
545 GET_PROC_ADDR(Uniform2f);
546 GET_PROC_ADDR(Uniform1f);
547 GET_PROC_ADDR(Uniform1i);
549 GET_PROC_ADDR(CreateProgram);
550 GET_PROC_ADDR(LinkProgram);
551 GET_PROC_ADDR(UseProgram);
552 GET_PROC_ADDR(DeleteProgram);
554 GET_PROC_ADDR(ActiveTexture);
556 GET_PROC_ADDR(GenBuffers);
557 GET_PROC_ADDR(BindBuffer);
558 GET_PROC_ADDR(BufferData);
559 GET_PROC_ADDR(DeleteBuffers);
561 GET_PROC_ADDR_OPTIONAL(GetFramebufferAttachmentParameteriv);
563 GET_PROC_ADDR_OPTIONAL(BufferSubData);
564 GET_PROC_ADDR_OPTIONAL(BufferStorage);
565 GET_PROC_ADDR_OPTIONAL(MapBufferRange);
566 GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange);
567 GET_PROC_ADDR_OPTIONAL(UnmapBuffer);
568 GET_PROC_ADDR_OPTIONAL(FenceSync);
569 GET_PROC_ADDR_OPTIONAL(DeleteSync);
570 GET_PROC_ADDR_OPTIONAL(ClientWaitSync);
571 #undef GET_PROC_ADDR
573 GL_ASSERT_NOERROR();
575 const char *extensions = (const char *)vgl->vt.GetString(GL_EXTENSIONS);
576 assert(extensions);
577 if (!extensions)
579 msg_Err(gl, "glGetString returned NULL");
580 free(vgl);
581 return NULL;
583 #if !defined(USE_OPENGL_ES2)
584 const unsigned char *ogl_version = vgl->vt.GetString(GL_VERSION);
585 bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
586 if (!supports_shaders)
588 msg_Err(gl, "shaders not supported, bailing out");
589 free(vgl);
590 return NULL;
592 #endif
594 /* Resize the format if it is greater than the maximum texture size
595 * supported by the hardware */
596 GLint max_tex_size;
597 vgl->vt.GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
599 if ((GLint)fmt->i_width > max_tex_size ||
600 (GLint)fmt->i_height > max_tex_size)
601 ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
603 /* Non-power-of-2 texture size support */
604 bool supports_npot;
605 #if defined(USE_OPENGL_ES2)
606 /* OpenGL ES 2 includes support for non-power of 2 textures by specification
607 * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
608 supports_npot = true;
609 #else
610 supports_npot = vlc_gl_StrHasToken(extensions, "GL_ARB_texture_non_power_of_two") ||
611 vlc_gl_StrHasToken(extensions, "GL_APPLE_texture_2D_limited_npot");
612 #endif
614 bool b_dump_shaders = var_InheritInteger(gl, "verbose") >= 4;
616 vgl->sub_renderer =
617 vlc_gl_sub_renderer_New(gl, &vgl->vt, supports_npot);
618 if (!vgl->sub_renderer)
620 msg_Err(gl, "Could not create sub renderer");
621 free(vgl);
622 return NULL;
625 GL_ASSERT_NOERROR();
626 int ret = opengl_init_program(vgl, context, fmt, b_dump_shaders);
627 if (ret != VLC_SUCCESS)
629 msg_Warn(gl, "could not init tex converter for %4.4s",
630 (const char *) &fmt->i_chroma);
631 vlc_gl_sub_renderer_Delete(vgl->sub_renderer);
632 free(vgl);
633 return NULL;
636 GL_ASSERT_NOERROR();
638 struct vlc_gl_renderer *renderer = vgl->renderer;
639 const struct vlc_gl_interop *interop = renderer->interop;
640 /* Update the fmt to main program one */
641 renderer->fmt = interop->fmt;
642 /* The orientation is handled by the orientation matrix */
643 renderer->fmt.orientation = fmt->orientation;
645 /* Texture size */
646 for (unsigned j = 0; j < interop->tex_count; j++) {
647 const GLsizei w = renderer->fmt.i_visible_width * interop->texs[j].w.num
648 / interop->texs[j].w.den;
649 const GLsizei h = renderer->fmt.i_visible_height * interop->texs[j].h.num
650 / interop->texs[j].h.den;
651 if (supports_npot) {
652 renderer->tex_width[j] = w;
653 renderer->tex_height[j] = h;
654 } else {
655 renderer->tex_width[j] = vlc_align_pot(w);
656 renderer->tex_height[j] = vlc_align_pot(h);
660 if (!interop->handle_texs_gen)
662 ret = vlc_gl_interop_GenerateTextures(interop, renderer->tex_width,
663 renderer->tex_height,
664 renderer->textures);
665 if (ret != VLC_SUCCESS)
667 vout_display_opengl_Delete(vgl);
668 return NULL;
672 /* */
673 vgl->vt.Disable(GL_BLEND);
674 vgl->vt.Disable(GL_DEPTH_TEST);
675 vgl->vt.DepthMask(GL_FALSE);
676 vgl->vt.Enable(GL_CULL_FACE);
677 vgl->vt.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
678 vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
680 vgl->vt.GenBuffers(1, &renderer->vertex_buffer_object);
681 vgl->vt.GenBuffers(1, &renderer->index_buffer_object);
682 vgl->vt.GenBuffers(interop->tex_count, renderer->texture_buffer_object);
684 if (renderer->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
685 && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
687 vout_display_opengl_Delete(vgl);
688 return NULL;
691 *fmt = renderer->fmt;
692 if (subpicture_chromas) {
693 *subpicture_chromas = gl_subpicture_chromas;
696 GL_ASSERT_NOERROR();
697 return vgl;
700 void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
702 GL_ASSERT_NOERROR();
704 /* */
705 vgl->vt.Finish();
706 vgl->vt.Flush();
708 struct vlc_gl_renderer *renderer = vgl->renderer;
709 const struct vlc_gl_interop *interop = renderer->interop;
710 const size_t main_tex_count = interop->tex_count;
711 const bool main_del_texs = !interop->handle_texs_gen;
713 vlc_gl_sub_renderer_Delete(vgl->sub_renderer);
715 opengl_deinit_program(vgl);
717 vgl->vt.DeleteBuffers(1, &renderer->vertex_buffer_object);
718 vgl->vt.DeleteBuffers(1, &renderer->index_buffer_object);
719 vgl->vt.DeleteBuffers(main_tex_count, renderer->texture_buffer_object);
721 if (main_del_texs)
722 vgl->vt.DeleteTextures(main_tex_count, renderer->textures);
724 GL_ASSERT_NOERROR();
726 free(vgl);
729 static void UpdateZ(struct vlc_gl_renderer *renderer)
731 /* Do trigonometry to calculate the minimal z value
732 * that will allow us to zoom out without seeing the outside of the
733 * sphere (black borders). */
734 float tan_fovx_2 = tanf(renderer->f_fovx / 2);
735 float tan_fovy_2 = tanf(renderer->f_fovy / 2);
736 float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
737 tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
739 /* The FOV value above which z is dynamically calculated. */
740 const float z_thresh = 90.f;
742 if (renderer->f_fovx <= z_thresh * M_PI / 180)
743 renderer->f_z = 0;
744 else
746 float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
747 renderer->f_z = f * renderer->f_fovx - f * z_thresh * M_PI / 180;
748 if (renderer->f_z < z_min)
749 renderer->f_z = z_min;
753 static void UpdateFOVy(struct vlc_gl_renderer *renderer)
755 renderer->f_fovy = 2 * atanf(tanf(renderer->f_fovx / 2) / renderer->f_sar);
758 int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
759 const vlc_viewpoint_t *p_vp)
761 struct vlc_gl_renderer *renderer = vgl->renderer;
762 if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
763 || p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
764 return VLC_EBADVAR;
766 // Convert degree into radian
767 float f_fovx = p_vp->fov * (float)M_PI / 180.f;
769 /* vgl->vp needs to be converted into world transform */
770 vlc_viewpoint_reverse(&renderer->vp, p_vp);
772 if (fabsf(f_fovx - renderer->f_fovx) >= 0.001f)
774 /* FOVx has changed. */
775 renderer->f_fovx = f_fovx;
776 UpdateFOVy(renderer);
777 UpdateZ(renderer);
779 getViewpointMatrixes(renderer, renderer->fmt.projection_mode);
781 return VLC_SUCCESS;
785 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t *vgl,
786 float f_sar)
788 struct vlc_gl_renderer *renderer = vgl->renderer;
789 /* Each time the window size changes, we must recompute the minimum zoom
790 * since the aspect ration changes.
791 * We must also set the new current zoom value. */
792 renderer->f_sar = f_sar;
793 UpdateFOVy(renderer);
794 UpdateZ(renderer);
795 getViewpointMatrixes(renderer, renderer->fmt.projection_mode);
798 void vout_display_opengl_Viewport(vout_display_opengl_t *vgl, int x, int y,
799 unsigned width, unsigned height)
801 vgl->vt.Viewport(x, y, width, height);
804 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
805 picture_t *picture, subpicture_t *subpicture)
807 GL_ASSERT_NOERROR();
809 struct vlc_gl_renderer *renderer = vgl->renderer;
810 const struct vlc_gl_interop *interop = renderer->interop;
812 /* Update the texture */
813 int ret = interop->ops->update_textures(interop, renderer->textures,
814 renderer->tex_width,
815 renderer->tex_height, picture,
816 NULL);
817 if (ret != VLC_SUCCESS)
818 return ret;
820 ret = vlc_gl_sub_renderer_Prepare(vgl->sub_renderer, subpicture);
821 GL_ASSERT_NOERROR();
822 return ret;
825 static int BuildSphere(unsigned nbPlanes,
826 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
827 GLushort **indices, unsigned *nbIndices,
828 const float *left, const float *top,
829 const float *right, const float *bottom)
831 unsigned nbLatBands = 128;
832 unsigned nbLonBands = 128;
834 *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
835 *nbIndices = nbLatBands * nbLonBands * 3 * 2;
837 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
838 if (*vertexCoord == NULL)
839 return VLC_ENOMEM;
840 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
841 if (*textureCoord == NULL)
843 free(*vertexCoord);
844 return VLC_ENOMEM;
846 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
847 if (*indices == NULL)
849 free(*textureCoord);
850 free(*vertexCoord);
851 return VLC_ENOMEM;
854 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
855 float theta = lat * (float) M_PI / nbLatBands;
856 float sinTheta, cosTheta;
858 sincosf(theta, &sinTheta, &cosTheta);
860 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
861 float phi = lon * 2 * (float) M_PI / nbLonBands;
862 float sinPhi, cosPhi;
864 sincosf(phi, &sinPhi, &cosPhi);
866 float x = cosPhi * sinTheta;
867 float y = cosTheta;
868 float z = sinPhi * sinTheta;
870 unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
871 (*vertexCoord)[off1] = SPHERE_RADIUS * x;
872 (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
873 (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
875 for (unsigned p = 0; p < nbPlanes; ++p)
877 unsigned off2 = (p * (nbLatBands + 1) * (nbLonBands + 1)
878 + lat * (nbLonBands + 1) + lon) * 2;
879 float width = right[p] - left[p];
880 float height = bottom[p] - top[p];
881 float u = (float)lon / nbLonBands * width;
882 float v = (float)lat / nbLatBands * height;
883 (*textureCoord)[off2] = u;
884 (*textureCoord)[off2 + 1] = v;
889 for (unsigned lat = 0; lat < nbLatBands; lat++) {
890 for (unsigned lon = 0; lon < nbLonBands; lon++) {
891 unsigned first = (lat * (nbLonBands + 1)) + lon;
892 unsigned second = first + nbLonBands + 1;
894 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
896 (*indices)[off] = first;
897 (*indices)[off + 1] = second;
898 (*indices)[off + 2] = first + 1;
900 (*indices)[off + 3] = second;
901 (*indices)[off + 4] = second + 1;
902 (*indices)[off + 5] = first + 1;
906 return VLC_SUCCESS;
910 static int BuildCube(unsigned nbPlanes,
911 float padW, float padH,
912 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
913 GLushort **indices, unsigned *nbIndices,
914 const float *left, const float *top,
915 const float *right, const float *bottom)
917 *nbVertices = 4 * 6;
918 *nbIndices = 6 * 6;
920 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
921 if (*vertexCoord == NULL)
922 return VLC_ENOMEM;
923 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
924 if (*textureCoord == NULL)
926 free(*vertexCoord);
927 return VLC_ENOMEM;
929 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
930 if (*indices == NULL)
932 free(*textureCoord);
933 free(*vertexCoord);
934 return VLC_ENOMEM;
937 static const GLfloat coord[] = {
938 -1.0, 1.0, -1.0f, // front
939 -1.0, -1.0, -1.0f,
940 1.0, 1.0, -1.0f,
941 1.0, -1.0, -1.0f,
943 -1.0, 1.0, 1.0f, // back
944 -1.0, -1.0, 1.0f,
945 1.0, 1.0, 1.0f,
946 1.0, -1.0, 1.0f,
948 -1.0, 1.0, -1.0f, // left
949 -1.0, -1.0, -1.0f,
950 -1.0, 1.0, 1.0f,
951 -1.0, -1.0, 1.0f,
953 1.0f, 1.0, -1.0f, // right
954 1.0f, -1.0, -1.0f,
955 1.0f, 1.0, 1.0f,
956 1.0f, -1.0, 1.0f,
958 -1.0, -1.0, 1.0f, // bottom
959 -1.0, -1.0, -1.0f,
960 1.0, -1.0, 1.0f,
961 1.0, -1.0, -1.0f,
963 -1.0, 1.0, 1.0f, // top
964 -1.0, 1.0, -1.0f,
965 1.0, 1.0, 1.0f,
966 1.0, 1.0, -1.0f,
969 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
971 for (unsigned p = 0; p < nbPlanes; ++p)
973 float width = right[p] - left[p];
974 float height = bottom[p] - top[p];
976 float col[] = {left[p],
977 left[p] + width * 1.f/3,
978 left[p] + width * 2.f/3,
979 left[p] + width};
981 float row[] = {top[p],
982 top[p] + height * 1.f/2,
983 top[p] + height};
985 const GLfloat tex[] = {
986 col[1] + padW, row[1] + padH, // front
987 col[1] + padW, row[2] - padH,
988 col[2] - padW, row[1] + padH,
989 col[2] - padW, row[2] - padH,
991 col[3] - padW, row[1] + padH, // back
992 col[3] - padW, row[2] - padH,
993 col[2] + padW, row[1] + padH,
994 col[2] + padW, row[2] - padH,
996 col[2] - padW, row[0] + padH, // left
997 col[2] - padW, row[1] - padH,
998 col[1] + padW, row[0] + padH,
999 col[1] + padW, row[1] - padH,
1001 col[0] + padW, row[0] + padH, // right
1002 col[0] + padW, row[1] - padH,
1003 col[1] - padW, row[0] + padH,
1004 col[1] - padW, row[1] - padH,
1006 col[0] + padW, row[2] - padH, // bottom
1007 col[0] + padW, row[1] + padH,
1008 col[1] - padW, row[2] - padH,
1009 col[1] - padW, row[1] + padH,
1011 col[2] + padW, row[0] + padH, // top
1012 col[2] + padW, row[1] - padH,
1013 col[3] - padW, row[0] + padH,
1014 col[3] - padW, row[1] - padH,
1017 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1018 *nbVertices * 2 * sizeof(GLfloat));
1021 const GLushort ind[] = {
1022 0, 1, 2, 2, 1, 3, // front
1023 6, 7, 4, 4, 7, 5, // back
1024 10, 11, 8, 8, 11, 9, // left
1025 12, 13, 14, 14, 13, 15, // right
1026 18, 19, 16, 16, 19, 17, // bottom
1027 20, 21, 22, 22, 21, 23, // top
1030 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1032 return VLC_SUCCESS;
1035 static int BuildRectangle(unsigned nbPlanes,
1036 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
1037 GLushort **indices, unsigned *nbIndices,
1038 const float *left, const float *top,
1039 const float *right, const float *bottom)
1041 *nbVertices = 4;
1042 *nbIndices = 6;
1044 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1045 if (*vertexCoord == NULL)
1046 return VLC_ENOMEM;
1047 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1048 if (*textureCoord == NULL)
1050 free(*vertexCoord);
1051 return VLC_ENOMEM;
1053 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1054 if (*indices == NULL)
1056 free(*textureCoord);
1057 free(*vertexCoord);
1058 return VLC_ENOMEM;
1061 static const GLfloat coord[] = {
1062 -1.0, 1.0, -1.0f,
1063 -1.0, -1.0, -1.0f,
1064 1.0, 1.0, -1.0f,
1065 1.0, -1.0, -1.0f
1068 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
1070 for (unsigned p = 0; p < nbPlanes; ++p)
1072 const GLfloat tex[] = {
1073 left[p], top[p],
1074 left[p], bottom[p],
1075 right[p], top[p],
1076 right[p], bottom[p]
1079 memcpy(*textureCoord + p * *nbVertices * 2, tex,
1080 *nbVertices * 2 * sizeof(GLfloat));
1083 const GLushort ind[] = {
1084 0, 1, 2,
1085 2, 1, 3
1088 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
1090 return VLC_SUCCESS;
1093 static int SetupCoords(struct vlc_gl_renderer *renderer,
1094 const float *left, const float *top,
1095 const float *right, const float *bottom)
1097 const struct vlc_gl_interop *interop = renderer->interop;
1098 const opengl_vtable_t *vt = renderer->vt;
1100 GLfloat *vertexCoord, *textureCoord;
1101 GLushort *indices;
1102 unsigned nbVertices, nbIndices;
1104 int i_ret;
1105 switch (renderer->fmt.projection_mode)
1107 case PROJECTION_MODE_RECTANGULAR:
1108 i_ret = BuildRectangle(interop->tex_count,
1109 &vertexCoord, &textureCoord, &nbVertices,
1110 &indices, &nbIndices,
1111 left, top, right, bottom);
1112 break;
1113 case PROJECTION_MODE_EQUIRECTANGULAR:
1114 i_ret = BuildSphere(interop->tex_count,
1115 &vertexCoord, &textureCoord, &nbVertices,
1116 &indices, &nbIndices,
1117 left, top, right, bottom);
1118 break;
1119 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
1120 i_ret = BuildCube(interop->tex_count,
1121 (float)renderer->fmt.i_cubemap_padding / renderer->fmt.i_width,
1122 (float)renderer->fmt.i_cubemap_padding / renderer->fmt.i_height,
1123 &vertexCoord, &textureCoord, &nbVertices,
1124 &indices, &nbIndices,
1125 left, top, right, bottom);
1126 break;
1127 default:
1128 i_ret = VLC_EGENERIC;
1129 break;
1132 if (i_ret != VLC_SUCCESS)
1133 return i_ret;
1135 for (unsigned j = 0; j < interop->tex_count; j++)
1137 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object[j]);
1138 vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
1139 textureCoord + j * nbVertices * 2, GL_STATIC_DRAW);
1142 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
1143 vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
1144 vertexCoord, GL_STATIC_DRAW);
1146 vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
1147 vt->BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
1148 indices, GL_STATIC_DRAW);
1150 free(textureCoord);
1151 free(vertexCoord);
1152 free(indices);
1154 renderer->nb_indices = nbIndices;
1156 return VLC_SUCCESS;
1159 static void DrawWithShaders(struct vlc_gl_renderer *renderer)
1161 const struct vlc_gl_interop *interop = renderer->interop;
1162 const opengl_vtable_t *vt = renderer->vt;
1163 renderer->pf_prepare_shader(renderer, renderer->tex_width,
1164 renderer->tex_height, 1.0f);
1166 for (unsigned j = 0; j < interop->tex_count; j++) {
1167 assert(renderer->textures[j] != 0);
1168 vt->ActiveTexture(GL_TEXTURE0+j);
1169 vt->BindTexture(interop->tex_target, renderer->textures[j]);
1171 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object[j]);
1173 assert(renderer->aloc.MultiTexCoord[j] != -1);
1174 vt->EnableVertexAttribArray(renderer->aloc.MultiTexCoord[j]);
1175 vt->VertexAttribPointer(renderer->aloc.MultiTexCoord[j], 2,
1176 GL_FLOAT, 0, 0, 0);
1179 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
1180 vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
1181 vt->EnableVertexAttribArray(renderer->aloc.VertexPosition);
1182 vt->VertexAttribPointer(renderer->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
1184 const GLfloat *tm = NULL;
1185 if (interop->ops && interop->ops->get_transform_matrix)
1186 tm = interop->ops->get_transform_matrix(interop);
1187 if (!tm)
1188 tm = identity;
1190 vt->UniformMatrix4fv(renderer->uloc.TransformMatrix, 1, GL_FALSE, tm);
1192 vt->UniformMatrix4fv(renderer->uloc.OrientationMatrix, 1, GL_FALSE,
1193 renderer->var.OrientationMatrix);
1194 vt->UniformMatrix4fv(renderer->uloc.ProjectionMatrix, 1, GL_FALSE,
1195 renderer->var.ProjectionMatrix);
1196 vt->UniformMatrix4fv(renderer->uloc.ViewMatrix, 1, GL_FALSE,
1197 renderer->var.ViewMatrix);
1198 vt->UniformMatrix4fv(renderer->uloc.ZoomMatrix, 1, GL_FALSE,
1199 renderer->var.ZoomMatrix);
1201 vt->DrawElements(GL_TRIANGLES, renderer->nb_indices, GL_UNSIGNED_SHORT, 0);
1205 static void GetTextureCropParamsForStereo(unsigned i_nbTextures,
1206 const float *stereoCoefs,
1207 const float *stereoOffsets,
1208 float *left, float *top,
1209 float *right, float *bottom)
1211 for (unsigned i = 0; i < i_nbTextures; ++i)
1213 float f_2eyesWidth = right[i] - left[i];
1214 left[i] = left[i] + f_2eyesWidth * stereoOffsets[0];
1215 right[i] = left[i] + f_2eyesWidth * stereoCoefs[0];
1217 float f_2eyesHeight = bottom[i] - top[i];
1218 top[i] = top[i] + f_2eyesHeight * stereoOffsets[1];
1219 bottom[i] = top[i] + f_2eyesHeight * stereoCoefs[1];
1223 static void TextureCropForStereo(struct vlc_gl_renderer *renderer,
1224 float *left, float *top,
1225 float *right, float *bottom)
1227 const struct vlc_gl_interop *interop = renderer->interop;
1229 float stereoCoefs[2];
1230 float stereoOffsets[2];
1232 switch (renderer->fmt.multiview_mode)
1234 case MULTIVIEW_STEREO_TB:
1235 // Display only the left eye.
1236 stereoCoefs[0] = 1; stereoCoefs[1] = 0.5;
1237 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1238 GetTextureCropParamsForStereo(interop->tex_count,
1239 stereoCoefs, stereoOffsets,
1240 left, top, right, bottom);
1241 break;
1242 case MULTIVIEW_STEREO_SBS:
1243 // Display only the left eye.
1244 stereoCoefs[0] = 0.5; stereoCoefs[1] = 1;
1245 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
1246 GetTextureCropParamsForStereo(interop->tex_count,
1247 stereoCoefs, stereoOffsets,
1248 left, top, right, bottom);
1249 break;
1250 default:
1251 break;
1255 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
1256 const video_format_t *source)
1258 GL_ASSERT_NOERROR();
1259 struct vlc_gl_renderer *renderer = vgl->renderer;
1261 /* Why drawing here and not in Render()? Because this way, the
1262 OpenGL providers can call vout_display_opengl_Display to force redraw.
1263 Currently, the OS X provider uses it to get a smooth window resizing */
1264 vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
1266 vgl->vt.UseProgram(vgl->renderer->program_id);
1268 if (source->i_x_offset != renderer->last_source.i_x_offset
1269 || source->i_y_offset != renderer->last_source.i_y_offset
1270 || source->i_visible_width != renderer->last_source.i_visible_width
1271 || source->i_visible_height != renderer->last_source.i_visible_height)
1273 float left[PICTURE_PLANE_MAX];
1274 float top[PICTURE_PLANE_MAX];
1275 float right[PICTURE_PLANE_MAX];
1276 float bottom[PICTURE_PLANE_MAX];
1277 const struct vlc_gl_interop *interop = renderer->interop;
1278 for (unsigned j = 0; j < interop->tex_count; j++)
1280 float scale_w = (float)interop->texs[j].w.num / interop->texs[j].w.den
1281 / renderer->tex_width[j];
1282 float scale_h = (float)interop->texs[j].h.num / interop->texs[j].h.den
1283 / renderer->tex_height[j];
1285 /* Warning: if NPOT is not supported a larger texture is
1286 allocated. This will cause right and bottom coordinates to
1287 land on the edge of two texels with the texels to the
1288 right/bottom uninitialized by the call to
1289 glTexSubImage2D. This might cause a green line to appear on
1290 the right/bottom of the display.
1291 There are two possible solutions:
1292 - Manually mirror the edges of the texture.
1293 - Add a "-1" when computing right and bottom, however the
1294 last row/column might not be displayed at all.
1296 left[j] = (source->i_x_offset + 0 ) * scale_w;
1297 top[j] = (source->i_y_offset + 0 ) * scale_h;
1298 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
1299 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
1302 TextureCropForStereo(renderer, left, top, right, bottom);
1303 int ret = SetupCoords(renderer, left, top, right, bottom);
1304 if (ret != VLC_SUCCESS)
1305 return ret;
1307 renderer->last_source.i_x_offset = source->i_x_offset;
1308 renderer->last_source.i_y_offset = source->i_y_offset;
1309 renderer->last_source.i_visible_width = source->i_visible_width;
1310 renderer->last_source.i_visible_height = source->i_visible_height;
1312 DrawWithShaders(renderer);
1314 int ret = vlc_gl_sub_renderer_Draw(vgl->sub_renderer);
1315 if (ret != VLC_SUCCESS)
1316 return ret;
1318 /* Display */
1319 vlc_gl_Swap(vgl->gl);
1321 GL_ASSERT_NOERROR();
1323 return VLC_SUCCESS;