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

Last change on this file since 2546 was 2546, checked in by sam, 7 years ago

base: remove some sprintf() calls in profit of string ops.

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