vout_helper.c 57.7 KB
Newer Older
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
6
 *
7
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8
 *          Ilkka Ollakka <ileoo@videolan.org>
9 10 11 12
 *          Rémi Denis-Courmont
 *          Adrien Maglo <magsoft at videolan dot org>
 *          Felix Paul Kühne <fkuehne at videolan dot org>
 *          Pierre d'Herbemont <pdherbemont at videolan dot org>
13
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
14 15 16
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
17 18 19 20
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
21 22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
23
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
24 25 26
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
27
 *****************************************************************************/
28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
31

32
#include <assert.h>
33
#include <math.h>
34

35 36
#include <vlc_common.h>
#include <vlc_picture_pool.h>
37
#include <vlc_subpicture.h>
38
#include <vlc_opengl.h>
39
#include <vlc_modules.h>
40
#include <vlc_vout.h>
41
#include <vlc_viewpoint.h>
42

Thomas Guillem's avatar
Thomas Guillem committed
43
#include "vout_helper.h"
44
#include "internal.h"
45 46 47 48 49

#ifndef GL_CLAMP_TO_EDGE
# define GL_CLAMP_TO_EDGE 0x812F
#endif

50 51
#define SPHERE_RADIUS 1.f

Thomas Guillem's avatar
Thomas Guillem committed
52
/* FIXME: GL_ASSERT_NOERROR disabled for now because:
53
 * Proper GL error handling need to be implemented
Thomas Guillem's avatar
Thomas Guillem committed
54 55 56
 * glClear(GL_COLOR_BUFFER_BIT) throws a GL_INVALID_FRAMEBUFFER_OPERATION on macOS
 * assert fails on vout_display_opengl_Delete on iOS
 */
57 58
#if 0
# define HAVE_GL_ASSERT_NOERROR
Thomas Guillem's avatar
Thomas Guillem committed
59 60 61 62
#endif

#ifdef HAVE_GL_ASSERT_NOERROR
# define GL_ASSERT_NOERROR() do { \
63
    GLenum glError = vgl->vt.GetError(); \
Thomas Guillem's avatar
Thomas Guillem committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    switch (glError) \
    { \
        case GL_NO_ERROR: break; \
        case GL_INVALID_ENUM: assert(!"GL_INVALID_ENUM"); \
        case GL_INVALID_VALUE: assert(!"GL_INVALID_VALUE"); \
        case GL_INVALID_OPERATION: assert(!"GL_INVALID_OPERATION"); \
        case GL_INVALID_FRAMEBUFFER_OPERATION: assert(!"GL_INVALID_FRAMEBUFFER_OPERATION"); \
        case GL_OUT_OF_MEMORY: assert(!"GL_OUT_OF_MEMORY"); \
        default: assert(!"GL_UNKNOWN_ERROR"); \
    } \
} while(0)
#else
# define GL_ASSERT_NOERROR()
#endif

79 80
typedef struct {
    GLuint   texture;
81 82
    GLsizei  width;
    GLsizei  height;
83 84 85 86 87 88 89

    float    alpha;

    float    top;
    float    left;
    float    bottom;
    float    right;
90 91 92

    float    tex_width;
    float    tex_height;
93 94
} gl_region_t;

95 96 97
struct prgm
{
    GLuint id;
98
    opengl_tex_converter_t *tc;
99

100 101 102 103 104 105 106 107 108
    struct {
        GLfloat OrientationMatrix[16];
        GLfloat ProjectionMatrix[16];
        GLfloat ZRotMatrix[16];
        GLfloat YRotMatrix[16];
        GLfloat XRotMatrix[16];
        GLfloat ZoomMatrix[16];
    } var;

109 110 111 112 113 114 115 116 117 118 119 120
    struct { /* UniformLocation */
        GLint OrientationMatrix;
        GLint ProjectionMatrix;
        GLint ZRotMatrix;
        GLint YRotMatrix;
        GLint XRotMatrix;
        GLint ZoomMatrix;
    } uloc;
    struct { /* AttribLocation */
        GLint MultiTexCoord[3];
        GLint VertexPosition;
    } aloc;
121 122
};

123
struct vout_display_opengl_t {
124

125
    vlc_gl_t   *gl;
126
    opengl_vtable_t vt;
127 128 129

    video_format_t fmt;

130 131
    GLsizei    tex_width[PICTURE_PLANE_MAX];
    GLsizei    tex_height[PICTURE_PLANE_MAX];
132

133
    GLuint     texture[PICTURE_PLANE_MAX];
134

135 136 137 138
    int         region_count;
    gl_region_t *region;


139
    picture_pool_t *pool;
140

141 142 143 144
    /* One YUV program and one RGBA program (for subpics) */
    struct prgm prgms[2];
    struct prgm *prgm; /* Main program */
    struct prgm *sub_prgm; /* Subpicture program */
145

146
    unsigned nb_indices;
147
    GLuint vertex_buffer_object;
148
    GLuint index_buffer_object;
149 150
    GLuint texture_buffer_object[PICTURE_PLANE_MAX];

151 152 153
    GLuint *subpicture_buffer_object;
    int    subpicture_buffer_object_count;

154 155 156 157 158 159 160
    struct {
        unsigned int i_x_offset;
        unsigned int i_y_offset;
        unsigned int i_visible_width;
        unsigned int i_visible_height;
    } last_source;

161 162
    /* Non-power-of-2 texture size support */
    bool supports_npot;
163

164 165 166
    /* View point */
    float f_teta;
    float f_phi;
167
    float f_roll;
168 169
    float f_fovx; /* f_fovx and f_fovy are linked but we keep both */
    float f_fovy; /* to avoid recalculating them when needed.      */
170
    float f_z;    /* Position of the camera on the shpere radius vector */
171
    float f_sar;
172 173
};

Thomas Guillem's avatar
Thomas Guillem committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
static const GLfloat identity[] = {
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
};

/* rotation around the Z axis */
static void getZRotMatrix(float theta, GLfloat matrix[static 16])
{
    float st, ct;

    sincosf(theta, &st, &ct);

    const GLfloat m[] = {
    /*  x    y    z    w */
        ct,  -st, 0.f, 0.f,
        st,  ct,  0.f, 0.f,
        0.f, 0.f, 1.f, 0.f,
        0.f, 0.f, 0.f, 1.f
    };

    memcpy(matrix, m, sizeof(m));
}

/* rotation around the Y axis */
static void getYRotMatrix(float theta, GLfloat matrix[static 16])
{
    float st, ct;

    sincosf(theta, &st, &ct);

    const GLfloat m[] = {
    /*  x    y    z    w */
        ct,  0.f, -st, 0.f,
        0.f, 1.f, 0.f, 0.f,
        st,  0.f, ct,  0.f,
        0.f, 0.f, 0.f, 1.f
    };

    memcpy(matrix, m, sizeof(m));
}

