]> git.sesse.net Git - vlc/blob - modules/video_output/opengl.c
Replace vout_opengl_t with vlc_gl_t
[vlc] / modules / video_output / opengl.c
1 /*****************************************************************************
2  * opengl.c: OpenGL and OpenGL ES output common code
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * Copyright (C) 2009 Laurent Aimar
6  *
7  * Authors: Cyril Deguet <asmax@videolan.org>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *          Eric Petit <titer@m0k.org>
10  *          Cedric Cocquebert <cedric.cocquebert@supelec.fr>
11  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 #include <vlc_common.h>
29 #include <vlc_picture_pool.h>
30 #include <vlc_opengl.h>
31
32 #include "opengl.h"
33 // Define USE_OPENGL_ES to the GL ES Version you want to select
34
35 #if USE_OPENGL_ES == 1
36 # include <OpenGLES/ES1/glext.h>
37 #elif USE_OPENGL_ES == 2
38 # include <OpenGLES/ES2/glext.h>
39 #elif defined(__APPLE__)
40 # define MACOS_OPENGL
41 # include <OpenGL/glext.h>
42 #endif
43
44 #ifndef YCBCR_MESA
45 # define YCBCR_MESA 0x8757
46 #endif
47 #ifndef UNSIGNED_SHORT_8_8_MESA
48 # define UNSIGNED_SHORT_8_8_MESA 0x85BA
49 #endif
50 /* RV16 */
51 #ifndef GL_UNSIGNED_SHORT_5_6_5
52 # define GL_UNSIGNED_SHORT_5_6_5 0x8363
53 #endif
54 #ifndef GL_CLAMP_TO_EDGE
55 # define GL_CLAMP_TO_EDGE 0x812F
56 #endif
57
58
59 #if USE_OPENGL_ES
60 # define VLCGL_TARGET GL_TEXTURE_2D
61
62 # define VLCGL_RGB_FORMAT GL_RGB
63 # define VLCGL_RGB_TYPE   GL_UNSIGNED_SHORT_5_6_5
64
65 // Use RGB with OpenGLES
66 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
67 # define VLCGL_TYPE   VLCGL_RGB_TYPE
68
69 #elif defined(MACOS_OPENGL)
70
71 /* On OS X, use GL_TEXTURE_RECTANGLE_EXT instead of GL_TEXTURE_2D.
72    This allows sizes which are not powers of 2 */
73 # define VLCGL_TARGET GL_TEXTURE_RECTANGLE_EXT
74
75 /* OS X OpenGL supports YUV. Hehe. */
76 # define VLCGL_FORMAT GL_YCBCR_422_APPLE
77 # define VLCGL_TYPE   GL_UNSIGNED_SHORT_8_8_APPLE
78
79 #else
80
81 # define VLCGL_TARGET GL_TEXTURE_2D
82
83 /* RV32 */
84 # define VLCGL_RGB_FORMAT GL_RGBA
85 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
86
87 /* YUY2 */
88 # define VLCGL_YUV_FORMAT YCBCR_MESA
89 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
90
91 /* Use RGB on Win32/GLX */
92 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
93 # define VLCGL_TYPE   VLCGL_RGB_TYPE
94 #endif
95
96 static inline int GetAlignedSize(unsigned size)
97 {
98     /* Return the smallest larger or equal power of 2 */
99     unsigned align = 1 << (8 * sizeof (unsigned) - clz(size));
100     return ((align >> 1) == size) ? size : align;
101 }
102
103 int vout_display_opengl_Init(vout_display_opengl_t *vgl,
104                              video_format_t *fmt,
105                              vlc_gl_t *gl)
106 {
107     vgl->gl = gl;
108
109     /* Find the chroma we will use and update fmt */
110     /* TODO: We use YCbCr on Mac which is Y422, but on OSX it seems to == YUY2. Verify */
111 #if (defined(WORDS_BIGENDIAN) && VLCGL_FORMAT == GL_YCBCR_422_APPLE) || (VLCGL_FORMAT == YCBCR_MESA)
112     fmt->i_chroma = VLC_CODEC_YUYV;
113     vgl->tex_pixel_size = 2;
114 #elif defined(GL_YCBCR_422_APPLE) && (VLCGL_FORMAT == GL_YCBCR_422_APPLE)
115     fmt->i_chroma = VLC_CODEC_UYVY;
116     vgl->tex_pixel_size = 2;
117 #elif VLCGL_FORMAT == GL_RGB
118 #   if VLCGL_TYPE == GL_UNSIGNED_BYTE
119     fmt->i_chroma = VLC_CODEC_RGB24;
120 #       if defined(WORDS_BIGENDIAN)
121     fmt->i_rmask = 0x00ff0000;
122     fmt->i_gmask = 0x0000ff00;
123     fmt->i_bmask = 0x000000ff;
124 #       else
125     fmt->i_rmask = 0x000000ff;
126     fmt->i_gmask = 0x0000ff00;
127     fmt->i_bmask = 0x00ff0000;
128 #       endif
129     vgl->tex_pixel_size = 3;
130 #   else
131     fmt->i_chroma = VLC_CODEC_RGB16;
132 #       if defined(WORDS_BIGENDIAN)
133     fmt->i_rmask = 0x001f;
134     fmt->i_gmask = 0x07e0;
135     fmt->i_bmask = 0xf800;
136 #       else
137     fmt->i_rmask = 0xf800;
138     fmt->i_gmask = 0x07e0;
139     fmt->i_bmask = 0x001f;
140 #       endif
141     vgl->tex_pixel_size = 2;
142 #   endif
143 #else
144     fmt->i_chroma = VLC_CODEC_RGB32;
145 #       if defined(WORDS_BIGENDIAN)
146     fmt->i_rmask = 0xff000000;
147     fmt->i_gmask = 0x00ff0000;
148     fmt->i_bmask = 0x0000ff00;
149 #       else
150     fmt->i_rmask = 0x000000ff;
151     fmt->i_gmask = 0x0000ff00;
152     fmt->i_bmask = 0x00ff0000;
153 #       endif
154     vgl->tex_pixel_size = 4;
155 #endif
156
157     vgl->fmt = *fmt;
158
159     /* */
160     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
161         vgl->texture[i] = 0;
162         vgl->buffer[i]  = NULL;
163     }
164     vgl->pool = NULL;
165
166     bool supports_npot = false;
167 #if USE_OPENGL_ES == 2
168     supports_npot = true;
169 #elif defined(MACOS_OPENGL)
170     supports_npot = true;
171 #endif
172
173 #if defined(__APPLE__) && USE_OPENGL_ES == 1
174     if (!vlc_gl_Lock(vgl->gl)) {
175         const char* extensions = (char*) glGetString(GL_EXTENSIONS);
176         if (extensions) {
177             bool npot = strstr(extensions, "GL_APPLE_texture_2D_limited_npot") != 0;
178             if (npot)
179                 supports_npot = true;
180         }
181         vlc_gl_Unlock(vgl->gl);
182     }
183 #endif
184
185     /* Texture size */
186     if (supports_npot) {
187         vgl->tex_width  = fmt->i_width;
188         vgl->tex_height = fmt->i_height;
189     }
190     else {
191         /* A texture must have a size aligned on a power of 2 */
192         vgl->tex_width  = GetAlignedSize(fmt->i_width);
193         vgl->tex_height = GetAlignedSize(fmt->i_height);
194     }
195
196
197     /* */
198     if (!vlc_gl_Lock(vgl->gl)) {
199
200         glDisable(GL_BLEND);
201         glDisable(GL_DEPTH_TEST);
202         glDepthMask(GL_FALSE);
203         glDisable(GL_CULL_FACE);
204         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
205         glClear(GL_COLOR_BUFFER_BIT);
206
207         vlc_gl_Unlock(vgl->gl);
208     }
209     return VLC_SUCCESS;
210 }
211
212 void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
213 {
214     /* */
215     if (!vlc_gl_Lock(vgl->gl)) {
216
217         glFinish();
218         glFlush();
219         glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
220
221         vlc_gl_Unlock(vgl->gl);
222     }
223     if (vgl->pool) {
224         picture_pool_Delete(vgl->pool);
225         for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++)
226             free(vgl->buffer[i]);
227     }
228 }
229
230 int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
231 {
232     if (vlc_gl_Lock(vgl->gl))
233         return VLC_EGENERIC;
234
235     glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
236
237     glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
238     for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
239         glBindTexture(VLCGL_TARGET, vgl->texture[i]);
240
241 #if !USE_OPENGL_ES
242         /* Set the texture parameters */
243         glTexParameterf(VLCGL_TARGET, GL_TEXTURE_PRIORITY, 1.0);
244         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
245 #endif
246
247         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
248         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
249         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
250         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
251
252 #ifdef MACOS_OPENGL
253         /* Tell the driver not to make a copy of the texture but to use
254            our buffer */
255         glEnable(GL_UNPACK_CLIENT_STORAGE_APPLE);
256         glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
257
258 #if 0
259         /* Use VRAM texturing */
260         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
261                          GL_STORAGE_CACHED_APPLE);
262 #else
263         /* Use AGP texturing */
264         glTexParameteri(VLCGL_TARGET, GL_TEXTURE_STORAGE_HINT_APPLE,
265                          GL_STORAGE_SHARED_APPLE);
266 #endif
267 #endif
268
269         /* Call glTexImage2D only once, and use glTexSubImage2D later */
270         if (vgl->buffer[i]) {
271             glTexImage2D(VLCGL_TARGET, 0, VLCGL_FORMAT, vgl->tex_width,
272                          vgl->tex_height, 0, VLCGL_FORMAT, VLCGL_TYPE,
273                          vgl->buffer[i]);
274         }
275     }
276
277     vlc_gl_Unlock(vgl->gl);
278     return VLC_SUCCESS;
279 }
280
281 #ifdef MACOS_OPENGL
282 /* XXX See comment vout_display_opengl_Prepare */
283 struct picture_sys_t {
284     vout_display_opengl_t *vgl;
285     GLuint *texture;
286 };
287
288 /* Small helper */
289 static inline GLuint get_texture(picture_t *picture)
290 {
291     return *picture->p_sys->texture;
292 }
293
294 static int PictureLock(picture_t *picture)
295 {
296     if (!picture->p_sys)
297         return VLC_SUCCESS;
298
299     vout_display_opengl_t *vgl = picture->p_sys->vgl;
300     if (!vlc_gl_Lock(vgl->gl)) {
301         glBindTexture(VLCGL_TARGET, get_texture(picture));
302         glTexSubImage2D(VLCGL_TARGET, 0, 0, 0,
303                         vgl->fmt.i_width, vgl->fmt.i_height,
304                         VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
305
306         vlc_gl_Unlock(vgl->gl);
307     }
308     return VLC_SUCCESS;
309 }
310
311 static void PictureUnlock(picture_t *picture)
312 {
313     VLC_UNUSED(picture);
314 }
315 #endif
316
317 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
318 {
319     picture_t *picture[VLCGL_TEXTURE_COUNT];
320
321     int i;
322     for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
323
324         /* TODO memalign would be way better */
325         vgl->buffer[i] = malloc(vgl->tex_width * vgl->tex_height * vgl->tex_pixel_size);
326         if (!vgl->buffer[i])
327             break;
328
329         picture_resource_t rsc;
330         memset(&rsc, 0, sizeof(rsc));
331 #ifdef MACOS_OPENGL
332         rsc.p_sys = malloc(sizeof(*rsc.p_sys));
333         if (rsc.p_sys)
334         {
335             rsc.p_sys->vgl = vgl;
336             rsc.p_sys->texture = &vgl->texture[i];
337         }
338 #endif
339         rsc.p[0].p_pixels = vgl->buffer[i];
340         rsc.p[0].i_pitch  = vgl->fmt.i_width * vgl->tex_pixel_size;
341         rsc.p[0].i_lines  = vgl->fmt.i_height;
342
343         picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
344         if (!picture[i]) {
345             free(vgl->buffer[i]);
346             vgl->buffer[i] = NULL;
347             break;
348         }
349     }
350     if (i < VLCGL_TEXTURE_COUNT)
351         goto error;
352
353     /* */
354     picture_pool_configuration_t cfg;
355     memset(&cfg, 0, sizeof(cfg));
356     cfg.picture_count = i;
357     cfg.picture = picture;
358 #ifdef MACOS_OPENGL
359     cfg.lock = PictureLock;
360     cfg.unlock = PictureUnlock;
361 #endif
362     vgl->pool = picture_pool_NewExtended(&cfg);
363     if (!vgl->pool)
364         goto error;
365
366     vout_display_opengl_ResetTextures(vgl);
367
368     return vgl->pool;
369
370 error:
371     for (int j = 0; j < i; j++) {
372         picture_Delete(picture[j]);
373         vgl->buffer[j] = NULL;
374     }
375     return NULL;
376 }
377
378 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
379                                 picture_t *picture)
380 {
381     /* On Win32/GLX, we do this the usual way:
382        + Fill the buffer with new content,
383        + Reload the texture,
384        + Use the texture.
385
386        On OS X with VRAM or AGP texturing, the order has to be:
387        + Reload the texture,
388        + Fill the buffer with new content,
389        + Use the texture.
390
391        (Thanks to gcc from the Arstechnica forums for the tip)
392
393        Therefore on OSX, we have to use two buffers and textures and use a
394        lock(/unlock) managed picture pool.
395      */
396
397     if (vlc_gl_Lock(vgl->gl))
398         return VLC_EGENERIC;
399
400 #ifdef MACOS_OPENGL
401     /* Bind to the texture for drawing */
402     glBindTexture(VLCGL_TARGET, get_texture(picture));
403 #else
404     /* Update the texture */
405     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
406                     vgl->fmt.i_width, vgl->fmt.i_height,
407                     VLCGL_FORMAT, VLCGL_TYPE, picture->p[0].p_pixels);
408 #endif
409
410     vlc_gl_Unlock(vgl->gl);
411     return VLC_SUCCESS;
412 }
413
414 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
415                                 const video_format_t *source)
416 {
417     if (vlc_gl_Lock(vgl->gl))
418         return VLC_EGENERIC;
419
420     /* glTexCoord works differently with GL_TEXTURE_2D and
421        GL_TEXTURE_RECTANGLE_EXT */
422 #if VLCGL_TARGET == GL_TEXTURE_2D
423     const float f_normw = vgl->tex_width;
424     const float f_normh = vgl->tex_height;
425 #elif defined (GL_TEXTURE_RECTANGLE_EXT) \
426    && (VLCGL_TARGET == GL_TEXTURE_RECTANGLE_EXT)
427     const float f_normw = 1.0;
428     const float f_normh = 1.0;
429 #else
430 # error Unknown texture type!
431 #endif
432
433     float f_x      = (source->i_x_offset +                       0 ) / f_normw;
434     float f_y      = (source->i_y_offset +                       0 ) / f_normh;
435     float f_width  = (source->i_x_offset + source->i_visible_width ) / f_normw;
436     float f_height = (source->i_y_offset + source->i_visible_height) / f_normh;
437
438     /* Why drawing here and not in Render()? Because this way, the
439        OpenGL providers can call vout_display_opengl_Display to force redraw.i
440        Currently, the OS X provider uses it to get a smooth window resizing */
441
442     glClear(GL_COLOR_BUFFER_BIT);
443
444     glEnable(VLCGL_TARGET);
445
446 #if USE_OPENGL_ES
447     static const GLfloat vertexCoord[] = {
448         -1.0f, -1.0f,
449          1.0f, -1.0f,
450         -1.0f,  1.0f,
451          1.0f,  1.0f,
452     };
453
454     const GLfloat textureCoord[8] = {
455         f_x,     f_height,
456         f_width, f_height,
457         f_x,     f_y,
458         f_width, f_y
459     };
460
461     glEnableClientState(GL_VERTEX_ARRAY);
462     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
463     glVertexPointer(2, GL_FLOAT, 0, vertexCoord);
464     glTexCoordPointer(2, GL_FLOAT, 0, textureCoord);
465
466     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
467 #else
468     glBegin(GL_POLYGON);
469     glTexCoord2f(f_x,      f_y);      glVertex2f(-1.0,  1.0);
470     glTexCoord2f(f_width,  f_y);      glVertex2f( 1.0,  1.0);
471     glTexCoord2f(f_width,  f_height); glVertex2f( 1.0, -1.0);
472     glTexCoord2f(f_x,      f_height); glVertex2f(-1.0, -1.0);
473     glEnd();
474 #endif
475
476     glDisable(VLCGL_TARGET);
477
478     vlc_gl_Swap(vgl->gl);
479
480     vlc_gl_Unlock(vgl->gl);
481     return VLC_SUCCESS;
482 }
483