opengl.c 21.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*****************************************************************************
 * opengl.c: OpenGL and OpenGL ES output common code
 *****************************************************************************
 * Copyright (C) 2004 the VideoLAN team
 * Copyright (C) 2009 Laurent Aimar
 *
 * Authors: Cyril Deguet <asmax@videolan.org>
 *          Gildas Bazin <gbazin@videolan.org>
 *          Eric Petit <titer@m0k.org>
 *          Cedric Cocquebert <cedric.cocquebert@supelec.fr>
 *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU 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
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
30 31 32

#include <vlc_common.h>
#include <vlc_picture_pool.h>
33
#include <vlc_opengl.h>
34 35 36 37

#include "opengl.h"
// Define USE_OPENGL_ES to the GL ES Version you want to select

38 39 40 41 42
#if !defined (__APPLE__)
# if USE_OPENGL_ES == 2
#  include <GLES2/gl2ext.h>
# elif USE_OPENGL_ES == 1
#  include <GLES/glext.h>
43 44 45
# else
#   include <GL/glext.h>
# endif
46 47 48 49 50 51 52 53 54
#else
# if USE_OPENGL_ES == 2
#  include <OpenGLES/ES2/gl.h>
# elif USE_OPENGL_ES == 1
#  include <OpenGLES/ES1/gl.h>
# else
#  define MACOS_OPENGL
#  include <OpenGL/glext.h>
# endif
55 56 57 58 59 60 61 62 63 64 65
#endif

/* RV16 */
#ifndef GL_UNSIGNED_SHORT_5_6_5
# define GL_UNSIGNED_SHORT_5_6_5 0x8363
#endif
#ifndef GL_CLAMP_TO_EDGE
# define GL_CLAMP_TO_EDGE 0x812F
#endif

#if USE_OPENGL_ES
66
#   define VLCGL_TEXTURE_COUNT 1
67
#   define VLCGL_PICTURE_MAX 1
68
#elif defined(MACOS_OPENGL)
69
#   define VLCGL_TEXTURE_COUNT 2
70
#   define VLCGL_PICTURE_MAX 2
71
#else
72
#   define VLCGL_TEXTURE_COUNT 1
73
#   define VLCGL_PICTURE_MAX 128
74 75
#endif

76 77 78 79
struct vout_display_opengl_t {
    vlc_gl_t   *gl;

    video_format_t fmt;
80
    const vlc_chroma_description_t *chroma;
81

82 83 84
    int        tex_target;
    int        tex_format;
    int        tex_type;
85

86 87 88 89
    int        tex_width[PICTURE_PLANE_MAX];
    int        tex_height[PICTURE_PLANE_MAX];

    GLuint     texture[VLCGL_TEXTURE_COUNT][PICTURE_PLANE_MAX];
90 91

    picture_pool_t *pool;
92 93

    GLuint     program;
94 95
    int        local_count;
    GLfloat    local_value[16][4];
96 97

    /* fragment_program */
98 99 100 101
    void (*GenProgramsARB)(GLsizei, GLuint *);
    void (*BindProgramARB)(GLenum, GLuint);
    void (*ProgramStringARB)(GLenum, GLenum, GLsizei, const GLvoid *);
    void (*DeleteProgramsARB)(GLsizei, const GLuint *);
102
    void (*ProgramLocalParameter4fvARB)(GLenum, GLuint, const GLfloat *);
103 104

    /* multitexture */
105
    void (*ActiveTextureARB)(GLenum);
106
    void (*MultiTexCoord2fARB)(GLenum, GLfloat, GLfloat);
107 108
};

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
109
static inline int GetAlignedSize(unsigned size)
110
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
111 112 113
    /* Return the smallest larger or equal power of 2 */
    unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
    return ((align >> 1) == size) ? size : align;
114 115
}

116
vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
117
                                               const vlc_fourcc_t **subpicture_chromas,
118
                                               vlc_gl_t *gl)