/* rotation around the X axis */
static void getXRotMatrix(float phi, GLfloat matrix[static 16])
{
    float sp, cp;

    sincosf(phi, &sp, &cp);

    const GLfloat m[] = {
    /*  x    y    z    w */
        1.f, 0.f, 0.f, 0.f,
        0.f, cp,  sp,  0.f,
        0.f, -sp, cp,  0.f,
        0.f, 0.f, 0.f, 1.f
    };

    memcpy(matrix, m, sizeof(m));
}

static void getZoomMatrix(float zoom, GLfloat matrix[static 16]) {

    const GLfloat m[] = {
        /* x   y     z     w */
        1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, zoom, 1.0f
    };

    memcpy(matrix, m, sizeof(m));
}

/* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
static void getProjectionMatrix(float sar, float fovy, GLfloat matrix[static 16]) {

    float zFar  = 1000;
    float zNear = 0.01;

    float f = 1.f / tanf(fovy / 2.f);

    const GLfloat m[] = {
        f / sar, 0.f,                   0.f,                0.f,
        0.f,     f,                     0.f,                0.f,
        0.f,     0.f,     (zNear + zFar) / (zNear - zFar), -1.f,
        0.f,     0.f, (2 * zNear * zFar) / (zNear - zFar),  0.f};

     memcpy(matrix, m, sizeof(m));
}

265 266 267 268 269 270 271
static void getViewpointMatrixes(vout_display_opengl_t *vgl,
                                 video_projection_mode_t projection_mode,
                                 struct prgm *prgm)
{
    if (projection_mode == PROJECTION_MODE_EQUIRECTANGULAR
        || projection_mode == PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD)
    {
Zhao Zhili's avatar
Zhao Zhili committed
272
        getProjectionMatrix(vgl->f_sar, vgl->f_fovy, prgm->var.ProjectionMatrix);
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
        getYRotMatrix(vgl->f_teta, prgm->var.YRotMatrix);
        getXRotMatrix(vgl->f_phi, prgm->var.XRotMatrix);
        getZRotMatrix(vgl->f_roll, prgm->var.ZRotMatrix);
        getZoomMatrix(vgl->f_z, prgm->var.ZoomMatrix);
    }
    else
    {
        memcpy(prgm->var.ProjectionMatrix, identity, sizeof(identity));
        memcpy(prgm->var.ZRotMatrix, identity, sizeof(identity));
        memcpy(prgm->var.YRotMatrix, identity, sizeof(identity));
        memcpy(prgm->var.XRotMatrix, identity, sizeof(identity));
        memcpy(prgm->var.ZoomMatrix, identity, sizeof(identity));
    }
}

static void getOrientationTransformMatrix(video_orientation_t orientation,
                                          GLfloat matrix[static 16])
Thomas Guillem's avatar
Thomas Guillem committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
{
    memcpy(matrix, identity, sizeof(identity));

    const int k_cos_pi = -1;
    const int k_cos_pi_2 = 0;
    const int k_cos_n_pi_2 = 0;

    const int k_sin_pi = 0;
    const int k_sin_pi_2 = 1;
    const int k_sin_n_pi_2 = -1;

    switch (orientation) {

        case ORIENT_ROTATED_90:
            matrix[0 * 4 + 0] = k_cos_pi_2;
            matrix[0 * 4 + 1] = -k_sin_pi_2;
            matrix[1 * 4 + 0] = k_sin_pi_2;
            matrix[1 * 4 + 1] = k_cos_pi_2;
            matrix[3 * 4 + 1] = 1;
            break;
        case ORIENT_ROTATED_180:
            matrix[0 * 4 + 0] = k_cos_pi;
            matrix[0 * 4 + 1] = -k_sin_pi;
            matrix[1 * 4 + 0] = k_sin_pi;
            matrix[1 * 4 + 1] = k_cos_pi;
            matrix[3 * 4 + 0] = 1;
            matrix[3 * 4 + 1] = 1;
            break;
        case ORIENT_ROTATED_270:
            matrix[0 * 4 + 0] = k_cos_n_pi_2;
            matrix[0 * 4 + 1] = -k_sin_n_pi_2;
            matrix[1 * 4 + 0] = k_sin_n_pi_2;
            matrix[1 * 4 + 1] = k_cos_n_pi_2;
            matrix[3 * 4 + 0] = 1;
            break;
        case ORIENT_HFLIPPED:
            matrix[0 * 4 + 0] = -1;
            matrix[3 * 4 + 0] = 1;
            break;
        case ORIENT_VFLIPPED:
            matrix[1 * 4 + 1] = -1;
            matrix[3 * 4 + 1] = 1;
            break;
        case ORIENT_TRANSPOSED:
            matrix[0 * 4 + 0] = 0;
            matrix[1 * 4 + 1] = 0;
            matrix[2 * 4 + 2] = -1;
            matrix[0 * 4 + 1] = 1;
            matrix[1 * 4 + 0] = 1;
            break;
        case ORIENT_ANTI_TRANSPOSED:
            matrix[0 * 4 + 0] = 0;
            matrix[1 * 4 + 1] = 0;
            matrix[2 * 4 + 2] = -1;
            matrix[0 * 4 + 1] = -1;
            matrix[1 * 4 + 0] = -1;
            matrix[3 * 4 + 0] = 1;
            matrix[3 * 4 + 1] = 1;
            break;
        default:
            break;
    }
}

354
static inline GLsizei GetAlignedSize(unsigned size)
355
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
356 357 358
    /* Return the smallest larger or equal power of 2 */
    unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
    return ((align >> 1) == size) ? size : align;
359 360
}

361 362
static GLuint BuildVertexShader(const opengl_tex_converter_t *tc,
                                unsigned plane_count)
363 364
{
    /* Basic vertex shader */
365
    static const char *template =
366
        "#version %u\n"
367 368
        "varying vec2 TexCoord0;\n"
        "attribute vec4 MultiTexCoord0;\n"
369
        "%s%s"
370 371 372 373 374 375 376 377 378
        "attribute vec3 VertexPosition;\n"
        "uniform mat4 OrientationMatrix;\n"
        "uniform mat4 ProjectionMatrix;\n"
        "uniform mat4 XRotMatrix;\n"
        "uniform mat4 YRotMatrix;\n"
        "uniform mat4 ZRotMatrix;\n"
        "uniform mat4 ZoomMatrix;\n"
        "void main() {\n"
        " TexCoord0 = vec4(OrientationMatrix * MultiTexCoord0).st;\n"
379
        "%s%s"
380
        " gl_Position = ProjectionMatrix * ZoomMatrix * ZRotMatrix * XRotMatrix * YRotMatrix * vec4(VertexPosition, 1.0);\n"
381 382
        "}";

383
    const char *coord1_header = plane_count > 1 ?
384
        "varying vec2 TexCoord1;\nattribute vec4 MultiTexCoord1;\n" : "";
385
    const char *coord1_code = plane_count > 1 ?
386
        " TexCoord1 = vec4(OrientationMatrix * MultiTexCoord1).st;\n" : "";
387
    const char *coord2_header = plane_count > 2 ?
388
        "varying vec2 TexCoord2;\nattribute vec4 MultiTexCoord2;\n" : "";
389
    const char *coord2_code = plane_count > 2 ?
390
        " TexCoord2 = vec4(OrientationMatrix * MultiTexCoord2).st;\n" : "";
391 392

    char *code;
393
    if (asprintf(&code, template, tc->glsl_version, coord1_header, coord2_header,
394 395 396
                 coord1_code, coord2_code) < 0)
        return 0;

397 398
    GLuint shader = tc->vt->CreateShader(GL_VERTEX_SHADER);
    tc->vt->ShaderSource(shader, 1, (const char **) &code, NULL);
399
    if (tc->b_dump_shaders)
400
        msg_Dbg(tc->gl, "\n=== Vertex shader for fourcc: %4.4s ===\n%s\n",
401
                (const char *)&tc->fmt.i_chroma, code);
402
    tc->vt->CompileShader(shader);
403 404
    free(code);
    return shader;
405 406
}

