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

Last change on this file since 2816 was 2816, checked in by lolbot, 7 years ago

fixed 542 files out of 2754:

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