opengl.c 66.2 KB
Newer Older
1 2 3
/*****************************************************************************
 * opengl.c: OpenGL and OpenGL ES output common code
 *****************************************************************************
4
 * Copyright (C) 2004-2013 VLC authors and VideoLAN
Laurent Aimar's avatar
Laurent Aimar committed
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
LGPL  
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
LGPL  
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
LGPL  
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_memory.h>
40
#include <vlc_vout.h>
41 42 43 44 45 46 47

#include "opengl.h"

#ifndef GL_CLAMP_TO_EDGE
# define GL_CLAMP_TO_EDGE 0x812F
#endif

48
#if defined(USE_OPENGL_ES2) || defined(__APPLE__)
49 50 51 52 53 54
#   define PFNGLGETPROGRAMIVPROC             typeof(glGetProgramiv)*
#   define PFNGLGETPROGRAMINFOLOGPROC        typeof(glGetProgramInfoLog)*
#   define PFNGLGETSHADERIVPROC              typeof(glGetShaderiv)*
#   define PFNGLGETSHADERINFOLOGPROC         typeof(glGetShaderInfoLog)*
#   define PFNGLGETUNIFORMLOCATIONPROC       typeof(glGetUniformLocation)*
#   define PFNGLGETATTRIBLOCATIONPROC        typeof(glGetAttribLocation)*
Ilkka Ollakka's avatar
Ilkka Ollakka committed
55
#   define PFNGLVERTEXATTRIBPOINTERPROC      typeof(glVertexAttribPointer)*
56
#   define PFNGLENABLEVERTEXATTRIBARRAYPROC  typeof(glEnableVertexAttribArray)*
57
#   define PFNGLUNIFORMMATRIX4FVPROC         typeof(glUniformMatrix4fv)*
58 59 60 61 62 63 64 65 66 67 68 69
#   define PFNGLUNIFORM4FVPROC               typeof(glUniform4fv)*
#   define PFNGLUNIFORM4FPROC                typeof(glUniform4f)*
#   define PFNGLUNIFORM1IPROC                typeof(glUniform1i)*
#   define PFNGLCREATESHADERPROC             typeof(glCreateShader)*
#   define PFNGLSHADERSOURCEPROC             typeof(glShaderSource)*
#   define PFNGLCOMPILESHADERPROC            typeof(glCompileShader)*
#   define PFNGLDELETESHADERPROC             typeof(glDeleteShader)*
#   define PFNGLCREATEPROGRAMPROC            typeof(glCreateProgram)*
#   define PFNGLLINKPROGRAMPROC              typeof(glLinkProgram)*
#   define PFNGLUSEPROGRAMPROC               typeof(glUseProgram)*
#   define PFNGLDELETEPROGRAMPROC            typeof(glDeleteProgram)*
#   define PFNGLATTACHSHADERPROC             typeof(glAttachShader)*
70 71 72 73
#   define PFNGLGENBUFFERSPROC               typeof(glGenBuffers)*
#   define PFNGLBINDBUFFERPROC               typeof(glBindBuffer)*
#   define PFNGLBUFFERDATAPROC               typeof(glBufferData)*
#   define PFNGLDELETEBUFFERSPROC            typeof(glDeleteBuffers)*
74
#if defined(__APPLE__)
75
#   import <CoreFoundation/CoreFoundation.h>
76
#endif
77 78
#endif

79
#if defined(USE_OPENGL_ES2)
80
#   define GLSL_VERSION "100"
81
#   define VLCGL_TEXTURE_COUNT 1
82
#   define PRECISION "precision highp float;"
83
#   define VLCGL_PICTURE_MAX 128
84 85 86
#   define SUPPORTS_SHADERS
#   define glClientActiveTexture(x)
#else
87
#   define GLSL_VERSION "120"
88
#   define VLCGL_TEXTURE_COUNT 1
89
#   define VLCGL_PICTURE_MAX 128
90
#   define PRECISION ""
91 92
#   define SUPPORTS_SHADERS
#   define SUPPORTS_FIXED_PIPELINE
93 94
#endif

95 96 97 98 99 100 101
#ifndef GL_RED
#define GL_RED 0
#endif
#ifndef GL_R16
#define GL_R16 0
#endif

102 103
#define SPHERE_RADIUS 1.f

104 105 106 107 108 109 110 111 112 113 114 115 116
typedef struct {
    GLuint   texture;
    unsigned format;
    unsigned type;
    unsigned width;
    unsigned height;

    float    alpha;

    float    top;
    float    left;
    float    bottom;
    float    right;
117 118 119

    float    tex_width;
    float    tex_height;
120 121
} gl_region_t;

122
struct vout_display_opengl_t {
123

124 125 126
    vlc_gl_t   *gl;

    video_format_t fmt;
127
    const vlc_chroma_description_t *chroma;
128

129 130
    int        tex_target;
    int        tex_format;
131
    int        tex_internal;
132
    int        tex_type;
133

134 135 136 137
    int        tex_width[PICTURE_PLANE_MAX];
    int        tex_height[PICTURE_PLANE_MAX];

    GLuint     texture[VLCGL_TEXTURE_COUNT][PICTURE_PLANE_MAX];
138

139 140 141 142
    int         region_count;
    gl_region_t *region;


143
    picture_pool_t *pool;
144

145 146 147
    /* index 0 for normal and 1 for subtitle overlay */
    GLuint     program[2];
    GLint      shader[3]; //3. is for the common vertex shader
