opengl: factorize program creation
[vlc.git] / modules / video_output / opengl / renderer.c
blob416530ffeae0d7f7bc86ec3880e6755bdab116a5
1 /*****************************************************************************
2 * renderer.c
3 *****************************************************************************
4 * Copyright (C) 2004-2020 VLC authors and VideoLAN
5 * Copyright (C) 2009, 2011 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8 * Ilkka Ollakka <ileoo@videolan.org>
9 * Rémi Denis-Courmont
10 * Adrien Maglo <magsoft at videolan dot org>
11 * Felix Paul Kühne <fkuehne at videolan dot org>
12 * Pierre d'Herbemont <pdherbemont at videolan dot org>
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU Lesser General Public License as published by
16 * the Free Software Foundation; either version 2.1 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public License
25 * along with this program; if not, write to the Free Software Foundation,
26 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include "renderer.h"
35 #include <assert.h>
36 #include <vlc_common.h>
37 #include <vlc_es.h>
38 #include <vlc_picture.h>
40 #include "gl_util.h"
41 #include "internal.h"
42 #include "interop.h"
43 #include "vout_helper.h"
45 #define SPHERE_RADIUS 1.f
47 static const GLfloat identity[] = {
48 1.0f, 0.0f, 0.0f, 0.0f,
49 0.0f, 1.0f, 0.0f, 0.0f,
50 0.0f, 0.0f, 1.0f, 0.0f,
51 0.0f, 0.0f, 0.0f, 1.0f
54 static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {
56 const GLfloat m[] = {
57 /* x y z w */
58 1.0f, 0.0f, 0.0f, 0.0f,
59 0.0f, 1.0f, 0.0f, 0.0f,
60 0.0f, 0.0f, 1.0f, 0.0f,
61 0.0f, 0.0f, zoom, 1.0f
64 memcpy(matrix, m, sizeof(m));
67 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
68 static void getProjectionMatrix(float sar, float fovy, GLfloat matrix[static 16]) {
70 float zFar = 1000;
71 float zNear = 0.01;
73 float f = 1.f / tanf(fovy / 2.f);
75 const GLfloat m[] = {
76 f / sar, 0.f, 0.f, 0.f,
77 0.f, f, 0.f, 0.f,
78 0.f, 0.f, (zNear + zFar) / (zNear - zFar), -1.f,
79 0.f, 0.f, (2 * zNear * zFar) / (zNear - zFar), 0.f};
81 memcpy(matrix, m, sizeof(m));
84 static void getViewpointMatrixes(struct vlc_gl_renderer *renderer,
85 video_projection_mode_t projection_mode)
87 if (projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
88 || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
90 getProjectionMatrix(renderer->f_sar, renderer->f_fovy,
91 renderer->var.ProjectionMatrix);
92 getZoomMatrix(renderer->f_z, renderer->var.ZoomMatrix);
94 /* renderer->vp has been reversed and is a world transform */
95 vlc_viewpoint_to_4x4(&renderer->vp, renderer->var.ViewMatrix);
97 else
99 memcpy(renderer->var.ProjectionMatrix, identity, sizeof(identity));
100 memcpy(renderer->var.ZoomMatrix, identity, sizeof(identity));
101 memcpy(renderer->var.ViewMatrix, identity, sizeof(identity));
106 static void getOrientationTransformMatrix(video_orientation_t orientation,
107 GLfloat matrix[static 16])
109 memcpy(matrix, identity, sizeof(identity));
111 const int k_cos_pi = -1;
112 const int k_cos_pi_2 = 0;
113 const int k_cos_n_pi_2 = 0;
115 const int k_sin_pi = 0;
116 const int k_sin_pi_2 = 1;
117 const int k_sin_n_pi_2 = -1;
119 switch (orientation) {
121 case ORIENT_ROTATED_90:
122 matrix[0 * 4 + 0] = k_cos_pi_2;
123 matrix[0 * 4 + 1] = -k_sin_pi_2;
124 matrix[1 * 4 + 0] = k_sin_pi_2;
125 matrix[1 * 4 + 1] = k_cos_pi_2;
126 matrix[3 * 4 + 1] = 1;
127 break;
128 case ORIENT_ROTATED_180:
129 matrix[0 * 4 + 0] = k_cos_pi;
130 matrix[0 * 4 + 1] = -k_sin_pi;
131 matrix[1 * 4 + 0] = k_sin_pi;
132 matrix[1 * 4 + 1] = k_cos_pi;
133 matrix[3 * 4 + 0] = 1;
134 matrix[3 * 4 + 1] = 1;
135 break;
136 case ORIENT_ROTATED_270:
137 matrix[0 * 4 + 0] = k_cos_n_pi_2;
138 matrix[0 * 4 + 1] = -k_sin_n_pi_2;
139 matrix[1 * 4 + 0] = k_sin_n_pi_2;
140 matrix[1 * 4 + 1] = k_cos_n_pi_2;
141 matrix[3 * 4 + 0] = 1;
142 break;
143 case ORIENT_HFLIPPED:
144 matrix[0 * 4 + 0] = -1;
145 matrix[3 * 4 + 0] = 1;
146 break;
147 case ORIENT_VFLIPPED:
148 matrix[1 * 4 + 1] = -1;
149 matrix[3 * 4 + 1] = 1;
150 break;
151 case ORIENT_TRANSPOSED:
152 matrix[0 * 4 + 0] = 0;
153 matrix[1 * 4 + 1] = 0;
154 matrix[2 * 4 + 2] = -1;
155 matrix[0 * 4 + 1] = 1;
156 matrix[1 * 4 + 0] = 1;
157 break;
158 case ORIENT_ANTI_TRANSPOSED:
159 matrix[0 * 4 + 0] = 0;
160 matrix[1 * 4 + 1] = 0;
161 matrix[2 * 4 + 2] = -1;
162 matrix[0 * 4 + 1] = -1;
163 matrix[1 * 4 + 0] = -1;
164 matrix[3 * 4 + 0] = 1;
165 matrix[3 * 4 + 1] = 1;
166 break;
167 default:
168 break;
172 static char *
173 BuildVertexShader(const struct vlc_gl_renderer *renderer, unsigned plane_count)
175 /* Basic vertex shader */
176 static const char *template =
177 "#version %u\n"
178 "varying vec2 TexCoord0;\n"
179 "attribute vec4 MultiTexCoord0;\n"
180 "%s%s"
181 "attribute vec3 VertexPosition;\n"
182 "uniform mat4 TransformMatrix;\n"
183 "uniform mat4 OrientationMatrix;\n"
184 "uniform mat4 ProjectionMatrix;\n"
185 "uniform mat4 ZoomMatrix;\n"
186 "uniform mat4 ViewMatrix;\n"
187 "void main() {\n"
188 " TexCoord0 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord0).st;\n"
189 "%s%s"
190 " gl_Position = ProjectionMatrix * ZoomMatrix * ViewMatrix\n"
191 " * vec4(VertexPosition, 1.0);\n"
192 "}";
194 const char *coord1_header = plane_count > 1 ?
195 "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
196 const char *coord1_code = plane_count > 1 ?
197 " TexCoord1 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord1).st;\n" : "";
198 const char *coord2_header = plane_count > 2 ?
199 "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
200 const char *coord2_code = plane_count > 2 ?
201 " TexCoord2 = vec4(TransformMatrix * OrientationMatrix * MultiTexCoord2).st;\n" : "";
203 char *code;
204 if (asprintf(&code, template, renderer->glsl_version, coord1_header,
205 coord2_header, coord1_code, coord2_code) < 0)
206 return NULL;
208 if (renderer->b_dump_shaders)
209 msg_Dbg(renderer->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
210 (const char *) &renderer->interop->fmt.i_chroma, code);
211 return code;
214 static int
215 opengl_link_program(struct vlc_gl_renderer *renderer)
217 struct vlc_gl_interop *interop = renderer->interop;
218 const opengl_vtable_t *vt = renderer->vt;
220 char *vertex_shader = BuildVertexShader(renderer, interop->tex_count);
221 if (!vertex_shader)
222 return VLC_EGENERIC;
224 char *fragment_shader =
225 opengl_fragment_shader_init(renderer, interop->tex_target,
226 interop->sw_fmt.i_chroma,
227 interop->sw_fmt.space);
228 if (!fragment_shader)
230 free(vertex_shader);
231 return VLC_EGENERIC;
234 assert(interop->tex_target != 0 &&
235 interop->tex_count > 0 &&
236 interop->ops->update_textures != NULL &&
237 renderer->pf_fetch_locations != NULL &&
238 renderer->pf_prepare_shader != NULL);
240 GLuint program_id =
241 vlc_gl_BuildProgram(VLC_OBJECT(renderer->gl), vt,
242 1, (const char **) &vertex_shader,
243 1, (const char **) &fragment_shader);
244 free(vertex_shader);
245 free(fragment_shader);
246 if (!program_id)
247 return VLC_EGENERIC;
249 /* Fetch UniformLocations and AttribLocations */
250 #define GET_LOC(type, x, str) do { \
251 x = vt->Get##type##Location(program_id, str); \
252 assert(x != -1); \
253 if (x == -1) { \
254 msg_Err(renderer->gl, "Unable to Get"#type"Location(%s)", str); \
255 goto error; \
257 } while (0)
258 #define GET_ULOC(x, str) GET_LOC(Uniform, renderer->uloc.x, str)
259 #define GET_ALOC(x, str) GET_LOC(Attrib, renderer->aloc.x, str)
260 GET_ULOC(TransformMatrix, "TransformMatrix");
261 GET_ULOC(OrientationMatrix, "OrientationMatrix");
262 GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
263 GET_ULOC(ViewMatrix, "ViewMatrix");
264 GET_ULOC(ZoomMatrix, "ZoomMatrix");
266 GET_ALOC(VertexPosition, "VertexPosition");
267 GET_ALOC(MultiTexCoord[0], "MultiTexCoord0");
268 /* MultiTexCoord 1 and 2 can be optimized out if not used */
269 if (interop->tex_count > 1)
270 GET_ALOC(MultiTexCoord[1], "MultiTexCoord1");
271 else
272 renderer->aloc.MultiTexCoord[1] = -1;
273 if (interop->tex_count > 2)
274 GET_ALOC(MultiTexCoord[2], "MultiTexCoord2");
275 else
276 renderer->aloc.MultiTexCoord[2] = -1;
277 #undef GET_LOC
278 #undef GET_ULOC
279 #undef GET_ALOC
280 int ret = renderer->pf_fetch_locations(renderer, program_id);
281 assert(ret == VLC_SUCCESS);
282 if (ret != VLC_SUCCESS)
284 msg_Err(renderer->gl, "Unable to get locations from tex_conv");
285 goto error;
288 renderer->program_id = program_id;
290 return VLC_SUCCESS;
292 error:
293 vt->DeleteProgram(program_id);
294 renderer->program_id = 0;
295 return VLC_EGENERIC;
298 void
299 vlc_gl_renderer_Delete(struct vlc_gl_renderer *renderer)
301 struct vlc_gl_interop *interop = renderer->interop;
302 const opengl_vtable_t *vt = renderer->vt;
304 vt->DeleteBuffers(1, &renderer->vertex_buffer_object);
305 vt->DeleteBuffers(1, &renderer->index_buffer_object);
306 vt->DeleteBuffers(interop->tex_count, renderer->texture_buffer_object);
308 if (!interop->handle_texs_gen)
309 vt->DeleteTextures(interop->tex_count, renderer->textures);
311 vlc_gl_interop_Delete(interop);
312 if (renderer->program_id != 0)
313 renderer->vt->DeleteProgram(renderer->program_id);
315 #ifdef HAVE_LIBPLACEBO
316 FREENULL(renderer->uloc.pl_vars);
317 if (renderer->pl_ctx)
318 pl_context_destroy(&renderer->pl_ctx);
319 #endif
321 free(renderer);
324 struct vlc_gl_renderer *
325 vlc_gl_renderer_New(vlc_gl_t *gl, const opengl_vtable_t *vt,
326 vlc_video_context *context, const video_format_t *fmt,
327 bool supports_npot, bool b_dump_shaders)
329 struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
330 if (!renderer)
331 return NULL;
333 struct vlc_gl_interop *interop =
334 vlc_gl_interop_New(gl, vt, context, fmt, false);
335 if (!interop)
337 free(renderer);
338 return NULL;
341 renderer->interop = interop;
343 renderer->gl = gl;
344 renderer->vt = vt;
345 renderer->b_dump_shaders = b_dump_shaders;
346 #if defined(USE_OPENGL_ES2)
347 renderer->glsl_version = 100;
348 renderer->glsl_precision_header = "precision highp float;\n";
349 #else
350 renderer->glsl_version = 120;
351 renderer->glsl_precision_header = "";
352 #endif
354 #ifdef HAVE_LIBPLACEBO
355 // Create the main libplacebo context
356 renderer->pl_ctx = vlc_placebo_Create(VLC_OBJECT(gl));
357 if (renderer->pl_ctx) {
358 # if PL_API_VER >= 20
359 renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL);
360 # elif PL_API_VER >= 6
361 renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL, 0);
362 # else
363 renderer->pl_sh = pl_shader_alloc(renderer->pl_ctx, NULL, 0, 0);
364 # endif
366 #endif
368 int ret = opengl_link_program(renderer);
369 if (ret != VLC_SUCCESS)
371 vlc_gl_renderer_Delete(renderer);
372 return NULL;
375 getOrientationTransformMatrix(interop->fmt.orientation,
376 renderer->var.OrientationMatrix);
377 getViewpointMatrixes(renderer, interop->fmt.projection_mode);
379 /* Update the fmt to main program one */
380 renderer->fmt = interop->fmt;
381 /* The orientation is handled by the orientation matrix */
382 renderer->fmt.orientation = fmt->orientation;
384 /* Texture size */
385 for (unsigned j = 0; j < interop->tex_count; j++) {
386 const GLsizei w = renderer->fmt.i_visible_width * interop->texs[j].w.num
387 / interop->texs[j].w.den;
388 const GLsizei h = renderer->fmt.i_visible_height * interop->texs[j].h.num
389 / interop->texs[j].h.den;
390 if (supports_npot) {
391 renderer->tex_width[j] = w;
392 renderer->tex_height[j] = h;
393 } else {
394 renderer->tex_width[j] = vlc_align_pot(w);
395 renderer->tex_height[j] = vlc_align_pot(h);
399 if (!interop->handle_texs_gen)
401 ret = vlc_gl_interop_GenerateTextures(interop, renderer->tex_width,
402 renderer->tex_height,
403 renderer->textures);
404 if (ret != VLC_SUCCESS)
406 vlc_gl_renderer_Delete(renderer);
407 return NULL;
411 /* */
412 vt->Disable(GL_BLEND);
413 vt->Disable(GL_DEPTH_TEST);
414 vt->DepthMask(GL_FALSE);
415 vt->Enable(GL_CULL_FACE);
416 vt->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
417 vt->Clear(GL_COLOR_BUFFER_BIT);
419 vt->GenBuffers(1, &renderer->vertex_buffer_object);
420 vt->GenBuffers(1, &renderer->index_buffer_object);
421 vt->GenBuffers(interop->tex_count, renderer->texture_buffer_object);
423 return renderer;
426 static void UpdateZ(struct vlc_gl_renderer *renderer)
428 /* Do trigonometry to calculate the minimal z value
429 * that will allow us to zoom out without seeing the outside of the
430 * sphere (black borders). */
431 float tan_fovx_2 = tanf(renderer->f_fovx / 2);
432 float tan_fovy_2 = tanf(renderer->f_fovy / 2);
433 float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
434 tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
436 /* The FOV value above which z is dynamically calculated. */
437 const float z_thresh = 90.f;
439 if (renderer->f_fovx <= z_thresh * M_PI / 180)
440 renderer->f_z = 0;
441 else
443 float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
444 renderer->f_z = f * renderer->f_fovx - f * z_thresh * M_PI / 180;
445 if (renderer->f_z < z_min)
446 renderer->f_z = z_min;
450 static void UpdateFOVy(struct vlc_gl_renderer *renderer)
452 renderer->f_fovy = 2 * atanf(tanf(renderer->f_fovx / 2) / renderer->f_sar);
456 vlc_gl_renderer_SetViewpoint(struct vlc_gl_renderer *renderer,
457 const vlc_viewpoint_t *p_vp)
459 if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
460 || p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
461 return VLC_EBADVAR;
463 // Convert degree into radian
464 float f_fovx = p_vp->fov * (float)M_PI / 180.f;
466 /* vgl->vp needs to be converted into world transform */
467 vlc_viewpoint_reverse(&renderer->vp, p_vp);
469 if (fabsf(f_fovx - renderer->f_fovx) >= 0.001f)
471 /* FOVx has changed. */
472 renderer->f_fovx = f_fovx;
473 UpdateFOVy(renderer);
474 UpdateZ(renderer);
476 getViewpointMatrixes(renderer, renderer->fmt.projection_mode);
478 return VLC_SUCCESS;
481 void
482 vlc_gl_renderer_SetWindowAspectRatio(struct vlc_gl_renderer *renderer,
483 float f_sar)
485 /* Each time the window size changes, we must recompute the minimum zoom
486 * since the aspect ration changes.
487 * We must also set the new current zoom value. */
488 renderer->f_sar = f_sar;
489 UpdateFOVy(renderer);
490 UpdateZ(renderer);
491 getViewpointMatrixes(renderer, renderer->fmt.projection_mode);
495 vlc_gl_renderer_Prepare(struct vlc_gl_renderer *renderer, picture_t *picture)
497 const struct vlc_gl_interop *interop = renderer->interop;
498 /* Update the texture */
499 return interop->ops->update_textures(interop, renderer->textures,
500 renderer->tex_width,
501 renderer->tex_height, picture,
502 NULL);
505 static int BuildSphere(unsigned nbPlanes,
506 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
507 GLushort **indices, unsigned *nbIndices,
508 const float *left, const float *top,
509 const float *right, const float *bottom)
511 unsigned nbLatBands = 128;
512 unsigned nbLonBands = 128;
514 *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
515 *nbIndices = nbLatBands * nbLonBands * 3 * 2;
517 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
518 if (*vertexCoord == NULL)
519 return VLC_ENOMEM;
520 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
521 if (*textureCoord == NULL)
523 free(*vertexCoord);
524 return VLC_ENOMEM;
526 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
527 if (*indices == NULL)
529 free(*textureCoord);
530 free(*vertexCoord);
531 return VLC_ENOMEM;
534 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
535 float theta = lat * (float) M_PI / nbLatBands;
536 float sinTheta, cosTheta;
538 sincosf(theta, &sinTheta, &cosTheta);
540 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
541 float phi = lon * 2 * (float) M_PI / nbLonBands;
542 float sinPhi, cosPhi;
544 sincosf(phi, &sinPhi, &cosPhi);
546 float x = cosPhi * sinTheta;
547 float y = cosTheta;
548 float z = sinPhi * sinTheta;
550 unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
551 (*vertexCoord)[off1] = SPHERE_RADIUS * x;
552 (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
553 (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
555 for (unsigned p = 0; p < nbPlanes; ++p)
557 unsigned off2 = (p * (nbLatBands + 1) * (nbLonBands + 1)
558 + lat * (nbLonBands + 1) + lon) * 2;
559 float width = right[p] - left[p];
560 float height = bottom[p] - top[p];
561 float u = (float)lon / nbLonBands * width;
562 float v = (float)lat / nbLatBands * height;
563 (*textureCoord)[off2] = u;
564 (*textureCoord)[off2 + 1] = v;
569 for (unsigned lat = 0; lat < nbLatBands; lat++) {
570 for (unsigned lon = 0; lon < nbLonBands; lon++) {
571 unsigned first = (lat * (nbLonBands + 1)) + lon;
572 unsigned second = first + nbLonBands + 1;
574 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
576 (*indices)[off] = first;
577 (*indices)[off + 1] = second;
578 (*indices)[off + 2] = first + 1;
580 (*indices)[off + 3] = second;
581 (*indices)[off + 4] = second + 1;
582 (*indices)[off + 5] = first + 1;
586 return VLC_SUCCESS;
590 static int BuildCube(unsigned nbPlanes,
591 float padW, float padH,
592 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
593 GLushort **indices, unsigned *nbIndices,
594 const float *left, const float *top,
595 const float *right, const float *bottom)
597 *nbVertices = 4 * 6;
598 *nbIndices = 6 * 6;
600 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
601 if (*vertexCoord == NULL)
602 return VLC_ENOMEM;
603 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
604 if (*textureCoord == NULL)
606 free(*vertexCoord);
607 return VLC_ENOMEM;
609 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
610 if (*indices == NULL)
612 free(*textureCoord);
613 free(*vertexCoord);
614 return VLC_ENOMEM;
617 static const GLfloat coord[] = {
618 -1.0, 1.0, -1.0f, // front
619 -1.0, -1.0, -1.0f,
620 1.0, 1.0, -1.0f,
621 1.0, -1.0, -1.0f,
623 -1.0, 1.0, 1.0f, // back
624 -1.0, -1.0, 1.0f,
625 1.0, 1.0, 1.0f,
626 1.0, -1.0, 1.0f,
628 -1.0, 1.0, -1.0f, // left
629 -1.0, -1.0, -1.0f,
630 -1.0, 1.0, 1.0f,
631 -1.0, -1.0, 1.0f,
633 1.0f, 1.0, -1.0f, // right
634 1.0f, -1.0, -1.0f,
635 1.0f, 1.0, 1.0f,
636 1.0f, -1.0, 1.0f,
638 -1.0, -1.0, 1.0f, // bottom
639 -1.0, -1.0, -1.0f,
640 1.0, -1.0, 1.0f,
641 1.0, -1.0, -1.0f,
643 -1.0, 1.0, 1.0f, // top
644 -1.0, 1.0, -1.0f,
645 1.0, 1.0, 1.0f,
646 1.0, 1.0, -1.0f,
649 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
651 for (unsigned p = 0; p < nbPlanes; ++p)
653 float width = right[p] - left[p];
654 float height = bottom[p] - top[p];
656 float col[] = {left[p],
657 left[p] + width * 1.f/3,
658 left[p] + width * 2.f/3,
659 left[p] + width};
661 float row[] = {top[p],
662 top[p] + height * 1.f/2,
663 top[p] + height};
665 const GLfloat tex[] = {
666 col[1] + padW, row[1] + padH, // front
667 col[1] + padW, row[2] - padH,
668 col[2] - padW, row[1] + padH,
669 col[2] - padW, row[2] - padH,
671 col[3] - padW, row[1] + padH, // back
672 col[3] - padW, row[2] - padH,
673 col[2] + padW, row[1] + padH,
674 col[2] + padW, row[2] - padH,
676 col[2] - padW, row[0] + padH, // left
677 col[2] - padW, row[1] - padH,
678 col[1] + padW, row[0] + padH,
679 col[1] + padW, row[1] - padH,
681 col[0] + padW, row[0] + padH, // right
682 col[0] + padW, row[1] - padH,
683 col[1] - padW, row[0] + padH,
684 col[1] - padW, row[1] - padH,
686 col[0] + padW, row[2] - padH, // bottom
687 col[0] + padW, row[1] + padH,
688 col[1] - padW, row[2] - padH,
689 col[1] - padW, row[1] + padH,
691 col[2] + padW, row[0] + padH, // top
692 col[2] + padW, row[1] - padH,
693 col[3] - padW, row[0] + padH,
694 col[3] - padW, row[1] - padH,
697 memcpy(*textureCoord + p * *nbVertices * 2, tex,
698 *nbVertices * 2 * sizeof(GLfloat));
701 const GLushort ind[] = {
702 0, 1, 2, 2, 1, 3, // front
703 6, 7, 4, 4, 7, 5, // back
704 10, 11, 8, 8, 11, 9, // left
705 12, 13, 14, 14, 13, 15, // right
706 18, 19, 16, 16, 19, 17, // bottom
707 20, 21, 22, 22, 21, 23, // top
710 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
712 return VLC_SUCCESS;
715 static int BuildRectangle(unsigned nbPlanes,
716 GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
717 GLushort **indices, unsigned *nbIndices,
718 const float *left, const float *top,
719 const float *right, const float *bottom)
721 *nbVertices = 4;
722 *nbIndices = 6;
724 *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
725 if (*vertexCoord == NULL)
726 return VLC_ENOMEM;
727 *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
728 if (*textureCoord == NULL)
730 free(*vertexCoord);
731 return VLC_ENOMEM;
733 *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
734 if (*indices == NULL)
736 free(*textureCoord);
737 free(*vertexCoord);
738 return VLC_ENOMEM;
741 static const GLfloat coord[] = {
742 -1.0, 1.0, -1.0f,
743 -1.0, -1.0, -1.0f,
744 1.0, 1.0, -1.0f,
745 1.0, -1.0, -1.0f
748 memcpy(*vertexCoord, coord, *nbVertices * 3 * sizeof(GLfloat));
750 for (unsigned p = 0; p < nbPlanes; ++p)
752 const GLfloat tex[] = {
753 left[p], top[p],
754 left[p], bottom[p],
755 right[p], top[p],
756 right[p], bottom[p]
759 memcpy(*textureCoord + p * *nbVertices * 2, tex,
760 *nbVertices * 2 * sizeof(GLfloat));
763 const GLushort ind[] = {
764 0, 1, 2,
765 2, 1, 3
768 memcpy(*indices, ind, *nbIndices * sizeof(GLushort));
770 return VLC_SUCCESS;
773 static int SetupCoords(struct vlc_gl_renderer *renderer,
774 const float *left, const float *top,
775 const float *right, const float *bottom)
777 const struct vlc_gl_interop *interop = renderer->interop;
778 const opengl_vtable_t *vt = renderer->vt;
780 GLfloat *vertexCoord, *textureCoord;
781 GLushort *indices;
782 unsigned nbVertices, nbIndices;
784 int i_ret;
785 switch (renderer->fmt.projection_mode)
787 case PROJECTION_MODE_RECTANGULAR:
788 i_ret = BuildRectangle(interop->tex_count,
789 &vertexCoord, &textureCoord, &nbVertices,
790 &indices, &nbIndices,
791 left, top, right, bottom);
792 break;
793 case PROJECTION_MODE_EQUIRECTANGULAR:
794 i_ret = BuildSphere(interop->tex_count,
795 &vertexCoord, &textureCoord, &nbVertices,
796 &indices, &nbIndices,
797 left, top, right, bottom);
798 break;
799 case PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD:
800 i_ret = BuildCube(interop->tex_count,
801 (float)renderer->fmt.i_cubemap_padding / renderer->fmt.i_width,
802 (float)renderer->fmt.i_cubemap_padding / renderer->fmt.i_height,
803 &vertexCoord, &textureCoord, &nbVertices,
804 &indices, &nbIndices,
805 left, top, right, bottom);
806 break;
807 default:
808 i_ret = VLC_EGENERIC;
809 break;
812 if (i_ret != VLC_SUCCESS)
813 return i_ret;
815 for (unsigned j = 0; j < interop->tex_count; j++)
817 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object[j]);
818 vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
819 textureCoord + j * nbVertices * 2, GL_STATIC_DRAW);
822 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
823 vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 3 * sizeof(GLfloat),
824 vertexCoord, GL_STATIC_DRAW);
826 vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
827 vt->BufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(GLushort),
828 indices, GL_STATIC_DRAW);
830 free(textureCoord);
831 free(vertexCoord);
832 free(indices);
834 renderer->nb_indices = nbIndices;
836 return VLC_SUCCESS;
839 static void DrawWithShaders(struct vlc_gl_renderer *renderer)
841 const struct vlc_gl_interop *interop = renderer->interop;
842 const opengl_vtable_t *vt = renderer->vt;
843 renderer->pf_prepare_shader(renderer, renderer->tex_width,
844 renderer->tex_height, 1.0f);
846 for (unsigned j = 0; j < interop->tex_count; j++) {
847 assert(renderer->textures[j] != 0);
848 vt->ActiveTexture(GL_TEXTURE0+j);
849 vt->BindTexture(interop->tex_target, renderer->textures[j]);
851 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object[j]);
853 assert(renderer->aloc.MultiTexCoord[j] != -1);
854 vt->EnableVertexAttribArray(renderer->aloc.MultiTexCoord[j]);
855 vt->VertexAttribPointer(renderer->aloc.MultiTexCoord[j], 2,
856 GL_FLOAT, 0, 0, 0);
859 vt->BindBuffer(GL_ARRAY_BUFFER, renderer->vertex_buffer_object);
860 vt->BindBuffer(GL_ELEMENT_ARRAY_BUFFER, renderer->index_buffer_object);
861 vt->EnableVertexAttribArray(renderer->aloc.VertexPosition);
862 vt->VertexAttribPointer(renderer->aloc.VertexPosition, 3, GL_FLOAT, 0, 0, 0);
864 const GLfloat *tm = NULL;
865 if (interop->ops && interop->ops->get_transform_matrix)
866 tm = interop->ops->get_transform_matrix(interop);
867 if (!tm)
868 tm = identity;
870 vt->UniformMatrix4fv(renderer->uloc.TransformMatrix, 1, GL_FALSE, tm);
872 vt->UniformMatrix4fv(renderer->uloc.OrientationMatrix, 1, GL_FALSE,
873 renderer->var.OrientationMatrix);
874 vt->UniformMatrix4fv(renderer->uloc.ProjectionMatrix, 1, GL_FALSE,
875 renderer->var.ProjectionMatrix);
876 vt->UniformMatrix4fv(renderer->uloc.ViewMatrix, 1, GL_FALSE,
877 renderer->var.ViewMatrix);
878 vt->UniformMatrix4fv(renderer->uloc.ZoomMatrix, 1, GL_FALSE,
879 renderer->var.ZoomMatrix);
881 vt->DrawElements(GL_TRIANGLES, renderer->nb_indices, GL_UNSIGNED_SHORT, 0);
885 static void GetTextureCropParamsForStereo(unsigned i_nbTextures,
886 const float *stereoCoefs,
887 const float *stereoOffsets,
888 float *left, float *top,
889 float *right, float *bottom)
891 for (unsigned i = 0; i < i_nbTextures; ++i)
893 float f_2eyesWidth = right[i] - left[i];
894 left[i] = left[i] + f_2eyesWidth * stereoOffsets[0];
895 right[i] = left[i] + f_2eyesWidth * stereoCoefs[0];
897 float f_2eyesHeight = bottom[i] - top[i];
898 top[i] = top[i] + f_2eyesHeight * stereoOffsets[1];
899 bottom[i] = top[i] + f_2eyesHeight * stereoCoefs[1];
903 static void TextureCropForStereo(struct vlc_gl_renderer *renderer,
904 float *left, float *top,
905 float *right, float *bottom)
907 const struct vlc_gl_interop *interop = renderer->interop;
909 float stereoCoefs[2];
910 float stereoOffsets[2];
912 switch (renderer->fmt.multiview_mode)
914 case MULTIVIEW_STEREO_TB:
915 // Display only the left eye.
916 stereoCoefs[0] = 1; stereoCoefs[1] = 0.5;
917 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
918 GetTextureCropParamsForStereo(interop->tex_count,
919 stereoCoefs, stereoOffsets,
920 left, top, right, bottom);
921 break;
922 case MULTIVIEW_STEREO_SBS:
923 // Display only the left eye.
924 stereoCoefs[0] = 0.5; stereoCoefs[1] = 1;
925 stereoOffsets[0] = 0; stereoOffsets[1] = 0;
926 GetTextureCropParamsForStereo(interop->tex_count,
927 stereoCoefs, stereoOffsets,
928 left, top, right, bottom);
929 break;
930 default:
931 break;
936 vlc_gl_renderer_Draw(struct vlc_gl_renderer *renderer,
937 const video_format_t *source)
939 const opengl_vtable_t *vt = renderer->vt;
941 vt->Clear(GL_COLOR_BUFFER_BIT);
943 vt->UseProgram(renderer->program_id);
945 if (source->i_x_offset != renderer->last_source.i_x_offset
946 || source->i_y_offset != renderer->last_source.i_y_offset
947 || source->i_visible_width != renderer->last_source.i_visible_width
948 || source->i_visible_height != renderer->last_source.i_visible_height)
950 float left[PICTURE_PLANE_MAX];
951 float top[PICTURE_PLANE_MAX];
952 float right[PICTURE_PLANE_MAX];
953 float bottom[PICTURE_PLANE_MAX];
954 const struct vlc_gl_interop *interop = renderer->interop;
955 for (unsigned j = 0; j < interop->tex_count; j++)
957 float scale_w = (float)interop->texs[j].w.num / interop->texs[j].w.den
958 / renderer->tex_width[j];
959 float scale_h = (float)interop->texs[j].h.num / interop->texs[j].h.den
960 / renderer->tex_height[j];
962 /* Warning: if NPOT is not supported a larger texture is
963 allocated. This will cause right and bottom coordinates to
964 land on the edge of two texels with the texels to the
965 right/bottom uninitialized by the call to
966 glTexSubImage2D. This might cause a green line to appear on
967 the right/bottom of the display.
968 There are two possible solutions:
969 - Manually mirror the edges of the texture.
970 - Add a "-1" when computing right and bottom, however the
971 last row/column might not be displayed at all.
973 left[j] = (source->i_x_offset + 0 ) * scale_w;
974 top[j] = (source->i_y_offset + 0 ) * scale_h;
975 right[j] = (source->i_x_offset + source->i_visible_width ) * scale_w;
976 bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
979 TextureCropForStereo(renderer, left, top, right, bottom);
980 int ret = SetupCoords(renderer, left, top, right, bottom);
981 if (ret != VLC_SUCCESS)
982 return ret;
984 renderer->last_source.i_x_offset = source->i_x_offset;
985 renderer->last_source.i_y_offset = source->i_y_offset;
986 renderer->last_source.i_visible_width = source->i_visible_width;
987 renderer->last_source.i_visible_height = source->i_visible_height;
989 DrawWithShaders(renderer);
991 return VLC_SUCCESS;