Unexported vlc_thread_set_priority().
[vlc/vlc-skelet.git] / modules / video_output / opengl.c
blob06bbfc2b20d2763eb173d67107bf77f7bd3ac556
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
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>
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.
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.
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 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_picture_pool.h>
30 #include <vlc_opengl.h>
32 #include "opengl.h"
33 // Define USE_OPENGL_ES to the GL ES Version you want to select
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
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
69 #if USE_OPENGL_ES
70 # define VLCGL_TARGET GL_TEXTURE_2D
72 # define VLCGL_RGB_FORMAT GL_RGB
73 # define VLCGL_RGB_TYPE GL_UNSIGNED_SHORT_5_6_5
75 // Use RGB with OpenGLES
76 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
77 # define VLCGL_TYPE VLCGL_RGB_TYPE
79 #elif defined(MACOS_OPENGL)
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
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
89 #else
91 # define VLCGL_TARGET GL_TEXTURE_2D
93 /* RV32 */
94 # define VLCGL_RGB_FORMAT GL_RGBA
95 # define VLCGL_RGB_TYPE GL_UNSIGNED_BYTE
97 /* YUY2 */
98 # define VLCGL_YUV_FORMAT YCBCR_MESA
99 # define VLCGL_YUV_TYPE UNSIGNED_SHORT_8_8_MESA
101 /* Use RGB on Win32/GLX */
102 # define VLCGL_FORMAT VLCGL_RGB_FORMAT
103 # define VLCGL_TYPE VLCGL_RGB_TYPE
104 #endif
106 static inline int GetAlignedSize(unsigned size)
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;
113 int vout_display_opengl_Init(vout_display_opengl_t *vgl,
114 video_format_t *fmt,
115 vlc_gl_t *gl)
117 vgl->gl = gl;
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
167 vgl->fmt = *fmt;
169 /* */
170 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
171 vgl->texture[i] = 0;
172 vgl->buffer[i] = NULL;
174 vgl->pool = NULL;
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
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;
191 vlc_gl_Unlock(vgl->gl);
193 #endif
195 /* Texture size */
196 if (supports_npot) {
197 vgl->tex_width = fmt->i_width;
198 vgl->tex_height = fmt->i_height;
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);
207 /* */
208 if (!vlc_gl_Lock(vgl->gl)) {
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);
217 vlc_gl_Unlock(vgl->gl);
219 return VLC_SUCCESS;
222 void vout_display_opengl_Clean(vout_display_opengl_t *vgl)
224 /* */
225 if (!vlc_gl_Lock(vgl->gl)) {
227 glFinish();
228 glFlush();
229 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
231 vlc_gl_Unlock(vgl->gl);
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]);
240 int vout_display_opengl_ResetTextures(vout_display_opengl_t *vgl)
242 if (vlc_gl_Lock(vgl->gl))
243 return VLC_EGENERIC;
245 glDeleteTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
247 glGenTextures(VLCGL_TEXTURE_COUNT, vgl->texture);
248 for (int i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
249 glBindTexture(VLCGL_TARGET, vgl->texture[i]);
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
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);
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);
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
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]);
287 vlc_gl_Unlock(vgl->gl);
288 return VLC_SUCCESS;
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;
298 /* Small helper */
299 static inline GLuint get_texture(picture_t *picture)
301 return *picture->p_sys->texture;
304 static int PictureLock(picture_t *picture)
306 if (!picture->p_sys)
307 return VLC_SUCCESS;
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);
316 vlc_gl_Unlock(vgl->gl);
318 return VLC_SUCCESS;
321 static void PictureUnlock(picture_t *picture)
323 VLC_UNUSED(picture);
325 #endif
327 picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl)
329 picture_t *picture[VLCGL_TEXTURE_COUNT];
331 int i;
332 for (i = 0; i < VLCGL_TEXTURE_COUNT; i++) {
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;
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)
345 rsc.p_sys->vgl = vgl;
346 rsc.p_sys->texture = &vgl->texture[i];
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;
353 picture[i] = picture_NewFromResource(&vgl->fmt, &rsc);
354 if (!picture[i]) {
355 free(vgl->buffer[i]);
356 vgl->buffer[i] = NULL;
357 break;
360 if (i < VLCGL_TEXTURE_COUNT)
361 goto error;
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;
376 vout_display_opengl_ResetTextures(vgl);
378 return vgl->pool;
380 error:
381 for (int j = 0; j < i; j++) {
382 picture_Delete(picture[j]);
383 vgl->buffer[j] = NULL;
385 return NULL;
388 int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
389 picture_t *picture)
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.
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.
401 (Thanks to gcc from the Arstechnica forums for the tip)
403 Therefore on OSX, we have to use two buffers and textures and use a
404 lock(/unlock) managed picture pool.
407 if (vlc_gl_Lock(vgl->gl))
408 return VLC_EGENERIC;
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
420 vlc_gl_Unlock(vgl->gl);
421 return VLC_SUCCESS;
424 int vout_display_opengl_Display(vout_display_opengl_t *vgl,
425 const video_format_t *source)
427 if (vlc_gl_Lock(vgl->gl))
428 return VLC_EGENERIC;
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
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;
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 */
452 glClear(GL_COLOR_BUFFER_BIT);
454 glEnable(VLCGL_TARGET);
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,
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
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);
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
486 glDisable(VLCGL_TARGET);
488 vlc_gl_Swap(vgl->gl);
490 vlc_gl_Unlock(vgl->gl);
491 return VLC_SUCCESS;