148
    int        local_count;
149 150
    GLfloat    local_value[16];

151
    GLuint vertex_buffer_object;
152
    GLuint index_buffer_object;
153 154
    GLuint texture_buffer_object[PICTURE_PLANE_MAX];

155 156 157
    GLuint *subpicture_buffer_object;
    int    subpicture_buffer_object_count;

158
    /* Shader variables commands*/
159
#ifdef SUPPORTS_SHADERS
160 161 162 163
    PFNGLGETUNIFORMLOCATIONPROC      GetUniformLocation;
    PFNGLGETATTRIBLOCATIONPROC       GetAttribLocation;
    PFNGLVERTEXATTRIBPOINTERPROC     VertexAttribPointer;
    PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
164

165 166 167 168
    PFNGLUNIFORMMATRIX4FVPROC   UniformMatrix4fv;
    PFNGLUNIFORM4FVPROC         Uniform4fv;
    PFNGLUNIFORM4FPROC          Uniform4f;
    PFNGLUNIFORM1IPROC          Uniform1i;
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187

    /* Shader command */
    PFNGLCREATESHADERPROC CreateShader;
    PFNGLSHADERSOURCEPROC ShaderSource;
    PFNGLCOMPILESHADERPROC CompileShader;
    PFNGLDELETESHADERPROC   DeleteShader;

    PFNGLCREATEPROGRAMPROC CreateProgram;
    PFNGLLINKPROGRAMPROC   LinkProgram;
    PFNGLUSEPROGRAMPROC    UseProgram;
    PFNGLDELETEPROGRAMPROC DeleteProgram;

    PFNGLATTACHSHADERPROC  AttachShader;

    /* Shader log commands */
    PFNGLGETPROGRAMIVPROC  GetProgramiv;
    PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
    PFNGLGETSHADERIVPROC   GetShaderiv;
    PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
188 189 190 191 192

    PFNGLGENBUFFERSPROC    GenBuffers;
    PFNGLBINDBUFFERPROC    BindBuffer;
    PFNGLBUFFERDATAPROC    BufferData;
    PFNGLDELETEBUFFERSPROC DeleteBuffers;
193
#endif
194

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
195 196 197 198 199
#if defined(_WIN32)
    PFNGLACTIVETEXTUREPROC  ActiveTexture;
    PFNGLCLIENTACTIVETEXTUREPROC  ClientActiveTexture;
#endif

200 201

    /* multitexture */
202
    bool use_multitexture;
203 204 205

    /* Non-power-of-2 texture size support */
    bool supports_npot;
206 207 208

    uint8_t *texture_temp_buf;
    int      texture_temp_buf_size;
209 210 211 212

    /* View point */
    float f_teta;
    float f_phi;
213
    float f_roll;
214 215
    float f_fovx; /* f_fovx and f_fovy are linked but we keep both */
    float f_fovy; /* to avoid recalculating them when needed.      */
216 217
    float f_z;    /* Position of the camera on the shpere radius vector */
    float f_z_min;
218
    float f_sar;
219 220
};

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
221
static inline int GetAlignedSize(unsigned size)
222
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
223 224 225
    /* Return the smallest larger or equal power of 2 */
    unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
    return ((align >> 1) == size) ? size : align;
226 227
}

228
#if !defined(USE_OPENGL_ES2)
229 230
static int GetTexFormatSize(int target, int tex_format, int tex_internal,
                            int tex_type)
231
{
232 233 234 235 236 237 238 239 240 241 242 243
    GLint tex_param_size;
    switch (tex_format)
    {
        case GL_RED:
            tex_param_size = GL_TEXTURE_RED_SIZE;
            break;
        case GL_LUMINANCE:
            tex_param_size = GL_TEXTURE_LUMINANCE_SIZE;
            break;
        default:
            return -1;
    }
244 245 246 247
    GLuint texture;

    glGenTextures(1, &texture);
    glBindTexture(target, texture);
248
    glTexImage2D(target, 0, tex_internal, 64, 64, 0, tex_format, tex_type, NULL);
249
    GLint size = 0;
250
    glGetTexLevelParameteriv(target, 0, tex_param_size, &size);
251 252

    glDeleteTextures(1, &texture);
253
    return size;
254
}
255
#endif
256

257
#ifdef SUPPORTS_SHADERS
258 259 260 261 262
static void BuildVertexShader(vout_display_opengl_t *vgl,
                              GLint *shader)
{
    /* Basic vertex shader */
    const char *vertexShader =
263
        "#version " GLSL_VERSION "\n"
264
        PRECISION
265
        "varying vec4 TexCoord0,TexCoord1, TexCoord2;"
266
        "attribute vec4 MultiTexCoord0,MultiTexCoord1,MultiTexCoord2;"
267 268
        "attribute vec3 VertexPosition;"
        "uniform mat4 OrientationMatrix;"
269 270 271
        "uniform mat4 ProjectionMatrix;"
        "uniform mat4 XRotMatrix;"
        "uniform mat4 YRotMatrix;"
272
        "uniform mat4 ZRotMatrix;"
273
        "uniform mat4 ZoomMatrix;"
274 275 276 277
        "void main() {"
        " TexCoord0 = MultiTexCoord0;"
        " TexCoord1 = MultiTexCoord1;"
        " TexCoord2 = MultiTexCoord2;"
278
        " gl_Position = ProjectionMatrix * OrientationMatrix * ZoomMatrix * ZRotMatrix * XRotMatrix * YRotMatrix * vec4(VertexPosition, 1.0);"
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
        "}";

    *shader = vgl->CreateShader(GL_VERTEX_SHADER);
    vgl->ShaderSource(*shader, 1, &vertexShader, NULL);
    vgl->CompileShader(*shader);
}