119
{
120
    vout_display_opengl_t *vgl = calloc(1, sizeof(*vgl));
121 122 123
    if (!vgl)
        return NULL;

124
    vgl->gl = gl;
125 126 127 128 129 130 131 132
    if (vlc_gl_Lock(vgl->gl)) {
        free(vgl);
        return NULL;
    }

    const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
    if (!extensions)
        extensions = "";
133

134 135 136
    /* Load extensions */
    bool supports_fp = false;
    if (strstr(extensions, "GL_ARB_fragment_program")) {
137 138 139 140
        vgl->GenProgramsARB    = (void (*)(GLsizei, GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glGenProgramsARB");
        vgl->BindProgramARB    = (void (*)(GLenum, GLuint))vlc_gl_GetProcAddress(vgl->gl, "glBindProgramARB");
        vgl->ProgramStringARB  = (void (*)(GLenum, GLenum, GLsizei, const GLvoid *))vlc_gl_GetProcAddress(vgl->gl, "glProgramStringARB");
        vgl->DeleteProgramsARB = (void (*)(GLsizei, const GLuint *))vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgramsARB");
141
        vgl->ProgramLocalParameter4fvARB = (void (*)(GLenum, GLuint, const GLfloat *))vlc_gl_GetProcAddress(vgl->gl, "glProgramLocalParameter4fvARB");
142 143 144 145

        supports_fp = vgl->GenProgramsARB &&
                      vgl->BindProgramARB &&
                      vgl->ProgramStringARB &&
146 147
                      vgl->DeleteProgramsARB &&
                      vgl->ProgramLocalParameter4fvARB;
148
    }
149 150
    bool supports_multitexture = false;
    if (strstr(extensions, "GL_ARB_multitexture")) {
151
        vgl->ActiveTextureARB   = (void (*)(GLenum))vlc_gl_GetProcAddress(vgl->gl, "glActiveTextureARB");
152 153 154 155 156
        vgl->MultiTexCoord2fARB = (void (*)(GLenum, GLfloat, GLfloat))vlc_gl_GetProcAddress(vgl->gl, "glMultiTexCoord2fARB");

        supports_multitexture = vgl->ActiveTextureARB &&
                                vgl->MultiTexCoord2fARB;
    }
157

158
    /* Initialize with default chroma */
159
    vgl->fmt = *fmt;
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
#if USE_OPENGL_ES
    vgl->fmt.i_chroma = VLC_CODEC_RGB16;
#   if defined(WORDS_BIGENDIAN)
    vgl->fmt.i_rmask  = 0x001f;
    vgl->fmt.i_gmask  = 0x07e0;
    vgl->fmt.i_bmask  = 0xf800;
#   else
    vgl->fmt.i_rmask  = 0xf800;
    vgl->fmt.i_gmask  = 0x07e0;
    vgl->fmt.i_bmask  = 0x001f;
#   endif
    vgl->tex_target   = GL_TEXTURE_2D;
    vgl->tex_format   = GL_RGB;
    vgl->tex_type     = GL_UNSIGNED_SHORT_5_6_5;
#elif defined(MACOS_OPENGL)
#   if defined(WORDS_BIGENDIAN)
176
    vgl->fmt.i_chroma = VLC_CODEC_YUYV;
177
#   else
178
    vgl->fmt.i_chroma = VLC_CODEC_UYVY;
179
#   endif
180 181 182
    vgl->tex_target   = GL_TEXTURE_RECTANGLE_EXT;
    vgl->tex_format   = GL_YCBCR_422_APPLE;
    vgl->tex_type     = GL_UNSIGNED_SHORT_8_8_APPLE;
183
#else
184
    vgl->fmt.i_chroma = VLC_CODEC_RGB32;
185 186 187 188 189 190 191 192 193 194 195 196
#   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;
    vgl->tex_type     = GL_UNSIGNED_BYTE;
197
#endif
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    /* Use YUV if possible and needed */
    bool need_fs_yuv = false;
    if (supports_fp && supports_multitexture &&
        vlc_fourcc_IsYUV(fmt->i_chroma) && !vlc_fourcc_IsYUV(vgl->fmt.i_chroma)) {
        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;
                vgl->tex_format   = GL_LUMINANCE;
                vgl->tex_type     = GL_UNSIGNED_BYTE;
                break;
            }
            list++;
        }
    }
216

217
    vgl->chroma = vlc_fourcc_GetChromaDescription(vgl->fmt.i_chroma);
218 219 220 221 222 223

    bool supports_npot = false;
#if USE_OPENGL_ES == 2
    supports_npot = true;
#elif defined(MACOS_OPENGL)
    supports_npot = true;
224 225 226
#else
    supports_npot |= strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != NULL ||
                     strstr(extensions, "GL_ARB_texture_non_power_of_two");
227 228
#endif

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    /* Texture size
     * TODO calculate the size such that the pictures can be used as
     * direct buffers
     */
    for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
        int w = vgl->fmt.i_width  * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den;
        int h = vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den;
        if (supports_npot) {
            vgl->tex_width[j]  = w;
            vgl->tex_height[j] = h;
        }
        else {
            /* A texture must have a size aligned on a power of 2 */
            vgl->tex_width[j]  = GetAlignedSize(w);
            vgl->tex_height[j] = GetAlignedSize(h);
        }
245 246
    }