407 408 409 410 411
static int
GenTextures(const opengl_tex_converter_t *tc,
            const GLsizei *tex_width, const GLsizei *tex_height,
            GLuint *textures)
{
412
    tc->vt->GenTextures(tc->tex_count, textures);
413

414
    for (unsigned i = 0; i < tc->tex_count; i++)
415
    {
416
        tc->vt->BindTexture(tc->tex_target, textures[i]);
417 418 419

#if !defined(USE_OPENGL_ES2)
        /* Set the texture parameters */
420 421
        tc->vt->TexParameterf(tc->tex_target, GL_TEXTURE_PRIORITY, 1.0);
        tc->vt->TexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
422 423
#endif

424 425 426 427
        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        tc->vt->TexParameteri(tc->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
428 429 430 431 432 433
    }

    if (tc->pf_allocate_textures != NULL)
    {
        int ret = tc->pf_allocate_textures(tc, textures, tex_width, tex_height);
        if (ret != VLC_SUCCESS)
434
        {
435
            tc->vt->DeleteTextures(tc->tex_count, textures);
436 437
            memset(textures, 0, tc->tex_count * sizeof(GLuint));
            return ret;
438 439 440 441 442 443 444 445
        }
    }
    return VLC_SUCCESS;
}

static void
DelTextures(const opengl_tex_converter_t *tc, GLuint *textures)
{
446
    tc->vt->DeleteTextures(tc->tex_count, textures);
447
    memset(textures, 0, tc->tex_count * sizeof(GLuint));
448 449
}

450
static int
451
opengl_link_program(struct prgm *prgm)
452
{
453
    opengl_tex_converter_t *tc = prgm->tc;
454 455

    GLuint vertex_shader = BuildVertexShader(tc, tc->tex_count);
456
    GLuint shaders[] = { tc->fshader, vertex_shader };
457 458 459 460

    /* Check shaders messages */
    for (unsigned i = 0; i < 2; i++) {
        int infoLength;
461
        tc->vt->GetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLength);
462 463 464 465 466 467 468
        if (infoLength <= 1)
            continue;

        char *infolog = malloc(infoLength);
        if (infolog != NULL)
        {
            int charsWritten;
469
            tc->vt->GetShaderInfoLog(shaders[i], infoLength, &charsWritten,
470
                                      infolog);
471
            msg_Err(tc->gl, "shader %u: %s", i, infolog);
472 473 474 475
            free(infolog);
        }
    }

476 477 478 479
    prgm->id = tc->vt->CreateProgram();
    tc->vt->AttachShader(prgm->id, tc->fshader);
    tc->vt->AttachShader(prgm->id, vertex_shader);
    tc->vt->LinkProgram(prgm->id);
480

481 482
    tc->vt->DeleteShader(vertex_shader);
    tc->vt->DeleteShader(tc->fshader);
483 484 485

    /* Check program messages */
    int infoLength = 0;
486
    tc->vt->GetProgramiv(prgm->id, GL_INFO_LOG_LENGTH, &infoLength);
487 488 489 490 491 492
    if (infoLength > 1)
    {
        char *infolog = malloc(infoLength);
        if (infolog != NULL)
        {
            int charsWritten;
493
            tc->vt->GetProgramInfoLog(prgm->id, infoLength, &charsWritten,
494
                                       infolog);
495
            msg_Err(tc->gl, "shader program: %s", infolog);
496 497 498 499 500
            free(infolog);
        }

        /* If there is some message, better to check linking is ok */
        GLint link_status = GL_TRUE;
501
        tc->vt->GetProgramiv(prgm->id, GL_LINK_STATUS, &link_status);
502 503
        if (link_status == GL_FALSE)
        {
504
            msg_Err(tc->gl, "Unable to use program");
505 506 507 508 509 510
            goto error;
        }
    }

    /* Fetch UniformLocations and AttribLocations */
#define GET_LOC(type, x, str) do { \
511
    x = tc->vt->Get##type##Location(prgm->id, str); \
512 513
    assert(x != -1); \
    if (x == -1) { \
514
        msg_Err(tc->gl, "Unable to Get"#type"Location(%s)", str); \
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
        goto error; \
    } \
} while (0)
#define GET_ULOC(x, str) GET_LOC(Uniform, prgm->uloc.x, str)
#define GET_ALOC(x, str) GET_LOC(Attrib, prgm->aloc.x, str)
    GET_ULOC(OrientationMatrix, "OrientationMatrix");
    GET_ULOC(ProjectionMatrix, "ProjectionMatrix");
    GET_ULOC(ZRotMatrix, "ZRotMatrix");
    GET_ULOC(YRotMatrix, "YRotMatrix");
    GET_ULOC(XRotMatrix, "XRotMatrix");
    GET_ULOC(ZoomMatrix, "ZoomMatrix");

    GET_ALOC(VertexPosition, "VertexPosition");
    GET_ALOC(MultiTexCoord[0], "MultiTexCoord0");
    /* MultiTexCoord 1 and 2 can be optimized out if not used */
530
    if (prgm->tc->tex_count > 1)
531 532 533
        GET_ALOC(MultiTexCoord[1], "MultiTexCoord1");
    else
        prgm->aloc.MultiTexCoord[1] = -1;
534
    if (prgm->tc->tex_count > 2)
535 536 537 538 539 540
        GET_ALOC(MultiTexCoord[2], "MultiTexCoord2");
    else
        prgm->aloc.MultiTexCoord[2] = -1;
#undef GET_LOC
#undef GET_ULOC
#undef GET_ALOC
541
    int ret = prgm->tc->pf_fetch_locations(prgm->tc, prgm->id);
542 543 544
    assert(ret == VLC_SUCCESS);
    if (ret != VLC_SUCCESS)
    {
545
        msg_Err(tc->gl, "Unable to get locations from tex_conv");
546 547 548 549 550 551
        goto error;
    }

    return VLC_SUCCESS;

error:
552
    tc->vt->DeleteProgram(prgm->id);
553 554 555 556
    prgm->id = 0;
    return VLC_EGENERIC;
}