static void BuildYUVFragmentShader(vout_display_opengl_t *vgl,
                                   GLint *shader,
                                   int *local_count,
                                   GLfloat *local_value,
                                   const video_format_t *fmt,
                                   float yuv_range_correction)

{
    /* [R/G/B][Y U V O] from TV range to full range
     * XXX we could also do hue/brightness/constrast/gamma
     * by simply changing the coefficients
     */
    const float matrix_bt601_tv2full[12] = {
        1.164383561643836,  0.0000,             1.596026785714286, -0.874202217873451 ,
        1.164383561643836, -0.391762290094914, -0.812967647237771,  0.531667823499146 ,
        1.164383561643836,  2.017232142857142,  0.0000,            -1.085630789302022 ,
    };
    const float matrix_bt709_tv2full[12] = {
        1.164383561643836,  0.0000,             1.792741071428571, -0.972945075016308 ,
        1.164383561643836, -0.21324861427373,  -0.532909328559444,  0.301482665475862 ,
        1.164383561643836,  2.112401785714286,  0.0000,            -1.133402217873451 ,
    };
308 309 310 311 312 313
    const float *matrix;
    switch( fmt->space )
    {
        case COLOR_SPACE_BT601:
            matrix = matrix_bt601_tv2full;
            break;
314
        default:
315 316
            matrix = matrix_bt709_tv2full;
    };
317 318 319

    /* Basic linear YUV -> RGB conversion using bilinear interpolation */
    const char *template_glsl_yuv =
320
        "#version " GLSL_VERSION "\n"
321
        PRECISION
322 323 324
        "uniform sampler2D Texture0;"
        "uniform sampler2D Texture1;"
        "uniform sampler2D Texture2;"
Laurent Aimar's avatar
Laurent Aimar committed
325
        "uniform vec4      Coefficient[4];"
326 327 328 329
        "varying vec4      TexCoord0,TexCoord1,TexCoord2;"

        "void main(void) {"
        " vec4 x,y,z,result;"
330 331 332 333 334 335 336 337 338 339 340

        /* The texture format can be GL_RED: vec4(R,0,0,1) or GL_LUMINANCE:
         * vec4(L,L,L,1). The following transform a vec4(x, y, z, w) into a
         * vec4(x, x, x, 1) (we may want to use texture swizzling starting
         * OpenGL 3.3). */
        " float val0 = texture2D(Texture0, TexCoord0.st).x;"
        " float val1 = texture2D(Texture1, TexCoord1.st).x;"
        " float val2 = texture2D(Texture2, TexCoord2.st).x;"
        " x  = vec4(val0, val0, val0, 1);"
        " %c = vec4(val1, val1, val1, 1);"
        " %c = vec4(val2, val2, val2, 1);"
341

Laurent Aimar's avatar
Laurent Aimar committed
342 343 344
        " result = x * Coefficient[0] + Coefficient[3];"
        " result = (y * Coefficient[1]) + result;"
        " result = (z * Coefficient[2]) + result;"
345 346 347 348 349 350 351 352 353
        " gl_FragColor = result;"
        "}";
    bool swap_uv = fmt->i_chroma == VLC_CODEC_YV12 ||
                   fmt->i_chroma == VLC_CODEC_YV9;

    char *code;
    if (asprintf(&code, template_glsl_yuv,
                 swap_uv ? 'z' : 'y',
                 swap_uv ? 'y' : 'z') < 0)
354
        return;
355 356

    for (int i = 0; i < 4; i++) {
357
        float correction = i < 3 ? yuv_range_correction : 1.f;
358 359 360 361
        /* We place coefficient values for coefficient[4] in one array from matrix values.
           Notice that we fill values from top down instead of left to right.*/
        for (int j = 0; j < 4; j++)
            local_value[*local_count + i*4+j] = j < 3 ? correction * matrix[j*4+i]
362
                                                      : 0.f;
363 364 365 366 367 368 369 370 371 372 373
    }
    (*local_count) += 4;


    *shader = vgl->CreateShader(GL_FRAGMENT_SHADER);
    vgl->ShaderSource(*shader, 1, (const char **)&code, NULL);
    vgl->CompileShader(*shader);

    free(code);
}

374
#if 0
375 376 377 378 379
static void BuildRGBFragmentShader(vout_display_opengl_t *vgl,
                                   GLint *shader)
{
    // Simple shader for RGB
    const char *code =
380
        "#version " GLSL_VERSION "\n"
381
        PRECISION
382 383 384 385
        "uniform sampler2D Texture[3];"
        "varying vec4 TexCoord0,TexCoord1,TexCoord2;"
        "void main()"
        "{ "
386
        "  gl_FragColor = texture2D(Texture[0], TexCoord0.st);"
387 388 389 390 391
        "}";
    *shader = vgl->CreateShader(GL_FRAGMENT_SHADER);
    vgl->ShaderSource(*shader, 1, &code, NULL);
    vgl->CompileShader(*shader);
}
392
#endif
393 394 395 396 397 398

