Initial pass at shader YV12 support - doesn't quite work yet.
authorSam Lantinga <slouken@libsdl.org>
Sat, 12 Feb 2011 00:25:02 -0800
changeset 5264 7ace5f8f432f
parent 5263 e1122f31fec5
child 5265 48724afcdc6e
Initial pass at shader YV12 support - doesn't quite work yet.
include/SDL_pixels.h
src/render/opengl/SDL_render_gl.c
test/testoverlay2.c
--- a/include/SDL_pixels.h	Fri Feb 11 23:02:35 2011 -0800
+++ b/include/SDL_pixels.h	Sat Feb 12 00:25:02 2011 -0800
@@ -124,9 +124,7 @@
 #define SDL_BITSPERPIXEL(X)	(((X) >> 8) & 0xFF)
 #define SDL_BYTESPERPIXEL(X) \
     (SDL_ISPIXELFORMAT_FOURCC(X) ? \
-        ((((X) == SDL_PIXELFORMAT_YV12) || \
-          ((X) == SDL_PIXELFORMAT_IYUV) || \
-          ((X) == SDL_PIXELFORMAT_YUY2) || \
+        ((((X) == SDL_PIXELFORMAT_YUY2) || \
           ((X) == SDL_PIXELFORMAT_UYVY) || \
           ((X) == SDL_PIXELFORMAT_YVYU)) ? 2 : 1) : (((X) >> 0) & 0xFF))
 
--- a/src/render/opengl/SDL_render_gl.c	Fri Feb 11 23:02:35 2011 -0800
+++ b/src/render/opengl/SDL_render_gl.c	Sat Feb 12 00:25:02 2011 -0800
@@ -119,6 +119,11 @@
     void *pixels;
     int pitch;
     SDL_Rect locked_rect;
+
+    /* YV12 texture support */
+    SDL_bool yuv;
+    GLuint utexture;
+    GLuint vtexture;
 } GL_TextureData;
 
 
@@ -292,12 +297,11 @@
     SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
                 data->shaders ? "ENABLED" : "DISABLED");
 
-#if 0
     /* We support YV12 textures using 3 textures and a shader */
     if (data->shaders && data->num_texture_units >= 3) {
         renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
+        renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
     }
-#endif
 
     /* Set up parameters for rendering */
     data->blendMode = -1;
@@ -372,6 +376,12 @@
         *format = GL_BGRA;
         *type = GL_UNSIGNED_INT_8_8_8_8_REV;
         break;
+    case SDL_PIXELFORMAT_YV12:
+    case SDL_PIXELFORMAT_IYUV:
+        *internalFormat = GL_LUMINANCE;
+        *format = GL_LUMINANCE;
+        *type = GL_UNSIGNED_BYTE;
+        break;
     default:
         return SDL_FALSE;
     }
@@ -404,8 +414,15 @@
     }
 
     if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
+        size_t size;
         data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
-        data->pixels = SDL_malloc(texture->h * data->pitch);
+        size = texture->h * data->pitch;
+        if (texture->format == SDL_PIXELFORMAT_YV12 ||
+            texture->format == SDL_PIXELFORMAT_IYUV) {
+            /* Need to add size for the U and V planes */
+            size += (2 * (texture->h * data->pitch) / 4);
+        }
+        data->pixels = SDL_malloc(size);
         if (!data->pixels) {
             SDL_OutOfMemory();
             SDL_free(data);
@@ -478,6 +495,41 @@
         GL_SetError("glTexImage2D()", result);
         return -1;
     }
+
+    if (texture->format == SDL_PIXELFORMAT_YV12 ||
+        texture->format == SDL_PIXELFORMAT_IYUV) {
+        data->yuv = SDL_TRUE;
+
+        renderdata->glGenTextures(1, &data->utexture);
+        renderdata->glGenTextures(1, &data->vtexture);
+        renderdata->glEnable(data->type);
+
+        renderdata->glBindTexture(data->type, data->utexture);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
+                                    GL_LINEAR);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
+                                    GL_LINEAR);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
+                                    GL_CLAMP_TO_EDGE);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
+                                    GL_CLAMP_TO_EDGE);
+        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
+                                 texture_h/2, 0, format, type, NULL);
+
+        renderdata->glBindTexture(data->type, data->vtexture);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
+                                    GL_LINEAR);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
+                                    GL_LINEAR);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
+                                    GL_CLAMP_TO_EDGE);
+        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
+                                    GL_CLAMP_TO_EDGE);
+        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
+                                 texture_h/2, 0, format, type, NULL);
+
+        renderdata->glDisable(data->type);
+    }
     return 0;
 }
 
@@ -500,6 +552,35 @@
     renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
                                 rect->h, data->format, data->formattype,
                                 pixels);