247 248
    /* Build fragment program if needed */
    vgl->program = 0;
249
    vgl->local_count = 0;
250 251
    if (supports_fp) {
        char *code = NULL;
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

        if (need_fs_yuv) {
            /* [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[3][4] = {
                { 1.1640,  0.0000,  1.4030, -0.7773 },
                { 1.1640, -0.3440, -0.7140,  0.4580 },
                { 1.1640,  1.7730,  0.0000, -0.9630 },
            };
            const float matrix_bt709_tv2full[3][4] = {
                { 1.1640,  0.0000,  1.5701, -0.8612 },
                { 1.1640, -0.1870, -0.4664,  0.2549 },
                { 1.1640,  1.8556,  0.0000, -1.0045 },
            };
            const float (*matrix)[4] = fmt->i_height > 576 ? matrix_bt709_tv2full
                                                           : matrix_bt601_tv2full;

            /* Basic linear YUV -> RGB conversion using bilinear interpolation */
            const char *template_yuv =
                "!!ARBfp1.0"
                "OPTION ARB_precision_hint_fastest;"

                "TEMP src;"
                "TEX src.x,  fragment.texcoord[0], texture[0], 2D;"
                "TEX src.%c, fragment.texcoord[1], texture[1], 2D;"
                "TEX src.%c, fragment.texcoord[2], texture[2], 2D;"

281
                "PARAM coefficient[4] = { program.local[0..3] };"
282 283

                "TEMP tmp;"
284 285 286
                "MAD  tmp.rgb,          src.xxxx, coefficient[0], coefficient[3];"
                "MAD  tmp.rgb,          src.yyyy, coefficient[1], tmp;"
                "MAD  result.color.rgb, src.zzzz, coefficient[2], tmp;"
287 288 289 290 291
                "END";
            bool swap_uv = vgl->fmt.i_chroma == VLC_CODEC_YV12 ||
                           vgl->fmt.i_chroma == VLC_CODEC_YV9;
            if (asprintf(&code, template_yuv,
                         swap_uv ? 'z' : 'y',
292
                         swap_uv ? 'y' : 'z') < 0)
293
                code = NULL;
294 295 296 297 298

            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                    vgl->local_value[vgl->local_count + i][j] = j < 3 ? matrix[j][i] : 0.0;
            vgl->local_count += 4;
299
        }
300 301 302 303 304 305 306 307
        if (code) {
            vgl->GenProgramsARB(1, &vgl->program);
            vgl->BindProgramARB(GL_FRAGMENT_PROGRAM_ARB, vgl->program);
            vgl->ProgramStringARB(GL_FRAGMENT_PROGRAM_ARB,
                                  GL_PROGRAM_FORMAT_ASCII_ARB,
                                  strlen(code), (const GLbyte*)code);
            if (glGetError() == GL_INVALID_OPERATION) {
                /* FIXME if the program was needed for YUV, the video will be broken */
308
#if 0
309 310 311 312 313 314 315 316 317 318 319 320 321
                GLint position;
                glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &position);

                const char *msg = (const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB);
                fprintf(stderr, "GL_INVALID_OPERATION: error at %d: %s\n", position, msg);
#endif
                vgl->DeleteProgramsARB(1, &vgl->program);
                vgl->program = 0;
            }
            free(code);
        }
    }

