source: trunk/src/shader.cpp @ 776

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

shader: support most standard parameters in the shader patcher.

File size: 8.3 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 *src);
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    GLsizei len;
88
89#if !defined __CELLOS_LV2__
90    /* Compile vertex shader */
91    ShaderData::Patch(buf, vert);
92    data->vert_crc = Hash::Crc32(buf);
93    data->vert_id = glCreateShader(GL_VERTEX_SHADER);
94    glShaderSource(data->vert_id, 1, &shader, NULL);
95    glCompileShader(data->vert_id);
96
97    glGetShaderInfoLog(data->vert_id, sizeof(errbuf), &len, errbuf);
98    if (len > 0)
99    {
100        Log::Error("failed to compile vertex shader: %s", errbuf);
101        Log::Error("shader source:\n%s\n", buf);
102    }
103
104    /* Compile fragment shader */
105    ShaderData::Patch(buf, frag);
106    data->frag_crc = Hash::Crc32(buf);
107    data->frag_id = glCreateShader(GL_FRAGMENT_SHADER);
108    glShaderSource(data->frag_id, 1, &shader, NULL);
109    glCompileShader(data->frag_id);
110
111    glGetShaderInfoLog(data->frag_id, sizeof(errbuf), &len, errbuf);
112    if (len > 0)
113    {
114        Log::Error("failed to compile fragment shader: %s", errbuf);
115        Log::Error("shader source:\n%s\n", buf);
116    }
117
118    /* Create program */
119    data->prog_id = glCreateProgram();
120    glAttachShader(data->prog_id, data->vert_id);
121    glAttachShader(data->prog_id, data->frag_id);
122
123    glLinkProgram(data->prog_id);
124    glValidateProgram(data->prog_id);
125#endif
126}
127
128int Shader::GetAttribLocation(char const *attr) const
129{
130#if defined __CELLOS_LV2__
131    return 0;
132#else
133    return glGetAttribLocation(data->prog_id, attr);
134#endif
135}
136
137int Shader::GetUniformLocation(char const *uni) const
138{
139#if defined __CELLOS_LV2__
140    return 0;
141#else
142    return glGetUniformLocation(data->prog_id, uni);
143#endif
144}
145
146void Shader::Bind() const
147{
148#if defined __CELLOS_LV2__
149    ;
150#else
151    glUseProgram(data->prog_id);
152#endif
153}
154
155Shader::~Shader()
156{
157#if defined __CELLOS_LV2__
158    ;
159#else
160    glDetachShader(data->prog_id, data->vert_id);
161    glDetachShader(data->prog_id, data->frag_id);
162    glDeleteShader(data->vert_id);
163    glDeleteShader(data->frag_id);
164    glDeleteProgram(data->prog_id);
165    delete data;
166#endif
167}
168
169/* Try to detect shader compiler features */
170int ShaderData::GetVersion()
171{
172    static int version = 0;
173
174    if (!version)
175    {
176        char buf[4096];
177        GLsizei len;
178
179        int id = glCreateShader(GL_VERTEX_SHADER);
180
181        /* Can we compile 1.30 shaders? */
182        char const *test130 =
183            "#version 130\n"
184            "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
185        glShaderSource(id, 1, &test130, NULL);
186        glCompileShader(id);
187        glGetShaderInfoLog(id, sizeof(buf), &len, buf);
188        if (len <= 0)
189            version = 130;
190
191        /* If not, can we compile 1.20 shaders? */
192        if (!version)
193        {
194            char const *test120 =
195                "#version 120\n"
196                "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
197            glShaderSource(id, 1, &test120, NULL);
198            glCompileShader(id);
199            glGetShaderInfoLog(id, sizeof(buf), &len, buf);
200            if (len <= 0)
201                version = 120;
202        }
203
204        /* Otherwise, assume we can compile 1.10 shaders. */
205        if (!version)
206            version = 110;
207
208        glDeleteShader(id);
209    }
210
211    return version;
212}
213
214/* Simple shader source patching for old GLSL versions.
215 * If supported version is 1.30, do nothing.
216 * If supported version is 1.20:
217 *  - replace "#version 130" with "#version 120"
218 */
219void ShaderData::Patch(char *dst, char const *src)
220{
221    int version = GetVersion();
222
223    strcpy(dst, src);
224    if (version >= 130)
225        return;
226
227    if (version >= 110)
228    {
229        char *parser, *main;
230        char const *end = dst + strlen(dst) + 1;
231
232        /* Find main() */
233        parser = strstr(dst, "main");
234        if (!parser) return;
235        parser = strstr(parser, "(");
236        if (!parser) return;
237        parser = strstr(parser, ")");
238        if (!parser) return;
239        parser = strstr(parser, "{");
240        if (!parser) return;
241        main = parser + 1;
242
243        /* Perform main() replaces */
244        static char const * const main_replaces[] =
245        {
246            "in vec2 in_Vertex;", "vec2 in_Vertex = gl_Vertex.xy;",
247            "in vec3 in_Vertex;", "vec3 in_Vertex = gl_Vertex.xyz;",
248            "in vec4 in_Vertex;", "vec4 in_Vertex = gl_Vertex.xyzw;",
249
250            "in vec2 in_Color;", "vec2 in_Color = gl_Color.xy;",
251            "in vec3 in_Color;", "vec3 in_Color = gl_Color.xyz;",
252            "in vec4 in_Color;", "vec4 in_Color = gl_Color.xyzw;",
253
254            "in vec2 in_MultiTexCoord0;",
255               "vec2 in_MultiTexCoord0 = gl_MultiTexCoord0.xy;",
256            "in vec2 in_MultiTexCoord1;",
257               "vec2 in_MultiTexCoord1 = gl_MultiTexCoord1.xy;",
258            "in vec2 in_MultiTexCoord2;",
259               "vec2 in_MultiTexCoord2 = gl_MultiTexCoord2.xy;",
260            "in vec2 in_MultiTexCoord3;",
261               "vec2 in_MultiTexCoord3 = gl_MultiTexCoord3.xy;",
262            "in vec2 in_MultiTexCoord4;",
263               "vec2 in_MultiTexCoord4 = gl_MultiTexCoord4.xy;",
264            "in vec2 in_MultiTexCoord5;",
265               "vec2 in_MultiTexCoord5 = gl_MultiTexCoord5.xy;",
266            "in vec2 in_MultiTexCoord6;",
267               "vec2 in_MultiTexCoord6 = gl_MultiTexCoord6.xy;",
268            "in vec2 in_MultiTexCoord7;",
269               "vec2 in_MultiTexCoord7 = gl_MultiTexCoord7.xy;",
270
271            NULL
272        };
273
274        for (char const * const *rep = main_replaces; rep[0]; rep += 2)
275        {
276            char *match = strstr(dst, rep[0]);
277            if (match && match < main)
278            {
279                size_t l0 = strlen(rep[0]);
280                size_t l1 = strlen(rep[1]);
281                memmove(main + l1, main, end - main);
282                memcpy(main, rep[1], l1);
283                memset(match, ' ', l0);
284                main += l1;
285                end += l1;
286            }
287        }
288
289        /* Perform small replaces */
290        static char const * const fast_replaces[] =
291        {
292            "#version 130", "#version 120",
293            "\nin vec2", "\n   vec2",
294            "\nin vec3", "\n   vec3",
295            "\nin vec4", "\n   vec4",
296            "\nin mat4", "\n   mat4",
297            NULL
298        };
299
300        for (char const * const *rep = fast_replaces; rep[0]; rep += 2)
301        {
302            char *match;
303            while ((match = strstr(dst, rep[0])))
304            {
305                size_t l0 = strlen(rep[0]);
306                size_t l1 = strlen(rep[1]);
307
308                if (l1 > l0)
309                    memmove(match + l1, match + l0, (end - match) - l0);
310                memcpy(match, rep[1], l1);
311                if (l1 < l0)
312                    memset(match + l0, ' ', l1 - l0);
313                end += l1 - l0;
314            }
315        }
316    }
317}
318
319} /* namespace lol */
320
Note: See TracBrowser for help on using the repository browser.