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

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

shader: factor the uniform handling logic into platform-independent methods
for both OpenGL and the PS3.

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