src/render/opengl/SDL_shaders_gl.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 08 Feb 2011 20:31:39 -0800
changeset 5231 e82908c86c9c
parent 5230 8efa43b915be
child 5237 55b31686f82b
permissions -rw-r--r--
Made the shaders easier to debug when they don't compile
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #if SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED
    25 
    26 #include "SDL_stdinc.h"
    27 #include "SDL_log.h"
    28 #include "SDL_opengl.h"
    29 #include "SDL_video.h"
    30 #include "SDL_shaders_gl.h"
    31 
    32 /* OpenGL shader implementation */
    33 
    34 /*#define DEBUG_SHADERS*/
    35 
    36 typedef struct
    37 {
    38     GLenum program;
    39     GLenum vert_shader;
    40     GLenum frag_shader;
    41 } GL_ShaderData;
    42 
    43 struct GL_ShaderContext
    44 {
    45     GLenum (*glGetError)(void);
    46 
    47     PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
    48     PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
    49     PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
    50     PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
    51     PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
    52     PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
    53     PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
    54     PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
    55     PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
    56     PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
    57     PFNGLUNIFORM1IARBPROC glUniform1iARB;
    58     PFNGLUNIFORM1FARBPROC glUniform1fARB;
    59     PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
    60 
    61     SDL_bool GL_ARB_texture_rectangle_supported;
    62 
    63     GL_Shader current_shader;
    64     GL_ShaderData shaders[NUM_SHADERS];
    65 };
    66 
    67 /*
    68  * NOTE: Always use sampler2D, etc here. We'll #define them to the
    69  *  texture_rectangle versions if we choose to use that extension.
    70  */
    71 static const char *shader_source[NUM_SHADERS][2] =
    72 {
    73     /* SHADER_NONE */
    74     { NULL, NULL },
    75 
    76     /* SHADER_SOLID */
    77     {
    78         /* vertex shader */
    79 "varying vec4 v_color;\n"
    80 "\n"
    81 "void main()\n"
    82 "{\n"
    83 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
    84 "    v_color = gl_Color;\n"
    85 "}",
    86         /* fragment shader */
    87 "varying vec4 v_color;\n"
    88 "\n"
    89 "void main()\n"
    90 "{\n"
    91 "    gl_FragColor = v_color;\n"
    92 "}"
    93     },
    94 
    95     /* SHADER_RGB */
    96     {
    97         /* vertex shader */
    98 "varying vec4 v_color;\n"
    99 "varying vec2 v_texCoord;\n"
   100 "\n"
   101 "void main()\n"
   102 "{\n"
   103 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
   104 "    v_color = gl_Color;\n"
   105 "    v_texCoord = vec2(gl_MultiTexCoord0);\n"
   106 "}",
   107         /* fragment shader */
   108 "varying vec4 v_color;\n"
   109 "varying vec2 v_texCoord;\n"
   110 "uniform sampler2D tex0;\n"
   111 "\n"
   112 "void main()\n"
   113 "{\n"
   114 "    gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n"
   115 "}"
   116     },
   117 };
   118 
   119 static SDL_bool
   120 CompileShader(GL_ShaderContext *ctx, GLenum shader, const char *defines, const char *source)
   121 {
   122     GLint status;
   123     const char *sources[2];
   124 
   125     sources[0] = defines;
   126     sources[1] = source;
   127 
   128     ctx->glShaderSourceARB(shader, SDL_arraysize(sources), sources, NULL);
   129     ctx->glCompileShaderARB(shader);
   130     ctx->glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
   131     if (status == 0) {
   132         GLint length;
   133         char *info;
   134 
   135         ctx->glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
   136         info = SDL_stack_alloc(char, length+1);
   137         ctx->glGetInfoLogARB(shader, length, NULL, info);
   138         SDL_LogError(SDL_LOG_CATEGORY_RENDER,
   139             "Failed to compile shader:\n%s%s\n%s", defines, source, info);
   140 #ifdef DEBUG_SHADERS
   141         fprintf(stderr,
   142             "Failed to compile shader:\n%s%s\n%s", defines, source, info);
   143 #endif
   144         SDL_stack_free(info);
   145 
   146         return SDL_FALSE;
   147     } else {
   148         return SDL_TRUE;
   149     }
   150 }
   151 
   152 static SDL_bool
   153 CompileShaderProgram(GL_ShaderContext *ctx, int index, GL_ShaderData *data)
   154 {
   155     const int num_tmus_bound = 4;
   156     const char *vert_defines = "";
   157     const char *frag_defines = "";
   158     int i;
   159     GLint location;
   160 
   161     if (index == SHADER_NONE) {
   162         return SDL_TRUE;
   163     }
   164 
   165     ctx->glGetError();
   166 
   167     /* Make sure we use the correct sampler type for our texture type */
   168     if (ctx->GL_ARB_texture_rectangle_supported) {
   169         frag_defines = 
   170 "#define sampler2D sampler2DRect\n"
   171 "#define texture2D texture2DRect\n";
   172     }
   173 
   174     /* Create one program object to rule them all */
   175     data->program = ctx->glCreateProgramObjectARB();
   176 
   177     /* Create the vertex shader */
   178     data->vert_shader = ctx->glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
   179     if (!CompileShader(ctx, data->vert_shader, vert_defines, shader_source[index][0])) {
   180         return SDL_FALSE;
   181     }
   182 
   183     /* Create the fragment shader */
   184     data->frag_shader = ctx->glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
   185     if (!CompileShader(ctx, data->frag_shader, frag_defines, shader_source[index][1])) {
   186         return SDL_FALSE;
   187     }
   188 
   189     /* ... and in the darkness bind them */
   190     ctx->glAttachObjectARB(data->program, data->vert_shader);
   191     ctx->glAttachObjectARB(data->program, data->frag_shader);
   192     ctx->glLinkProgramARB(data->program);
   193 
   194     /* Set up some uniform variables */
   195     ctx->glUseProgramObjectARB(data->program);
   196     for (i = 0; i < num_tmus_bound; ++i) {
   197         char tex_name[5];
   198         SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i);
   199         location = ctx->glGetUniformLocationARB(data->program, tex_name);
   200         if (location >= 0) {
   201             ctx->glUniform1iARB(location, i);
   202         }
   203     }
   204     ctx->glUseProgramObjectARB(0);
   205     
   206     return (ctx->glGetError() == GL_NO_ERROR);
   207 }
   208 
   209 static void
   210 DestroyShaderProgram(GL_ShaderContext *ctx, GL_ShaderData *data)
   211 {
   212     if (index == SHADER_NONE) {
   213         return;
   214     }
   215 
   216     ctx->glDeleteObjectARB(data->vert_shader);
   217     ctx->glDeleteObjectARB(data->frag_shader);
   218     ctx->glDeleteObjectARB(data->program);
   219 }
   220 
   221 GL_ShaderContext *
   222 GL_CreateShaderContext()
   223 {
   224     GL_ShaderContext *ctx;
   225     SDL_bool shaders_supported;
   226     int i;
   227 
   228     ctx = (GL_ShaderContext *)SDL_calloc(1, sizeof(*ctx));
   229     if (!ctx) {
   230         return NULL;
   231     }
   232 
   233     if (SDL_GL_ExtensionSupported("GL_ARB_texture_rectangle")
   234         || SDL_GL_ExtensionSupported("GL_EXT_texture_rectangle")) {
   235         ctx->GL_ARB_texture_rectangle_supported = SDL_TRUE;
   236     }
   237 
   238     /* Check for shader support */
   239     shaders_supported = SDL_FALSE;
   240     if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") &&
   241         SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") &&
   242         SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") &&
   243         SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) {
   244         ctx->glGetError = (GLenum (*)(void)) SDL_GL_GetProcAddress("glGetError");
   245         ctx->glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
   246         ctx->glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
   247         ctx->glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
   248         ctx->glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
   249         ctx->glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
   250         ctx->glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
   251         ctx->glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
   252         ctx->glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
   253         ctx->glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
   254         ctx->glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
   255         ctx->glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
   256         ctx->glUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB");
   257         ctx->glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
   258         if (ctx->glGetError &&
   259             ctx->glAttachObjectARB &&
   260             ctx->glCompileShaderARB &&
   261             ctx->glCreateProgramObjectARB &&
   262             ctx->glCreateShaderObjectARB &&
   263             ctx->glDeleteObjectARB &&
   264             ctx->glGetInfoLogARB &&
   265             ctx->glGetObjectParameterivARB &&
   266             ctx->glGetUniformLocationARB &&
   267             ctx->glLinkProgramARB &&
   268             ctx->glShaderSourceARB &&
   269             ctx->glUniform1iARB &&
   270             ctx->glUniform1fARB &&
   271             ctx->glUseProgramObjectARB) {
   272             shaders_supported = SDL_TRUE;
   273         }
   274     }
   275 
   276     if (!shaders_supported) {
   277         GL_DestroyShaderContext(ctx);
   278         return NULL;
   279     }
   280 
   281     /* Compile all the shaders */
   282     for (i = 0; i < NUM_SHADERS; ++i) {
   283         if (!CompileShaderProgram(ctx, i, &ctx->shaders[i])) {
   284 fprintf(stderr, "Unable to compile shader!\n");
   285             GL_DestroyShaderContext(ctx);
   286             return NULL;
   287         }
   288     }
   289 
   290     /* We're done! */
   291     return ctx;
   292 }
   293 
   294 void
   295 GL_SelectShader(GL_ShaderContext *ctx, GL_Shader shader)
   296 {
   297     /* Nothing to do if there's no shader support */
   298     if (!ctx) {
   299         return;
   300     }
   301 
   302     /* Nothing to do if there's no shader change */
   303     if (shader == ctx->current_shader) {
   304         return;
   305     }
   306 
   307     ctx->glUseProgramObjectARB(ctx->shaders[shader].program);
   308     ctx->current_shader = shader;
   309 }
   310 
   311 void
   312 GL_DestroyShaderContext(GL_ShaderContext *ctx)
   313 {
   314     int i;
   315 
   316     for (i = 0; i < NUM_SHADERS; ++i) {
   317         DestroyShaderProgram(ctx, &ctx->shaders[i]);
   318     }
   319     SDL_free(ctx);
   320 }
   321 
   322 #endif /* SDL_VIDEO_RENDER_OGL && !SDL_RENDER_DISABLED */
   323 
   324 /* vi: set ts=4 sw=4 expandtab: */