557 558 559
static void
opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm)
{
560 561 562 563 564
    opengl_tex_converter_t *tc = prgm->tc;
    if (tc->p_module != NULL)
        module_unneed(tc, tc->p_module);
    else if (tc->priv != NULL)
        opengl_tex_converter_generic_deinit(tc);
565
    if (prgm->id != 0)
566
        vgl->vt.DeleteProgram(prgm->id);
567 568 569

#ifdef HAVE_LIBPLACEBO
    FREENULL(tc->uloc.pl_vars);
570 571
    if (tc->pl_ctx)
        pl_context_destroy(&tc->pl_ctx);
572
#endif
573 574

    vlc_object_release(tc);
575 576 577 578 579 580 581 582 583 584 585 586 587 588
}

#ifdef HAVE_LIBPLACEBO
static void
log_cb(void *priv, enum pl_log_level level, const char *msg)
{
    opengl_tex_converter_t *tc = priv;
    switch (level) {
    case PL_LOG_FATAL: // fall through
    case PL_LOG_ERR:  msg_Err(tc->gl, "%s", msg); break;
    case PL_LOG_WARN: msg_Warn(tc->gl,"%s", msg); break;
    case PL_LOG_INFO: msg_Info(tc->gl,"%s", msg); break;
    default: break;
    }
589
}
590
#endif
591 592 593

static int
opengl_init_program(vout_display_opengl_t *vgl, struct prgm *prgm,
594 595
                    const char *glexts, const video_format_t *fmt, bool subpics,
                    bool b_dump_shaders)
596
{
597 598 599 600 601 602 603
    opengl_tex_converter_t *tc =
        vlc_object_create(vgl->gl, sizeof(opengl_tex_converter_t));
    if (tc == NULL)
        return VLC_ENOMEM;

    tc->gl = vgl->gl;
    tc->vt = &vgl->vt;
604
    tc->b_dump_shaders = b_dump_shaders;
605 606
    tc->pf_fragment_shader_init = opengl_fragment_shader_init_impl;
    tc->glexts = glexts;
607
#if defined(USE_OPENGL_ES2)
608 609 610
    tc->is_gles = true;
    tc->glsl_version = 100;
    tc->glsl_precision_header = "precision highp float;\n";
611
#else
612 613 614
    tc->is_gles = false;
    tc->glsl_version = 120;
    tc->glsl_precision_header = "";
615
#endif
616
    tc->fmt = *fmt;
617

618 619
#ifdef HAVE_LIBPLACEBO
    // create the main libplacebo context
620 621 622 623 624 625 626 627
    if (!subpics)
    {
        tc->pl_ctx = pl_context_create(PL_API_VER, &(struct pl_context_params) {
            .log_cb    = log_cb,
            .log_priv  = tc,
            .log_level = PL_LOG_INFO,
        });
        if (tc->pl_ctx)
628
            tc->pl_sh = pl_shader_alloc(tc->pl_ctx, NULL, 0, 0);
629
    }
630 631
#endif

632
    int ret;
633 634
    if (subpics)
    {
635
        tc->fmt.i_chroma = VLC_CODEC_RGB32;
636
        /* Normal orientation and no projection for subtitles */
637 638
        tc->fmt.orientation = ORIENT_NORMAL;
        tc->fmt.projection_mode = PROJECTION_MODE_RECTANGULAR;
639 640 641
        tc->fmt.primaries = COLOR_PRIMARIES_UNDEF;
        tc->fmt.transfer = TRANSFER_FUNC_UNDEF;
        tc->fmt.space = COLOR_SPACE_UNDEF;
642

643
        ret = opengl_tex_converter_generic_init(tc, false);
644 645 646
    }
    else
    {
647 648 649 650
        const vlc_chroma_description_t *desc =
            vlc_fourcc_GetChromaDescription(fmt->i_chroma);

        if (desc == NULL)
Zhao Zhili's avatar
Zhao Zhili committed
651 652
        {
            vlc_object_release(tc);
653
            return VLC_EGENERIC;
Zhao Zhili's avatar
Zhao Zhili committed
654
        }
655 656 657
        if (desc->plane_count == 0)
        {
            /* Opaque chroma: load a module to handle it */
658
            tc->p_module = module_need_var(tc, "glconv", "glconv");
659 660 661 662 663
        }

        if (tc->p_module != NULL)
            ret = VLC_SUCCESS;
        else
664
        {
665 666 667
            /* Software chroma or gl hw converter failed: use a generic
             * converter */
            ret = opengl_tex_converter_generic_init(tc, true);
668 669 670 671
        }
    }

    if (ret != VLC_SUCCESS)
672 673 674 675 676 677 678 679
    {
        vlc_object_release(tc);
        return VLC_EGENERIC;
    }

    assert(tc->fshader != 0 && tc->tex_target != 0 && tc->tex_count > 0 &&
           tc->pf_update != NULL && tc->pf_fetch_locations != NULL &&
           tc->pf_prepare_shader != NULL);
680

681
    prgm->tc = tc;
682 683 684 685

    ret = opengl_link_program(prgm);
    if (ret != VLC_SUCCESS)
    {
686
        opengl_deinit_program(vgl, prgm);
687 688 689
        return VLC_EGENERIC;
    }

690
    getOrientationTransformMatrix(tc->fmt.orientation,
691
                                  prgm->var.OrientationMatrix);
692
    getViewpointMatrixes(vgl, tc->fmt.projection_mode, prgm);
693 694 695 696

    return VLC_SUCCESS;
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
static void
ResizeFormatToGLMaxTexSize(video_format_t *fmt, unsigned int max_tex_size)
{
    if (fmt->i_width > fmt->i_height)
    {
        unsigned int const  vis_w = fmt->i_visible_width;
        unsigned int const  vis_h = fmt->i_visible_height;
        unsigned int const  nw_w = max_tex_size;
        unsigned int const  nw_vis_w = nw_w * vis_w / fmt->i_width;

        fmt->i_height = nw_w * fmt->i_height / fmt->i_width;
        fmt->i_width = nw_w;
        fmt->i_visible_height = nw_vis_w * vis_h / vis_w;
        fmt->i_visible_width = nw_vis_w;
    }
    else
    {
        unsigned int const  vis_w = fmt->i_visible_width;
        unsigned int const  vis_h = fmt->i_visible_height;
        unsigned int const  nw_h = max_tex_size;
        unsigned int const  nw_vis_h = nw_h * vis_h / fmt->i_height;

        fmt->i_width = nw_h * fmt->i_width / fmt->i_height;
        fmt->i_height = nw_h;
        fmt->i_visible_width = nw_vis_h * vis_w / vis_h;
        fmt->i_visible_height = nw_vis_h;
    }
}

726
vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
727
                                               const vlc_fourcc_t **subpicture_chromas,
728 729
                                               vlc_gl_t *gl,
                                               const vlc_viewpoint_t *viewpoint)
