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>
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 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_subpicture.h>
37 #include <vlc_opengl.h>
38 #include <vlc_modules.h>
40 #include <vlc_viewpoint.h>
43 #include "vout_helper.h"
45 #include "sub_renderer.h"
47 #define SPHERE_RADIUS 1.f
49 struct vout_display_opengl_t
{
54 struct vlc_gl_renderer
*renderer
;
55 struct vlc_gl_sub_renderer
*sub_renderer
;
58 static const vlc_fourcc_t gl_subpicture_chromas
[] = {
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]) {
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]) {
89 float f
= 1.f
/ tanf(fovy
/ 2.f
);
92 f
/ sar
, 0.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
);
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;
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;
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;
159 case ORIENT_HFLIPPED
:
160 matrix
[0 * 4 + 0] = -1;
161 matrix
[3 * 4 + 0] = 1;
163 case ORIENT_VFLIPPED
:
164 matrix
[1 * 4 + 1] = -1;
165 matrix
[3 * 4 + 1] = 1;
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;
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;
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 =
196 "varying vec2 TexCoord0;\n"
197 "attribute vec4 MultiTexCoord0;\n"
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"
206 " TexCoord0 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord0).st;\n"
208 " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
209 " * vec4(VertexPosition, 1.0);\n"
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" : "";
222 if (asprintf(&code
, template, renderer
->glsl_version
, coord1_header
,
223 coord2_header
, coord1_code
, coord2_code
) < 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
);
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
);
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
)
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
++) {
264 vt
->GetShaderiv(shaders
[i
], GL_INFO_LOG_LENGTH
, &infoLength
);
268 char *infolog
= malloc(infoLength
);
272 vt
->GetShaderInfoLog(shaders
[i
], infoLength
, &charsWritten
,
274 msg_Err(renderer
->gl
, "shader %u: %s", i
, 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 */
289 vt
->GetProgramiv(program_id
, GL_INFO_LOG_LENGTH
, &infoLength
);
292 char *infolog
= malloc(infoLength
);
296 vt
->GetProgramInfoLog(program_id
, infoLength
, &charsWritten
,
298 msg_Err(renderer
->gl
, "shader program: %s", 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");
312 /* Fetch UniformLocations and AttribLocations */
313 #define GET_LOC(type, x, str) do { \
314 x = vt->Get##type##Location(program_id, str); \
317 msg_Err(renderer->gl, "Unable to Get"#type"Location(%s)", str); \
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");
335 renderer
->aloc
.MultiTexCoord
[1] = -1;
336 if (interop
->tex_count
> 2)
337 GET_ALOC(MultiTexCoord
[2], "MultiTexCoord2");
339 renderer
->aloc
.MultiTexCoord
[2] = -1;
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");
354 vt
->DeleteProgram(program_id
);
355 renderer
->program_id
= 0;
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
);
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
));
384 struct vlc_gl_interop
*interop
=
385 vlc_gl_interop_New(vgl
->gl
, &vgl
->vt
, context
, fmt
, false);
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";
401 renderer
->glsl_version
= 120;
402 renderer
->glsl_precision_header
= "";
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);
414 renderer
->pl_sh
= pl_shader_alloc(renderer
->pl_ctx
, NULL
, 0, 0);
419 vgl
->renderer
= renderer
;
421 int ret
= opengl_link_program(renderer
);
422 if (ret
!= VLC_SUCCESS
)
424 opengl_deinit_program(vgl
);
428 getOrientationTransformMatrix(interop
->fmt
.orientation
,
429 renderer
->var
.OrientationMatrix
);
430 getViewpointMatrixes(renderer
, interop
->fmt
.projection_mode
);
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
;
447 fmt
->i_visible_height
= nw_vis_w
* vis_h
/ vis_w
;
448 fmt
->i_visible_width
= nw_vis_w
;
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
,
467 const vlc_viewpoint_t
*viewpoint
,
468 vlc_video_context
*context
)
470 vout_display_opengl_t
*vgl
= calloc(1, sizeof(*vgl
));
476 #if defined(USE_OPENGL_ES2) || defined(HAVE_GL_CORE_SYMBOLS)
477 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
479 #define GET_PROC_ADDR_CORE(name) GET_PROC_ADDR_EXT(name, true)
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"); \
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 */
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)
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
);
575 const char *extensions
= (const char *)vgl
->vt
.GetString(GL_EXTENSIONS
);
579 msg_Err(gl
, "glGetString returned 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");
594 /* Resize the format if it is greater than the maximum texture size
595 * supported by the hardware */
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 */
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;
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");
614 bool b_dump_shaders
= var_InheritInteger(gl
, "verbose") >= 4;
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");
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
);
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
;
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
;
652 renderer
->tex_width
[j
] = w
;
653 renderer
->tex_height
[j
] = h
;
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
,
665 if (ret
!= VLC_SUCCESS
)
667 vout_display_opengl_Delete(vgl
);
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
);
691 *fmt
= renderer
->fmt
;
692 if (subpicture_chromas
) {
693 *subpicture_chromas
= gl_subpicture_chromas
;
700 void vout_display_opengl_Delete(vout_display_opengl_t
*vgl
)
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
);
722 vgl
->vt
.DeleteTextures(main_tex_count
, renderer
->textures
);
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)
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
)
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
);
779 getViewpointMatrixes(renderer
, renderer
->fmt
.projection_mode
);
785 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t
*vgl
,
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
);
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
)
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
,
815 renderer
->tex_height
, picture
,
817 if (ret
!= VLC_SUCCESS
)
820 ret
= vlc_gl_sub_renderer_Prepare(vgl
->sub_renderer
, subpicture
);
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
)
840 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
841 if (*textureCoord
== NULL
)
846 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
847 if (*indices
== NULL
)
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
;
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;
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
)
920 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
921 if (*vertexCoord
== NULL
)
923 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
924 if (*textureCoord
== NULL
)
929 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
930 if (*indices
== NULL
)
937 static const GLfloat coord
[] = {
938 -1.0, 1.0, -1.0f
, // front
943 -1.0, 1.0, 1.0f
, // back
948 -1.0, 1.0, -1.0f
, // left
953 1.0f
, 1.0, -1.0f
, // right
958 -1.0, -1.0, 1.0f
, // bottom
963 -1.0, 1.0, 1.0f
, // top
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,
981 float row
[] = {top
[p
],
982 top
[p
] + height
* 1.f
/2,
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
));
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
)
1044 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1045 if (*vertexCoord
== NULL
)
1047 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1048 if (*textureCoord
== NULL
)
1053 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1054 if (*indices
== NULL
)
1056 free(*textureCoord
);
1061 static const GLfloat coord
[] = {
1068 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
1070 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1072 const GLfloat tex
[] = {
1079 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
1080 *nbVertices
* 2 * sizeof(GLfloat
));
1083 const GLushort ind
[] = {
1088 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
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
;
1102 unsigned nbVertices
, nbIndices
;
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
);
1113 case PROJECTION_MODE_EQUIRECTANGULAR
:
1114 i_ret
= BuildSphere(interop
->tex_count
,
1115 &vertexCoord
, &textureCoord
, &nbVertices
,
1116 &indices
, &nbIndices
,
1117 left
, top
, right
, bottom
);
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
);
1128 i_ret
= VLC_EGENERIC
;
1132 if (i_ret
!= VLC_SUCCESS
)
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
);
1154 renderer
->nb_indices
= nbIndices
;
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,
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
);
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
);
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
);
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
)
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
)
1319 vlc_gl_Swap(vgl
->gl
);
1321 GL_ASSERT_NOERROR();