]> git.sesse.net Git - vlc/blobdiff - modules/video_output/opengl.c
android/opaque: check if subtitles_picture is not NULL
[vlc] / modules / video_output / opengl.c
index a4c9d2e85199b1477640e249bc93b93a178e0901..7525c4f4ceb98852a1000138be71486fdad7de01 100644 (file)
@@ -33,6 +33,7 @@
 #include <vlc_picture_pool.h>
 #include <vlc_subpicture.h>
 #include <vlc_opengl.h>
+#include <vlc_memory.h>
 
 #include "opengl.h"
 
@@ -49,6 +50,7 @@
 #   define PFNGLGETATTRIBLOCATIONPROC        typeof(glGetAttribLocation)*
 #   define PFNGLVERTEXATTRIBPOINTERPROC      typeof(glVertexAttribPointer)*
 #   define PFNGLENABLEVERTEXATTRIBARRAYPROC  typeof(glEnableVertexAttribArray)*
+#   define PFNGLUNIFORMMATRIX4FVPROC         typeof(glUniformMatrix4fv)*
 #   define PFNGLUNIFORM4FVPROC               typeof(glUniform4fv)*
 #   define PFNGLUNIFORM4FPROC                typeof(glUniform4f)*
 #   define PFNGLUNIFORM1IPROC                typeof(glUniform1i)*
 #   define PFNGLUSEPROGRAMPROC               typeof(glUseProgram)*
 #   define PFNGLDELETEPROGRAMPROC            typeof(glDeleteProgram)*
 #   define PFNGLATTACHSHADERPROC             typeof(glAttachShader)*
+#   define PFNGLGENBUFFERSPROC               typeof(glGenBuffers)*
+#   define PFNGLBINDBUFFERPROC               typeof(glBindBuffer)*
+#   define PFNGLBUFFERDATAPROC               typeof(glBufferData)*
+#   define PFNGLDELETEBUFFERSPROC            typeof(glDeleteBuffers)*
 #if defined(__APPLE__) && USE_OPENGL_ES
 #   import <CoreFoundation/CoreFoundation.h>
 #endif