730
{
731
    if (gl->getProcAddress == NULL) {
732
        msg_Err(gl, "getProcAddress not implemented, bailing out");
733
        return NULL;
734
    }
735

736 737
    vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
    if (!vgl)
738
        return NULL;
739 740

    vgl->gl = gl;
741

742
#if defined(USE_OPENGL_ES2) || defined(HAVE_GL_CORE_SYMBOLS)
743
#define GET_PROC_ADDR_CORE(name) vgl->vt.name = gl##name
744 745 746
#else
#define GET_PROC_ADDR_CORE(name) GET_PROC_ADDR_EXT(name, true)
#endif
Thomas Guillem's avatar
Thomas Guillem committed
747
#define GET_PROC_ADDR_EXT(name, critical) do { \
748 749
    vgl->vt.name = vlc_gl_GetProcAddress(gl, "gl"#name); \
    if (vgl->vt.name == NULL && critical) { \
750
        msg_Err(gl, "gl"#name" symbol not found, bailing out"); \
751 752 753 754
        free(vgl); \
        return NULL; \
    } \
} while(0)
Thomas Guillem's avatar
Thomas Guillem committed
755
#if defined(USE_OPENGL_ES2)
756
#define GET_PROC_ADDR(name) GET_PROC_ADDR_CORE(name)
757
#define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_EXT(name, false) /* optional for GLES */
Thomas Guillem's avatar
Thomas Guillem committed
758 759
#else
#define GET_PROC_ADDR(name) GET_PROC_ADDR_EXT(name, true)
760
#define GET_PROC_ADDR_CORE_GL(name) GET_PROC_ADDR_CORE(name)
761
#endif
Thomas Guillem's avatar
Thomas Guillem committed
762
#define GET_PROC_ADDR_OPTIONAL(name) GET_PROC_ADDR_EXT(name, false) /* GL 3 or more */
763

764
    GET_PROC_ADDR_CORE(BindTexture);
765 766 767
    GET_PROC_ADDR_CORE(BlendFunc);
    GET_PROC_ADDR_CORE(Clear);
    GET_PROC_ADDR_CORE(ClearColor);
768
    GET_PROC_ADDR_CORE(DeleteTextures);
769 770 771 772 773 774 775
    GET_PROC_ADDR_CORE(DepthMask);
    GET_PROC_ADDR_CORE(Disable);
    GET_PROC_ADDR_CORE(DrawArrays);
    GET_PROC_ADDR_CORE(DrawElements);
    GET_PROC_ADDR_CORE(Enable);
    GET_PROC_ADDR_CORE(Finish);
    GET_PROC_ADDR_CORE(Flush);
776
    GET_PROC_ADDR_CORE(GenTextures);
777 778
    GET_PROC_ADDR_CORE(GetError);
    GET_PROC_ADDR_CORE(GetIntegerv);
779
    GET_PROC_ADDR_CORE(GetString);
780 781
    GET_PROC_ADDR_CORE(PixelStorei);
    GET_PROC_ADDR_CORE(TexImage2D);
782 783
    GET_PROC_ADDR_CORE(TexParameterf);
    GET_PROC_ADDR_CORE(TexParameteri);
784
    GET_PROC_ADDR_CORE(TexSubImage2D);
785
    GET_PROC_ADDR_CORE(Viewport);
786 787

    GET_PROC_ADDR_CORE_GL(GetTexLevelParameteriv);
788
    GET_PROC_ADDR_CORE_GL(TexEnvf);
789

Thomas Guillem's avatar
Thomas Guillem committed
790 791 792 793
    GET_PROC_ADDR(CreateShader);
    GET_PROC_ADDR(ShaderSource);
    GET_PROC_ADDR(CompileShader);
    GET_PROC_ADDR(AttachShader);
794
    GET_PROC_ADDR(DeleteShader);
Thomas Guillem's avatar
Thomas Guillem committed
795 796 797 798 799 800 801 802 803 804 805

    GET_PROC_ADDR(GetProgramiv);
    GET_PROC_ADDR(GetShaderiv);
    GET_PROC_ADDR(GetProgramInfoLog);
    GET_PROC_ADDR(GetShaderInfoLog);

    GET_PROC_ADDR(GetUniformLocation);
    GET_PROC_ADDR(GetAttribLocation);
    GET_PROC_ADDR(VertexAttribPointer);
    GET_PROC_ADDR(EnableVertexAttribArray);
    GET_PROC_ADDR(UniformMatrix4fv);
806 807
    GET_PROC_ADDR(UniformMatrix3fv);
    GET_PROC_ADDR(UniformMatrix2fv);
Thomas Guillem's avatar
Thomas Guillem committed
808 809
    GET_PROC_ADDR(Uniform4fv);
    GET_PROC_ADDR(Uniform4f);
810
    GET_PROC_ADDR(Uniform3f);
Thomas Guillem's avatar
Thomas Guillem committed
811
    GET_PROC_ADDR(Uniform2f);
812
    GET_PROC_ADDR(Uniform1f);
Thomas Guillem's avatar
Thomas Guillem committed
813 814 815 816 817 818 819
    GET_PROC_ADDR(Uniform1i);

    GET_PROC_ADDR(CreateProgram);
    GET_PROC_ADDR(LinkProgram);
    GET_PROC_ADDR(UseProgram);
    GET_PROC_ADDR(DeleteProgram);

820 821
    GET_PROC_ADDR(ActiveTexture);

Thomas Guillem's avatar
Thomas Guillem committed
822 823 824 825
    GET_PROC_ADDR(GenBuffers);
    GET_PROC_ADDR(BindBuffer);
    GET_PROC_ADDR(BufferData);
    GET_PROC_ADDR(DeleteBuffers);
826

827 828
    GET_PROC_ADDR_OPTIONAL(GetFramebufferAttachmentParameteriv);

Thomas Guillem's avatar
Thomas Guillem committed
829 830 831 832 833 834 835 836
    GET_PROC_ADDR_OPTIONAL(BufferSubData);
    GET_PROC_ADDR_OPTIONAL(BufferStorage);
    GET_PROC_ADDR_OPTIONAL(MapBufferRange);
    GET_PROC_ADDR_OPTIONAL(FlushMappedBufferRange);
    GET_PROC_ADDR_OPTIONAL(UnmapBuffer);
    GET_PROC_ADDR_OPTIONAL(FenceSync);
    GET_PROC_ADDR_OPTIONAL(DeleteSync);
    GET_PROC_ADDR_OPTIONAL(ClientWaitSync);
837
#undef GET_PROC_ADDR
838

839 840 841
    GL_ASSERT_NOERROR();

    const char *extensions = (const char *)vgl->vt.GetString(GL_EXTENSIONS);
Thomas Guillem's avatar
Thomas Guillem committed
842 843 844
    assert(extensions);
    if (!extensions)
    {
845
        msg_Err(gl, "glGetString returned NULL");
Thomas Guillem's avatar
Thomas Guillem committed
846 847 848
        free(vgl);
        return NULL;
    }
849 850 851 852 853
#if !defined(USE_OPENGL_ES2)
    const unsigned char *ogl_version = vgl->vt.GetString(GL_VERSION);
    bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
    if (!supports_shaders)
    {
854
        msg_Err(gl, "shaders not supported, bailing out");
855 856 857 858 859
        free(vgl);
        return NULL;
    }
#endif

860 861 862
    /* Resize the format if it is greater than the maximum texture size
     * supported by the hardware */
    GLint       max_tex_size;
863
    vgl->vt.GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
864 865 866 867 868

    if ((GLint)fmt->i_width > max_tex_size ||
        (GLint)fmt->i_height > max_tex_size)
        ResizeFormatToGLMaxTexSize(fmt, max_tex_size);

869
#if defined(USE_OPENGL_ES2)
870 871
    /* OpenGL ES 2 includes support for non-power of 2 textures by specification
     * so checks for extensions are bound to fail. Check for OpenGL ES version instead. */
872
    vgl->supports_npot = true;
873 874 875
#else
    vgl->supports_npot = HasExtension(extensions, "GL_ARB_texture_non_power_of_two") ||
                         HasExtension(extensions, "GL_APPLE_texture_2D_limited_npot");
876 877
#endif

878 879
    bool b_dump_shaders = var_InheritInteger(gl, "verbose") >= 4;

880 881 882
    vgl->prgm = &vgl->prgms[0];
    vgl->sub_prgm = &vgl->prgms[1];

Thomas Guillem's avatar
Thomas Guillem committed
883
    GL_ASSERT_NOERROR();
884
    int ret;
885 886
    ret = opengl_init_program(vgl, vgl->prgm, extensions, fmt, false,
                              b_dump_shaders);
887
    if (ret != VLC_SUCCESS)
888
    {
889 890
        msg_Warn(gl, "could not init tex converter for %4.4s",
                 (const char *) &fmt->i_chroma);
891 892
        free(vgl);
        return NULL;
893
    }
894

Thomas Guillem's avatar
Thomas Guillem committed
895
    GL_ASSERT_NOERROR();
896 897
    ret = opengl_init_program(vgl, vgl->sub_prgm, extensions, fmt, true,
                              b_dump_shaders);
898
    if (ret != VLC_SUCCESS)
899
    {
900
        msg_Warn(gl, "could not init subpictures tex converter for %4.4s",
901
                 (const char *) &fmt->i_chroma);
902
        opengl_deinit_program(vgl, vgl->prgm);
903 904 905
        free(vgl);
        return NULL;
    }
Thomas Guillem's avatar
Thomas Guillem committed
906
    GL_ASSERT_NOERROR();
907
    /* Update the fmt to main program one */
908
    vgl->fmt = vgl->prgm->tc->fmt;
909
    /* The orientation is handled by the orientation matrix */
Thomas Guillem's avatar
Thomas Guillem committed
910
    vgl->fmt.orientation = fmt->orientation;
911

912
    /* Texture size */
913
    const opengl_tex_converter_t *tc = vgl->prgm->tc;
914 915 916 917 918
    for (unsigned j = 0; j < tc->tex_count; j++) {
        const GLsizei w = vgl->fmt.i_visible_width  * tc->texs[j].w.num
                        / tc->texs[j].w.den;
        const GLsizei h = vgl->fmt.i_visible_height * tc->texs[j].h.num
                        / tc->texs[j].h.den;
919 920 921 922 923 924 925 926 927
        if (vgl->supports_npot) {
            vgl->tex_width[j]  = w;
            vgl->tex_height[j] = h;
        } else {
            vgl->tex_width[j]  = GetAlignedSize(w);
            vgl->tex_height[j] = GetAlignedSize(h);
        }
    }

928
    /* Allocates our textures */
929
    assert(!vgl->sub_prgm->tc->handle_texs_gen);
930

931
    if (!vgl->prgm->tc->handle_texs_gen)
932
    {
933
        ret = GenTextures(vgl->prgm->tc, vgl->tex_width, vgl->tex_height,
934 935 936 937 938 939
                          vgl->texture);
        if (ret != VLC_SUCCESS)
        {
            vout_display_opengl_Delete(vgl);
            return NULL;
        }
940 941
    }

942
    /* */
943 944 945 946 947 948
    vgl->vt.Disable(GL_BLEND);
    vgl->vt.Disable(GL_DEPTH_TEST);
    vgl->vt.DepthMask(GL_FALSE);
    vgl->vt.Enable(GL_CULL_FACE);
    vgl->vt.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    vgl->vt.Clear(GL_COLOR_BUFFER_BIT);
949

950 951
    vgl->vt.GenBuffers(1, &vgl->vertex_buffer_object);
    vgl->vt.GenBuffers(1, &vgl->index_buffer_object);
952
    vgl->vt.GenBuffers(vgl->prgm->tc->tex_count, vgl->texture_buffer_object);
953 954 955

    /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
    int subpicture_buffer_object_count = 8;
956
    vgl->subpicture_buffer_object = vlc_alloc(subpicture_buffer_object_count, sizeof(GLuint));
957 958 959 960 961
    if (!vgl->subpicture_buffer_object) {
        vout_display_opengl_Delete(vgl);
        return NULL;
    }
    vgl->subpicture_buffer_object_count = subpicture_buffer_object_count;
962
    vgl->vt.GenBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
963

964
    /* */
965 966
    vgl->region_count = 0;
    vgl->region = NULL;
967 968
    vgl->pool = NULL;

969 970 971 972 973 974 975
    if (vgl->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
     && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
    {
        vout_display_opengl_Delete(vgl);
        return NULL;
    }

976
    *fmt = vgl->fmt;
977
    if (subpicture_chromas) {
978
        *subpicture_chromas = gl_subpicture_chromas;
979
    }
Thomas Guillem's avatar
Thomas Guillem committed
980 981

    GL_ASSERT_NOERROR();
982
    return vgl;
983 984
}

985
void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
986
{
Thomas Guillem's avatar
Thomas Guillem committed
987 988
    GL_ASSERT_NOERROR();

989
    /* */
990 991
    vgl->vt.Finish();
    vgl->vt.Flush();
992

993 994
    const size_t main_tex_count = vgl->prgm->tc->tex_count;
    const bool main_del_texs = !vgl->prgm->tc->handle_texs_gen;
995

996 997 998 999
    if (vgl->pool)
        picture_pool_Release(vgl->pool);
    opengl_deinit_program(vgl, vgl->prgm);
    opengl_deinit_program(vgl, vgl->sub_prgm);
1000

1001 1002
    vgl->vt.DeleteBuffers(1, &vgl->vertex_buffer_object);
    vgl->vt.DeleteBuffers(1, &vgl->index_buffer_object);
1003
    vgl->vt.DeleteBuffers(main_tex_count, vgl->texture_buffer_object);
1004

1005
    if (vgl->subpicture_buffer_object_count > 0)
1006 1007
        vgl->vt.DeleteBuffers(vgl->subpicture_buffer_object_count,
                              vgl->subpicture_buffer_object);
1008 1009
    free(vgl->subpicture_buffer_object);

1010 1011
    if (main_del_texs)
        vgl->vt.DeleteTextures(main_tex_count, vgl->texture);
1012

1013 1014 1015 1016 1017 1018
    for (int i = 0; i < vgl->region_count; i++)
    {
        if (vgl->region[i].texture)
            vgl->vt.DeleteTextures(1, &vgl->region[i].texture);
    }
    free(vgl->region);
Thomas Guillem's avatar
Thomas Guillem committed
1019
    GL_ASSERT_NOERROR();
1020 1021

    free(vgl);
1022 1023
}

1024
static void UpdateZ(vout_display_opengl_t *vgl)
1025
{
1026
    /* Do trigonometry to calculate the minimal z value
1027 1028
     * that will allow us to zoom out without seeing the outside of the
     * sphere (black borders). */
1029 1030
    float tan_fovx_2 = tanf(vgl->f_fovx / 2);
    float tan_fovy_2 = tanf(vgl->f_fovy / 2);
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
    float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
                    tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));

    /* The FOV value above which z is dynamically calculated. */
    const float z_thresh = 90.f;

    if (vgl->f_fovx <= z_thresh * M_PI / 180)
        vgl->f_z = 0;
    else
    {
        float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
        vgl->f_z = f * vgl->f_fovx - f * z_thresh * M_PI / 180;
        if (vgl->f_z < z_min)
            vgl->f_z = z_min;
    }
}

