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