source: trunk/src/shader.cpp @ 775

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

shader: write a minimalist shader patcher for future GLSL 1.20 compatibility.

File size: 5.8 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];
86    char const *shader = buf;
87    GLsizei len;
88
89#if !defined __CELLOS_LV2__
90
91    /* Compile vertex shader and fragment shader */
92    ShaderData::Patch(buf, vert);
93    data->vert_crc = Hash::Crc32(buf);
94    data->vert_id = glCreateShader(GL_VERTEX_SHADER);
95    glShaderSource(data->vert_id, 1, &shader, NULL);
96    glCompileShader(data->vert_id);
97
98    glGetShaderInfoLog(data->vert_id, sizeof(buf), &len, buf);
99    if (len > 0)
100        Log::Error("failed to compile vertex shader: %s", buf);
101
102    ShaderData::Patch(buf, frag);
103    data->frag_crc = Hash::Crc32(buf);
104    data->frag_id = glCreateShader(GL_FRAGMENT_SHADER);
105    glShaderSource(data->frag_id, 1, &shader, NULL);
106    glCompileShader(data->frag_id);
107
108    glGetShaderInfoLog(data->frag_id, sizeof(buf), &len, buf);
109    if (len > 0)
110        Log::Error("failed to compile fragment shader: %s", buf);
111
112    data->prog_id = glCreateProgram();
113    glAttachShader(data->prog_id, data->vert_id);
114    glAttachShader(data->prog_id, data->frag_id);
115
116    glLinkProgram(data->prog_id);
117    glValidateProgram(data->prog_id);
118#endif
119}
120
121int Shader::GetAttribLocation(char const *attr) const
122{
123#if defined __CELLOS_LV2__
124    return 0;
125#else
126    return glGetAttribLocation(data->prog_id, attr);
127#endif
128}
129
130int Shader::GetUniformLocation(char const *uni) const
131{
132#if defined __CELLOS_LV2__
133    return 0;
134#else
135    return glGetUniformLocation(data->prog_id, uni);
136#endif
137}
138
139void Shader::Bind() const
140{
141#if defined __CELLOS_LV2__
142    ;
143#else
144    glUseProgram(data->prog_id);
145#endif
146}
147
148Shader::~Shader()
149{
150#if defined __CELLOS_LV2__
151    ;
152#else
153    glDetachShader(data->prog_id, data->vert_id);
154    glDetachShader(data->prog_id, data->frag_id);
155    glDeleteShader(data->vert_id);
156    glDeleteShader(data->frag_id);
157    glDeleteProgram(data->prog_id);
158    delete data;
159#endif
160}
161
162/* Try to detect shader compiler features */
163int ShaderData::GetVersion()
164{
165    static int version = 0;
166
167    if (!version)
168    {
169        char buf[4096];
170        GLsizei len;
171
172        int id = glCreateShader(GL_VERTEX_SHADER);
173
174        /* Can we compile 1.30 shaders? */
175        char const *test130 =
176            "#version 130\n"
177            "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
178        glShaderSource(id, 1, &test130, NULL);
179        glCompileShader(id);
180        glGetShaderInfoLog(id, sizeof(buf), &len, buf);
181        if (len <= 0)
182            version = 130;
183
184        /* If not, can we compile 1.20 shaders? */
185        if (!version)
186        {
187            char const *test120 =
188                "#version 120\n"
189                "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
190            glShaderSource(id, 1, &test120, NULL);
191            glCompileShader(id);
192            glGetShaderInfoLog(id, sizeof(buf), &len, buf);
193            if (len <= 0)
194                version = 120;
195        }
196
197        /* Otherwise, assume we can compile 1.10 shaders. */
198        if (!version)
199            version = 110;
200
201        glDeleteShader(id);
202    }
203
204    return version;
205}
206
207/* Simple shader source patching for old GLSL versions.
208 * If supported version is 1.30, do nothing.
209 * If supported version is 1.20:
210 *  - replace "#version 130" with "#version 120"
211 */
212void ShaderData::Patch(char *dst, char const *src)
213{
214    int version = GetVersion();
215
216    if (version >= 130)
217    {
218        strcpy(dst, src);
219        return;
220    }
221
222    if (version == 120)
223    {
224        static char const * const replaces[] =
225        {
226            "#version 130", "#version 120",
227            "\nin vec2", "\nvec2",
228            "\nin vec3", "\nvec3",
229            "\nin vec4", "\nvec4",
230            "\nin mat4", "\nmat4",
231            NULL
232        };
233
234        while (*src)
235        {
236            for (char const * const *rep = replaces; rep[0]; rep += 2)
237            {
238                size_t l0 = strlen(rep[0]);
239                if (!strncmp(src, rep[0], l0))
240                {
241                    src += l0;
242                    dst += sprintf(dst, "%s", rep[1]);
243                    goto next;
244                }
245            }
246
247            *dst++ = *src++;
248
249            next: continue;
250        }
251        *dst = '\0';
252    }
253}
254
255} /* namespace lol */
256
Note: See TracBrowser for help on using the repository browser.