static void BuildRGBAFragmentShader(vout_display_opengl_t *vgl,
                                   GLint *shader)
{
    // Simple shader for RGBA
    const char *code =
399
        "#version " GLSL_VERSION "\n"
400
        PRECISION
401
        "uniform sampler2D Texture;"
Laurent Aimar's avatar
Laurent Aimar committed
402
        "uniform vec4 FillColor;"
403
        "varying vec4 TexCoord0;"
404 405
        "void main()"
        "{ "
406
        "  gl_FragColor = texture2D(Texture, TexCoord0.st) * FillColor;"
407 408 409 410 411
        "}";
    *shader = vgl->CreateShader(GL_FRAGMENT_SHADER);
    vgl->ShaderSource(*shader, 1, &code, NULL);
    vgl->CompileShader(*shader);
}
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451

static void BuildXYZFragmentShader(vout_display_opengl_t *vgl,
                                   GLint *shader)
{
    /* Shader for XYZ to RGB correction
     * 3 steps :
     *  - XYZ gamma correction
     *  - XYZ to RGB matrix conversion
     *  - reverse RGB gamma correction
     */
      const char *code =
        "#version " GLSL_VERSION "\n"
        PRECISION
        "uniform sampler2D Texture0;"
        "uniform vec4 xyz_gamma = vec4(2.6);"
        "uniform vec4 rgb_gamma = vec4(1.0/2.2);"
        // WARN: matrix Is filled column by column (not row !)
        "uniform mat4 matrix_xyz_rgb = mat4("
        "    3.240454 , -0.9692660, 0.0556434, 0.0,"
        "   -1.5371385,  1.8760108, -0.2040259, 0.0,"
        "    -0.4985314, 0.0415560, 1.0572252,  0.0,"
        "    0.0,      0.0,         0.0,        1.0 "
        " );"

        "varying vec4 TexCoord0;"
        "void main()"
        "{ "
        " vec4 v_in, v_out;"
        " v_in  = texture2D(Texture0, TexCoord0.st);"
        " v_in = pow(v_in, xyz_gamma);"
        " v_out = matrix_xyz_rgb * v_in ;"
        " v_out = pow(v_out, rgb_gamma) ;"
        " v_out = clamp(v_out, 0.0, 1.0) ;"
        " gl_FragColor = v_out;"
        "}";
    *shader = vgl->CreateShader(GL_FRAGMENT_SHADER);
    vgl->ShaderSource(*shader, 1, &code, NULL);
    vgl->CompileShader(*shader);
}

452
#endif
453

454
vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
455
                                               const vlc_fourcc_t **subpicture_chromas,
456 457
                                               vlc_gl_t *gl,
                                               const vlc_viewpoint_t *viewpoint)
458
{
459
    vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
460 461 462
    if (!vgl)
        return NULL;

463
    vgl->gl = gl;
464 465 466 467 468
    if (vlc_gl_Lock(vgl->gl)) {
        free(vgl);
        return NULL;
    }

Laurent Aimar's avatar
Laurent Aimar committed
469
    if (vgl->gl->getProcAddress == NULL) {
470
        fprintf(stderr, "getProcAddress not implemented, bailing out\n");
Laurent Aimar's avatar
Laurent Aimar committed
471
        free(vgl);
472
        return NULL;
473
    }
474

475
    const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
476
#if !defined(USE_OPENGL_ES2)
477 478
    const unsigned char *ogl_version = glGetString(GL_VERSION);
    bool supports_shaders = strverscmp((const char *)ogl_version, "2.0") >= 0;
479 480 481
    const bool oglv3 = strverscmp((const char *)ogl_version, "3.0") >= 0;
    const int yuv_plane_texformat = oglv3 ? GL_RED : GL_LUMINANCE;
    const int yuv_plane_texformat_16 = oglv3 ? GL_R16 : GL_LUMINANCE16;
482 483
#else
    bool supports_shaders = false;
484
    const int yuv_plane_texformat = GL_LUMINANCE;
485 486
#endif

487
#if defined(USE_OPENGL_ES2)
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
    vgl->CreateShader  = glCreateShader;
    vgl->ShaderSource  = glShaderSource;
    vgl->CompileShader = glCompileShader;
    vgl->AttachShader  = glAttachShader;

    vgl->GetProgramiv  = glGetProgramiv;
    vgl->GetShaderiv   = glGetShaderiv;
    vgl->GetProgramInfoLog  = glGetProgramInfoLog;
    vgl->GetShaderInfoLog   = glGetShaderInfoLog;

    vgl->DeleteShader  = glDeleteShader;

    vgl->GetUniformLocation = glGetUniformLocation;
    vgl->GetAttribLocation  = glGetAttribLocation;
    vgl->VertexAttribPointer= glVertexAttribPointer;
    vgl->EnableVertexAttribArray = glEnableVertexAttribArray;
504
    vgl->UniformMatrix4fv = glUniformMatrix4fv;
505 506 507 508 509 510 511 512
    vgl->Uniform4fv    = glUniform4fv;
    vgl->Uniform4f     = glUniform4f;
    vgl->Uniform1i     = glUniform1i;

    vgl->CreateProgram = glCreateProgram;
    vgl->LinkProgram   = glLinkProgram;
    vgl->UseProgram    = glUseProgram;
    vgl->DeleteProgram = glDeleteProgram;
513 514 515 516 517 518

    vgl->GenBuffers    = glGenBuffers;
    vgl->BindBuffer    = glBindBuffer;
    vgl->BufferData    = glBufferData;
    vgl->DeleteBuffers = glDeleteBuffers;

519 520
    supports_shaders = true;
#elif defined(SUPPORTS_SHADERS)
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
    vgl->CreateShader  = (PFNGLCREATESHADERPROC)vlc_gl_GetProcAddress(vgl->gl, "glCreateShader");
    vgl->ShaderSource  = (PFNGLSHADERSOURCEPROC)vlc_gl_GetProcAddress(vgl->gl, "glShaderSource");
    vgl->CompileShader = (PFNGLCOMPILESHADERPROC)vlc_gl_GetProcAddress(vgl->gl, "glCompileShader");
    vgl->AttachShader  = (PFNGLATTACHSHADERPROC)vlc_gl_GetProcAddress(vgl->gl, "glAttachShader");

    vgl->GetProgramiv  = (PFNGLGETPROGRAMIVPROC)vlc_gl_GetProcAddress(vgl->gl, "glGetProgramiv");
    vgl->GetShaderiv   = (PFNGLGETSHADERIVPROC)vlc_gl_GetProcAddress(vgl->gl, "glGetShaderiv");
    vgl->GetProgramInfoLog  = (PFNGLGETPROGRAMINFOLOGPROC)vlc_gl_GetProcAddress(vgl->gl, "glGetProgramInfoLog");
    vgl->GetShaderInfoLog   = (PFNGLGETSHADERINFOLOGPROC)vlc_gl_GetProcAddress(vgl->gl, "glGetShaderInfoLog");

    vgl->DeleteShader  = (PFNGLDELETESHADERPROC)vlc_gl_GetProcAddress(vgl->gl, "glDeleteShader");

    vgl->GetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)vlc_gl_GetProcAddress(vgl->gl, "glGetUniformLocation");
    vgl->GetAttribLocation  = (PFNGLGETATTRIBLOCATIONPROC)vlc_gl_GetProcAddress(vgl->gl, "glGetAttribLocation");
    vgl->VertexAttribPointer= (PFNGLVERTEXATTRIBPOINTERPROC)vlc_gl_GetProcAddress(vgl->gl, "glVertexAttribPointer");
    vgl->EnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)vlc_gl_GetProcAddress(vgl->gl, "glEnableVertexAttribArray");
