1 /**
2 * @file
3 * @brief Shader (GLSL) backend functions
4 */
5
6 /*
7 * Copyright(c) 1997-2001 Id Software, Inc.
8 * Copyright(c) 2002 The Quakeforge Project.
9 * Copyright(c) 2006 Quake2World.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or(at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 #include "r_local.h"
28 #include "r_error.h"
29 #include "r_program.h"
30 #include "../../shared/parse.h"
31 #include "../../shared/shared.h"
32
33 #define SHADER_BUF_SIZE 16384
34
35 typedef enum {SHQ_LOW, SHQ_MID, SHQ_HIGH, SHQ_NUM} shaderQualityLevel_t;
36
37 const char* shaderQualityLevelNames[SHQ_NUM][2] = {
38 {"world_low","model_low"},
39 {"world_med","model_med"},
40 {"world_med","model_high"} /** @todo high quality world shader */
41 };
42
R_UseProgram(r_program_t * prog)43 void R_UseProgram (r_program_t* prog)
44 {
45 if (!qglUseProgram || r_state.active_program == prog)
46 return;
47
48 if (!r_state.active_program)
49 refdef.FFPToShaderCount++;
50 else if (!prog)
51 refdef.shaderToFFPCount++;
52 else
53 refdef.shaderToShaderCount++;
54
55 r_state.active_program = prog;
56
57 if (prog) {
58 qglUseProgram(prog->id);
59
60 if (prog->use) /* invoke use function */
61 prog->use(prog);
62 } else {
63 qglUseProgram(0);
64 }
65 }
66
R_ProgramVariable(int type,const char * name)67 static r_progvar_t* R_ProgramVariable (int type, const char* name)
68 {
69 r_progvar_t* v;
70 int i;
71
72 if (!r_state.active_program) {
73 Com_DPrintf(DEBUG_RENDERER, "R_ProgramVariable: \"%s\" - No program bound.\n", name);
74 return nullptr;
75 }
76
77 /* find the variable */
78 for (i = 0; i < MAX_PROGRAM_VARS; i++) {
79 v = &r_state.active_program->vars[i];
80
81 if (!v->location)
82 break;
83
84 if (v->type == type && Q_streq(v->name, name))
85 return v;
86 }
87
88 if (i == MAX_PROGRAM_VARS) {
89 Com_Printf("R_ProgramVariable: MAX_PROGRAM_VARS reached.\n");
90 return nullptr;
91 }
92
93 /* or query for it */
94 if (type == GL_UNIFORM)
95 v->location = qglGetUniformLocation(r_state.active_program->id, name);
96 else
97 v->location = qglGetAttribLocation(r_state.active_program->id, name);
98
99 if (v->location == -1) {
100 Com_Printf("R_ProgramVariable: Could not find parameter %s in program %s.\n", name, r_state.active_program->name);
101 v->location = 0;
102 return nullptr;
103 }
104
105 v->type = type;
106 Q_strncpyz(v->name, name, sizeof(v->name));
107
108 return v;
109 }
110
R_ProgramParameter1i(const char * name,GLint value)111 void R_ProgramParameter1i (const char* name, GLint value)
112 {
113 r_progvar_t* v;
114
115 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
116 return;
117
118 qglUniform1i(v->location, value);
119 }
120
R_ProgramParameter1f(const char * name,GLfloat value)121 void R_ProgramParameter1f (const char* name, GLfloat value)
122 {
123 r_progvar_t* v;
124
125 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
126 return;
127
128 qglUniform1f(v->location, value);
129 }
130
R_ProgramParameter1fvs(const char * name,GLint size,GLfloat * value)131 void R_ProgramParameter1fvs (const char* name, GLint size, GLfloat* value)
132 {
133 r_progvar_t* v;
134
135 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
136 return;
137
138 qglUniform1fv(v->location, size, value);
139 }
140
R_ProgramParameter2fv(const char * name,GLfloat * value)141 void R_ProgramParameter2fv (const char* name, GLfloat* value)
142 {
143 r_progvar_t* v;
144
145 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
146 return;
147
148 qglUniform2fv(v->location, 1, value);
149 }
150
R_ProgramParameter2fvs(const char * name,GLint size,GLfloat * value)151 void R_ProgramParameter2fvs (const char* name, GLint size, GLfloat* value)
152 {
153 r_progvar_t* v;
154
155 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
156 return;
157
158 qglUniform2fv(v->location, size, value);
159 }
160
R_ProgramParameter3fv(const char * name,GLfloat * value)161 void R_ProgramParameter3fv (const char* name, GLfloat* value)
162 {
163 r_progvar_t* v;
164
165 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
166 return;
167
168 qglUniform3fv(v->location, 1, value);
169 }
170
R_ProgramParameter3fvs(const char * name,GLint size,GLfloat * value)171 void R_ProgramParameter3fvs (const char* name, GLint size, GLfloat* value)
172 {
173 r_progvar_t* v;
174
175 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
176 return;
177
178 qglUniform3fv(v->location, size, value);
179 }
180
R_ProgramParameter4fv(const char * name,GLfloat * value)181 void R_ProgramParameter4fv (const char* name, GLfloat* value)
182 {
183 r_progvar_t* v;
184
185 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
186 return;
187
188 qglUniform4fv(v->location, 1, value);
189 }
190
R_ProgramParameter4fvs(const char * name,GLint size,GLfloat * value)191 void R_ProgramParameter4fvs (const char* name, GLint size, GLfloat* value)
192 {
193 r_progvar_t* v;
194
195 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
196 return;
197
198 qglUniform4fv(v->location, size, value);
199 }
200
R_ProgramParameterMatrix4fv(const char * name,GLfloat * value)201 void R_ProgramParameterMatrix4fv (const char* name, GLfloat* value)
202 {
203 r_progvar_t* v;
204
205 if (!(v = R_ProgramVariable(GL_UNIFORM, name)))
206 return;
207
208 qglUniformMatrix4fv(v->location, 1, GL_FALSE, value);
209 }
210
R_AttributePointer(const char * name,GLuint size,const GLvoid * array)211 void R_AttributePointer (const char* name, GLuint size, const GLvoid* array)
212 {
213 r_progvar_t* v;
214
215 if (!(v = R_ProgramVariable(GL_ATTRIBUTE, name)))
216 return;
217
218 qglVertexAttribPointer(v->location, size, GL_FLOAT, GL_FALSE, 0, array);
219 }
220
R_EnableAttribute(const char * name)221 void R_EnableAttribute (const char* name)
222 {
223 r_progvar_t* v;
224
225 if (!(v = R_ProgramVariable(GL_ATTRIBUTE, name)))
226 return;
227
228 qglEnableVertexAttribArray(v->location);
229 }
230
R_DisableAttribute(const char * name)231 void R_DisableAttribute (const char* name)
232 {
233 r_progvar_t* v;
234
235 if (!(v = R_ProgramVariable(GL_ATTRIBUTE, name)))
236 return;
237
238 qglDisableVertexAttribArray(v->location);
239 }
240
R_ShutdownShader(r_shader_t * sh)241 static void R_ShutdownShader (r_shader_t* sh)
242 {
243 qglDeleteShader(sh->id);
244 OBJZERO(*sh);
245 }
246
R_ShutdownProgram(r_program_t * prog)247 static void R_ShutdownProgram (r_program_t* prog)
248 {
249 if (prog->v) {
250 qglDetachShader(prog->id, prog->v->id);
251 R_ShutdownShader(prog->v);
252 R_CheckError();
253 }
254 if (prog->f) {
255 qglDetachShader(prog->id, prog->f->id);
256 R_ShutdownShader(prog->f);
257 R_CheckError();
258 }
259
260 qglDeleteProgram(prog->id);
261
262 OBJZERO(*prog);
263 }
264
R_ShutdownPrograms(void)265 void R_ShutdownPrograms (void)
266 {
267 int i;
268
269 if (!qglDeleteProgram)
270 return;
271
272 if (!r_programs->integer)
273 return;
274
275 for (i = 0; i < MAX_PROGRAMS; i++) {
276 if (!r_state.programs[i].id)
277 continue;
278
279 R_ShutdownProgram(&r_state.programs[i]);
280 }
281 }
282
283 /**
284 * @brief Prefixes shader string (out) with in.
285 * @param[in] name The name of the shader.
286 * @param[in] in The string to prefix onto the shader string (out).
287 * @param[in,out] out The shader string (initially was the whole shader file).
288 * @param[in,out] len The amount of space left in the buffer pointed to by *out.
289 * @return strlen(in)
290 */
R_PreprocessShaderAddToShaderBuf(const char * name,const char * in,char ** out,size_t * len)291 static size_t R_PreprocessShaderAddToShaderBuf (const char* name, const char* in, char** out, size_t* len)
292 {
293 const size_t inLength = strlen(in);
294 strcpy(*out, in);
295 *out += inLength;
296 *len -= inLength;
297 return inLength;
298 }
299
300 /**
301 * @brief Prefixes the shader string with user settings and the video hardware manufacturer.
302 *
303 * The shader needs to know the rendered width & height, the glsl version, and
304 * the hardware manufacturer.
305 * @param[in] type The type of shader, currently either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
306 * @param[in] name The name of the shader.
307 * @param[in,out] out
308 * @param[in,out] len The amount of space left in the buffer pointed to by *out.
309 * @return The number of characters prefixed to the shader string.
310 */
R_InitializeShader(const GLenum type,const char * name,char * out,size_t len)311 static size_t R_InitializeShader (const GLenum type, const char* name, char* out, size_t len)
312 {
313 size_t initialChars = 0;
314 const char* hwHack, *defines;
315 /* in the format GLSL compiler expects it -- e.g. 110 for version 1.10 */
316 const int shaderVersion = (int)(r_glsl_version->value * 100 + 0.1);
317
318 switch (r_config.hardwareType) {
319 case GLHW_ATI:
320 hwHack = "#ifndef ATI\n#define ATI\n#endif\n";
321 break;
322 case GLHW_INTEL:
323 hwHack = "#ifndef INTEL\n#define INTEL\n#endif\n";
324 break;
325 case GLHW_NVIDIA:
326 hwHack = "#ifndef NVIDIA\n#define NVIDIA\n#endif\n";
327 break;
328 case GLHW_MESA:
329 hwHack = "#ifndef MESA\n#define MESA\n#endif\n";
330 break;
331 case GLHW_GENERIC:
332 hwHack = nullptr;
333 break;
334 default:
335 Com_Error(ERR_FATAL, "R_PreprocessShader: Unknown hardwaretype");
336 }
337
338 /*
339 * Prefix "#version xxx" onto shader string.
340 * This causes GLSL compiler to compile to that version.
341 */
342 defines = va("#version %d\n", shaderVersion);
343 initialChars += R_PreprocessShaderAddToShaderBuf(name, defines, &out, &len);
344
345 /*
346 * Prefix "#define glslxxx" onto shader string.
347 * This named constant is used to setup shader code to match the desired GLSL spec.
348 */
349 defines = va("#define glsl%d\n", shaderVersion);
350 initialChars += R_PreprocessShaderAddToShaderBuf(name, defines, &out, &len);
351
352 /* Define r_width.*/
353 defines = va("#ifndef r_width\n#define r_width %f\n#endif\n", (float)viddef.context.width);
354 initialChars += R_PreprocessShaderAddToShaderBuf(name, defines, &out, &len);
355
356 /* Define r_height.*/
357 defines = va("#ifndef r_height\n#define r_height %f\n#endif\n", (float)viddef.context.height);
358 initialChars += R_PreprocessShaderAddToShaderBuf(name, defines, &out, &len);
359
360 if (hwHack) {
361 initialChars += R_PreprocessShaderAddToShaderBuf(name, hwHack, &out, &len);
362 }
363
364 if (GL_VERTEX_SHADER == type) {
365 /* Define 'in_qualifier as 'attribute' & 'out_qualifier' as 'varying' if GLSL v1.10, otherwise define them as 'in' and 'out' respectively.*/
366 initialChars += R_PreprocessShaderAddToShaderBuf(name, "#ifndef glsl110\n#define in_qualifier in\n#define out_qualifier out\n#else\n#define in_qualifier attribute\n#define out_qualifier varying\n#endif\n", &out, &len);
367 } else if (GL_FRAGMENT_SHADER == type) {
368 /* Define 'texture2D' as 'texture', as texture2D was deprecated (replaced with 'texture') as of GLSL v1.30.*/
369 initialChars += R_PreprocessShaderAddToShaderBuf(name, "#ifndef glsl110\n#define texture2D texture\n#endif\n", &out, &len);
370 /* Define 'in_qualifier as 'varying' if GLSL v1.10, otherwise define it as 'in'.*/
371 initialChars += R_PreprocessShaderAddToShaderBuf(name, "#ifndef glsl110\n#define in_qualifier in\n#else\n#define in_qualifier varying\n#endif\n", &out, &len);
372 }
373
374 return initialChars;
375 }
376
377 /**
378 * @brief Do our own preprocessing to the shader file, before the
379 * GLSL implementation calls it's preprocessor.
380 *
381 * "#if/#endif" pairs, "#unroll", "#endunroll", "#include", "#replace" are handled by our
382 * preprocessor, not the GLSL implementation's preprocessor (except "#include" which may
383 * also be handled by the implementation's preprocessor). "#if" operates off
384 * of the value of a cvar interpreted as a bool. Note the GLSL implementation
385 * preprocessor handles "#ifdef" and "#ifndef", not "#if".
386 * @param[in] name The file name of the shader (e.g. "world_fs.glsl").
387 * @param[in] inPtr The non-preprocessed shader string.
388 * @param[in,out] out The preprocessed shader string, nullptr if we don't want to write to it.
389 * @param[in,out] remainingOutChars The number of characters left in the out buffer.
390 * @param[in] nested If true, parsing a part of "#if" clause, so "#else" and "#endif" tokens are allowed
391 * @param[in] inElse If true, parsing an "#else" clause and shouldn't expect another "#else"
392 * @return The number of characters added to the buffer pointed to by out.
393 */
R_PreprocessShaderR(const char * name,const char ** inPtr,char * out,long * remainingOutChars,bool nested,bool inElse)394 static size_t R_PreprocessShaderR (const char* name, const char** inPtr, char* out, long *remainingOutChars, bool nested, bool inElse)
395 {
396 const size_t INITIAL_REMAINING_OUT_CHARS = (size_t)*remainingOutChars;
397 /* Keep looping till we reach the end of the shader string, or a parsing error.*/
398 while (**inPtr) {
399 if ('#' == **inPtr) {
400 bool endBlockToken;
401 (*inPtr)++;
402
403 endBlockToken = !strncmp(*inPtr, "endif", 5);
404
405 if (!strncmp(*inPtr, "else", 4)) {
406 if (inElse) {
407 /* Error in shader! Print a message saying our preprocessor failed parsing.*/
408 Com_Error(ERR_DROP, "R_PreprocessShaderR: #else without #if: %s", name);
409 }
410 endBlockToken = true;
411 }
412
413 if (endBlockToken) {
414 if (!nested) {
415 /* Error in shader! Print a message saying our preprocessor failed parsing.*/
416 Com_Error(ERR_DROP, "R_PreprocessShaderR: Unmatched #endif/#else: %s", name);
417 }
418 /* Whoever called us will have to deal with closing the block */
419 return (INITIAL_REMAINING_OUT_CHARS - (size_t)*remainingOutChars);
420 }
421
422 if (!strncmp((*inPtr), "if ", 3)) {
423 /* The line looks like "#if r_postprocess".*/
424 (*inPtr) += 3;
425 /* Get the corresponding cvar value.*/
426 float f = Cvar_GetValue(Com_Parse(inPtr));
427 if (f) { /* Condition is true, recursively preprocess #if block, and skip over #else block, if any */
428 int size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, false);
429 if (out) out += size;
430
431 if (!strncmp((*inPtr), "else", 4)) {/* Preprocess and skip #else block */
432 (*inPtr) +=4 ;
433 R_PreprocessShaderR(name, inPtr, (char*)0, remainingOutChars, true, true);
434 }
435 } else {
436 /* The cvar was false, don't add to out. Lets look and see if we hit a #else, or #endif.*/
437 R_PreprocessShaderR(name, inPtr, (char*)0, remainingOutChars, true, false);
438 if (!strncmp((*inPtr), "else", 4)) {
439 int size;
440 /* All right, we want to add this to out.*/
441 (*inPtr) +=4 ;
442 size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, true);
443 if (out) out += size;
444 }
445 }
446 /* skip #endif, if any (could also get here by unexpected EOF */
447 if (!strncmp((*inPtr), "endif", 5))
448 (*inPtr) +=5 ;
449 } else if (!strncmp((*inPtr), "ifndef", 6) || !strncmp((*inPtr), "ifdef", 5)) { /* leave those for GLSL compiler, but follow #else/#endif nesting */
450 int size;
451 if (out) {
452 if (*remainingOutChars <= 0)
453 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
454 *out++ = '#';
455 (*remainingOutChars)--;
456 }
457
458 size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, false);
459 if (out) out += size;
460
461 if (!strncmp((*inPtr), "else", 4)) {
462 if (out) {
463 if (*remainingOutChars <= 0)
464 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
465 *out++ = '#';
466 (*remainingOutChars)--;
467 }
468 size = R_PreprocessShaderR(name, inPtr, out, remainingOutChars, true, true);
469 if (out) out += size;
470 }
471
472 if (out) {
473 if (*remainingOutChars <= 0)
474 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
475 *out++ = '#';
476 (*remainingOutChars)--;
477 }
478
479 } else if (!strncmp((*inPtr), "include", 7)) {
480 char path[MAX_QPATH];
481 byte* buf = (byte*)0;
482 const char* bufAsChar = (const char*)0;
483 const char** bufAsCharPtr = (const char**)0;
484 (*inPtr) += 8;
485 Com_sprintf(path, sizeof(path), "shaders/%s", Com_Parse(inPtr));
486 if (FS_LoadFile(path, &buf) == -1) {
487 Com_Printf("Failed to resolve #include: %s.\n", path);
488 continue;
489 }
490 bufAsChar = (const char*)buf;
491 bufAsCharPtr = &bufAsChar;
492 if (out) {
493 out += R_PreprocessShaderR(name, bufAsCharPtr, out, remainingOutChars, nested, false);
494 } else {
495 R_PreprocessShaderR(name, bufAsCharPtr, out, remainingOutChars, nested, false);
496 }
497 FS_FreeFile(buf);
498 } else if (!strncmp((*inPtr), "unroll", 6)) {
499 /* loop unrolling */
500 size_t subLength = 0;
501 byte* const buffer = Mem_PoolAllocTypeN(byte, SHADER_BUF_SIZE, vid_imagePool);
502 (*inPtr) += 6;
503 int z = Cvar_GetValue(Com_Parse(inPtr));
504 while (*(*inPtr)) {
505 if (!strncmp((*inPtr), "#endunroll", 10)) {
506 (*inPtr) += 10;
507 break;
508 }
509 buffer[subLength++] = *(*inPtr)++;
510 if (subLength >= SHADER_BUF_SIZE)
511 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
512 }
513 if (out) {
514 for (int j = 0; j < z; j++) {
515 int l = 0;
516 for (; l < subLength; l++) {
517 if (buffer[l] == '$') {
518 byte insertedLen = (j / 10) + 1;
519 if (!Com_sprintf(out, (size_t)*remainingOutChars, "%d", j))
520 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
521 out += insertedLen;
522 (*remainingOutChars) -= insertedLen;
523 } else {
524 if (*remainingOutChars <= 0)
525 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
526 *out++ = buffer[l];
527 (*remainingOutChars)--;
528 }
529 }
530 }
531 }
532 Mem_Free(buffer);
533 } else if (!strncmp((*inPtr), "replace", 7)) {
534 int r = 0;
535 (*inPtr) += 8;
536 r = Cvar_GetValue(Com_Parse(inPtr));
537 if (out) {
538 byte insertedLen = 0;
539 if (!Com_sprintf(out, (size_t)*remainingOutChars, "%d", r))
540 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
541 insertedLen = (r / 10) + 1;
542 out += insertedLen;
543 (*remainingOutChars) -= insertedLen;
544 }
545 } else {
546 /* general case is to copy so long as the buffer has room */
547 if (out) {
548 if (*remainingOutChars <= 0)
549 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
550 *out++ = '#';
551 (*remainingOutChars)--;
552 }
553 }
554 } else {
555 /* general case is to copy so long as the buffer has room */
556 if (out) {
557 if (*remainingOutChars <= 0)
558 Com_Error(ERR_FATAL, "R_PreprocessShaderR: Overflow in shader loading '%s'", name);
559 *out++ = *(*inPtr);
560 (*remainingOutChars)--;
561 }
562 (*inPtr)++;
563 }
564 }
565 /* Return the number of characters added to the buffer.*/
566 return (INITIAL_REMAINING_OUT_CHARS - *remainingOutChars);
567 }
568
569 /**
570 * @brief Do our own preprocessing to the shader file, before the
571 * GLSL implementation calls it's preprocessor.
572 *
573 * "#if/#endif" pairs, "#unroll", "#endunroll", "#include", "#replace" are handled by our
574 * preprocessor, not the GLSL implementation's preprocessor (except "#include" which may
575 * also be handled by the implementation's preprocessor). "#if" operates off
576 * of the value of a cvar interpreted as a bool. Note the GLSL implementation
577 * preprocessor handles "#ifdef" and "#ifndef", not "#if".
578 * @param[in] name The file name of the shader (e.g. "world_fs.glsl").
579 * @param[in] in The non-preprocessed shader string.
580 * @param[in,out] out The preprocessed shader string, nullptr if we don't want to write to it.
581 * @param[in,out] remainingOutChars The number of characters left in the out buffer.
582 * @return The number of characters added to the buffer pointed to by out.
583 */
R_PreprocessShader(const char * name,const char * in,char * out,size_t * remainingOutChars)584 static size_t R_PreprocessShader (const char* name, const char* in, char* out, size_t* remainingOutChars)
585 {
586 long remainingOutCharsAsLong = *remainingOutChars;
587 size_t numCharactersAddedToOutBuffer = R_PreprocessShaderR(name, &in, out, &remainingOutCharsAsLong, false, false);
588 *remainingOutChars = remainingOutCharsAsLong;
589 return numCharactersAddedToOutBuffer;
590 }
591
592 /**
593 * @brief Reads/Preprocesses/Compiles the specified shader into a program.
594 * @param[in] type The type of shader, currently either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
595 * @param[in] name The file name of the shader to load from ./base/shaders/ (e.g. "world_fs.glsl").
596 * @return A structure used as a handle to the compiled shader (program).
597 */
R_LoadShader(const GLenum type,const char * name)598 static r_shader_t* R_LoadShader (const GLenum type, const char* name)
599 {
600 r_shader_t* sh;
601 char path[MAX_QPATH], *src[1];
602 unsigned e, length[1];
603 char* srcBuf;
604 byte* buf;
605 int i;
606 size_t bufLength = SHADER_BUF_SIZE;
607 size_t initializeLength;
608
609 #ifdef DEBUG
610 /* Used to contain result of shader compile.*/
611 char log[MAX_STRING_CHARS];
612 #endif
613
614 snprintf(path, sizeof(path), "shaders/%s", name);
615
616 if (FS_LoadFile(path, &buf) == -1) {
617 Com_DPrintf(DEBUG_RENDERER, "R_LoadShader: Failed to load ./base/shaders/%s.\n", name);
618 return nullptr;
619 }
620
621 Com_DPrintf(DEBUG_RENDERER, "R_LoadShader: Loading ./base/shaders/%s.\n", name);
622
623 char* const source = srcBuf = Mem_PoolAllocTypeN(char, bufLength, vid_imagePool);
624
625 initializeLength = R_InitializeShader(type, name, srcBuf, bufLength);
626 srcBuf += initializeLength;
627 bufLength -= initializeLength;
628
629 R_PreprocessShader(name, (const char*)buf, srcBuf, &bufLength);
630 FS_FreeFile(buf);
631
632 src[0] = source;
633 length[0] = strlen(source);
634
635 for (i = 0; i < MAX_SHADERS; i++) {
636 sh = &r_state.shaders[i];
637
638 if (!sh->id)
639 break;
640 }
641
642 if (i == MAX_SHADERS) {
643 Com_Printf("R_LoadShader: MAX_SHADERS reached.\n");
644 Mem_Free(source);
645 return nullptr;
646 }
647
648 Q_strncpyz(sh->name, name, sizeof(sh->name));
649
650 sh->type = type;
651
652 sh->id = qglCreateShader(sh->type);
653 if (!sh->id) {
654 Mem_Free(source);
655 return nullptr;
656 }
657
658 /* upload the shader source */
659 qglShaderSource(sh->id, 1, src, length);
660
661 /* compile it and check for errors */
662 qglCompileShader(sh->id);
663
664 Mem_Free(source);
665
666 qglGetShaderiv(sh->id, GL_COMPILE_STATUS, &e);
667 #ifdef DEBUG
668 qglGetShaderInfoLog(sh->id, sizeof(log) - 1, nullptr, log);
669 Com_Printf("R_LoadShader: %s: %s", sh->name, log);
670 #endif
671 if (!e) {
672 #ifndef DEBUG
673 char log[MAX_STRING_CHARS];
674 qglGetShaderInfoLog(sh->id, sizeof(log) - 1, nullptr, log);
675 Com_Printf("R_LoadShader: %s: %s", sh->name, log);
676 #endif
677
678 qglDeleteShader(sh->id);
679 OBJZERO(*sh);
680
681 return nullptr;
682 }
683
684 return sh;
685 }
686
R_LoadProgram(const char * name,programInitFunc_t init,programUseFunc_t use)687 r_program_t* R_LoadProgram (const char* name, programInitFunc_t init, programUseFunc_t use)
688 {
689 r_program_t* prog;
690 unsigned e;
691 int i;
692
693 /* shaders are deactivated */
694 if (!r_programs->integer)
695 return nullptr;
696
697 /* search existing one */
698 for (i = 0; i < MAX_PROGRAMS; i++) {
699 prog = &r_state.programs[i];
700
701 if (Q_streq(prog->name, name))
702 return prog;
703 }
704
705 /* search free slot */
706 for (i = 0; i < MAX_PROGRAMS; i++) {
707 prog = &r_state.programs[i];
708
709 if (!prog->id)
710 break;
711 }
712
713 if (i == MAX_PROGRAMS) {
714 Com_Printf("R_LoadProgram: MAX_PROGRAMS reached.\n");
715 return nullptr;
716 }
717
718 Q_strncpyz(prog->name, name, sizeof(prog->name));
719
720 prog->id = qglCreateProgram();
721
722 prog->v = R_LoadShader(GL_VERTEX_SHADER, va("%s_vs.glsl", name));
723 prog->f = R_LoadShader(GL_FRAGMENT_SHADER, va("%s_fs.glsl", name));
724
725 if (prog->v)
726 qglAttachShader(prog->id, prog->v->id);
727 if (prog->f)
728 qglAttachShader(prog->id, prog->f->id);
729
730 qglLinkProgram(prog->id);
731
732 qglGetProgramiv(prog->id, GL_LINK_STATUS, &e);
733 if (!e || !prog->v || !prog->f) {
734 char log[MAX_STRING_CHARS];
735 qglGetProgramInfoLog(prog->id, sizeof(log) - 1, nullptr, log);
736 Com_Printf("R_LoadProgram: %s: %s\n", prog->name, log);
737
738 R_ShutdownProgram(prog);
739 return nullptr;
740 }
741
742 prog->init = init;
743
744 if (prog->init) { /* invoke initialization function */
745 R_UseProgram(prog);
746
747 prog->init(prog);
748
749 R_UseProgram(nullptr);
750 }
751
752 prog->use = use;
753
754 Com_Printf("R_LoadProgram: '%s' loaded.\n", name);
755
756 return prog;
757 }
758
759 extern vec2_t fogRange;
760
R_InitWorldProgram(r_program_t * prog)761 static void R_InitWorldProgram (r_program_t* prog)
762 {
763 R_ProgramParameter1i("SAMPLER_DIFFUSE", 0);
764 R_ProgramParameter1i("SAMPLER_LIGHTMAP", 1);
765 R_ProgramParameter1i("SAMPLER_DELUXEMAP", 2);
766 R_ProgramParameter1i("SAMPLER_NORMALMAP", 3);
767 R_ProgramParameter1i("SAMPLER_GLOWMAP", 4);
768
769 R_ProgramParameter1i("BUMPMAP", 0);
770
771 if (r_programs->integer > 1) {
772 R_ProgramParameter3fv("AMBIENT", refdef.ambientColor);
773
774 R_ProgramParameter1i("SPECULARMAP", 0);
775 R_ProgramParameter1i("SAMPLER_SPECULAR", 5);
776
777 R_ProgramParameter1f("HARDNESS", defaultMaterial.hardness);
778 R_ProgramParameter1f("SPECULAR", defaultMaterial.specular);
779 R_ProgramParameter1f("PARALLAX", defaultMaterial.parallax);
780 }
781
782 R_ProgramParameter1f("BUMP", defaultMaterial.bump);
783 R_ProgramParameter1f("GLOWSCALE", defaultMaterial.glowscale);
784
785 if (r_fog->integer) {
786 if (r_state.fog_enabled) {
787 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
788 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
789 R_ProgramParameter2fv("FOGRANGE", fogRange);
790 } else {
791 R_ProgramParameter1f("FOGDENSITY", 0.0f);
792 }
793 }
794 }
795
R_UseWorldProgram(r_program_t * prog)796 static void R_UseWorldProgram (r_program_t* prog)
797 {
798 if (r_programs->integer > 1) {
799 R_ProgramParameter3fv("AMBIENT", refdef.ambientColor);
800 }
801
802 if (r_fog->integer) {
803 if (r_state.fog_enabled) {
804 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
805 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
806 R_ProgramParameter2fv("FOGRANGE", fogRange);
807 } else {
808 R_ProgramParameter1f("FOGDENSITY", 0.0f);
809 }
810 }
811 }
812
R_InitModelProgram(r_program_t * prog)813 static void R_InitModelProgram (r_program_t* prog)
814 {
815 vec4_t sunDirection;
816
817 R_ProgramParameter1i("SAMPLER_DIFFUSE", 0);
818 R_ProgramParameter1i("SAMPLER_NORMALMAP", 3);
819 R_ProgramParameter1i("SAMPLER_GLOWMAP", 4);
820
821 R_ProgramParameter1i("BUMPMAP", 0);
822 R_ProgramParameter1i("ANIMATE", 0);
823
824 R_ProgramParameter1f("BUMP", defaultMaterial.bump);
825 R_ProgramParameter1f("GLOWSCALE", defaultMaterial.glowscale);
826 R_ProgramParameter1f("OFFSET", 0.0);
827
828 R_ProgramParameter3fv("AMBIENT", refdef.modelAmbientColor);
829 R_ProgramParameter3fv("SUNCOLOR", refdef.sunDiffuseColor);
830
831 GLVectorTransform(r_locals.world_matrix, refdef.sunVector, sunDirection);
832 R_ProgramParameter3fv("SUNDIRECTION", sunDirection); /* last component is not needed */
833
834 if (r_programs->integer > 1) {
835 R_ProgramParameter1i("SAMPLER_SPECULAR", 5);
836 R_ProgramParameter1i("SPECULARMAP", 0);
837 R_ProgramParameter1f("HARDNESS", defaultMaterial.hardness);
838 R_ProgramParameter1f("SPECULAR", defaultMaterial.specular);
839 R_ProgramParameter1f("PARALLAX", defaultMaterial.parallax);
840 if (r_programs->integer > 2) {
841 R_ProgramParameter1i("SAMPLER_ROUGHMAP", 2);
842 R_ProgramParameter1i("ROUGHMAP", 0);
843 }
844 }
845
846 if (r_fog->integer) {
847 if (r_state.fog_enabled) {
848 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
849 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
850 R_ProgramParameter2fv("FOGRANGE", fogRange);
851 } else {
852 R_ProgramParameter1f("FOGDENSITY", 0.0f);
853 }
854 }
855 }
856
R_UseModelProgram(r_program_t * prog)857 static void R_UseModelProgram (r_program_t* prog)
858 {
859 vec4_t sunDirection;
860 /*R_ProgramParameter1i("LIGHTS", refdef.numLights);*/
861
862 R_ProgramParameter1f("OFFSET", 0.0);
863 R_ProgramParameter3fv("AMBIENT", refdef.modelAmbientColor);
864 R_ProgramParameter3fv("SUNCOLOR", refdef.sunDiffuseColor);
865
866 GLVectorTransform(r_locals.world_matrix, refdef.sunVector, sunDirection);
867 R_ProgramParameter3fv("SUNDIRECTION", sunDirection); /* last component is not needed */
868
869 if (r_fog->integer) {
870 if (r_state.fog_enabled) {
871 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
872 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
873 R_ProgramParameter2fv("FOGRANGE", fogRange);
874 } else {
875 R_ProgramParameter1f("FOGDENSITY", 0.0f);
876 }
877 }
878 }
879
R_InitWarpProgram(r_program_t * prog)880 static void R_InitWarpProgram (r_program_t* prog)
881 {
882 static vec4_t offset;
883
884 R_ProgramParameter1i("SAMPLER_DIFFUSE", 0);
885 R_ProgramParameter1i("SAMPLER_WARP", 1);
886 R_ProgramParameter1i("SAMPLER_GLOWMAP", 4);
887 R_ProgramParameter1f("GLOWSCALE", 0.0);
888 R_ProgramParameter4fv("OFFSET", offset);
889 if (r_fog->integer) {
890 if (r_state.fog_enabled) {
891 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
892 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
893 R_ProgramParameter2fv("FOGRANGE", fogRange);
894 } else {
895 R_ProgramParameter1f("FOGDENSITY", 0.0f);
896 }
897 }
898 }
899
R_UseWarpProgram(r_program_t * prog)900 static void R_UseWarpProgram (r_program_t* prog)
901 {
902 static vec4_t offset;
903
904 offset[0] = offset[1] = refdef.time / 8.0;
905 R_ProgramParameter4fv("OFFSET", offset);
906 if (r_fog->integer) {
907 if (r_state.fog_enabled) {
908 R_ProgramParameter3fv("FOGCOLOR", refdef.fogColor);
909 R_ProgramParameter1f("FOGDENSITY", refdef.fogColor[3]);
910 R_ProgramParameter2fv("FOGRANGE", fogRange);
911 } else {
912 R_ProgramParameter1f("FOGDENSITY", 0.0f);
913 }
914 }
915 }
916
R_InitGeoscapeProgram(r_program_t * prog)917 static void R_InitGeoscapeProgram (r_program_t* prog)
918 {
919 static vec4_t defaultColor = {0.0, 0.0, 0.0, 1.0};
920 static vec4_t cityLightColor = {1.0, 1.0, 0.8, 1.0};
921 static vec2_t uvScale = {2.0, 1.0};
922
923 R_ProgramParameter1i("SAMPLER_DIFFUSE", 0);
924 R_ProgramParameter1i("SAMPLER_BLEND", 1);
925 R_ProgramParameter1i("SAMPLER_NORMALMAP", 2);
926
927 R_ProgramParameter4fv("DEFAULTCOLOR", defaultColor);
928 R_ProgramParameter4fv("CITYLIGHTCOLOR", cityLightColor);
929 R_ProgramParameter2fv("UVSCALE", uvScale);
930 }
931
932 /**
933 * @note this is a not-terribly-efficient recursive implementation,
934 * but it only happens once and shouldn't have to go very deep.
935 */
R_PascalTriangle(int row,int col)936 static int R_PascalTriangle (int row, int col)
937 {
938 if (row <= 1 || col <= 1 || col >= row)
939 return 1;
940 return R_PascalTriangle(row - 1, col) + R_PascalTriangle(row - 1, col - 1);
941 }
942
943 /** @brief width of convolution filter (for blur/bloom effects) */
944 #define FILTER_SIZE 3
945
R_InitConvolveProgram(r_program_t * prog)946 static void R_InitConvolveProgram (r_program_t* prog)
947 {
948 float filter[FILTER_SIZE];
949 float sum = 0;
950 int i;
951 const size_t size = lengthof(filter);
952
953 /* approximate a Gaussian by normalizing the Nth row of Pascale's Triangle */
954 for (i = 0; i < size; i++) {
955 filter[i] = (float)R_PascalTriangle(size, i + 1);
956 sum += filter[i];
957 }
958
959 for (i = 0; i < size; i++)
960 filter[i] = (filter[i] / sum);
961
962 R_ProgramParameter1i("SAMPLER0", 0);
963 R_ProgramParameter1fvs("COEFFICIENTS", size, filter);
964 }
965
966 /**
967 * @brief Use the filter convolution glsl program
968 */
R_UseConvolveProgram(r_program_t * prog)969 static void R_UseConvolveProgram (r_program_t* prog)
970 {
971 int i;
972 const float* userdata= (float*)prog->userdata;
973 float offsets[FILTER_SIZE * 2];
974 const float halfWidth = (FILTER_SIZE - 1) * 0.5;
975 const float offset = 1.2f / userdata[0];
976 const float x = userdata[1] * offset;
977
978 for (i = 0; i < FILTER_SIZE; i++) {
979 const float y = (float)i - halfWidth;
980 const float z = x * y;
981 offsets[i * 2 + 0] = offset * y - z;
982 offsets[i * 2 + 1] = z;
983 }
984 R_ProgramParameter2fvs("OFFSETS", FILTER_SIZE, offsets);
985 }
986
R_InitCombine2Program(r_program_t * prog)987 static void R_InitCombine2Program (r_program_t* prog)
988 {
989 GLfloat defaultColor[4] = {0.0, 0.0, 0.0, 0.0};
990
991 R_ProgramParameter1i("SAMPLER0", 0);
992 R_ProgramParameter1i("SAMPLER1", 1);
993
994 R_ProgramParameter4fv("DEFAULTCOLOR", defaultColor);
995 }
996
R_InitAtmosphereProgram(r_program_t * prog)997 static void R_InitAtmosphereProgram (r_program_t* prog)
998 {
999 static vec4_t defaultColor = {0.0, 0.0, 0.0, 1.0};
1000 static vec2_t uvScale = {2.0, 1.0};
1001
1002 R_ProgramParameter1i("SAMPLER_DIFFUSE", 0);
1003 R_ProgramParameter1i("SAMPLER_NORMALMAP", 2);
1004
1005 R_ProgramParameter4fv("DEFAULTCOLOR", defaultColor);
1006 R_ProgramParameter2fv("UVSCALE", uvScale);
1007 }
1008
R_InitSimpleGlowProgram(r_program_t * prog)1009 static void R_InitSimpleGlowProgram (r_program_t* prog)
1010 {
1011 R_ProgramParameter1i("SAMPLER_DIFFUSE", 0);
1012 R_ProgramParameter1i("SAMPLER_GLOWMAP", 4);
1013 R_ProgramParameter1f("GLOWSCALE", 1.0);
1014 }
1015
R_InitParticleProgram(r_program_t * prog)1016 void R_InitParticleProgram (r_program_t* prog)
1017 {
1018 R_ProgramParameter1i("SAMPLER0", 0);
1019 }
1020
R_UseParticleProgram(r_program_t * prog)1021 void R_UseParticleProgram (r_program_t* prog)
1022 {
1023 /* ptl_t* ptl = (ptl_t*)prog->userdata;*/
1024 }
1025
R_InitPrograms(void)1026 void R_InitPrograms (void)
1027 {
1028 if (!qglCreateProgram) {
1029 Com_Printf("not using GLSL shaders\n");
1030 Cvar_Set("r_programs", "0");
1031 r_programs->modified = false;
1032 return;
1033 }
1034
1035 OBJZERO(r_state.shaders);
1036 OBJZERO(r_state.programs);
1037
1038 /* Capable of running shaders, but have got them disabled, so do nothing */
1039 if (!r_programs->integer)
1040 return;
1041
1042 r_state.world_program = R_LoadProgram(shaderQualityLevelNames[r_programs->integer - 1][0], R_InitWorldProgram, R_UseWorldProgram);
1043 r_state.model_program = R_LoadProgram(shaderQualityLevelNames[r_programs->integer - 1][1], R_InitModelProgram, R_UseModelProgram);
1044 r_state.warp_program = R_LoadProgram("warp", R_InitWarpProgram, R_UseWarpProgram);
1045 r_state.geoscape_program = R_LoadProgram("geoscape", R_InitGeoscapeProgram, nullptr);
1046 r_state.combine2_program = R_LoadProgram("combine2", R_InitCombine2Program, nullptr);
1047 r_state.convolve_program = R_LoadProgram("convolve" DOUBLEQUOTE(FILTER_SIZE), R_InitConvolveProgram, R_UseConvolveProgram);
1048 r_state.atmosphere_program = R_LoadProgram("atmosphere", R_InitAtmosphereProgram, nullptr);
1049 r_state.simple_glow_program = R_LoadProgram("simple_glow", R_InitSimpleGlowProgram, nullptr);
1050
1051 if (!(r_state.world_program && r_state.model_program && r_state.warp_program && r_state.geoscape_program && r_state.combine2_program
1052 && r_state.convolve_program && r_state.atmosphere_program && r_state.simple_glow_program)) {
1053 Com_Printf("disabled shaders because they failed to compile\n");
1054 Cvar_Set("r_programs", "0");
1055 r_programs->modified = false;
1056 }
1057 }
1058
1059 /**
1060 * @brief Reloads the glsl shaders
1061 */
R_RestartPrograms_f(void)1062 void R_RestartPrograms_f (void)
1063 {
1064 if (r_programs->integer) {
1065 Com_Printf("glsl restart to a version of v%s\n", Cvar_Get("r_glsl_version", nullptr, 0, nullptr)->string);
1066 } else {
1067 Com_Printf("glsl shutdown\n");
1068 }
1069
1070 R_ShutdownPrograms();
1071 R_InitPrograms();
1072 R_InitFBObjects();
1073 }
1074