static void UpdateFOVy(vout_display_opengl_t *vgl)
{
    vgl->f_fovy = 2 * atanf(tanf(vgl->f_fovx / 2) / vgl->f_sar);
1051 1052
}

1053 1054 1055
int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
                                     const vlc_viewpoint_t *p_vp)
{
1056 1057 1058 1059
    if (p_vp->fov > FIELD_OF_VIEW_DEGREES_MAX
            || p_vp->fov < FIELD_OF_VIEW_DEGREES_MIN)
        return VLC_EBADVAR;

Steve Lhomme's avatar
Steve Lhomme committed
1060
#define RAD(d) ((float) ((d) * M_PI / 180.f))
1061
    float f_fovx = RAD(p_vp->fov);
1062

Steve Lhomme's avatar
Steve Lhomme committed
1063
    vgl->f_teta = RAD(p_vp->yaw) - (float) M_PI_2;
1064 1065
    vgl->f_phi  = RAD(p_vp->pitch);
    vgl->f_roll = RAD(p_vp->roll);
1066

1067

1068
    if (fabsf(f_fovx - vgl->f_fovx) >= 0.001f)
1069
    {
1070
        /* FOVx has changed. */
1071
        vgl->f_fovx = f_fovx;
1072 1073
        UpdateFOVy(vgl);
        UpdateZ(vgl);
1074
    }
1075
    getViewpointMatrixes(vgl, vgl->fmt.projection_mode, vgl->prgm);
1076

1077 1078 1079 1080
    return VLC_SUCCESS;
#undef RAD
}

