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_picture_pool.h>
37 #include <vlc_subpicture.h>
38 #include <vlc_opengl.h>
39 #include <vlc_memory.h>
40 #include <vlc_modules.h>
42 #include <vlc_viewpoint.h>
46 #ifndef GL_CLAMP_TO_EDGE
47 # define GL_CLAMP_TO_EDGE 0x812F
50 #define SPHERE_RADIUS 1.f
52 /* FIXME: GL_ASSERT_NOERROR disabled for now because:
53 * Proper GL error handling need to be implemented
54 * glClear(GL_COLOR_BUFFER_BIT) throws a GL_INVALID_FRAMEBUFFER_OPERATION on macOS
55 * assert fails on vout_display_opengl_Delete on iOS
58 # define HAVE_GL_ASSERT_NOERROR
61 #ifdef HAVE_GL_ASSERT_NOERROR
62 # define GL_ASSERT_NOERROR() do { \
63 GLenum glError = glGetError(); \
66 case GL_NO_ERROR: break; \
67 case GL_INVALID_ENUM: assert(!"GL_INVALID_ENUM"); \
68 case GL_INVALID_VALUE: assert(!"GL_INVALID_VALUE"); \
69 case GL_INVALID_OPERATION: assert(!"GL_INVALID_OPERATION"); \
70 case GL_INVALID_FRAMEBUFFER_OPERATION: assert(!"GL_INVALID_FRAMEBUFFER_OPERATION"); \
71 case GL_OUT_OF_MEMORY: assert(!"GL_OUT_OF_MEMORY"); \
72 default: assert(!"GL_UNKNOWN_ERROR"); \
76 # define GL_ASSERT_NOERROR()
98 opengl_tex_converter_t
*tc
;
101 GLfloat OrientationMatrix
[16];
102 GLfloat ProjectionMatrix
[16];
103 GLfloat ZRotMatrix
[16];
104 GLfloat YRotMatrix
[16];
105 GLfloat XRotMatrix
[16];
106 GLfloat ZoomMatrix
[16];
109 struct { /* UniformLocation */
110 GLint OrientationMatrix
;
111 GLint ProjectionMatrix
;
117 struct { /* AttribLocation */
118 GLint MultiTexCoord
[3];
119 GLint VertexPosition
;
123 struct vout_display_opengl_t
{
130 GLsizei tex_width
[PICTURE_PLANE_MAX
];
131 GLsizei tex_height
[PICTURE_PLANE_MAX
];
133 GLuint texture
[PICTURE_PLANE_MAX
];
139 picture_pool_t
*pool
;
141 /* One YUV program and one RGBA program (for subpics) */
142 struct prgm prgms
[2];
143 struct prgm
*prgm
; /* Main program */
144 struct prgm
*sub_prgm
; /* Subpicture program */
147 GLuint vertex_buffer_object
;
148 GLuint index_buffer_object
;
149 GLuint texture_buffer_object
[PICTURE_PLANE_MAX
];
151 GLuint
*subpicture_buffer_object
;
152 int subpicture_buffer_object_count
;
155 unsigned int i_x_offset
;
156 unsigned int i_y_offset
;
157 unsigned int i_visible_width
;
158 unsigned int i_visible_height
;
161 /* Non-power-of-2 texture size support */
168 float f_fovx
; /* f_fovx and f_fovy are linked but we keep both */
169 float f_fovy
; /* to avoid recalculating them when needed. */
170 float f_z
; /* Position of the camera on the shpere radius vector */
175 static const GLfloat identity
[] = {
176 1.0f
, 0.0f
, 0.0f
, 0.0f
,
177 0.0f
, 1.0f
, 0.0f
, 0.0f
,
178 0.0f
, 0.0f
, 1.0f
, 0.0f
,
179 0.0f
, 0.0f
, 0.0f
, 1.0f
182 /* rotation around the Z axis */
183 static void getZRotMatrix(float theta
, GLfloat matrix
[static 16])
187 sincosf(theta
, &st
, &ct
);
189 const GLfloat m
[] = {
197 memcpy(matrix
, m
, sizeof(m
));
200 /* rotation around the Y axis */
201 static void getYRotMatrix(float theta
, GLfloat matrix
[static 16])
205 sincosf(theta
, &st
, &ct
);
207 const GLfloat m
[] = {
215 memcpy(matrix
, m
, sizeof(m
));
218 /* rotation around the X axis */
219 static void getXRotMatrix(float phi
, GLfloat matrix
[static 16])
223 sincosf(phi
, &sp
, &cp
);
225 const GLfloat m
[] = {
233 memcpy(matrix
, m
, sizeof(m
));
236 static void getZoomMatrix(float zoom
, GLfloat matrix
[static 16]) {
238 const GLfloat m
[] = {
240 1.0f
, 0.0f
, 0.0f
, 0.0f
,
241 0.0f
, 1.0f
, 0.0f
, 0.0f
,
242 0.0f
, 0.0f
, 1.0f
, 0.0f
,
243 0.0f
, 0.0f
, zoom
, 1.0f
246 memcpy(matrix
, m
, sizeof(m
));
249 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
250 static void getProjectionMatrix(float sar
, float fovy
, GLfloat matrix
[static 16]) {
255 float f
= 1.f
/ tanf(fovy
/ 2.f
);
257 const GLfloat m
[] = {
258 f
/ sar
, 0.f
, 0.f
, 0.f
,
260 0.f
, 0.f
, (zNear
+ zFar
) / (zNear
- zFar
), -1.f
,
261 0.f
, 0.f
, (2 * zNear
* zFar
) / (zNear
- zFar
), 0.f
};
263 memcpy(matrix
, m
, sizeof(m
));
266 static void getViewpointMatrixes(vout_display_opengl_t
*vgl
,
267 video_projection_mode_t projection_mode
,
270 if (projection_mode
== PROJECTION_MODE_EQUIRECTANGULAR
271 || projection_mode
== PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
)
273 float sar
= (float) vgl
->f_sar
;
274 getProjectionMatrix(sar
, vgl
->f_fovy
, prgm
->var
.ProjectionMatrix
);
275 getYRotMatrix(vgl
->f_teta
, prgm
->var
.YRotMatrix
);
276 getXRotMatrix(vgl
->f_phi
, prgm
->var
.XRotMatrix
);
277 getZRotMatrix(vgl
->f_roll
, prgm
->var
.ZRotMatrix
);
278 getZoomMatrix(vgl
->f_z
, prgm
->var
.ZoomMatrix
);
282 memcpy(prgm
->var
.ProjectionMatrix
, identity
, sizeof(identity
));
283 memcpy(prgm
->var
.ZRotMatrix
, identity
, sizeof(identity
));
284 memcpy(prgm
->var
.YRotMatrix
, identity
, sizeof(identity
));
285 memcpy(prgm
->var
.XRotMatrix
, identity
, sizeof(identity
));
286 memcpy(prgm
->var
.ZoomMatrix
, identity
, sizeof(identity
));
290 static void getOrientationTransformMatrix(video_orientation_t orientation
,
291 GLfloat matrix
[static 16])
293 memcpy(matrix
, identity
, sizeof(identity
));
295 const int k_cos_pi
= -1;
296 const int k_cos_pi_2
= 0;
297 const int k_cos_n_pi_2
= 0;
299 const int k_sin_pi
= 0;
300 const int k_sin_pi_2
= 1;
301 const int k_sin_n_pi_2
= -1;
303 switch (orientation
) {
305 case ORIENT_ROTATED_90
:
306 matrix
[0 * 4 + 0] = k_cos_pi_2
;
307 matrix
[0 * 4 + 1] = -k_sin_pi_2
;
308 matrix
[1 * 4 + 0] = k_sin_pi_2
;
309 matrix
[1 * 4 + 1] = k_cos_pi_2
;
310 matrix
[3 * 4 + 1] = 1;
312 case ORIENT_ROTATED_180
:
313 matrix
[0 * 4 + 0] = k_cos_pi
;
314 matrix
[0 * 4 + 1] = -k_sin_pi
;
315 matrix
[1 * 4 + 0] = k_sin_pi
;
316 matrix
[1 * 4 + 1] = k_cos_pi
;
317 matrix
[3 * 4 + 0] = 1;
318 matrix
[3 * 4 + 1] = 1;
320 case ORIENT_ROTATED_270
:
321 matrix
[0 * 4 + 0] = k_cos_n_pi_2
;
322 matrix
[0 * 4 + 1] = -k_sin_n_pi_2
;
323 matrix
[1 * 4 + 0] = k_sin_n_pi_2
;
324 matrix
[1 * 4 + 1] = k_cos_n_pi_2
;
325 matrix
[3 * 4 + 0] = 1;
327 case ORIENT_HFLIPPED
:
328 matrix
[0 * 4 + 0] = -1;
329 matrix
[3 * 4 + 0] = 1;
331 case ORIENT_VFLIPPED
:
332 matrix
[1 * 4 + 1] = -1;
333 matrix
[3 * 4 + 1] = 1;
335 case ORIENT_TRANSPOSED
:
336 matrix
[0 * 4 + 0] = 0;
337 matrix
[1 * 4 + 1] = 0;
338 matrix
[2 * 4 + 2] = -1;
339 matrix
[0 * 4 + 1] = 1;
340 matrix
[1 * 4 + 0] = 1;
342 case ORIENT_ANTI_TRANSPOSED
:
343 matrix
[0 * 4 + 0] = 0;
344 matrix
[1 * 4 + 1] = 0;
345 matrix
[2 * 4 + 2] = -1;
346 matrix
[0 * 4 + 1] = -1;
347 matrix
[1 * 4 + 0] = -1;
348 matrix
[3 * 4 + 0] = 1;
349 matrix
[3 * 4 + 1] = 1;
356 static inline GLsizei
GetAlignedSize(unsigned size
)
358 /* Return the smallest larger or equal power of 2 */
359 unsigned align
= 1 << (8 * sizeof (unsigned) - clz(size
));
360 return ((align
>> 1) == size
) ? size
: align
;
363 static GLuint
BuildVertexShader(const opengl_tex_converter_t
*tc
,
364 unsigned plane_count
)
366 /* Basic vertex shader */
367 static const char *template =
369 "varying vec2 TexCoord0;\n"
370 "attribute vec4 MultiTexCoord0;\n"
372 "attribute vec3 VertexPosition;\n"
373 "uniform mat4 OrientationMatrix;\n"
374 "uniform mat4 ProjectionMatrix;\n"
375 "uniform mat4 XRotMatrix;\n"
376 "uniform mat4 YRotMatrix;\n"
377 "uniform mat4 ZRotMatrix;\n"
378 "uniform mat4 ZoomMatrix;\n"
380 " TexCoord0 = vec4(OrientationMatrix * MultiTexCoord0).st;\n"
382 " gl_Position = ProjectionMatrix * ZoomMatrix * ZRotMatrix * XRotMatrix * YRotMatrix * vec4(VertexPosition, 1.0);\n"
385 const char *coord1_header
= plane_count
> 1 ?
386 "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
387 const char *coord1_code
= plane_count
> 1 ?
388 " TexCoord1 = vec4(OrientationMatrix * MultiTexCoord1).st;\n" : "";
389 const char *coord2_header
= plane_count
> 2 ?
390 "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
391 const char *coord2_code
= plane_count
> 2 ?
392 " TexCoord2 = vec4(OrientationMatrix * MultiTexCoord2).st;\n" : "";
395 if (asprintf(&code
, template, tc
->glsl_version
, coord1_header
, coord2_header
,
396 coord1_code
, coord2_code
) < 0)
399 GLuint shader
= tc
->vt
->CreateShader(GL_VERTEX_SHADER
);
400 tc
->vt
->ShaderSource(shader
, 1, (const char **) &code
, NULL
);
401 if (tc
->b_dump_shaders
)
402 fprintf(stderr
, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
403 (const char *)&tc
->fmt
.i_chroma
, code
);
404 tc
->vt
->CompileShader(shader
);
410 GenTextures(const opengl_tex_converter_t
*tc
,
411 const GLsizei
*tex_width
, const GLsizei
*tex_height
,
414 glGenTextures(tc
->tex_count
, textures
);
416 for (unsigned i
= 0; i
< tc
->tex_count
; i
++)
418 tc
->vt
->BindTexture(tc
->tex_target
, textures
[i
]);
420 #if !defined(USE_OPENGL_ES2)
421 /* Set the texture parameters */
422 glTexParameterf(tc
->tex_target
, GL_TEXTURE_PRIORITY
, 1.0);
423 glTexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
426 glTexParameteri(tc
->tex_target
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
427 glTexParameteri(tc
->tex_target
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
428 glTexParameteri(tc
->tex_target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
429 glTexParameteri(tc
->tex_target
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
432 if (tc
->pf_allocate_textures
!= NULL
)
434 int ret
= tc
->pf_allocate_textures(tc
, textures
, tex_width
, tex_height
);
435 if (ret
!= VLC_SUCCESS
)
437 glDeleteTextures(tc
->tex_count
, textures
);
438 memset(textures
, 0, tc
->tex_count
* sizeof(GLuint
));
446 DelTextures(const opengl_tex_converter_t
*tc
, GLuint
*textures
)
448 glDeleteTextures(tc
->tex_count
, textures
);
449 memset(textures
, 0, tc
->tex_count
* sizeof(GLuint
));
453 opengl_link_program(struct prgm
*prgm
)
455 opengl_tex_converter_t
*tc
= prgm
->tc
;
457 GLuint vertex_shader
= BuildVertexShader(tc
, tc
->tex_count
);
458 GLuint shaders
[] = { tc
->fshader
, vertex_shader
};
460 /* Check shaders messages */
461 for (unsigned i
= 0; i
< 2; i
++) {
463 tc
->vt
->GetShaderiv(shaders
[i
], GL_INFO_LOG_LENGTH
, &infoLength
);
467 char *infolog
= malloc(infoLength
);
471 tc
->vt
->GetShaderInfoLog(shaders
[i
], infoLength
, &charsWritten
,
473 msg_Err(tc
->gl
, "shader %d: %s", i
, infolog
);
478 prgm
->id
= tc
->vt
->CreateProgram();
479 tc
->vt
->AttachShader(prgm
->id
, tc
->fshader
);
480 tc
->vt
->AttachShader(prgm
->id
, vertex_shader
);
481 tc
->vt
->LinkProgram(prgm
->id
);
483 tc
->vt
->DeleteShader(vertex_shader
);
484 tc
->vt
->DeleteShader(tc
->fshader
);
486 /* Check program messages */
488 tc
->vt
->GetProgramiv(prgm
->id
, GL_INFO_LOG_LENGTH
, &infoLength
);
491 char *infolog
= malloc(infoLength
);
495 tc
->vt
->GetProgramInfoLog(prgm
->id
, infoLength
, &charsWritten
,
497 msg_Err(tc
->gl
, "shader program: %s", infolog
);
501 /* If there is some message, better to check linking is ok */
502 GLint link_status
= GL_TRUE
;
503 tc
->vt
->GetProgramiv(prgm
->id
, GL_LINK_STATUS
, &link_status
);
504 if (link_status
== GL_FALSE
)
506 msg_Err(tc
->gl
, "Unable to use program");
511 /* Fetch UniformLocations and AttribLocations */
512 #define GET_LOC(type, x, str) do { \
513 x = tc->vt->Get##type##Location(prgm->id, str); \
516 msg_Err(tc->gl, "Unable to Get"#type"Location(%s)\n", str); \
520 #define GET_ULOC(x, str) GET_LOC(Uniform, prgm->uloc.x, str)
521 #define GET_ALOC(x, str) GET_LOC(Attrib, prgm->aloc.x, str)
522 GET_ULOC(OrientationMatrix
, "OrientationMatrix");
523 GET_ULOC(ProjectionMatrix
, "ProjectionMatrix");
524 GET_ULOC(ZRotMatrix
, "ZRotMatrix");
525 GET_ULOC(YRotMatrix
, "YRotMatrix");
526 GET_ULOC(XRotMatrix
, "XRotMatrix");
527 GET_ULOC(ZoomMatrix
, "ZoomMatrix");
529 GET_ALOC(VertexPosition
, "VertexPosition");
530 GET_ALOC(MultiTexCoord
[0], "MultiTexCoord0");
531 /* MultiTexCoord 1 and 2 can be optimized out if not used */
532 if (prgm
->tc
->tex_count
> 1)
533 GET_ALOC(MultiTexCoord
[1], "MultiTexCoord1");
535 prgm
->aloc
.MultiTexCoord
[1] = -1;
536 if (prgm
->tc
->tex_count
> 2)
537 GET_ALOC(MultiTexCoord
[2], "MultiTexCoord2");
539 prgm
->aloc
.MultiTexCoord
[2] = -1;
543 int ret
= prgm
->tc
->pf_fetch_locations(prgm
->tc
, prgm
->id
);
544 assert(ret
== VLC_SUCCESS
);
545 if (ret
!= VLC_SUCCESS
)
547 msg_Err(tc
->gl
, "Unable to get locations from tex_conv");
554 tc
->vt
->DeleteProgram(prgm
->id
);
560 opengl_deinit_program(vout_display_opengl_t
*vgl
, struct prgm
*prgm
)
562 opengl_tex_converter_t
*tc
= prgm
->tc
;
563 if (tc
->p_module
!= NULL
)
564 module_unneed(tc
, tc
->p_module
);
565 else if (tc
->priv
!= NULL
)
566 opengl_tex_converter_generic_deinit(tc
);
568 vgl
->vt
.DeleteProgram(prgm
->id
);
570 #ifdef HAVE_LIBPLACEBO
571 FREENULL(tc
->uloc
.pl_vars
);
573 pl_context_destroy(&tc
->pl_ctx
);
576 vlc_object_release(tc
);
579 #ifdef HAVE_LIBPLACEBO
581 log_cb(void *priv
, enum pl_log_level level
, const char *msg
)
583 opengl_tex_converter_t
*tc
= priv
;
585 case PL_LOG_FATAL
: // fall through
586 case PL_LOG_ERR
: msg_Err(tc
->gl
, "%s", msg
); break;
587 case PL_LOG_WARN
: msg_Warn(tc
->gl
,"%s", msg
); break;
588 case PL_LOG_INFO
: msg_Info(tc
->gl
,"%s", msg
); break;
595 opengl_init_program(vout_display_opengl_t
*vgl
, struct prgm
*prgm
,
596 const char *glexts
, const video_format_t
*fmt
, bool subpics
,
599 opengl_tex_converter_t
*tc
=
600 vlc_object_create(vgl
->gl
, sizeof(opengl_tex_converter_t
));
606 tc
->b_dump_shaders
= b_dump_shaders
;
607 tc
->pf_fragment_shader_init
= opengl_fragment_shader_init_impl
;
609 #if defined(USE_OPENGL_ES2)
611 tc
->glsl_version
= 100;
612 tc
->glsl_precision_header
= "precision highp float;\n";
615 tc
->glsl_version
= 120;
616 tc
->glsl_precision_header
= "";
620 #ifdef HAVE_LIBPLACEBO
621 // create the main libplacebo context
624 tc
->pl_ctx
= pl_context_create(PL_API_VER
, &(struct pl_context_params
) {
627 .log_level
= PL_LOG_INFO
,
630 tc
->pl_sh
= pl_shader_alloc(tc
->pl_ctx
, NULL
, 0);
637 tc
->fmt
.i_chroma
= VLC_CODEC_RGB32
;
638 /* Normal orientation and no projection for subtitles */
639 tc
->fmt
.orientation
= ORIENT_NORMAL
;
640 tc
->fmt
.projection_mode
= PROJECTION_MODE_RECTANGULAR
;
641 tc
->fmt
.primaries
= COLOR_PRIMARIES_UNDEF
;
642 tc
->fmt
.transfer
= TRANSFER_FUNC_UNDEF
;
643 tc
->fmt
.space
= COLOR_SPACE_UNDEF
;
645 ret
= opengl_tex_converter_generic_init(tc
, false);
649 const vlc_chroma_description_t
*desc
=
650 vlc_fourcc_GetChromaDescription(fmt
->i_chroma
);
654 if (desc
->plane_count
== 0)
656 /* Opaque chroma: load a module to handle it */
657 tc
->p_module
= module_need(tc
, "glconv", "$glconv", true);
660 if (tc
->p_module
!= NULL
)
664 /* Software chroma or gl hw converter failed: use a generic
666 ret
= opengl_tex_converter_generic_init(tc
, true);
670 if (ret
!= VLC_SUCCESS
)
672 vlc_object_release(tc
);
676 assert(tc
->fshader
!= 0 && tc
->tex_target
!= 0 && tc
->tex_count
> 0 &&
677 tc
->pf_update
!= NULL
&& tc
->pf_fetch_locations
!= NULL
&&
678 tc
->pf_prepare_shader
!= NULL
);
682 ret
= opengl_link_program(prgm
);
683 if (ret
!= VLC_SUCCESS
)
685 opengl_deinit_program(vgl
, prgm
);
689 getOrientationTransformMatrix(tc
->fmt
.orientation
,
690 prgm
->var
.OrientationMatrix
);
691 getViewpointMatrixes(vgl
, tc
->fmt
.projection_mode
, prgm
);
697 ResizeFormatToGLMaxTexSize(video_format_t
*fmt
, unsigned int max_tex_size
)
699 if (fmt
->i_width
> fmt
->i_height
)
701 unsigned int const vis_w
= fmt
->i_visible_width
;
702 unsigned int const vis_h
= fmt
->i_visible_height
;
703 unsigned int const nw_w
= max_tex_size
;
704 unsigned int const nw_vis_w
= nw_w
* vis_w
/ fmt
->i_width
;
706 fmt
->i_height
= nw_w
* fmt
->i_height
/ fmt
->i_width
;
708 fmt
->i_visible_height
= nw_vis_w
* vis_h
/ vis_w
;
709 fmt
->i_visible_width
= nw_vis_w
;
713 unsigned int const vis_w
= fmt
->i_visible_width
;
714 unsigned int const vis_h
= fmt
->i_visible_height
;
715 unsigned int const nw_h
= max_tex_size
;
716 unsigned int const nw_vis_h
= nw_h
* vis_h
/ fmt
->i_height
;
718 fmt
->i_width
= nw_h
* fmt
->i_width
/ fmt
->i_height
;
719 fmt
->i_height
= nw_h
;
720 fmt
->i_visible_width
= nw_vis_h
* vis_w
/ vis_h
;
721 fmt
->i_visible_height
= nw_vis_h
;
725 vout_display_opengl_t
*vout_display_opengl_New(video_format_t
*fmt
,
726 const vlc_fourcc_t
**subpicture_chromas
,
728 const vlc_viewpoint_t
*viewpoint
)
730 vout_display_opengl_t
*vgl
= calloc(1, sizeof(*vgl
));
737 if (gl
->getProcAddress
== NULL
) {
738 msg_Err(gl
, "getProcAddress not implemented, bailing out\n");
743 const char *extensions
= (const char *)glGetString(GL_EXTENSIONS
);
744 #if !defined(USE_OPENGL_ES2)
745 const unsigned char *ogl_version
= glGetString(GL_VERSION
);
746 bool supports_shaders
= strverscmp((const char *)ogl_version
, "2.0") >= 0;
747 if (!supports_shaders
)
749 msg_Err(gl
, "shaders not supported, bailing out\n");
755 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
756 #define GET_PROC_ADDR_EXT(name, critical) do { \
757 vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
758 if (vgl->vt.name == NULL && critical) { \
759 msg_Err(gl, "gl"#name" symbol not found, bailing out\n"); \
764 #if defined(USE_OPENGL_ES2)
765 #define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
766 #define GET_PROC_ADDR_CORE_GL(name) vgl->vt.name = NULL /* GL only functions (not GLES) */
768 #define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
769 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
771 #define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
773 GET_PROC_ADDR_CORE(GetError
);
774 GET_PROC_ADDR_CORE(GetString
);
775 GET_PROC_ADDR_CORE(GetIntegerv
);
776 GET_PROC_ADDR_CORE(BindTexture
);
777 GET_PROC_ADDR_CORE(TexParameteri
);
778 GET_PROC_ADDR_CORE(TexParameterf
);
779 GET_PROC_ADDR_CORE(PixelStorei
);
780 GET_PROC_ADDR_CORE(GenTextures
);
781 GET_PROC_ADDR_CORE(DeleteTextures
);
782 GET_PROC_ADDR_CORE(TexImage2D
);
783 GET_PROC_ADDR_CORE(TexSubImage2D
);
785 GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv
);
787 GET_PROC_ADDR(CreateShader
);
788 GET_PROC_ADDR(ShaderSource
);
789 GET_PROC_ADDR(CompileShader
);
790 GET_PROC_ADDR(AttachShader
);
791 GET_PROC_ADDR(DeleteShader
);
793 GET_PROC_ADDR(GetProgramiv
);
794 GET_PROC_ADDR(GetShaderiv
);
795 GET_PROC_ADDR(GetProgramInfoLog
);
796 GET_PROC_ADDR(GetShaderInfoLog
);
798 GET_PROC_ADDR(GetUniformLocation
);
799 GET_PROC_ADDR(GetAttribLocation
);
800 GET_PROC_ADDR(VertexAttribPointer
);
801 GET_PROC_ADDR(EnableVertexAttribArray
);
802 GET_PROC_ADDR(UniformMatrix4fv
);
803 GET_PROC_ADDR(UniformMatrix3fv
);
804 GET_PROC_ADDR(UniformMatrix2fv
);
805 GET_PROC_ADDR(Uniform4fv
);
806 GET_PROC_ADDR(Uniform4f
);
807 GET_PROC_ADDR(Uniform3f
);
808 GET_PROC_ADDR(Uniform2f
);
809 GET_PROC_ADDR(Uniform1f
);
810 GET_PROC_ADDR(Uniform1i
);
812 GET_PROC_ADDR(CreateProgram
);
813 GET_PROC_ADDR(LinkProgram
);
814 GET_PROC_ADDR(UseProgram
);
815 GET_PROC_ADDR(DeleteProgram
);
817 GET_PROC_ADDR(ActiveTexture
);
819 GET_PROC_ADDR(GenBuffers
);
820 GET_PROC_ADDR(BindBuffer
);
821 GET_PROC_ADDR(BufferData
);
822 GET_PROC_ADDR(DeleteBuffers
);
824 GET_PROC_ADDR_OPTIONAL(BufferSubData
);
825 GET_PROC_ADDR_OPTIONAL(BufferStorage
);
826 GET_PROC_ADDR_OPTIONAL(MapBufferRange
);
827 GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange
);
828 GET_PROC_ADDR_OPTIONAL(UnmapBuffer
);
829 GET_PROC_ADDR_OPTIONAL(FenceSync
);
830 GET_PROC_ADDR_OPTIONAL(DeleteSync
);
831 GET_PROC_ADDR_OPTIONAL(ClientWaitSync
);
834 /* Resize the format if it is greater than the maximum texture size
835 * supported by the hardware */
837 glGetIntegerv(GL_MAX_TEXTURE_SIZE
, &max_tex_size
);
839 if ((GLint
)fmt
->i_width
> max_tex_size
||
840 (GLint
)fmt
->i_height
> max_tex_size
)
841 ResizeFormatToGLMaxTexSize(fmt
, max_tex_size
);
843 #if defined(USE_OPENGL_ES2)
844 /* OpenGL ES 2 includes support for non-power of 2 textures by specification
845 * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
846 vgl
->supports_npot
= true;
848 vgl
->supports_npot
= HasExtension(extensions
, "GL_ARB_texture_non_power_of_two") ||
849 HasExtension(extensions
, "GL_APPLE_texture_2D_limited_npot");
852 bool b_dump_shaders
= var_InheritInteger(gl
, "verbose") >= 4;
854 vgl
->prgm
= &vgl
->prgms
[0];
855 vgl
->sub_prgm
= &vgl
->prgms
[1];
859 ret
= opengl_init_program(vgl
, vgl
->prgm
, extensions
, fmt
, false,
861 if (ret
!= VLC_SUCCESS
)
863 msg_Warn(gl
, "could not init tex converter for %4.4s",
864 (const char *) &fmt
->i_chroma
);
870 ret
= opengl_init_program(vgl
, vgl
->sub_prgm
, extensions
, fmt
, true,
872 if (ret
!= VLC_SUCCESS
)
874 msg_Warn(gl
, "could not init subpictures tex converter for %4.4s",
875 (const char *) &fmt
->i_chroma
);
876 opengl_deinit_program(vgl
, vgl
->prgm
);
881 /* Update the fmt to main program one */
882 vgl
->fmt
= vgl
->prgm
->tc
->fmt
;
883 /* The orientation is handled by the orientation matrix */
884 vgl
->fmt
.orientation
= fmt
->orientation
;
887 const opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
888 for (unsigned j
= 0; j
< tc
->tex_count
; j
++) {
889 const GLsizei w
= vgl
->fmt
.i_visible_width
* tc
->texs
[j
].w
.num
891 const GLsizei h
= vgl
->fmt
.i_visible_height
* tc
->texs
[j
].h
.num
893 if (vgl
->supports_npot
) {
894 vgl
->tex_width
[j
] = w
;
895 vgl
->tex_height
[j
] = h
;
897 vgl
->tex_width
[j
] = GetAlignedSize(w
);
898 vgl
->tex_height
[j
] = GetAlignedSize(h
);
902 /* Allocates our textures */
903 assert(!vgl
->sub_prgm
->tc
->handle_texs_gen
);
905 if (!vgl
->prgm
->tc
->handle_texs_gen
)
907 ret
= GenTextures(vgl
->prgm
->tc
, vgl
->tex_width
, vgl
->tex_height
,
909 if (ret
!= VLC_SUCCESS
)
911 vout_display_opengl_Delete(vgl
);
918 glDisable(GL_DEPTH_TEST
);
919 glDepthMask(GL_FALSE
);
920 glEnable(GL_CULL_FACE
);
921 glClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
922 glClear(GL_COLOR_BUFFER_BIT
);
924 vgl
->vt
.GenBuffers(1, &vgl
->vertex_buffer_object
);
925 vgl
->vt
.GenBuffers(1, &vgl
->index_buffer_object
);
926 vgl
->vt
.GenBuffers(vgl
->prgm
->tc
->tex_count
, vgl
->texture_buffer_object
);
928 /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
929 int subpicture_buffer_object_count
= 8;
930 vgl
->subpicture_buffer_object
= vlc_alloc(subpicture_buffer_object_count
, sizeof(GLuint
));
931 if (!vgl
->subpicture_buffer_object
) {
932 vout_display_opengl_Delete(vgl
);
935 vgl
->subpicture_buffer_object_count
= subpicture_buffer_object_count
;
936 vgl
->vt
.GenBuffers(vgl
->subpicture_buffer_object_count
, vgl
->subpicture_buffer_object
);
939 vgl
->region_count
= 0;
943 if (vgl
->fmt
.projection_mode
!= PROJECTION_MODE_RECTANGULAR
944 && vout_display_opengl_SetViewpoint(vgl
, viewpoint
) != VLC_SUCCESS
)
946 vout_display_opengl_Delete(vgl
);
951 if (subpicture_chromas
) {
952 *subpicture_chromas
= gl_subpicture_chromas
;
959 void vout_display_opengl_Delete(vout_display_opengl_t
*vgl
)
967 opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
968 if (!tc
->handle_texs_gen
)
969 DelTextures(tc
, vgl
->texture
);
971 tc
= vgl
->sub_prgm
->tc
;
972 for (int i
= 0; i
< vgl
->region_count
; i
++)
974 if (vgl
->region
[i
].texture
)
975 DelTextures(tc
, &vgl
->region
[i
].texture
);
979 vgl
->vt
.DeleteBuffers(1, &vgl
->vertex_buffer_object
);
980 vgl
->vt
.DeleteBuffers(1, &vgl
->index_buffer_object
);
981 vgl
->vt
.DeleteBuffers(vgl
->prgm
->tc
->tex_count
, vgl
->texture_buffer_object
);
983 if (vgl
->subpicture_buffer_object_count
> 0)
984 vgl
->vt
.DeleteBuffers(vgl
->subpicture_buffer_object_count
,
985 vgl
->subpicture_buffer_object
);
986 free(vgl
->subpicture_buffer_object
);
989 picture_pool_Release(vgl
->pool
);
990 opengl_deinit_program(vgl
, vgl
->prgm
);
991 opengl_deinit_program(vgl
, vgl
->sub_prgm
);
998 static void UpdateZ(vout_display_opengl_t
*vgl
)
1000 /* Do trigonometry to calculate the minimal z value
1001 * that will allow us to zoom out without seeing the outside of the
1002 * sphere (black borders). */
1003 float tan_fovx_2
= tanf(vgl
->f_fovx
/ 2);
1004 float tan_fovy_2
= tanf(vgl
->f_fovy
/ 2);
1005 float z_min
= - SPHERE_RADIUS
/ sinf(atanf(sqrtf(
1006 tan_fovx_2
* tan_fovx_2
+ tan_fovy_2
* tan_fovy_2
)));
1008 /* The FOV value above which z is dynamically calculated. */
1009 const float z_thresh
= 90.f
;
1011 if (vgl
->f_fovx
<= z_thresh
* M_PI
/ 180)
1015 float f
= z_min
/ ((FIELD_OF_VIEW_DEGREES_MAX
- z_thresh
) * M_PI
/ 180);
1016 vgl
->f_z
= f
* vgl
->f_fovx
- f
* z_thresh
* M_PI
/ 180;
1017 if (vgl
->f_z
< z_min
)
1022 static void UpdateFOVy(vout_display_opengl_t
*vgl
)
1024 vgl
->f_fovy
= 2 * atanf(tanf(vgl
->f_fovx
/ 2) / vgl
->f_sar
);
1027 int vout_display_opengl_SetViewpoint(vout_display_opengl_t
*vgl
,
1028 const vlc_viewpoint_t
*p_vp
)
1030 #define RAD(d) ((float) ((d) * M_PI / 180.f))
1031 float f_fovx
= RAD(p_vp
->fov
);
1032 if (f_fovx
> FIELD_OF_VIEW_DEGREES_MAX
* M_PI
/ 180 + 0.001f
1033 || f_fovx
< -0.001f
)
1036 vgl
->f_teta
= RAD(p_vp
->yaw
) - (float) M_PI_2
;
1037 vgl
->f_phi
= RAD(p_vp
->pitch
);
1038 vgl
->f_roll
= RAD(p_vp
->roll
);
1041 if (fabsf(f_fovx
- vgl
->f_fovx
) >= 0.001f
)
1043 /* FOVx has changed. */
1044 vgl
->f_fovx
= f_fovx
;
1048 getViewpointMatrixes(vgl
, vgl
->fmt
.projection_mode
, vgl
->prgm
);
1055 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t
*vgl
,
1058 /* Each time the window size changes, we must recompute the minimum zoom
1059 * since the aspect ration changes.
1060 * We must also set the new current zoom value. */
1064 getViewpointMatrixes(vgl
, vgl
->fmt
.projection_mode
, vgl
->prgm
);
1067 picture_pool_t
*vout_display_opengl_GetPool(vout_display_opengl_t
*vgl
, unsigned requested_count
)
1069 GL_ASSERT_NOERROR();
1074 opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
1075 requested_count
= __MIN(VLCGL_PICTURE_MAX
, requested_count
);
1076 /* Allocate with tex converter pool callback if it exists */
1077 if (tc
->pf_get_pool
!= NULL
)
1079 vgl
->pool
= tc
->pf_get_pool(tc
, requested_count
);
1085 /* Allocate our pictures */
1086 picture_t
*picture
[VLCGL_PICTURE_MAX
] = {NULL
, };
1088 for (count
= 0; count
< requested_count
; count
++)
1090 picture
[count
] = picture_NewFromFormat(&vgl
->fmt
);
1091 if (!picture
[count
])
1097 /* Wrap the pictures into a pool */
1098 vgl
->pool
= picture_pool_New(count
, picture
);
1101 for (unsigned i
= 0; i
< count
; i
++)
1102 picture_Release(picture
[i
]);
1106 GL_ASSERT_NOERROR();
1110 DelTextures(tc
, vgl
->texture
);
1114 int vout_display_opengl_Prepare(vout_display_opengl_t
*vgl
,
1115 picture_t
*picture
, subpicture_t
*subpicture
)
1117 GL_ASSERT_NOERROR();
1119 opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
1121 /* Update the texture */
1122 int ret
= tc
->pf_update(tc
, vgl
->texture
, vgl
->tex_width
, vgl
->tex_height
,
1124 if (ret
!= VLC_SUCCESS
)
1127 int last_count
= vgl
->region_count
;
1128 gl_region_t
*last
= vgl
->region
;
1130 vgl
->region_count
= 0;
1133 tc
= vgl
->sub_prgm
->tc
;
1137 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
)
1140 vgl
->region_count
= count
;
1141 vgl
->region
= calloc(count
, sizeof(*vgl
->region
));
1144 for (subpicture_region_t
*r
= subpicture
->p_region
;
1145 r
&& ret
== VLC_SUCCESS
; r
= r
->p_next
, i
++) {
1146 gl_region_t
*glr
= &vgl
->region
[i
];
1148 glr
->width
= r
->fmt
.i_visible_width
;
1149 glr
->height
= r
->fmt
.i_visible_height
;
1150 if (!vgl
->supports_npot
) {
1151 glr
->width
= GetAlignedSize(glr
->width
);
1152 glr
->height
= GetAlignedSize(glr
->height
);
1153 glr
->tex_width
= (float) r
->fmt
.i_visible_width
/ glr
->width
;
1154 glr
->tex_height
= (float) r
->fmt
.i_visible_height
/ glr
->height
;
1156 glr
->tex_width
= 1.0;
1157 glr
->tex_height
= 1.0;
1159 glr
->alpha
= (float)subpicture
->i_alpha
* r
->i_alpha
/ 255 / 255;
1160 glr
->left
= 2.0 * (r
->i_x
) / subpicture
->i_original_picture_width
- 1.0;
1161 glr
->top
= -2.0 * (r
->i_y
) / subpicture
->i_original_picture_height
+ 1.0;
1162 glr
->right
= 2.0 * (r
->i_x
+ r
->fmt
.i_visible_width
) / subpicture
->i_original_picture_width
- 1.0;
1163 glr
->bottom
= -2.0 * (r
->i_y
+ r
->fmt
.i_visible_height
) / subpicture
->i_original_picture_height
+ 1.0;
1166 /* Try to recycle the textures allocated by the previous
1167 call to this function. */
1168 for (int j
= 0; j
< last_count
; j
++) {
1169 if (last
[j
].texture
&&
1170 last
[j
].width
== glr
->width
&&
1171 last
[j
].height
== glr
->height
) {
1172 glr
->texture
= last
[j
].texture
;
1173 memset(&last
[j
], 0, sizeof(last
[j
]));
1178 const size_t pixels_offset
=
1179 r
->fmt
.i_y_offset
* r
->p_picture
->p
->i_pitch
+
1180 r
->fmt
.i_x_offset
* r
->p_picture
->p
->i_pixel_pitch
;
1183 /* Could not recycle a previous texture, generate a new one. */
1184 ret
= GenTextures(tc
, &glr
->width
, &glr
->height
, &glr
->texture
);
1185 if (ret
!= VLC_SUCCESS
)
1188 ret
= tc
->pf_update(tc
, &glr
->texture
, &glr
->width
, &glr
->height
,
1189 r
->p_picture
, &pixels_offset
);
1192 for (int i
= 0; i
< last_count
; i
++) {
1193 if (last
[i
].texture
)
1194 DelTextures(tc
, &last
[i
].texture
);
1198 VLC_UNUSED(subpicture
);
1200 GL_ASSERT_NOERROR();
1204 static int BuildSphere(unsigned nbPlanes
,
1205 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
1206 GLushort
**indices
, unsigned *nbIndices
,
1207 const float *left
, const float *top
,
1208 const float *right
, const float *bottom
)
1210 unsigned nbLatBands
= 128;
1211 unsigned nbLonBands
= 128;
1213 *nbVertices
= (nbLatBands
+ 1) * (nbLonBands
+ 1);
1214 *nbIndices
= nbLatBands
* nbLonBands
* 3 * 2;
1216 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1217 if (*vertexCoord
== NULL
)
1219 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1220 if (*textureCoord
== NULL
)
1225 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1226 if (*indices
== NULL
)
1228 free(*textureCoord
);
1233 for (unsigned lat
= 0; lat
<= nbLatBands
; lat
++) {
1234 float theta
= lat
* (float) M_PI
/ nbLatBands
;
1235 float sinTheta
, cosTheta
;
1237 sincosf(theta
, &sinTheta
, &cosTheta
);
1239 for (unsigned lon
= 0; lon
<= nbLonBands
; lon
++) {
1240 float phi
= lon
* 2 * (float) M_PI
/ nbLonBands
;
1241 float sinPhi
, cosPhi
;
1243 sincosf(phi
, &sinPhi
, &cosPhi
);
1245 float x
= cosPhi
* sinTheta
;
1247 float z
= sinPhi
* sinTheta
;
1249 unsigned off1
= (lat
* (nbLonBands
+ 1) + lon
) * 3;
1250 (*vertexCoord
)[off1
] = SPHERE_RADIUS
* x
;
1251 (*vertexCoord
)[off1
+ 1] = SPHERE_RADIUS
* y
;
1252 (*vertexCoord
)[off1
+ 2] = SPHERE_RADIUS
* z
;
1254 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1256 unsigned off2
= (p
* (nbLatBands
+ 1) * (nbLonBands
+ 1)
1257 + lat
* (nbLonBands
+ 1) + lon
) * 2;
1258 float width
= right
[p
] - left
[p
];
1259 float height
= bottom
[p
] - top
[p
];
1260 float u
= (float)lon
/ nbLonBands
* width
;
1261 float v
= (float)lat
/ nbLatBands
* height
;
1262 (*textureCoord
)[off2
] = u
;
1263 (*textureCoord
)[off2
+ 1] = v
;
1268 for (unsigned lat
= 0; lat
< nbLatBands
; lat
++) {
1269 for (unsigned lon
= 0; lon
< nbLonBands
; lon
++) {
1270 unsigned first
= (lat
* (nbLonBands
+ 1)) + lon
;
1271 unsigned second
= first
+ nbLonBands
+ 1;
1273 unsigned off
= (lat
* nbLatBands
+ lon
) * 3 * 2;
1275 (*indices
)[off
] = first
;
1276 (*indices
)[off
+ 1] = second
;
1277 (*indices
)[off
+ 2] = first
+ 1;
1279 (*indices
)[off
+ 3] = second
;
1280 (*indices
)[off
+ 4] = second
+ 1;
1281 (*indices
)[off
+ 5] = first
+ 1;
1289 static int BuildCube(unsigned nbPlanes
,
1290 float padW
, float padH
,
1291 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
1292 GLushort
**indices
, unsigned *nbIndices
,
1293 const float *left
, const float *top
,
1294 const float *right
, const float *bottom
)
1296 *nbVertices
= 4 * 6;
1299 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1300 if (*vertexCoord
== NULL
)
1302 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1303 if (*textureCoord
== NULL
)
1308 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1309 if (*indices
== NULL
)
1311 free(*textureCoord
);
1316 static const GLfloat coord
[] = {
1317 -1.0, 1.0, -1.0f
, // front
1322 -1.0, 1.0, 1.0f
, // back
1327 -1.0, 1.0, -1.0f
, // left
1332 1.0f
, 1.0, -1.0f
, // right
1337 -1.0, -1.0, 1.0f
, // bottom
1342 -1.0, 1.0, 1.0f
, // top
1348 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
1350 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1352 float width
= right
[p
] - left
[p
];
1353 float height
= bottom
[p
] - top
[p
];
1355 float col
[] = {left
[p
],
1356 left
[p
] + width
* 1.f
/3,
1357 left
[p
] + width
* 2.f
/3,
1360 float row
[] = {top
[p
],
1361 top
[p
] + height
* 1.f
/2,
1364 const GLfloat tex
[] = {
1365 col
[1] + padW
, row
[1] + padH
, // front
1366 col
[1] + padW
, row
[2] - padH
,
1367 col
[2] - padW
, row
[1] + padH
,
1368 col
[2] - padW
, row
[2] - padH
,
1370 col
[3] - padW
, row
[1] + padH
, // back
1371 col
[3] - padW
, row
[2] - padH
,
1372 col
[2] + padW
, row
[1] + padH
,
1373 col
[2] + padW
, row
[2] - padH
,
1375 col
[2] - padW
, row
[0] + padH
, // left
1376 col
[2] - padW
, row
[1] - padH
,
1377 col
[1] + padW
, row
[0] + padH
,
1378 col
[1] + padW
, row
[1] - padH
,
1380 col
[0] + padW
, row
[0] + padH
, // right
1381 col
[0] + padW
, row
[1] - padH
,
1382 col
[1] - padW
, row
[0] + padH
,
1383 col
[1] - padW
, row
[1] - padH
,
1385 col
[0] + padW
, row
[2] - padH
, // bottom
1386 col
[0] + padW
, row
[1] + padH
,
1387 col
[1] - padW
, row
[2] - padH
,
1388 col
[1] - padW
, row
[1] + padH
,
1390 col
[2] + padW
, row
[0] + padH
, // top
1391 col
[2] + padW
, row
[1] - padH
,
1392 col
[3] - padW
, row
[0] + padH
,
1393 col
[3] - padW
, row
[1] - padH
,
1396 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
1397 *nbVertices
* 2 * sizeof(GLfloat
));
1400 const GLushort ind
[] = {
1401 0, 1, 2, 2, 1, 3, // front
1402 6, 7, 4, 4, 7, 5, // back
1403 10, 11, 8, 8, 11, 9, // left
1404 12, 13, 14, 14, 13, 15, // right
1405 18, 19, 16, 16, 19, 17, // bottom
1406 20, 21, 22, 22, 21, 23, // top
1409 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
1414 static int BuildRectangle(unsigned nbPlanes
,
1415 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
1416 GLushort
**indices
, unsigned *nbIndices
,
1417 const float *left
, const float *top
,
1418 const float *right
, const float *bottom
)
1423 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1424 if (*vertexCoord
== NULL
)
1426 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1427 if (*textureCoord
== NULL
)
1432 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1433 if (*indices
== NULL
)
1435 free(*textureCoord
);
1440 static const GLfloat coord
[] = {
1447 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
1449 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1451 const GLfloat tex
[] = {
1458 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
1459 *nbVertices
* 2 * sizeof(GLfloat
));
1462 const GLushort ind
[] = {
1467 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
1472 static int SetupCoords(vout_display_opengl_t
*vgl
,
1473 const float *left
, const float *top
,
1474 const float *right
, const float *bottom
)
1476 GLfloat
*vertexCoord
, *textureCoord
;
1478 unsigned nbVertices
, nbIndices
;
1481 switch (vgl
->fmt
.projection_mode
)
1483 case PROJECTION_MODE_RECTANGULAR
:
1484 i_ret
= BuildRectangle(vgl
->prgm
->tc
->tex_count
,
1485 &vertexCoord
, &textureCoord
, &nbVertices
,
1486 &indices
, &nbIndices
,
1487 left
, top
, right
, bottom
);
1489 case PROJECTION_MODE_EQUIRECTANGULAR
:
1490 i_ret
= BuildSphere(vgl
->prgm
->tc
->tex_count
,
1491 &vertexCoord
, &textureCoord
, &nbVertices
,
1492 &indices
, &nbIndices
,
1493 left
, top
, right
, bottom
);
1495 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
:
1496 i_ret
= BuildCube(vgl
->prgm
->tc
->tex_count
,
1497 (float)vgl
->fmt
.i_cubemap_padding
/ vgl
->fmt
.i_width
,
1498 (float)vgl
->fmt
.i_cubemap_padding
/ vgl
->fmt
.i_height
,
1499 &vertexCoord
, &textureCoord
, &nbVertices
,
1500 &indices
, &nbIndices
,
1501 left
, top
, right
, bottom
);
1504 i_ret
= VLC_EGENERIC
;
1508 if (i_ret
!= VLC_SUCCESS
)
1511 for (unsigned j
= 0; j
< vgl
->prgm
->tc
->tex_count
; j
++)
1513 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->texture_buffer_object
[j
]);
1514 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, nbVertices
* 2 * sizeof(GLfloat
),
1515 textureCoord
+ j
* nbVertices
* 2, GL_STATIC_DRAW
);
1518 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->vertex_buffer_object
);
1519 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, nbVertices
* 3 * sizeof(GLfloat
),
1520 vertexCoord
, GL_STATIC_DRAW
);
1522 vgl
->vt
.BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, vgl
->index_buffer_object
);
1523 vgl
->vt
.BufferData(GL_ELEMENT_ARRAY_BUFFER
, nbIndices
* sizeof(GLushort
),
1524 indices
, GL_STATIC_DRAW
);
1530 vgl
->nb_indices
= nbIndices
;
1535 static void DrawWithShaders(vout_display_opengl_t
*vgl
, struct prgm
*prgm
)
1537 opengl_tex_converter_t
*tc
= prgm
->tc
;
1538 tc
->pf_prepare_shader(tc
, vgl
->tex_width
, vgl
->tex_height
, 1.0f
);
1540 for (unsigned j
= 0; j
< vgl
->prgm
->tc
->tex_count
; j
++) {
1541 assert(vgl
->texture
[j
] != 0);
1542 vgl
->vt
.ActiveTexture(GL_TEXTURE0
+j
);
1543 vgl
->vt
.BindTexture(tc
->tex_target
, vgl
->texture
[j
]);
1545 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->texture_buffer_object
[j
]);
1547 assert(prgm
->aloc
.MultiTexCoord
[j
] != -1);
1548 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.MultiTexCoord
[j
]);
1549 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.MultiTexCoord
[j
], 2, GL_FLOAT
,
1553 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->vertex_buffer_object
);
1554 vgl
->vt
.BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, vgl
->index_buffer_object
);
1555 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.VertexPosition
);
1556 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.VertexPosition
, 3, GL_FLOAT
, 0, 0, 0);
1558 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.OrientationMatrix
, 1, GL_FALSE
,
1559 prgm
->var
.OrientationMatrix
);
1560 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ProjectionMatrix
, 1, GL_FALSE
,
1561 prgm
->var
.ProjectionMatrix
);
1562 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZRotMatrix
, 1, GL_FALSE
,
1563 prgm
->var
.ZRotMatrix
);
1564 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.YRotMatrix
, 1, GL_FALSE
,
1565 prgm
->var
.YRotMatrix
);
1566 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.XRotMatrix
, 1, GL_FALSE
,
1567 prgm
->var
.XRotMatrix
);
1568 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZoomMatrix
, 1, GL_FALSE
,
1569 prgm
->var
.ZoomMatrix
);
1571 glDrawElements(GL_TRIANGLES
, vgl
->nb_indices
, GL_UNSIGNED_SHORT
, 0);
1575 static void GetTextureCropParamsForStereo(unsigned i_nbTextures
,
1576 const float *stereoCoefs
,
1577 const float *stereoOffsets
,
1578 float *left
, float *top
,
1579 float *right
, float *bottom
)
1581 for (unsigned i
= 0; i
< i_nbTextures
; ++i
)
1583 float f_2eyesWidth
= right
[i
] - left
[i
];
1584 left
[i
] = left
[i
] + f_2eyesWidth
* stereoOffsets
[0];
1585 right
[i
] = left
[i
] + f_2eyesWidth
* stereoCoefs
[0];
1587 float f_2eyesHeight
= bottom
[i
] - top
[i
];
1588 top
[i
] = top
[i
] + f_2eyesHeight
* stereoOffsets
[1];
1589 bottom
[i
] = top
[i
] + f_2eyesHeight
* stereoCoefs
[1];
1593 static void TextureCropForStereo(vout_display_opengl_t
*vgl
,
1594 float *left
, float *top
,
1595 float *right
, float *bottom
)
1597 float stereoCoefs
[2];
1598 float stereoOffsets
[2];
1600 switch (vgl
->fmt
.multiview_mode
)
1602 case MULTIVIEW_STEREO_TB
:
1603 // Display only the left eye.
1604 stereoCoefs
[0] = 1; stereoCoefs
[1] = 0.5;
1605 stereoOffsets
[0] = 0; stereoOffsets
[1] = 0;
1606 GetTextureCropParamsForStereo(vgl
->prgm
->tc
->tex_count
,
1607 stereoCoefs
, stereoOffsets
,
1608 left
, top
, right
, bottom
);
1610 case MULTIVIEW_STEREO_SBS
:
1611 // Display only the left eye.
1612 stereoCoefs
[0] = 0.5; stereoCoefs
[1] = 1;
1613 stereoOffsets
[0] = 0; stereoOffsets
[1] = 0;
1614 GetTextureCropParamsForStereo(vgl
->prgm
->tc
->tex_count
,
1615 stereoCoefs
, stereoOffsets
,
1616 left
, top
, right
, bottom
);
1623 int vout_display_opengl_Display(vout_display_opengl_t
*vgl
,
1624 const video_format_t
*source
)
1626 GL_ASSERT_NOERROR();
1628 /* Why drawing here and not in Render()? Because this way, the
1629 OpenGL providers can call vout_display_opengl_Display to force redraw.
1630 Currently, the OS X provider uses it to get a smooth window resizing */
1631 glClear(GL_COLOR_BUFFER_BIT
);
1633 vgl
->vt
.UseProgram(vgl
->prgm
->id
);
1635 if (source
->i_x_offset
!= vgl
->last_source
.i_x_offset
1636 || source
->i_y_offset
!= vgl
->last_source
.i_y_offset
1637 || source
->i_visible_width
!= vgl
->last_source
.i_visible_width
1638 || source
->i_visible_height
!= vgl
->last_source
.i_visible_height
)
1640 float left
[PICTURE_PLANE_MAX
];
1641 float top
[PICTURE_PLANE_MAX
];
1642 float right
[PICTURE_PLANE_MAX
];
1643 float bottom
[PICTURE_PLANE_MAX
];
1644 const opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
1645 for (unsigned j
= 0; j
< tc
->tex_count
; j
++)
1647 float scale_w
= (float)tc
->texs
[j
].w
.num
/ tc
->texs
[j
].w
.den
1648 / vgl
->tex_width
[j
];
1649 float scale_h
= (float)tc
->texs
[j
].h
.num
/ tc
->texs
[j
].h
.den
1650 / vgl
->tex_height
[j
];
1652 /* Warning: if NPOT is not supported a larger texture is
1653 allocated. This will cause right and bottom coordinates to
1654 land on the edge of two texels with the texels to the
1655 right/bottom uninitialized by the call to
1656 glTexSubImage2D. This might cause a green line to appear on
1657 the right/bottom of the display.
1658 There are two possible solutions:
1659 - Manually mirror the edges of the texture.
1660 - Add a "-1" when computing right and bottom, however the
1661 last row/column might not be displayed at all.
1663 left
[j
] = (source
->i_x_offset
+ 0 ) * scale_w
;
1664 top
[j
] = (source
->i_y_offset
+ 0 ) * scale_h
;
1665 right
[j
] = (source
->i_x_offset
+ source
->i_visible_width
) * scale_w
;
1666 bottom
[j
] = (source
->i_y_offset
+ source
->i_visible_height
) * scale_h
;
1669 TextureCropForStereo(vgl
, left
, top
, right
, bottom
);
1670 int ret
= SetupCoords(vgl
, left
, top
, right
, bottom
);
1671 if (ret
!= VLC_SUCCESS
)
1674 vgl
->last_source
.i_x_offset
= source
->i_x_offset
;
1675 vgl
->last_source
.i_y_offset
= source
->i_y_offset
;
1676 vgl
->last_source
.i_visible_width
= source
->i_visible_width
;
1677 vgl
->last_source
.i_visible_height
= source
->i_visible_height
;
1679 DrawWithShaders(vgl
, vgl
->prgm
);
1681 /* Draw the subpictures */
1682 // Change the program for overlays
1683 struct prgm
*prgm
= vgl
->sub_prgm
;
1684 GLuint program
= prgm
->id
;
1685 opengl_tex_converter_t
*tc
= prgm
->tc
;
1686 vgl
->vt
.UseProgram(program
);
1689 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1691 /* We need two buffer objects for each region: for vertex and texture coordinates. */
1692 if (2 * vgl
->region_count
> vgl
->subpicture_buffer_object_count
) {
1693 if (vgl
->subpicture_buffer_object_count
> 0)
1694 vgl
->vt
.DeleteBuffers(vgl
->subpicture_buffer_object_count
,
1695 vgl
->subpicture_buffer_object
);
1696 vgl
->subpicture_buffer_object_count
= 0;
1698 int new_count
= 2 * vgl
->region_count
;
1699 vgl
->subpicture_buffer_object
= realloc_or_free(vgl
->subpicture_buffer_object
, new_count
* sizeof(GLuint
));
1700 if (!vgl
->subpicture_buffer_object
)
1703 vgl
->subpicture_buffer_object_count
= new_count
;
1704 vgl
->vt
.GenBuffers(vgl
->subpicture_buffer_object_count
,
1705 vgl
->subpicture_buffer_object
);
1708 vgl
->vt
.ActiveTexture(GL_TEXTURE0
+ 0);
1709 for (int i
= 0; i
< vgl
->region_count
; i
++) {
1710 gl_region_t
*glr
= &vgl
->region
[i
];
1711 const GLfloat vertexCoord
[] = {
1712 glr
->left
, glr
->top
,
1713 glr
->left
, glr
->bottom
,
1714 glr
->right
, glr
->top
,
1715 glr
->right
, glr
->bottom
,
1717 const GLfloat textureCoord
[] = {
1719 0.0, glr
->tex_height
,
1720 glr
->tex_width
, 0.0,
1721 glr
->tex_width
, glr
->tex_height
,
1724 assert(glr
->texture
!= 0);
1725 vgl
->vt
.BindTexture(tc
->tex_target
, glr
->texture
);
1727 tc
->pf_prepare_shader(tc
, &glr
->width
, &glr
->height
, glr
->alpha
);
1729 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->subpicture_buffer_object
[2 * i
]);
1730 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, sizeof(textureCoord
), textureCoord
, GL_STATIC_DRAW
);
1731 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.MultiTexCoord
[0]);
1732 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.MultiTexCoord
[0], 2, GL_FLOAT
,
1735 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->subpicture_buffer_object
[2 * i
+ 1]);
1736 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, sizeof(vertexCoord
), vertexCoord
, GL_STATIC_DRAW
);
1737 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.VertexPosition
);
1738 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.VertexPosition
, 2, GL_FLOAT
,
1741 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.OrientationMatrix
, 1, GL_FALSE
,
1742 prgm
->var
.OrientationMatrix
);
1743 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ProjectionMatrix
, 1, GL_FALSE
,
1744 prgm
->var
.ProjectionMatrix
);
1745 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZRotMatrix
, 1, GL_FALSE
,
1746 prgm
->var
.ZRotMatrix
);
1747 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.YRotMatrix
, 1, GL_FALSE
,
1748 prgm
->var
.YRotMatrix
);
1749 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.XRotMatrix
, 1, GL_FALSE
,
1750 prgm
->var
.XRotMatrix
);
1751 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZoomMatrix
, 1, GL_FALSE
,
1752 prgm
->var
.ZoomMatrix
);
1754 glDrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
1756 glDisable(GL_BLEND
);
1759 vlc_gl_Swap(vgl
->gl
);
1761 GL_ASSERT_NOERROR();