322
    /* */
323 324 325 326 327 328 329 330
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
    glDisable(GL_CULL_FACE);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    vlc_gl_Unlock(vgl->gl);
331 332 333

    /* */
    for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
334 335
        for (int j = 0; j < PICTURE_PLANE_MAX; j++)
            vgl->texture[i][j] = 0;
336 337 338 339
    }
    vgl->pool = NULL;

    *fmt = vgl->fmt;
340 341 342
    if (subpicture_chromas) {
        *subpicture_chromas = NULL;
    }
343
    return vgl;
344 345
}

346
void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
347 348
{
    /* */
349
    if (!vlc_gl_Lock(vgl->gl)) {
350 351 352

        glFinish();
        glFlush();
353 354
        for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
            glDeleteTextures(vgl->chroma->plane_count, vgl->texture[i]);
355

356 357 358
        if (vgl->program)
            vgl->DeleteProgramsARB(1, &vgl->program);

359
        vlc_gl_Unlock(vgl->gl);
360
    }
361
    if (vgl->pool)
362
        picture_pool_Delete(vgl->pool);
363
    free(vgl);
364 365 366 367 368 369 370 371 372
}

#ifdef MACOS_OPENGL
struct picture_sys_t {
    vout_display_opengl_t *vgl;
    GLuint *texture;
};

/* Small helper */
373
static inline GLuint PictureGetTexture(picture_t *picture)
374 375 376 377 378 379 380 381 382 383
{
    return *picture->p_sys->texture;
}

static int PictureLock(picture_t *picture)
{
    if (!picture->p_sys)
        return VLC_SUCCESS;

    vout_display_opengl_t *vgl = picture->p_sys->vgl;
384
    if (!vlc_gl_Lock(vgl->gl)) {
385
        glBindTexture(vgl->tex_target, PictureGetTexture(picture));
386 387
        glTexSubImage2D(vgl->tex_target, 0,
                        0, 0, vgl->fmt.i_width, vgl->fmt.i_height,
388
                        vgl->tex_format, vgl->tex_type, picture->p[0].p_pixels);
389

390
        vlc_gl_Unlock(vgl->gl);
391 392 393 394 395 396 397 398 399 400
    }
    return VLC_SUCCESS;
}

static void PictureUnlock(picture_t *picture)
{
    VLC_UNUSED(picture);
}
#endif

