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

Last change on this file since 818 was 818, checked in by sam, 8 years ago

ps3: some compilation fixes to accomodate for recent debug additions.

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