dront78 implemented YUV texture support for OpenGL ES 2.0
authorSam Lantinga <slouken@libsdl.org>
Sat, 07 Jun 2014 11:36:08 -0700
changeset 8835 bc5ec1b6904c
parent 8834 b300e097899d
child 8836 ab4cb2acb4d1
dront78 implemented YUV texture support for OpenGL ES 2.0
src/render/opengles2/SDL_render_gles2.c
src/render/opengles2/SDL_shaders_gles2.c
src/render/opengles2/SDL_shaders_gles2.h
--- a/src/render/opengles2/SDL_render_gles2.c	Fri Jun 06 18:33:17 2014 -0300
+++ b/src/render/opengles2/SDL_render_gles2.c	Sat Jun 07 11:36:08 2014 -0700
@@ -79,6 +79,10 @@
     GLenum pixel_type;
     void *pixel_data;
     size_t pitch;
+    /* YV12 texture support */
+    SDL_bool yuv;
+    GLenum texture_v;
+    GLenum texture_u;
     GLES2_FBOList *fbo;
 } GLES2_TextureData;
 
@@ -133,7 +137,9 @@
     GLES2_UNIFORM_PROJECTION,
     GLES2_UNIFORM_TEXTURE,
     GLES2_UNIFORM_MODULATION,
-    GLES2_UNIFORM_COLOR
+    GLES2_UNIFORM_COLOR,
+    GLES2_UNIFORM_TEXTURE_U,
+    GLES2_UNIFORM_TEXTURE_V
 } GLES2_Uniform;
 
 typedef enum
@@ -142,7 +148,8 @@
     GLES2_IMAGESOURCE_TEXTURE_ABGR,
     GLES2_IMAGESOURCE_TEXTURE_ARGB,
     GLES2_IMAGESOURCE_TEXTURE_RGB,
-    GLES2_IMAGESOURCE_TEXTURE_BGR
+    GLES2_IMAGESOURCE_TEXTURE_BGR,
+    GLES2_IMAGESOURCE_TEXTURE_YUV
 } GLES2_ImageSource;
 
 typedef struct GLES2_DriverContext
@@ -433,6 +440,11 @@
 static int GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture);
 static int GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
                                const void *pixels, int pitch);
+static int GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
+                               const SDL_Rect * rect,
+                               const Uint8 *Yplane, int Ypitch,
+                               const Uint8 *Uplane, int Upitch,
+                               const Uint8 *Vplane, int Vpitch);
 static int GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
                              void **pixels, int *pitch);
 static void GLES2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture);
@@ -472,6 +484,11 @@
         format = GL_RGBA;
         type = GL_UNSIGNED_BYTE;
         break;
+    case SDL_PIXELFORMAT_IYUV:
+    case SDL_PIXELFORMAT_YV12:
+        format = GL_LUMINANCE;
+        type = GL_UNSIGNED_BYTE;
+        break;
     default:
         return SDL_SetError("Texture format not supported");
     }
@@ -485,12 +502,21 @@
     data->texture_type = GL_TEXTURE_2D;
     data->pixel_format = format;
     data->pixel_type = type;
+    data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12));
+    data->texture_u = 0;
+    data->texture_v = 0;
     scaleMode = GetScaleQuality();
 
     /* Allocate a blob for image renderdata */
     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
+        size_t size;
         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
-        data->pixel_data = SDL_calloc(1, data->pitch * texture->h);
+        size = texture->h * data->pitch;
+        if (data->yuv) {
+            /* Need to add size for the U and V planes */
+            size += (2 * (texture->h * data->pitch) / 4);
+        }
+        data->pixel_data = SDL_calloc(1, size);
         if (!data->pixel_data) {
             SDL_free(data);
             return SDL_OutOfMemory();
@@ -499,11 +525,42 @@
 
     /* Allocate the texture */
     GL_CheckError("", renderer);
+
+    if (data->yuv) {
+        renderdata->glGenTextures(1, &data->texture_v);
+        if (GL_CheckError("glGenTexures()", renderer) < 0) {
+            return -1;
+        }
+        renderdata->glActiveTexture(GL_TEXTURE2);
+        renderdata->glBindTexture(data->texture_type, data->texture_v);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        renderdata->glTexImage2D(data->texture_type, 0, format, texture->w / 2, texture->h / 2, 0, format, type, NULL);
+
+        renderdata->glGenTextures(1, &data->texture_u);
+        if (GL_CheckError("glGenTexures()", renderer) < 0) {
+            return -1;
+        }
+        renderdata->glActiveTexture(GL_TEXTURE1);
+        renderdata->glBindTexture(data->texture_type, data->texture_u);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        renderdata->glTexImage2D(data->texture_type, 0, format, texture->w / 2, texture->h / 2, 0, format, type, NULL);
+        if (GL_CheckError("glTexImage2D()", renderer) < 0) {
+            return -1;
+        }
+    }
+
     renderdata->glGenTextures(1, &data->texture);
     if (GL_CheckError("glGenTexures()", renderer) < 0) {
         return -1;
     }
     texture->driverdata = data;
+    renderdata->glActiveTexture(GL_TEXTURE0);
     renderdata->glBindTexture(data->texture_type, data->texture);
     renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);
     renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);