401
picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned requested_count)
402
{
403 404 405
    if (vgl->pool)
        return vgl->pool;

406 407 408 409 410 411 412 413
    /* Allocate our pictures */
    picture_t *picture[VLCGL_PICTURE_MAX] = {NULL, };
    unsigned count = 0;

    for (count = 0; count < __MIN(VLCGL_PICTURE_MAX, requested_count); count++) {
        picture[count] = picture_NewFromFormat(&vgl->fmt);
        if (!picture[count])
            break;
414 415

#ifdef MACOS_OPENGL
416 417 418 419
        picture_sys_t *sys = picture[count]->p_sys = malloc(sizeof(*sys));
        if (sys) {
            sys->vgl = vgl;
            sys->texture = vgl->texture[count];
420 421 422
        }
#endif
    }
423 424
    if (count <= 0)
        return NULL;
425

426
    /* Wrap the pictures into a pool */
427 428
    picture_pool_configuration_t cfg;
    memset(&cfg, 0, sizeof(cfg));
429 430
    cfg.picture_count = count;
    cfg.picture       = picture;
431
#ifdef MACOS_OPENGL
432 433
    cfg.lock          = PictureLock;
    cfg.unlock        = PictureUnlock;
434 435 436 437 438
#endif
    vgl->pool = picture_pool_NewExtended(&cfg);
    if (!vgl->pool)
        goto error;

439
    /* Allocates our textures */
440 441 442 443
    if (vlc_gl_Lock(vgl->gl))
        return vgl->pool;

    for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
444 445 446 447 448
        glGenTextures(vgl->chroma->plane_count, vgl->texture[i]);
        for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
            if (vgl->chroma->plane_count > 1)
                vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
            glBindTexture(vgl->tex_target, vgl->texture[i][j]);
449 450

#if !USE_OPENGL_ES
451 452 453
            /* Set the texture parameters */
            glTexParameterf(vgl->tex_target, GL_TEXTURE_PRIORITY, 1.0);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
454 455
#endif

456 457 458 459
            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);
460 461

#ifdef MACOS_OPENGL
462 463 464 465
            /* Tell the driver not to make a copy of the texture but to use
               our buffer */
            glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
            glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
466 467

#if 0
468 469 470
            /* Use VRAM texturing */
            glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
                             GL_STORAGE_CACHED_APPLE);
471
#else
472 473 474
            /* Use AGP texturing */
            glTexParameteri(vgl->tex_target, GL_TEXTURE_STORAGE_HINT_APPLE,
                             GL_STORAGE_SHARED_APPLE);
475 476 477
#endif
#endif

478
            /* Call glTexImage2D only once, and use glTexSubImage2D later */
479 480 481
            glTexImage2D(vgl->tex_target, 0,
                         vgl->tex_format, vgl->tex_width[j], vgl->tex_height[j],
                         0, vgl->tex_format, vgl->tex_type, NULL);
482 483 484 485
        }
    }

    vlc_gl_Unlock(vgl->gl);
486 487 488 489

    return vgl->pool;

error:
490 491
    for (unsigned i = 0; i < count; i++)
        picture_Delete(picture[i]);
492 493 494 495
    return NULL;
}

int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
496
                                picture_t *picture, subpicture_t *subpicture)
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
{
    /* On Win32/GLX, we do this the usual way:
       + Fill the buffer with new content,
       + Reload the texture,
       + Use the texture.

       On OS X with VRAM or AGP texturing, the order has to be:
       + Reload the texture,
       + Fill the buffer with new content,
       + Use the texture.

       (Thanks to gcc from the Arstechnica forums for the tip)

       Therefore on OSX, we have to use two buffers and textures and use a
       lock(/unlock) managed picture pool.
     */

514
    if (vlc_gl_Lock(vgl->gl))
515 516 517 518
        return VLC_EGENERIC;

#ifdef MACOS_OPENGL
    /* Bind to the texture for drawing */
519
    glBindTexture(vgl->tex_target, PictureGetTexture(picture));
520 521
#else
    /* Update the texture */
522 523 524
    for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
        if (vgl->chroma->plane_count > 1)
            vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
525
        glBindTexture(vgl->tex_target, vgl->texture[0][j]);
526 527 528 529 530
        glPixelStorei(GL_UNPACK_ROW_LENGTH, picture->p[j].i_pitch / picture->p[j].i_pixel_pitch);
        glTexSubImage2D(vgl->tex_target, 0,
                        0, 0,
                        vgl->fmt.i_width  * vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den,
                        vgl->fmt.i_height * vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den,
531 532
                        vgl->tex_format, vgl->tex_type, picture->p[j].p_pixels);
    }
533 534
#endif

535
    vlc_gl_Unlock(vgl->gl);
536
    VLC_UNUSED(subpicture);
537 538 539 540 541 542
    return VLC_SUCCESS;
}