537
    vgl->UniformMatrix4fv   = (PFNGLUNIFORMMATRIX4FVPROC)vlc_gl_GetProcAddress(vgl->gl,"glUniformMatrix4fv");
538 539 540 541 542 543 544 545 546
    vgl->Uniform4fv    = (PFNGLUNIFORM4FVPROC)vlc_gl_GetProcAddress(vgl->gl,"glUniform4fv");
    vgl->Uniform4f     = (PFNGLUNIFORM4FPROC)vlc_gl_GetProcAddress(vgl->gl,"glUniform4f");
    vgl->Uniform1i     = (PFNGLUNIFORM1IPROC)vlc_gl_GetProcAddress(vgl->gl,"glUniform1i");

    vgl->CreateProgram = (PFNGLCREATEPROGRAMPROC)vlc_gl_GetProcAddress(vgl->gl, "glCreateProgram");
    vgl->LinkProgram   = (PFNGLLINKPROGRAMPROC)vlc_gl_GetProcAddress(vgl->gl, "glLinkProgram");
    vgl->UseProgram    = (PFNGLUSEPROGRAMPROC)vlc_gl_GetProcAddress(vgl->gl, "glUseProgram");
    vgl->DeleteProgram = (PFNGLDELETEPROGRAMPROC)vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgram");

547 548 549 550 551
    vgl->GenBuffers    = (PFNGLGENBUFFERSPROC)vlc_gl_GetProcAddress(vgl->gl, "glGenBuffers");
    vgl->BindBuffer    = (PFNGLBINDBUFFERPROC)vlc_gl_GetProcAddress(vgl->gl, "glBindBuffer");
    vgl->BufferData    = (PFNGLBUFFERDATAPROC)vlc_gl_GetProcAddress(vgl->gl, "glBufferData");
    vgl->DeleteBuffers = (PFNGLDELETEBUFFERSPROC)vlc_gl_GetProcAddress(vgl->gl, "glDeleteBuffers");

552 553
    if (!vgl->CreateShader || !vgl->ShaderSource || !vgl->CreateProgram)
        supports_shaders = false;
554
#endif
555

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
556 557 558 559 560 561 562
#if defined(_WIN32)
    vgl->ActiveTexture = (PFNGLACTIVETEXTUREPROC)vlc_gl_GetProcAddress(vgl->gl, "glActiveTexture");
    vgl->ClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)vlc_gl_GetProcAddress(vgl->gl, "glClientActiveTexture");
#   define glActiveTexture vgl->ActiveTexture
#   define glClientActiveTexture vgl->ClientActiveTexture
#endif

563 564 565
    vgl->supports_npot = HasExtension(extensions, "GL_ARB_texture_non_power_of_two") ||
                         HasExtension(extensions, "GL_APPLE_texture_2D_limited_npot");

566
#if defined(USE_OPENGL_ES2)
567 568
    /* 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. */
569
    vgl->supports_npot = true;
