cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_render_gl.c (53561B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
      4
      5  This software is provided 'as-is', without any express or implied
      6  warranty.  In no event will the authors be held liable for any damages
      7  arising from the use of this software.
      8
      9  Permission is granted to anyone to use this software for any purpose,
     10  including commercial applications, and to alter it and redistribute it
     11  freely, subject to the following restrictions:
     12
     13  1. The origin of this software must not be misrepresented; you must not
     14     claim that you wrote the original software. If you use this software
     15     in a product, an acknowledgment in the product documentation would be
     16     appreciated but is not required.
     17  2. Altered source versions must be plainly marked as such, and must not be
     18     misrepresented as being the original software.
     19  3. This notice may not be removed or altered from any source distribution.
     20*/
     21#include "../../SDL_internal.h"
     22
     23#if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED
     24
     25#include "SDL_hints.h"
     26#include "SDL_log.h"
     27#include "SDL_assert.h"
     28#include "SDL_opengl.h"
     29#include "../SDL_sysrender.h"
     30#include "SDL_shaders_gl.h"
     31
     32#ifdef __MACOSX__
     33#include <OpenGL/OpenGL.h>
     34#endif
     35
     36/* To prevent unnecessary window recreation, 
     37 * these should match the defaults selected in SDL_GL_ResetAttributes 
     38 */
     39
     40#define RENDERER_CONTEXT_MAJOR 2
     41#define RENDERER_CONTEXT_MINOR 1
     42
     43/* OpenGL renderer implementation */
     44
     45/* Details on optimizing the texture path on Mac OS X:
     46   http://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_texturedata/opengl_texturedata.html
     47*/
     48
     49/* Used to re-create the window with OpenGL capability */
     50extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
     51
     52static const float inv255f = 1.0f / 255.0f;
     53
     54static SDL_Renderer *GL_CreateRenderer(SDL_Window * window, Uint32 flags);
     55static void GL_WindowEvent(SDL_Renderer * renderer,
     56                           const SDL_WindowEvent *event);
     57static int GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
     58static int GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
     59static int GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
     60                            const SDL_Rect * rect, const void *pixels,
     61                            int pitch);
     62static int GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
     63                               const SDL_Rect * rect,
     64                               const Uint8 *Yplane, int Ypitch,
     65                               const Uint8 *Uplane, int Upitch,
     66                               const Uint8 *Vplane, int Vpitch);
     67static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
     68                          const SDL_Rect * rect, void **pixels, int *pitch);
     69static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
     70static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
     71static int GL_UpdateViewport(SDL_Renderer * renderer);
     72static int GL_UpdateClipRect(SDL_Renderer * renderer);
     73static int GL_RenderClear(SDL_Renderer * renderer);
     74static int GL_RenderDrawPoints(SDL_Renderer * renderer,
     75                               const SDL_FPoint * points, int count);
     76static int GL_RenderDrawLines(SDL_Renderer * renderer,
     77                              const SDL_FPoint * points, int count);
     78static int GL_RenderFillRects(SDL_Renderer * renderer,
     79                              const SDL_FRect * rects, int count);
     80static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
     81                         const SDL_Rect * srcrect, const SDL_FRect * dstrect);
     82static int GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
     83                         const SDL_Rect * srcrect, const SDL_FRect * dstrect,
     84                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
     85static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
     86                               Uint32 pixel_format, void * pixels, int pitch);
     87static void GL_RenderPresent(SDL_Renderer * renderer);
     88static void GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
     89static void GL_DestroyRenderer(SDL_Renderer * renderer);
     90static int GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh);
     91static int GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture);
     92
     93SDL_RenderDriver GL_RenderDriver = {
     94    GL_CreateRenderer,
     95    {
     96     "opengl",
     97     (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
     98     1,
     99     {SDL_PIXELFORMAT_ARGB8888},
    100     0,
    101     0}
    102};
    103
    104typedef struct GL_FBOList GL_FBOList;
    105
    106struct GL_FBOList
    107{
    108    Uint32 w, h;
    109    GLuint FBO;
    110    GL_FBOList *next;
    111};
    112
    113typedef struct
    114{
    115    SDL_GLContext context;
    116
    117    SDL_bool debug_enabled;
    118    SDL_bool GL_ARB_debug_output_supported;
    119    int errors;
    120    char **error_messages;
    121    GLDEBUGPROCARB next_error_callback;
    122    GLvoid *next_error_userparam;
    123
    124    SDL_bool GL_ARB_texture_non_power_of_two_supported;
    125    SDL_bool GL_ARB_texture_rectangle_supported;
    126    struct {
    127        GL_Shader shader;
    128        Uint32 color;
    129        int blendMode;
    130    } current;
    131
    132    SDL_bool GL_EXT_framebuffer_object_supported;
    133    GL_FBOList *framebuffers;
    134
    135    /* OpenGL functions */
    136#define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
    137#include "SDL_glfuncs.h"
    138#undef SDL_PROC
    139
    140    /* Multitexture support */
    141    SDL_bool GL_ARB_multitexture_supported;
    142    PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
    143    GLint num_texture_units;
    144
    145    PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT;
    146    PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT;
    147    PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT;
    148    PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
    149    PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;
    150
    151    /* Shader support */
    152    GL_ShaderContext *shaders;
    153
    154} GL_RenderData;
    155
    156typedef struct
    157{
    158    GLuint texture;
    159    GLenum type;
    160    GLfloat texw;
    161    GLfloat texh;
    162    GLenum format;
    163    GLenum formattype;
    164    void *pixels;
    165    int pitch;
    166    SDL_Rect locked_rect;
    167
    168    /* YUV texture support */
    169    SDL_bool yuv;
    170    SDL_bool nv12;
    171    GLuint utexture;
    172    GLuint vtexture;
    173
    174    GL_FBOList *fbo;
    175} GL_TextureData;
    176
    177SDL_FORCE_INLINE const char*
    178GL_TranslateError (GLenum error)
    179{
    180#define GL_ERROR_TRANSLATE(e) case e: return #e;
    181    switch (error) {
    182    GL_ERROR_TRANSLATE(GL_INVALID_ENUM)
    183    GL_ERROR_TRANSLATE(GL_INVALID_VALUE)
    184    GL_ERROR_TRANSLATE(GL_INVALID_OPERATION)
    185    GL_ERROR_TRANSLATE(GL_OUT_OF_MEMORY)
    186    GL_ERROR_TRANSLATE(GL_NO_ERROR)
    187    GL_ERROR_TRANSLATE(GL_STACK_OVERFLOW)
    188    GL_ERROR_TRANSLATE(GL_STACK_UNDERFLOW)
    189    GL_ERROR_TRANSLATE(GL_TABLE_TOO_LARGE)
    190    default:
    191        return "UNKNOWN";
    192}
    193#undef GL_ERROR_TRANSLATE
    194}
    195
    196SDL_FORCE_INLINE void
    197GL_ClearErrors(SDL_Renderer *renderer)
    198{
    199    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
    200
    201    if (!data->debug_enabled)
    202    {
    203        return;
    204    }
    205    if (data->GL_ARB_debug_output_supported) {
    206        if (data->errors) {
    207            int i;
    208            for (i = 0; i < data->errors; ++i) {
    209                SDL_free(data->error_messages[i]);
    210            }
    211            SDL_free(data->error_messages);
    212
    213            data->errors = 0;
    214            data->error_messages = NULL;
    215        }
    216    } else {
    217        while (data->glGetError() != GL_NO_ERROR) {
    218            continue;
    219        }
    220    }
    221}
    222
    223SDL_FORCE_INLINE int
    224GL_CheckAllErrors (const char *prefix, SDL_Renderer *renderer, const char *file, int line, const char *function)
    225{
    226    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
    227    int ret = 0;
    228
    229    if (!data->debug_enabled)
    230    {
    231        return 0;
    232    }
    233    if (data->GL_ARB_debug_output_supported) {
    234        if (data->errors) {
    235            int i;
    236            for (i = 0; i < data->errors; ++i) {
    237                SDL_SetError("%s: %s (%d): %s %s", prefix, file, line, function, data->error_messages[i]);
    238                ret = -1;
    239            }
    240            GL_ClearErrors(renderer);
    241        }
    242    } else {
    243        /* check gl errors (can return multiple errors) */
    244        for (;;) {
    245            GLenum error = data->glGetError();
    246            if (error != GL_NO_ERROR) {
    247                if (prefix == NULL || prefix[0] == '\0') {
    248                    prefix = "generic";
    249                }
    250                SDL_SetError("%s: %s (%d): %s %s (0x%X)", prefix, file, line, function, GL_TranslateError(error), error);
    251                ret = -1;
    252            } else {
    253                break;
    254            }
    255        }
    256    }
    257    return ret;
    258}
    259
    260#if 0
    261#define GL_CheckError(prefix, renderer)
    262#elif defined(_MSC_VER)
    263#define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, __FILE__, __LINE__, __FUNCTION__)
    264#else
    265#define GL_CheckError(prefix, renderer) GL_CheckAllErrors(prefix, renderer, __FILE__, __LINE__, __PRETTY_FUNCTION__)
    266#endif
    267
    268static int
    269GL_LoadFunctions(GL_RenderData * data)
    270{
    271#ifdef __SDL_NOGETPROCADDR__
    272#define SDL_PROC(ret,func,params) data->func=func;
    273#else
    274#define SDL_PROC(ret,func,params) \
    275    do { \
    276        data->func = SDL_GL_GetProcAddress(#func); \
    277        if ( ! data->func ) { \
    278            return SDL_SetError("Couldn't load GL function %s: %s\n", #func, SDL_GetError()); \
    279        } \
    280    } while ( 0 );
    281#endif /* __SDL_NOGETPROCADDR__ */
    282
    283#include "SDL_glfuncs.h"
    284#undef SDL_PROC
    285    return 0;
    286}
    287
    288static SDL_GLContext SDL_CurrentContext = NULL;
    289
    290static int
    291GL_ActivateRenderer(SDL_Renderer * renderer)
    292{
    293    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
    294
    295    if (SDL_CurrentContext != data->context ||
    296        SDL_GL_GetCurrentContext() != data->context) {
    297        if (SDL_GL_MakeCurrent(renderer->window, data->context) < 0) {
    298            return -1;
    299        }
    300        SDL_CurrentContext = data->context;
    301
    302        GL_UpdateViewport(renderer);
    303    }
    304
    305    GL_ClearErrors(renderer);
    306
    307    return 0;
    308}
    309
    310/* This is called if we need to invalidate all of the SDL OpenGL state */
    311static void
    312GL_ResetState(SDL_Renderer *renderer)
    313{
    314    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
    315
    316    if (SDL_GL_GetCurrentContext() == data->context) {
    317        GL_UpdateViewport(renderer);
    318    } else {
    319        GL_ActivateRenderer(renderer);
    320    }
    321
    322    data->current.shader = SHADER_NONE;
    323    data->current.color = 0;
    324    data->current.blendMode = -1;
    325
    326    data->glDisable(GL_DEPTH_TEST);
    327    data->glDisable(GL_CULL_FACE);
    328    /* This ended up causing video discrepancies between OpenGL and Direct3D */
    329    /* data->glEnable(GL_LINE_SMOOTH); */
    330
    331    data->glMatrixMode(GL_MODELVIEW);
    332    data->glLoadIdentity();
    333
    334    GL_CheckError("", renderer);
    335}
    336
    337static void APIENTRY
    338GL_HandleDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam)
    339{
    340    SDL_Renderer *renderer = (SDL_Renderer *) userParam;
    341    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
    342
    343    if (type == GL_DEBUG_TYPE_ERROR_ARB) {
    344        /* Record this error */
    345        ++data->errors;
    346        data->error_messages = SDL_realloc(data->error_messages, data->errors * sizeof(*data->error_messages));
    347        if (data->error_messages) {
    348            data->error_messages[data->errors-1] = SDL_strdup(message);
    349        }
    350    }
    351
    352    /* If there's another error callback, pass it along, otherwise log it */
    353    if (data->next_error_callback) {
    354        data->next_error_callback(source, type, id, severity, length, message, data->next_error_userparam);
    355    } else {
    356        if (type == GL_DEBUG_TYPE_ERROR_ARB) {
    357            SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", message);
    358        } else {
    359            SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "%s", message);
    360        }
    361    }
    362}
    363
    364static GL_FBOList *
    365GL_GetFBO(GL_RenderData *data, Uint32 w, Uint32 h)
    366{
    367    GL_FBOList *result = data->framebuffers;
    368
    369    while (result && ((result->w != w) || (result->h != h))) {
    370        result = result->next;
    371    }
    372
    373    if (!result) {
    374        result = SDL_malloc(sizeof(GL_FBOList));
    375        if (result) {
    376            result->w = w;
    377            result->h = h;
    378            data->glGenFramebuffersEXT(1, &result->FBO);
    379            result->next = data->framebuffers;
    380            data->framebuffers = result;
    381        }
    382    }
    383    return result;
    384}
    385
    386SDL_Renderer *
    387GL_CreateRenderer(SDL_Window * window, Uint32 flags)
    388{
    389    SDL_Renderer *renderer;
    390    GL_RenderData *data;
    391    const char *hint;
    392    GLint value;
    393    Uint32 window_flags;
    394    int profile_mask, major, minor;
    395    SDL_bool changed_window = SDL_FALSE;
    396
    397    SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask);
    398    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major);
    399    SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor);
    400    
    401    window_flags = SDL_GetWindowFlags(window);
    402    if (!(window_flags & SDL_WINDOW_OPENGL) ||
    403        profile_mask == SDL_GL_CONTEXT_PROFILE_ES || major != RENDERER_CONTEXT_MAJOR || minor != RENDERER_CONTEXT_MINOR) {
    404
    405        changed_window = SDL_TRUE;
    406        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0);
    407        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);
    408        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);
    409
    410        if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {
    411            goto error;
    412        }
    413    }
    414
    415    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
    416    if (!renderer) {
    417        SDL_OutOfMemory();
    418        goto error;
    419    }
    420
    421    data = (GL_RenderData *) SDL_calloc(1, sizeof(*data));
    422    if (!data) {
    423        GL_DestroyRenderer(renderer);
    424        SDL_OutOfMemory();
    425        goto error;
    426    }
    427
    428    renderer->WindowEvent = GL_WindowEvent;
    429    renderer->GetOutputSize = GL_GetOutputSize;
    430    renderer->CreateTexture = GL_CreateTexture;
    431    renderer->UpdateTexture = GL_UpdateTexture;
    432    renderer->UpdateTextureYUV = GL_UpdateTextureYUV;
    433    renderer->LockTexture = GL_LockTexture;
    434    renderer->UnlockTexture = GL_UnlockTexture;
    435    renderer->SetRenderTarget = GL_SetRenderTarget;
    436    renderer->UpdateViewport = GL_UpdateViewport;
    437    renderer->UpdateClipRect = GL_UpdateClipRect;
    438    renderer->RenderClear = GL_RenderClear;
    439    renderer->RenderDrawPoints = GL_RenderDrawPoints;
    440    renderer->RenderDrawLines = GL_RenderDrawLines;
    441    renderer->RenderFillRects = GL_RenderFillRects;
    442    renderer->RenderCopy = GL_RenderCopy;
    443    renderer->RenderCopyEx = GL_RenderCopyEx;
    444    renderer->RenderReadPixels = GL_RenderReadPixels;
    445    renderer->RenderPresent = GL_RenderPresent;
    446    renderer->DestroyTexture = GL_DestroyTexture;
    447    renderer->DestroyRenderer = GL_DestroyRenderer;
    448    renderer->GL_BindTexture = GL_BindTexture;
    449    renderer->GL_UnbindTexture = GL_UnbindTexture;
    450    renderer->info = GL_RenderDriver.info;
    451    renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
    452    renderer->driverdata = data;
    453    renderer->window = window;
    454
    455    data->context = SDL_GL_CreateContext(window);
    456    if (!data->context) {
    457        GL_DestroyRenderer(renderer);
    458        goto error;
    459    }
    460    if (SDL_GL_MakeCurrent(window, data->context) < 0) {
    461        GL_DestroyRenderer(renderer);
    462        goto error;
    463    }
    464
    465    if (GL_LoadFunctions(data) < 0) {
    466        GL_DestroyRenderer(renderer);
    467        goto error;
    468    }
    469
    470#ifdef __MACOSX__
    471    /* Enable multi-threaded rendering */
    472    /* Disabled until Ryan finishes his VBO/PBO code...
    473       CGLEnable(CGLGetCurrentContext(), kCGLCEMPEngine);
    474     */
    475#endif
    476
    477    if (flags & SDL_RENDERER_PRESENTVSYNC) {
    478        SDL_GL_SetSwapInterval(1);
    479    } else {
    480        SDL_GL_SetSwapInterval(0);
    481    }
    482    if (SDL_GL_GetSwapInterval() > 0) {
    483        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
    484    }
    485
    486    /* Check for debug output support */
    487    if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) == 0 &&
    488        (value & SDL_GL_CONTEXT_DEBUG_FLAG)) {
    489        data->debug_enabled = SDL_TRUE;
    490    }
    491    if (data->debug_enabled && SDL_GL_ExtensionSupported("GL_ARB_debug_output")) {
    492        PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB");
    493
    494        data->GL_ARB_debug_output_supported = SDL_TRUE;
    495        data->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION_ARB, (GLvoid **)&data->next_error_callback);
    496        data->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM_ARB, &data->next_error_userparam);
    497        glDebugMessageCallbackARBFunc(GL_HandleDebugMessage, renderer);
    498
    499        /* Make sure our callback is called when errors actually happen */
    500        data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
    501    }
    502
    503    if (SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) {
    504        data->GL_ARB_texture_non_power_of_two_supported = SDL_TRUE;
    505    } else if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") ||
    506               SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
    507        data->GL_ARB_texture_rectangle_supported = SDL_TRUE;
    508    }
    509    if (data->GL_ARB_texture_rectangle_supported) {
    510        data->glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &value);
    511        renderer->info.max_texture_width = value;
    512        renderer->info.max_texture_height = value;
    513    } else {
    514        data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);
    515        renderer->info.max_texture_width = value;
    516        renderer->info.max_texture_height = value;
    517    }
    518
    519    /* Check for multitexture support */
    520    if (SDL_GL_ExtensionSupported("GL_ARB_multitexture")) {
    521        data->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
    522        if (data->glActiveTextureARB) {
    523            data->GL_ARB_multitexture_supported = SDL_TRUE;
    524            data->glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &data->num_texture_units);
    525        }
    526    }
    527
    528    /* Check for shader support */
    529    hint = SDL_GetHint(SDL_HINT_RENDER_OPENGL_SHADERS);
    530    if (!hint || *hint != '0') {
    531        data->shaders = GL_CreateShaderContext();
    532    }
    533    SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
    534                data->shaders ? "ENABLED" : "DISABLED");
    535
    536    /* We support YV12 textures using 3 textures and a shader */
    537    if (data->shaders && data->num_texture_units >= 3) {
    538        renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;
    539        renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;
    540        renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV12;
    541        renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21;
    542    }
    543
    544#ifdef __MACOSX__
    545    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_UYVY;
    546#endif
    547
    548    if (SDL_GL_ExtensionSupported("GL_EXT_framebuffer_object")) {
    549        data->GL_EXT_framebuffer_object_supported = SDL_TRUE;
    550        data->glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)
    551            SDL_GL_GetProcAddress("glGenFramebuffersEXT");
    552        data->glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
    553            SDL_GL_GetProcAddress("glDeleteFramebuffersEXT");
    554        data->glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
    555            SDL_GL_GetProcAddress("glFramebufferTexture2DEXT");
    556        data->glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)
    557            SDL_GL_GetProcAddress("glBindFramebufferEXT");
    558        data->glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
    559            SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT");
    560        renderer->info.flags |= SDL_RENDERER_TARGETTEXTURE;
    561    }
    562    data->framebuffers = NULL;
    563
    564    /* Set up parameters for rendering */
    565    GL_ResetState(renderer);
    566
    567    return renderer;
    568
    569error:
    570    if (changed_window) {
    571        /* Uh oh, better try to put it back... */
    572        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);
    573        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
    574        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
    575        SDL_RecreateWindow(window, window_flags);
    576    }
    577    return NULL;
    578}
    579
    580static void
    581GL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
    582{
    583    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
    584        event->event == SDL_WINDOWEVENT_SHOWN ||
    585        event->event == SDL_WINDOWEVENT_HIDDEN) {
    586        /* Rebind the context to the window area and update matrices */
    587        SDL_CurrentContext = NULL;
    588    }
    589}
    590
    591static int
    592GL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
    593{
    594    SDL_GL_GetDrawableSize(renderer->window, w, h);
    595
    596    return 0;
    597}
    598
    599SDL_FORCE_INLINE int
    600power_of_2(int input)
    601{
    602    int value = 1;
    603
    604    while (value < input) {
    605        value <<= 1;
    606    }
    607    return value;
    608}
    609
    610SDL_FORCE_INLINE SDL_bool
    611convert_format(GL_RenderData *renderdata, Uint32 pixel_format,
    612               GLint* internalFormat, GLenum* format, GLenum* type)
    613{
    614    switch (pixel_format) {
    615    case SDL_PIXELFORMAT_ARGB8888:
    616        *internalFormat = GL_RGBA8;
    617        *format = GL_BGRA;
    618        *type = GL_UNSIGNED_INT_8_8_8_8_REV;
    619        break;
    620    case SDL_PIXELFORMAT_YV12:
    621    case SDL_PIXELFORMAT_IYUV:
    622    case SDL_PIXELFORMAT_NV12:
    623    case SDL_PIXELFORMAT_NV21:
    624        *internalFormat = GL_LUMINANCE;
    625        *format = GL_LUMINANCE;
    626        *type = GL_UNSIGNED_BYTE;
    627        break;
    628#ifdef __MACOSX__
    629    case SDL_PIXELFORMAT_UYVY:
    630        *internalFormat = GL_RGB8;
    631        *format = GL_YCBCR_422_APPLE;
    632        *type = GL_UNSIGNED_SHORT_8_8_APPLE;
    633        break;
    634#endif
    635    default:
    636        return SDL_FALSE;
    637    }
    638    return SDL_TRUE;
    639}
    640
    641static GLenum
    642GetScaleQuality(void)
    643{
    644    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
    645
    646    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
    647        return GL_NEAREST;
    648    } else {
    649        return GL_LINEAR;
    650    }
    651}
    652
    653static int
    654GL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
    655{
    656    GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
    657    GL_TextureData *data;
    658    GLint internalFormat;
    659    GLenum format, type;
    660    int texture_w, texture_h;
    661    GLenum scaleMode;
    662
    663    GL_ActivateRenderer(renderer);
    664
    665    if (!convert_format(renderdata, texture->format, &internalFormat,
    666                        &format, &type)) {
    667        return SDL_SetError("Texture format %s not supported by OpenGL",
    668                            SDL_GetPixelFormatName(texture->format));
    669    }
    670
    671    data = (GL_TextureData *) SDL_calloc(1, sizeof(*data));
    672    if (!data) {
    673        return SDL_OutOfMemory();
    674    }
    675
    676    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
    677        size_t size;
    678        data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);
    679        size = texture->h * data->pitch;
    680        if (texture->format == SDL_PIXELFORMAT_YV12 ||
    681            texture->format == SDL_PIXELFORMAT_IYUV) {
    682            /* Need to add size for the U and V planes */
    683            size += (2 * (texture->h * data->pitch) / 4);
    684        }
    685        if (texture->format == SDL_PIXELFORMAT_NV12 ||
    686            texture->format == SDL_PIXELFORMAT_NV21) {
    687            /* Need to add size for the U/V plane */
    688            size += ((texture->h * data->pitch) / 2);
    689        }
    690        data->pixels = SDL_calloc(1, size);
    691        if (!data->pixels) {
    692            SDL_free(data);
    693            return SDL_OutOfMemory();
    694        }
    695    }
    696
    697    if (texture->access == SDL_TEXTUREACCESS_TARGET) {
    698        data->fbo = GL_GetFBO(renderdata, texture->w, texture->h);
    699    } else {
    700        data->fbo = NULL;
    701    }
    702
    703    GL_CheckError("", renderer);
    704    renderdata->glGenTextures(1, &data->texture);
    705    if (GL_CheckError("glGenTextures()", renderer) < 0) {
    706        if (data->pixels) {
    707            SDL_free(data->pixels);
    708        }
    709        SDL_free(data);
    710        return -1;
    711    }
    712    texture->driverdata = data;
    713
    714    if (renderdata->GL_ARB_texture_non_power_of_two_supported) {
    715        data->type = GL_TEXTURE_2D;
    716        texture_w = texture->w;
    717        texture_h = texture->h;
    718        data->texw = 1.0f;
    719        data->texh = 1.0f;
    720    } else if (renderdata->GL_ARB_texture_rectangle_supported) {
    721        data->type = GL_TEXTURE_RECTANGLE_ARB;
    722        texture_w = texture->w;
    723        texture_h = texture->h;
    724        data->texw = (GLfloat) texture_w;
    725        data->texh = (GLfloat) texture_h;
    726    } else {
    727        data->type = GL_TEXTURE_2D;
    728        texture_w = power_of_2(texture->w);
    729        texture_h = power_of_2(texture->h);
    730        data->texw = (GLfloat) (texture->w) / texture_w;
    731        data->texh = (GLfloat) texture->h / texture_h;
    732    }
    733
    734    data->format = format;
    735    data->formattype = type;
    736    scaleMode = GetScaleQuality();
    737    renderdata->glEnable(data->type);
    738    renderdata->glBindTexture(data->type, data->texture);
    739    renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER, scaleMode);
    740    renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER, scaleMode);
    741    /* According to the spec, CLAMP_TO_EDGE is the default for TEXTURE_RECTANGLE
    742       and setting it causes an INVALID_ENUM error in the latest NVidia drivers.
    743    */
    744    if (data->type != GL_TEXTURE_RECTANGLE_ARB) {
    745        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
    746                                    GL_CLAMP_TO_EDGE);
    747        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
    748                                    GL_CLAMP_TO_EDGE);
    749    }
    750#ifdef __MACOSX__
    751#ifndef GL_TEXTURE_STORAGE_HINT_APPLE
    752#define GL_TEXTURE_STORAGE_HINT_APPLE       0x85BC
    753#endif
    754#ifndef STORAGE_CACHED_APPLE
    755#define STORAGE_CACHED_APPLE                0x85BE
    756#endif
    757#ifndef STORAGE_SHARED_APPLE
    758#define STORAGE_SHARED_APPLE                0x85BF
    759#endif
    760    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {
    761        renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
    762                                    GL_STORAGE_SHARED_APPLE);
    763    } else {
    764        renderdata->glTexParameteri(data->type, GL_TEXTURE_STORAGE_HINT_APPLE,
    765                                    GL_STORAGE_CACHED_APPLE);
    766    }
    767    if (texture->access == SDL_TEXTUREACCESS_STREAMING
    768        && texture->format == SDL_PIXELFORMAT_ARGB8888
    769        && (texture->w % 8) == 0) {
    770        renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
    771        renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    772        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH,
    773                          (data->pitch / SDL_BYTESPERPIXEL(texture->format)));
    774        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
    775                                 texture_h, 0, format, type, data->pixels);
    776        renderdata->glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
    777    }
    778    else
    779#endif
    780    {
    781        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w,
    782                                 texture_h, 0, format, type, NULL);
    783    }
    784    renderdata->glDisable(data->type);
    785    if (GL_CheckError("glTexImage2D()", renderer) < 0) {
    786        return -1;
    787    }
    788
    789    if (texture->format == SDL_PIXELFORMAT_YV12 ||
    790        texture->format == SDL_PIXELFORMAT_IYUV) {
    791        data->yuv = SDL_TRUE;
    792
    793        renderdata->glGenTextures(1, &data->utexture);
    794        renderdata->glGenTextures(1, &data->vtexture);
    795        renderdata->glEnable(data->type);
    796
    797        renderdata->glBindTexture(data->type, data->utexture);
    798        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
    799                                    scaleMode);
    800        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
    801                                    scaleMode);
    802        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
    803                                    GL_CLAMP_TO_EDGE);
    804        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
    805                                    GL_CLAMP_TO_EDGE);
    806        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
    807                                 texture_h/2, 0, format, type, NULL);
    808
    809        renderdata->glBindTexture(data->type, data->vtexture);
    810        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
    811                                    scaleMode);
    812        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
    813                                    scaleMode);
    814        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
    815                                    GL_CLAMP_TO_EDGE);
    816        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
    817                                    GL_CLAMP_TO_EDGE);
    818        renderdata->glTexImage2D(data->type, 0, internalFormat, texture_w/2,
    819                                 texture_h/2, 0, format, type, NULL);
    820
    821        renderdata->glDisable(data->type);
    822    }
    823
    824    if (texture->format == SDL_PIXELFORMAT_NV12 ||
    825        texture->format == SDL_PIXELFORMAT_NV21) {
    826        data->nv12 = SDL_TRUE;
    827
    828        renderdata->glGenTextures(1, &data->utexture);
    829        renderdata->glEnable(data->type);
    830
    831        renderdata->glBindTexture(data->type, data->utexture);
    832        renderdata->glTexParameteri(data->type, GL_TEXTURE_MIN_FILTER,
    833                                    scaleMode);
    834        renderdata->glTexParameteri(data->type, GL_TEXTURE_MAG_FILTER,
    835                                    scaleMode);
    836        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_S,
    837                                    GL_CLAMP_TO_EDGE);
    838        renderdata->glTexParameteri(data->type, GL_TEXTURE_WRAP_T,
    839                                    GL_CLAMP_TO_EDGE);
    840        renderdata->glTexImage2D(data->type, 0, GL_LUMINANCE_ALPHA, texture_w/2,
    841                                 texture_h/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);
    842        renderdata->glDisable(data->type);
    843    }
    844
    845    return GL_CheckError("", renderer);
    846}
    847
    848static int
    849GL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    850                 const SDL_Rect * rect, const void *pixels, int pitch)
    851{
    852    GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
    853    GL_TextureData *data = (GL_TextureData *) texture->driverdata;
    854    const int texturebpp = SDL_BYTESPERPIXEL(texture->format);
    855
    856    SDL_assert(texturebpp != 0);  /* otherwise, division by zero later. */
    857
    858    GL_ActivateRenderer(renderer);
    859
    860    renderdata->glEnable(data->type);
    861    renderdata->glBindTexture(data->type, data->texture);
    862    renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    863    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / texturebpp));
    864    renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
    865                                rect->h, data->format, data->formattype,
    866                                pixels);
    867    if (data->yuv) {
    868        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
    869
    870        /* Skip to the correct offset into the next texture */
    871        pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
    872        if (texture->format == SDL_PIXELFORMAT_YV12) {
    873            renderdata->glBindTexture(data->type, data->vtexture);
    874        } else {
    875            renderdata->glBindTexture(data->type, data->utexture);
    876        }
    877        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
    878                                    rect->w/2, rect->h/2,
    879                                    data->format, data->formattype, pixels);
    880
    881        /* Skip to the correct offset into the next texture */
    882        pixels = (const void*)((const Uint8*)pixels + (rect->h * pitch)/4);
    883        if (texture->format == SDL_PIXELFORMAT_YV12) {
    884            renderdata->glBindTexture(data->type, data->utexture);
    885        } else {
    886            renderdata->glBindTexture(data->type, data->vtexture);
    887        }
    888        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
    889                                    rect->w/2, rect->h/2,
    890                                    data->format, data->formattype, pixels);
    891    }
    892
    893    if (data->nv12) {
    894        renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, (pitch / 2));
    895
    896        /* Skip to the correct offset into the next texture */
    897        pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
    898        renderdata->glBindTexture(data->type, data->utexture);
    899        renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
    900                                    rect->w/2, rect->h/2,
    901                                    GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels);
    902    }
    903    renderdata->glDisable(data->type);
    904
    905    return GL_CheckError("glTexSubImage2D()", renderer);
    906}
    907
    908static int
    909GL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
    910                    const SDL_Rect * rect,
    911                    const Uint8 *Yplane, int Ypitch,
    912                    const Uint8 *Uplane, int Upitch,
    913                    const Uint8 *Vplane, int Vpitch)
    914{
    915    GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
    916    GL_TextureData *data = (GL_TextureData *) texture->driverdata;
    917
    918    GL_ActivateRenderer(renderer);
    919
    920    renderdata->glEnable(data->type);
    921    renderdata->glBindTexture(data->type, data->texture);
    922    renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    923    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Ypitch);
    924    renderdata->glTexSubImage2D(data->type, 0, rect->x, rect->y, rect->w,
    925                                rect->h, data->format, data->formattype,
    926                                Yplane);
    927
    928    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Upitch);
    929    renderdata->glBindTexture(data->type, data->utexture);
    930    renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
    931                                rect->w/2, rect->h/2,
    932                                data->format, data->formattype, Uplane);
    933
    934    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, Vpitch);
    935    renderdata->glBindTexture(data->type, data->vtexture);
    936    renderdata->glTexSubImage2D(data->type, 0, rect->x/2, rect->y/2,
    937                                rect->w/2, rect->h/2,
    938                                data->format, data->formattype, Vplane);
    939    renderdata->glDisable(data->type);
    940
    941    return GL_CheckError("glTexSubImage2D()", renderer);
    942}
    943
    944static int
    945GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    946               const SDL_Rect * rect, void **pixels, int *pitch)
    947{
    948    GL_TextureData *data = (GL_TextureData *) texture->driverdata;
    949
    950    data->locked_rect = *rect;
    951    *pixels =
    952        (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
    953                  rect->x * SDL_BYTESPERPIXEL(texture->format));
    954    *pitch = data->pitch;
    955    return 0;
    956}
    957
    958static void
    959GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
    960{
    961    GL_TextureData *data = (GL_TextureData *) texture->driverdata;
    962    const SDL_Rect *rect;
    963    void *pixels;
    964
    965    rect = &data->locked_rect;
    966    pixels =
    967        (void *) ((Uint8 *) data->pixels + rect->y * data->pitch +
    968                  rect->x * SDL_BYTESPERPIXEL(texture->format));
    969    GL_UpdateTexture(renderer, texture, rect, pixels, data->pitch);
    970}
    971
    972static int
    973GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
    974{
    975    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
    976    GL_TextureData *texturedata;
    977    GLenum status;
    978
    979    GL_ActivateRenderer(renderer);
    980
    981    if (texture == NULL) {
    982        data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    983        return 0;
    984    }
    985
    986    texturedata = (GL_TextureData *) texture->driverdata;
    987    data->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, texturedata->fbo->FBO);
    988    /* TODO: check if texture pixel format allows this operation */
    989    data->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, texturedata->type, texturedata->texture, 0);
    990    /* Check FBO status */
    991    status = data->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    992    if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
    993        return SDL_SetError("glFramebufferTexture2DEXT() failed");
    994    }
    995    return 0;
    996}
    997
    998static int
    999GL_UpdateViewport(SDL_Renderer * renderer)
   1000{
   1001    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1002
   1003    if (SDL_CurrentContext != data->context) {
   1004        /* We'll update the viewport after we rebind the context */
   1005        return 0;
   1006    }
   1007
   1008    if (renderer->target) {
   1009        data->glViewport(renderer->viewport.x, renderer->viewport.y,
   1010                         renderer->viewport.w, renderer->viewport.h);
   1011    } else {
   1012        int w, h;
   1013
   1014        SDL_GetRendererOutputSize(renderer, &w, &h);
   1015        data->glViewport(renderer->viewport.x, (h - renderer->viewport.y - renderer->viewport.h),
   1016                         renderer->viewport.w, renderer->viewport.h);
   1017    }
   1018
   1019    data->glMatrixMode(GL_PROJECTION);
   1020    data->glLoadIdentity();
   1021    if (renderer->viewport.w && renderer->viewport.h) {
   1022        if (renderer->target) {
   1023            data->glOrtho((GLdouble) 0,
   1024                          (GLdouble) renderer->viewport.w,
   1025                          (GLdouble) 0,
   1026                          (GLdouble) renderer->viewport.h,
   1027                           0.0, 1.0);
   1028        } else {
   1029            data->glOrtho((GLdouble) 0,
   1030                          (GLdouble) renderer->viewport.w,
   1031                          (GLdouble) renderer->viewport.h,
   1032                          (GLdouble) 0,
   1033                           0.0, 1.0);
   1034        }
   1035    }
   1036    return GL_CheckError("", renderer);
   1037}
   1038
   1039static int
   1040GL_UpdateClipRect(SDL_Renderer * renderer)
   1041{
   1042    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1043
   1044    if (renderer->clipping_enabled) {
   1045        const SDL_Rect *rect = &renderer->clip_rect;
   1046        data->glEnable(GL_SCISSOR_TEST);
   1047        data->glScissor(rect->x, renderer->viewport.h - rect->y - rect->h, rect->w, rect->h);
   1048    } else {
   1049        data->glDisable(GL_SCISSOR_TEST);
   1050    }
   1051    return 0;
   1052}
   1053
   1054static void
   1055GL_SetShader(GL_RenderData * data, GL_Shader shader)
   1056{
   1057    if (data->shaders && shader != data->current.shader) {
   1058        GL_SelectShader(data->shaders, shader);
   1059        data->current.shader = shader;
   1060    }
   1061}
   1062
   1063static void
   1064GL_SetColor(GL_RenderData * data, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
   1065{
   1066    Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
   1067
   1068    if (color != data->current.color) {
   1069        data->glColor4f((GLfloat) r * inv255f,
   1070                        (GLfloat) g * inv255f,
   1071                        (GLfloat) b * inv255f,
   1072                        (GLfloat) a * inv255f);
   1073        data->current.color = color;
   1074    }
   1075}
   1076
   1077static void
   1078GL_SetBlendMode(GL_RenderData * data, int blendMode)
   1079{
   1080    if (blendMode != data->current.blendMode) {
   1081        switch (blendMode) {
   1082        case SDL_BLENDMODE_NONE:
   1083            data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   1084            data->glDisable(GL_BLEND);
   1085            break;
   1086        case SDL_BLENDMODE_BLEND:
   1087            data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   1088            data->glEnable(GL_BLEND);
   1089            data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
   1090            break;
   1091        case SDL_BLENDMODE_ADD:
   1092            data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   1093            data->glEnable(GL_BLEND);
   1094            data->glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
   1095            break;
   1096        case SDL_BLENDMODE_MOD:
   1097            data->glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   1098            data->glEnable(GL_BLEND);
   1099            data->glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE);
   1100            break;
   1101        }
   1102        data->current.blendMode = blendMode;
   1103    }
   1104}
   1105
   1106static void
   1107GL_SetDrawingState(SDL_Renderer * renderer)
   1108{
   1109    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1110
   1111    GL_ActivateRenderer(renderer);
   1112
   1113    GL_SetColor(data, renderer->r,
   1114                      renderer->g,
   1115                      renderer->b,
   1116                      renderer->a);
   1117
   1118    GL_SetBlendMode(data, renderer->blendMode);
   1119
   1120    GL_SetShader(data, SHADER_SOLID);
   1121}
   1122
   1123static int
   1124GL_RenderClear(SDL_Renderer * renderer)
   1125{
   1126    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1127
   1128    GL_ActivateRenderer(renderer);
   1129
   1130    data->glClearColor((GLfloat) renderer->r * inv255f,
   1131                       (GLfloat) renderer->g * inv255f,
   1132                       (GLfloat) renderer->b * inv255f,
   1133                       (GLfloat) renderer->a * inv255f);
   1134
   1135    data->glClear(GL_COLOR_BUFFER_BIT);
   1136
   1137    return 0;
   1138}
   1139
   1140static int
   1141GL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points,
   1142                    int count)
   1143{
   1144    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1145    int i;
   1146
   1147    GL_SetDrawingState(renderer);
   1148
   1149    data->glBegin(GL_POINTS);
   1150    for (i = 0; i < count; ++i) {
   1151        data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   1152    }
   1153    data->glEnd();
   1154
   1155    return 0;
   1156}
   1157
   1158static int
   1159GL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points,
   1160                   int count)
   1161{
   1162    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1163    int i;
   1164
   1165    GL_SetDrawingState(renderer);
   1166
   1167    if (count > 2 &&
   1168        points[0].x == points[count-1].x && points[0].y == points[count-1].y) {
   1169        data->glBegin(GL_LINE_LOOP);
   1170        /* GL_LINE_LOOP takes care of the final segment */
   1171        --count;
   1172        for (i = 0; i < count; ++i) {
   1173            data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   1174        }
   1175        data->glEnd();
   1176    } else {
   1177#if defined(__MACOSX__) || defined(__WIN32__)
   1178#else
   1179        int x1, y1, x2, y2;
   1180#endif
   1181
   1182        data->glBegin(GL_LINE_STRIP);
   1183        for (i = 0; i < count; ++i) {
   1184            data->glVertex2f(0.5f + points[i].x, 0.5f + points[i].y);
   1185        }
   1186        data->glEnd();
   1187
   1188        /* The line is half open, so we need one more point to complete it.
   1189         * http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html
   1190         * If we have to, we can use vertical line and horizontal line textures
   1191         * for vertical and horizontal lines, and then create custom textures
   1192         * for diagonal lines and software render those.  It's terrible, but at
   1193         * least it would be pixel perfect.
   1194         */
   1195        data->glBegin(GL_POINTS);
   1196#if defined(__MACOSX__) || defined(__WIN32__)
   1197        /* Mac OS X and Windows seem to always leave the last point open */
   1198        data->glVertex2f(0.5f + points[count-1].x, 0.5f + points[count-1].y);
   1199#else
   1200        /* Linux seems to leave the right-most or bottom-most point open */
   1201        x1 = points[0].x;
   1202        y1 = points[0].y;
   1203        x2 = points[count-1].x;
   1204        y2 = points[count-1].y;
   1205
   1206        if (x1 > x2) {
   1207            data->glVertex2f(0.5f + x1, 0.5f + y1);
   1208        } else if (x2 > x1) {
   1209            data->glVertex2f(0.5f + x2, 0.5f + y2);
   1210        }
   1211        if (y1 > y2) {
   1212            data->glVertex2f(0.5f + x1, 0.5f + y1);
   1213        } else if (y2 > y1) {
   1214            data->glVertex2f(0.5f + x2, 0.5f + y2);
   1215        }
   1216#endif
   1217        data->glEnd();
   1218    }
   1219    return GL_CheckError("", renderer);
   1220}
   1221
   1222static int
   1223GL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
   1224{
   1225    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1226    int i;
   1227
   1228    GL_SetDrawingState(renderer);
   1229
   1230    for (i = 0; i < count; ++i) {
   1231        const SDL_FRect *rect = &rects[i];
   1232
   1233        data->glRectf(rect->x, rect->y, rect->x + rect->w, rect->y + rect->h);
   1234    }
   1235    return GL_CheckError("", renderer);
   1236}
   1237
   1238static int
   1239GL_SetupCopy(SDL_Renderer * renderer, SDL_Texture * texture)
   1240{
   1241    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1242    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   1243
   1244    data->glEnable(texturedata->type);
   1245    if (texturedata->yuv) {
   1246        data->glActiveTextureARB(GL_TEXTURE2_ARB);
   1247        data->glBindTexture(texturedata->type, texturedata->vtexture);
   1248
   1249        data->glActiveTextureARB(GL_TEXTURE1_ARB);
   1250        data->glBindTexture(texturedata->type, texturedata->utexture);
   1251
   1252        data->glActiveTextureARB(GL_TEXTURE0_ARB);
   1253    }
   1254    if (texturedata->nv12) {
   1255        data->glActiveTextureARB(GL_TEXTURE1_ARB);
   1256        data->glBindTexture(texturedata->type, texturedata->utexture);
   1257
   1258        data->glActiveTextureARB(GL_TEXTURE0_ARB);
   1259    }
   1260    data->glBindTexture(texturedata->type, texturedata->texture);
   1261
   1262    if (texture->modMode) {
   1263        GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
   1264    } else {
   1265        GL_SetColor(data, 255, 255, 255, 255);
   1266    }
   1267
   1268    GL_SetBlendMode(data, texture->blendMode);
   1269
   1270    if (texturedata->yuv) {
   1271        GL_SetShader(data, SHADER_YUV);
   1272    } else if (texturedata->nv12) {
   1273        if (texture->format == SDL_PIXELFORMAT_NV12) {
   1274            GL_SetShader(data, SHADER_NV12);
   1275        } else {
   1276            GL_SetShader(data, SHADER_NV21);
   1277        }
   1278    } else {
   1279        GL_SetShader(data, SHADER_RGB);
   1280    }
   1281    return 0;
   1282}
   1283
   1284static int
   1285GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   1286              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   1287{
   1288    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1289    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   1290    GLfloat minx, miny, maxx, maxy;
   1291    GLfloat minu, maxu, minv, maxv;
   1292
   1293    GL_ActivateRenderer(renderer);
   1294
   1295    if (GL_SetupCopy(renderer, texture) < 0) {
   1296        return -1;
   1297    }
   1298
   1299    minx = dstrect->x;
   1300    miny = dstrect->y;
   1301    maxx = dstrect->x + dstrect->w;
   1302    maxy = dstrect->y + dstrect->h;
   1303
   1304    minu = (GLfloat) srcrect->x / texture->w;
   1305    minu *= texturedata->texw;
   1306    maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
   1307    maxu *= texturedata->texw;
   1308    minv = (GLfloat) srcrect->y / texture->h;
   1309    minv *= texturedata->texh;
   1310    maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
   1311    maxv *= texturedata->texh;
   1312
   1313    data->glBegin(GL_TRIANGLE_STRIP);
   1314    data->glTexCoord2f(minu, minv);
   1315    data->glVertex2f(minx, miny);
   1316    data->glTexCoord2f(maxu, minv);
   1317    data->glVertex2f(maxx, miny);
   1318    data->glTexCoord2f(minu, maxv);
   1319    data->glVertex2f(minx, maxy);
   1320    data->glTexCoord2f(maxu, maxv);
   1321    data->glVertex2f(maxx, maxy);
   1322    data->glEnd();
   1323
   1324    data->glDisable(texturedata->type);
   1325
   1326    return GL_CheckError("", renderer);
   1327}
   1328
   1329static int
   1330GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
   1331              const SDL_Rect * srcrect, const SDL_FRect * dstrect,
   1332              const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
   1333{
   1334    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1335    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   1336    GLfloat minx, miny, maxx, maxy;
   1337    GLfloat centerx, centery;
   1338    GLfloat minu, maxu, minv, maxv;
   1339
   1340    GL_ActivateRenderer(renderer);
   1341
   1342    if (GL_SetupCopy(renderer, texture) < 0) {
   1343        return -1;
   1344    }
   1345
   1346    centerx = center->x;
   1347    centery = center->y;
   1348
   1349    if (flip & SDL_FLIP_HORIZONTAL) {
   1350        minx =  dstrect->w - centerx;
   1351        maxx = -centerx;
   1352    }
   1353    else {
   1354        minx = -centerx;
   1355        maxx =  dstrect->w - centerx;
   1356    }
   1357
   1358    if (flip & SDL_FLIP_VERTICAL) {
   1359        miny =  dstrect->h - centery;
   1360        maxy = -centery;
   1361    }
   1362    else {
   1363        miny = -centery;
   1364        maxy =  dstrect->h - centery;
   1365    }
   1366
   1367    minu = (GLfloat) srcrect->x / texture->w;
   1368    minu *= texturedata->texw;
   1369    maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
   1370    maxu *= texturedata->texw;
   1371    minv = (GLfloat) srcrect->y / texture->h;
   1372    minv *= texturedata->texh;
   1373    maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
   1374    maxv *= texturedata->texh;
   1375
   1376    /* Translate to flip, rotate, translate to position */
   1377    data->glPushMatrix();
   1378    data->glTranslatef((GLfloat)dstrect->x + centerx, (GLfloat)dstrect->y + centery, (GLfloat)0.0);
   1379    data->glRotated(angle, (GLdouble)0.0, (GLdouble)0.0, (GLdouble)1.0);
   1380
   1381    data->glBegin(GL_TRIANGLE_STRIP);
   1382    data->glTexCoord2f(minu, minv);
   1383    data->glVertex2f(minx, miny);
   1384    data->glTexCoord2f(maxu, minv);
   1385    data->glVertex2f(maxx, miny);
   1386    data->glTexCoord2f(minu, maxv);
   1387    data->glVertex2f(minx, maxy);
   1388    data->glTexCoord2f(maxu, maxv);
   1389    data->glVertex2f(maxx, maxy);
   1390    data->glEnd();
   1391    data->glPopMatrix();
   1392
   1393    data->glDisable(texturedata->type);
   1394
   1395    return GL_CheckError("", renderer);
   1396}
   1397
   1398static int
   1399GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   1400                    Uint32 pixel_format, void * pixels, int pitch)
   1401{
   1402    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1403    Uint32 temp_format = SDL_PIXELFORMAT_ARGB8888;
   1404    void *temp_pixels;
   1405    int temp_pitch;
   1406    GLint internalFormat;
   1407    GLenum format, type;
   1408    Uint8 *src, *dst, *tmp;
   1409    int w, h, length, rows;
   1410    int status;
   1411
   1412    GL_ActivateRenderer(renderer);
   1413
   1414    temp_pitch = rect->w * SDL_BYTESPERPIXEL(temp_format);
   1415    temp_pixels = SDL_malloc(rect->h * temp_pitch);
   1416    if (!temp_pixels) {
   1417        return SDL_OutOfMemory();
   1418    }
   1419
   1420    convert_format(data, temp_format, &internalFormat, &format, &type);
   1421
   1422    SDL_GetRendererOutputSize(renderer, &w, &h);
   1423
   1424    data->glPixelStorei(GL_PACK_ALIGNMENT, 1);
   1425    data->glPixelStorei(GL_PACK_ROW_LENGTH,
   1426                        (temp_pitch / SDL_BYTESPERPIXEL(temp_format)));
   1427
   1428    data->glReadPixels(rect->x, (h-rect->y)-rect->h, rect->w, rect->h,
   1429                       format, type, temp_pixels);
   1430
   1431    if (GL_CheckError("glReadPixels()", renderer) < 0) {
   1432        SDL_free(temp_pixels);
   1433        return -1;
   1434    }
   1435
   1436    /* Flip the rows to be top-down */
   1437    length = rect->w * SDL_BYTESPERPIXEL(temp_format);
   1438    src = (Uint8*)temp_pixels + (rect->h-1)*temp_pitch;
   1439    dst = (Uint8*)temp_pixels;
   1440    tmp = SDL_stack_alloc(Uint8, length);
   1441    rows = rect->h / 2;
   1442    while (rows--) {
   1443        SDL_memcpy(tmp, dst, length);
   1444        SDL_memcpy(dst, src, length);
   1445        SDL_memcpy(src, tmp, length);
   1446        dst += temp_pitch;
   1447        src -= temp_pitch;
   1448    }
   1449    SDL_stack_free(tmp);
   1450
   1451    status = SDL_ConvertPixels(rect->w, rect->h,
   1452                               temp_format, temp_pixels, temp_pitch,
   1453                               pixel_format, pixels, pitch);
   1454    SDL_free(temp_pixels);
   1455
   1456    return status;
   1457}
   1458
   1459static void
   1460GL_RenderPresent(SDL_Renderer * renderer)
   1461{
   1462    GL_ActivateRenderer(renderer);
   1463
   1464    SDL_GL_SwapWindow(renderer->window);
   1465}
   1466
   1467static void
   1468GL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   1469{
   1470    GL_RenderData *renderdata = (GL_RenderData *) renderer->driverdata;
   1471    GL_TextureData *data = (GL_TextureData *) texture->driverdata;
   1472
   1473    GL_ActivateRenderer(renderer);
   1474
   1475    if (!data) {
   1476        return;
   1477    }
   1478    if (data->texture) {
   1479        renderdata->glDeleteTextures(1, &data->texture);
   1480    }
   1481    if (data->yuv) {
   1482        renderdata->glDeleteTextures(1, &data->utexture);
   1483        renderdata->glDeleteTextures(1, &data->vtexture);
   1484    }
   1485    SDL_free(data->pixels);
   1486    SDL_free(data);
   1487    texture->driverdata = NULL;
   1488}
   1489
   1490static void
   1491GL_DestroyRenderer(SDL_Renderer * renderer)
   1492{
   1493    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1494
   1495    if (data) {
   1496        GL_ClearErrors(renderer);
   1497        if (data->GL_ARB_debug_output_supported) {
   1498            PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARBFunc = (PFNGLDEBUGMESSAGECALLBACKARBPROC) SDL_GL_GetProcAddress("glDebugMessageCallbackARB");
   1499
   1500            /* Uh oh, we don't have a safe way of removing ourselves from the callback chain, if it changed after we set our callback. */
   1501            /* For now, just always replace the callback with the original one */
   1502            glDebugMessageCallbackARBFunc(data->next_error_callback, data->next_error_userparam);
   1503        }
   1504        if (data->shaders) {
   1505            GL_DestroyShaderContext(data->shaders);
   1506        }
   1507        if (data->context) {
   1508            while (data->framebuffers) {
   1509                GL_FBOList *nextnode = data->framebuffers->next;
   1510                /* delete the framebuffer object */
   1511                data->glDeleteFramebuffersEXT(1, &data->framebuffers->FBO);
   1512                GL_CheckError("", renderer);
   1513                SDL_free(data->framebuffers);
   1514                data->framebuffers = nextnode;
   1515            }
   1516            SDL_GL_DeleteContext(data->context);
   1517        }
   1518        SDL_free(data);
   1519    }
   1520    SDL_free(renderer);
   1521}
   1522
   1523static int
   1524GL_BindTexture (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh)
   1525{
   1526    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1527    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   1528    GL_ActivateRenderer(renderer);
   1529
   1530    data->glEnable(texturedata->type);
   1531    if (texturedata->yuv) {
   1532        data->glActiveTextureARB(GL_TEXTURE2_ARB);
   1533        data->glBindTexture(texturedata->type, texturedata->vtexture);
   1534
   1535        data->glActiveTextureARB(GL_TEXTURE1_ARB);
   1536        data->glBindTexture(texturedata->type, texturedata->utexture);
   1537
   1538        data->glActiveTextureARB(GL_TEXTURE0_ARB);
   1539    }
   1540    data->glBindTexture(texturedata->type, texturedata->texture);
   1541
   1542    if(texw) *texw = (float)texturedata->texw;
   1543    if(texh) *texh = (float)texturedata->texh;
   1544
   1545    return 0;
   1546}
   1547
   1548static int
   1549GL_UnbindTexture (SDL_Renderer * renderer, SDL_Texture *texture)
   1550{
   1551    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
   1552    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
   1553    GL_ActivateRenderer(renderer);
   1554
   1555    if (texturedata->yuv) {
   1556        data->glActiveTextureARB(GL_TEXTURE2_ARB);
   1557        data->glDisable(texturedata->type);
   1558
   1559        data->glActiveTextureARB(GL_TEXTURE1_ARB);
   1560        data->glDisable(texturedata->type);
   1561
   1562        data->glActiveTextureARB(GL_TEXTURE0_ARB);
   1563    }
   1564
   1565    data->glDisable(texturedata->type);
   1566
   1567    return 0;
   1568}
   1569
   1570#endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
   1571
   1572/* vi: set ts=4 sw=4 expandtab: */