int vout_display_opengl_Display(vout_display_opengl_t *vgl,
                                const video_format_t *source)
{
543
    if (vlc_gl_Lock(vgl->gl))
544 545 546 547
        return VLC_EGENERIC;

    /* glTexCoord works differently with GL_TEXTURE_2D and
       GL_TEXTURE_RECTANGLE_EXT */
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
    float left[PICTURE_PLANE_MAX];
    float top[PICTURE_PLANE_MAX];
    float right[PICTURE_PLANE_MAX];
    float bottom[PICTURE_PLANE_MAX];
    for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
        float scale_w, scale_h;
        if (vgl->tex_target == GL_TEXTURE_2D) {
            scale_w = (float)vgl->chroma->p[j].w.num / vgl->chroma->p[j].w.den / vgl->tex_width[j];
            scale_h = (float)vgl->chroma->p[j].h.num / vgl->chroma->p[j].h.den / vgl->tex_height[j];

        } else {
            scale_w = 1.0;
            scale_h = 1.0;
        }
        left[j]   = (source->i_x_offset +                       0 ) * scale_w;
        top[j]    = (source->i_y_offset +                       0 ) * scale_h;
        right[j]  = (source->i_x_offset + source->i_visible_width ) * scale_w;
        bottom[j] = (source->i_y_offset + source->i_visible_height) * scale_h;
566
    }
567 568 569 570 571 572 573 574


    /* Why drawing here and not in Render()? Because this way, the
       OpenGL providers can call vout_display_opengl_Display to force redraw.i
       Currently, the OS X provider uses it to get a smooth window resizing */

    glClear(GL_COLOR_BUFFER_BIT);

575
    if (vgl->program) {
576
        glEnable(GL_FRAGMENT_PROGRAM_ARB);
577 578 579
        for (int i = 0; i < vgl->local_count; i++)
            vgl->ProgramLocalParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, i, vgl->local_value[i]);
    } else {
580
        glEnable(vgl->tex_target);
581
    }
582 583 584 585 586 587 588 589 590 591

#if USE_OPENGL_ES
    static const GLfloat vertexCoord[] = {
        -1.0f, -1.0f,
         1.0f, -1.0f,
        -1.0f,  1.0f,
         1.0f,  1.0f,
    };

    const GLfloat textureCoord[8] = {
592 593 594 595
        left[0],  bottom[0],
        right[0], bottom[0],
        left[0],  top[0],
        right[0], top[0]
596 597 598 599 600 601 602 603 604
    };

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
    glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#else
605 606 607 608 609 610 611
#if !defined(MACOS_OPENGL)
    for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
        if (vgl->chroma->plane_count > 1)
            vgl->ActiveTextureARB(GL_TEXTURE0_ARB + j);
        glBindTexture(vgl->tex_target, vgl->texture[0][j]);
    }
#endif
612
    glBegin(GL_POLYGON);
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633

    glTexCoord2f(left[0],  top[0]);
    for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
        vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], top[j]);
    glVertex2f(-1.0,  1.0);

    glTexCoord2f(right[0], top[0]);
    for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
        vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], top[j]);
    glVertex2f( 1.0,  1.0);

    glTexCoord2f(right[0], bottom[0]);
    for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
        vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, right[j], bottom[j]);
    glVertex2f( 1.0, -1.0);

    glTexCoord2f(left[0],  bottom[0]);
    for (unsigned j = 1; j < vgl->chroma->plane_count; j++)
        vgl->MultiTexCoord2fARB(GL_TEXTURE0_ARB + j, left[j], bottom[j]);
    glVertex2f(-1.0, -1.0);

634 635 636
    glEnd();
#endif

637 638 639
    if (vgl->program)
        glDisable(GL_FRAGMENT_PROGRAM_ARB);
    else
640
        glDisable(vgl->tex_target);
641

642
    vlc_gl_Swap(vgl->gl);
643

644
    vlc_gl_Unlock(vgl->gl);
645 646 647
    return VLC_SUCCESS;
}