source: trunk/src/gpu/shader.cpp @ 2787

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

build: fix the X360 port.

  • Property svn:keywords set to Id
File size: 26.9 KB
Line 
1//
2// Lol Engine
3//
4// Copyright: (c) 2010-2013 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://www.wtfpl.net/ for more details.
9//
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <cstring>
16#include <cstdio>
17
18#if defined WIN32 && !defined _XBOX
19#   define WIN32_LEAN_AND_MEAN
20#   include <windows.h>
21#   if defined USE_D3D9
22#       include <algorithm>
23        using std::min;
24        using std::max;
25#       include <d3d9.h>
26#       include <d3dx9shader.h>
27#   endif
28#elif defined _XBOX
29#   include <xtl.h>
30#   undef near /* Fuck Microsoft */
31#   undef far /* Fuck Microsoft again */
32#endif
33
34#include "core.h"
35#include "lolgl.h"
36
37using namespace std;
38
39namespace lol
40{
41
42/*
43 * Shader implementation class
44 */
45
46class ShaderData
47{
48    friend class Shader;
49
50private:
51#if defined USE_D3D9
52    IDirect3DDevice9 *m_dev;
53    IDirect3DVertexShader9 *vert_shader;
54    IDirect3DPixelShader9 *frag_shader;
55    ID3DXConstantTable *vert_table, *frag_table;
56#elif defined _XBOX
57    D3DDevice *m_dev;
58    D3DVertexShader *vert_shader;
59    D3DPixelShader *frag_shader;
60    ID3DXConstantTable *vert_table, *frag_table;
61#elif !defined __CELLOS_LV2__
62    GLuint prog_id, vert_id, frag_id;
63#else
64    CGprogram vert_id, frag_id;
65#endif
66    uint32_t vert_crc, frag_crc;
67
68    /* Shader patcher */
69    static int GetVersion();
70    static void Patch(char *dst, char const *vert, char const *frag);
71
72    /* Global shader cache */
73    static Shader *shaders[];
74    static Hash<char const *> hash;
75    static int nshaders;
76};
77
78Shader *ShaderData::shaders[256];
79Hash<char const *> ShaderData::hash;
80int ShaderData::nshaders = 0;
81
82/*
83 * Public Shader class
84 */
85
86Shader *Shader::Create(char const *lolfx)
87{
88    char *src = new char[strlen(lolfx) + 2];
89    memcpy(src + 1, lolfx, strlen(lolfx) + 1);
90    src[0] = '\n';
91
92    /* Parse the crap */
93    Array<char const *, char const *> sections;
94    char *key = nullptr;
95    for (char *parser = src; *parser; )
96    {
97        if (key == nullptr && (parser[0] == '\n' || parser[0] == '\r')
98             && parser[1] == '[')
99        {
100            *parser = '\0';
101            parser += 2;
102            key = parser;
103        }
104        else if (key && parser[0] == ']')
105        {
106            *parser++ = '\0';
107        }
108        else if (key && (parser[0] == '\n' || parser[0] == '\r'))
109        {
110            sections.Push(key, parser);
111            parser++;
112            key = nullptr;
113        }
114        else
115        {
116            parser++;
117        }
118    }
119
120    char const *vert = nullptr, *frag = nullptr;
121    for (int i = 0; i < sections.Count(); i++)
122    {
123#if !defined __CELLOS_LV2__ && !defined _XBOX && !defined USE_D3D9
124        if (!strcmp(sections[i].m1, "vert.glsl"))
125            vert = sections[i].m2;
126        if (!strcmp(sections[i].m1, "frag.glsl"))
127            frag = sections[i].m2;
128#else
129        if (!strcmp(sections[i].m1, "vert.hlsl"))
130            vert = sections[i].m2;
131        if (!strcmp(sections[i].m1, "frag.hlsl"))
132            frag = sections[i].m2;
133#endif
134    }
135
136    /* FIXME: we don’t know how to handle these yet. */
137    if (!vert)
138        Log::Error("no vertex shader found… sorry, I’m gonna crash now.\n");
139    if (!frag)
140        Log::Error("no fragment shader found… sorry, I’m gonna crash now.\n");
141
142    uint32_t new_vert_crc = ShaderData::hash(vert);
143    uint32_t new_frag_crc = ShaderData::hash(frag);
144
145    for (int n = 0; n < ShaderData::nshaders; n++)
146    {
147        if (ShaderData::shaders[n]->data->vert_crc == new_vert_crc
148             && ShaderData::shaders[n]->data->frag_crc == new_frag_crc)
149        {
150            delete[] src;
151            return ShaderData::shaders[n];
152        }
153    }
154
155    Shader *ret = new Shader(vert, frag);
156    ShaderData::shaders[ShaderData::nshaders] = ret;
157    ShaderData::nshaders++;
158
159    delete[] src;
160    return ret;
161}
162
163void Shader::Destroy(Shader *shader)
164{
165    /* XXX: do nothing! the shader should remain in cache */
166    UNUSED(shader);
167}
168
169Shader::Shader(char const *vert, char const *frag)
170  : data(new ShaderData())
171{
172#if defined USE_D3D9 || defined _XBOX
173    ID3DXBuffer *shader_code, *error_msg;
174    HRESULT hr;
175    D3DXMACRO macros[] =
176    {
177#if defined _XBOX
178        { "_XBOX", "1" },
179#endif
180        { nullptr, nullptr }
181    };
182#elif !defined __CELLOS_LV2__
183    char buf[4096], errbuf[4096];
184    char const *shader = buf;
185    GLint status;
186    GLsizei len;
187#else
188    /* Initialise the runtime shader compiler. FIXME: this needs only
189     * to be done once. */
190    cgRTCgcInit();
191#endif
192
193    /* Compile vertex shader */
194    data->vert_crc = ShaderData::hash(vert);
195#if defined USE_D3D9 || defined _XBOX
196#   if defined USE_D3D9
197    data->m_dev = (IDirect3DDevice9 *)g_renderer->GetDevice();
198#   elif defined _XBOX
199    data->m_dev = (D3DDevice *)g_renderer->GetDevice();
200#   endif
201
202    hr = D3DXCompileShader(vert, (UINT)strlen(vert), macros, nullptr, "main",
203                           "vs_3_0", 0, &shader_code, &error_msg,
204                           &data->vert_table);
205    if (FAILED(hr))
206    {
207        Log::Error("failed to compile vertex shader: %s",
208                   error_msg ? error_msg->GetBufferPointer() : "error");
209        Log::Error("shader source:\n%s\n", vert);
210    }
211    data->m_dev->CreateVertexShader((DWORD *)shader_code->GetBufferPointer(),
212                                    &data->vert_shader);
213    shader_code->Release();
214#elif !defined __CELLOS_LV2__
215    ShaderData::Patch(buf, vert, nullptr);
216    data->vert_id = glCreateShader(GL_VERTEX_SHADER);
217    glShaderSource(data->vert_id, 1, &shader, nullptr);
218    glCompileShader(data->vert_id);
219
220    glGetShaderInfoLog(data->vert_id, sizeof(errbuf), &len, errbuf);
221    glGetShaderiv(data->vert_id, GL_COMPILE_STATUS, &status);
222    if (status != GL_TRUE)
223    {
224        Log::Error("failed to compile vertex shader: %s", errbuf);
225        Log::Error("shader source:\n%s\n", buf);
226    }
227    else if (len > 16)
228    {
229        Log::Debug("compile log for vertex shader: %s", errbuf);
230        Log::Debug("shader source:\n%s\n", buf);
231    }
232#else
233    data->vert_id = cgCreateProgram(cgCreateContext(), CG_SOURCE, vert,
234                                    cgGLGetLatestProfile(CG_GL_VERTEX),
235                                    nullptr, nullptr);
236    if (data->vert_id == nullptr)
237    {
238        Log::Error("failed to compile vertex shader");
239        Log::Error("shader source:\n%s\n", vert);
240    }
241#endif
242
243    /* Compile fragment shader */
244    data->frag_crc = ShaderData::hash(frag);
245#if defined USE_D3D9 || defined _XBOX
246    hr = D3DXCompileShader(frag, (UINT)strlen(frag), macros, nullptr, "main",
247                           "ps_3_0", 0, &shader_code, &error_msg,
248                           &data->frag_table);
249    if (FAILED(hr))
250    {
251        Log::Error("failed to compile fragment shader: %s",
252                   error_msg ? error_msg->GetBufferPointer() : "error");
253        Log::Error("shader source:\n%s\n", frag);
254    }
255    data->m_dev->CreatePixelShader((DWORD *)shader_code->GetBufferPointer(),
256                                   &data->frag_shader);
257    shader_code->Release();
258#elif !defined __CELLOS_LV2__
259    ShaderData::Patch(buf, nullptr, frag);
260    data->frag_id = glCreateShader(GL_FRAGMENT_SHADER);
261    glShaderSource(data->frag_id, 1, &shader, nullptr);
262    glCompileShader(data->frag_id);
263
264    glGetShaderInfoLog(data->frag_id, sizeof(errbuf), &len, errbuf);
265    glGetShaderiv(data->frag_id, GL_COMPILE_STATUS, &status);
266    if (status != GL_TRUE)
267    {
268        Log::Error("failed to compile fragment shader: %s", errbuf);
269        Log::Error("shader source:\n%s\n", buf);
270    }
271    else if (len > 16)
272    {
273        Log::Debug("compile log for fragment shader: %s", errbuf);
274        Log::Debug("shader source:\n%s\n", buf);
275    }
276#else
277    data->frag_id = cgCreateProgram(cgCreateContext(), CG_SOURCE, frag,
278                                    cgGLGetLatestProfile(CG_GL_FRAGMENT),
279                                    nullptr, nullptr);
280    if (data->frag_id == nullptr)
281    {
282        Log::Error("failed to compile fragment shader");
283        Log::Error("shader source:\n%s\n", frag);
284    }
285#endif
286
287#if defined USE_D3D9 || defined _XBOX
288    /* FIXME: this is only debug code, we don't need it. */
289    D3DXCONSTANTTABLE_DESC desc;
290    data->frag_table->GetDesc(&desc);
291    for (int i = 0; i < desc.Constants; i++)
292    {
293        D3DXCONSTANT_DESC cdesc;
294        UINT count = 1;
295        D3DXHANDLE h = data->frag_table->GetConstant(nullptr, i);
296        data->frag_table->GetConstantDesc(h, &cdesc, &count);
297    }
298    data->vert_table->GetDesc(&desc);
299    for (int i = 0; i < desc.Constants; i++)
300    {
301        D3DXCONSTANT_DESC cdesc;
302        UINT count = 1;
303        D3DXHANDLE h = data->vert_table->GetConstant(nullptr, i);
304        data->frag_table->GetConstantDesc(h, &cdesc, &count);
305    }
306#elif !defined __CELLOS_LV2__
307    /* Create program */
308    data->prog_id = glCreateProgram();
309    glAttachShader(data->prog_id, data->vert_id);
310    glAttachShader(data->prog_id, data->frag_id);
311
312    glLinkProgram(data->prog_id);
313    glGetProgramInfoLog(data->prog_id, sizeof(errbuf), &len, errbuf);
314    glGetProgramiv(data->prog_id, GL_LINK_STATUS, &status);
315    if (status != GL_TRUE)
316    {
317        Log::Error("failed to link program: %s", errbuf);
318    }
319    else if (len > 16)
320    {
321        Log::Debug("link log for program: %s", errbuf);
322    }
323    glValidateProgram(data->prog_id);
324#endif
325}
326
327ShaderAttrib Shader::GetAttribLocation(char const *attr,
328                                       VertexUsage usage, int index) const
329{
330    ShaderAttrib ret;
331    ret.m_flags = (uint64_t)(uint16_t)usage << 16;
332    ret.m_flags |= (uint64_t)(uint16_t)index;
333#if defined USE_D3D9 || defined _XBOX
334#elif !defined __CELLOS_LV2__
335    GLint l = glGetAttribLocation(data->prog_id, attr);
336    if (l < 0)
337    {
338        Log::Warn("tried to query invalid attribute: %s\n", attr);
339        l = 0;
340    }
341    ret.m_flags |= (uint64_t)(uint32_t)l << 32;
342#else
343    /* FIXME: can we do this at all on the PS3? */
344#endif
345    return ret;
346}
347
348ShaderUniform Shader::GetUniformLocation(char const *uni) const
349{
350    ShaderUniform ret;
351#if defined USE_D3D9 || defined _XBOX
352    /* Global variables are prefixed with "$" */
353    String tmpname = String("$") + uni;
354    D3DXCONSTANT_DESC cdesc;
355    D3DXHANDLE hr;
356    UINT count;
357
358    count = 0;
359    hr = data->frag_table->GetConstantByName(nullptr, tmpname.C());
360    if (hr)
361        data->frag_table->GetConstantDesc(hr, &cdesc, &count);
362    if (count)
363    {
364        ret.frag = cdesc.RegisterIndex;
365        ret.flags |= 1;
366    }
367
368    count = 0;
369    hr = data->vert_table->GetConstantByName(nullptr, tmpname.C());
370    if (hr)
371        data->vert_table->GetConstantDesc(hr, &cdesc, &count);
372    if (count)
373    {
374        ret.vert = cdesc.RegisterIndex;
375        ret.flags |= 2;
376    }
377#elif !defined __CELLOS_LV2__
378    ret.frag = (uintptr_t)glGetUniformLocation(data->prog_id, uni);
379    ret.vert = 0;
380#else
381    ret.frag = (uintptr_t)cgGetNamedParameter(data->frag_id, uni);
382    ret.vert = (uintptr_t)cgGetNamedParameter(data->vert_id, uni);
383#endif
384    return ret;
385}
386
387/*
388 * Uniform setters for scalars
389 */
390
391void Shader::SetUniform(ShaderUniform const &uni, int i)
392{
393#if defined USE_D3D9 || defined _XBOX
394    SetUniform(uni, ivec4(i, 0, 0, 0));
395#elif !defined __CELLOS_LV2__
396    glUniform1i(uni.frag, i);
397#else
398    /* FIXME: does this exist at all? cgGLSetParameter1i doesn't. */
399#endif
400}
401
402void Shader::SetUniform(ShaderUniform const &uni, ivec2 const &v)
403{
404#if defined USE_D3D9 || defined _XBOX
405    SetUniform(uni, ivec4(v, 0, 0));
406#elif !defined __CELLOS_LV2__
407    glUniform2i(uni.frag, v.x, v.y);
408#else
409    /* FIXME: does this exist at all? */
410#endif
411}
412
413void Shader::SetUniform(ShaderUniform const &uni, ivec3 const &v)
414{
415#if defined USE_D3D9 || defined _XBOX
416    SetUniform(uni, ivec4(v, 0));
417#elif !defined __CELLOS_LV2__
418    glUniform3i(uni.frag, v.x, v.y, v.z);
419#else
420    /* FIXME: does this exist at all? */
421#endif
422}
423
424void Shader::SetUniform(ShaderUniform const &uni, ivec4 const &v)
425{
426#if defined USE_D3D9 || defined _XBOX
427    if (uni.flags & 1)
428        data->m_dev->SetPixelShaderConstantI((UINT)uni.frag, &v[0], 1);
429    if (uni.flags & 2)
430        data->m_dev->SetVertexShaderConstantI((UINT)uni.vert, &v[0], 1);
431#elif !defined __CELLOS_LV2__
432    glUniform4i(uni.frag, v.x, v.y, v.z, v.w);
433#else
434    /* FIXME: does this exist at all? */
435#endif
436}
437
438void Shader::SetUniform(ShaderUniform const &uni, float f)
439{
440#if defined USE_D3D9 || defined _XBOX
441    SetUniform(uni, vec4(f, 0, 0, 0));
442#elif !defined __CELLOS_LV2__
443    glUniform1f(uni.frag, f);
444#else
445    if (uni.frag)
446        cgGLSetParameter1f((CGparameter)uni.frag, f);
447    if (uni.vert)
448        cgGLSetParameter1f((CGparameter)uni.vert, f);
449#endif
450}
451
452void Shader::SetUniform(ShaderUniform const &uni, vec2 const &v)
453{
454#if defined USE_D3D9 || defined _XBOX
455    SetUniform(uni, vec4(v, 0, 0));
456#elif !defined __CELLOS_LV2__
457    glUniform2fv(uni.frag, 1, &v[0]);
458#else
459    if (uni.frag)
460        cgGLSetParameter2fv((CGparameter)uni.frag, &v[0]);
461    if (uni.vert)
462        cgGLSetParameter2fv((CGparameter)uni.vert, &v[0]);
463#endif
464}
465
466void Shader::SetUniform(ShaderUniform const &uni, vec3 const &v)
467{
468#if defined USE_D3D9 || defined _XBOX
469    SetUniform(uni, vec4(v, 0));
470#elif !defined __CELLOS_LV2__
471    glUniform3fv(uni.frag, 1, &v[0]);
472#else
473    if (uni.frag)
474        cgGLSetParameter3fv((CGparameter)uni.frag, &v[0]);
475    if (uni.vert)
476        cgGLSetParameter3fv((CGparameter)uni.vert, &v[0]);
477#endif
478}
479
480void Shader::SetUniform(ShaderUniform const &uni, vec4 const &v)
481{
482#if defined USE_D3D9 || defined _XBOX
483    if (uni.flags & 1)
484        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag, &v[0], 1);
485    if (uni.flags & 2)
486        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert, &v[0], 1);
487#elif !defined __CELLOS_LV2__
488    glUniform4fv(uni.frag, 1, &v[0]);
489#else
490    if (uni.frag)
491        cgGLSetParameter4fv((CGparameter)uni.frag, &v[0]);
492    if (uni.vert)
493        cgGLSetParameter4fv((CGparameter)uni.vert, &v[0]);
494#endif
495}
496
497void Shader::SetUniform(ShaderUniform const &uni, mat2 const &m)
498{
499#if defined USE_D3D9 || defined _XBOX
500    /* FIXME: do we need padding here like for the mat3 version? */
501    if (uni.flags & 1)
502        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag, &m[0][0], 1);
503    if (uni.flags & 2)
504        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert, &m[0][0], 1);
505#elif !defined __CELLOS_LV2__
506    glUniformMatrix2fv(uni.frag, 1, GL_FALSE, &m[0][0]);
507#else
508    mat4 tmp(m, 1.0f, 1.0f);
509    if (uni.frag)
510        cgGLSetMatrixParameterfc((CGparameter)uni.frag, &m[0][0]);
511    if (uni.vert)
512        cgGLSetMatrixParameterfc((CGparameter)uni.vert, &m[0][0]);
513#endif
514}
515
516void Shader::SetUniform(ShaderUniform const &uni, mat3 const &m)
517{
518#if defined USE_D3D9 || defined _XBOX
519    /* Padding matrix columns is necessary on DirectX. We need to create
520     * a new data structure; a 4×4 matrix will do. */
521    mat4 tmp(m, 1.0f);
522    if (uni.flags & 1)
523        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag, &tmp[0][0], 3);
524    if (uni.flags & 2)
525        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert, &tmp[0][0], 3);
526#elif !defined __CELLOS_LV2__
527    glUniformMatrix3fv(uni.frag, 1, GL_FALSE, &m[0][0]);
528#else
529    /* FIXME: check it's the proper way to do this */
530    mat4 tmp(m, 1.0f);
531    if (uni.frag)
532        cgGLSetMatrixParameterfc((CGparameter)uni.frag, &m[0][0]);
533    if (uni.vert)
534        cgGLSetMatrixParameterfc((CGparameter)uni.vert, &m[0][0]);
535#endif
536}
537
538void Shader::SetUniform(ShaderUniform const &uni, mat4 const &m)
539{
540#if defined USE_D3D9 || defined _XBOX
541    if (uni.flags & 1)
542        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag, &m[0][0], 4);
543    if (uni.flags & 2)
544        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert, &m[0][0], 4);
545#elif !defined __CELLOS_LV2__
546    glUniformMatrix4fv(uni.frag, 1, GL_FALSE, &m[0][0]);
547#else
548    if (uni.frag)
549        cgGLSetMatrixParameterfc((CGparameter)uni.frag, &m[0][0]);
550    if (uni.vert)
551        cgGLSetMatrixParameterfc((CGparameter)uni.vert, &m[0][0]);
552#endif
553}
554
555void Shader::SetUniform(ShaderUniform const &uni, ShaderTexture tex, int index)
556{
557#if defined USE_D3D9 || defined _XBOX
558    data->m_dev->SetTexture(index, (LPDIRECT3DTEXTURE9)tex.m_flags);
559    data->m_dev->SetSamplerState(index, D3DSAMP_MAGFILTER, tex.m_attrib & 0xff);
560    data->m_dev->SetSamplerState(index, D3DSAMP_MINFILTER, (tex.m_attrib >> 8) & 0xff);
561    data->m_dev->SetSamplerState(index, D3DSAMP_MIPFILTER, (tex.m_attrib >> 16) & 0xff);
562#elif !defined __CELLOS_LV2__
563    glActiveTexture(GL_TEXTURE0 + index);
564    //glEnable(GL_TEXTURE_2D);
565    glBindTexture(GL_TEXTURE_2D, (int)tex.m_flags);
566    SetUniform(uni, index);
567#else
568    /* FIXME: unimplemented */
569#endif
570}
571
572/*
573 * Uniform setters for arrays
574 */
575
576void Shader::SetUniform(ShaderUniform const &uni, Array<float> const &v)
577{
578#if defined USE_D3D9 || defined _XBOX
579    /* FIXME: this will not work properly because we don't know how tell DX9
580     * it's a bunch of floats instead of vec4. */
581    if (uni.flags & 1)
582        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag,
583                                             &v[0], v.Count() / 4);
584    if (uni.flags & 2)
585        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert,
586                                              &v[0], v.Count() / 4);
587#elif !defined __CELLOS_LV2__
588    glUniform1fv(uni.frag, v.Count(), &v[0]);
589#else
590    if (uni.frag)
591        cgGLSetParameterArray1f((CGparameter)uni.frag,
592                                0, v.Count(), &v[0]);
593    if (uni.vert)
594        cgGLSetParameterArray1f((CGparameter)uni.vert,
595                                0, v.Count(), &v[0]);
596#endif
597}
598
599void Shader::SetUniform(ShaderUniform const &uni, Array<vec2> const &v)
600{
601#if defined USE_D3D9 || defined _XBOX
602    /* FIXME: this will not work properly because we don't know how tell DX9
603     * it's a bunch of vec2 instead of vec4. */
604    if (uni.flags & 1)
605        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag,
606                                             &v[0][0], v.Count() / 2);
607    if (uni.flags & 2)
608        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert,
609                                              &v[0][0], v.Count() / 2);
610#elif !defined __CELLOS_LV2__
611    glUniform2fv(uni.frag, v.Count(), &v[0][0]);
612#else
613    if (uni.frag)
614        cgGLSetParameterArray2f((CGparameter)uni.frag,
615                                0, v.Count(), &v[0][0]);
616    if (uni.vert)
617        cgGLSetParameterArray2f((CGparameter)uni.vert,
618                                0, v.Count(), &v[0][0]);
619#endif
620}
621
622void Shader::SetUniform(ShaderUniform const &uni, Array<vec3> const &v)
623{
624#if defined USE_D3D9 || defined _XBOX
625    /* FIXME: this will not work properly because we don't know how tell DX9
626     * it's a bunch of vec3 instead of vec4. */
627    if (uni.flags & 1)
628        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag,
629                                             &v[0][0], v.Count());
630    if (uni.flags & 2)
631        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert,
632                                              &v[0][0], v.Count());
633#elif !defined __CELLOS_LV2__
634    glUniform3fv(uni.frag, v.Count(), &v[0][0]);
635#else
636    if (uni.frag)
637        cgGLSetParameterArray3f((CGparameter)uni.frag,
638                                0, v.Count(), &v[0][0]);
639    if (uni.vert)
640        cgGLSetParameterArray3f((CGparameter)uni.vert,
641                                0, v.Count(), &v[0][0]);
642#endif
643}
644
645void Shader::SetUniform(ShaderUniform const &uni, Array<vec4> const &v)
646{
647#if defined USE_D3D9 || defined _XBOX
648    if (uni.flags & 1)
649        data->m_dev->SetPixelShaderConstantF((UINT)uni.frag,
650                                             &v[0][0], v.Count());
651    if (uni.flags & 2)
652        data->m_dev->SetVertexShaderConstantF((UINT)uni.vert,
653                                              &v[0][0], v.Count());
654#elif !defined __CELLOS_LV2__
655    glUniform4fv(uni.frag, v.Count(), &v[0][0]);
656#else
657    if (uni.frag)
658        cgGLSetParameterArray4f((CGparameter)uni.frag,
659                                0, v.Count(), &v[0][0]);
660    if (uni.vert)
661        cgGLSetParameterArray4f((CGparameter)uni.vert,
662                                0, v.Count(), &v[0][0]);
663#endif
664}
665
666void Shader::Bind() const
667{
668#if defined USE_D3D9 || defined _XBOX
669    HRESULT hr;
670    hr = data->m_dev->SetVertexShader(data->vert_shader);
671    hr = data->m_dev->SetPixelShader(data->frag_shader);
672#elif !defined __CELLOS_LV2__
673    glUseProgram(data->prog_id);
674#else
675    cgGLEnableProfile(cgGLGetLatestProfile(CG_GL_VERTEX));
676    cgGLBindProgram(data->vert_id);
677    cgGLEnableProfile(cgGLGetLatestProfile(CG_GL_FRAGMENT));
678    cgGLBindProgram(data->frag_id);
679#endif
680}
681
682void Shader::Unbind() const
683{
684#if defined USE_D3D9 || defined _XBOX
685    HRESULT hr;
686    hr = data->m_dev->SetVertexShader(nullptr);
687    hr = data->m_dev->SetPixelShader(nullptr);
688#elif !defined __CELLOS_LV2__
689    /* FIXME: untested */
690    glUseProgram(0);
691#else
692    /* FIXME: untested */
693    cgGLDisableProfile(cgGLGetLatestProfile(CG_GL_VERTEX));
694    cgGLDisableProfile(cgGLGetLatestProfile(CG_GL_FRAGMENT));
695#endif
696}
697
698Shader::~Shader()
699{
700#if defined USE_D3D9 || defined _XBOX
701    data->vert_shader->Release();
702    data->vert_table->Release();
703    data->frag_shader->Release();
704    data->frag_table->Release();
705#elif !defined __CELLOS_LV2__
706    glDetachShader(data->prog_id, data->vert_id);
707    glDetachShader(data->prog_id, data->frag_id);
708    glDeleteShader(data->vert_id);
709    glDeleteShader(data->frag_id);
710    glDeleteProgram(data->prog_id);
711#else
712    cgDestroyProgram(data->vert_id);
713    cgDestroyProgram(data->frag_id);
714#endif
715    delete data;
716}
717
718/* Try to detect shader compiler features */
719int ShaderData::GetVersion()
720{
721    static int version = 0;
722
723#if !defined USE_D3D9 && !defined _XBOX && !defined __CELLOS_LV2__
724    if (!version)
725    {
726#if defined HAVE_GLES_2X
727        /* GLES 2.x supports #version 100, that's all. */
728        return 100;
729#else
730        char buf[4096];
731        GLsizei len;
732
733        int id = glCreateShader(GL_VERTEX_SHADER);
734
735        /* Can we compile 1.30 shaders? */
736        char const *test130 =
737            "#version 130\n"
738            "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
739        glShaderSource(id, 1, &test130, nullptr);
740        glCompileShader(id);
741        glGetShaderInfoLog(id, sizeof(buf), &len, buf);
742        if (len <= 0)
743            version = 130;
744
745        /* If not, can we compile 1.20 shaders? */
746        if (!version)
747        {
748            char const *test120 =
749                "#version 120\n"
750                "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }";
751            glShaderSource(id, 1, &test120, nullptr);
752            glCompileShader(id);
753            glGetShaderInfoLog(id, sizeof(buf), &len, buf);
754            if (len <= 0)
755                version = 120;
756        }
757
758        /* Otherwise, assume we can compile 1.10 shaders. */
759        if (!version)
760            version = 110;
761
762        glDeleteShader(id);
763#endif
764    }
765#endif
766
767    return version;
768}
769
770/* Simple shader source patching for old GLSL versions.
771 */
772void ShaderData::Patch(char *dst, char const *vert, char const *frag)
773{
774    int ver_driver = GetVersion();
775
776    strcpy(dst, vert ? vert : frag);
777    if (ver_driver >= 130)
778        return;
779
780    int ver_shader = 110;
781    char *parser = strstr(dst, "#version");
782    if (parser)
783        ver_shader = atoi(parser + strlen("#version"));
784
785    /* This is GL ES, we only know version 100. */
786    if (ver_shader > 100 && ver_driver == 100)
787    {
788        /* FIXME: this isn't elegant but honestly, we don't care, this
789         * whole file is going to die soon. */
790        char *p = strstr(dst, "#version");
791        if (p)
792        {
793            p += 8;
794            while (*p == ' ')
795                p++;
796            if (p[0] == '1' && p[1] && p[2])
797                p[1] = p[2] = '0';
798        }
799    }
800
801    if (ver_shader > 120 && ver_driver <= 120)
802    {
803        char const *end = dst + strlen(dst) + 1;
804
805        /* Find main() */
806        parser = strstr(dst, "main");
807        if (!parser) return;
808        parser = strstr(parser, "(");
809        if (!parser) return;
810        parser = strstr(parser, ")");
811        if (!parser) return;
812        parser = strstr(parser, "{");
813        if (!parser) return;
814        char *main = parser + 1;
815
816        /* Perform main() replaces */
817        char const * const main_replaces[] =
818        {
819#if 0
820            "in vec2 in_Vertex;", "vec2 in_Vertex = gl_Vertex.xy;",
821            "in vec3 in_Vertex;", "vec3 in_Vertex = gl_Vertex.xyz;",
822            "in vec4 in_Vertex;", "vec4 in_Vertex = gl_Vertex.xyzw;",
823
824            "in vec2 in_Color;", "vec2 in_Color = gl_Color.xy;",
825            "in vec3 in_Color;", "vec3 in_Color = gl_Color.xyz;",
826            "in vec4 in_Color;", "vec4 in_Color = gl_Color.xyzw;",
827
828            "in vec2 in_MultiTexCoord0;",
829               "vec2 in_MultiTexCoord0 = gl_MultiTexCoord0.xy;",
830            "in vec2 in_MultiTexCoord1;",
831               "vec2 in_MultiTexCoord1 = gl_MultiTexCoord1.xy;",
832            "in vec2 in_MultiTexCoord2;",
833               "vec2 in_MultiTexCoord2 = gl_MultiTexCoord2.xy;",
834            "in vec2 in_MultiTexCoord3;",
835               "vec2 in_MultiTexCoord3 = gl_MultiTexCoord3.xy;",
836            "in vec2 in_MultiTexCoord4;",
837               "vec2 in_MultiTexCoord4 = gl_MultiTexCoord4.xy;",
838            "in vec2 in_MultiTexCoord5;",
839               "vec2 in_MultiTexCoord5 = gl_MultiTexCoord5.xy;",
840            "in vec2 in_MultiTexCoord6;",
841               "vec2 in_MultiTexCoord6 = gl_MultiTexCoord6.xy;",
842            "in vec2 in_MultiTexCoord7;",
843               "vec2 in_MultiTexCoord7 = gl_MultiTexCoord7.xy;",
844#endif
845
846            nullptr
847        };
848
849        for (char const * const *rep = main_replaces; rep[0]; rep += 2)
850        {
851            char *match = strstr(dst, rep[0]);
852            if (match && match < main)
853            {
854                size_t l0 = strlen(rep[0]);
855                size_t l1 = strlen(rep[1]);
856                memmove(main + l1, main, end - main);
857                memcpy(main, rep[1], l1);
858                memset(match, ' ', l0);
859                main += l1;
860                end += l1;
861            }
862        }
863
864        /* Perform small replaces */
865        char const * const fast_replaces[] =
866        {
867            "#version 130", "#version 120",
868            "in vec2", vert ? "attribute vec2" : "varying vec2",
869            "in vec3", vert ? "attribute vec3" : "varying vec3",
870            "in vec4", vert ? "attribute vec4" : "varying vec4",
871            "in mat4", vert ? "attribute mat4" : "varying mat4",
872            "out vec2", "varying vec2",
873            "out vec3", "varying vec3",
874            "out vec4", "varying vec4",
875            "out mat4", "varying mat4",
876            nullptr
877        };
878
879        for (char const * const *rep = fast_replaces; rep[0]; rep += 2)
880        {
881            char *match;
882            while ((match = strstr(dst, rep[0])))
883            {
884                size_t l0 = strlen(rep[0]);
885                size_t l1 = strlen(rep[1]);
886
887                if (l1 > l0)
888                    memmove(match + l1, match + l0, (end - match) - l0);
889                memcpy(match, rep[1], l1);
890                if (l1 < l0)
891                    memset(match + l0, ' ', l1 - l0);
892                end += l1 - l0;
893            }
894        }
895    }
896}
897
898} /* namespace lol */
899
Note: See TracBrowser for help on using the repository browser.