1081 1082 1083 1084 1085 1086 1087 1088

void vout_display_opengl_SetWindowAspectRatio(vout_display_opengl_t *vgl,
                                              float f_sar)
{
    /* Each time the window size changes, we must recompute the minimum zoom
     * since the aspect ration changes.
     * We must also set the new current zoom value. */
    vgl->f_sar = f_sar;
1089 1090
    UpdateFOVy(vgl);
    UpdateZ(vgl);
1091
    getViewpointMatrixes(vgl, vgl->fmt.projection_mode, vgl->prgm);
1092 1093
}

1094 1095 1096 1097 1098 1099
void vout_display_opengl_Viewport(vout_display_opengl_t *vgl, int x, int y,
                                  unsigned width, unsigned height)
{
    vgl->vt.Viewport(x, y, width, height);
}

1100
picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
1101
{
Thomas Guillem's avatar
Thomas Guillem committed
1102 1103
    GL_ASSERT_NOERROR();

1104 1105 1106
    if (vgl->pool)
        return vgl->pool;

1107
    opengl_tex_converter_t *tc = vgl->prgm->tc;
1108 1109 1110 1111
    requested_count = __MIN(VLCGL_PICTURE_MAX, requested_count);
    /* Allocate with tex converter pool callback if it exists */
    if (tc->pf_get_pool != NULL)
    {
1112
        vgl->pool = tc->pf_get_pool(tc, requested_count);
1113 1114 1115 1116
        if (!vgl->pool)
            goto error;
        return vgl->pool;
    }
1117

1118 1119
    /* Allocate our pictures */
    picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
Laurent Aimar's avatar
Laurent Aimar committed
1120
    unsigned count;
1121
    for (count = 0; count < requested_count; count++)
1122
    {
1123 1124 1125
        picture[count] = picture_NewFromFormat(&vgl->fmt);
        if (!picture[count])
            break;
1126
    }
1127
    if (count <= 0)
1128
        goto error;
1129

1130
    /* Wrap the pictures into a pool */
1131
    vgl->pool = picture_pool_New(count, picture);
1132
    if (!vgl->pool)
1133 1134 1135
    {
        for (unsigned i = 0; i < count; i++)
            picture_Release(picture[i]);
1136
        goto error;
1137
    }
1138

Thomas Guillem's avatar
Thomas Guillem committed
1139
    GL_ASSERT_NOERROR();
1140 1141 1142
    return vgl->pool;

error:
1143
    DelTextures(tc, vgl->texture);
1144 1145 1146
    return NULL;
}

