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