sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_shaders_gl.c (20906B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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_stdinc.h"
     26 #include "SDL_opengl.h"
     27 #include "SDL_video.h"
     28 #include "SDL_shaders_gl.h"
     29 
     30 /* OpenGL shader implementation */
     31 
     32 /* #define DEBUG_SHADERS */
     33 
     34 typedef struct
     35 {
     36     GLhandleARB program;
     37     GLhandleARB vert_shader;
     38     GLhandleARB frag_shader;
     39 } GL_ShaderData;
     40 
     41 struct GL_ShaderContext
     42 {
     43     GLenum (*glGetError)(void);
     44 
     45     PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
     46     PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
     47     PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
     48     PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
     49     PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
     50     PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
     51     PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
     52     PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
     53     PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
     54     PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
     55     PFNGLUNIFORM1IARBPROC glUniform1iARB;
     56     PFNGLUNIFORM1FARBPROC glUniform1fARB;
     57     PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
     58 
     59     SDL_bool GL_ARB_texture_rectangle_supported;
     60 
     61     GL_ShaderData shaders[NUM_SHADERS];
     62 };
     63 
     64 #define COLOR_VERTEX_SHADER                                     \
     65 "varying vec4 v_color;\n"                                       \
     66 "\n"                                                            \
     67 "void main()\n"                                                 \
     68 "{\n"                                                           \
     69 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \
     70 "    v_color = gl_Color;\n"                                     \
     71 "}"                                                             \
     72 
     73 #define TEXTURE_VERTEX_SHADER                                   \
     74 "varying vec4 v_color;\n"                                       \
     75 "varying vec2 v_texCoord;\n"                                    \
     76 "\n"                                                            \
     77 "void main()\n"                                                 \
     78 "{\n"                                                           \
     79 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" \
     80 "    v_color = gl_Color;\n"                                     \
     81 "    v_texCoord = vec2(gl_MultiTexCoord0);\n"                   \
     82 "}"                                                             \
     83 
     84 #define JPEG_SHADER_CONSTANTS                                   \
     85 "// YUV offset \n"                                              \
     86 "const vec3 offset = vec3(0, -0.501960814, -0.501960814);\n"    \
     87 "\n"                                                            \
     88 "// RGB coefficients \n"                                        \
     89 "const vec3 Rcoeff = vec3(1,  0.000,  1.402);\n"                \
     90 "const vec3 Gcoeff = vec3(1, -0.3441, -0.7141);\n"              \
     91 "const vec3 Bcoeff = vec3(1,  1.772,  0.000);\n"                \
     92 
     93 #define BT601_SHADER_CONSTANTS                                  \
     94 "// YUV offset \n"                                              \
     95 "const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \
     96 "\n"                                                            \
     97 "// RGB coefficients \n"                                        \
     98 "const vec3 Rcoeff = vec3(1.1644,  0.000,  1.596);\n"           \
     99 "const vec3 Gcoeff = vec3(1.1644, -0.3918, -0.813);\n"          \
    100 "const vec3 Bcoeff = vec3(1.1644,  2.0172,  0.000);\n"          \
    101 
    102 #define BT709_SHADER_CONSTANTS                                  \
    103 "// YUV offset \n"                                              \
    104 "const vec3 offset = vec3(-0.0627451017, -0.501960814, -0.501960814);\n" \
    105 "\n"                                                            \
    106 "// RGB coefficients \n"                                        \
    107 "const vec3 Rcoeff = vec3(1.1644,  0.000,  1.7927);\n"          \
    108 "const vec3 Gcoeff = vec3(1.1644, -0.2132, -0.5329);\n"         \
    109 "const vec3 Bcoeff = vec3(1.1644,  2.1124,  0.000);\n"          \
    110 
    111 #define YUV_SHADER_PROLOGUE                                     \
    112 "varying vec4 v_color;\n"                                       \
    113 "varying vec2 v_texCoord;\n"                                    \
    114 "uniform sampler2D tex0; // Y \n"                               \
    115 "uniform sampler2D tex1; // U \n"                               \
    116 "uniform sampler2D tex2; // V \n"                               \
    117 "\n"                                                            \
    118 
    119 #define YUV_SHADER_BODY                                         \
    120 "\n"                                                            \
    121 "void main()\n"                                                 \
    122 "{\n"                                                           \
    123 "    vec2 tcoord;\n"                                            \
    124 "    vec3 yuv, rgb;\n"                                          \
    125 "\n"                                                            \
    126 "    // Get the Y value \n"                                     \
    127 "    tcoord = v_texCoord;\n"                                    \
    128 "    yuv.x = texture2D(tex0, tcoord).r;\n"                      \
    129 "\n"                                                            \
    130 "    // Get the U and V values \n"                              \
    131 "    tcoord *= UVCoordScale;\n"                                 \
    132 "    yuv.y = texture2D(tex1, tcoord).r;\n"                      \
    133 "    yuv.z = texture2D(tex2, tcoord).r;\n"                      \
    134 "\n"                                                            \
    135 "    // Do the color transform \n"                              \
    136 "    yuv += offset;\n"                                          \
    137 "    rgb.r = dot(yuv, Rcoeff);\n"                               \
    138 "    rgb.g = dot(yuv, Gcoeff);\n"                               \
    139 "    rgb.b = dot(yuv, Bcoeff);\n"                               \
    140 "\n"                                                            \
    141 "    // That was easy. :) \n"                                   \
    142 "    gl_FragColor = vec4(rgb, 1.0) * v_color;\n"                \
    143 "}"                                                             \
    144 
    145 #define NV12_SHADER_PROLOGUE                                    \
    146 "varying vec4 v_color;\n"                                       \
    147 "varying vec2 v_texCoord;\n"                                    \
    148 "uniform sampler2D tex0; // Y \n"                               \
    149 "uniform sampler2D tex1; // U/V \n"                             \
    150 "\n"                                                            \
    151 
    152 #define NV12_SHADER_BODY                                        \
    153 "\n"                                                            \
    154 "void main()\n"                                                 \
    155 "{\n"                                                           \
    156 "    vec2 tcoord;\n"                                            \
    157 "    vec3 yuv, rgb;\n"                                          \
    158 "\n"                                                            \
    159 "    // Get the Y value \n"                                     \
    160 "    tcoord = v_texCoord;\n"                                    \
    161 "    yuv.x = texture2D(tex0, tcoord).r;\n"                      \
    162 "\n"                                                            \
    163 "    // Get the U and V values \n"                              \
    164 "    tcoord *= UVCoordScale;\n"                                 \
    165 "    yuv.yz = texture2D(tex1, tcoord).ra;\n"                    \
    166 "\n"                                                            \
    167 "    // Do the color transform \n"                              \
    168 "    yuv += offset;\n"                                          \
    169 "    rgb.r = dot(yuv, Rcoeff);\n"                               \
    170 "    rgb.g = dot(yuv, Gcoeff);\n"                               \
    171 "    rgb.b = dot(yuv, Bcoeff);\n"                               \
    172 "\n"                                                            \
    173 "    // That was easy. :) \n"                                   \
    174 "    gl_FragColor = vec4(rgb, 1.0) * v_color;\n"                \
    175 "}"                                                             \
    176 
    177 #define NV21_SHADER_PROLOGUE                                    \
    178 "varying vec4 v_color;\n"                                       \
    179 "varying vec2 v_texCoord;\n"                                    \
    180 "uniform sampler2D tex0; // Y \n"                               \
    181 "uniform sampler2D tex1; // U/V \n"                             \
    182 "\n"                                                            \
    183 
    184 #define NV21_SHADER_BODY                                        \
    185 "\n"                                                            \
    186 "void main()\n"                                                 \
    187 "{\n"                                                           \
    188 "    vec2 tcoord;\n"                                            \
    189 "    vec3 yuv, rgb;\n"                                          \
    190 "\n"                                                            \
    191 "    // Get the Y value \n"                                     \
    192 "    tcoord = v_texCoord;\n"                                    \
    193 "    yuv.x = texture2D(tex0, tcoord).r;\n"                      \
    194 "\n"                                                            \
    195 "    // Get the U and V values \n"                              \
    196 "    tcoord *= UVCoordScale;\n"                                 \
    197 "    yuv.yz = texture2D(tex1, tcoord).ar;\n"                    \
    198 "\n"                                                            \
    199 "    // Do the color transform \n"                              \
    200 "    yuv += offset;\n"                                          \
    201 "    rgb.r = dot(yuv, Rcoeff);\n"                               \
    202 "    rgb.g = dot(yuv, Gcoeff);\n"                               \
    203 "    rgb.b = dot(yuv, Bcoeff);\n"                               \
    204 "\n"                                                            \
    205 "    // That was easy. :) \n"                                   \
    206 "    gl_FragColor = vec4(rgb, 1.0) * v_color;\n"                \
    207 "}"                                                             \
    208 
    209 /*
    210  * NOTE: Always use sampler2D, etc here. We'll #define them to the
    211  *  texture_rectangle versions if we choose to use that extension.
    212  */
    213 static const char *shader_source[NUM_SHADERS][2] =
    214 {
    215     /* SHADER_NONE */
    216     { NULL, NULL },
    217 
    218     /* SHADER_SOLID */
    219     {
    220         /* vertex shader */
    221         COLOR_VERTEX_SHADER,
    222         /* fragment shader */
    223 "varying vec4 v_color;\n"
    224 "\n"
    225 "void main()\n"
    226 "{\n"
    227 "    gl_FragColor = v_color;\n"
    228 "}"
    229     },
    230 
    231     /* SHADER_RGB */
    232     {
    233         /* vertex shader */
    234         TEXTURE_VERTEX_SHADER,
    235         /* fragment shader */
    236 "varying vec4 v_color;\n"
    237 "varying vec2 v_texCoord;\n"
    238 "uniform sampler2D tex0;\n"
    239 "\n"
    240 "void main()\n"
    241 "{\n"
    242 "    gl_FragColor = texture2D(tex0, v_texCoord);\n"
    243 "    gl_FragColor.a = 1.0;\n"
    244 "    gl_FragColor *= v_color;\n"
    245 "}"
    246     },
    247 
    248     /* SHADER_RGBA */
    249     {
    250         /* vertex shader */
    251         TEXTURE_VERTEX_SHADER,
    252         /* fragment shader */
    253 "varying vec4 v_color;\n"
    254 "varying vec2 v_texCoord;\n"
    255 "uniform sampler2D tex0;\n"
    256 "\n"
    257 "void main()\n"
    258 "{\n"
    259 "    gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n"
    260 "}"
    261     },
    262 
    263     /* SHADER_YUV_JPEG */
    264     {
    265         /* vertex shader */
    266         TEXTURE_VERTEX_SHADER,
    267         /* fragment shader */
    268         YUV_SHADER_PROLOGUE
    269         JPEG_SHADER_CONSTANTS
    270         YUV_SHADER_BODY
    271     },
    272     /* SHADER_YUV_BT601 */
    273     {
    274         /* vertex shader */
    275         TEXTURE_VERTEX_SHADER,
    276         /* fragment shader */
    277         YUV_SHADER_PROLOGUE
    278         BT601_SHADER_CONSTANTS
    279         YUV_SHADER_BODY
    280     },
    281     /* SHADER_YUV_BT709 */
    282     {
    283         /* vertex shader */
    284         TEXTURE_VERTEX_SHADER,
    285         /* fragment shader */
    286         YUV_SHADER_PROLOGUE
    287         BT709_SHADER_CONSTANTS
    288         YUV_SHADER_BODY
    289     },
    290     /* SHADER_NV12_JPEG */
    291     {
    292         /* vertex shader */
    293         TEXTURE_VERTEX_SHADER,
    294         /* fragment shader */
    295         NV12_SHADER_PROLOGUE
    296         JPEG_SHADER_CONSTANTS
    297         NV12_SHADER_BODY
    298     },
    299     /* SHADER_NV12_BT601 */
    300     {
    301         /* vertex shader */
    302         TEXTURE_VERTEX_SHADER,
    303         /* fragment shader */
    304         NV12_SHADER_PROLOGUE
    305         BT601_SHADER_CONSTANTS
    306         NV12_SHADER_BODY
    307     },
    308     /* SHADER_NV12_BT709 */
    309     {
    310         /* vertex shader */
    311         TEXTURE_VERTEX_SHADER,
    312         /* fragment shader */
    313         NV12_SHADER_PROLOGUE
    314         BT709_SHADER_CONSTANTS
    315         NV12_SHADER_BODY
    316     },
    317     /* SHADER_NV21_JPEG */
    318     {
    319         /* vertex shader */
    320         TEXTURE_VERTEX_SHADER,
    321         /* fragment shader */
    322         NV21_SHADER_PROLOGUE
    323         JPEG_SHADER_CONSTANTS
    324         NV21_SHADER_BODY
    325     },
    326     /* SHADER_NV21_BT601 */
    327     {
    328         /* vertex shader */
    329         TEXTURE_VERTEX_SHADER,
    330         /* fragment shader */
    331         NV21_SHADER_PROLOGUE
    332         BT601_SHADER_CONSTANTS
    333         NV21_SHADER_BODY
    334     },
    335     /* SHADER_NV21_BT709 */
    336     {
    337         /* vertex shader */
    338         TEXTURE_VERTEX_SHADER,
    339         /* fragment shader */
    340         NV21_SHADER_PROLOGUE
    341         BT709_SHADER_CONSTANTS
    342         NV21_SHADER_BODY
    343     },
    344 };
    345 
    346 static SDL_bool
    347 CompileShader(GL_ShaderContext *ctx, GLhandleARB shader, const char *defines, const char *source)
    348 {
    349     GLint status;
    350     const char *sources[2];
    351 
    352     sources[0] = defines;
    353     sources[1] = source;
    354 
    355     ctx->glShaderSourceARB(shader, SDL_arraysize(sources), sources, NULL);
    356     ctx->glCompileShaderARB(shader);
    357     ctx->glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
    358     if (status == 0) {
    359         SDL_bool isstack;
    360         GLint length;
    361         char *info;
    362 
    363         ctx->glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
    364         info = SDL_small_alloc(char, length+1, &isstack);
    365         ctx->glGetInfoLogARB(shader, length, NULL, info);
    366         SDL_LogError(SDL_LOG_CATEGORY_RENDER,
    367             "Failed to compile shader:\n%s%s\n%s", defines, source, info);
    368 #ifdef DEBUG_SHADERS
    369         fprintf(stderr,
    370             "Failed to compile shader:\n%s%s\n%s", defines, source, info);
    371 #endif
    372         SDL_small_free(info, isstack);
    373 
    374         return SDL_FALSE;
    375     } else {
    376         return SDL_TRUE;
    377     }
    378 }
    379 
    380 static SDL_bool
    381 CompileShaderProgram(GL_ShaderContext *ctx, int index, GL_ShaderData *data)
    382 {
    383     const int num_tmus_bound = 4;
    384     const char *vert_defines = "";
    385     const char *frag_defines = "";
    386     int i;
    387     GLint location;
    388 
    389     if (index == SHADER_NONE) {
    390         return SDL_TRUE;
    391     }
    392 
    393     ctx->glGetError();
    394 
    395     /* Make sure we use the correct sampler type for our texture type */
    396     if (ctx->GL_ARB_texture_rectangle_supported) {
    397         frag_defines =
    398 "#define sampler2D sampler2DRect\n"
    399 "#define texture2D texture2DRect\n"
    400 "#define UVCoordScale 0.5\n";
    401     } else {
    402         frag_defines = 
    403 "#define UVCoordScale 1.0\n";
    404     }
    405 
    406     /* Create one program object to rule them all */
    407     data->program = ctx->glCreateProgramObjectARB();
    408 
    409     /* Create the vertex shader */
    410     data->vert_shader = ctx->glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
    411     if (!CompileShader(ctx, data->vert_shader, vert_defines, shader_source[index][0])) {
    412         return SDL_FALSE;
    413     }
    414 
    415     /* Create the fragment shader */
    416     data->frag_shader = ctx->glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
    417     if (!CompileShader(ctx, data->frag_shader, frag_defines, shader_source[index][1])) {
    418         return SDL_FALSE;
    419     }
    420 
    421     /* ... and in the darkness bind them */
    422     ctx->glAttachObjectARB(data->program, data->vert_shader);
    423     ctx->glAttachObjectARB(data->program, data->frag_shader);
    424     ctx->glLinkProgramARB(data->program);
    425 
    426     /* Set up some uniform variables */
    427     ctx->glUseProgramObjectARB(data->program);
    428     for (i = 0; i < num_tmus_bound; ++i) {
    429         char tex_name[10];
    430         SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i);
    431         location = ctx->glGetUniformLocationARB(data->program, tex_name);
    432         if (location >= 0) {
    433             ctx->glUniform1iARB(location, i);
    434         }
    435     }
    436     ctx->glUseProgramObjectARB(0);
    437 
    438     return (ctx->glGetError() == GL_NO_ERROR);
    439 }
    440 
    441 static void
    442 DestroyShaderProgram(GL_ShaderContext *ctx, GL_ShaderData *data)
    443 {
    444     ctx->glDeleteObjectARB(data->vert_shader);
    445     ctx->glDeleteObjectARB(data->frag_shader);
    446     ctx->glDeleteObjectARB(data->program);
    447 }
    448 
    449 GL_ShaderContext *
    450 GL_CreateShaderContext(void)
    451 {
    452     GL_ShaderContext *ctx;
    453     SDL_bool shaders_supported;
    454     int i;
    455 
    456     ctx = (GL_ShaderContext *)SDL_calloc(1, sizeof(*ctx));
    457     if (!ctx) {
    458         return NULL;
    459     }
    460 
    461     if (!SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two") &&
    462         (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle") ||
    463          SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle"))) {
    464         ctx->GL_ARB_texture_rectangle_supported = SDL_TRUE;
    465     }
    466 
    467     /* Check for shader support */
    468     shaders_supported = SDL_FALSE;
    469     if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") &&
    470         SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") &&
    471         SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") &&
    472         SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) {
    473         ctx->glGetError = (GLenum (*)(void)) SDL_GL_GetProcAddress("glGetError");
    474         ctx->glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
    475         ctx->glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
    476         ctx->glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
    477         ctx->glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
    478         ctx->glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
    479         ctx->glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
    480         ctx->glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
    481         ctx->glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
    482         ctx->glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
    483         ctx->glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
    484         ctx->glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
    485         ctx->glUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB");
    486         ctx->glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
    487         if (ctx->glGetError &&
    488             ctx->glAttachObjectARB &&
    489             ctx->glCompileShaderARB &&
    490             ctx->glCreateProgramObjectARB &&
    491             ctx->glCreateShaderObjectARB &&
    492             ctx->glDeleteObjectARB &&
    493             ctx->glGetInfoLogARB &&
    494             ctx->glGetObjectParameterivARB &&
    495             ctx->glGetUniformLocationARB &&
    496             ctx->glLinkProgramARB &&
    497             ctx->glShaderSourceARB &&
    498             ctx->glUniform1iARB &&
    499             ctx->glUniform1fARB &&
    500             ctx->glUseProgramObjectARB) {
    501             shaders_supported = SDL_TRUE;
    502         }
    503     }
    504 
    505     if (!shaders_supported) {
    506         SDL_free(ctx);
    507         return NULL;
    508     }
    509 
    510     /* Compile all the shaders */
    511     for (i = 0; i < NUM_SHADERS; ++i) {
    512         if (!CompileShaderProgram(ctx, i, &ctx->shaders[i])) {
    513             GL_DestroyShaderContext(ctx);
    514             return NULL;
    515         }
    516     }
    517 
    518     /* We're done! */
    519     return ctx;
    520 }
    521 
    522 void
    523 GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader)
    524 {
    525     ctx->glUseProgramObjectARB(ctx->shaders[shader].program);
    526 }
    527 
    528 void
    529 GL_DestroyShaderContext(GL_ShaderContext *ctx)
    530 {
    531     int i;
    532 
    533     for (i = 0; i < NUM_SHADERS; ++i) {
    534         DestroyShaderProgram(ctx, &ctx->shaders[i]);
    535     }
    536     SDL_free(ctx);
    537 }
    538 
    539 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
    540 
    541 /* vi: set ts=4 sw=4 expandtab: */