570 571
#endif

572
    GLint max_texture_units = 0;
573
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
574

575
#ifdef __APPLE__
576
#if defined(USE_OPENGL_ES2)
577
    supports_shaders = true;
578
#endif
579 580
#endif

581
    /* Initialize with default chroma */
582 583
    vgl->fmt = *fmt;
    vgl->fmt.i_chroma = VLC_CODEC_RGB32;
584 585 586 587 588 589 590 591 592 593 594
#   if defined(WORDS_BIGENDIAN)
    vgl->fmt.i_rmask  = 0xff000000;
    vgl->fmt.i_gmask  = 0x00ff0000;
    vgl->fmt.i_bmask  = 0x0000ff00;
#   else
    vgl->fmt.i_rmask  = 0x000000ff;
    vgl->fmt.i_gmask  = 0x0000ff00;
    vgl->fmt.i_bmask  = 0x00ff0000;
#   endif
    vgl->tex_target   = GL_TEXTURE_2D;
    vgl->tex_format   = GL_RGBA;
595
    vgl->tex_internal = GL_RGBA;
596
    vgl->tex_type     = GL_UNSIGNED_BYTE;
597 598
    /* Use YUV if possible and needed */
    bool need_fs_yuv = false;
599
    bool need_fs_xyz = false;
600 601 602 603 604
#   if defined (USE_OPENGL_ES2)
    bool need_fs_rgba = true;
#   else
    bool need_fs_rgba = false;
#   endif
605
    bool need_vs = fmt->projection_mode != PROJECTION_MODE_RECTANGULAR;
606
    float yuv_range_correction = 1.0;
607

608
    if (max_texture_units >= 3 && supports_shaders && vlc_fourcc_IsYUV(fmt->i_chroma)) {
609 610 611 612 613 614 615
        const vlc_fourcc_t *list = vlc_fourcc_GetYUVFallback(fmt->i_chroma);
        while (*list) {
            const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(*list);
            if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 1) {
                need_fs_yuv       = true;
                vgl->fmt          = *fmt;
                vgl->fmt.i_chroma = *list;
616 617
                vgl->tex_format   = yuv_plane_texformat;
                vgl->tex_internal = yuv_plane_texformat;
618
                vgl->tex_type     = GL_UNSIGNED_BYTE;
619 620
                yuv_range_correction = 1.0;
                break;
621
#if !defined(USE_OPENGL_ES2)
622
            } else if (dsc && dsc->plane_count == 3 && dsc->pixel_size == 2 &&
623 624 625 626
                       GetTexFormatSize(vgl->tex_target,
                                        yuv_plane_texformat,
                                        yuv_plane_texformat_16,
                                        GL_UNSIGNED_SHORT) == 16) {
627 628 629
                need_fs_yuv       = true;
                vgl->fmt          = *fmt;
                vgl->fmt.i_chroma = *list;
630 631
                vgl->tex_format   = yuv_plane_texformat;
                vgl->tex_internal = yuv_plane_texformat_16;
632 633
                vgl->tex_type     = GL_UNSIGNED_SHORT;
                yuv_range_correction = (float)((1 << 16) - 1) / ((1 << dsc->pixel_bits) - 1);
634
                break;
635
#endif
636 637 638 639
            }
            list++;
        }
    }
640

641 642 643 644 645 646 647 648
    if (fmt->i_chroma == VLC_CODEC_XYZ12) {
        need_fs_xyz       = true;
        vgl->fmt          = *fmt;
        vgl->fmt.i_chroma = VLC_CODEC_XYZ12;
        vgl->tex_format   = GL_RGB;
        vgl->tex_internal = GL_RGB;
        vgl->tex_type     = GL_UNSIGNED_SHORT;
    }
649
    vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
650 651
    assert(vgl->chroma != NULL);
    vgl->use_multitexture = vgl->chroma->plane_count > 1;
652

Laurent Aimar's avatar
Laurent Aimar committed
653
    /* Texture size */
654
    for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
655 656
        int w = vgl->fmt.i_visible_width  * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
        int h = vgl->fmt.i_visible_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
Laurent Aimar's avatar
Laurent Aimar committed
657
        if (vgl->supports_npot) {
658 659 660 661 662 663
            vgl->tex_width[j]  = w;
            vgl->tex_height[j] = h;
        } else {
            vgl->tex_width[j]  = GetAlignedSize(w);
            vgl->tex_height[j] = GetAlignedSize(h);
        }
664 665
    }

666 667
    /* Build program if needed */
    vgl->program[0] =
668
    vgl->program[1] = 0;
669 670 671
    vgl->shader[0] =
    vgl->shader[1] =
    vgl->shader[2] = -1;
672
    vgl->local_count = 0;
