From 9fb2cc10c098566b3ecf89d9579f7f6d779a697f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 7 Jun 2014 11:36:08 -0700 Subject: [PATCH] dront78 implemented YUV texture support for OpenGL ES 2.0 --- src/render/opengles2/SDL_render_gles2.c | 145 ++++++++++++++++++++++- src/render/opengles2/SDL_shaders_gles2.c | 45 +++++++ src/render/opengles2/SDL_shaders_gles2.h | 3 +- 3 files changed, 188 insertions(+), 5 deletions(-) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 2671973f1f810..0d0131bf014d0 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -79,6 +79,10 @@ typedef struct GLES2_TextureData 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 @@ typedef enum 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 @@ typedef enum 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 @@ GLES2_DestroyRenderer(SDL_Renderer *renderer) 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 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) 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 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) 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 @@ GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) /* 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); @@ -574,6 +631,53 @@ GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect return GL_CheckError("glTexSubImage2D()", renderer); } +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 @@ GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) 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 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, /* 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 @@ GLES2_CacheProgram(SDL_Renderer *renderer, GLES2_ShaderCacheEntry *vertex, entry->color_r = entry->color_g = entry->color_b = entry->color_a = 255; data->glUseProgram(entry->id); - data->glUniformMatrix4fv(entry->uniform_locations[GLES2_UNIFORM_PROJECTION], 1, GL_FALSE, (GLfloat *)entry->projection); + 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->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 @@ GLES2_SelectProgram(SDL_Renderer *renderer, GLES2_ImageSource source, SDL_BlendM 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 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s 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 @@ GLES2_RenderCopy(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *s } /* 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 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) 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 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags) 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; diff --git a/src/render/opengles2/SDL_shaders_gles2.c b/src/render/opengles2/SDL_shaders_gles2.c index 3b16cc3dfa215..bdc14aeda0d61 100644 --- a/src/render/opengles2/SDL_shaders_gles2.c +++ b/src/render/opengles2/SDL_shaders_gles2.c @@ -126,6 +126,30 @@ static const Uint8 GLES2_FragmentSrc_TextureBGRSrc_[] = " \ } \ "; +/* 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 @@ static const GLES2_ShaderInstance GLES2_FragmentSrc_TextureBGRSrc = { 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_Modulated_TextureBGRSrc = { } }; +static GLES2_Shader GLES2_FragmentShader_TextureYUVSrc = { + 1, + { + &GLES2_FragmentSrc_TextureYUVSrc + } +}; + + /************************************************************************************************* * Shader selector * *************************************************************************************************/ @@ -774,6 +814,11 @@ const GLES2_Shader *GLES2_GetShader(GLES2_ShaderType type, SDL_BlendMode blendMo default: return NULL; } + + case GLES2_SHADER_FRAGMENT_TEXTURE_YUV_SRC: + { + return &GLES2_FragmentShader_TextureYUVSrc; + } default: return NULL; diff --git a/src/render/opengles2/SDL_shaders_gles2.h b/src/render/opengles2/SDL_shaders_gles2.h index 77c75c9f1761a..51bbbcc401d8e 100644 --- a/src/render/opengles2/SDL_shaders_gles2.h +++ b/src/render/opengles2/SDL_shaders_gles2.h @@ -46,7 +46,8 @@ typedef enum 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