@@ -575,6 +632,53 @@
 }
 
 static int
+GLES2_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
+                    const SDL_Rect * rect,
+                    const Uint8 *Yplane, int Ypitch,
+                    const Uint8 *Uplane, int Upitch,
+                    const Uint8 *Vplane, int Vpitch)
+{
+    GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;
+    GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
+
+    data->glBindTexture(tdata->texture_type, tdata->texture_v);
+    data->glTexSubImage2D(tdata->texture_type,
+                    0,
+                    rect->x,
+                    rect->y,
+                    rect->w / 2,
+                    rect->h / 2,
+                    tdata->pixel_format,
+                    tdata->pixel_type,
+                    Vplane);
+
+    data->glBindTexture(tdata->texture_type, tdata->texture_u);
+    data->glTexSubImage2D(tdata->texture_type,
+                    0,
+                    rect->x,
+                    rect->y,
+                    rect->w / 2,
+                    rect->h / 2,
+                    tdata->pixel_format,
+                    tdata->pixel_type,
+                    Uplane);
+
+    data->glBindTexture(tdata->texture_type, tdata->texture);
+    data->glTexSubImage2D(tdata->texture_type,
+                    0,
+                    rect->x,
+                    rect->y,
+                    rect->w,
+                    rect->h,
+                    tdata->pixel_format,
+                    tdata->pixel_type,
+                    Yplane);
+
+
+    return GL_CheckError("glTexSubImage2D()", renderer);
+}
+
+static int
 GLES2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
                   void **pixels, int *pitch)
 {
@@ -638,6 +742,12 @@
     if (tdata)
     {
         data->glDeleteTextures(1, &tdata->texture);
+        if (tdata->texture_v) {
+            data->glDeleteTextures(1, &tdata->texture_v);
+        }
+        if (tdata->texture_u) {
+            data->glDeleteTextures(1, &tdata->texture_u);
+        }
         SDL_free(tdata->pixel_data);
         SDL_free(tdata);
         texture->driverdata = NULL;
@@ -723,6 +833,10 @@
     /* Predetermine locations of uniform variables */
     entry->uniform_locations[GLES2_UNIFORM_PROJECTION] =
         data->glGetUniformLocation(entry->id, "u_projection");
+    entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V] =
+        data->glGetUniformLocation(entry->id, "u_texture_v");
+    entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U] =
+        data->glGetUniformLocation(entry->id, "u_texture_u");
     entry->uniform_locations[GLES2_UNIFORM_TEXTURE] =
         data->glGetUniformLocation(entry->id, "u_texture");
     entry->uniform_locations[GLES2_UNIFORM_MODULATION] =
@@ -734,8 +848,10 @@
     entry->color_r = entry->color_g = entry->color_b = entry->color_a = 255;
 
     data->glUseProgram(entry->id);
+    data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_V], 2);  /* always texture unit 2. */
+    data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE_U], 1);  /* always texture unit 1. */
+    data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE], 0);  /* always texture unit 0. */
     data->glUniformMatrix4fv(entry->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)entry->projection);
-    data->glUniform1i(entry->uniform_locations[GLES2_UNIFORM_TEXTURE], 0);  /* always texture unit 0. */
     data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_MODULATION], 1.0f, 1.0f, 1.0f, 1.0f);
     data->glUniform4f(entry->uniform_locations[GLES2_UNIFORM_COLOR], 1.0f, 1.0f, 1.0f, 1.0f);
 
@@ -927,6 +1043,9 @@
     case GLES2_IMAGESOURCE_TEXTURE_BGR:
         ftype = GLES2_SHADER_FRAGMENT_TEXTURE_BGR_SRC;
         break;
+    case GLES2_IMAGESOURCE_TEXTURE_YUV:
+        ftype = GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC;
+        break;
     default:
         goto fault;
     }
@@ -1358,6 +1477,11 @@
             case SDL_PIXELFORMAT_RGB888:
                 sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB;
                 break;
+            // TODO: new shader to change yv planes YV12 format
+            case SDL_PIXELFORMAT_IYUV:
+            case SDL_PIXELFORMAT_YV12:
+                sourceType = GLES2_IMAGESOURCE_TEXTURE_YUV;
+                break;
             default:
                 return -1;
         }
@@ -1368,6 +1492,15 @@
     }
 
     /* Select the target texture */