673
    if (supports_shaders && (need_vs || need_fs_yuv || need_fs_xyz|| need_fs_rgba)) {
674
#ifdef SUPPORTS_SHADERS
675 676 677 678 679 680
        if (need_fs_xyz)
            BuildXYZFragmentShader(vgl, &vgl->shader[0]);
        else
            BuildYUVFragmentShader(vgl, &vgl->shader[0], &vgl->local_count,
                                vgl->local_value, fmt, yuv_range_correction);

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
        BuildRGBAFragmentShader(vgl, &vgl->shader[1]);
        BuildVertexShader(vgl, &vgl->shader[2]);

        /* Check shaders messages */
        for (unsigned j = 0; j < 3; j++) {
            int infoLength;
            vgl->GetShaderiv(vgl->shader[j], GL_INFO_LOG_LENGTH, &infoLength);
            if (infoLength <= 1)
                continue;

            char *infolog = malloc(infoLength);
            int charsWritten;
            vgl->GetShaderInfoLog(vgl->shader[j], infoLength, &charsWritten, infolog);
            fprintf(stderr, "shader %d: %s\n", j, infolog);
            free(infolog);
Laurent Aimar's avatar
Laurent Aimar committed
696 697
        }

698
        /* YUV/XYZ & Vertex shaders */
699 700 701 702
        vgl->program[0] = vgl->CreateProgram();
        vgl->AttachShader(vgl->program[0], vgl->shader[0]);
        vgl->AttachShader(vgl->program[0], vgl->shader[2]);
        vgl->LinkProgram(vgl->program[0]);
Laurent Aimar's avatar
Laurent Aimar committed
703

Steve Lhomme's avatar
Steve Lhomme committed
704
        /* RGB & Vertex shaders */
Laurent Aimar's avatar
Laurent Aimar committed
705 706 707 708 709
        vgl->program[1] = vgl->CreateProgram();
        vgl->AttachShader(vgl->program[1], vgl->shader[1]);
        vgl->AttachShader(vgl->program[1], vgl->shader[2]);
        vgl->LinkProgram(vgl->program[1]);

710
        /* Check program messages */
Laurent Aimar's avatar
Laurent Aimar committed
711 712 713
        for (GLuint i = 0; i < 2; i++) {
            int infoLength = 0;
            vgl->GetProgramiv(vgl->program[i], GL_INFO_LOG_LENGTH, &infoLength);
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
            if (infoLength <= 1)
                continue;
            char *infolog = malloc(infoLength);
            int charsWritten;
            vgl->GetProgramInfoLog(vgl->program[i], infoLength, &charsWritten, infolog);
            fprintf(stderr, "shader program %d: %s\n", i, infolog);
            free(infolog);

            /* If there is some message, better to check linking is ok */
            GLint link_status = GL_TRUE;
            vgl->GetProgramiv(vgl->program[i], GL_LINK_STATUS, &link_status);
            if (link_status == GL_FALSE) {
                fprintf(stderr, "Unable to use program %d\n", i);
                free(vgl);
                return NULL;
729
            }
Laurent Aimar's avatar
Laurent Aimar committed
730
        }
731 732
#else
        (void)yuv_range_correction;
733
#endif
734 735
    }

736
    /* */
737 738 739
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
740
    glEnable(GL_CULL_FACE);
741 742 743
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

744 745
#ifdef SUPPORTS_SHADERS
    vgl->GenBuffers(1, &vgl->vertex_buffer_object);
746
    vgl->GenBuffers(1, &vgl->index_buffer_object);
747
    vgl->GenBuffers(vgl->chroma->plane_count, vgl->texture_buffer_object);
748 749 750 751 752 753 754 755 756 757 758

    /* Initial number of allocated buffer objects for subpictures, will grow dynamically. */
    int subpicture_buffer_object_count = 8;
    vgl->subpicture_buffer_object = malloc(subpicture_buffer_object_count * sizeof(GLuint));
    if (!vgl->subpicture_buffer_object) {
        vlc_gl_Unlock(vgl->gl);
        vout_display_opengl_Delete(vgl);
        return NULL;
    }
    vgl->subpicture_buffer_object_count = subpicture_buffer_object_count;
    vgl->GenBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
759 760
#endif

761
    vlc_gl_Unlock(vgl->gl);
762 763 764

    /* */
    for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
765 766
        for (int j = 0; j < PICTURE_PLANE_MAX; j++)
            vgl->texture[i][j] = 0;
767
    }
768 769
    vgl->region_count = 0;
    vgl->region = NULL;
770 771
    vgl->pool = NULL;

772 773 774 775 776 777 778
    if (vgl->fmt.projection_mode != PROJECTION_MODE_RECTANGULAR
     && vout_display_opengl_SetViewpoint(vgl, viewpoint) != VLC_SUCCESS)
    {
        vout_display_opengl_Delete(vgl);
        return NULL;
    }

779
    *fmt = vgl->fmt;
780
    if (subpicture_chromas) {
781
        *subpicture_chromas = gl_subpicture_chromas;
782
    }
783
    return vgl;
784 785
}

786
void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
787 788
{
    /* */
789
    if (!vlc_gl_Lock(vgl->gl)) {
790 791
        glFinish();
        glFlush();
792 793
        for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
            glDeleteTextures(vgl->chroma->plane_count, vgl->texture[i]);
Laurent Aimar's avatar
Laurent Aimar committed
794 795 796 797 798
        for (int i = 0; i < vgl->region_count; i++) {
            if (vgl->region[i].texture)
                glDeleteTextures(1, &vgl->region[i].texture);
        }
        free(vgl->region);
799

800
#ifdef SUPPORTS_SHADERS
Laurent Aimar's avatar
Laurent Aimar committed
801 802 803 804 805
        if (vgl->program[0]) {
            for (int i = 0; i < 2; i++)
                vgl->DeleteProgram(vgl->program[i]);
            for (int i = 0; i < 3; i++)
                vgl->DeleteShader(vgl->shader[i]);
806
        }
807
        vgl->DeleteBuffers(1, &vgl->vertex_buffer_object);
808
        vgl->DeleteBuffers(1, &vgl->index_buffer_object);
809
        vgl->DeleteBuffers(vgl->chroma->plane_count, vgl->texture_buffer_object);
810 811 812
        if (vgl->subpicture_buffer_object_count > 0)
            vgl->DeleteBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
        free(vgl->subpicture_buffer_object);
813
#endif
814

815
        free(vgl->texture_temp_buf);
816
        vlc_gl_Unlock(vgl->gl);
817
    }
818
    if (vgl->pool)
819
        picture_pool_Release(vgl->pool);
820
    free(vgl);
821 822
}

