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 = vgl->vt.GetError(); \
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 msg_Dbg(tc
->gl
, "\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 tc
->vt
->GenTextures(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 tc
->vt
->TexParameterf(tc
->tex_target
, GL_TEXTURE_PRIORITY
, 1.0);
423 tc
->vt
->TexEnvf(GL_TEXTURE_ENV
, GL_TEXTURE_ENV_MODE
, GL_MODULATE
);
426 tc
->vt
->TexParameteri(tc
->tex_target
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
427 tc
->vt
->TexParameteri(tc
->tex_target
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
428 tc
->vt
->TexParameteri(tc
->tex_target
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
429 tc
->vt
->TexParameteri(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 tc
->vt
->DeleteTextures(tc
->tex_count
, textures
);
438 memset(textures
, 0, tc
->tex_count
* sizeof(GLuint
));
446 DelTextures(const opengl_tex_converter_t
*tc
, GLuint
*textures
)
448 tc
->vt
->DeleteTextures(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 if (gl
->getProcAddress
== NULL
) {
731 msg_Err(gl
, "getProcAddress not implemented, bailing out\n");
735 vout_display_opengl_t
*vgl
= calloc(1, sizeof(*vgl
));
741 #define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
742 #define GET_PROC_ADDR_EXT(name, critical) do { \
743 vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
744 if (vgl->vt.name == NULL && critical) { \
745 msg_Err(gl, "gl"#name" symbol not found, bailing out\n"); \
750 #if defined(USE_OPENGL_ES2)
751 #define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
752 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_EXT(name, false) /* optional for GLES */
754 #define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
755 #define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
757 #define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
759 GET_PROC_ADDR_CORE(BindTexture
);
760 GET_PROC_ADDR_CORE(BlendFunc
);
761 GET_PROC_ADDR_CORE(Clear
);
762 GET_PROC_ADDR_CORE(ClearColor
);
763 GET_PROC_ADDR_CORE(DeleteTextures
);
764 GET_PROC_ADDR_CORE(DepthMask
);
765 GET_PROC_ADDR_CORE(Disable
);
766 GET_PROC_ADDR_CORE(DrawArrays
);
767 GET_PROC_ADDR_CORE(DrawElements
);
768 GET_PROC_ADDR_CORE(Enable
);
769 GET_PROC_ADDR_CORE(Finish
);
770 GET_PROC_ADDR_CORE(Flush
);
771 GET_PROC_ADDR_CORE(GenTextures
);
772 GET_PROC_ADDR_CORE(GetError
);
773 GET_PROC_ADDR_CORE(GetIntegerv
);
774 GET_PROC_ADDR_CORE(GetString
);
775 GET_PROC_ADDR_CORE(PixelStorei
);
776 GET_PROC_ADDR_CORE(TexImage2D
);
777 GET_PROC_ADDR_CORE(TexParameterf
);
778 GET_PROC_ADDR_CORE(TexParameteri
);
779 GET_PROC_ADDR_CORE(TexSubImage2D
);
780 GET_PROC_ADDR_CORE(Viewport
);
782 GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv
);
783 GET_PROC_ADDR_CORE_GL(TexEnvf
);
785 GET_PROC_ADDR(CreateShader
);
786 GET_PROC_ADDR(ShaderSource
);
787 GET_PROC_ADDR(CompileShader
);
788 GET_PROC_ADDR(AttachShader
);
789 GET_PROC_ADDR(DeleteShader
);
791 GET_PROC_ADDR(GetProgramiv
);
792 GET_PROC_ADDR(GetShaderiv
);
793 GET_PROC_ADDR(GetProgramInfoLog
);
794 GET_PROC_ADDR(GetShaderInfoLog
);
796 GET_PROC_ADDR(GetUniformLocation
);
797 GET_PROC_ADDR(GetAttribLocation
);
798 GET_PROC_ADDR(VertexAttribPointer
);
799 GET_PROC_ADDR(EnableVertexAttribArray
);
800 GET_PROC_ADDR(UniformMatrix4fv
);
801 GET_PROC_ADDR(UniformMatrix3fv
);
802 GET_PROC_ADDR(UniformMatrix2fv
);
803 GET_PROC_ADDR(Uniform4fv
);
804 GET_PROC_ADDR(Uniform4f
);
805 GET_PROC_ADDR(Uniform3f
);
806 GET_PROC_ADDR(Uniform2f
);
807 GET_PROC_ADDR(Uniform1f
);
808 GET_PROC_ADDR(Uniform1i
);
810 GET_PROC_ADDR(CreateProgram
);
811 GET_PROC_ADDR(LinkProgram
);
812 GET_PROC_ADDR(UseProgram
);
813 GET_PROC_ADDR(DeleteProgram
);
815 GET_PROC_ADDR(ActiveTexture
);
817 GET_PROC_ADDR(GenBuffers
);
818 GET_PROC_ADDR(BindBuffer
);
819 GET_PROC_ADDR(BufferData
);
820 GET_PROC_ADDR(DeleteBuffers
);
822 GET_PROC_ADDR_OPTIONAL(BufferSubData
);
823 GET_PROC_ADDR_OPTIONAL(BufferStorage
);
824 GET_PROC_ADDR_OPTIONAL(MapBufferRange
);
825 GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange
);
826 GET_PROC_ADDR_OPTIONAL(UnmapBuffer
);
827 GET_PROC_ADDR_OPTIONAL(FenceSync
);
828 GET_PROC_ADDR_OPTIONAL(DeleteSync
);
829 GET_PROC_ADDR_OPTIONAL(ClientWaitSync
);
834 const char *extensions
= (const char *)vgl
->vt
.GetString(GL_EXTENSIONS
);
835 #if !defined(USE_OPENGL_ES2)
836 const unsigned char *ogl_version
= vgl
->vt
.GetString(GL_VERSION
);
837 bool supports_shaders
= strverscmp((const char *)ogl_version
, "2.0") >= 0;
838 if (!supports_shaders
)
840 msg_Err(gl
, "shaders not supported, bailing out\n");
846 /* Resize the format if it is greater than the maximum texture size
847 * supported by the hardware */
849 vgl
->vt
.GetIntegerv(GL_MAX_TEXTURE_SIZE
, &max_tex_size
);
851 if ((GLint
)fmt
->i_width
> max_tex_size
||
852 (GLint
)fmt
->i_height
> max_tex_size
)
853 ResizeFormatToGLMaxTexSize(fmt
, max_tex_size
);
855 #if defined(USE_OPENGL_ES2)
856 /* OpenGL ES 2 includes support for non-power of 2 textures by specification
857 * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
858 vgl
->supports_npot
= true;
860 vgl
->supports_npot
= HasExtension(extensions
, "GL_ARB_texture_non_power_of_two") ||
861 HasExtension(extensions
, "GL_APPLE_texture_2D_limited_npot");
864 bool b_dump_shaders
= var_InheritInteger(gl
, "verbose") >= 4;
866 vgl
->prgm
= &vgl
->prgms
[0];
867 vgl
->sub_prgm
= &vgl
->prgms
[1];
871 ret
= opengl_init_program(vgl
, vgl
->prgm
, extensions
, fmt
, false,
873 if (ret
!= VLC_SUCCESS
)
875 msg_Warn(gl
, "could not init tex converter for %4.4s",
876 (const char *) &fmt
->i_chroma
);
882 ret
= opengl_init_program(vgl
, vgl
->sub_prgm
, extensions
, fmt
, true,
884 if (ret
!= VLC_SUCCESS
)
886 msg_Warn(gl
, "could not init subpictures tex converter for %4.4s",
887 (const char *) &fmt
->i_chroma
);
888 opengl_deinit_program(vgl
, vgl
->prgm
);
893 /* Update the fmt to main program one */
894 vgl
->fmt
= vgl
->prgm
->tc
->fmt
;
895 /* The orientation is handled by the orientation matrix */
896 vgl
->fmt
.orientation
= fmt
->orientation
;
899 const opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
900 for (unsigned j
= 0; j
< tc
->tex_count
; j
++) {
901 const GLsizei w
= vgl
->fmt
.i_visible_width
* tc
->texs
[j
].w
.num
903 const GLsizei h
= vgl
->fmt
.i_visible_height
* tc
->texs
[j
].h
.num
905 if (vgl
->supports_npot
) {
906 vgl
->tex_width
[j
] = w
;
907 vgl
->tex_height
[j
] = h
;
909 vgl
->tex_width
[j
] = GetAlignedSize(w
);
910 vgl
->tex_height
[j
] = GetAlignedSize(h
);
914 /* Allocates our textures */
915 assert(!vgl
->sub_prgm
->tc
->handle_texs_gen
);
917 if (!vgl
->prgm
->tc
->handle_texs_gen
)
919 ret
= GenTextures(vgl
->prgm
->tc
, vgl
->tex_width
, vgl
->tex_height
,
921 if (ret
!= VLC_SUCCESS
)
923 vout_display_opengl_Delete(vgl
);
929 vgl
->vt
.Disable(GL_BLEND
);
930 vgl
->vt
.Disable(GL_DEPTH_TEST
);
931 vgl
->vt
.DepthMask(GL_FALSE
);
932 vgl
->vt
.Enable(GL_CULL_FACE
);
933 vgl
->vt
.ClearColor(0.0f
, 0.0f
, 0.0f
, 1.0f
);
934 vgl
->vt
.Clear(GL_COLOR_BUFFER_BIT
);
936 vgl
->vt
.GenBuffers(1, &vgl
->vertex_buffer_object
);
937 vgl
->vt
.GenBuffers(1, &vgl
->index_buffer_object
);
938 vgl
->vt
.GenBuffers(vgl
->prgm
->tc
->tex_count
, vgl
->texture_buffer_object
);
940 /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
941 int subpicture_buffer_object_count
= 8;
942 vgl
->subpicture_buffer_object
= vlc_alloc(subpicture_buffer_object_count
, sizeof(GLuint
));
943 if (!vgl
->subpicture_buffer_object
) {
944 vout_display_opengl_Delete(vgl
);
947 vgl
->subpicture_buffer_object_count
= subpicture_buffer_object_count
;
948 vgl
->vt
.GenBuffers(vgl
->subpicture_buffer_object_count
, vgl
->subpicture_buffer_object
);
951 vgl
->region_count
= 0;
955 if (vgl
->fmt
.projection_mode
!= PROJECTION_MODE_RECTANGULAR
956 && vout_display_opengl_SetViewpoint(vgl
, viewpoint
) != VLC_SUCCESS
)
958 vout_display_opengl_Delete(vgl
);
963 if (subpicture_chromas
) {
964 *subpicture_chromas
= gl_subpicture_chromas
;
971 void vout_display_opengl_Delete(vout_display_opengl_t
*vgl
)
979 opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
980 if (!tc
->handle_texs_gen
)
981 DelTextures(tc
, vgl
->texture
);
983 tc
= vgl
->sub_prgm
->tc
;
984 for (int i
= 0; i
< vgl
->region_count
; i
++)
986 if (vgl
->region
[i
].texture
)
987 DelTextures(tc
, &vgl
->region
[i
].texture
);
991 vgl
->vt
.DeleteBuffers(1, &vgl
->vertex_buffer_object
);
992 vgl
->vt
.DeleteBuffers(1, &vgl
->index_buffer_object
);
993 vgl
->vt
.DeleteBuffers(vgl
->prgm
->tc
->tex_count
, vgl
->texture_buffer_object
);
995 if (vgl
->subpicture_buffer_object_count
> 0)
996 vgl
->vt
.DeleteBuffers(vgl
->subpicture_buffer_object_count
,
997 vgl
->subpicture_buffer_object
);
998 free(vgl
->subpicture_buffer_object
);
1001 picture_pool_Release(vgl
->pool
);
1002 opengl_deinit_program(vgl
, vgl
->prgm
);
1003 opengl_deinit_program(vgl
, vgl
->sub_prgm
);
1005 GL_ASSERT_NOERROR();
1010 static void UpdateZ(vout_display_opengl_t
*vgl
)
1012 /* Do trigonometry to calculate the minimal z value
1013 * that will allow us to zoom out without seeing the outside of the
1014 * sphere (black borders). */
1015 float tan_fovx_2
= tanf(vgl
->f_fovx
/ 2);
1016 float tan_fovy_2
= tanf(vgl
->f_fovy
/ 2);
1017 float z_min
= - SPHERE_RADIUS
/ sinf(atanf(sqrtf(
1018 tan_fovx_2
* tan_fovx_2
+ tan_fovy_2
* tan_fovy_2
)));
1020 /* The FOV value above which z is dynamically calculated. */
1021 const float z_thresh
= 90.f
;
1023 if (vgl
->f_fovx
<= z_thresh
* M_PI
/ 180)
1027 float f
= z_min
/ ((FIELD_OF_VIEW_DEGREES_MAX
- z_thresh
) * M_PI
/ 180);
1028 vgl
->f_z
= f
* vgl
->f_fovx
- f
* z_thresh
* M_PI
/ 180;
1029 if (vgl
->f_z
< z_min
)
1034 static void UpdateFOVy(vout_display_opengl_t
*vgl
)
1036 vgl
->f_fovy
= 2 * atanf(tanf(vgl
->f_fovx
/ 2) / vgl
->f_sar
);
1039 int vout_display_opengl_SetViewpoint(vout_display_opengl_t
*vgl
,
1040 const vlc_viewpoint_t
*p_vp
)
1042 #define RAD(d) ((float) ((d) * M_PI / 180.f))
1043 float f_fovx
= RAD(p_vp
->fov
);
1044 if (f_fovx
> FIELD_OF_VIEW_DEGREES_MAX
* M_PI
/ 180 + 0.001f
1045 || f_fovx
< -0.001f
)
1048 vgl
->f_teta
= RAD(p_vp
->yaw
) - (float) M_PI_2
;
1049 vgl
->f_phi
= RAD(p_vp
->pitch
);
1050 vgl
->f_roll
= RAD(p_vp
->roll
);
1053 if (fabsf(f_fovx
- vgl
->f_fovx
) >= 0.001f
)
1055 /* FOVx has changed. */
1056 vgl
->f_fovx
= f_fovx
;
1060 getViewpointMatrixes(vgl
, vgl
->fmt
.projection_mode
, vgl
->prgm
);
1067 void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t
*vgl
,
1070 /* Each time the window size changes, we must recompute the minimum zoom
1071 * since the aspect ration changes.
1072 * We must also set the new current zoom value. */
1076 getViewpointMatrixes(vgl
, vgl
->fmt
.projection_mode
, vgl
->prgm
);
1079 picture_pool_t
*vout_display_opengl_GetPool(vout_display_opengl_t
*vgl
, unsigned requested_count
)
1081 GL_ASSERT_NOERROR();
1086 opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
1087 requested_count
= __MIN(VLCGL_PICTURE_MAX
, requested_count
);
1088 /* Allocate with tex converter pool callback if it exists */
1089 if (tc
->pf_get_pool
!= NULL
)
1091 vgl
->pool
= tc
->pf_get_pool(tc
, requested_count
);
1097 /* Allocate our pictures */
1098 picture_t
*picture
[VLCGL_PICTURE_MAX
] = {NULL
, };
1100 for (count
= 0; count
< requested_count
; count
++)
1102 picture
[count
] = picture_NewFromFormat(&vgl
->fmt
);
1103 if (!picture
[count
])
1109 /* Wrap the pictures into a pool */
1110 vgl
->pool
= picture_pool_New(count
, picture
);
1113 for (unsigned i
= 0; i
< count
; i
++)
1114 picture_Release(picture
[i
]);
1118 GL_ASSERT_NOERROR();
1122 DelTextures(tc
, vgl
->texture
);
1126 int vout_display_opengl_Prepare(vout_display_opengl_t
*vgl
,
1127 picture_t
*picture
, subpicture_t
*subpicture
)
1129 GL_ASSERT_NOERROR();
1131 opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
1133 /* Update the texture */
1134 int ret
= tc
->pf_update(tc
, vgl
->texture
, vgl
->tex_width
, vgl
->tex_height
,
1136 if (ret
!= VLC_SUCCESS
)
1139 int last_count
= vgl
->region_count
;
1140 gl_region_t
*last
= vgl
->region
;
1142 vgl
->region_count
= 0;
1145 tc
= vgl
->sub_prgm
->tc
;
1149 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
)
1152 vgl
->region_count
= count
;
1153 vgl
->region
= calloc(count
, sizeof(*vgl
->region
));
1156 for (subpicture_region_t
*r
= subpicture
->p_region
;
1157 r
&& ret
== VLC_SUCCESS
; r
= r
->p_next
, i
++) {
1158 gl_region_t
*glr
= &vgl
->region
[i
];
1160 glr
->width
= r
->fmt
.i_visible_width
;
1161 glr
->height
= r
->fmt
.i_visible_height
;
1162 if (!vgl
->supports_npot
) {
1163 glr
->width
= GetAlignedSize(glr
->width
);
1164 glr
->height
= GetAlignedSize(glr
->height
);
1165 glr
->tex_width
= (float) r
->fmt
.i_visible_width
/ glr
->width
;
1166 glr
->tex_height
= (float) r
->fmt
.i_visible_height
/ glr
->height
;
1168 glr
->tex_width
= 1.0;
1169 glr
->tex_height
= 1.0;
1171 glr
->alpha
= (float)subpicture
->i_alpha
* r
->i_alpha
/ 255 / 255;
1172 glr
->left
= 2.0 * (r
->i_x
) / subpicture
->i_original_picture_width
- 1.0;
1173 glr
->top
= -2.0 * (r
->i_y
) / subpicture
->i_original_picture_height
+ 1.0;
1174 glr
->right
= 2.0 * (r
->i_x
+ r
->fmt
.i_visible_width
) / subpicture
->i_original_picture_width
- 1.0;
1175 glr
->bottom
= -2.0 * (r
->i_y
+ r
->fmt
.i_visible_height
) / subpicture
->i_original_picture_height
+ 1.0;
1178 /* Try to recycle the textures allocated by the previous
1179 call to this function. */
1180 for (int j
= 0; j
< last_count
; j
++) {
1181 if (last
[j
].texture
&&
1182 last
[j
].width
== glr
->width
&&
1183 last
[j
].height
== glr
->height
) {
1184 glr
->texture
= last
[j
].texture
;
1185 memset(&last
[j
], 0, sizeof(last
[j
]));
1190 const size_t pixels_offset
=
1191 r
->fmt
.i_y_offset
* r
->p_picture
->p
->i_pitch
+
1192 r
->fmt
.i_x_offset
* r
->p_picture
->p
->i_pixel_pitch
;
1195 /* Could not recycle a previous texture, generate a new one. */
1196 ret
= GenTextures(tc
, &glr
->width
, &glr
->height
, &glr
->texture
);
1197 if (ret
!= VLC_SUCCESS
)
1200 ret
= tc
->pf_update(tc
, &glr
->texture
, &glr
->width
, &glr
->height
,
1201 r
->p_picture
, &pixels_offset
);
1204 for (int i
= 0; i
< last_count
; i
++) {
1205 if (last
[i
].texture
)
1206 DelTextures(tc
, &last
[i
].texture
);
1210 VLC_UNUSED(subpicture
);
1212 GL_ASSERT_NOERROR();
1216 static int BuildSphere(unsigned nbPlanes
,
1217 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
1218 GLushort
**indices
, unsigned *nbIndices
,
1219 const float *left
, const float *top
,
1220 const float *right
, const float *bottom
)
1222 unsigned nbLatBands
= 128;
1223 unsigned nbLonBands
= 128;
1225 *nbVertices
= (nbLatBands
+ 1) * (nbLonBands
+ 1);
1226 *nbIndices
= nbLatBands
* nbLonBands
* 3 * 2;
1228 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1229 if (*vertexCoord
== NULL
)
1231 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1232 if (*textureCoord
== NULL
)
1237 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1238 if (*indices
== NULL
)
1240 free(*textureCoord
);
1245 for (unsigned lat
= 0; lat
<= nbLatBands
; lat
++) {
1246 float theta
= lat
* (float) M_PI
/ nbLatBands
;
1247 float sinTheta
, cosTheta
;
1249 sincosf(theta
, &sinTheta
, &cosTheta
);
1251 for (unsigned lon
= 0; lon
<= nbLonBands
; lon
++) {
1252 float phi
= lon
* 2 * (float) M_PI
/ nbLonBands
;
1253 float sinPhi
, cosPhi
;
1255 sincosf(phi
, &sinPhi
, &cosPhi
);
1257 float x
= cosPhi
* sinTheta
;
1259 float z
= sinPhi
* sinTheta
;
1261 unsigned off1
= (lat
* (nbLonBands
+ 1) + lon
) * 3;
1262 (*vertexCoord
)[off1
] = SPHERE_RADIUS
* x
;
1263 (*vertexCoord
)[off1
+ 1] = SPHERE_RADIUS
* y
;
1264 (*vertexCoord
)[off1
+ 2] = SPHERE_RADIUS
* z
;
1266 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1268 unsigned off2
= (p
* (nbLatBands
+ 1) * (nbLonBands
+ 1)
1269 + lat
* (nbLonBands
+ 1) + lon
) * 2;
1270 float width
= right
[p
] - left
[p
];
1271 float height
= bottom
[p
] - top
[p
];
1272 float u
= (float)lon
/ nbLonBands
* width
;
1273 float v
= (float)lat
/ nbLatBands
* height
;
1274 (*textureCoord
)[off2
] = u
;
1275 (*textureCoord
)[off2
+ 1] = v
;
1280 for (unsigned lat
= 0; lat
< nbLatBands
; lat
++) {
1281 for (unsigned lon
= 0; lon
< nbLonBands
; lon
++) {
1282 unsigned first
= (lat
* (nbLonBands
+ 1)) + lon
;
1283 unsigned second
= first
+ nbLonBands
+ 1;
1285 unsigned off
= (lat
* nbLatBands
+ lon
) * 3 * 2;
1287 (*indices
)[off
] = first
;
1288 (*indices
)[off
+ 1] = second
;
1289 (*indices
)[off
+ 2] = first
+ 1;
1291 (*indices
)[off
+ 3] = second
;
1292 (*indices
)[off
+ 4] = second
+ 1;
1293 (*indices
)[off
+ 5] = first
+ 1;
1301 static int BuildCube(unsigned nbPlanes
,
1302 float padW
, float padH
,
1303 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
1304 GLushort
**indices
, unsigned *nbIndices
,
1305 const float *left
, const float *top
,
1306 const float *right
, const float *bottom
)
1308 *nbVertices
= 4 * 6;
1311 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1312 if (*vertexCoord
== NULL
)
1314 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1315 if (*textureCoord
== NULL
)
1320 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1321 if (*indices
== NULL
)
1323 free(*textureCoord
);
1328 static const GLfloat coord
[] = {
1329 -1.0, 1.0, -1.0f
, // front
1334 -1.0, 1.0, 1.0f
, // back
1339 -1.0, 1.0, -1.0f
, // left
1344 1.0f
, 1.0, -1.0f
, // right
1349 -1.0, -1.0, 1.0f
, // bottom
1354 -1.0, 1.0, 1.0f
, // top
1360 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
1362 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1364 float width
= right
[p
] - left
[p
];
1365 float height
= bottom
[p
] - top
[p
];
1367 float col
[] = {left
[p
],
1368 left
[p
] + width
* 1.f
/3,
1369 left
[p
] + width
* 2.f
/3,
1372 float row
[] = {top
[p
],
1373 top
[p
] + height
* 1.f
/2,
1376 const GLfloat tex
[] = {
1377 col
[1] + padW
, row
[1] + padH
, // front
1378 col
[1] + padW
, row
[2] - padH
,
1379 col
[2] - padW
, row
[1] + padH
,
1380 col
[2] - padW
, row
[2] - padH
,
1382 col
[3] - padW
, row
[1] + padH
, // back
1383 col
[3] - padW
, row
[2] - padH
,
1384 col
[2] + padW
, row
[1] + padH
,
1385 col
[2] + padW
, row
[2] - padH
,
1387 col
[2] - padW
, row
[0] + padH
, // left
1388 col
[2] - padW
, row
[1] - padH
,
1389 col
[1] + padW
, row
[0] + padH
,
1390 col
[1] + padW
, row
[1] - padH
,
1392 col
[0] + padW
, row
[0] + padH
, // right
1393 col
[0] + padW
, row
[1] - padH
,
1394 col
[1] - padW
, row
[0] + padH
,
1395 col
[1] - padW
, row
[1] - padH
,
1397 col
[0] + padW
, row
[2] - padH
, // bottom
1398 col
[0] + padW
, row
[1] + padH
,
1399 col
[1] - padW
, row
[2] - padH
,
1400 col
[1] - padW
, row
[1] + padH
,
1402 col
[2] + padW
, row
[0] + padH
, // top
1403 col
[2] + padW
, row
[1] - padH
,
1404 col
[3] - padW
, row
[0] + padH
,
1405 col
[3] - padW
, row
[1] - padH
,
1408 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
1409 *nbVertices
* 2 * sizeof(GLfloat
));
1412 const GLushort ind
[] = {
1413 0, 1, 2, 2, 1, 3, // front
1414 6, 7, 4, 4, 7, 5, // back
1415 10, 11, 8, 8, 11, 9, // left
1416 12, 13, 14, 14, 13, 15, // right
1417 18, 19, 16, 16, 19, 17, // bottom
1418 20, 21, 22, 22, 21, 23, // top
1421 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
1426 static int BuildRectangle(unsigned nbPlanes
,
1427 GLfloat
**vertexCoord
, GLfloat
**textureCoord
, unsigned *nbVertices
,
1428 GLushort
**indices
, unsigned *nbIndices
,
1429 const float *left
, const float *top
,
1430 const float *right
, const float *bottom
)
1435 *vertexCoord
= vlc_alloc(*nbVertices
* 3, sizeof(GLfloat
));
1436 if (*vertexCoord
== NULL
)
1438 *textureCoord
= vlc_alloc(nbPlanes
* *nbVertices
* 2, sizeof(GLfloat
));
1439 if (*textureCoord
== NULL
)
1444 *indices
= vlc_alloc(*nbIndices
, sizeof(GLushort
));
1445 if (*indices
== NULL
)
1447 free(*textureCoord
);
1452 static const GLfloat coord
[] = {
1459 memcpy(*vertexCoord
, coord
, *nbVertices
* 3 * sizeof(GLfloat
));
1461 for (unsigned p
= 0; p
< nbPlanes
; ++p
)
1463 const GLfloat tex
[] = {
1470 memcpy(*textureCoord
+ p
* *nbVertices
* 2, tex
,
1471 *nbVertices
* 2 * sizeof(GLfloat
));
1474 const GLushort ind
[] = {
1479 memcpy(*indices
, ind
, *nbIndices
* sizeof(GLushort
));
1484 static int SetupCoords(vout_display_opengl_t
*vgl
,
1485 const float *left
, const float *top
,
1486 const float *right
, const float *bottom
)
1488 GLfloat
*vertexCoord
, *textureCoord
;
1490 unsigned nbVertices
, nbIndices
;
1493 switch (vgl
->fmt
.projection_mode
)
1495 case PROJECTION_MODE_RECTANGULAR
:
1496 i_ret
= BuildRectangle(vgl
->prgm
->tc
->tex_count
,
1497 &vertexCoord
, &textureCoord
, &nbVertices
,
1498 &indices
, &nbIndices
,
1499 left
, top
, right
, bottom
);
1501 case PROJECTION_MODE_EQUIRECTANGULAR
:
1502 i_ret
= BuildSphere(vgl
->prgm
->tc
->tex_count
,
1503 &vertexCoord
, &textureCoord
, &nbVertices
,
1504 &indices
, &nbIndices
,
1505 left
, top
, right
, bottom
);
1507 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
:
1508 i_ret
= BuildCube(vgl
->prgm
->tc
->tex_count
,
1509 (float)vgl
->fmt
.i_cubemap_padding
/ vgl
->fmt
.i_width
,
1510 (float)vgl
->fmt
.i_cubemap_padding
/ vgl
->fmt
.i_height
,
1511 &vertexCoord
, &textureCoord
, &nbVertices
,
1512 &indices
, &nbIndices
,
1513 left
, top
, right
, bottom
);
1516 i_ret
= VLC_EGENERIC
;
1520 if (i_ret
!= VLC_SUCCESS
)
1523 for (unsigned j
= 0; j
< vgl
->prgm
->tc
->tex_count
; j
++)
1525 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->texture_buffer_object
[j
]);
1526 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, nbVertices
* 2 * sizeof(GLfloat
),
1527 textureCoord
+ j
* nbVertices
* 2, GL_STATIC_DRAW
);
1530 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->vertex_buffer_object
);
1531 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, nbVertices
* 3 * sizeof(GLfloat
),
1532 vertexCoord
, GL_STATIC_DRAW
);
1534 vgl
->vt
.BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, vgl
->index_buffer_object
);
1535 vgl
->vt
.BufferData(GL_ELEMENT_ARRAY_BUFFER
, nbIndices
* sizeof(GLushort
),
1536 indices
, GL_STATIC_DRAW
);
1542 vgl
->nb_indices
= nbIndices
;
1547 static void DrawWithShaders(vout_display_opengl_t
*vgl
, struct prgm
*prgm
)
1549 opengl_tex_converter_t
*tc
= prgm
->tc
;
1550 tc
->pf_prepare_shader(tc
, vgl
->tex_width
, vgl
->tex_height
, 1.0f
);
1552 for (unsigned j
= 0; j
< vgl
->prgm
->tc
->tex_count
; j
++) {
1553 assert(vgl
->texture
[j
] != 0);
1554 vgl
->vt
.ActiveTexture(GL_TEXTURE0
+j
);
1555 vgl
->vt
.BindTexture(tc
->tex_target
, vgl
->texture
[j
]);
1557 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->texture_buffer_object
[j
]);
1559 assert(prgm
->aloc
.MultiTexCoord
[j
] != -1);
1560 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.MultiTexCoord
[j
]);
1561 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.MultiTexCoord
[j
], 2, GL_FLOAT
,
1565 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->vertex_buffer_object
);
1566 vgl
->vt
.BindBuffer(GL_ELEMENT_ARRAY_BUFFER
, vgl
->index_buffer_object
);
1567 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.VertexPosition
);
1568 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.VertexPosition
, 3, GL_FLOAT
, 0, 0, 0);
1570 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.OrientationMatrix
, 1, GL_FALSE
,
1571 prgm
->var
.OrientationMatrix
);
1572 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ProjectionMatrix
, 1, GL_FALSE
,
1573 prgm
->var
.ProjectionMatrix
);
1574 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZRotMatrix
, 1, GL_FALSE
,
1575 prgm
->var
.ZRotMatrix
);
1576 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.YRotMatrix
, 1, GL_FALSE
,
1577 prgm
->var
.YRotMatrix
);
1578 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.XRotMatrix
, 1, GL_FALSE
,
1579 prgm
->var
.XRotMatrix
);
1580 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZoomMatrix
, 1, GL_FALSE
,
1581 prgm
->var
.ZoomMatrix
);
1583 vgl
->vt
.DrawElements(GL_TRIANGLES
, vgl
->nb_indices
, GL_UNSIGNED_SHORT
, 0);
1587 static void GetTextureCropParamsForStereo(unsigned i_nbTextures
,
1588 const float *stereoCoefs
,
1589 const float *stereoOffsets
,
1590 float *left
, float *top
,
1591 float *right
, float *bottom
)
1593 for (unsigned i
= 0; i
< i_nbTextures
; ++i
)
1595 float f_2eyesWidth
= right
[i
] - left
[i
];
1596 left
[i
] = left
[i
] + f_2eyesWidth
* stereoOffsets
[0];
1597 right
[i
] = left
[i
] + f_2eyesWidth
* stereoCoefs
[0];
1599 float f_2eyesHeight
= bottom
[i
] - top
[i
];
1600 top
[i
] = top
[i
] + f_2eyesHeight
* stereoOffsets
[1];
1601 bottom
[i
] = top
[i
] + f_2eyesHeight
* stereoCoefs
[1];
1605 static void TextureCropForStereo(vout_display_opengl_t
*vgl
,
1606 float *left
, float *top
,
1607 float *right
, float *bottom
)
1609 float stereoCoefs
[2];
1610 float stereoOffsets
[2];
1612 switch (vgl
->fmt
.multiview_mode
)
1614 case MULTIVIEW_STEREO_TB
:
1615 // Display only the left eye.
1616 stereoCoefs
[0] = 1; stereoCoefs
[1] = 0.5;
1617 stereoOffsets
[0] = 0; stereoOffsets
[1] = 0;
1618 GetTextureCropParamsForStereo(vgl
->prgm
->tc
->tex_count
,
1619 stereoCoefs
, stereoOffsets
,
1620 left
, top
, right
, bottom
);
1622 case MULTIVIEW_STEREO_SBS
:
1623 // Display only the left eye.
1624 stereoCoefs
[0] = 0.5; stereoCoefs
[1] = 1;
1625 stereoOffsets
[0] = 0; stereoOffsets
[1] = 0;
1626 GetTextureCropParamsForStereo(vgl
->prgm
->tc
->tex_count
,
1627 stereoCoefs
, stereoOffsets
,
1628 left
, top
, right
, bottom
);
1635 int vout_display_opengl_Display(vout_display_opengl_t
*vgl
,
1636 const video_format_t
*source
)
1638 GL_ASSERT_NOERROR();
1640 /* Why drawing here and not in Render()? Because this way, the
1641 OpenGL providers can call vout_display_opengl_Display to force redraw.
1642 Currently, the OS X provider uses it to get a smooth window resizing */
1643 vgl
->vt
.Clear(GL_COLOR_BUFFER_BIT
);
1645 vgl
->vt
.UseProgram(vgl
->prgm
->id
);
1647 if (source
->i_x_offset
!= vgl
->last_source
.i_x_offset
1648 || source
->i_y_offset
!= vgl
->last_source
.i_y_offset
1649 || source
->i_visible_width
!= vgl
->last_source
.i_visible_width
1650 || source
->i_visible_height
!= vgl
->last_source
.i_visible_height
)
1652 float left
[PICTURE_PLANE_MAX
];
1653 float top
[PICTURE_PLANE_MAX
];
1654 float right
[PICTURE_PLANE_MAX
];
1655 float bottom
[PICTURE_PLANE_MAX
];
1656 const opengl_tex_converter_t
*tc
= vgl
->prgm
->tc
;
1657 for (unsigned j
= 0; j
< tc
->tex_count
; j
++)
1659 float scale_w
= (float)tc
->texs
[j
].w
.num
/ tc
->texs
[j
].w
.den
1660 / vgl
->tex_width
[j
];
1661 float scale_h
= (float)tc
->texs
[j
].h
.num
/ tc
->texs
[j
].h
.den
1662 / vgl
->tex_height
[j
];
1664 /* Warning: if NPOT is not supported a larger texture is
1665 allocated. This will cause right and bottom coordinates to
1666 land on the edge of two texels with the texels to the
1667 right/bottom uninitialized by the call to
1668 glTexSubImage2D. This might cause a green line to appear on
1669 the right/bottom of the display.
1670 There are two possible solutions:
1671 - Manually mirror the edges of the texture.
1672 - Add a "-1" when computing right and bottom, however the
1673 last row/column might not be displayed at all.
1675 left
[j
] = (source
->i_x_offset
+ 0 ) * scale_w
;
1676 top
[j
] = (source
->i_y_offset
+ 0 ) * scale_h
;
1677 right
[j
] = (source
->i_x_offset
+ source
->i_visible_width
) * scale_w
;
1678 bottom
[j
] = (source
->i_y_offset
+ source
->i_visible_height
) * scale_h
;
1681 TextureCropForStereo(vgl
, left
, top
, right
, bottom
);
1682 int ret
= SetupCoords(vgl
, left
, top
, right
, bottom
);
1683 if (ret
!= VLC_SUCCESS
)
1686 vgl
->last_source
.i_x_offset
= source
->i_x_offset
;
1687 vgl
->last_source
.i_y_offset
= source
->i_y_offset
;
1688 vgl
->last_source
.i_visible_width
= source
->i_visible_width
;
1689 vgl
->last_source
.i_visible_height
= source
->i_visible_height
;
1691 DrawWithShaders(vgl
, vgl
->prgm
);
1693 /* Draw the subpictures */
1694 // Change the program for overlays
1695 struct prgm
*prgm
= vgl
->sub_prgm
;
1696 GLuint program
= prgm
->id
;
1697 opengl_tex_converter_t
*tc
= prgm
->tc
;
1698 vgl
->vt
.UseProgram(program
);
1700 vgl
->vt
.Enable(GL_BLEND
);
1701 vgl
->vt
.BlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
1703 /* We need two buffer objects for each region: for vertex and texture coordinates. */
1704 if (2 * vgl
->region_count
> vgl
->subpicture_buffer_object_count
) {
1705 if (vgl
->subpicture_buffer_object_count
> 0)
1706 vgl
->vt
.DeleteBuffers(vgl
->subpicture_buffer_object_count
,
1707 vgl
->subpicture_buffer_object
);
1708 vgl
->subpicture_buffer_object_count
= 0;
1710 int new_count
= 2 * vgl
->region_count
;
1711 vgl
->subpicture_buffer_object
= realloc_or_free(vgl
->subpicture_buffer_object
, new_count
* sizeof(GLuint
));
1712 if (!vgl
->subpicture_buffer_object
)
1715 vgl
->subpicture_buffer_object_count
= new_count
;
1716 vgl
->vt
.GenBuffers(vgl
->subpicture_buffer_object_count
,
1717 vgl
->subpicture_buffer_object
);
1720 vgl
->vt
.ActiveTexture(GL_TEXTURE0
+ 0);
1721 for (int i
= 0; i
< vgl
->region_count
; i
++) {
1722 gl_region_t
*glr
= &vgl
->region
[i
];
1723 const GLfloat vertexCoord
[] = {
1724 glr
->left
, glr
->top
,
1725 glr
->left
, glr
->bottom
,
1726 glr
->right
, glr
->top
,
1727 glr
->right
, glr
->bottom
,
1729 const GLfloat textureCoord
[] = {
1731 0.0, glr
->tex_height
,
1732 glr
->tex_width
, 0.0,
1733 glr
->tex_width
, glr
->tex_height
,
1736 assert(glr
->texture
!= 0);
1737 vgl
->vt
.BindTexture(tc
->tex_target
, glr
->texture
);
1739 tc
->pf_prepare_shader(tc
, &glr
->width
, &glr
->height
, glr
->alpha
);
1741 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->subpicture_buffer_object
[2 * i
]);
1742 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, sizeof(textureCoord
), textureCoord
, GL_STATIC_DRAW
);
1743 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.MultiTexCoord
[0]);
1744 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.MultiTexCoord
[0], 2, GL_FLOAT
,
1747 vgl
->vt
.BindBuffer(GL_ARRAY_BUFFER
, vgl
->subpicture_buffer_object
[2 * i
+ 1]);
1748 vgl
->vt
.BufferData(GL_ARRAY_BUFFER
, sizeof(vertexCoord
), vertexCoord
, GL_STATIC_DRAW
);
1749 vgl
->vt
.EnableVertexAttribArray(prgm
->aloc
.VertexPosition
);
1750 vgl
->vt
.VertexAttribPointer(prgm
->aloc
.VertexPosition
, 2, GL_FLOAT
,
1753 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.OrientationMatrix
, 1, GL_FALSE
,
1754 prgm
->var
.OrientationMatrix
);
1755 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ProjectionMatrix
, 1, GL_FALSE
,
1756 prgm
->var
.ProjectionMatrix
);
1757 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZRotMatrix
, 1, GL_FALSE
,
1758 prgm
->var
.ZRotMatrix
);
1759 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.YRotMatrix
, 1, GL_FALSE
,
1760 prgm
->var
.YRotMatrix
);
1761 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.XRotMatrix
, 1, GL_FALSE
,
1762 prgm
->var
.XRotMatrix
);
1763 vgl
->vt
.UniformMatrix4fv(prgm
->uloc
.ZoomMatrix
, 1, GL_FALSE
,
1764 prgm
->var
.ZoomMatrix
);
1766 vgl
->vt
.DrawArrays(GL_TRIANGLE_STRIP
, 0, 4);
1768 vgl
->vt
.Disable(GL_BLEND
);
1771 vlc_gl_Swap(vgl
->gl
);
1773 GL_ASSERT_NOERROR();