Mercurial > sdl-ios-xcode
view src/video/SDL_renderer_gl.c @ 5145:c8e049de174c
Making the API simpler, the renderer present semantics are always having a backbuffer and then discarding it. This is best for hardware accelerated rendering.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Tue, 01 Feb 2011 12:19:46 -0800 |
parents | 31e7f523ab3d |
children | ad50b3db78bd |
line wrap: on
line source
/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2010 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" #if SDL_VIDEO_RENDER_OGL #include "SDL_video.h" #include "SDL_opengl.h" #include "SDL_sysvideo.h" #include "SDL_pixels_c.h" #include "SDL_rect_c.h" #include "SDL_yuv_sw_c.h" #ifdef __MACOSX__ #include <OpenGL/OpenGL.h> #endif /* OpenGL renderer implementation */ /* Details on optimizing the texture path on Mac OS X: http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/chapter_10_section_2.html */ /* !!! FIXME: this should go in a higher level than the GL renderer. */ static __inline__ int bytes_per_pixel(const Uint32 format) { if (!SDL_ISPIXELFORMAT_FOURCC(format)) { return SDL_BYTESPERPIXEL(format); } /* FOURCC format */ switch (format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_YUY2: case SDL_PIXELFORMAT_UYVY: case SDL_PIXELFORMAT_YVYU: return 2; default: return 1; /* shouldn't ever hit this. */ } } static const float inv255f = 1.0f / 255.0f; static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags); static int GL_ActivateRenderer(SDL_Renderer * renderer); static int GL_DisplayModeChanged(SDL_Renderer * renderer); static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); static int GL_QueryTexturePixels(SDL_Renderer * renderer, SDL_Texture * texture, void **pixels, int *pitch); static int GL_SetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Color * colors, int firstcolor, int ncolors); static int GL_GetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture, SDL_Color * colors, int firstcolor, int ncolors); static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch); static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, int markDirty, void **pixels, int *pitch); static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture); static void GL_DirtyTexture(SDL_Renderer * renderer, SDL_Texture * texture, int numrects, const SDL_Rect * rects); static int GL_RenderClear(SDL_Renderer * renderer); static int GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points, int count); static int GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points, int count); static int GL_RenderDrawRects(SDL_Renderer * renderer, const SDL_Rect ** rects, int count); static int GL_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect ** rects, int count); static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect); static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, void * pixels, int pitch); static int GL_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, const void * pixels, int pitch); static void GL_RenderPresent(SDL_Renderer * renderer); static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture); static void GL_DestroyRenderer(SDL_Renderer * renderer); SDL_RenderDriver GL_RenderDriver = { GL_CreateRenderer, { "opengl", (SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED), 15, { SDL_PIXELFORMAT_INDEX1LSB, SDL_PIXELFORMAT_INDEX1MSB, SDL_PIXELFORMAT_INDEX8, SDL_PIXELFORMAT_RGB332, SDL_PIXELFORMAT_RGB444, SDL_PIXELFORMAT_RGB555, SDL_PIXELFORMAT_ARGB4444, SDL_PIXELFORMAT_ARGB1555, SDL_PIXELFORMAT_RGB565, SDL_PIXELFORMAT_RGB24, SDL_PIXELFORMAT_BGR24, SDL_PIXELFORMAT_RGB888, SDL_PIXELFORMAT_BGR888, SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888, SDL_PIXELFORMAT_ARGB2101010}, 0, 0} }; typedef struct { SDL_GLContext context; SDL_bool updateSize; SDL_bool GL_ARB_texture_rectangle_supported; SDL_bool GL_EXT_paletted_texture_supported; SDL_bool GL_APPLE_ycbcr_422_supported; SDL_bool GL_MESA_ycbcr_texture_supported; SDL_bool GL_ARB_fragment_program_supported; int blendMode; /* OpenGL functions */ #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params; #include "SDL_glfuncs.h" #undef SDL_PROC PFNGLCOLORTABLEEXTPROC glColorTableEXT; void (*glTextureRangeAPPLE) (GLenum target, GLsizei length, const GLvoid * pointer); PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; PFNGLGENPROGRAMSARBPROC glGenProgramsARB; PFNGLBINDPROGRAMARBPROC glBindProgramARB; PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; /* (optional) fragment programs */ GLuint fragment_program_UYVY; } GL_RenderData; typedef struct { GLuint texture; GLuint shader; GLenum type; GLfloat texw; GLfloat texh; GLenum format; GLenum formattype; Uint8 *palette; void *pixels; int pitch; SDL_DirtyRectList dirty; int HACK_RYAN_FIXME; } GL_TextureData; static void GL_SetError(const char *prefix, GLenum result) { const char *error; switch (result) { case GL_NO_ERROR: error = "GL_NO_ERROR"; break; case GL_INVALID_ENUM: error = "GL_INVALID_ENUM"; break; case GL_INVALID_VALUE: error = "GL_INVALID_VALUE"; break; case GL_INVALID_OPERATION: error = "GL_INVALID_OPERATION"; break; case GL_STACK_OVERFLOW: error = "GL_STACK_OVERFLOW"; break; case GL_STACK_UNDERFLOW: error = "GL_STACK_UNDERFLOW"; break; case GL_OUT_OF_MEMORY: error = "GL_OUT_OF_MEMORY"; break; case GL_TABLE_TOO_LARGE: error = "GL_TABLE_TOO_LARGE"; break; default: error = "UNKNOWN"; break; } SDL_SetError("%s: %s", prefix, error); } static int GL_LoadFunctions(GL_RenderData * data) { #if defined(__QNXNTO__) && (_NTO_VERSION < 630) #define __SDL_NOGETPROCADDR__ #endif #ifdef __SDL_NOGETPROCADDR__ #define SDL_PROC(ret,func,params) data->func=func; #else #define SDL_PROC(ret,func,params) \ do { \ data->func = SDL_GL_GetProcAddress(#func); \ if ( ! data->func ) { \ SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \ return -1; \ } \ } while ( 0 ); #endif /* __SDL_NOGETPROCADDR__ */ #include "SDL_glfuncs.h" #undef SDL_PROC return 0; } SDL_Renderer * GL_CreateRenderer(SDL_Window * window, Uint32 flags) { SDL_Renderer *renderer; GL_RenderData *data; GLint value; /* Render directly to the window, unless we're compositing */ #ifndef __MACOSX__ if (flags & SDL_RENDERER_SINGLEBUFFER) { SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0); } #endif if (!(window->flags & SDL_WINDOW_OPENGL)) { if (SDL_RecreateWindow(window, window->flags | SDL_WINDOW_OPENGL) < 0) { return NULL; } } renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); if (!renderer) { SDL_OutOfMemory(); return NULL; } data = (GL_RenderData *) SDL_calloc(1, sizeof(*data)); if (!data) { GL_DestroyRenderer(renderer); SDL_OutOfMemory(); return NULL; } renderer->ActivateRenderer = GL_ActivateRenderer; renderer->DisplayModeChanged = GL_DisplayModeChanged; renderer->CreateTexture = GL_CreateTexture; renderer->QueryTexturePixels = GL_QueryTexturePixels; renderer->SetTexturePalette = GL_SetTexturePalette; renderer->GetTexturePalette = GL_GetTexturePalette; renderer->UpdateTexture = GL_UpdateTexture; renderer->LockTexture = GL_LockTexture; renderer->UnlockTexture = GL_UnlockTexture; renderer->DirtyTexture = GL_DirtyTexture; renderer->RenderClear = GL_RenderClear; renderer->RenderDrawPoints = GL_RenderDrawPoints; renderer->RenderDrawLines = GL_RenderDrawLines; renderer->RenderDrawRects = GL_RenderDrawRects; renderer->RenderFillRects = GL_RenderFillRects; renderer->RenderCopy = GL_RenderCopy; renderer->RenderReadPixels = GL_RenderReadPixels; renderer->RenderWritePixels = GL_RenderWritePixels; renderer->RenderPresent = GL_RenderPresent; renderer->DestroyTexture = GL_DestroyTexture; renderer->DestroyRenderer = GL_DestroyRenderer; renderer->info = GL_RenderDriver.info; renderer->window = window; renderer->driverdata = data; renderer->info.flags = SDL_RENDERER_ACCELERATED; if (GL_LoadFunctions(data) < 0) { GL_DestroyRenderer(renderer); return NULL; } data->context = SDL_GL_CreateContext(window); if (!data->context) { GL_DestroyRenderer(renderer); return NULL; } if (SDL_GL_MakeCurrent(window, data->context) < 0) { GL_DestroyRenderer(renderer); return NULL; } #ifdef __MACOSX__ /* Enable multi-threaded rendering */ /* Disabled until Ryan finishes his VBO/PBO code... CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine); */ #endif if (flags & SDL_RENDERER_PRESENTVSYNC) { SDL_GL_SetSwapInterval(1); } else { SDL_GL_SetSwapInterval(0); } if (SDL_GL_GetSwapInterval() > 0) { renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; } data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); renderer->info.max_texture_width = value; data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); renderer->info.max_texture_height = value; if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) { data->GL_ARB_texture_rectangle_supported = SDL_TRUE; } if (SDL_GL_ExtensionSupported("GL_EXT_paletted_texture")) { data->GL_EXT_paletted_texture_supported = SDL_TRUE; data->glColorTableEXT = (PFNGLCOLORTABLEEXTPROC) SDL_GL_GetProcAddress("glColorTableEXT"); } else { /* Don't advertise support for 8-bit indexed texture format */ Uint32 i, j; SDL_RendererInfo *info = &renderer->info; for (i = 0, j = 0; i < info->num_texture_formats; ++i) { if (info->texture_formats[i] != SDL_PIXELFORMAT_INDEX8) { info->texture_formats[j++] = info->texture_formats[i]; } } --info->num_texture_formats; } if (SDL_GL_ExtensionSupported("GL_APPLE_ycbcr_422")) { data->GL_APPLE_ycbcr_422_supported = SDL_TRUE; } if (SDL_GL_ExtensionSupported("GL_MESA_ycbcr_texture")) { data->GL_MESA_ycbcr_texture_supported = SDL_TRUE; } if (SDL_GL_ExtensionSupported("GL_APPLE_texture_range")) { data->glTextureRangeAPPLE = (void (*)(GLenum, GLsizei, const GLvoid *)) SDL_GL_GetProcAddress("glTextureRangeAPPLE"); } /* we might use fragment programs for YUV data, etc. */ if (SDL_GL_ExtensionSupported("GL_ARB_fragment_program")) { /* !!! FIXME: this doesn't check for errors. */ /* !!! FIXME: this should really reuse the glfuncs.h stuff. */ data->glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) SDL_GL_GetProcAddress("glGetProgramivARB"); data->glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) SDL_GL_GetProcAddress("glGetProgramStringARB"); data->glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) SDL_GL_GetProcAddress("glProgramLocalParameter4fvARB"); data->glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) SDL_GL_GetProcAddress("glDeleteProgramsARB"); data->glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) SDL_GL_GetProcAddress("glGenProgramsARB"); data->glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) SDL_GL_GetProcAddress("glBindProgramARB"); data->glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) SDL_GL_GetProcAddress("glProgramStringARB"); data->GL_ARB_fragment_program_supported = SDL_TRUE; } /* Set up parameters for rendering */ data->blendMode = -1; data->glDisable(GL_DEPTH_TEST); data->glDisable(GL_CULL_FACE); /* This ended up causing video discrepancies between OpenGL and Direct3D */ /*data->glEnable(GL_LINE_SMOOTH);*/ if (data->GL_ARB_texture_rectangle_supported) { data->glEnable(GL_TEXTURE_RECTANGLE_ARB); } else { data->glEnable(GL_TEXTURE_2D); } data->updateSize = SDL_TRUE; return renderer; } static int GL_ActivateRenderer(SDL_Renderer * renderer) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; SDL_Window *window = renderer->window; if (SDL_GL_MakeCurrent(window, data->context) < 0) { return -1; } if (data->updateSize) { data->glMatrixMode(GL_PROJECTION); data->glLoadIdentity(); data->glMatrixMode(GL_MODELVIEW); data->glLoadIdentity(); data->glViewport(0, 0, window->w, window->h); data->glOrtho(0.0, (GLdouble) window->w, (GLdouble) window->h, 0.0, 0.0, 1.0); data->updateSize = SDL_FALSE; } return 0; } static int GL_DisplayModeChanged(SDL_Renderer * renderer) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; /* Rebind the context to the window area and update matrices */ data->updateSize = SDL_TRUE; return GL_ActivateRenderer(renderer); } static __inline__ int power_of_2(int input) { int value = 1; while (value < input) { value <<= 1; } return value; } //#define DEBUG_PROGRAM_COMPILE 1 static void set_shader_error(GL_RenderData * data, const char *prefix) { GLint pos = 0; const GLubyte *errstr; data->glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); errstr = data->glGetString(GL_PROGRAM_ERROR_STRING_ARB); SDL_SetError("%s: shader compile error at position %d: %s", prefix, (int) pos, (const char *) errstr); } static GLuint compile_shader(GL_RenderData * data, GLenum shader_type, const char *_code) { const int have_texture_rects = data->GL_ARB_texture_rectangle_supported; const char *replacement = have_texture_rects ? "RECT" : "2D"; const size_t replacementlen = SDL_strlen(replacement); const char *token = "%TEXTURETARGET%"; const size_t tokenlen = SDL_strlen(token); char *code = NULL; char *ptr = NULL; GLuint program = 0; /* * The TEX instruction needs a different target depending on what we use. * To handle this, we use "%TEXTURETARGET%" and replace the string before * compiling the shader. */ code = SDL_strdup(_code); if (code == NULL) return 0; for (ptr = SDL_strstr(code, token); ptr; ptr = SDL_strstr(ptr + 1, token)) { SDL_memcpy(ptr, replacement, replacementlen); SDL_memmove(ptr + replacementlen, ptr + tokenlen, SDL_strlen(ptr + tokenlen) + 1); } #if DEBUG_PROGRAM_COMPILE printf("compiling shader:\n%s\n\n", code); #endif data->glGetError(); /* flush any existing error state. */ data->glGenProgramsARB(1, &program); data->glBindProgramARB(shader_type, program); data->glProgramStringARB(shader_type, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)SDL_strlen(code), code); SDL_free(code); if (data->glGetError() == GL_INVALID_OPERATION) { #if DEBUG_PROGRAM_COMPILE GLint pos = 0; const GLubyte *errstr; data->glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); errstr = data->glGetString(GL_PROGRAM_ERROR_STRING_ARB); printf("program compile error at position %d: %s\n\n", (int) pos, (const char *) errstr); #endif data->glBindProgramARB(shader_type, 0); data->glDeleteProgramsARB(1, &program); return 0; } return program; } /* * Fragment program that renders from UYVY textures. * The UYVY to RGB equasion is: * R = 1.164(Y-16) + 1.596(Cr-128) * G = 1.164(Y-16) - 0.813(Cr-128) - 0.391(Cb-128) * B = 1.164(Y-16) + 2.018(Cb-128) * Byte layout is Cb, Y1, Cr, Y2, stored in the R, G, B, A channels. * 4 bytes == 2 pixels: Y1/Cb/Cr, Y2/Cb/Cr * * !!! FIXME: this ignores blendmodes, etc. * !!! FIXME: this could be more efficient...use a dot product for green, etc. */ static const char *fragment_program_UYVY_source_code = "!!ARBfp1.0\n" /* outputs... */ "OUTPUT outcolor = result.color;\n" /* scratch registers... */ "TEMP uyvy;\n" "TEMP luminance;\n" "TEMP work;\n" /* Halve the coordinates to grab the correct 32 bits for the fragment. */ "MUL work, fragment.texcoord, { 0.5, 1.0, 1.0, 1.0 };\n" /* Sample the YUV texture. Cb, Y1, Cr, Y2, are stored in x, y, z, w. */ "TEX uyvy, work, texture[0], %TEXTURETARGET%;\n" /* Do subtractions (128/255, 16/255, 128/255, 16/255) */ "SUB uyvy, uyvy, { 0.501960784313726, 0.06274509803922, 0.501960784313726, 0.06274509803922 };\n" /* Choose the luminance component by texcoord. */ /* !!! FIXME: laziness wins out for now... just average Y1 and Y2. */ "ADD luminance, uyvy.yyyy, uyvy.wwww;\n" "MUL luminance, luminance, { 0.5, 0.5, 0.5, 0.5 };\n" /* Multiply luminance by its magic value. */ "MUL luminance, luminance, { 1.164, 1.164, 1.164, 1.164 };\n" /* uyvy.xyzw becomes Cr/Cr/Cb/Cb, with multiplications. */ "MUL uyvy, uyvy.zzxx, { 1.596, -0.813, 2.018, -0.391 };\n" /* Add luminance to Cr and Cb, store to RGB channels. */ "ADD work.rgb, luminance, uyvy;\n" /* Do final addition for Green channel. (!!! FIXME: this should be a DPH?) */ "ADD work.g, work.g, uyvy.w;\n" /* Make sure alpha channel is fully opaque. (!!! FIXME: blend modes!) */ "MOV work.a, { 1.0 };\n" /* Store out the final fragment color... */ "MOV outcolor, work;\n" /* ...and we're done! */ "END\n"; static __inline__ SDL_bool convert_format(GL_RenderData *renderdata, Uint32 pixel_format, GLint* internalFormat, GLenum* format, GLenum* type) { switch (pixel_format) { case SDL_PIXELFORMAT_INDEX1LSB: case SDL_PIXELFORMAT_INDEX1MSB: *internalFormat = GL_RGB; *format = GL_COLOR_INDEX; *type = GL_BITMAP; break; case SDL_PIXELFORMAT_INDEX8: if (!renderdata->GL_EXT_paletted_texture_supported) { return SDL_FALSE; } *internalFormat = GL_COLOR_INDEX8_EXT; *format = GL_COLOR_INDEX; *type = GL_UNSIGNED_BYTE; break; case SDL_PIXELFORMAT_RGB332: *internalFormat = GL_R3_G3_B2; *format = GL_RGB; *type = GL_UNSIGNED_BYTE_3_3_2; break; case SDL_PIXELFORMAT_RGB444: *internalFormat = GL_RGB4; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_4_4_4_4; break; case SDL_PIXELFORMAT_RGB555: *internalFormat = GL_RGB5; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_5_5_1; break; case SDL_PIXELFORMAT_ARGB4444: *internalFormat = GL_RGBA4; *format = GL_BGRA; *type = GL_UNSIGNED_SHORT_4_4_4_4_REV; break; case SDL_PIXELFORMAT_ARGB1555: *internalFormat = GL_RGB5_A1; *format = GL_BGRA; *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; break; case SDL_PIXELFORMAT_RGB565: *internalFormat = GL_RGB8; *format = GL_RGB; *type = GL_UNSIGNED_SHORT_5_6_5; break; case SDL_PIXELFORMAT_RGB24: *internalFormat = GL_RGB8; *format = GL_RGB; *type = GL_UNSIGNED_BYTE; break; case SDL_PIXELFORMAT_RGB888: *internalFormat = GL_RGB8; *format = GL_BGRA; *type = GL_UNSIGNED_BYTE; break; case SDL_PIXELFORMAT_BGR24: *internalFormat = GL_RGB8; *format = GL_BGR; *type = GL_UNSIGNED_BYTE; break; case SDL_PIXELFORMAT_BGR888: *internalFormat = GL_RGB8; *format = GL_RGBA; *type = GL_UNSIGNED_BYTE; break; case SDL_PIXELFORMAT_ARGB8888: #ifdef __MACOSX__ *internalFormat = GL_RGBA; *format = GL_BGRA; *type = GL_UNSIGNED_INT_8_8_8_8_REV; #else *internalFormat = GL_RGBA8; *format = GL_BGRA; *type = GL_UNSIGNED_BYTE; #endif break; case SDL_PIXELFORMAT_ABGR8888: *internalFormat = GL_RGBA8; *format = GL_RGBA; *type = GL_UNSIGNED_BYTE; break; case SDL_PIXELFORMAT_ARGB2101010: *internalFormat = GL_RGB10_A2; *format = GL_BGRA; *type = GL_UNSIGNED_INT_2_10_10_10_REV; break; case SDL_PIXELFORMAT_UYVY: if (renderdata->GL_APPLE_ycbcr_422_supported) { *internalFormat = GL_RGB; *format = GL_YCBCR_422_APPLE; #if SDL_BYTEORDER == SDL_LIL_ENDIAN *type = GL_UNSIGNED_SHORT_8_8_APPLE; #else *type = GL_UNSIGNED_SHORT_8_8_REV_APPLE; #endif } else if (renderdata->GL_MESA_ycbcr_texture_supported) { *internalFormat = GL_YCBCR_MESA; *format = GL_YCBCR_MESA; #if SDL_BYTEORDER == SDL_LIL_ENDIAN *type = GL_UNSIGNED_SHORT_8_8_MESA; #else *type = GL_UNSIGNED_SHORT_8_8_REV_MESA; #endif } else if (renderdata->GL_ARB_fragment_program_supported) { *internalFormat = GL_RGBA; *format = GL_RGBA; *type = GL_UNSIGNED_BYTE; } else { return SDL_FALSE; } break; case SDL_PIXELFORMAT_YUY2: if (renderdata->GL_APPLE_ycbcr_422_supported) { *internalFormat = GL_RGB; *format = GL_YCBCR_422_APPLE; #if SDL_BYTEORDER == SDL_LIL_ENDIAN *type = GL_UNSIGNED_SHORT_8_8_REV_APPLE; #else *type = GL_UNSIGNED_SHORT_8_8_APPLE; #endif } else if (renderdata->GL_MESA_ycbcr_texture_supported) { *internalFormat = GL_YCBCR_MESA; *format = GL_YCBCR_MESA; #if SDL_BYTEORDER == SDL_LIL_ENDIAN *type = GL_UNSIGNED_SHORT_8_8_REV_MESA; #else *type = GL_UNSIGNED_SHORT_8_8_MESA; #endif } else { return SDL_FALSE; } break; default: return SDL_FALSE; } return SDL_TRUE; } static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; GL_TextureData *data; GLint internalFormat; GLenum format, type; int texture_w, texture_h; GLuint shader = 0; GLenum result; if (!convert_format(renderdata, texture->format, &internalFormat, &format, &type)) { SDL_SetError("Texture format %s not supported by OpenGL", SDL_GetPixelFormatName(texture->format)); return -1; } if (texture->format == SDL_PIXELFORMAT_UYVY && !renderdata->GL_APPLE_ycbcr_422_supported && !renderdata->GL_MESA_ycbcr_texture_supported && renderdata->GL_ARB_fragment_program_supported) { if (renderdata->fragment_program_UYVY == 0) { renderdata->fragment_program_UYVY = compile_shader(renderdata, GL_FRAGMENT_PROGRAM_ARB, fragment_program_UYVY_source_code); if (renderdata->fragment_program_UYVY == 0) { set_shader_error(renderdata, "UYVY"); return -1; } } shader = renderdata->fragment_program_UYVY; } data = (GL_TextureData *) SDL_calloc(1, sizeof(*data)); if (!data) { SDL_OutOfMemory(); return -1; } data->shader = shader; if (texture->format == SDL_PIXELFORMAT_INDEX8) { data->palette = (Uint8 *) SDL_malloc(3 * 256 * sizeof(Uint8)); if (!data->palette) { SDL_OutOfMemory(); SDL_free(data); return -1; } SDL_memset(data->palette, 0xFF, 3 * 256 * sizeof(Uint8)); } if (texture->access == SDL_TEXTUREACCESS_STREAMING) { data->pitch = texture->w * bytes_per_pixel(texture->format); data->pixels = SDL_malloc(texture->h * data->pitch); if (!data->pixels) { SDL_OutOfMemory(); SDL_free(data); return -1; } } texture->driverdata = data; renderdata->glGetError(); renderdata->glGenTextures(1, &data->texture); if (renderdata->GL_ARB_texture_rectangle_supported) { data->type = GL_TEXTURE_RECTANGLE_ARB; texture_w = texture->w; texture_h = texture->h; data->texw = (GLfloat) texture_w; data->texh = (GLfloat) texture_h; } else { data->type = GL_TEXTURE_2D; texture_w = power_of_2(texture->w); texture_h = power_of_2(texture->h); data->texw = (GLfloat) (texture->w) / texture_w; data->texh = (GLfloat) texture->h / texture_h; } /* YUV formats use RGBA but are really two bytes per pixel */ if (internalFormat == GL_RGBA && bytes_per_pixel(texture->format) < 4) { texture_w /= 2; if (data->type == GL_TEXTURE_2D) { data->texw *= 2.0f; } data->HACK_RYAN_FIXME = 2; } else { data->HACK_RYAN_FIXME = 1; } data->format = format; data->formattype = type; renderdata->glEnable(data->type); renderdata->glBindTexture(data->type, data->texture); 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); #ifdef __MACOSX__ #ifndef GL_TEXTURE_STORAGE_HINT_APPLE #define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC #endif #ifndef STORAGE_CACHED_APPLE #define STORAGE_CACHED_APPLE 0x85BE #endif #ifndef STORAGE_SHARED_APPLE #define STORAGE_SHARED_APPLE 0x85BF #endif if (texture->access == SDL_TEXTUREACCESS_STREAMING) { renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); } else { renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE); } /* This causes a crash in testoverlay for some reason. Apple bug? */ #if 0 if (texture->access == SDL_TEXTUREACCESS_STREAMING && texture->format == SDL_PIXELFORMAT_ARGB8888) { /* if (renderdata->glTextureRangeAPPLE) { renderdata->glTextureRangeAPPLE(data->type, texture->h * data->pitch, data->pixels); } */ renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w, texture_h, 0, format, type, data->pixels); } else #endif #endif { renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w, texture_h, 0, format, type, NULL); } renderdata->glDisable(data->type); result = renderdata->glGetError(); if (result != GL_NO_ERROR) { GL_SetError("glTexImage2D()", result); return -1; } return 0; } static int GL_QueryTexturePixels(SDL_Renderer * renderer, SDL_Texture * texture, void **pixels, int *pitch) { GL_TextureData *data = (GL_TextureData *) texture->driverdata; *pixels = data->pixels; *pitch = data->pitch; return 0; } static int GL_SetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Color * colors, int firstcolor, int ncolors) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; GL_TextureData *data = (GL_TextureData *) texture->driverdata; Uint8 *palette; if (!data->palette) { SDL_SetError("Texture doesn't have a palette"); return -1; } palette = data->palette + firstcolor * 3; while (ncolors--) { *palette++ = colors->r; *palette++ = colors->g; *palette++ = colors->b; ++colors; } renderdata->glEnable(data->type); renderdata->glBindTexture(data->type, data->texture); renderdata->glColorTableEXT(data->type, GL_RGB8, 256, GL_RGB, GL_UNSIGNED_BYTE, data->palette); return 0; } static int GL_GetTexturePalette(SDL_Renderer * renderer, SDL_Texture * texture, SDL_Color * colors, int firstcolor, int ncolors) { GL_TextureData *data = (GL_TextureData *) texture->driverdata; Uint8 *palette; if (!data->palette) { SDL_SetError("Texture doesn't have a palette"); return -1; } palette = data->palette + firstcolor * 3; while (ncolors--) { colors->r = *palette++; colors->g = *palette++; colors->b = *palette++; colors->unused = SDL_ALPHA_OPAQUE; ++colors; } return 0; } static void SetupTextureUpdate(GL_RenderData * renderdata, SDL_Texture * texture, int pitch) { if (texture->format == SDL_PIXELFORMAT_INDEX1LSB) { renderdata->glPixelStorei(GL_UNPACK_LSB_FIRST, 1); } else if (texture->format == SDL_PIXELFORMAT_INDEX1MSB) { renderdata->glPixelStorei(GL_UNPACK_LSB_FIRST, 0); } renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / bytes_per_pixel(texture->format)) / ((GL_TextureData *) texture->driverdata)-> HACK_RYAN_FIXME); } static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; GL_TextureData *data = (GL_TextureData *) texture->driverdata; GLenum result; renderdata->glGetError(); SetupTextureUpdate(renderdata, texture, pitch); renderdata->glEnable(data->type); renderdata->glBindTexture(data->type, data->texture); renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w, rect->h, data->format, data->formattype, pixels); renderdata->glDisable(data->type); result = renderdata->glGetError(); if (result != GL_NO_ERROR) { GL_SetError("glTexSubImage2D()", result); return -1; } return 0; } static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, int markDirty, void **pixels, int *pitch) { GL_TextureData *data = (GL_TextureData *) texture->driverdata; if (markDirty) { SDL_AddDirtyRect(&data->dirty, rect); } *pixels = (void *) ((Uint8 *) data->pixels + rect->y * data->pitch + rect->x * bytes_per_pixel(texture->format)); *pitch = data->pitch; return 0; } static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) { } static void GL_DirtyTexture(SDL_Renderer * renderer, SDL_Texture * texture, int numrects, const SDL_Rect * rects) { GL_TextureData *data = (GL_TextureData *) texture->driverdata; int i; for (i = 0; i < numrects; ++i) { SDL_AddDirtyRect(&data->dirty, &rects[i]); } } static void GL_SetBlendMode(GL_RenderData * data, int blendMode) { if (blendMode != data->blendMode) { switch (blendMode) { case SDL_BLENDMODE_NONE: data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); data->glDisable(GL_BLEND); break; case SDL_BLENDMODE_BLEND: data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); data->glEnable(GL_BLEND); data->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; case SDL_BLENDMODE_ADD: data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); data->glEnable(GL_BLEND); data->glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; } data->blendMode = blendMode; } } static int GL_RenderClear(SDL_Renderer * renderer) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; data->glClearColor((GLfloat) renderer->r * inv255f, (GLfloat) renderer->g * inv255f, (GLfloat) renderer->b * inv255f, (GLfloat) renderer->a * inv255f); data->glClear(GL_COLOR_BUFFER_BIT); return 0; } static int GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_Point * points, int count) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; int i; GL_SetBlendMode(data, renderer->blendMode); data->glColor4f((GLfloat) renderer->r * inv255f, (GLfloat) renderer->g * inv255f, (GLfloat) renderer->b * inv255f, (GLfloat) renderer->a * inv255f); data->glBegin(GL_POINTS); for (i = 0; i < count; ++i) { data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); } data->glEnd(); return 0; } static int GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_Point * points, int count) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; int i; GL_SetBlendMode(data, renderer->blendMode); data->glColor4f((GLfloat) renderer->r * inv255f, (GLfloat) renderer->g * inv255f, (GLfloat) renderer->b * inv255f, (GLfloat) renderer->a * inv255f); if (count > 2 && points[0].x == points[count-1].x && points[0].y == points[count-1].y) { data->glBegin(GL_LINE_LOOP); /* GL_LINE_LOOP takes care of the final segment */ --count; for (i = 0; i < count; ++i) { data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); } data->glEnd(); } else { #if defined(__APPLE__) || defined(__WIN32__) #else int x1, y1, x2, y2; #endif data->glBegin(GL_LINE_STRIP); for (i = 0; i < count; ++i) { data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y); } data->glEnd(); /* The line is half open, so we need one more point to complete it. * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html * If we have to, we can use vertical line and horizontal line textures * for vertical and horizontal lines, and then create custom textures * for diagonal lines and software render those. It's terrible, but at * least it would be pixel perfect. */ data->glBegin(GL_POINTS); #if defined(__APPLE__) || defined(__WIN32__) /* Mac OS X and Windows seem to always leave the second point open */ data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y); #else /* Linux seems to leave the right-most or bottom-most point open */ x1 = points[0].x; y1 = points[0].y; x2 = points[count-1].x; y2 = points[count-1].y; if (x1 > x2) { data->glVertex2f(0.5f + x1, 0.5f + y1); } else if (x2 > x1) { data->glVertex2f(0.5f + x2, 0.5f + y2); } else if (y1 > y2) { data->glVertex2f(0.5f + x1, 0.5f + y1); } else if (y2 > y1) { data->glVertex2f(0.5f + x2, 0.5f + y2); } #endif data->glEnd(); } return 0; } static int GL_RenderDrawRects(SDL_Renderer * renderer, const SDL_Rect ** rects, int count) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; int i, x, y; GL_SetBlendMode(data, renderer->blendMode); data->glColor4f((GLfloat) renderer->r * inv255f, (GLfloat) renderer->g * inv255f, (GLfloat) renderer->b * inv255f, (GLfloat) renderer->a * inv255f); data->glBegin(GL_LINE_LOOP); for (i = 0; i < count; ++i) { const SDL_Rect *rect = rects[i]; x = rect->x; y = rect->y; data->glVertex2f(0.5f + x, 0.5f + y); x = rect->x+rect->w-1; y = rect->y; data->glVertex2f(0.5f + x, 0.5f + y); x = rect->x+rect->w-1; y = rect->y+rect->h-1; data->glVertex2f(0.5f + x, 0.5f + y); x = rect->x; y = rect->y+rect->h-1; data->glVertex2f(0.5f + x, 0.5f + y); } data->glEnd(); return 0; } static int GL_RenderFillRects(SDL_Renderer * renderer, const SDL_Rect ** rects, int count) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; int i; GL_SetBlendMode(data, renderer->blendMode); data->glColor4f((GLfloat) renderer->r * inv255f, (GLfloat) renderer->g * inv255f, (GLfloat) renderer->b * inv255f, (GLfloat) renderer->a * inv255f); for (i = 0; i < count; ++i) { const SDL_Rect *rect = rects[i]; data->glRecti(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h); } return 0; } static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * srcrect, const SDL_Rect * dstrect) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata; int minx, miny, maxx, maxy; GLfloat minu, maxu, minv, maxv; if (texturedata->dirty.list) { SDL_DirtyRect *dirty; void *pixels; int bpp = bytes_per_pixel(texture->format); int pitch = texturedata->pitch; SetupTextureUpdate(data, texture, pitch); data->glEnable(texturedata->type); data->glBindTexture(texturedata->type, texturedata->texture); for (dirty = texturedata->dirty.list; dirty; dirty = dirty->next) { SDL_Rect *rect = &dirty->rect; pixels = (void *) ((Uint8 *) texturedata->pixels + rect->y * pitch + rect->x * bpp); data->glTexSubImage2D(texturedata->type, 0, rect->x, rect->y, rect->w / texturedata->HACK_RYAN_FIXME, rect->h, texturedata->format, texturedata->formattype, pixels); } SDL_ClearDirtyRects(&texturedata->dirty); } minx = dstrect->x; miny = dstrect->y; maxx = dstrect->x + dstrect->w; maxy = dstrect->y + dstrect->h; minu = (GLfloat) srcrect->x / texture->w; minu *= texturedata->texw; maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w; maxu *= texturedata->texw; minv = (GLfloat) srcrect->y / texture->h; minv *= texturedata->texh; maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h; maxv *= texturedata->texh; data->glEnable(texturedata->type); data->glBindTexture(texturedata->type, texturedata->texture); if (texture->modMode) { data->glColor4f((GLfloat) texture->r * inv255f, (GLfloat) texture->g * inv255f, (GLfloat) texture->b * inv255f, (GLfloat) texture->a * inv255f); } else { data->glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } GL_SetBlendMode(data, texture->blendMode); /* Set up the shader for the copy, if any */ if (texturedata->shader) { data->glEnable(GL_FRAGMENT_PROGRAM_ARB); data->glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, texturedata->shader); } data->glBegin(GL_TRIANGLE_STRIP); data->glTexCoord2f(minu, minv); data->glVertex2f((GLfloat) minx, (GLfloat) miny); data->glTexCoord2f(maxu, minv); data->glVertex2f((GLfloat) maxx, (GLfloat) miny); data->glTexCoord2f(minu, maxv); data->glVertex2f((GLfloat) minx, (GLfloat) maxy); data->glTexCoord2f(maxu, maxv); data->glVertex2f((GLfloat) maxx, (GLfloat) maxy); data->glEnd(); if (texturedata->shader) { data->glDisable(GL_FRAGMENT_PROGRAM_ARB); } data->glDisable(texturedata->type); return 0; } static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, void * pixels, int pitch) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; SDL_Window *window = renderer->window; GLint internalFormat; GLenum format, type; Uint8 *src, *dst, *tmp; int length, rows; if (!convert_format(data, pixel_format, &internalFormat, &format, &type)) { /* FIXME: Do a temp copy to a format that is supported */ SDL_SetError("Unsupported pixel format"); return -1; } if (pixel_format == SDL_PIXELFORMAT_INDEX1LSB) { data->glPixelStorei(GL_PACK_LSB_FIRST, 1); } else if (pixel_format == SDL_PIXELFORMAT_INDEX1MSB) { data->glPixelStorei(GL_PACK_LSB_FIRST, 0); } data->glPixelStorei(GL_PACK_ALIGNMENT, 1); data->glPixelStorei(GL_PACK_ROW_LENGTH, (pitch / bytes_per_pixel(pixel_format))); data->glReadPixels(rect->x, (window->h-rect->y)-rect->h, rect->w, rect->h, format, type, pixels); /* Flip the rows to be top-down */ length = rect->w * bytes_per_pixel(pixel_format); src = (Uint8*)pixels + (rect->h-1)*pitch; dst = (Uint8*)pixels; tmp = SDL_stack_alloc(Uint8, length); rows = rect->h / 2; while (rows--) { SDL_memcpy(tmp, dst, length); SDL_memcpy(dst, src, length); SDL_memcpy(src, tmp, length); dst += pitch; src -= pitch; } SDL_stack_free(tmp); return 0; } static int GL_RenderWritePixels(SDL_Renderer * renderer, const SDL_Rect * rect, Uint32 pixel_format, const void * pixels, int pitch) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; SDL_Window *window = renderer->window; GLint internalFormat; GLenum format, type; Uint8 *src, *dst, *tmp; int length, rows; if (!convert_format(data, pixel_format, &internalFormat, &format, &type)) { /* FIXME: Do a temp copy to a format that is supported */ SDL_SetError("Unsupported pixel format"); return -1; } if (pixel_format == SDL_PIXELFORMAT_INDEX1LSB) { data->glPixelStorei(GL_UNPACK_LSB_FIRST, 1); } else if (pixel_format == SDL_PIXELFORMAT_INDEX1MSB) { data->glPixelStorei(GL_UNPACK_LSB_FIRST, 0); } data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); data->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / bytes_per_pixel(pixel_format))); /* Flip the rows to be bottom-up */ length = rect->h * rect->w * pitch; tmp = SDL_stack_alloc(Uint8, length); src = (Uint8*)pixels + (rect->h-1)*pitch; dst = (Uint8*)tmp; rows = rect->h; while (rows--) { SDL_memcpy(dst, src, pitch); dst += pitch; src -= pitch; } data->glRasterPos2i(rect->x, (window->h-rect->y)); data->glDrawPixels(rect->w, rect->h, format, type, tmp); SDL_stack_free(tmp); return 0; } static void GL_RenderPresent(SDL_Renderer * renderer) { SDL_GL_SwapWindow(renderer->window); } static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture) { GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata; GL_TextureData *data = (GL_TextureData *) texture->driverdata; if (!data) { return; } if (data->texture) { renderdata->glDeleteTextures(1, &data->texture); } if (data->palette) { SDL_free(data->palette); } if (data->pixels) { SDL_free(data->pixels); } SDL_FreeDirtyRects(&data->dirty); SDL_free(data); texture->driverdata = NULL; } static void GL_DestroyRenderer(SDL_Renderer * renderer) { GL_RenderData *data = (GL_RenderData *) renderer->driverdata; if (data) { if (data->context) { if (data->GL_ARB_fragment_program_supported) { data->glDisable(GL_FRAGMENT_PROGRAM_ARB); data->glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0); if (data->fragment_program_UYVY && data->fragment_program_UYVY != ~0) { data->glDeleteProgramsARB(1, &data->fragment_program_UYVY); } } /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */ SDL_GL_DeleteContext(data->context); } SDL_free(data); } SDL_free(renderer); } #endif /* SDL_VIDEO_RENDER_OGL */ /* vi: set ts=4 sw=4 expandtab: */