823
static void UpdateZ(vout_display_opengl_t *vgl)
824
{
825
    /* Do trigonometry to calculate the minimal z value
826 827
     * that will allow us to zoom out without seeing the outside of the
     * sphere (black borders). */
828 829
    float tan_fovx_2 = tanf(vgl->f_fovx / 2);
    float tan_fovy_2 = tanf(vgl->f_fovy / 2);
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
    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);
850 851
}

852 853 854
int vout_display_opengl_SetViewpoint(vout_display_opengl_t *vgl,
                                     const vlc_viewpoint_t *p_vp)
{
Steve Lhomme's avatar
Steve Lhomme committed
855
#define RAD(d) ((float) ((d) * M_PI / 180.f))
856
    float f_fovx = RAD(p_vp->fov);
857 858
    if (f_fovx > FIELD_OF_VIEW_DEGREES_MAX * M_PI / 180 + 0.001f
        || f_fovx < -0.001f)
859
        return VLC_EBADVAR;
860

Steve Lhomme's avatar
Steve Lhomme committed
861
    vgl->f_teta = RAD(p_vp->yaw) - (float) M_PI_2;
862 863
    vgl->f_phi  = RAD(p_vp->pitch);
    vgl->f_roll = RAD(p_vp->roll);
864

865

866
    if (fabsf(f_fovx - vgl->f_fovx) >= 0.001f)
867
    {
868
        /* FOVx has changed. */
869
        vgl->f_fovx = f_fovx;
870 871
        UpdateFOVy(vgl);
        UpdateZ(vgl);
872 873
    }

874 875 876 877
    return VLC_SUCCESS;
#undef RAD
}

878 879 880 881 882 883 884 885

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;
886 887
    UpdateFOVy(vgl);
    UpdateZ(vgl);
888 889 890
}


891
picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
892
{
893 894 895
    if (vgl->pool)
        return vgl->pool;

896 897
    /* Allocate our pictures */
    picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
Laurent Aimar's avatar
Laurent Aimar committed
898
    unsigned count;
899 900 901 902 903

    for (count = 0; count < __MIN(VLCGL_PICTURE_MAX, requested_count); count++) {
        picture[count] = picture_NewFromFormat(&vgl->fmt);
        if (!picture[count])
            break;
904
    }
905 906
    if (count <= 0)
        return NULL;
907

908
    /* Wrap the pictures into a pool */
909
    vgl->pool = picture_pool_New(count, picture);
910 911 912
    if (!vgl->pool)
        goto error;

913
    /* Allocates our textures */
914 915 916 917
    if (vlc_gl_Lock(vgl->gl))
        return vgl->pool;

    for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
918 919
        glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
        for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
Laurent Aimar's avatar
Laurent Aimar committed
920
            if (vgl->use_multitexture) {
921 922
                glActiveTexture(GL_TEXTURE0 + j);
                glClientActiveTexture(GL_TEXTURE0 + j);
923
            }
924
            glBindTexture(vgl->tex_target, vgl->texture[i][j]);
925

926
#if !defined(USE_OPENGL_ES2)
927 928 929
            /* Set the texture parameters */
            glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
930 931
#endif

932 933 934 935
            glTexParameteri(vgl->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(vgl->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(vgl->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
936

937
            /* Call glTexImage2D only once, and use glTexSubImage2D later */
938
            glTexImage2D(vgl->tex_target, 0,
939
                         vgl->tex_internal, vgl->tex_width[j], vgl->tex_height[j],
940
                         0, vgl->tex_format, vgl->tex_type, NULL);
941 942 943 944
        }
    }

    vlc_gl_Unlock(vgl->gl);
945 946 947 948

    return vgl->pool;

error:
949
    for (unsigned i = 0; i < count; i++)
950
        picture_Release(picture[i]);
951 952 953
    return NULL;
}

954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
#define ALIGN(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
static void Upload(vout_display_opengl_t *vgl, int in_width, int in_height,
                   int in_full_width, int in_full_height,
                   int w_num, int w_den, int h_num, int h_den,
                   int pitch, int pixel_pitch,
                   int full_upload, const uint8_t *pixels,
                   int tex_target, int tex_format, int tex_type)
{
    int width       =       in_width * w_num / w_den;
    int full_width  =  in_full_width * w_num / w_den;
    int height      =      in_height * h_num / h_den;
    int full_height = in_full_height * h_num / h_den;
    // This unpack alignment is the default, but setting it just in case.
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
#ifndef GL_UNPACK_ROW_LENGTH
969 970 971
    int dst_width = full_upload ? full_width : width;
    int dst_pitch = ALIGN(dst_width * pixel_pitch, 4);
    if ( pitch != dst_pitch )