1 #include <set>
2
3 #include <cstdio>
4 #include <cstring>
5 #include <sys/stat.h>
6
7 #include "Common/File/VFS/VFS.h"
8 #include "Common/File/FileUtil.h"
9 #include "Common/GPU/OpenGL/GLSLProgram.h"
10
11 #include "Common/Log.h"
12
13 static std::set<GLSLProgram *> active_programs;
14
CompileShader(const char * source,GLuint shader,const char * filename,std::string * error_message)15 bool CompileShader(const char *source, GLuint shader, const char *filename, std::string *error_message) {
16 glShaderSource(shader, 1, &source, NULL);
17 glCompileShader(shader);
18 GLint success;
19 glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
20 if (!success) {
21 #define MAX_INFO_LOG_SIZE 2048
22 GLchar infoLog[MAX_INFO_LOG_SIZE];
23 GLsizei len;
24 glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog);
25 infoLog[len] = '\0';
26 ERROR_LOG(G3D, "Error in shader compilation of %s!\n", filename);
27 ERROR_LOG(G3D, "Info log: %s\n", infoLog);
28 ERROR_LOG(G3D, "Shader source:\n%s\n", (const char *)source);
29 if (error_message)
30 *error_message = infoLog;
31 return false;
32 }
33 return true;
34 }
35
glsl_create(const char * vshader,const char * fshader,std::string * error_message)36 GLSLProgram *glsl_create(const char *vshader, const char *fshader, std::string *error_message) {
37 GLSLProgram *program = new GLSLProgram();
38 program->program_ = 0;
39 program->vsh_ = 0;
40 program->fsh_ = 0;
41 program->vshader_source = 0;
42 program->fshader_source = 0;
43 strcpy(program->name, vshader + strlen(vshader) - 15);
44 strcpy(program->vshader_filename, vshader);
45 strcpy(program->fshader_filename, fshader);
46 if (glsl_recompile(program, error_message)) {
47 active_programs.insert(program);
48 } else {
49 ERROR_LOG(G3D, "Failed compiling GLSL program: %s %s", vshader, fshader);
50 delete program;
51 return 0;
52 }
53 return program;
54 }
55
glsl_create_source(const char * vshader_src,const char * fshader_src,std::string * error_message)56 GLSLProgram *glsl_create_source(const char *vshader_src, const char *fshader_src, std::string *error_message) {
57 GLSLProgram *program = new GLSLProgram();
58 program->program_ = 0;
59 program->vsh_ = 0;
60 program->fsh_ = 0;
61 program->vshader_source = vshader_src;
62 program->fshader_source = fshader_src;
63 strcpy(program->name, "[srcshader]");
64 strcpy(program->vshader_filename, "");
65 strcpy(program->fshader_filename, "");
66 if (glsl_recompile(program, error_message)) {
67 active_programs.insert(program);
68 } else {
69 ERROR_LOG(G3D, "Failed compiling GLSL program from source strings");
70 delete program;
71 return 0;
72 }
73 return program;
74 }
75
76 // Not wanting to change ReadLocalFile semantics.
77 // TODO: Use C++11 unique_ptr, remove delete[]
78 struct AutoCharArrayBuf {
AutoCharArrayBufAutoCharArrayBuf79 AutoCharArrayBuf(char *buf = nullptr) : buf_(buf) {
80 }
~AutoCharArrayBufAutoCharArrayBuf81 ~AutoCharArrayBuf() {
82 delete [] buf_;
83 buf_ = nullptr;
84 }
resetAutoCharArrayBuf85 void reset(char *buf) {
86 if (buf_) {
87 delete[] buf_;
88 }
89 buf_ = buf;
90 }
operator char*AutoCharArrayBuf91 operator char *() {
92 return buf_;
93 }
94
95 private:
96 char *buf_;
97 };
98
glsl_recompile(GLSLProgram * program,std::string * error_message)99 bool glsl_recompile(GLSLProgram *program, std::string *error_message) {
100 struct stat vs, fs;
101 AutoCharArrayBuf vsh_src, fsh_src;
102
103 if (strlen(program->vshader_filename) > 0 && 0 == stat(program->vshader_filename, &vs)) {
104 program->vshader_mtime = vs.st_mtime;
105 if (!program->vshader_source) {
106 size_t sz;
107 vsh_src.reset((char *)File::ReadLocalFile(Path(program->vshader_filename), &sz));
108 }
109 } else {
110 program->vshader_mtime = 0;
111 }
112
113 if (strlen(program->fshader_filename) > 0 && 0 == stat(program->fshader_filename, &fs)) {
114 program->fshader_mtime = fs.st_mtime;
115 if (!program->fshader_source) {
116 size_t sz;
117 fsh_src.reset((char *)File::ReadLocalFile(Path(program->fshader_filename), &sz));
118 }
119 } else {
120 program->fshader_mtime = 0;
121 }
122
123 if (!program->vshader_source && !vsh_src) {
124 size_t sz;
125 vsh_src.reset((char *)VFSReadFile(program->vshader_filename, &sz));
126 }
127 if (!program->vshader_source && !vsh_src) {
128 ERROR_LOG(G3D, "File missing: %s", program->vshader_filename);
129 if (error_message) {
130 *error_message = std::string("File missing: ") + program->vshader_filename;
131 }
132 return false;
133 }
134 if (!program->fshader_source && !fsh_src) {
135 size_t sz;
136 fsh_src.reset((char *)VFSReadFile(program->fshader_filename, &sz));
137 }
138 if (!program->fshader_source && !fsh_src) {
139 ERROR_LOG(G3D, "File missing: %s", program->fshader_filename);
140 if (error_message) {
141 *error_message = std::string("File missing: ") + program->fshader_filename;
142 }
143 return false;
144 }
145
146 GLuint vsh = glCreateShader(GL_VERTEX_SHADER);
147 const GLchar *vsh_str = program->vshader_source ? program->vshader_source : (const GLchar *)(vsh_src);
148 if (!CompileShader(vsh_str, vsh, program->vshader_filename, error_message)) {
149 return false;
150 }
151
152 const GLchar *fsh_str = program->fshader_source ? program->fshader_source : (const GLchar *)(fsh_src);
153 GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER);
154 if (!CompileShader(fsh_str, fsh, program->fshader_filename, error_message)) {
155 glDeleteShader(vsh);
156 return false;
157 }
158
159 GLuint prog = glCreateProgram();
160 glAttachShader(prog, vsh);
161 glAttachShader(prog, fsh);
162
163 glLinkProgram(prog);
164
165 GLint linkStatus;
166 glGetProgramiv(prog, GL_LINK_STATUS, &linkStatus);
167 if (linkStatus == GL_FALSE) {
168 GLint bufLength = 0;
169 glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &bufLength);
170 if (bufLength) {
171 char* buf = new char[bufLength + 1]; // safety
172 glGetProgramInfoLog(prog, bufLength, NULL, buf);
173 INFO_LOG(G3D, "vsh: %i fsh: %i", vsh, fsh);
174 ERROR_LOG(G3D, "Could not link shader program (linkstatus=%i):\n %s \n", linkStatus, buf);
175 if (error_message) {
176 *error_message = buf;
177 }
178 delete [] buf;
179 } else {
180 INFO_LOG(G3D, "vsh: %i fsh: %i", vsh, fsh);
181 ERROR_LOG(G3D, "Could not link shader program (linkstatus=%i). No OpenGL error log was available.", linkStatus);
182 if (error_message) {
183 *error_message = "(no error message available)";
184 }
185 }
186 glDeleteShader(vsh);
187 glDeleteShader(fsh);
188 return false;
189 }
190
191 // Destroy the old program, if any.
192 if (program->program_) {
193 glDeleteProgram(program->program_);
194 }
195
196 program->program_ = prog;
197 program->vsh_ = vsh;
198 program->fsh_ = fsh;
199
200 program->sampler0 = glGetUniformLocation(program->program_, "sampler0");
201 program->sampler1 = glGetUniformLocation(program->program_, "sampler1");
202
203 program->a_position = glGetAttribLocation(program->program_, "a_position");
204 program->a_color = glGetAttribLocation(program->program_, "a_color");
205 program->a_normal = glGetAttribLocation(program->program_, "a_normal");
206 program->a_texcoord0 = glGetAttribLocation(program->program_, "a_texcoord0");
207 program->a_texcoord1 = glGetAttribLocation(program->program_, "a_texcoord1");
208
209 program->u_worldviewproj = glGetUniformLocation(program->program_, "u_worldviewproj");
210 program->u_world = glGetUniformLocation(program->program_, "u_world");
211 program->u_viewproj = glGetUniformLocation(program->program_, "u_viewproj");
212 program->u_fog = glGetUniformLocation(program->program_, "u_fog");
213 program->u_sundir = glGetUniformLocation(program->program_, "u_sundir");
214 program->u_camerapos = glGetUniformLocation(program->program_, "u_camerapos");
215
216 //INFO_LOG(G3D, "Shader compilation success: %s %s",
217 // program->vshader_filename,
218 // program->fshader_filename);
219 return true;
220 }
221
glsl_attrib_loc(const GLSLProgram * program,const char * name)222 int glsl_attrib_loc(const GLSLProgram *program, const char *name) {
223 return glGetAttribLocation(program->program_, name);
224 }
225
glsl_uniform_loc(const GLSLProgram * program,const char * name)226 int glsl_uniform_loc(const GLSLProgram *program, const char *name) {
227 return glGetUniformLocation(program->program_, name);
228 }
229
glsl_destroy(GLSLProgram * program)230 void glsl_destroy(GLSLProgram *program) {
231 if (program) {
232 glDeleteShader(program->vsh_);
233 glDeleteShader(program->fsh_);
234 glDeleteProgram(program->program_);
235 active_programs.erase(program);
236 } else {
237 ERROR_LOG(G3D, "Deleting null GLSL program!");
238 }
239 delete program;
240 }
241
242 static const GLSLProgram *curProgram;
243
glsl_bind(const GLSLProgram * program)244 void glsl_bind(const GLSLProgram *program) {
245 if (program)
246 glUseProgram(program->program_);
247 else
248 glUseProgram(0);
249 curProgram = program;
250 }
251
glsl_unbind()252 void glsl_unbind() {
253 glUseProgram(0);
254 curProgram = nullptr;
255 }
256
glsl_get_program()257 const GLSLProgram *glsl_get_program() {
258 return curProgram;
259 }
260