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

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

xbox: start working on an Xbox/Direct3D port.

File size: 11.6 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2012 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#if defined _XBOX
42
43#if !defined __CELLOS_LV2__
44    GLuint prog_id, vert_id, frag_id;
45#else
46    CGprogram vert_id, frag_id;
47#endif
48    uint32_t vert_crc, frag_crc;
49
50    /* Shader patcher */
51    static int GetVersion();
52    static void Patch(char *dst, char const *vert, char const *frag);
53
54    /* Global shader cache */
55    static Shader *shaders[];
56    static int nshaders;
57};
58
59Shader *ShaderData::shaders[256];
60int ShaderData::nshaders = 0;
61
62/*
63 * Public Shader class
64 */
65
66Shader *Shader::Create(char const *vert, char const *frag)
67{
68    uint32_t new_vert_crc = Hash::Crc32(vert);
69    uint32_t new_frag_crc = Hash::Crc32(frag);
70
71    for (int n = 0; n < ShaderData::nshaders; n++)
72    {
73        if (ShaderData::shaders[n]->data->vert_crc == new_vert_crc
74             && ShaderData::shaders[n]->data->frag_crc == new_frag_crc)
75            return ShaderData::shaders[n];
76    }
77
78    Shader *ret = new Shader(vert, frag);
79    ShaderData::shaders[ShaderData::nshaders] = ret;
80    ShaderData::nshaders++;
81    return ret;
82}
83
84void Shader::Destroy(Shader *shader)
85{
86    /* XXX: do nothing! the shader should remain in cache */
87    (void)shader;
88}
89
90Shader::Shader(char const *vert, char const *frag)
91  : data(new ShaderData())
92{
93#if !defined __CELLOS_LV2__
94    char buf[4096], errbuf[4096];
95    char const *shader = buf;
96    GLint status;
97    GLsizei len;
98#else
99    /* Initialise the runtime shader compiler. FIXME: this needs only
100     * to be done once. */
101    cgRTCgcInit();
102#endif
103
104    /* Compile vertex shader */
105    data->vert_crc = Hash::Crc32(vert);
106#if !defined __CELLOS_LV2__
107    ShaderData::Patch(buf, vert, NULL);
108    data->vert_id = glCreateShader(GL_VERTEX_SHADER);
109    glShaderSource(data->vert_id, 1, &shader, NULL);
110    glCompileShader(data->vert_id);
111
112    glGetShaderiv(data->vert_id, GL_COMPILE_STATUS, &status);
113    if (status != GL_TRUE)
114    {
115        glGetShaderInfoLog(data->vert_id, sizeof(errbuf), &len, errbuf);
116        Log::Error("failed to compile vertex shader: %s", errbuf);
117        Log::Error("shader source:\n%s\n", buf);
118    }
119#else
120    data->vert_id = cgCreateProgram(cgCreateContext(), CG_SOURCE, vert,
121                                    cgGLGetLatestProfile(CG_GL_VERTEX),
122                                    NULL, NULL);
123    if (data->vert_id == NULL)
124    {
125        Log::Error("failed to compile vertex shader");
126        Log::Error("shader source:\n%s\n", vert);
127    }
128#endif
129
130    /* Compile fragment shader */
131    data->frag_crc = Hash::Crc32(frag);
132#if !defined __CELLOS_LV2__
133    ShaderData::Patch(buf, NULL, frag);
134    data->frag_id = glCreateShader(GL_FRAGMENT_SHADER);
135    glShaderSource(data->frag_id, 1, &shader, NULL);
136    glCompileShader(data->frag_id);
137
138    glGetShaderiv(data->frag_id, GL_COMPILE_STATUS, &status);
139    if (status != GL_TRUE)
140    {
141        glGetShaderInfoLog(data->frag_id, sizeof(errbuf), &len, errbuf);
142        Log::Error("failed to compile fragment shader: %s", errbuf);
143        Log::Error("shader source:\n%s\n", buf);
144    }
145#else
146    data->frag_id = cgCreateProgram(cgCreateContext(), CG_SOURCE, frag,
147                                    cgGLGetLatestProfile(CG_GL_FRAGMENT),
148                                    NULL, NULL);
149    if (data->frag_id == NULL)
150    {
151        Log::Error("failed to compile fragment shader");
152        Log::Error("shader source:\n%s\n", frag);
153    }
154#endif
155
156#if !defined __CELLOS_LV2__
157    /* Create program */
158    data->prog_id = glCreateProgram();
159    glAttachShader(data->prog_id, data->vert_id);
160    glAttachShader(data->prog_id, data->frag_id);
161
162    glLinkProgram(data->prog_id);
163    glValidateProgram(data->prog_id);
164#endif
165}
166
167int Shader::GetAttribLocation(char const *attr) const
168{
169#if !defined __CELLOS_LV2__
170    return glGetAttribLocation(data->prog_id, attr);
171#else
172    /* FIXME: need to differentiate between vertex and fragment program */
173    return 0;
174#endif
175}
176
177int Shader::GetUniformLocation(char const *uni) const
178{
179#if !defined __CELLOS_LV2__
180    return glGetUniformLocation(data->prog_id, uni);
181#else
182    /* FIXME: need to differentiate between vertex and fragment program,
183     * and replace the ugly cast. */
184    CGparameter tmp = cgGetNamedParameter(data->vert_id, uni);
185    if (tmp == NULL)
186        tmp = cgGetNamedParameter(data->frag_id, uni);
187    return (int)(intptr_t)tmp;
188#endif
189}
190
191void Shader::SetUniform(int uni, float f)
192{
193#if !defined __CELLOS_LV2__
194    glUniform1f(uni, f);
195#else
196    cgGLSetParameter1f((CGparameter)(intptr_t)uni, f);
197#endif
198}
199
200void Shader::SetUniform(int uni, vec2 const &v)
201{
202#if !defined __CELLOS_LV2__
203    glUniform2f(uni, v.x, v.y);
204#else
205    cgGLSetParameter2f((CGparameter)(intptr_t)uni, v.x, v.y);
206#endif
207}
208
209void Shader::SetUniform(int uni, vec3 const &v)
210{
211#if !defined __CELLOS_LV2__
212    glUniform3f(uni, v.x, v.y, v.z);
213#else
214    cgGLSetParameter3f((CGparameter)(intptr_t)uni, v.x, v.y, v.z);
215#endif
216}
217
218void Shader::SetUniform(int uni, vec4 const &v)
219{
220    /* FIXME: use the array versions of these functions */
221#if !defined __CELLOS_LV2__
222    glUniform4f(uni, v.x, v.y, v.z, v.w);
223#else
224    cgGLSetParameter4f((CGparameter)(intptr_t)uni, v.x, v.y, v.z, v.w);
225#endif
226}
227
228void Shader::SetUniform(int uni, mat4 const &m)
229{
230#if !defined __CELLOS_LV2__
231    glUniformMatrix4fv(uni, 1, GL_FALSE, &m[0][0]);
232#else
233    cgGLSetMatrixParameterfc((CGparameter)(intptr_t)uni, &m[0][0]);
234#endif
235}
236
237void Shader::Bind() const
238{
239#if !defined __CELLOS_LV2__
240    glUseProgram(data->prog_id);
241#else
242    cgGLEnableProfile(cgGLGetLatestProfile(CG_GL_VERTEX));
243    cgGLBindProgram(data->vert_id);
244    cgGLEnableProfile(cgGLGetLatestProfile(CG_GL_FRAGMENT));
245    cgGLBindProgram(data->frag_id);
246#endif
247}
248
249Shader::~Shader()
250{
251#if !defined __CELLOS_LV2__
252    glDetachShader(data->prog_id, data->vert_id);
253    glDetachShader(data->prog_id, data->frag_id);
254    glDeleteShader(data->vert_id);
255    glDeleteShader(data->frag_id);
256    glDeleteProgram(data->prog_id);
257#else
258    cgDestroyProgram(data->vert_id);
259    cgDestroyProgram(data->frag_id);
260#endif
261    delete data;
262}
263
264/* Try to detect shader compiler features */
265int ShaderData::GetVersion()
266{
267    static int version = 0;
268
269#if !defined __CELLOS_LV2__
270    if (!version)
271    {
272        char buf[4096];
273        GLsizei len;
274
275        int id = glCreateShader(GL_VERTEX_SHADER);
276
277        /* Can we compile 1.30 shaders? */
278        char const *test130 =
279            "#version 130\n"
280            "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
281        glShaderSource(id, 1, &test130, NULL);
282        glCompileShader(id);
283        glGetShaderInfoLog(id, sizeof(buf), &len, buf);
284        if (len <= 0)
285            version = 130;
286
287        /* If not, can we compile 1.20 shaders? */
288        if (!version)
289        {
290            char const *test120 =
291                "#version 120\n"
292                "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
293            glShaderSource(id, 1, &test120, NULL);
294            glCompileShader(id);
295            glGetShaderInfoLog(id, sizeof(buf), &len, buf);
296            if (len <= 0)
297                version = 120;
298        }
299
300        /* Otherwise, assume we can compile 1.10 shaders. */
301        if (!version)
302            version = 110;
303
304        glDeleteShader(id);
305    }
306#endif
307
308    return version;
309}
310
311/* Simple shader source patching for old GLSL versions.
312 * If supported version is 1.30, do nothing.
313 * If supported version is 1.20:
314 *  - replace "#version 130" with "#version 120"
315 */
316void ShaderData::Patch(char *dst, char const *vert, char const *frag)
317{
318    int ver_driver = GetVersion();
319
320    strcpy(dst, vert ? vert : frag);
321    if (ver_driver >= 130)
322        return;
323
324    int ver_shader = 110;
325    char *parser = strstr(dst, "#version");
326    if (parser)
327        ver_shader = atoi(parser + strlen("#version"));
328
329    if (ver_shader > 120 && ver_driver <= 120)
330    {
331        char const *end = dst + strlen(dst) + 1;
332
333        /* Find main() */
334        parser = strstr(dst, "main");
335        if (!parser) return;
336        parser = strstr(parser, "(");
337        if (!parser) return;
338        parser = strstr(parser, ")");
339        if (!parser) return;
340        parser = strstr(parser, "{");
341        if (!parser) return;
342        char *main = parser + 1;
343
344        /* Perform main() replaces */
345        char const * const main_replaces[] =
346        {
347#if 0
348            "in vec2 in_Vertex;", "vec2 in_Vertex = gl_Vertex.xy;",
349            "in vec3 in_Vertex;", "vec3 in_Vertex = gl_Vertex.xyz;",
350            "in vec4 in_Vertex;", "vec4 in_Vertex = gl_Vertex.xyzw;",
351
352            "in vec2 in_Color;", "vec2 in_Color = gl_Color.xy;",
353            "in vec3 in_Color;", "vec3 in_Color = gl_Color.xyz;",
354            "in vec4 in_Color;", "vec4 in_Color = gl_Color.xyzw;",
355
356            "in vec2 in_MultiTexCoord0;",
357               "vec2 in_MultiTexCoord0 = gl_MultiTexCoord0.xy;",
358            "in vec2 in_MultiTexCoord1;",
359               "vec2 in_MultiTexCoord1 = gl_MultiTexCoord1.xy;",
360            "in vec2 in_MultiTexCoord2;",
361               "vec2 in_MultiTexCoord2 = gl_MultiTexCoord2.xy;",
362            "in vec2 in_MultiTexCoord3;",
363               "vec2 in_MultiTexCoord3 = gl_MultiTexCoord3.xy;",
364            "in vec2 in_MultiTexCoord4;",
365               "vec2 in_MultiTexCoord4 = gl_MultiTexCoord4.xy;",
366            "in vec2 in_MultiTexCoord5;",
367               "vec2 in_MultiTexCoord5 = gl_MultiTexCoord5.xy;",
368            "in vec2 in_MultiTexCoord6;",
369               "vec2 in_MultiTexCoord6 = gl_MultiTexCoord6.xy;",
370            "in vec2 in_MultiTexCoord7;",
371               "vec2 in_MultiTexCoord7 = gl_MultiTexCoord7.xy;",
372#endif
373
374            NULL
375        };
376
377        for (char const * const *rep = main_replaces; rep[0]; rep += 2)
378        {
379            char *match = strstr(dst, rep[0]);
380            if (match && match < main)
381            {
382                size_t l0 = strlen(rep[0]);
383                size_t l1 = strlen(rep[1]);
384                memmove(main + l1, main, end - main);
385                memcpy(main, rep[1], l1);
386                memset(match, ' ', l0);
387                main += l1;
388                end += l1;
389            }
390        }
391
392        /* Perform small replaces */
393        char const * const fast_replaces[] =
394        {
395            "#version 130", "#version 120",
396            "in vec2", vert ? "attribute vec2" : "varying vec2",
397            "in vec3", vert ? "attribute vec3" : "varying vec3",
398            "in vec4", vert ? "attribute vec4" : "varying vec4",
399            "in mat4", vert ? "attribute mat4" : "varying mat4",
400            "out vec2", "varying vec2",
401            "out vec3", "varying vec3",
402            "out vec4", "varying vec4",
403            "out mat4", "varying mat4",
404            NULL
405        };
406
407        for (char const * const *rep = fast_replaces; rep[0]; rep += 2)
408        {
409            char *match;
410            while ((match = strstr(dst, rep[0])))
411            {
412                size_t l0 = strlen(rep[0]);
413                size_t l1 = strlen(rep[1]);
414
415                if (l1 > l0)
416                    memmove(match + l1, match + l0, (end - match) - l0);
417                memcpy(match, rep[1], l1);
418                if (l1 < l0)
419                    memset(match + l0, ' ', l1 - l0);
420                end += l1 - l0;
421            }
422        }
423    }
424}
425
426} /* namespace lol */
427
Note: See TracBrowser for help on using the repository browser.