+    if (tdata->yuv) {
+        data->glActiveTexture(GL_TEXTURE2);
+        data->glBindTexture(tdata->texture_type, tdata->texture_v);
+
+        data->glActiveTexture(GL_TEXTURE1);
+        data->glBindTexture(tdata->texture_type, tdata->texture_u);
+
+        data->glActiveTexture(GL_TEXTURE0);
+    }
     data->glBindTexture(tdata->texture_type, tdata->texture);
 
     /* Configure color modulation */
@@ -1869,6 +2002,7 @@
     renderer->WindowEvent         = &GLES2_WindowEvent;
     renderer->CreateTexture       = &GLES2_CreateTexture;
     renderer->UpdateTexture       = &GLES2_UpdateTexture;
+    renderer->UpdateTextureYUV    = &GLES2_UpdateTextureYUV;
     renderer->LockTexture         = &GLES2_LockTexture;
     renderer->UnlockTexture       = &GLES2_UnlockTexture;
     renderer->SetRenderTarget     = &GLES2_SetRenderTarget;
@@ -1887,6 +2021,9 @@
     renderer->GL_BindTexture      = &GLES2_BindTexture;
     renderer->GL_UnbindTexture    = &GLES2_UnbindTexture;
 
+    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
+    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
+
     GLES2_ResetState(renderer);
 
     return renderer;
--- a/src/render/opengles2/SDL_shaders_gles2.c	Fri Jun 06 18:33:17 2014 -0300
+++ b/src/render/opengles2/SDL_shaders_gles2.c	Sat Jun 07 11:36:08 2014 -0700
@@ -126,6 +126,30 @@
     } \
 ";
 
+/* YUV to ABGR conversion */
+static const Uint8 GLES2_FragmentSrc_TextureYUVSrc_[] = " \
+    precision mediump float; \
+    uniform sampler2D u_texture; \
+    uniform sampler2D u_texture_u; \
+    uniform sampler2D u_texture_v; \
+    uniform vec4 u_modulation; \
+    varying vec2 v_texCoord; \
+    \
+    void main() \
+    { \
+        mediump vec3 yuv; \
+        lowp vec3 rgb; \
+        yuv.x = texture2D(u_texture,   v_texCoord).r; \
+        yuv.y = texture2D(u_texture_u, v_texCoord).r - 0.5; \
+        yuv.z = texture2D(u_texture_v, v_texCoord).r - 0.5; \
+        rgb = mat3( 1,        1,       1, \
+                    0,       -0.39465, 2.03211, \
+                    1.13983, -0.58060, 0) * yuv; \
+        gl_FragColor = vec4(rgb, 1); \
+        gl_FragColor *= u_modulation; \
+    } \
+";
+
 static const GLES2_ShaderInstance GLES2_VertexSrc_Default = {
     GL_VERTEX_SHADER,
     GLES2_SOURCE_SHADER,
@@ -168,6 +192,14 @@
     GLES2_FragmentSrc_TextureBGRSrc_
 };
 
+static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureYUVSrc = {
+    GL_FRAGMENT_SHADER,
+    GLES2_SOURCE_SHADER,
+    sizeof(GLES2_FragmentSrc_TextureYUVSrc_),
+    GLES2_FragmentSrc_TextureYUVSrc_
+};
+
+
 /*************************************************************************************************
  * Vertex/fragment shader binaries (NVIDIA Tegra 1/2)                                            *
  *************************************************************************************************/
@@ -692,6 +724,14 @@
     }
 };
 
+static GLES2_Shader GLES2_FragmentShader_TextureYUVSrc = {
+    1,
+    {
+        &GLES2_FragmentSrc_TextureYUVSrc
+    }
+};
+
+
 /*************************************************************************************************
  * Shader selector                                                                               *
  *************************************************************************************************/
@@ -774,6 +814,11 @@
         default:
             return NULL;
     }
+    
+    case GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC:
+    {
+        return &GLES2_FragmentShader_TextureYUVSrc;
+    }
 
     default:
         return NULL;
--- a/src/render/opengles2/SDL_shaders_gles2.h	Fri Jun 06 18:33:17 2014 -0300
+++ b/src/render/opengles2/SDL_shaders_gles2.h	Sat Jun 07 11:36:08 2014 -0700
@@ -46,7 +46,8 @@
     GLES2_SHADER_FRAGMENT_TEXTURE_ABGR_SRC,
     GLES2_SHADER_FRAGMENT_TEXTURE_ARGB_SRC,
     GLES2_SHADER_FRAGMENT_TEXTURE_BGR_SRC,
-    GLES2_SHADER_FRAGMENT_TEXTURE_RGB_SRC
+    GLES2_SHADER_FRAGMENT_TEXTURE_RGB_SRC,
+    GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC
 } GLES2_ShaderType;
 
 #define GLES2_SOURCE_SHADER (GLenum)-1