| @@ -13,6 +13,8 @@ | |||||
| #endif | #endif | ||||
| #include <cmath> | #include <cmath> | ||||
| #include <cstring> | |||||
| #include <cstdio> | |||||
| #ifdef WIN32 | #ifdef WIN32 | ||||
| # define WIN32_LEAN_AND_MEAN | # define WIN32_LEAN_AND_MEAN | ||||
| @@ -37,6 +39,10 @@ private: | |||||
| GLuint prog_id, vert_id, frag_id; | GLuint prog_id, vert_id, frag_id; | ||||
| uint32_t vert_crc, frag_crc; | uint32_t vert_crc, frag_crc; | ||||
| /* Shader patcher */ | |||||
| static int GetVersion(); | |||||
| static void Patch(char *dst, char const *src); | |||||
| /* Global shader cache */ | /* Global shader cache */ | ||||
| static Shader *shaders[]; | static Shader *shaders[]; | ||||
| static int nshaders; | static int nshaders; | ||||
| @@ -77,21 +83,26 @@ Shader::Shader(char const *vert, char const *frag) | |||||
| : data(new ShaderData()) | : data(new ShaderData()) | ||||
| { | { | ||||
| char buf[4096]; | char buf[4096]; | ||||
| char const *shader = buf; | |||||
| GLsizei len; | GLsizei len; | ||||
| #if !defined __CELLOS_LV2__ | #if !defined __CELLOS_LV2__ | ||||
| data->vert_crc = Hash::Crc32(vert); | |||||
| /* Compile vertex shader and fragment shader */ | |||||
| ShaderData::Patch(buf, vert); | |||||
| data->vert_crc = Hash::Crc32(buf); | |||||
| data->vert_id = glCreateShader(GL_VERTEX_SHADER); | data->vert_id = glCreateShader(GL_VERTEX_SHADER); | ||||
| glShaderSource(data->vert_id, 1, &vert, NULL); | |||||
| glShaderSource(data->vert_id, 1, &shader, NULL); | |||||
| glCompileShader(data->vert_id); | glCompileShader(data->vert_id); | ||||
| glGetShaderInfoLog(data->vert_id, sizeof(buf), &len, buf); | glGetShaderInfoLog(data->vert_id, sizeof(buf), &len, buf); | ||||
| if (len > 0) | if (len > 0) | ||||
| Log::Error("failed to compile vertex shader: %s", buf); | Log::Error("failed to compile vertex shader: %s", buf); | ||||
| data->frag_crc = Hash::Crc32(frag); | |||||
| ShaderData::Patch(buf, frag); | |||||
| data->frag_crc = Hash::Crc32(buf); | |||||
| data->frag_id = glCreateShader(GL_FRAGMENT_SHADER); | data->frag_id = glCreateShader(GL_FRAGMENT_SHADER); | ||||
| glShaderSource(data->frag_id, 1, &frag, NULL); | |||||
| glShaderSource(data->frag_id, 1, &shader, NULL); | |||||
| glCompileShader(data->frag_id); | glCompileShader(data->frag_id); | ||||
| glGetShaderInfoLog(data->frag_id, sizeof(buf), &len, buf); | glGetShaderInfoLog(data->frag_id, sizeof(buf), &len, buf); | ||||
| @@ -148,5 +159,98 @@ Shader::~Shader() | |||||
| #endif | #endif | ||||
| } | } | ||||
| /* Try to detect shader compiler features */ | |||||
| int ShaderData::GetVersion() | |||||
| { | |||||
| static int version = 0; | |||||
| if (!version) | |||||
| { | |||||
| char buf[4096]; | |||||
| GLsizei len; | |||||
| int id = glCreateShader(GL_VERTEX_SHADER); | |||||
| /* Can we compile 1.30 shaders? */ | |||||
| char const *test130 = | |||||
| "#version 130\n" | |||||
| "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }"; | |||||
| glShaderSource(id, 1, &test130, NULL); | |||||
| glCompileShader(id); | |||||
| glGetShaderInfoLog(id, sizeof(buf), &len, buf); | |||||
| if (len <= 0) | |||||
| version = 130; | |||||
| /* If not, can we compile 1.20 shaders? */ | |||||
| if (!version) | |||||
| { | |||||
| char const *test120 = | |||||
| "#version 120\n" | |||||
| "void main() { gl_Position = vec4(0.0, 0.0, 0.0, 0.0); }"; | |||||
| glShaderSource(id, 1, &test120, NULL); | |||||
| glCompileShader(id); | |||||
| glGetShaderInfoLog(id, sizeof(buf), &len, buf); | |||||
| if (len <= 0) | |||||
| version = 120; | |||||
| } | |||||
| /* Otherwise, assume we can compile 1.10 shaders. */ | |||||
| if (!version) | |||||
| version = 110; | |||||
| glDeleteShader(id); | |||||
| } | |||||
| return version; | |||||
| } | |||||
| /* Simple shader source patching for old GLSL versions. | |||||
| * If supported version is 1.30, do nothing. | |||||
| * If supported version is 1.20: | |||||
| * - replace "#version 130" with "#version 120" | |||||
| */ | |||||
| void ShaderData::Patch(char *dst, char const *src) | |||||
| { | |||||
| int version = GetVersion(); | |||||
| if (version >= 130) | |||||
| { | |||||
| strcpy(dst, src); | |||||
| return; | |||||
| } | |||||
| if (version == 120) | |||||
| { | |||||
| static char const * const replaces[] = | |||||
| { | |||||
| "#version 130", "#version 120", | |||||
| "\nin vec2", "\nvec2", | |||||
| "\nin vec3", "\nvec3", | |||||
| "\nin vec4", "\nvec4", | |||||
| "\nin mat4", "\nmat4", | |||||
| NULL | |||||
| }; | |||||
| while (*src) | |||||
| { | |||||
| for (char const * const *rep = replaces; rep[0]; rep += 2) | |||||
| { | |||||
| size_t l0 = strlen(rep[0]); | |||||
| if (!strncmp(src, rep[0], l0)) | |||||
| { | |||||
| src += l0; | |||||
| dst += sprintf(dst, "%s", rep[1]); | |||||
| goto next; | |||||
| } | |||||
| } | |||||
| *dst++ = *src++; | |||||
| next: continue; | |||||
| } | |||||
| *dst = '\0'; | |||||
| } | |||||
| } | |||||
| } /* namespace lol */ | } /* namespace lol */ | ||||