+    if (data->yuv) {
+        /* Skip to the top of the next texture */
+        const void *top = (const void*)((const Uint8*)pixels + (texture->h-rect->y) * pitch - rect->x);
+
+        /* Skip to the correct offset into the next texture */
+        pixels = (const void*)((const Uint8*)top + (rect->y / 2) * pitch + rect->x / 2);
+        if (texture->format == SDL_PIXELFORMAT_YV12) {
+            renderdata->glBindTexture(data->type, data->vtexture);
+        } else {
+            renderdata->glBindTexture(data->type, data->utexture);
+        }
+        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+                                    rect->w/2, rect->h/2,
+                                    data->format, data->formattype, pixels);
+
+        /* Skip to the top of the next texture */
+        top = (const void*)((const Uint8*)top + (texture->h * pitch)/4);
+
+        /* Skip to the correct offset into the next texture */
+        pixels = (const void*)((const Uint8*)top + (rect->y / 2) * pitch + rect->x / 2);
+        if (texture->format == SDL_PIXELFORMAT_YV12) {
+            renderdata->glBindTexture(data->type, data->utexture);
+        } else {
+            renderdata->glBindTexture(data->type, data->vtexture);
+        }
+        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
+                                    rect->w/2, rect->h/2,
+                                    data->format, data->formattype, pixels);
+    }
     renderdata->glDisable(data->type);
     result = renderdata->glGetError();
     if (result != GL_NO_ERROR) {
@@ -750,6 +831,13 @@
     maxv *= texturedata->texh;
 
     data->glEnable(texturedata->type);
+    if (texturedata->yuv) {
+        data->glActiveTextureARB(GL_TEXTURE2_ARB);
+        data->glBindTexture(texturedata->type, texturedata->vtexture);
+        data->glActiveTextureARB(GL_TEXTURE1_ARB);
+        data->glBindTexture(texturedata->type, texturedata->utexture);
+        data->glActiveTextureARB(GL_TEXTURE0_ARB);
+    }
     data->glBindTexture(texturedata->type, texturedata->texture);
 
     if (texture->modMode) {
@@ -762,7 +850,11 @@
     }
 
     GL_SetBlendMode(data, texture->blendMode);
-    GL_SelectShader(data->shaders, SHADER_RGB);
+    if (texturedata->yuv) {
+        GL_SelectShader(data->shaders, SHADER_YV12);
+    } else {
+        GL_SelectShader(data->shaders, SHADER_RGB);
+    }
 
     data->glBegin(GL_TRIANGLE_STRIP);
     data->glTexCoord2f(minu, minv);
@@ -848,6 +940,10 @@
     if (data->texture) {
         renderdata->glDeleteTextures(1, &data->texture);
     }
+    if (data->yuv) {
+        renderdata->glDeleteTextures(1, &data->utexture);
+        renderdata->glDeleteTextures(1, &data->vtexture);
+    }
     if (data->pixels) {
         SDL_free(data->pixels);
     }
--- a/test/testoverlay2.c	Fri Feb 11 23:02:35 2011 -0800
+++ b/test/testoverlay2.c	Sat Feb 12 00:25:02 2011 -0800
@@ -360,7 +360,7 @@
     int fps = 12;
     int fpsdelay;
     int nodelay = 0;
-    int overlay_format = SDL_YUY2_OVERLAY;
+    Uint32 pixel_format = SDL_PIXELFORMAT_YV12;
     int scale = 5;
     SDL_bool done = SDL_FALSE;
 
@@ -397,15 +397,15 @@
         } else if (strcmp(argv[1], "-format") == 0) {
             if (argv[2]) {
                 if (!strcmp(argv[2], "YV12"))
-                    overlay_format = SDL_YV12_OVERLAY;
+                    pixel_format = SDL_PIXELFORMAT_YV12;
                 else if (!strcmp(argv[2], "IYUV"))
-                    overlay_format = SDL_IYUV_OVERLAY;
+                    pixel_format = SDL_PIXELFORMAT_IYUV;
                 else if (!strcmp(argv[2], "YUY2"))
-                    overlay_format = SDL_YUY2_OVERLAY;
+                    pixel_format = SDL_PIXELFORMAT_YUY2;
                 else if (!strcmp(argv[2], "UYVY"))
-                    overlay_format = SDL_UYVY_OVERLAY;
+                    pixel_format = SDL_PIXELFORMAT_UYVY;
                 else if (!strcmp(argv[2], "YVYU"))
-                    overlay_format = SDL_YVYU_OVERLAY;
+                    pixel_format = SDL_PIXELFORMAT_YVYU;
                 else {
                     fprintf(stderr,
                             "The -format option %s is not recognized, see help for info.\n",
@@ -490,7 +490,7 @@
         quit(4);
     }
 
-    MooseTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, MOOSEPIC_W, MOOSEPIC_H);
+    MooseTexture = SDL_CreateTexture(renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, MOOSEPIC_W, MOOSEPIC_H);
     if (!MooseTexture) {
         fprintf(stderr, "Couldn't set create texture: %s\n", SDL_GetError());
         free(RawMooseData);
@@ -569,7 +569,7 @@
         if (!paused) {
             i = (i + 1) % MOOSEFRAMES_COUNT;
 
-            SDL_UpdateTexture(MooseTexture, NULL, MooseFrame[i], MOOSEPIC_W*2);
+            SDL_UpdateTexture(MooseTexture, NULL, MooseFrame[i], MOOSEPIC_W*SDL_BYTESPERPIXEL(pixel_format));
         }
         SDL_RenderClear(renderer);
         SDL_RenderCopy(renderer, MooseTexture, NULL, &displayrect);