@@ -139,6 +145,12 @@ struct vout_display_opengl_t {
     int        local_count;
     GLfloat    local_value[16];
 
+    GLuint vertex_buffer_object;
+    GLuint texture_buffer_object[PICTURE_PLANE_MAX];
+
+    GLuint *subpicture_buffer_object;
+    int    subpicture_buffer_object_count;
+
     /* Shader variables commands*/
 #ifdef SUPPORTS_SHADERS
     PFNGLGETUNIFORMLOCATIONPROC      GetUniformLocation;
@@ -146,9 +158,10 @@ struct vout_display_opengl_t {
     PFNGLVERTEXATTRIBPOINTERPROC     VertexAttribPointer;
     PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
 
-    PFNGLUNIFORM4FVPROC   Uniform4fv;
-    PFNGLUNIFORM4FPROC    Uniform4f;
-    PFNGLUNIFORM1IPROC    Uniform1i;
+    PFNGLUNIFORMMATRIX4FVPROC   UniformMatrix4fv;
+    PFNGLUNIFORM4FVPROC         Uniform4fv;
+    PFNGLUNIFORM4FPROC          Uniform4f;
+    PFNGLUNIFORM1IPROC          Uniform1i;
 
     /* Shader command */
     PFNGLCREATESHADERPROC CreateShader;
@@ -168,6 +181,11 @@ struct vout_display_opengl_t {
     PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
     PFNGLGETSHADERIVPROC   GetShaderiv;
     PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
+
+    PFNGLGENBUFFERSPROC    GenBuffers;
+    PFNGLBINDBUFFERPROC    BindBuffer;
+    PFNGLBUFFERDATAPROC    BufferData;
+    PFNGLDELETEBUFFERSPROC DeleteBuffers;
 #endif
 
 #if defined(_WIN32)
@@ -221,12 +239,13 @@ static void BuildVertexShader(vout_display_opengl_t *vgl,
         PRECISION
         "varying vec4 TexCoord0,TexCoord1, TexCoord2;"
         "attribute vec4 MultiTexCoord0,MultiTexCoord1,MultiTexCoord2;"
-        "attribute vec4 VertexPosition;"
+        "attribute vec2 VertexPosition;"
+        "uniform mat4 RotationMatrix;"
         "void main() {"
         " TexCoord0 = MultiTexCoord0;"
         " TexCoord1 = MultiTexCoord1;"
         " TexCoord2 = MultiTexCoord2;"
-        " gl_Position = VertexPosition;"
+        " gl_Position = RotationMatrix * vec4(VertexPosition, 0.0, 1.0);"
         "}";
 
     *shader = vgl->CreateShader(GL_VERTEX_SHADER);
@@ -290,12 +309,12 @@ static void BuildYUVFragmentShader(vout_display_opengl_t *vgl,
         code = NULL;
 
     for (int i = 0; i < 4; i++) {
-        float correction = i < 3 ? yuv_range_correction : 1.0;
+        float correction = i < 3 ? yuv_range_correction : 1.f;
         /* 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]
-                                                      : 0.;
+                                                      : 0.f;
     }
     (*local_count) += 4;
 
@@ -432,6 +451,7 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     vgl->GetAttribLocation  = glGetAttribLocation;
     vgl->VertexAttribPointer= glVertexAttribPointer;
     vgl->EnableVertexAttribArray = glEnableVertexAttribArray;
+    vgl->UniformMatrix4fv = glUniformMatrix4fv;
     vgl->Uniform4fv    = glUniform4fv;
     vgl->Uniform4f     = glUniform4f;
     vgl->Uniform1i     = glUniform1i;
@@ -440,6 +460,12 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     vgl->LinkProgram   = glLinkProgram;
     vgl->UseProgram    = glUseProgram;
     vgl->DeleteProgram = glDeleteProgram;
+
+    vgl->GenBuffers    = glGenBuffers;
+    vgl->BindBuffer    = glBindBuffer;
+    vgl->BufferData    = glBufferData;
+    vgl->DeleteBuffers = glDeleteBuffers;
+
     supports_shaders = true;
 #elif defined(SUPPORTS_SHADERS)
     vgl->CreateShader  = (PFNGLCREATESHADERPROC)vlc_gl_GetProcAddress(vgl->gl, "glCreateShader");
@@ -458,6 +484,7 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     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");
+    vgl->UniformMatrix4fv   = (PFNGLUNIFORMMATRIX4FVPROC)vlc_gl_GetProcAddress(vgl->gl,"glUniformMatrix4fv");
     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");
@@ -467,6 +494,11 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     vgl->UseProgram    = (PFNGLUSEPROGRAMPROC)vlc_gl_GetProcAddress(vgl->gl, "glUseProgram");
     vgl->DeleteProgram = (PFNGLDELETEPROGRAMPROC)vlc_gl_GetProcAddress(vgl->gl, "glDeleteProgram");
 
+    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");
+
     if (!vgl->CreateShader || !vgl->ShaderSource || !vgl->CreateProgram)
         supports_shaders = false;
 #endif
@@ -652,6 +684,22 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
     glClear(GL_COLOR_BUFFER_BIT);
 
+#ifdef SUPPORTS_SHADERS
+    vgl->GenBuffers(1, &vgl->vertex_buffer_object);
+    vgl->GenBuffers(vgl->chroma->plane_count, vgl->texture_buffer_object);
+
+    /* 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);
+#endif
+
     vlc_gl_Unlock(vgl->gl);
 
     /* */
@@ -691,6 +739,11 @@ void vout_display_opengl_Delete(vout_display_opengl_t *vgl)
             for (int i = 0; i < 3; i++)
                 vgl->DeleteShader(vgl->shader[i]);
         }
+        vgl->DeleteBuffers(1, &vgl->vertex_buffer_object);
+        vgl->DeleteBuffers(vgl->chroma->plane_count, vgl->texture_buffer_object);
+        if (vgl->subpicture_buffer_object_count > 0)
+            vgl->DeleteBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
+        free(vgl->subpicture_buffer_object);
 #endif
 
         free(vgl->texture_temp_buf);
@@ -719,11 +772,7 @@ picture_pool_t *vout_display_opengl_GetPool(vout_display_opengl_t *vgl, unsigned
         return NULL;
 
     /* Wrap the pictures into a pool */
-    picture_pool_configuration_t cfg;
-    memset(&cfg, 0, sizeof(cfg));
-    cfg.picture_count = count;
-    cfg.picture       = picture;
-    vgl->pool = picture_pool_NewExtended(&cfg);
+    vgl->pool = picture_pool_New(count, picture);
     if (!vgl->pool)
         goto error;
 
@@ -896,6 +945,8 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
             glr->bottom = -2.0 * (r->i_y + r->fmt.i_visible_height) / subpicture->i_original_picture_height + 1.0;
 
             glr->texture = 0;
+            /* Try to recycle the textures allocated by the previous
+               call to this function. */
             for (int j = 0; j < last_count; j++) {
                 if (last[j].texture &&
                     last[j].width  == glr->width &&
@@ -911,11 +962,13 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
             const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
                                       r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
             if (glr->texture) {
+                /* A texture was successfully recycled, reuse it. */
                 glBindTexture(GL_TEXTURE_2D, glr->texture);
                 Upload(vgl, r->fmt.i_visible_width, r->fmt.i_visible_height, glr->width, glr->height, 1, 1, 1, 1,
                        r->p_picture->p->i_pitch, r->p_picture->p->i_pixel_pitch, 0,
                        &r->p_picture->p->p_pixels[pixels_offset], GL_TEXTURE_2D, glr->format, glr->type);
             } else {
+                /* Could not recycle a previous texture, generate a new one. */
                 glGenTextures(1, &glr->texture);
                 glBindTexture(GL_TEXTURE_2D, glr->texture);
 #if !USE_OPENGL_ES
@@ -943,6 +996,76 @@ int vout_display_opengl_Prepare(vout_display_opengl_t *vgl,
     return VLC_SUCCESS;
 }
 
+static const GLfloat identity[] = {
+    1.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 1.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 1.0f, 0.0f,
+    0.0f, 0.0f, 0.0f, 1.0f
+};
+
+static void orientationTransformMatrix(GLfloat matrix[static 16], video_orientation_t orientation) {
+
+    memcpy(matrix, identity, sizeof(identity));
+
+    const int k_cos_pi = -1;
+    const int k_cos_pi_2 = 0;
+    const int k_cos_n_pi_2 = 0;
+
+    const int k_sin_pi = 0;
+    const int k_sin_pi_2 = 1;
+    const int k_sin_n_pi_2 = -1;
+
+    bool rotate = false;
+    int cos = 0, sin = 0;
+
+    switch (orientation) {
+
+        case ORIENT_ROTATED_90:
+            cos = k_cos_pi_2;
+            sin = k_sin_pi_2;
+            rotate = true;
+            break;
+        case ORIENT_ROTATED_180:
+            cos = k_cos_pi;
+            sin = k_sin_pi;
+            rotate = true;
+            break;
+        case ORIENT_ROTATED_270:
+            cos = k_cos_n_pi_2;
+            sin = k_sin_n_pi_2;
+            rotate = true;
+            break;
+        case ORIENT_HFLIPPED:
+            matrix[0 * 4 + 0] = -1;
+            break;
+        case ORIENT_VFLIPPED:
+            matrix[1 * 4 + 1] = -1;
+            break;
+        case ORIENT_TRANSPOSED:
+            matrix[0 * 4 + 0] = 0;
+            matrix[0 * 4 + 1] = -1;
+            matrix[1 * 4 + 0] = -1;
+            matrix[1 * 4 + 1] = 0;
+            break;
+        case ORIENT_ANTI_TRANSPOSED:
+            matrix[0 * 4 + 0] = 0;
+            matrix[0 * 4 + 1] = 1;
+            matrix[1 * 4 + 0] = 1;
+            matrix[1 * 4 + 1] = 0;
+            break;
+        default:
+            break;
+    }
+
+    if (rotate) {
+
+        matrix[0 * 4 + 0] = cos;
+        matrix[0 * 4 + 1] = -sin;
+        matrix[1 * 4 + 0] = sin;
+        matrix[1 * 4 + 1] = cos;
+    }
+}
+
 #ifdef SUPPORTS_FIXED_PIPELINE
 static void DrawWithoutShaders(vout_display_opengl_t *vgl,
                                float *left, float *top, float *right, float *bottom)
@@ -961,6 +1084,13 @@ static void DrawWithoutShaders(vout_display_opengl_t *vgl,
         right[0], top[0]
     };
 
+    GLfloat transformMatrix[16];
+    orientationTransformMatrix(transformMatrix, vgl->fmt.orientation);
+
+    glPushMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glLoadMatrixf(transformMatrix);
+
     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
     glEnable(vgl->tex_target);
     glActiveTexture(GL_TEXTURE0 + 0);
@@ -979,6 +1109,9 @@ static void DrawWithoutShaders(vout_display_opengl_t *vgl,
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     glDisableClientState(GL_VERTEX_ARRAY);
     glDisable(vgl->tex_target);
+
+    glPopMatrix();
+
 }
 #endif
 
@@ -1010,6 +1143,9 @@ static void DrawWithShaders(vout_display_opengl_t *vgl,
          1.0, -1.0,
     };
 
+    GLfloat transformMatrix[16];
+    orientationTransformMatrix(transformMatrix, vgl->fmt.orientation);
+
     for (unsigned j = 0; j < vgl->chroma->plane_count; j++) {
         const GLfloat textureCoord[] = {
             left[j],  top[j],
@@ -1021,15 +1157,23 @@ static void DrawWithShaders(vout_display_opengl_t *vgl,
         glClientActiveTexture(GL_TEXTURE0+j);
         glBindTexture(vgl->tex_target, vgl->texture[0][j]);
 
+        vgl->BindBuffer(GL_ARRAY_BUFFER, vgl->texture_buffer_object[j]);
+        vgl->BufferData(GL_ARRAY_BUFFER, sizeof(textureCoord), textureCoord, GL_STATIC_DRAW);
+
         char attribute[20];
         snprintf(attribute, sizeof(attribute), "MultiTexCoord%1d", j);
         vgl->EnableVertexAttribArray(vgl->GetAttribLocation(vgl->program[program], attribute));
-        vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[program], attribute), 2, GL_FLOAT, 0, 0, textureCoord);
+        vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[program], attribute), 2, GL_FLOAT, 0, 0, 0);
     }
     glActiveTexture(GL_TEXTURE0 + 0);
     glClientActiveTexture(GL_TEXTURE0 + 0);
+
+    vgl->BindBuffer(GL_ARRAY_BUFFER, vgl->vertex_buffer_object);
+    vgl->BufferData(GL_ARRAY_BUFFER, sizeof(vertexCoord), vertexCoord, GL_STATIC_DRAW);
     vgl->EnableVertexAttribArray(vgl->GetAttribLocation(vgl->program[program], "VertexPosition"));
-    vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[program], "VertexPosition"), 2, GL_FLOAT, 0, 0, vertexCoord);
+    vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[program], "VertexPosition"), 2, GL_FLOAT, 0, 0, 0);
+
+    vgl->UniformMatrix4fv(vgl->GetUniformLocation(vgl->program[program], "RotationMatrix"), 1, GL_FALSE, transformMatrix);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 }
@@ -1064,6 +1208,17 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
             scale_w = 1.0;
             scale_h = 1.0;
         }
+        /* Warning: if NPOT is not supported a larger texture is
+           allocated. This will cause right and bottom coordinates to
+           land on the edge of two texels with the texels to the
+           right/bottom uninitialized by the call to
+           glTexSubImage2D. This might cause a green line to appear on
+           the right/bottom of the display.
+           There are two possible solutions:
+           - Manually mirror the edges of the texture.
+           - Add a "-1" when computing right and bottom, however the
+           last row/column might not be displayed at all.
+        */
         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;
@@ -1098,6 +1253,25 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
+#ifdef SUPPORTS_SHADERS
+    /* We need two buffer objects for each region: for vertex and texture coordinates. */
+    if (2 * vgl->region_count > vgl->subpicture_buffer_object_count) {
+        if (vgl->subpicture_buffer_object_count > 0)
+            vgl->DeleteBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
+        vgl->subpicture_buffer_object_count = 0;
+
+        int new_count = 2 * vgl->region_count;
+        vgl->subpicture_buffer_object = realloc_or_free(vgl->subpicture_buffer_object, new_count * sizeof(GLuint));
+        if (!vgl->subpicture_buffer_object) {
+            vlc_gl_Unlock(vgl->gl);
+            return VLC_ENOMEM;
+        }
+
+        vgl->subpicture_buffer_object_count = new_count;
+        vgl->GenBuffers(vgl->subpicture_buffer_object_count, vgl->subpicture_buffer_object);
+    }
+#endif
+
     glActiveTexture(GL_TEXTURE0 + 0);
     glClientActiveTexture(GL_TEXTURE0 + 0);
     for (int i = 0; i < vgl->region_count; i++) {
@@ -1119,10 +1293,19 @@ int vout_display_opengl_Display(vout_display_opengl_t *vgl,
         if (vgl->program[1]) {
 #ifdef SUPPORTS_SHADERS
             vgl->Uniform4f(vgl->GetUniformLocation(vgl->program[1], "FillColor"), 1.0f, 1.0f, 1.0f, glr->alpha);
+
+            vgl->BindBuffer(GL_ARRAY_BUFFER, vgl->subpicture_buffer_object[2 * i]);
+            vgl->BufferData(GL_ARRAY_BUFFER, sizeof(textureCoord), textureCoord, GL_STATIC_DRAW);
             vgl->EnableVertexAttribArray(vgl->GetAttribLocation(vgl->program[1], "MultiTexCoord0"));
-            vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[1], "MultiTexCoord0"), 2, GL_FLOAT, 0, 0, textureCoord);
+            vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[1], "MultiTexCoord0"), 2, GL_FLOAT, 0, 0, 0);
+
+            vgl->BindBuffer(GL_ARRAY_BUFFER, vgl->subpicture_buffer_object[2 * i + 1]);
+            vgl->BufferData(GL_ARRAY_BUFFER, sizeof(vertexCoord), vertexCoord, GL_STATIC_DRAW);
             vgl->EnableVertexAttribArray(vgl->GetAttribLocation(vgl->program[1], "VertexPosition"));
-            vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[1], "VertexPosition"), 2, GL_FLOAT, 0, 0, vertexCoord);
+            vgl->VertexAttribPointer(vgl->GetAttribLocation(vgl->program[1], "VertexPosition"), 2, GL_FLOAT, 0, 0, 0);
+
+            // Subpictures have the correct orientation:
+            vgl->UniformMatrix4fv(vgl->GetUniformLocation(vgl->program[1], "RotationMatrix"), 1, GL_FALSE, identity);
 #endif
         } else {
 #ifdef SUPPORTS_FIXED_PIPELINE