source: trunk/src/shader/shader.cpp @ 799

Last change on this file since 799 was 799, checked in by sam, 9 years ago

shader: check that the shader really failed to compile before outputting
the compilation log as an error.

File size: 9.0 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <cmath>
16#include <cstring>
17#include <cstdio>
18
19#ifdef WIN32
20#   define WIN32_LEAN_AND_MEAN
21#   include <windows.h>
22#endif
23
24#include "core.h"
25#include "lolgl.h"
26
27namespace lol
28{
29
30/*
31 * Shader implementation class
32 */
33
34class ShaderData
35{
36    friend class Shader;
37
38private:
39    GLuint prog_id, vert_id, frag_id;
40    uint32_t vert_crc, frag_crc;
41
42    /* Shader patcher */
43    static int GetVersion();
44    static void Patch(char *dst, char const *vert, char const *frag);
45
46    /* Global shader cache */
47    static Shader *shaders[];
48    static int nshaders;
49};
50
51Shader *ShaderData::shaders[256];
52int ShaderData::nshaders = 0;
53
54/*
55 * Public Shader class
56 */
57
58Shader *Shader::Create(char const *vert, char const *frag)
59{
60    uint32_t new_vert_crc = Hash::Crc32(vert);
61    uint32_t new_frag_crc = Hash::Crc32(frag);
62
63    for (int n = 0; n < ShaderData::nshaders; n++)
64    {
65        if (ShaderData::shaders[n]->data->vert_crc == new_vert_crc
66             && ShaderData::shaders[n]->data->frag_crc == new_frag_crc)
67            return ShaderData::shaders[n];
68    }
69
70    Shader *ret = new Shader(vert, frag);
71    ShaderData::shaders[ShaderData::nshaders] = ret;
72    ShaderData::nshaders++;
73    return ret;
74}
75
76void Shader::Destroy(Shader *shader)
77{
78    /* XXX: do nothing! the shader should remain in cache */
79    (void)shader;
80}
81
82Shader::Shader(char const *vert, char const *frag)
83  : data(new ShaderData())
84{
85    char buf[4096], errbuf[4096];
86    char const *shader = buf;
87    GLint status;
88    GLsizei len;
89
90#if !defined __CELLOS_LV2__
91    /* Compile vertex shader */
92    data->vert_crc = Hash::Crc32(vert);
93    ShaderData::Patch(buf, vert, NULL);
94    data->vert_id = glCreateShader(GL_VERTEX_SHADER);
95    glShaderSource(data->vert_id, 1, &shader, NULL);
96    glCompileShader(data->vert_id);
97
98    glGetShaderiv(data->vert_id, GL_COMPILE_STATUS, &status);
99    if (status != GL_TRUE)
100    {
101        glGetShaderInfoLog(data->vert_id, sizeof(errbuf), &len, errbuf);
102        Log::Error("failed to compile vertex shader: %s", errbuf);
103        Log::Error("shader source:\n%s\n", buf);
104    }
105
106    /* Compile fragment shader */
107    data->frag_crc = Hash::Crc32(frag);
108    ShaderData::Patch(buf, NULL, frag);
109    data->frag_id = glCreateShader(GL_FRAGMENT_SHADER);
110    glShaderSource(data->frag_id, 1, &shader, NULL);
111    glCompileShader(data->frag_id);
112
113    glGetShaderiv(data->frag_id, GL_COMPILE_STATUS, &status);
114    if (status != GL_TRUE)
115    {
116        glGetShaderInfoLog(data->frag_id, sizeof(errbuf), &len, errbuf);
117        Log::Error("failed to compile fragment shader: %s", errbuf);
118        Log::Error("shader source:\n%s\n", buf);
119    }
120
121    /* Create program */
122    data->prog_id = glCreateProgram();
123    glAttachShader(data->prog_id, data->vert_id);
124    glAttachShader(data->prog_id, data->frag_id);
125
126    glLinkProgram(data->prog_id);
127    glValidateProgram(data->prog_id);
128#endif
129}
130
131int Shader::GetAttribLocation(char const *attr) const
132{
133#if defined __CELLOS_LV2__
134    return 0;
135#else
136    return glGetAttribLocation(data->prog_id, attr);
137#endif
138}
139
140int Shader::GetUniformLocation(char const *uni) const
141{
142#if defined __CELLOS_LV2__
143    return 0;
144#else
145    return glGetUniformLocation(data->prog_id, uni);
146#endif
147}
148
149void Shader::Bind() const
150{
151#if defined __CELLOS_LV2__
152    ;
153#else
154    glUseProgram(data->prog_id);
155#endif
156}
157
158Shader::~Shader()
159{
160#if defined __CELLOS_LV2__
161    ;
162#else
163    glDetachShader(data->prog_id, data->vert_id);
164    glDetachShader(data->prog_id, data->frag_id);
165    glDeleteShader(data->vert_id);
166    glDeleteShader(data->frag_id);
167    glDeleteProgram(data->prog_id);
168    delete data;
169#endif
170}
171
172/* Try to detect shader compiler features */
173int ShaderData::GetVersion()
174{
175    static int version = 0;
176
177    if (!version)
178    {
179        char buf[4096];
180        GLsizei len;
181
182        int id = glCreateShader(GL_VERTEX_SHADER);
183
184        /* Can we compile 1.30 shaders? */
185        char const *test130 =
186            "#version 130\n"
187            "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
188        glShaderSource(id, 1, &test130, NULL);
189        glCompileShader(id);
190        glGetShaderInfoLog(id, sizeof(buf), &len, buf);
191        if (len <= 0)
192            version = 130;
193
194        /* If not, can we compile 1.20 shaders? */
195        if (!version)
196        {
197            char const *test120 =
198                "#version 120\n"
199                "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
200            glShaderSource(id, 1, &test120, NULL);
201            glCompileShader(id);
202            glGetShaderInfoLog(id, sizeof(buf), &len, buf);
203            if (len <= 0)
204                version = 120;
205        }
206
207        /* Otherwise, assume we can compile 1.10 shaders. */
208        if (!version)
209            version = 110;
210
211        glDeleteShader(id);
212    }
213
214    return version;
215}
216
217/* Simple shader source patching for old GLSL versions.
218 * If supported version is 1.30, do nothing.
219 * If supported version is 1.20:
220 *  - replace "#version 130" with "#version 120"
221 */
222void ShaderData::Patch(char *dst, char const *vert, char const *frag)
223{
224    int ver_driver = GetVersion();
225
226    strcpy(dst, vert ? vert : frag);
227    if (ver_driver >= 130)
228        return;
229
230    int ver_shader = 110;
231    char *parser = strstr(dst, "#version");
232    if (parser)
233        ver_shader = atoi(parser + strlen("#version"));
234
235    if (ver_shader > 120 && ver_driver <= 120)
236    {
237        char const *end = dst + strlen(dst) + 1;
238
239        /* Find main() */
240        parser = strstr(dst, "main");
241        if (!parser) return;
242        parser = strstr(parser, "(");
243        if (!parser) return;
244        parser = strstr(parser, ")");
245        if (!parser) return;
246        parser = strstr(parser, "{");
247        if (!parser) return;
248        char *main = parser + 1;
249
250        /* Perform main() replaces */
251        char const * const main_replaces[] =
252        {
253#if 0
254            "in vec2 in_Vertex;", "vec2 in_Vertex = gl_Vertex.xy;",
255            "in vec3 in_Vertex;", "vec3 in_Vertex = gl_Vertex.xyz;",
256            "in vec4 in_Vertex;", "vec4 in_Vertex = gl_Vertex.xyzw;",
257
258            "in vec2 in_Color;", "vec2 in_Color = gl_Color.xy;",
259            "in vec3 in_Color;", "vec3 in_Color = gl_Color.xyz;",
260            "in vec4 in_Color;", "vec4 in_Color = gl_Color.xyzw;",
261
262            "in vec2 in_MultiTexCoord0;",
263               "vec2 in_MultiTexCoord0 = gl_MultiTexCoord0.xy;",
264            "in vec2 in_MultiTexCoord1;",
265               "vec2 in_MultiTexCoord1 = gl_MultiTexCoord1.xy;",
266            "in vec2 in_MultiTexCoord2;",
267               "vec2 in_MultiTexCoord2 = gl_MultiTexCoord2.xy;",
268            "in vec2 in_MultiTexCoord3;",
269               "vec2 in_MultiTexCoord3 = gl_MultiTexCoord3.xy;",
270            "in vec2 in_MultiTexCoord4;",
271               "vec2 in_MultiTexCoord4 = gl_MultiTexCoord4.xy;",
272            "in vec2 in_MultiTexCoord5;",
273               "vec2 in_MultiTexCoord5 = gl_MultiTexCoord5.xy;",
274            "in vec2 in_MultiTexCoord6;",
275               "vec2 in_MultiTexCoord6 = gl_MultiTexCoord6.xy;",
276            "in vec2 in_MultiTexCoord7;",
277               "vec2 in_MultiTexCoord7 = gl_MultiTexCoord7.xy;",
278#endif
279
280            NULL
281        };
282
283        for (char const * const *rep = main_replaces; rep[0]; rep += 2)
284        {
285            char *match = strstr(dst, rep[0]);
286            if (match && match < main)
287            {
288                size_t l0 = strlen(rep[0]);
289                size_t l1 = strlen(rep[1]);
290                memmove(main + l1, main, end - main);
291                memcpy(main, rep[1], l1);
292                memset(match, ' ', l0);
293                main += l1;
294                end += l1;
295            }
296        }
297
298        /* Perform small replaces */
299        char const * const fast_replaces[] =
300        {
301            "#version 130", "#version 120",
302            "in vec2", vert ? "attribute vec2" : "varying vec2",
303            "in vec3", vert ? "attribute vec3" : "varying vec3",
304            "in vec4", vert ? "attribute vec4" : "varying vec4",
305            "in mat4", vert ? "attribute mat4" : "varying mat4",
306            "out vec2", "varying vec2",
307            "out vec3", "varying vec3",
308            "out vec4", "varying vec4",
309            "out mat4", "varying mat4",
310            NULL
311        };
312
313        for (char const * const *rep = fast_replaces; rep[0]; rep += 2)
314        {
315            char *match;
316            while ((match = strstr(dst, rep[0])))
317            {
318                size_t l0 = strlen(rep[0]);
319                size_t l1 = strlen(rep[1]);
320
321                if (l1 > l0)
322                    memmove(match + l1, match + l0, (end - match) - l0);
323                memcpy(match, rep[1], l1);
324                if (l1 < l0)
325                    memset(match + l0, ' ', l1 - l0);
326                end += l1 - l0;
327            }
328        }
329    }
330}
331
332} /* namespace lol */
333
Note: See TracBrowser for help on using the repository browser.