1147 1148 1149
int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
                                picture_t *picture, subpicture_t *subpicture)
{
Thomas Guillem's avatar
Thomas Guillem committed
1150 1151
    GL_ASSERT_NOERROR();

1152
    opengl_tex_converter_t *tc = vgl->prgm->tc;
1153

1154
    /* Update the texture */
1155
    int ret = tc->pf_update(tc, vgl->texture, vgl->tex_width, vgl->tex_height,
1156 1157 1158
                            picture, NULL);
    if (ret != VLC_SUCCESS)
        return ret;
1159

1160 1161 1162
    int         last_count = vgl->region_count;
    gl_region_t *last = vgl->region;

1163 1164 1165
    vgl->region_count = 0;
    vgl->region       = NULL;

1166
    tc = vgl->sub_prgm->tc;
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
    if (subpicture) {

        int count = 0;
        for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
            count++;

        vgl->region_count = count;
        vgl->region       = calloc(count, sizeof(*vgl->region));

        int i = 0;
1177 1178
        for (subpicture_region_t *r = subpicture->p_region;
             r && ret == VLC_SUCCESS; r = r->p_next, i++) {
1179 1180 1181 1182
            gl_region_t *glr = &vgl->region[i];

            glr->width  = r->fmt.i_visible_width;
            glr->height = r->fmt.i_visible_height;
Laurent Aimar's avatar
Laurent Aimar committed
1183 1184 1185
            if (!vgl->supports_npot) {
                glr->width  = GetAlignedSize(glr->width);
                glr->height = GetAlignedSize(glr->height);
1186 1187 1188 1189 1190
                glr->tex_width  = (float) r->fmt.i_visible_width  / glr->width;
                glr->tex_height = (float) r->fmt.i_visible_height / glr->height;
            } else {
                glr->tex_width  = 1.0;
                glr->tex_height = 1.0;
1191
            }
1192 1193 1194 1195 1196 1197
            glr->alpha  = (float)subpicture->i_alpha * r->i_alpha / 255 / 255;
            glr->left   =  2.0 * (r->i_x                          ) / subpicture->i_original_picture_width  - 1.0;
            glr->top    = -2.0 * (r->i_y                          ) / subpicture->i_original_picture_height + 1.0;
            glr->right  =  2.0 * (r->i_x + r->fmt.i_visible_width ) / subpicture->i_original_picture_width  - 1.0;
            glr->bottom = -2.0 * (r->i_y + r->fmt.i_visible_height) / subpicture->i_original_picture_height + 1.0;

1198
            glr->texture = 0;
1199 1200
            /* Try to recycle the textures allocated by the previous
               call to this function. */
1201
            for (int j = 0; j < last_count; j++) {
1202 1203
                if (last[j].texture &&
                    last[j].width  == glr->width &&
1204
                    last[j].height == glr->height) {
1205 1206
                    glr->texture = last[j].texture;
                    memset(&last[j], 0, sizeof(last[j]));
1207 1208 1209 1210
                    break;
                }
            }

1211 1212 1213
            const size_t pixels_offset =
                r->fmt.i_y_offset * r->p_picture->p->i_pitch +
                r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
1214 1215
            if (!glr->texture)
            {
1216
                /* Could not recycle a previous texture, generate a new one. */
1217
                ret = GenTextures(tc, &glr->width, &glr->height, &glr->texture);
1218 1219
                if (ret != VLC_SUCCESS)
                    continue;
1220
            }
1221
            ret = tc->pf_update(tc, &glr->texture, &glr->width, &glr->height,
1222
                                r->p_picture, &pixels_offset);
1223 1224
        }
    }
1225 1226
    for (int i = 0; i < last_count; i++) {
        if (last[i].texture)
1227
            DelTextures(tc, &last[i].texture);
1228 1229
    }
    free(last);
1230

1231
    VLC_UNUSED(subpicture);
Thomas Guillem's avatar
Thomas Guillem committed
1232 1233

    GL_ASSERT_NOERROR();
1234
    return ret;
1235 1236
}

1237 1238 1239
static int BuildSphere(unsigned nbPlanes,
                        GLfloat **vertexCoord, GLfloat **textureCoord, unsigned *nbVertices,
                        GLushort **indices, unsigned *nbIndices,
Steve Lhomme's avatar
Steve Lhomme committed
1240 1241
                        const float *left, const float *top,
                        const float *right, const float *bottom)
1242 1243 1244 1245 1246 1247 1248
{
    unsigned nbLatBands = 128;
    unsigned nbLonBands = 128;

    *nbVertices = (nbLatBands + 1) * (nbLonBands + 1);
    *nbIndices = nbLatBands * nbLonBands * 3 * 2;

1249
    *vertexCoord = vlc_alloc(*nbVertices * 3, sizeof(GLfloat));
1250 1251
    if (*vertexCoord == NULL)
        return VLC_ENOMEM;
1252
    *textureCoord = vlc_alloc(nbPlanes * *nbVertices * 2, sizeof(GLfloat));
1253 1254 1255 1256 1257
    if (*textureCoord == NULL)
    {
        free(*vertexCoord);
        return VLC_ENOMEM;
    }
1258
    *indices = vlc_alloc(*nbIndices, sizeof(GLushort));
1259 1260 1261 1262 1263 1264 1265 1266
    if (*indices == NULL)
    {
        free(*textureCoord);
        free(*vertexCoord);
        return VLC_ENOMEM;
    }

    for (unsigned lat = 0; lat <= nbLatBands; lat++) {
1267
        float theta = lat * (float) M_PI / nbLatBands;
1268 1269 1270
        float sinTheta, cosTheta;

        sincosf(theta, &sinTheta, &cosTheta);
1271 1272

        for (unsigned lon = 0; lon <= nbLonBands; lon++) {
1273
            float phi = lon * 2 * (float) M_PI / nbLonBands;
1274 1275 1276
            float sinPhi, cosPhi;

            sincosf(phi, &sinPhi, &cosPhi);
1277 1278 1279 1280 1281 1282

            float x = cosPhi * sinTheta;
            float y = cosTheta;
            float z = sinPhi * sinTheta;

            unsigned off1 = (lat * (nbLonBands + 1) + lon) * 3;
1283 1284 1285
            (*vertexCoord)[off1] = SPHERE_RADIUS * x;
            (*vertexCoord)[off1 + 1] = SPHERE_RADIUS * y;
            (*vertexCoord)[off1 + 2] = SPHERE_RADIUS * z;
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325

            for (unsigned p = 0; p < nbPlanes; ++p)
            {
                unsigned off2 = (p * (nbLatBands + 1) * (nbLonBands + 1)
                                + lat * (nbLonBands + 1) + lon) * 2;
                float width = right[p] - left[p];
                float height = bottom[p] - top[p];
                float u = (float)lon / nbLonBands * width;
                float v = (float)lat / nbLatBands * height;
                (*textureCoord)[off2] = u;
                (*textureCoord)[off2 + 1] = v;
            }
        }
    }

    for (unsigned lat = 0; lat < nbLatBands; lat++) {
        for (unsigned lon = 0; lon < nbLonBands; lon++) {
            unsigned first = (lat * (nbLonBands + 1)) + lon;
            unsigned second = first + nbLonBands + 1;

            unsigned off = (lat * nbLatBands + lon) * 3 * 2;

            (*indices)[off] = first;
            (*indices)[off + 1] = second;
            (*indices)[off + 2] = first + 1;

            (*indices)[off + 3] = second;
            (*indices)[off + 4] = second + 1;
            (*indices)[off + 5] =<