Panda3D

shader.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file shader.cxx
10 * @author jyelon
11 * @date 2005-09-01
12 * @author fperazzi, PandaSE
13 * @date 2010-04-06
14 * @author fperazzi, PandaSE
15 * @date 2010-04-29
16 */
17
18 #include "pandabase.h"
19 #include "shader.h"
20 #include "preparedGraphicsObjects.h"
21 #include "virtualFileSystem.h"
22 #include "config_putil.h"
23 #include "bamCache.h"
24 #include "string_utils.h"
25
26 #ifdef HAVE_CG
27 #include <Cg/cg.h>
28 #endif
29
30 using std::istream;
31 using std::move;
32 using std::ostream;
33 using std::ostringstream;
34 using std::string;
35
36 TypeHandle Shader::_type_handle;
37 Shader::ShaderTable Shader::_load_table;
38 Shader::ShaderTable Shader::_make_table;
39 Shader::ShaderCaps Shader::_default_caps;
40 int Shader::_shaders_generated;
41
42 #ifdef HAVE_CG
43 CGcontext Shader::_cg_context = 0;
44 #endif
45
46 /**
47 * Generate an error message including a description of the specified
48 * parameter.
49 */
50 void Shader::
51 cp_report_error(ShaderArgInfo &p, const string &msg) {
52
53 string vstr;
54 if (p._varying) {
55 vstr = "varying ";
56 } else {
57 vstr = "uniform ";
58 }
59
60 string dstr = "unknown ";
61 if (p._direction == SAD_in) {
62 dstr = "in ";
63 } else if (p._direction == SAD_out) {
64 dstr = "out ";
65 } else if (p._direction == SAD_inout) {
66 dstr = "inout ";
67 }
68
69 string tstr = "invalid ";
70 switch (p._type) {
71 case SAT_scalar: tstr = "scalar "; break;
72 case SAT_vec1: tstr = "vec1 "; break;
73 case SAT_vec2: tstr = "vec2 "; break;
74 case SAT_vec3: tstr = "vec3 "; break;
75 case SAT_vec4: tstr = "vec4 "; break;
76 case SAT_mat1x1: tstr = "mat1x1 "; break;
77 case SAT_mat1x2: tstr = "mat1x2 "; break;
78 case SAT_mat1x3: tstr = "mat1x3 "; break;
79 case SAT_mat1x4: tstr = "mat1x4 "; break;
80 case SAT_mat2x1: tstr = "mat2x1 "; break;
81 case SAT_mat2x2: tstr = "mat2x2 "; break;
82 case SAT_mat2x3: tstr = "mat2x3 "; break;
83 case SAT_mat2x4: tstr = "mat2x4 "; break;
84 case SAT_mat3x1: tstr = "mat3x1 "; break;
85 case SAT_mat3x2: tstr = "mat3x2 "; break;
86 case SAT_mat3x3: tstr = "mat3x3 "; break;
87 case SAT_mat3x4: tstr = "mat3x4 "; break;
88 case SAT_mat4x1: tstr = "mat4x1 "; break;
89 case SAT_mat4x2: tstr = "mat4x2 "; break;
90 case SAT_mat4x3: tstr = "mat4x3 "; break;
91 case SAT_mat4x4: tstr = "mat4x4 "; break;
92 case SAT_sampler1d: tstr = "sampler1D "; break;
93 case SAT_sampler2d: tstr = "sampler2D "; break;
94 case SAT_sampler3d: tstr = "sampler3D "; break;
95 case SAT_sampler2d_array: tstr = "sampler2DArray "; break;
96 case SAT_sampler_cube: tstr = "samplerCUBE "; break;
97 case SAT_sampler_buffer: tstr = "samplerBUF "; break;
98 case SAT_sampler_cube_array:tstr = "samplerCUBEARRAY "; break;
99 default: tstr = "unknown "; break;
100 }
101
102 string cstr = "invalid";
103 switch (p._class) {
104 case SAC_scalar: cstr = "scalar "; break;
105 case SAC_vector: cstr = "vector "; break;
106 case SAC_matrix: cstr = "matrix "; break;
107 case SAC_sampler: cstr = "sampler "; break;
108 case SAC_array: cstr = "array "; break;
109 default: cstr = "unknown "; break;
110 }
111
112 Filename fn = get_filename(p._id._type);
113 p._cat->error() << fn << ": " << vstr << dstr << tstr <<
114 p._id._name << ": " << msg << "\n";
115 }
116
117 /**
118 * Make sure the provided parameter contains the specified number of words.
119 * If not, print error message and return false.
120 */
121 bool Shader::
122 cp_errchk_parameter_words(ShaderArgInfo &p, int len)
123 {
124 vector_string words;
125 tokenize(p._id._name, words, "_");
126 if ((int)words.size() != len) {
127 cp_report_error(p, "parameter name has wrong number of words");
128 return false;
129 }
130 return true;
131 }
132
133 /**
134 * Make sure the provided parameter has the 'in' direction. If not, print
135 * error message and return false.
136 */
137 bool Shader::
138 cp_errchk_parameter_in(ShaderArgInfo &p)
139 {
140 if (p._direction != SAD_in) {
141 cp_report_error(p, "parameter should be declared 'in'");
142 return false;
143 }
144 return true;
145 }
146
147 /**
148 * Make sure the provided parameter has the correct variance. If not, print
149 * error message and return false.
150 */
151 bool Shader::
152 cp_errchk_parameter_varying(ShaderArgInfo &p)
153 {
154 if (!p._varying) {
155 cp_report_error(p, "parameter should be declared 'varying'");
156 return false;
157 }
158 return true;
159 }
160
161 /**
162 * Make sure the provided parameter has the correct variance. If not, print
163 * error message and return false.
164 */
165 bool Shader::
166 cp_errchk_parameter_uniform(ShaderArgInfo &p)
167 {
168 if (p._varying) {
169 cp_report_error(p, "parameter should be declared 'uniform'");
170 return false;
171 }
172 return true;
173 }
174
175 /**
176 * Make sure the provided parameter has a floating point type. If not, print
177 * error message and return false.
178 */
179 bool Shader::
180 cp_errchk_parameter_float(ShaderArgInfo &p, int lo, int hi)
181 {
182 int nfloat;
183 switch (p._type) {
184 case SAT_scalar: nfloat = 1; break;
185 case SAT_vec2: nfloat = 2; break;
186 case SAT_vec3: nfloat = 3; break;
187 case SAT_vec4: nfloat = 4; break;
188 case SAT_mat3x3: nfloat = 9; break;
189 case SAT_mat4x4: nfloat = 16; break;
190 default: nfloat = 0; break;
191 }
192 if ((nfloat < lo)||(nfloat > hi)) {
193 string msg = "wrong type for parameter:";
194 cp_report_error(p, msg);
195 return false;
196 }
197 return true;
198 }
199
200 /**
201 *
202 */
203 bool Shader::
204 cp_errchk_parameter_ptr(ShaderArgInfo &p) {
205 switch (p._class) {
206 case SAC_scalar: return true;
207 case SAC_vector: return true;
208 case SAC_matrix: return true;
209 case SAC_array:
210 switch (p._subclass) {
211 case SAC_scalar: return true;
212 case SAC_vector: return true;
213 case SAC_matrix: return true;
214 default:
215 string msg = "unsupported array subclass.";
216 cp_report_error(p, msg);
217 return false;
218 }
219 default:
220 string msg = "unsupported class.";
221 cp_report_error(p,msg);
222 return false;
223 }
224 }
225
226 /**
227 * Make sure the provided parameter has a texture type. If not, print error
228 * message and return false.
229 */
230 bool Shader::
231 cp_errchk_parameter_sampler(ShaderArgInfo &p)
232 {
233 if ((p._type!=SAT_sampler1d)&&
234 (p._type!=SAT_sampler2d)&&
235 (p._type!=SAT_sampler3d)&&
236 (p._type!=SAT_sampler2d_array)&&
237 (p._type!=SAT_sampler_cube)&&
238 (p._type!=SAT_sampler_buffer)&&
239 (p._type!=SAT_sampler_cube_array)) {
240 cp_report_error(p, "parameter should have a 'sampler' type");
241 return false;
242 }
243 return true;
244 }
245
246 /**
247 * Make sure the next thing on the word list is EOL
248 */
249 bool Shader::
250 cp_parse_eol(ShaderArgInfo &p, vector_string &words, int &next) {
251 if (words[next] != "") {
252 cp_report_error(p, "Too many words in parameter");
253 return false;
254 }
255 return true;
256 }
257
258 /**
259 * Pop a delimiter ('to' or 'rel') from the word list.
260 */
261 bool Shader::
262 cp_parse_delimiter(ShaderArgInfo &p, vector_string &words, int &next) {
263 if ((words[next] != "to")&&(words[next] != "rel")) {
264 cp_report_error(p, "Keyword 'to' or 'rel' expected");
265 return false;
266 }
267 next += 1;
268 return true;
269 }
270
271 /**
272 * Pop a non-delimiter word from the word list. Delimiters are 'to' and
273 * 'rel.'
274 */
275 string Shader::
276 cp_parse_non_delimiter(vector_string &words, int &next) {
277 const string &nword = words[next];
278 if ((nword == "")||(nword == "to")||(nword == "rel")) {
279 return "";
280 }
281 next += 1;
282 return nword;
283 }
284
285 /**
286 * Convert a single-word coordinate system name into a PART/ARG of a
287 * ShaderMatSpec.
288 */
289 bool Shader::
290 cp_parse_coord_sys(ShaderArgInfo &p,
291 vector_string &pieces, int &next,
292 ShaderMatSpec &bind, bool fromflag) {
293
294 string word1 = cp_parse_non_delimiter(pieces, next);
295 if (pieces[next] == "of") next++;
296 string word2 = cp_parse_non_delimiter(pieces, next);
297
298 ShaderMatInput from_single;
299 ShaderMatInput from_double;
300 ShaderMatInput to_single;
301 ShaderMatInput to_double;
302
303 if (word1 == "") {
304 cp_report_error(p, "Could not parse coordinate system name");
305 return false;
306 } else if (word1 == "world") {
307 from_single = SMO_world_to_view;
308 from_double = SMO_INVALID;
309 to_single = SMO_view_to_world;
310 to_double = SMO_INVALID;
311 } else if (word1 == "model") {
312 from_single = SMO_model_to_view;
313 from_double = SMO_view_x_to_view;
314 to_single = SMO_view_to_model;
315 to_double = SMO_view_to_view_x;
316 } else if (word1 == "clip") {
317 from_single = SMO_clip_to_view;
318 from_double = SMO_clip_x_to_view;
319 to_single = SMO_view_to_clip;
320 to_double = SMO_view_to_clip_x;
321 } else if (word1 == "view") {
322 from_single = SMO_identity;
323 from_double = SMO_view_x_to_view;
324 to_single = SMO_identity;
325 to_double = SMO_view_to_view_x;
326 } else if (word1 == "apiview") {
327 from_single = SMO_apiview_to_view;
328 from_double = SMO_apiview_x_to_view;
329 to_single = SMO_view_to_apiview;
330 to_double = SMO_view_to_apiview_x;
331 } else if (word1 == "apiclip") {
332 from_single = SMO_apiclip_to_view;
333 from_double = SMO_apiclip_x_to_view;
334 to_single = SMO_view_to_apiclip;
335 to_double = SMO_view_to_apiclip_x;
336 } else {
337 from_single = SMO_view_x_to_view;
338 from_double = SMO_view_x_to_view;
339 to_single = SMO_view_to_view_x;
340 to_double = SMO_view_to_view_x;
341 word2 = word1;
342 }
343
344 if (fromflag) {
345 if (word2 == "") {
346 bind._part[0] = from_single;
347 bind._arg[0] = nullptr;
348 } else {
349 if (from_double == SMO_INVALID) {
350 cp_report_error(p, "Could not parse coordinate system name");
351 return false;
352 }
353 bind._part[0] = from_double;
354 bind._arg[0] = InternalName::make(word2);
355 }
356 } else {
357 if (word2 == "") {
358 bind._part[1] = to_single;
359 bind._arg[1] = nullptr;
360 } else {
361 if (to_double == SMO_INVALID) {
362 cp_report_error(p, "Could not parse coordinate system name");
363 return false;
364 }
365 bind._part[1] = to_double;
366 bind._arg[1] = InternalName::make(word2);
367 }
368 }
369 return true;
370 }
371
372 /**
373 * Given ShaderMatInput, returns an indication of what part or parts of the
374 * state_and_transform the ShaderMatInput depends upon.
375 */
376 int Shader::
377 cp_dependency(ShaderMatInput inp) {
378
379 int dep = SSD_general;
380
381 if (inp == SMO_INVALID) {
382 return SSD_NONE;
383 }
384 if (inp == SMO_attr_material || inp == SMO_attr_material2) {
385 dep |= SSD_material | SSD_frame;
386 }
387 if (inp == SMO_attr_color) {
388 dep |= SSD_color;
389 }
390 if (inp == SMO_attr_colorscale) {
391 dep |= SSD_colorscale;
392 }
393 if (inp == SMO_attr_fog || inp == SMO_attr_fogcolor) {
394 dep |= SSD_fog;
395 }
396 if ((inp == SMO_model_to_view) ||
397 (inp == SMO_view_to_model) ||
398 (inp == SMO_model_to_apiview) ||
399 (inp == SMO_apiview_to_model)) {
400 dep |= SSD_transform;
401 }
402 if ((inp == SMO_view_to_world) ||
403 (inp == SMO_world_to_view) ||
404 (inp == SMO_view_x_to_view) ||
405 (inp == SMO_view_to_view_x) ||
406 (inp == SMO_apiview_x_to_view) ||
407 (inp == SMO_view_to_apiview_x) ||
408 (inp == SMO_clip_x_to_view) ||
409 (inp == SMO_view_to_clip_x) ||
410 (inp == SMO_apiclip_x_to_view) ||
411 (inp == SMO_view_to_apiclip_x) ||
412 (inp == SMO_dlight_x) ||
413 (inp == SMO_plight_x) ||
414 (inp == SMO_slight_x)) {
415 dep |= SSD_view_transform;
416 }
417 if ((inp == SMO_texpad_x) ||
418 (inp == SMO_texpix_x) ||
419 (inp == SMO_alight_x) ||
420 (inp == SMO_dlight_x) ||
421 (inp == SMO_plight_x) ||
422 (inp == SMO_slight_x) ||
423 (inp == SMO_satten_x) ||
424 (inp == SMO_mat_constant_x) ||
425 (inp == SMO_vec_constant_x) ||
426 (inp == SMO_vec_constant_x_attrib) ||
427 (inp == SMO_view_x_to_view) ||
428 (inp == SMO_view_to_view_x) ||
429 (inp == SMO_apiview_x_to_view) ||
430 (inp == SMO_view_to_apiview_x) ||
431 (inp == SMO_clip_x_to_view) ||
432 (inp == SMO_view_to_clip_x) ||
433 (inp == SMO_apiclip_x_to_view) ||
434 (inp == SMO_view_to_apiclip_x)) {
435 dep |= SSD_shaderinputs;
436
437 if ((inp == SMO_texpad_x) ||
438 (inp == SMO_texpix_x) ||
439 (inp == SMO_alight_x) ||
440 (inp == SMO_dlight_x) ||
441 (inp == SMO_plight_x) ||
442 (inp == SMO_slight_x) ||
443 (inp == SMO_satten_x) ||
444 (inp == SMO_vec_constant_x_attrib) ||
445 (inp == SMO_view_x_to_view) ||
446 (inp == SMO_view_to_view_x) ||
447 (inp == SMO_apiview_x_to_view) ||
448 (inp == SMO_view_to_apiview_x) ||
449 (inp == SMO_clip_x_to_view) ||
450 (inp == SMO_view_to_clip_x) ||
451 (inp == SMO_apiclip_x_to_view) ||
452 (inp == SMO_view_to_apiclip_x)) {
453 // We can't track changes to these yet, so we have to assume that they
454 // are modified every frame.
455 dep |= SSD_frame;
456 }
457 }
458 if ((inp == SMO_light_ambient) ||
459 (inp == SMO_light_source_i_attrib) ||
460 (inp == SMO_light_source_i_packed)) {
461 dep |= SSD_light | SSD_frame;
462 if (inp == SMO_light_source_i_attrib ||
463 inp == SMO_light_source_i_packed) {
464 dep |= SSD_view_transform;
465 }
466 }
467 if ((inp == SMO_light_product_i_ambient) ||
468 (inp == SMO_light_product_i_diffuse) ||
469 (inp == SMO_light_product_i_specular)) {
470 dep |= (SSD_light | SSD_material);
471 }
472 if ((inp == SMO_clipplane_x) ||
473 (inp == SMO_apiview_clipplane_i)) {
474 dep |= SSD_clip_planes;
475 }
476 if (inp == SMO_texmat_i || inp == SMO_inv_texmat_i || inp == SMO_texscale_i) {
477 dep |= SSD_tex_matrix;
478 }
479 if ((inp == SMO_window_size) ||
480 (inp == SMO_pixel_size) ||
481 (inp == SMO_frame_number) ||
482 (inp == SMO_frame_time) ||
483 (inp == SMO_frame_delta)) {
484 dep |= SSD_frame;
485 }
486 if ((inp == SMO_clip_to_view) ||
487 (inp == SMO_view_to_clip) ||
488 (inp == SMO_apiclip_to_view) ||
489 (inp == SMO_view_to_apiclip) ||
490 (inp == SMO_apiview_to_apiclip) ||
491 (inp == SMO_apiclip_to_apiview)) {
492 dep |= SSD_projection;
493 }
494 if (inp == SMO_tex_is_alpha_i || inp == SMO_texcolor_i) {
495 dep |= SSD_texture | SSD_frame;
496 }
497
498 return dep;
499 }
500
501 /**
502 * Analyzes a ShaderMatSpec and decides what it should use its cache for. It
503 * can cache the results of any one opcode, or, it can cache the entire
504 * result. This routine needs to be smart enough to know which data items can
505 * be correctly cached, and which cannot.
506 */
507 void Shader::
508 cp_optimize_mat_spec(ShaderMatSpec &spec) {
509
510 // If we're composing with identity, simplify.
511
512 if (spec._func == SMF_first) {
513 spec._part[1] = SMO_INVALID;
514 spec._arg[1] = nullptr;
515 }
516 if (spec._func == SMF_compose) {
517 if (spec._part[1] == SMO_identity) {
518 spec._func = SMF_first;
519 }
520 }
521 if (spec._func == SMF_compose) {
522 if (spec._part[0] == SMO_identity) {
523 spec._func = SMF_first;
524 spec._part[0] = spec._part[1];
525 spec._arg[0] = spec._arg[1];
526 }
527
528 // More optimal combinations for common matrices.
529
530 if (spec._part[0] == SMO_model_to_view &&
531 spec._part[1] == SMO_view_to_apiclip) {
532 spec._part[0] = SMO_model_to_apiview;
533 spec._part[1] = SMO_apiview_to_apiclip;
534
535 } else if (spec._part[0] == SMO_apiclip_to_view &&
536 spec._part[1] == SMO_view_to_model) {
537 spec._part[0] = SMO_apiclip_to_apiview;
538 spec._part[1] = SMO_apiview_to_model;
539
540 } else if (spec._part[0] == SMO_apiview_to_view &&
541 spec._part[1] == SMO_view_to_apiclip) {
542 spec._func = SMF_first;
543 spec._part[0] = SMO_apiview_to_apiclip;
544 spec._part[1] = SMO_identity;
545
546 } else if (spec._part[0] == SMO_apiclip_to_view &&
547 spec._part[1] == SMO_view_to_apiview) {
548 spec._func = SMF_first;
549 spec._part[0] = SMO_apiclip_to_apiview;
550 spec._part[1] = SMO_identity;
551
552 } else if (spec._part[0] == SMO_apiview_to_view &&
553 spec._part[1] == SMO_view_to_model) {
554 spec._func = SMF_first;
555 spec._part[0] = SMO_apiview_to_model;
556 spec._part[1] = SMO_identity;
557
558 } else if (spec._part[0] == SMO_model_to_view &&
559 spec._part[1] == SMO_view_to_apiview) {
560 spec._func = SMF_first;
561 spec._part[0] = SMO_model_to_apiview;
562 spec._part[1] = SMO_identity;
563 }
564 }
565
566 // Calculate state and transform dependencies.
567
568 spec._dep[0] = cp_dependency(spec._part[0]);
569 spec._dep[1] = cp_dependency(spec._part[1]);
570 }
571
572 #ifdef HAVE_CG
573 /**
574 *
575 */
576 void Shader::
577 cg_recurse_parameters(CGparameter parameter, const ShaderType &type,
578 bool &success) {
579
580 if (parameter == 0) {
581 return;
582 }
583
584 do {
585 if (cgIsParameterReferenced(parameter)) {
586 int arg_dim[] = {1,0,0};
587 ShaderArgDir arg_dir = cg_parameter_dir(parameter);
588 ShaderArgType arg_type = cg_parameter_type(parameter);
589 ShaderArgClass arg_class = cg_parameter_class(parameter);
590 ShaderArgClass arg_subclass = arg_class;
591
592 CGenum vbl = cgGetParameterVariability(parameter);
593 CGtype base_type = cgGetParameterBaseType(parameter);
594
595 if ((vbl==CG_VARYING)||(vbl==CG_UNIFORM)) {
596 switch (cgGetParameterType(parameter)) {
597 case CG_STRUCT:
598 cg_recurse_parameters(
599 cgGetFirstStructParameter(parameter), type, success);
600 break;
601
602 case CG_ARRAY:
603 arg_type = cg_parameter_type(cgGetArrayParameter(parameter, 0));
604 arg_subclass = cg_parameter_class(cgGetArrayParameter(parameter, 0));
605
606 arg_dim[0] = cgGetArraySize(parameter, 0);
607
608 // Fall through
609 default: {
610 arg_dim[1] = cgGetParameterRows(parameter);
611 arg_dim[2] = cgGetParameterColumns(parameter);
612
613 ShaderArgInfo p;
614 p._id._name = cgGetParameterName(parameter);
615 p._id._type = type;
616 p._id._seqno = -1;
617 p._class = arg_class;
618 p._subclass = arg_subclass;
619 p._type = arg_type;
620 p._direction = arg_dir;
621 p._varying = (vbl == CG_VARYING);
622 p._cat = shader_cat.get_safe_ptr();
623
624 //NB. Cg does have a CG_DOUBLE type, but at least for the ARB
625 // profiles and GLSL profiles it just maps to float.
626 switch (base_type) {
627 case CG_UINT:
628 case CG_ULONG:
629 case CG_USHORT:
630 case CG_UCHAR:
631 case CG_BOOL:
632 p._numeric_type = SPT_uint;
633 break;
634 case CG_INT:
635 case CG_LONG:
636 case CG_SHORT:
637 case CG_CHAR:
638 p._numeric_type = SPT_int;
639 break;
640 default:
641 p._numeric_type = SPT_float;
642 break;
643 }
644
645 success &= compile_parameter(p, arg_dim);
646 break;
647 }
648 }
649 }
650 } else if (shader_cat.is_debug()) {
651 shader_cat.debug()
652 << "Parameter " << cgGetParameterName(parameter)
653 << " is unreferenced within shader " << get_filename(type) << "\n";
654 }
655 } while((parameter = cgGetNextParameter(parameter))!= 0);
656 }
657 #endif // HAVE_CG
658
659 /**
660 * Analyzes a parameter and decides how to bind the parameter to some part of
661 * panda's internal state. Updates one of the bind arrays to cause the
662 * binding to occur.
663 *
664 * If there is an error, this routine will append an error message onto the
665 * error messages.
666 */
667 bool Shader::
668 compile_parameter(ShaderArgInfo &p, int *arg_dim) {
669 if (p._id._name.size() == 0) return true;
670 if (p._id._name[0] == '$') return true;
671
672 // It could be inside a struct, strip off everything before the last dot.
673 size_t loc = p._id._name.find_last_of('.');
674
675 string basename (p._id._name);
676 string struct_name ("");
677
678 if (loc < string::npos) {
679 basename = p._id._name.substr(loc + 1);
680 struct_name = p._id._name.substr(0,loc+1);
681 }
682
683 // Split it at the underscores.
684 vector_string pieces;
685 tokenize(basename, pieces, "_");
686
687 if (basename.size() >= 2 && basename.substr(0, 2) == "__") {
688 return true;
689 }
690
691 // Implement vtx parameters - the varying kind.
692 if (pieces[0] == "vtx") {
693 if ((!cp_errchk_parameter_in(p)) ||
694 (!cp_errchk_parameter_varying(p)) ||
695 (!cp_errchk_parameter_float(p, 1, 4))) {
696 return false;
697 }
698 ShaderVarSpec bind;
699 bind._id = p._id;
700 bind._append_uv = -1;
701 bind._numeric_type = p._numeric_type;
702
703 if (pieces.size() == 2) {
704 if (pieces[1] == "position") {
705 bind._name = InternalName::get_vertex();
706 bind._append_uv = -1;
707 _var_spec.push_back(bind);
708 return true;
709 }
710 if (pieces[1].substr(0, 8) == "texcoord") {
711 bind._name = InternalName::get_texcoord();
712 if (pieces[1].size() > 8) {
713 bind._append_uv = atoi(pieces[1].c_str() + 8);
714 }
715 _var_spec.push_back(bind);
716 return true;
717 }
718 if (pieces[1].substr(0, 7) == "tangent") {
719 bind._name = InternalName::get_tangent();
720 if (pieces[1].size() > 7) {
721 bind._append_uv = atoi(pieces[1].c_str() + 7);
722 }
723 _var_spec.push_back(bind);
724 return true;
725 }
726 if (pieces[1].substr(0, 8) == "binormal") {
727 bind._name = InternalName::get_binormal();
728 if (pieces[1].size() > 8) {
729 bind._append_uv = atoi(pieces[1].c_str() + 8);
730 }
731 _var_spec.push_back(bind);
732 return true;
733 }
734 } else if (pieces.size() == 3) {
735 if (pieces[1] == "transform") {
736 if (pieces[2] == "blend") {
737 bind._name = InternalName::get_transform_blend();
738 _var_spec.push_back(bind);
739 return true;
740 }
741 if (pieces[2] == "index") {
742 bind._name = InternalName::get_transform_index();
743 _var_spec.push_back(bind);
744 return true;
745 }
746 if (pieces[2] == "weight") {
747 bind._name = InternalName::get_transform_weight();
748 _var_spec.push_back(bind);
749 return true;
750 }
751 }
752 }
753
754 bind._name = InternalName::get_root();
755 for (size_t i = 1; i < pieces.size(); ++i) {
756 bind._name = bind._name->append(pieces[i]);
757 }
758 _var_spec.push_back(bind);
759 return true;
760 }
761
762 if (pieces[0] == "mat" && pieces[1] == "shadow") {
763 if ((!cp_errchk_parameter_words(p,3))||
764 (!cp_errchk_parameter_in(p)) ||
765 (!cp_errchk_parameter_uniform(p))||
766 (!cp_errchk_parameter_float(p,16,16))) {
767 return false;
768 }
769 ShaderMatSpec bind;
770 bind._id = p._id;
771 bind._piece = SMP_whole;
772 bind._func = SMF_compose;
773 bind._part[1] = SMO_light_source_i_attrib;
774 bind._arg[1] = InternalName::make("shadowViewMatrix");
775 bind._part[0] = SMO_view_to_apiview;
776 bind._arg[0] = nullptr;
777 bind._index = atoi(pieces[2].c_str());
778
779 cp_optimize_mat_spec(bind);
780 _mat_spec.push_back(bind);
781 _mat_deps |= bind._dep[0] | bind._dep[1];
782 return true;
783 }
784
785 // Implement some macros. Macros work by altering the contents of the
786 // 'pieces' array, and then falling through.
787
788 if (pieces[0] == "mstrans") {
789 pieces[0] = "trans";
790 pieces.push_back("to");
791 pieces.push_back("model");
792 }
793 if (pieces[0] == "wstrans") {
794 pieces[0] = "trans";
795 pieces.push_back("to");
796 pieces.push_back("world");
797 }
798 if (pieces[0] == "vstrans") {
799 pieces[0] = "trans";
800 pieces.push_back("to");
801 pieces.push_back("view");
802 }
803 if (pieces[0] == "cstrans") {
804 pieces[0] = "trans";
805 pieces.push_back("to");
806 pieces.push_back("clip");
807 }
808 if (pieces[0] == "mspos") {
809 pieces[0] = "row3";
810 pieces.push_back("to");
811 pieces.push_back("model");
812 }
813 if (pieces[0] == "wspos") {
814 pieces[0] = "row3";
815 pieces.push_back("to");
816 pieces.push_back("world");
817 }
818 if (pieces[0] == "vspos") {
819 pieces[0] = "row3";
820 pieces.push_back("to");
821 pieces.push_back("view");
822 }
823 if (pieces[0] == "cspos") {
824 pieces[0] = "row3";
825 pieces.push_back("to");
826 pieces.push_back("clip");
827 }
828
829 // Implement the modelview macros.
830
831 if ((pieces[0] == "mat")||(pieces[0] == "inv")||
832 (pieces[0] == "tps")||(pieces[0] == "itp")) {
833 if (!cp_errchk_parameter_words(p, 2)) {
834 return false;
835 }
836 string trans = pieces[0];
837 string matrix = pieces[1];
838 pieces.clear();
839 if (matrix == "modelview") {
840 tokenize("trans_model_to_apiview", pieces, "_");
841 } else if (matrix == "projection") {
842 tokenize("trans_apiview_to_apiclip", pieces, "_");
843 } else if (matrix == "modelproj") {
844 tokenize("trans_model_to_apiclip", pieces, "_");
845 } else {
846 cp_report_error(p,"unrecognized matrix name");
847 return false;
848 }
849 if (trans=="mat") {
850 pieces[0] = "trans";
851 } else if (trans=="inv") {
852 string t = pieces[1];
853 pieces[1] = pieces[3];
854 pieces[3] = t;
855 } else if (trans=="tps") {
856 pieces[0] = "tpose";
857 } else if (trans=="itp") {
858 string t = pieces[1];
859 pieces[1] = pieces[3];
860 pieces[3] = t;
861 pieces[0] = "tpose";
862 }
863 }
864
865 // Implement the transform-matrix generator.
866
867 if ((pieces[0]=="trans")||
868 (pieces[0]=="tpose")||
869 (pieces[0]=="row0")||
870 (pieces[0]=="row1")||
871 (pieces[0]=="row2")||
872 (pieces[0]=="row3")||
873 (pieces[0]=="col0")||
874 (pieces[0]=="col1")||
875 (pieces[0]=="col2")||
876 (pieces[0]=="col3")) {
877
878 if ((!cp_errchk_parameter_in(p)) ||
879 (!cp_errchk_parameter_uniform(p)))
880 return false;
881
882 ShaderMatSpec bind;
883 bind._id = p._id;
884 bind._piece = SMP_whole;
885 bind._func = SMF_compose;
886 bind._part[1] = SMO_light_source_i_attrib;
887 bind._arg[1] = InternalName::make("shadowViewMatrix");
888 bind._part[0] = SMO_view_to_apiview;
889 bind._arg[0] = nullptr;
890 bind._index = atoi(pieces[2].c_str());
891
892 int next = 1;
893 pieces.push_back("");
894
895 // Decide whether this is a matrix or vector.
896 if (pieces[0]=="trans") bind._piece = SMP_whole;
897 else if (pieces[0]=="tpose") bind._piece = SMP_transpose;
898 else if (pieces[0]=="row0") bind._piece = SMP_row0;
899 else if (pieces[0]=="row1") bind._piece = SMP_row1;
900 else if (pieces[0]=="row2") bind._piece = SMP_row2;
901 else if (pieces[0]=="row3") bind._piece = SMP_row3;
902 else if (pieces[0]=="col0") bind._piece = SMP_col0;
903 else if (pieces[0]=="col1") bind._piece = SMP_col1;
904 else if (pieces[0]=="col2") bind._piece = SMP_col2;
905 else if (pieces[0]=="col3") bind._piece = SMP_col3;
906 if ((bind._piece == SMP_whole)||(bind._piece == SMP_transpose)) {
907 if (p._type == SAT_mat3x3) {
908 if (!cp_errchk_parameter_float(p, 9, 9)) return false;
909
910 if (bind._piece == SMP_transpose) {
911 bind._piece = SMP_transpose3x3;
912 } else {
913 bind._piece = SMP_upper3x3;
914 }
915 } else if (!cp_errchk_parameter_float(p, 16, 16)) {
916 return false;
917 }
918 } else {
919 if (!cp_errchk_parameter_float(p, 4, 4)) return false;
920 }
921
922 if (!cp_parse_coord_sys(p, pieces, next, bind, true)) {
923 return false;
924 }
925 if (!cp_parse_delimiter(p, pieces, next)) {
926 return false;
927 }
928 if (!cp_parse_coord_sys(p, pieces, next, bind, false)) {
929 return false;
930 }
931 if (!cp_parse_eol(p, pieces, next)) {
932 return false;
933 }
934 cp_optimize_mat_spec(bind);
935 _mat_spec.push_back(bind);
936 _mat_deps |= bind._dep[0] | bind._dep[1];
937 return true;
938 }
939
940 // Special parameter: attr_material or attr_color
941
942 if (pieces[0] == "attr") {
943 if ((!cp_errchk_parameter_words(p,2)) ||
944 (!cp_errchk_parameter_in(p)) ||
945 (!cp_errchk_parameter_uniform(p))) {
946 return false;
947 }
948 ShaderMatSpec bind;
949 if (pieces[1] == "material") {
950 if (!cp_errchk_parameter_float(p,16,16)) {
951 return false;
952 }
953 bind._id = p._id;
954 bind._piece = SMP_transpose;
955 bind._func = SMF_first;
956 bind._part[0] = SMO_attr_material;
957 bind._arg[0] = nullptr;
958 bind._part[1] = SMO_identity;
959 bind._arg[1] = nullptr;
960 } else if (pieces[1] == "color") {
961 if (!cp_errchk_parameter_float(p,3,4)) {
962 return false;
963 }
964 bind._id = p._id;
965 bind._piece = SMP_row3;
966 bind._func = SMF_first;
967 bind._part[0] = SMO_attr_color;
968 bind._arg[0] = nullptr;
969 bind._part[1] = SMO_identity;
970 bind._arg[1] = nullptr;
971 } else if (pieces[1] == "colorscale") {
972 if (!cp_errchk_parameter_float(p,3,4)) {
973 return false;
974 }
975 bind._id = p._id;
976 bind._piece = SMP_row3;
977 bind._func = SMF_first;
978 bind._part[0] = SMO_attr_colorscale;
979 bind._arg[0] = nullptr;
980 bind._part[1] = SMO_identity;
981 bind._arg[1] = nullptr;
982 } else if (pieces[1] == "fog") {
983 if (!cp_errchk_parameter_float(p,3,4)) {
984 return false;
985 }
986 bind._id = p._id;
987 bind._piece = SMP_row3;
988 bind._func = SMF_first;
989 bind._part[0] = SMO_attr_fog;
990 bind._arg[0] = nullptr;
991 bind._part[1] = SMO_identity;
992 bind._arg[1] = nullptr;
993 } else if (pieces[1] == "fogcolor") {
994 if (!cp_errchk_parameter_float(p,3,4)) {
995 return false;
996 }
997 bind._id = p._id;
998 bind._piece = SMP_row3;
999 bind._func = SMF_first;
1000 bind._part[0] = SMO_attr_fogcolor;
1001 bind._arg[0] = nullptr;
1002 bind._part[1] = SMO_identity;
1003 bind._arg[1] = nullptr;
1004 } else if (pieces[1] == "ambient") {
1005 if (!cp_errchk_parameter_float(p,3,4)) {
1006 return false;
1007 }
1008 bind._id = p._id;
1009 bind._piece = SMP_row3;
1010 bind._func = SMF_first;
1011 bind._part[0] = SMO_light_ambient;
1012 bind._arg[0] = nullptr;
1013 bind._part[1] = SMO_identity;
1014 bind._arg[1] = nullptr;
1015 } else if (pieces[1].compare(0, 5, "light") == 0) {
1016 if (!cp_errchk_parameter_float(p,16,16)) {
1017 return false;
1018 }
1019 bind._id = p._id;
1020 bind._piece = SMP_transpose;
1021 bind._func = SMF_first;
1022 bind._part[0] = SMO_light_source_i_packed;
1023 bind._arg[0] = nullptr;
1024 bind._part[1] = SMO_identity;
1025 bind._arg[1] = nullptr;
1026 bind._index = atoi(pieces[1].c_str() + 5);
1027 } else if (pieces[1].compare(0, 5, "lspec") == 0) {
1028 if (!cp_errchk_parameter_float(p,3,4)) {
1029 return false;
1030 }
1031 bind._id = p._id;
1032 bind._piece = SMP_row3;
1033 bind._func = SMF_first;
1034 bind._part[0] = SMO_light_source_i_attrib;
1035 bind._arg[0] = InternalName::make("specular");
1036 bind._part[1] = SMO_identity;
1037 bind._arg[1] = nullptr;
1038 bind._index = atoi(pieces[1].c_str() + 5);
1039 } else {
1040 cp_report_error(p,"Unknown attr parameter.");
1041 return false;
1042 }
1043
1044 cp_optimize_mat_spec(bind);
1045 _mat_spec.push_back(bind);
1046 _mat_deps |= bind._dep[0] | bind._dep[1];
1047 return true;
1048 }
1049
1050 if (pieces[0] == "color") {
1051 if ((!cp_errchk_parameter_words(p,1)) ||
1052 (!cp_errchk_parameter_in(p)) ||
1053 (!cp_errchk_parameter_uniform(p))) {
1054 return false;
1055 }
1056 ShaderMatSpec bind;
1057
1058 cp_optimize_mat_spec(bind);
1059 _mat_spec.push_back(bind);
1060 _mat_deps |= bind._dep[0] | bind._dep[1];
1061 return true;
1062 }
1063
1064 // Keywords to access light properties.
1065
1066 if (pieces[0] == "alight") {
1067 if ((!cp_errchk_parameter_words(p,2))||
1068 (!cp_errchk_parameter_in(p)) ||
1069 (!cp_errchk_parameter_uniform(p))||
1070 (!cp_errchk_parameter_float(p,3,4))) {
1071 return false;
1072 }
1073 ShaderMatSpec bind;
1074 bind._id = p._id;
1075 bind._piece = SMP_row3;
1076 bind._func = SMF_first;
1077 bind._part[0] = SMO_alight_x;
1078 bind._arg[0] = InternalName::make(pieces[1]);
1079 bind._part[1] = SMO_identity;
1080 bind._arg[1] = nullptr;
1081
1082 cp_optimize_mat_spec(bind);
1083 _mat_spec.push_back(bind);
1084 _mat_deps |= bind._dep[0] | bind._dep[1];
1085 return true;
1086 }
1087
1088 if (pieces[0] == "satten") {
1089 if ((!cp_errchk_parameter_words(p,2))||
1090 (!cp_errchk_parameter_in(p)) ||
1091 (!cp_errchk_parameter_uniform(p))||
1092 (!cp_errchk_parameter_float(p,4,4))) {
1093 return false;
1094 }
1095 ShaderMatSpec bind;
1096 bind._id = p._id;
1097 bind._piece = SMP_row3;
1098 bind._func = SMF_first;
1099 bind._part[0] = SMO_satten_x;
1100 bind._arg[0] = InternalName::make(pieces[1]);
1101 bind._part[1] = SMO_identity;
1102 bind._arg[1] = nullptr;
1103
1104 cp_optimize_mat_spec(bind);
1105 _mat_spec.push_back(bind);
1106 _mat_deps |= bind._dep[0] | bind._dep[1];
1107 return true;
1108 }
1109
1110 if ((pieces[0]=="dlight")||(pieces[0]=="plight")||(pieces[0]=="slight")) {
1111 if ((!cp_errchk_parameter_in(p)) ||
1112 (!cp_errchk_parameter_uniform(p))||
1113 (!cp_errchk_parameter_float(p,16,16))) {
1114 return false;
1115 }
1116 ShaderMatSpec bind;
1117 bind._id = p._id;
1118 bind._piece = SMP_transpose;
1119 int next = 1;
1120 pieces.push_back("");
1121 if (pieces[next] == "") {
1122 cp_report_error(p, "Light name expected");
1123 return false;
1124 }
1125 if (pieces[0] == "dlight") {
1126 bind._func = SMF_transform_dlight;
1127 bind._part[0] = SMO_dlight_x;
1128 } else if (pieces[0] == "plight") {
1129 bind._func = SMF_transform_plight;
1130 bind._part[0] = SMO_plight_x;
1131 } else if (pieces[0] == "slight") {
1132 bind._func = SMF_transform_slight;
1133 bind._part[0] = SMO_slight_x;
1134 }
1135 bind._arg[0] = InternalName::make(pieces[next]);
1136 next += 1;
1137 if (!cp_parse_delimiter(p, pieces, next)) {
1138 return false;
1139 }
1140 if (!cp_parse_coord_sys(p, pieces, next, bind, false)) {
1141 return false;
1142 }
1143 if (!cp_parse_eol(p, pieces, next)) {
1144 return false;
1145 }
1146 cp_optimize_mat_spec(bind);
1147 _mat_spec.push_back(bind);
1148 _mat_deps |= bind._dep[0] | bind._dep[1];
1149 return true;
1150 }
1151
1152 if (pieces[0] == "texmat") {
1153 if ((!cp_errchk_parameter_words(p,2))||
1154 (!cp_errchk_parameter_in(p)) ||
1155 (!cp_errchk_parameter_uniform(p))||
1156 (!cp_errchk_parameter_float(p,16,16))) {
1157 return false;
1158 }
1159 ShaderMatSpec bind;
1160 bind._id = p._id;
1161 bind._piece = SMP_whole;
1162 bind._func = SMF_first;
1163 bind._part[0] = SMO_texmat_i;
1164 bind._arg[0] = nullptr;
1165 bind._part[1] = SMO_identity;
1166 bind._arg[1] = nullptr;
1167 bind._index = atoi(pieces[1].c_str());
1168
1169 cp_optimize_mat_spec(bind);
1170 _mat_spec.push_back(bind);
1171 _mat_deps |= bind._dep[0] | bind._dep[1];
1172 return true;
1173 }
1174
1175 if (pieces[0] == "texscale") {
1176 if ((!cp_errchk_parameter_words(p,2))||
1177 (!cp_errchk_parameter_in(p)) ||
1178 (!cp_errchk_parameter_uniform(p))||
1179 (!cp_errchk_parameter_float(p,3,4))) {
1180 return false;
1181 }
1182 ShaderMatSpec bind;
1183 bind._id = p._id;
1184 bind._piece = SMP_row3;
1185 bind._func = SMF_first;
1186 bind._part[0] = SMO_texscale_i;
1187 bind._arg[0] = nullptr;
1188 bind._part[1] = SMO_identity;
1189 bind._arg[1] = nullptr;
1190 bind._index = atoi(pieces[1].c_str());
1191
1192 cp_optimize_mat_spec(bind);
1193 _mat_spec.push_back(bind);
1194 _mat_deps |= bind._dep[0] | bind._dep[1];
1195 return true;
1196 }
1197
1198 if (pieces[0] == "texcolor") {
1199 if ((!cp_errchk_parameter_words(p,2))||
1200 (!cp_errchk_parameter_in(p)) ||
1201 (!cp_errchk_parameter_uniform(p))||
1202 (!cp_errchk_parameter_float(p,3,4))) {
1203 return false;
1204 }
1205 ShaderMatSpec bind;
1206 bind._id = p._id;
1207 bind._piece = SMP_row3;
1208 bind._func = SMF_first;
1209 bind._part[0] = SMO_texcolor_i;
1210 bind._arg[0] = nullptr;
1211 bind._part[1] = SMO_identity;
1212 bind._arg[1] = nullptr;
1213 bind._index = atoi(pieces[1].c_str());
1214
1215 cp_optimize_mat_spec(bind);
1216 _mat_spec.push_back(bind);
1217 _mat_deps |= bind._dep[0] | bind._dep[1];
1218 return true;
1219 }
1220
1221 if (pieces[0] == "plane") {
1222 if ((!cp_errchk_parameter_words(p,2))||
1223 (!cp_errchk_parameter_in(p)) ||
1224 (!cp_errchk_parameter_uniform(p))||
1225 (!cp_errchk_parameter_float(p,4,4))) {
1226 return false;
1227 }
1228 ShaderMatSpec bind;
1229 bind._id = p._id;
1230 bind._piece = SMP_row3;
1231 bind._func = SMF_first;
1232 bind._part[0] = SMO_plane_x;
1233 bind._arg[0] = InternalName::make(pieces[1]);
1234 bind._part[1] = SMO_identity;
1235 bind._arg[1] = nullptr;
1236
1237 cp_optimize_mat_spec(bind);
1238 _mat_spec.push_back(bind);
1239 _mat_deps |= bind._dep[0] | bind._dep[1];
1240 return true;
1241 }
1242
1243 if (pieces[0] == "clipplane") {
1244 if ((!cp_errchk_parameter_words(p,2))||
1245 (!cp_errchk_parameter_in(p)) ||
1246 (!cp_errchk_parameter_uniform(p))||
1247 (!cp_errchk_parameter_float(p,4,4))) {
1248 return false;
1249 }
1250 ShaderMatSpec bind;
1251 bind._id = p._id;
1252 bind._piece = SMP_row3;
1253 bind._func = SMF_first;
1254 bind._part[0] = SMO_clipplane_x;
1255 bind._arg[0] = InternalName::make(pieces[1]);
1256 bind._part[1] = SMO_identity;
1257 bind._arg[1] = nullptr;
1258
1259 cp_optimize_mat_spec(bind);
1260 _mat_spec.push_back(bind);
1261 _mat_deps |= bind._dep[0] | bind._dep[1];
1262 return true;
1263 }
1264
1265 // Keywords to access unusual parameters.
1266
1267 if (pieces[0] == "sys") {
1268 if ((!cp_errchk_parameter_words(p, 2)) ||
1269 (!cp_errchk_parameter_in(p)) ||
1270 (!cp_errchk_parameter_uniform(p))) {
1271 return false;
1272 }
1273 ShaderMatSpec bind;
1274 bind._id = p._id;
1275 bind._piece = SMP_row3;
1276 bind._func = SMF_first;
1277 bind._part[1] = SMO_identity;
1278 bind._arg[1] = nullptr;
1279 if (pieces[1] == "pixelsize") {
1280 if (!cp_errchk_parameter_float(p, 2, 2)) {
1281 return false;
1282 }
1283 bind._part[0] = SMO_pixel_size;
1284 bind._arg[0] = nullptr;
1285
1286 } else if (pieces[1] == "windowsize") {
1287 if (!cp_errchk_parameter_float(p, 2, 2)) {
1288 return false;
1289 }
1290 bind._part[0] = SMO_window_size;
1291 bind._arg[0] = nullptr;
1292
1293 } else if (pieces[1] == "time") {
1294 if (!cp_errchk_parameter_float(p, 1, 1)) {
1295 return false;
1296 }
1297 bind._piece = SMP_row3x1;
1298 bind._part[0] = SMO_frame_time;
1299 bind._arg[0] = nullptr;
1300
1301 } else {
1302 cp_report_error(p, "unknown system parameter");
1303 return false;
1304 }
1305
1306 cp_optimize_mat_spec(bind);
1307 _mat_spec.push_back(bind);
1308 _mat_deps |= bind._dep[0] | bind._dep[1];
1309 return true;
1310 }
1311
1312 // Keywords to access textures.
1313
1314 if (pieces[0] == "tex") {
1315 if ((!cp_errchk_parameter_in(p)) ||
1316 (!cp_errchk_parameter_uniform(p)) ||
1317 (!cp_errchk_parameter_sampler(p)))
1318 return false;
1319 if ((pieces.size() != 2)&&(pieces.size() != 3)) {
1320 cp_report_error(p, "Invalid parameter name");
1321 return false;
1322 }
1323 ShaderTexSpec bind;
1324 bind._id = p._id;
1325 bind._name = nullptr;
1326 bind._stage = atoi(pieces[1].c_str());
1327 bind._part = STO_stage_i;
1328 switch (p._type) {
1329 case SAT_sampler1d: bind._desired_type = Texture::TT_1d_texture; break;
1330 case SAT_sampler2d: bind._desired_type = Texture::TT_2d_texture; break;
1331 case SAT_sampler3d: bind._desired_type = Texture::TT_3d_texture; break;
1332 case SAT_sampler2d_array:bind._desired_type = Texture::TT_2d_texture_array; break;
1333 case SAT_sampler_cube: bind._desired_type = Texture::TT_cube_map; break;
1334 case SAT_sampler_buffer: bind._desired_type = Texture::TT_buffer_texture; break;
1335 case SAT_sampler_cube_array:bind._desired_type = Texture::TT_cube_map_array; break;
1336 default:
1337 cp_report_error(p, "Invalid type for a tex-parameter");
1338 return false;
1339 }
1340 if (pieces.size() == 3) {
1341 bind._suffix = InternalName::make(((string)"-") + pieces[2]);
1342 shader_cat.warning()
1343 << "Parameter " << p._id._name << ": use of a texture suffix is deprecated.\n";
1344 }
1345 _tex_spec.push_back(bind);
1346 return true;
1347 }
1348
1349 if (pieces[0] == "shadow") {
1350 if ((!cp_errchk_parameter_in(p)) ||
1351 (!cp_errchk_parameter_uniform(p)) ||
1352 (!cp_errchk_parameter_sampler(p)))
1353 return false;
1354 if (pieces.size() != 2) {
1355 cp_report_error(p, "Invalid parameter name");
1356 return false;
1357 }
1358 ShaderTexSpec bind;
1359 bind._id = p._id;
1360 bind._name = nullptr;
1361 bind._stage = atoi(pieces[1].c_str());
1362 bind._part = STO_light_i_shadow_map;
1363 switch (p._type) {
1364 case SAT_sampler2d: bind._desired_type = Texture::TT_2d_texture; break;
1365 case SAT_sampler_cube: bind._desired_type = Texture::TT_cube_map; break;
1366 default:
1367 cp_report_error(p, "Invalid type for a shadow-parameter");
1368 return false;
1369 }
1370 _tex_spec.push_back(bind);
1371 return true;
1372 }
1373
1374 // Keywords to fetch texture parameter data.
1375
1376 if (pieces[0] == "texpad") {
1377 if ((!cp_errchk_parameter_words(p,2)) ||
1378 (!cp_errchk_parameter_in(p)) ||
1379 (!cp_errchk_parameter_uniform(p))||
1380 (!cp_errchk_parameter_float(p,3,4))) {
1381 return false;
1382 }
1383 ShaderMatSpec bind;
1384 bind._id = p._id;
1385 bind._piece = SMP_row3;
1386 bind._func = SMF_first;
1387 bind._part[0] = SMO_texpad_x;
1388 bind._arg[0] = InternalName::make(pieces[1]);
1389 bind._part[1] = SMO_identity;
1390 bind._arg[1] = nullptr;
1391 cp_optimize_mat_spec(bind);
1392 _mat_spec.push_back(bind);
1393 _mat_deps |= bind._dep[0] | bind._dep[1];
1394 return true;
1395 }
1396
1397 if (pieces[0] == "texpix") {
1398 if ((!cp_errchk_parameter_words(p,2)) ||
1399 (!cp_errchk_parameter_in(p)) ||
1400 (!cp_errchk_parameter_uniform(p))||
1401 (!cp_errchk_parameter_float(p,2,4))) {
1402 return false;
1403 }
1404 ShaderMatSpec bind;
1405 bind._id = p._id;
1406 bind._piece = SMP_row3;
1407 bind._func = SMF_first;
1408 bind._part[0] = SMO_texpix_x;
1409 bind._arg[0] = InternalName::make(pieces[1]);
1410 bind._part[1] = SMO_identity;
1411 bind._arg[1] = nullptr;
1412 cp_optimize_mat_spec(bind);
1413 _mat_spec.push_back(bind);
1414 _mat_deps |= bind._dep[0] | bind._dep[1];
1415 return true;
1416 }
1417
1418 if (pieces[0] == "tbl") {
1419 // Handled elsewhere.
1420 return true;
1421 }
1422
1423 if (pieces[0] == "l") {
1424 // IMPLEMENT THE ERROR CHECKING
1425 return true; // Cg handles this automatically.
1426 }
1427
1428 if (pieces[0] == "o") {
1429 // IMPLEMENT THE ERROR CHECKING
1430 return true; // Cg handles this automatically.
1431 }
1432
1433 // Fetch uniform parameters without prefix
1434
1435 if ((!cp_errchk_parameter_in(p)) ||
1436 (!cp_errchk_parameter_uniform(p))) {
1437 return false;
1438 }
1439 bool k_prefix = false;
1440
1441 // solve backward compatibility issue
1442 if (pieces[0] == "k") {
1443 k_prefix = true;
1444 basename = basename.substr(2);
1445 }
1446
1447 PT(InternalName) kinputname = InternalName::make(struct_name + basename);
1448
1449 switch (p._class) {
1450 case SAC_vector:
1451 case SAC_matrix:
1452 case SAC_scalar:
1453 case SAC_array: {
1454 if (!cp_errchk_parameter_ptr(p))
1455 return false;
1456
1457 ShaderPtrSpec bind;
1458 bind._id = p._id;
1459 bind._arg = kinputname;
1460 bind._info = p;
1461
1462 // We specify SSD_frame because a PTA may be modified by the app from
1463 // frame to frame, and we have no way to know. So, we must respecify a
1464 // PTA at least once every frame.
1465 bind._dep[0] = SSD_general | SSD_shaderinputs | SSD_frame;
1466 bind._dep[1] = SSD_NONE;
1467
1468 memcpy(bind._dim,arg_dim,sizeof(int)*3);
1469
1470 // if dim[0] = -1, glShaderContext will not check the param size
1471 if (k_prefix) bind._dim[0] = -1;
1472 _ptr_spec.push_back(bind);
1473 return true;
1474 }
1475
1476 case SAC_sampler: {
1477 switch (p._type) {
1478 case SAT_sampler1d: {
1479 ShaderTexSpec bind;
1480 bind._id = p._id;
1481 bind._name = kinputname;
1482 bind._part = STO_named_input;
1483 bind._desired_type = Texture::TT_1d_texture;
1484 _tex_spec.push_back(bind);
1485 return true;
1486 }
1487 case SAT_sampler2d: {
1488 ShaderTexSpec bind;
1489 bind._id = p._id;
1490 bind._name = kinputname;
1491 bind._part = STO_named_input;
1492 bind._desired_type = Texture::TT_2d_texture;
1493 _tex_spec.push_back(bind);
1494 return true;
1495 }
1496 case SAT_sampler3d: {
1497 ShaderTexSpec bind;
1498 bind._id = p._id;
1499 bind._name = kinputname;
1500 bind._part = STO_named_input;
1501 bind._desired_type = Texture::TT_3d_texture;
1502 _tex_spec.push_back(bind);
1503 return true;
1504 }
1505 case SAT_sampler2d_array: {
1506 ShaderTexSpec bind;
1507 bind._id = p._id;
1508 bind._name = kinputname;
1509 bind._part = STO_named_input;
1510 bind._desired_type = Texture::TT_2d_texture_array;
1511 _tex_spec.push_back(bind);
1512 return true;
1513 }
1514 case SAT_sampler_cube: {
1515 ShaderTexSpec bind;
1516 bind._id = p._id;
1517 bind._name = kinputname;
1518 bind._part = STO_named_input;
1519 bind._desired_type = Texture::TT_cube_map;
1520 _tex_spec.push_back(bind);
1521 return true;
1522 }
1523 case SAT_sampler_buffer: {
1524 ShaderTexSpec bind;
1525 bind._id = p._id;
1526 bind._name = kinputname;
1527 bind._part = STO_named_input;
1528 bind._desired_type = Texture::TT_buffer_texture;
1529 _tex_spec.push_back(bind);
1530 return true;
1531 }
1532 case SAT_sampler_cube_array: {
1533 ShaderTexSpec bind;
1534 bind._id = p._id;
1535 bind._name = kinputname;
1536 bind._part = STO_named_input;
1537 bind._desired_type = Texture::TT_cube_map_array;
1538 _tex_spec.push_back(bind);
1539 return true;
1540 }
1541 default:
1542 cp_report_error(p, "invalid type for non-prefix parameter");
1543 return false;
1544 }
1545 }
1546 default:
1547 cp_report_error(p, "invalid class for non-prefix parameter");
1548 return false;
1549 }
1550
1551 cp_report_error(p, "unrecognized parameter name");
1552 return false;
1553 }
1554
1555 /**
1556 *
1557 */
1558 void Shader::
1559 clear_parameters() {
1560 _mat_spec.clear();
1561 _var_spec.clear();
1562 _tex_spec.clear();
1563 }
1564
1565 /**
1566 * Called by the back-end when the shader has compiled data available.
1567 */
1568 void Shader::
1569 set_compiled(unsigned int format, const char *data, size_t length) {
1570 _compiled_format = format;
1571 _compiled_binary.assign(data, length);
1572
1573 // Store the compiled shader in the cache.
1574 if (_cache_compiled_shader && !_record.is_null()) {
1575 _record->set_data(this);
1576
1577 BamCache *cache = BamCache::get_global_ptr();
1578 cache->store(_record);
1579 }
1580 }
1581
1582 /**
1583 * Called by the back-end to retrieve compiled data.
1584 */
1585 bool Shader::
1586 get_compiled(unsigned int &format, string &binary) const {
1587 format = _compiled_format;
1588 binary = _compiled_binary;
1589 return !binary.empty();
1590 }
1591
1592 /**
1593 * Called by the graphics back-end to specify the caps with which we will
1594 * likely want to be compiling our shaders.
1595 */
1596 void Shader::
1597 set_default_caps(const ShaderCaps &caps) {
1598 _default_caps = caps;
1599 }
1600
1601 #ifdef HAVE_CG
1602 /**
1603 *
1604 */
1605 Shader::ShaderArgType Shader::
1606 cg_parameter_type(CGparameter p) {
1607 switch (cgGetParameterClass(p)) {
1608 case CG_PARAMETERCLASS_SCALAR: return SAT_scalar;
1609 case CG_PARAMETERCLASS_VECTOR:
1610 switch (cgGetParameterColumns(p)) {
1611 case 1: return SAT_vec1;
1612 case 2: return SAT_vec2;
1613 case 3: return SAT_vec3;
1614 case 4: return SAT_vec4;
1615 default: return SAT_unknown;
1616 }
1617 case CG_PARAMETERCLASS_MATRIX:
1618 switch (cgGetParameterRows(p)) {
1619 case 1:
1620 switch (cgGetParameterColumns(p)) {
1621 case 1: return SAT_mat1x1;
1622 case 2: return SAT_mat1x2;
1623 case 3: return SAT_mat1x3;
1624 case 4: return SAT_mat1x4;
1625 default: return SAT_unknown;
1626 }
1627 case 2:
1628 switch (cgGetParameterColumns(p)) {
1629 case 1: return SAT_mat2x1;
1630 case 2: return SAT_mat2x2;
1631 case 3: return SAT_mat2x3;
1632 case 4: return SAT_mat2x4;
1633 default: return SAT_unknown;
1634 }
1635 case 3:
1636 switch (cgGetParameterColumns(p)) {
1637 case 1: return SAT_mat3x1;
1638 case 2: return SAT_mat3x2;
1639 case 3: return SAT_mat3x3;
1640 case 4: return SAT_mat3x4;
1641 default: return SAT_unknown;
1642 }
1643 case 4:
1644 switch (cgGetParameterColumns(p)) {
1645 case 1: return SAT_mat4x1;
1646 case 2: return SAT_mat4x2;
1647 case 3: return SAT_mat4x3;
1648 case 4: return SAT_mat4x4;
1649 default: return SAT_unknown;
1650 }
1651 default: return SAT_unknown;
1652 }
1653 case CG_PARAMETERCLASS_SAMPLER:
1654 switch (cgGetParameterType(p)) {
1655 case CG_SAMPLER1D: return Shader::SAT_sampler1d;
1656 case CG_SAMPLER2D: return Shader::SAT_sampler2d;
1657 case CG_SAMPLER3D: return Shader::SAT_sampler3d;
1658 case CG_SAMPLER2DARRAY: return Shader::SAT_sampler2d_array;
1659 case CG_SAMPLERCUBE: return Shader::SAT_sampler_cube;
1660 case CG_SAMPLERBUF: return Shader::SAT_sampler_buffer;
1661 case CG_SAMPLERCUBEARRAY:return Shader::SAT_sampler_cube_array;
1662 // CG_SAMPLER1DSHADOW and CG_SAMPLER2DSHADOW
1663 case 1313: return Shader::SAT_sampler1d;
1664 case 1314: return Shader::SAT_sampler2d;
1665 default: return SAT_unknown;
1666 }
1667 case CG_PARAMETERCLASS_ARRAY: return SAT_unknown;
1668 default:
1669 return SAT_unknown;
1670 }
1671 }
1672
1673 /**
1674 *
1675 */
1676 Shader::ShaderArgClass Shader::cg_parameter_class(CGparameter p) {
1677 switch (cgGetParameterClass(p)) {
1678 case CG_PARAMETERCLASS_SCALAR: return Shader::SAC_scalar;
1679 case CG_PARAMETERCLASS_VECTOR: return Shader::SAC_vector;
1680 case CG_PARAMETERCLASS_MATRIX: return Shader::SAC_matrix;
1681 case CG_PARAMETERCLASS_SAMPLER: return Shader::SAC_sampler;
1682 case CG_PARAMETERCLASS_ARRAY: return Shader::SAC_array;
1683 default: return Shader::SAC_unknown;
1684 }
1685 }
1686
1687 /**
1688 *
1689 */
1690 Shader::ShaderArgDir Shader::
1691 cg_parameter_dir(CGparameter p) {
1692 switch (cgGetParameterDirection(p)) {
1693 case CG_IN: return Shader::SAD_in;
1694 case CG_OUT: return Shader::SAD_out;
1695 case CG_INOUT: return Shader::SAD_inout;
1696 default: return Shader::SAD_unknown;
1697 }
1698 }
1699
1700 /**
1701 * xyz
1702 */
1703 void Shader::
1704 cg_release_resources() {
1705 if (_cg_vprogram != 0) {
1706 cgDestroyProgram(_cg_vprogram);
1707 _cg_vprogram = 0;
1708 }
1709 if (_cg_fprogram != 0) {
1710 cgDestroyProgram(_cg_fprogram);
1711 _cg_fprogram = 0;
1712 }
1713 if (_cg_gprogram != 0) {
1714 cgDestroyProgram(_cg_gprogram);
1715 _cg_gprogram = 0;
1716 }
1717 }
1718
1719 /**
1720 * xyz
1721 */
1722 CGprogram Shader::
1723 cg_compile_entry_point(const char *entry, const ShaderCaps &caps,
1724 CGcontext context, ShaderType type) {
1725 CGprogram prog;
1726 CGerror err;
1727 const char *compiler_args[100];
1728 const string text = get_text(type);
1729 int nargs = 0;
1730
1731 int active, ultimate;
1732
1733 switch (type) {
1734 case ST_vertex:
1735 active = caps._active_vprofile;
1736 ultimate = caps._ultimate_vprofile;
1737 break;
1738
1739 case ST_fragment:
1740 active = caps._active_fprofile;
1741 ultimate = caps._ultimate_fprofile;
1742 break;
1743
1744 case ST_geometry:
1745 active = caps._active_gprofile;
1746 ultimate = caps._ultimate_gprofile;
1747 break;
1748
1749 case ST_tess_evaluation:
1750 case ST_tess_control:
1751 active = caps._active_tprofile;
1752 ultimate = caps._ultimate_tprofile;
1753 break;
1754
1755 case ST_none:
1756 default:
1757 active = CG_PROFILE_UNKNOWN;
1758 ultimate = CG_PROFILE_UNKNOWN;
1759 };
1760
1761 if (type == ST_fragment && caps._bug_list.count(SBUG_ati_draw_buffers)) {
1762 compiler_args[nargs++] = "-po";
1763 compiler_args[nargs++] = "ATI_draw_buffers";
1764 }
1765
1766 string version_arg;
1767 if (!cg_glsl_version.empty() && active != CG_PROFILE_UNKNOWN &&
1768 cgGetProfileProperty((CGprofile) active, CG_IS_GLSL_PROFILE)) {
1769
1770 version_arg = "version=";
1771 version_arg += cg_glsl_version;
1772
1773 compiler_args[nargs++] = "-po";
1774 compiler_args[nargs++] = version_arg.c_str();
1775 }
1776
1777 compiler_args[nargs] = 0;
1778
1779 cgGetError();
1780
1781 if ((active != (int)CG_PROFILE_UNKNOWN) && (active != ultimate)) {
1782 // Print out some debug information about what we're doing.
1783 if (shader_cat.is_debug()) {
1784 shader_cat.debug()
1785 << "Compiling Cg shader " << get_filename(type) << " with entry point " << entry
1786 << " and active profile " << cgGetProfileString((CGprofile) active) << "\n";
1787
1788 if (nargs > 0) {
1789 shader_cat.debug() << "Using compiler arguments:";
1790 for (int i = 0; i < nargs; ++i) {
1791 shader_cat.debug(false) << " " << compiler_args[i];
1792 }
1793 shader_cat.debug(false) << "\n";
1794 }
1795 }
1796
1797 // Compile the shader with the active profile.
1798 prog = cgCreateProgram(context, CG_SOURCE, text.c_str(),
1799 (CGprofile)active, entry, (const char **)compiler_args);
1800 err = cgGetError();
1801 if (err == CG_NO_ERROR) {
1802 return prog;
1803 }
1804 if (prog != 0) {
1805 cgDestroyProgram(prog);
1806 }
1807 if (shader_cat.is_debug()) {
1808 shader_cat.debug()
1809 << "Compilation with active profile failed: " << cgGetErrorString(err) << "\n";
1810 if (err == CG_COMPILER_ERROR) {
1811 const char *listing = cgGetLastListing(context);
1812 if (listing != nullptr) {
1813 shader_cat.debug(false) << listing;
1814 }
1815 }
1816 }
1817 }
1818
1819 if (shader_cat.is_debug()) {
1820 shader_cat.debug()
1821 << "Compiling Cg shader " << get_filename(type) << " with entry point " << entry
1822 << " and ultimate profile " << cgGetProfileString((CGprofile) ultimate) << "\n";
1823 }
1824
1825 // The active profile failed, so recompile it with the ultimate profile.
1826 prog = cgCreateProgram(context, CG_SOURCE, text.c_str(),
1827 (CGprofile)ultimate, entry, nullptr);
1828
1829 // Extract the output listing.
1830 err = cgGetError();
1831 const char *listing = cgGetLastListing(context);
1832
1833 if (err == CG_NO_ERROR && listing != nullptr && strlen(listing) > 1) {
1834 shader_cat.warning()
1835 << "Encountered warnings during compilation of " << get_filename(type)
1836 << ":\n" << listing;
1837
1838 } else if (err == CG_COMPILER_ERROR) {
1839 shader_cat.error()
1840 << "Failed to compile Cg shader " << get_filename(type);
1841 if (listing != nullptr) {
1842 shader_cat.error(false) << ":\n" << listing;
1843 } else {
1844 shader_cat.error(false) << "!\n";
1845 }
1846 }
1847
1848 if (err == CG_NO_ERROR) {
1849 return prog;
1850 }
1851
1852 if (shader_cat.is_debug()) {
1853 shader_cat.debug()
1854 << "Compilation with ultimate profile failed: " << cgGetErrorString(err) << "\n";
1855 }
1856
1857 if (prog != 0) {
1858 cgDestroyProgram(prog);
1859 }
1860 return 0;
1861 }
1862
1863 /**
1864 * Compiles a Cg shader for a given set of capabilities. If successful, the
1865 * shader is stored in the instance variables _cg_context, _cg_vprogram,
1866 * _cg_fprogram.
1867 */
1868 bool Shader::
1869 cg_compile_shader(const ShaderCaps &caps, CGcontext context) {
1870 _cg_last_caps = caps;
1871
1872 if (!_text._separate || !_text._vertex.empty()) {
1873 _cg_vprogram = cg_compile_entry_point("vshader", caps, context, ST_vertex);
1874 if (_cg_vprogram == 0) {
1875 cg_release_resources();
1876 return false;
1877 }
1878 _cg_vprofile = cgGetProgramProfile(_cg_vprogram);
1879 }
1880
1881 if (!_text._separate || !_text._fragment.empty()) {
1882 _cg_fprogram = cg_compile_entry_point("fshader", caps, context, ST_fragment);
1883 if (_cg_fprogram == 0) {
1884 cg_release_resources();
1885 return false;
1886 }
1887 _cg_fprofile = cgGetProgramProfile(_cg_fprogram);
1888 }
1889
1890 if ((_text._separate && !_text._geometry.empty()) || (!_text._separate && _text._shared.find("gshader") != string::npos)) {
1891 _cg_gprogram = cg_compile_entry_point("gshader", caps, context, ST_geometry);
1892 if (_cg_gprogram == 0) {
1893 cg_release_resources();
1894 return false;
1895 }
1896 _cg_gprofile = cgGetProgramProfile(_cg_gprogram);
1897 }
1898
1899 if (_cg_vprogram == 0 && _cg_fprogram == 0 && _cg_gprogram == 0) {
1900 shader_cat.error() << "Shader must at least have one program!\n";
1901 cg_release_resources();
1902 return false;
1903 }
1904
1905 // This is present to work around a bug in the Cg compiler for Direct3D 9.
1906 // It generates "texld_sat" instructions that the result in an
1907 // D3DXERR_INVALIDDATA error when trying to load the shader, since the _sat
1908 // modifier may not be used on tex* instructions.
1909 if (_cg_fprofile == CG_PROFILE_PS_2_0 ||
1910 _cg_fprofile == CG_PROFILE_PS_2_X ||
1911 _cg_fprofile == CG_PROFILE_PS_3_0) {
1912 vector_string lines;
1913 tokenize(cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM), lines, "\n");
1914
1915 ostringstream out;
1916 int num_modified = 0;
1917
1918 for (size_t i = 0; i < lines.size(); ++i) {
1919 const string &line = lines[i];
1920
1921 size_t space = line.find(' ');
1922 if (space == string::npos) {
1923 out << line << '\n';
1924 continue;
1925 }
1926
1927 string instr = line.substr(0, space);
1928
1929 // Look for a texld instruction with _sat modifier.
1930 if (instr.compare(0, 5, "texld") == 0 &&
1931 instr.compare(instr.size() - 4, 4, "_sat") == 0) {
1932 // Which destination register are we operating on?
1933 string reg = line.substr(space + 1, line.find(',', space) - space - 1);
1934
1935 // Move the saturation operation to a separate instruction.
1936 instr.resize(instr.size() - 4);
1937 out << instr << ' ' << line.substr(space + 1) << '\n';
1938 out << "mov_sat " << reg << ", " << reg << '\n';
1939 ++num_modified;
1940 } else {
1941 out << line << '\n';
1942 }
1943 }
1944
1945 if (num_modified > 0) {
1946 string result = out.str();
1947 CGprogram new_program;
1948 new_program = cgCreateProgram(context, CG_OBJECT, result.c_str(),
1949 (CGprofile)_cg_fprofile, "fshader",
1950 nullptr);
1951 if (new_program) {
1952 cgDestroyProgram(_cg_fprogram);
1953 _cg_fprogram = new_program;
1954
1955 if (shader_cat.is_debug()) {
1956 shader_cat.debug()
1957 << "Replaced " << num_modified << " invalid texld_sat instruction"
1958 << ((num_modified == 1) ? "" : "s") << " in compiled shader\n";
1959 }
1960 } else {
1961 shader_cat.warning()
1962 << "Failed to load shader with fixed texld_sat instructions: "
1963 << cgGetErrorString(cgGetError()) << "\n";
1964 }
1965 }
1966 }
1967
1968 // DEBUG: output the generated program
1969 if (shader_cat.is_debug()) {
1970 const char *vertex_program;
1971 const char *fragment_program;
1972 const char *geometry_program;
1973
1974 if (_cg_vprogram != 0) {
1975 shader_cat.debug()
1976 << "Cg vertex profile: " << cgGetProfileString((CGprofile)_cg_vprofile) << "\n";
1977 vertex_program = cgGetProgramString(_cg_vprogram, CG_COMPILED_PROGRAM);
1978 shader_cat.spam() << vertex_program << "\n";
1979 }
1980 if (_cg_fprogram != 0) {
1981 shader_cat.debug()
1982 << "Cg fragment profile: " << cgGetProfileString((CGprofile)_cg_fprofile) << "\n";
1983 fragment_program = cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM);
1984 shader_cat.spam() << fragment_program << "\n";
1985 }
1986 if (_cg_gprogram != 0) {
1987 shader_cat.debug()
1988 << "Cg geometry profile: " << cgGetProfileString((CGprofile)_cg_gprofile) << "\n";
1989 geometry_program = cgGetProgramString(_cg_gprogram, CG_COMPILED_PROGRAM);
1990 shader_cat.spam() << geometry_program << "\n";
1991 }
1992 }
1993
1994 return true;
1995 }
1996
1997 /**
1998 *
1999 */
2000 bool Shader::
2001 cg_analyze_entry_point(CGprogram prog, ShaderType type) {
2002 bool success = true;
2003
2004 cg_recurse_parameters(cgGetFirstParameter(prog, CG_PROGRAM), type, success);
2005 return success;
2006 }
2007
2008 /**
2009 * This subroutine analyzes the parameters of a Cg shader. The output is
2010 * stored in instance variables: _mat_spec, _var_spec, and _tex_spec.
2011 *
2012 * In order to do this, it is necessary to compile the shader. It would be a
2013 * waste of CPU time to compile the shader, analyze the parameters, and then
2014 * discard the compiled shader. This would force us to compile it again
2015 * later, when we need to build the ShaderContext. Instead, we cache the
2016 * compiled Cg program in instance variables. Later, a ShaderContext can pull
2017 * the compiled shader from these instance vars.
2018 *
2019 * To compile a shader, you need to first choose a profile. There are two
2020 * contradictory objectives:
2021 *
2022 * 1. If you don't use the gsg's active profile, then the cached compiled
2023 * shader will not be useful to the ShaderContext.
2024 *
2025 * 2. If you use too weak a profile, then the shader may not compile. So to
2026 * guarantee success, you should use the ultimate profile.
2027 *
2028 * To resolve this conflict, we try the active profile first, and if that
2029 * doesn't work, we try the ultimate profile.
2030 *
2031 */
2032 bool Shader::
2033 cg_analyze_shader(const ShaderCaps &caps) {
2034
2035 // Make sure we have a context for analyzing the shader.
2036 if (_cg_context == 0) {
2037 _cg_context = cgCreateContext();
2038 if (_cg_context == 0) {
2039 shader_cat.error()
2040 << "Could not create a Cg context object: "
2041 << cgGetErrorString(cgGetError()) << "\n";
2042 return false;
2043 }
2044 }
2045
2046 if (!cg_compile_shader(caps, _cg_context)) {
2047 return false;
2048 }
2049
2050 if (_cg_fprogram != 0) {
2051 if (!cg_analyze_entry_point(_cg_fprogram, ST_fragment)) {
2052 cg_release_resources();
2053 clear_parameters();
2054 return false;
2055 }
2056 }
2057
2058 if (_var_spec.size() != 0) {
2059 shader_cat.error() << "Cannot use vtx parameters in an fshader\n";
2060 cg_release_resources();
2061 clear_parameters();
2062 return false;
2063 }
2064
2065 if (_cg_vprogram != 0) {
2066 if (!cg_analyze_entry_point(_cg_vprogram, ST_vertex)) {
2067 cg_release_resources();
2068 clear_parameters();
2069 return false;
2070 }
2071 }
2072
2073 if (_cg_gprogram != 0) {
2074 if (!cg_analyze_entry_point(_cg_gprogram, ST_geometry)) {
2075 cg_release_resources();
2076 clear_parameters();
2077 return false;
2078 }
2079 }
2080
2081 // Assign sequence numbers to all parameters. GLCgShaderContext relies on
2082 // the fact that the varyings start at seqno 0.
2083 int seqno = 0;
2084 for (size_t i = 0; i < _var_spec.size(); ++i) {
2085 _var_spec[i]._id._seqno = seqno++;
2086 }
2087 for (size_t i = 0; i < _mat_spec.size(); ++i) {
2088 _mat_spec[i]._id._seqno = seqno++;
2089 }
2090 for (size_t i = 0; i < _tex_spec.size(); ++i) {
2091 _tex_spec[i]._id._seqno = seqno++;
2092 }
2093
2094 for (size_t i = 0; i < _ptr_spec.size(); ++i) {
2095 _ptr_spec[i]._id._seqno = seqno++;
2096 _ptr_spec[i]._info._id = _ptr_spec[i]._id;
2097 }
2098
2099 /*
2100 // The following code is present to work around a bug in the Cg compiler.
2101 // It does not generate correct code for shadow map lookups when using arbfp1.
2102 // This is a particularly onerous limitation, given that arbfp1 is the only
2103 // Cg target that works on radeons. I suspect this is an intentional
2104 // omission on nvidia's part. The following code fetches the output listing,
2105 // detects the error, repairs the code, and resumbits the repaired code to Cg.
2106 if ((_cg_fprofile == CG_PROFILE_ARBFP1) && (gsghint->_supports_shadow_filter)) {
2107 bool shadowunit[32];
2108 bool anyshadow = false;
2109 memset(shadowunit, 0, sizeof(shadowunit));
2110 vector_string lines;
2111 tokenize(cgGetProgramString(_cg_program[SHADER_type_frag],
2112 CG_COMPILED_PROGRAM), lines, "\n");
2113 // figure out which texture units contain shadow maps.
2114 for (int lineno=0; lineno<(int)lines.size(); lineno++) {
2115 if (lines[lineno].compare(0,21,"#var sampler2DSHADOW ")) {
2116 continue;
2117 }
2118 vector_string fields;
2119 tokenize(lines[lineno], fields, ":");
2120 if (fields.size()!=5) {
2121 continue;
2122 }
2123 vector_string words;
2124 tokenize(trim(fields[2]), words, " ");
2125 if (words.size()!=2) {
2126 continue;
2127 }
2128 int unit = atoi(words[1].c_str());
2129 if ((unit < 0)||(unit >= 32)) {
2130 continue;
2131 }
2132 anyshadow = true;
2133 shadowunit[unit] = true;
2134 }
2135 // modify all TEX statements that use the relevant texture units.
2136 if (anyshadow) {
2137 for (int lineno=0; lineno<(int)lines.size(); lineno++) {
2138 if (lines[lineno].compare(0,4,"TEX ")) {
2139 continue;
2140 }
2141 vector_string fields;
2142 tokenize(lines[lineno], fields, ",");
2143 if ((fields.size()!=4)||(trim(fields[3]) != "2D;")) {
2144 continue;
2145 }
2146 vector_string texunitf;
2147 tokenize(trim(fields[2]), texunitf, "[]");
2148 if ((texunitf.size()!=3)||(texunitf[0] != "texture")||(texunitf[2]!="")) {
2149 continue;
2150 }
2151 int unit = atoi(texunitf[1].c_str());
2152 if ((unit < 0) || (unit >= 32) || (shadowunit[unit]==false)) {
2153 continue;
2154 }
2155 lines[lineno] = fields[0]+","+fields[1]+","+fields[2]+", SHADOW2D;";
2156 }
2157 string result = "!!ARBfp1.0\nOPTION ARB_fragment_program_shadow;\n";
2158 for (int lineno=1; lineno<(int)lines.size(); lineno++) {
2159 result += (lines[lineno] + "\n");
2160 }
2161 _cg_program[2] = _cg_program[SHADER_type_frag];
2162 _cg_program[SHADER_type_frag] =
2163 cgCreateProgram(_cg_context, CG_OBJECT, result.c_str(),
2164 _cg_profile[SHADER_type_frag], "fshader", (const char**)NULL);
2165 cg_report_errors(s->get_name(), _cg_context);
2166 if (_cg_program[SHADER_type_frag]==0) {
2167 release_resources();
2168 return false;
2169 }
2170 }
2171 }
2172 */
2173
2174 cg_release_resources();
2175 return true;
2176 }
2177
2178 /**
2179 * Returns the CGprogram of the given shadertype that belongs to this shader.
2180 */
2181 CGprogram Shader::
2182 cg_program_from_shadertype(ShaderType type) {
2183 switch (type) {
2184 case ST_vertex:
2185 return _cg_vprogram;
2186
2187 case ST_fragment:
2188 return _cg_fprogram;
2189
2190 case ST_geometry:
2191 return _cg_gprogram;
2192
2193 default:
2194 return 0;
2195 }
2196 }
2197
2198 /**
2199 * This routine is used by the ShaderContext constructor to compile the
2200 * shader. The CGprogram objects are turned over to the ShaderContext, we no
2201 * longer own them.
2202 */
2203 bool Shader::
2204 cg_compile_for(const ShaderCaps &caps, CGcontext context,
2205 CGprogram &combined_program, pvector<CGparameter> &map) {
2206
2207 // Initialize the return values to empty.
2208 combined_program = 0;
2209 map.clear();
2210
2211 // Make sure the shader is compiled for the target caps. Most of the time,
2212 // it will already be - this is usually a no-op.
2213
2214 _default_caps = caps;
2215 if (!cg_compile_shader(caps, context)) {
2216 return false;
2217 }
2218
2219 // If the compile routine used the ultimate profile instead of the active
2220 // one, it means the active one isn't powerful enough to compile the shader.
2221 if (_cg_vprogram != 0 && _cg_vprofile != caps._active_vprofile) {
2222 shader_cat.error() << "Cg vertex program not supported by profile "
2223 << cgGetProfileString((CGprofile) caps._active_vprofile) << ": "
2224 << get_filename(ST_vertex) << ". Try choosing a different profile.\n";
2225 return false;
2226 }
2227 if (_cg_fprogram != 0 && _cg_fprofile != caps._active_fprofile) {
2228 shader_cat.error() << "Cg fragment program not supported by profile "
2229 << cgGetProfileString((CGprofile) caps._active_fprofile) << ": "
2230 << get_filename(ST_fragment) << ". Try choosing a different profile.\n";
2231 return false;
2232 }
2233 if (_cg_gprogram != 0 && _cg_gprofile != caps._active_gprofile) {
2234 shader_cat.error() << "Cg geometry program not supported by profile "
2235 << cgGetProfileString((CGprofile) caps._active_gprofile) << ": "
2236 << get_filename(ST_geometry) << ". Try choosing a different profile.\n";
2237 return false;
2238 }
2239
2240 // Gather the programs we will be combining.
2241 pvector<CGprogram> programs;
2242 if (_cg_vprogram != 0) {
2243 programs.push_back(_cg_vprogram);
2244 }
2245 if (_cg_fprogram != 0) {
2246 programs.push_back(_cg_fprogram);
2247 }
2248 if (_cg_gprogram != 0) {
2249 programs.push_back(_cg_gprogram);
2250 }
2251
2252 // Combine the programs. This can be more optimal than loading them
2253 // individually, and it is even necessary for some profiles (particularly
2254 // GLSL profiles on non-NVIDIA GPUs).
2255 combined_program = cgCombinePrograms(programs.size(), &programs[0]);
2256
2257 // Build a parameter map.
2258 size_t n_mat = _mat_spec.size();
2259 size_t n_tex = _tex_spec.size();
2260 size_t n_var = _var_spec.size();
2261 size_t n_ptr = _ptr_spec.size();
2262
2263 map.resize(n_mat + n_tex + n_var + n_ptr);
2264
2265 // This is a bit awkward, we have to go in and seperate out the combined
2266 // program, since all the parameter bindings have changed.
2267 CGprogram programs_by_type[ST_COUNT];
2268 for (int i = 0; i < cgGetNumProgramDomains(combined_program); ++i) {
2269 // Conveniently, the CGdomain enum overlaps with ShaderType.
2270 CGprogram program = cgGetProgramDomainProgram(combined_program, i);
2271 programs_by_type[cgGetProgramDomain(program)] = program;
2272 }
2273
2274 for (size_t i = 0; i < n_mat; ++i) {
2275 const ShaderArgId &id = _mat_spec[i]._id;
2276 map[id._seqno] = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2277
2278 if (shader_cat.is_debug()) {
2279 const char *resource = cgGetParameterResourceName(map[id._seqno]);
2280 if (resource != nullptr) {
2281 shader_cat.debug() << "Uniform parameter " << id._name
2282 << " is bound to resource " << resource << "\n";
2283 }
2284 }
2285 }
2286
2287 for (size_t i = 0; i < n_tex; ++i) {
2288 const ShaderArgId &id = _tex_spec[i]._id;
2289 CGparameter p = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2290
2291 if (shader_cat.is_debug()) {
2292 const char *resource = cgGetParameterResourceName(p);
2293 if (resource != nullptr) {
2294 shader_cat.debug() << "Texture parameter " << id._name
2295 << " is bound to resource " << resource << "\n";
2296 }
2297 }
2298 map[id._seqno] = p;
2299 }
2300
2301 for (size_t i = 0; i < n_var; ++i) {
2302 const ShaderArgId &id = _var_spec[i]._id;
2303 CGparameter p = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2304
2305 const char *resource = cgGetParameterResourceName(p);
2306 if (shader_cat.is_debug() && resource != nullptr) {
2307 if (cgGetParameterResource(p) == CG_GLSL_ATTRIB) {
2308 shader_cat.debug()
2309 << "Varying parameter " << id._name << " is bound to GLSL attribute "
2310 << resource << "\n";
2311 } else {
2312 shader_cat.debug()
2313 << "Varying parameter " << id._name << " is bound to resource "
2314 << resource << " (" << cgGetParameterResource(p)
2315 << ", index " << cgGetParameterResourceIndex(p) << ")\n";
2316 }
2317 }
2318
2319 map[id._seqno] = p;
2320 }
2321
2322 for (size_t i = 0; i < n_ptr; ++i) {
2323 const ShaderArgId &id = _ptr_spec[i]._id;
2324 map[id._seqno] = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2325
2326 if (shader_cat.is_debug()) {
2327 const char *resource = cgGetParameterResourceName(map[id._seqno]);
2328 if (resource != nullptr) {
2329 shader_cat.debug() << "Uniform ptr parameter " << id._name
2330 << " is bound to resource " << resource << "\n";
2331 }
2332 }
2333 }
2334
2335 // Transfer ownership of the compiled shader.
2336 if (_cg_vprogram != 0) {
2337 cgDestroyProgram(_cg_vprogram);
2338 _cg_vprogram = 0;
2339 }
2340 if (_cg_fprogram != 0) {
2341 cgDestroyProgram(_cg_fprogram);
2342 _cg_fprogram = 0;
2343 }
2344 if (_cg_gprogram != 0) {
2345 cgDestroyProgram(_cg_gprogram);
2346 _cg_gprogram = 0;
2347 }
2348
2349 _cg_last_caps.clear();
2350
2351 return true;
2352 }
2353 #endif // HAVE_CG
2354
2355 /**
2356 * Construct a Shader that will be filled in using fillin() or read() later.
2357 */
2358 Shader::
2359 Shader(ShaderLanguage lang) :
2360 _error_flag(false),
2361 _parse(0),
2362 _loaded(false),
2363 _language(lang),
2364 _last_modified(0),
2365 _mat_deps(0),
2366 _cache_compiled_shader(false)
2367 {
2368 #ifdef HAVE_CG
2369 _cg_vprogram = 0;
2370 _cg_fprogram = 0;
2371 _cg_gprogram = 0;
2372 _cg_vprofile = CG_PROFILE_UNKNOWN;
2373 _cg_fprofile = CG_PROFILE_UNKNOWN;
2374 _cg_gprofile = CG_PROFILE_UNKNOWN;
2375 if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) {
2376 if (basic_shaders_only) {
2377 _default_caps._active_vprofile = CG_PROFILE_ARBVP1;
2378 _default_caps._active_fprofile = CG_PROFILE_ARBFP1;
2379 _default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
2380 } else {
2381 _default_caps._active_vprofile = CG_PROFILE_UNKNOWN;
2382 _default_caps._active_fprofile = CG_PROFILE_UNKNOWN;
2383 _default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
2384 }
2385 _default_caps._ultimate_vprofile = cgGetProfile("glslv");
2386 _default_caps._ultimate_fprofile = cgGetProfile("glslf");
2387 _default_caps._ultimate_gprofile = cgGetProfile("glslg");
2388 if (_default_caps._ultimate_gprofile == CG_PROFILE_UNKNOWN) {
2389 _default_caps._ultimate_gprofile = cgGetProfile("gp4gp");
2390 }
2391 }
2392 #endif
2393 }
2394
2395 /**
2396 * Reads the shader from the given filename(s). Returns a boolean indicating
2397 * success or failure.
2398 */
2399 bool Shader::
2400 read(const ShaderFile &sfile, BamCacheRecord *record) {
2401 _text._separate = sfile._separate;
2402
2403 if (sfile._separate) {
2404 if (_language == SL_none) {
2405 shader_cat.error()
2406 << "No shader language was specified!\n";
2407 return false;
2408 }
2409
2410 if (!sfile._vertex.empty() &&
2411 !do_read_source(_text._vertex, sfile._vertex, record)) {
2412 return false;
2413 }
2414 if (!sfile._fragment.empty() &&
2415 !do_read_source(_text._fragment, sfile._fragment, record)) {
2416 return false;
2417 }
2418 if (!sfile._geometry.empty() &&
2419 !do_read_source(_text._geometry, sfile._geometry, record)) {
2420 return false;
2421 }
2422 if (!sfile._tess_control.empty() &&
2423 !do_read_source(_text._tess_control, sfile._tess_control, record)) {
2424 return false;
2425 }
2426 if (!sfile._tess_evaluation.empty() &&
2427 !do_read_source(_text._tess_evaluation, sfile._tess_evaluation, record)) {
2428 return false;
2429 }
2430 if (!sfile._compute.empty() &&
2431 !do_read_source(_text._compute, sfile._compute, record)) {
2432 return false;
2433 }
2434 _filename = sfile;
2435
2436 } else {
2437 if (!do_read_source(_text._shared, sfile._shared, record)) {
2438 return false;
2439 }
2440 _fullpath = _source_files[0];
2441 _filename = sfile;
2442
2443 // Determine which language the shader is written in.
2444 if (_language == SL_none) {
2445 string header;
2446 parse_init();
2447 parse_line(header, true, true);
2448 if (header == "//Cg") {
2449 _language = SL_Cg;
2450 } else {
2451 shader_cat.error()
2452 << "Unable to determine shader language of " << sfile._shared << "\n";
2453 return false;
2454 }
2455 } else if (_language == SL_GLSL) {
2456 shader_cat.error()
2457 << "GLSL shaders must have separate shader bodies!\n";
2458 return false;
2459 }
2460
2461 // Determine which language the shader is written in.
2462 if (_language == SL_Cg) {
2463 #ifdef HAVE_CG
2464 ShaderCaps caps = _default_caps;
2465 cg_get_profile_from_header(caps);
2466
2467 if (!cg_analyze_shader(caps)) {
2468 shader_cat.error()
2469 << "Shader encountered an error.\n";
2470 return false;
2471 }
2472 #else
2473 shader_cat.error()
2474 << "Tried to load Cg shader, but no Cg support is enabled.\n";
2475 #endif
2476 } else {
2477 shader_cat.error()
2478 << "Shader is not in a supported shader-language.\n";
2479 return false;
2480 }
2481 }
2482
2483 _loaded = true;
2484 return true;
2485 }
2486
2487 /**
2488 * Loads the shader from the given string(s). Returns a boolean indicating
2489 * success or failure.
2490 */
2491 bool Shader::
2492 load(const ShaderFile &sbody, BamCacheRecord *record) {
2493 _filename = ShaderFile("created-shader");
2494 _fullpath = Filename();
2495 _text._separate = sbody._separate;
2496
2497 if (sbody._separate) {
2498 if (_language == SL_none) {
2499 shader_cat.error()
2500 << "No shader language was specified!\n";
2501 return false;
2502 }
2503
2504 if (!sbody._vertex.empty() &&
2505 !do_load_source(_text._vertex, sbody._vertex, record)) {
2506 return false;
2507 }
2508 if (!sbody._fragment.empty() &&
2509 !do_load_source(_text._fragment, sbody._fragment, record)) {
2510 return false;
2511 }
2512 if (!sbody._geometry.empty() &&
2513 !do_load_source(_text._geometry, sbody._geometry, record)) {
2514 return false;
2515 }
2516 if (!sbody._tess_control.empty() &&
2517 !do_load_source(_text._tess_control, sbody._tess_control, record)) {
2518 return false;
2519 }
2520 if (!sbody._tess_evaluation.empty() &&
2521 !do_load_source(_text._tess_evaluation, sbody._tess_evaluation, record)) {
2522 return false;
2523 }
2524 if (!sbody._compute.empty() &&
2525 !do_load_source(_text._compute, sbody._compute, record)) {
2526 return false;
2527 }
2528
2529 } else {
2530 if (!do_load_source(_text._shared, sbody._shared, record)) {
2531 return false;
2532 }
2533
2534 // Determine which language the shader is written in.
2535 if (_language == SL_none) {
2536 string header;
2537 parse_init();
2538 parse_line(header, true, true);
2539 if (header == "//Cg") {
2540 _language = SL_Cg;
2541 } else {
2542 shader_cat.error()
2543 << "Unable to determine shader language of " << sbody._shared << "\n";
2544 return false;
2545 }
2546 } else if (_language == SL_GLSL) {
2547 shader_cat.error()
2548 << "GLSL shaders must have separate shader bodies!\n";
2549 return false;
2550 }
2551
2552 // Determine which language the shader is written in.
2553 if (_language == SL_Cg) {
2554 #ifdef HAVE_CG
2555 ShaderCaps caps = _default_caps;
2556 cg_get_profile_from_header(caps);
2557
2558 if (!cg_analyze_shader(caps)) {
2559 shader_cat.error()
2560 << "Shader encountered an error.\n";
2561 return false;
2562 }
2563 #else
2564 shader_cat.error()
2565 << "Tried to load Cg shader, but no Cg support is enabled.\n";
2566 #endif
2567 } else {
2568 shader_cat.error()
2569 << "Shader is not in a supported shader-language.\n";
2570 return false;
2571 }
2572 }
2573
2574 _loaded = true;
2575 return true;
2576 }
2577
2578 /**
2579 * Reads the shader file from the given path into the given string.
2580 *
2581 * Returns false if there was an error with this shader bad enough to consider
2582 * it 'invalid'.
2583 */
2584 bool Shader::
2585 do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
2586 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
2587 PT(VirtualFile) vf = vfs->find_file(fn, get_model_path());
2588 if (vf == nullptr) {
2589 shader_cat.error()
2590 << "Could not find shader file: " << fn << "\n";
2591 return false;
2592 }
2593
2594 if (_language == SL_GLSL && glsl_preprocess) {
2595 istream *source = vf->open_read_file(true);
2596 if (source == nullptr) {
2597 shader_cat.error()
2598 << "Could not open shader file: " << fn << "\n";
2599 return false;
2600 }
2601
2602 // Preprocess the GLSL file as we read it.
2603 shader_cat.info()
2604 << "Preprocessing shader file: " << fn << "\n";
2605
2606 std::set<Filename> open_files;
2607 ostringstream sstr;
2608 if (!r_preprocess_source(sstr, *source, fn, vf->get_filename(), open_files, record)) {
2609 vf->close_read_file(source);
2610 return false;
2611 }
2612 vf->close_read_file(source);
2613 into = sstr.str();
2614
2615 } else {
2616 shader_cat.info() << "Reading shader file: " << fn << "\n";
2617
2618 if (!vf->read_file(into, true)) {
2619 shader_cat.error()
2620 << "Could not read shader file: " << fn << "\n";
2621 return false;
2622 }
2623 }
2624
2625 if (record != nullptr) {
2626 record->add_dependent_file(vf);
2627 }
2628
2629 _last_modified = std::max(_last_modified, vf->get_timestamp());
2630 _source_files.push_back(vf->get_filename());
2631
2632 // Strip trailing whitespace.
2633 while (!into.empty() && isspace(into[into.size() - 1])) {
2634 into.resize(into.size() - 1);
2635 }
2636
2637 // Except add back a newline at the end, which is needed by Intel drivers.
2638 into += "\n";
2639
2640 return true;
2641 }
2642
2643 /**
2644 * Loads the shader file from the given string into the given string,
2645 * performing any pre-processing on it that may be necessary.
2646 *
2647 * Returns false if there was an error with this shader bad enough to consider
2648 * it 'invalid'.
2649 */
2650 bool Shader::
2651 do_load_source(string &into, const std::string &source, BamCacheRecord *record) {
2652 if (_language == SL_GLSL && glsl_preprocess) {
2653 // Preprocess the GLSL file as we read it.
2654 std::set<Filename> open_files;
2655 std::ostringstream sstr;
2656 std::istringstream in(source);
2657 if (!r_preprocess_source(sstr, in, Filename("created-shader"), Filename(),
2658 open_files, record)) {
2659 return false;
2660 }
2661 into = sstr.str();
2662
2663 } else {
2664 into = source;
2665 }
2666
2667 // Strip trailing whitespace.
2668 while (!into.empty() && isspace(into[into.size() - 1])) {
2669 into.resize(into.size() - 1);
2670 }
2671
2672 // Except add back a newline at the end, which is needed by Intel drivers.
2673 into += "\n";
2674
2675 return true;
2676 }
2677
2678 /**
2679 * Loads a given GLSL file line by line, and processes any #pragma include and
2680 * once statements, as well as removes any comments.
2681 *
2682 * The set keeps track of which files we have already included, for checking
2683 * recursive includes.
2684 */
2685 bool Shader::
2686 r_preprocess_include(ostream &out, const Filename &fn,
2687 const Filename &source_dir,
2688 std::set<Filename> &once_files,
2689 BamCacheRecord *record, int depth) {
2690
2691 if (depth > glsl_include_recursion_limit) {
2692 shader_cat.error()
2693 << "GLSL includes nested too deeply, raise glsl-include-recursion-limit"
2694 " if necessary\n";
2695 return false;
2696 }
2697
2698 DSearchPath path(get_model_path());
2699 if (!source_dir.empty()) {
2700 path.prepend_directory(source_dir);
2701 }
2702
2703 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
2704 PT(VirtualFile) vf = vfs->find_file(fn, path);
2705 if (vf == nullptr) {
2706 shader_cat.error()
2707 << "Could not find shader include: " << fn << "\n";
2708 return false;
2709 }
2710
2711 Filename full_fn = vf->get_filename();
2712 if (once_files.find(full_fn) != once_files.end()) {
2713 // If this file had a #pragma once, just move on.
2714 return true;
2715 }
2716
2717 istream *source = vf->open_read_file(true);
2718 if (source == nullptr) {
2719 shader_cat.error()
2720 << "Could not open shader include: " << fn << "\n";
2721 return false;
2722 }
2723
2724 if (record != nullptr) {
2725 record->add_dependent_file(vf);
2726 }
2727 _last_modified = std::max(_last_modified, vf->get_timestamp());
2728 _source_files.push_back(full_fn);
2729
2730 // We give each file an unique index. This is so that we can identify a
2731 // particular shader in the error output. We offset them by 2048 so that
2732 // they are more recognizable. GLSL doesn't give us anything more useful
2733 // than that, unfortunately. Don't do this for the top-level file, though.
2734 // We don't want anything to get in before a potential #version directive.
2735 int fileno = 0;
2736 fileno = 2048 + _included_files.size();
2737
2738 // Write it into the vector so that we can substitute it later when we are
2739 // parsing the GLSL error log. Don't store the full filename because it
2740 // would just be too long to display.
2741 _included_files.push_back(fn);
2742
2743 if (shader_cat.is_debug()) {
2744 shader_cat.debug()
2745 << "Preprocessing shader include " << fileno << ": " << fn << "\n";
2746 }
2747
2748 bool result = r_preprocess_source(out, *source, fn, full_fn, once_files, record, fileno, depth);
2749 vf->close_read_file(source);
2750 return result;
2751 }
2752
2753 /**
2754 * Loads a given GLSL stream line by line, processing any #pragma include and
2755 * once statements, as well as removing any comments.
2756 *
2757 * The set keeps track of which files we have already included, for checking
2758 * recursive includes.
2759 */
2760 bool Shader::
2761 r_preprocess_source(ostream &out, istream &in, const Filename &fn,
2762 const Filename &full_fn, std::set<Filename> &once_files,
2763 BamCacheRecord *record, int fileno, int depth) {
2764
2765 // Iterate over the lines for things we may need to preprocess.
2766 string line;
2767 int ext_google_include = 0; // 1 = warn, 2 = enable
2768 int ext_google_line = 0;
2769 bool had_include = false;
2770 bool had_version = false;
2771 int lineno = 0;
2772 bool write_line_directive = (fileno != 0);
2773
2774 while (std::getline(in, line)) {
2775 ++lineno;
2776
2777 if (line.empty()) {
2778 // We still write a newline to make sure the line numbering remains
2779 // consistent, unless we are about to write a #line directive anyway.
2780 if (!write_line_directive) {
2781 out.put('\n');
2782 }
2783 continue;
2784 }
2785
2786 // If the line ends with a backslash, concatenate the following line.
2787 // Preprocessor definitions may be broken up into multiple lines.
2788 while (line[line.size() - 1] == '\\') {
2789 line.resize(line.size() - 1);
2790 string line2;
2791
2792 if (std::getline(in, line2)) {
2793 line += line2;
2794 if (!write_line_directive) {
2795 out.put('\n');
2796 }
2797 ++lineno;
2798 } else {
2799 break;
2800 }
2801 }
2802
2803 // Look for comments to strip. This is necessary because comments may
2804 // appear in the middle of or around a preprocessor definition.
2805 size_t line_comment = line.find("//");
2806 size_t block_comment = line.find("/*");
2807 if (line_comment < block_comment) {
2808 // A line comment - strip off the rest of the line.
2809 line.resize(line_comment);
2810
2811 } else if (block_comment < line_comment) {
2812 // A block comment. Search for closing block.
2813 string line2 = line.substr(block_comment + 2);
2814
2815 // According to the GLSL specification, a block comment is replaced with
2816 // a single whitespace character.
2817 line.resize(block_comment);
2818 line += ' ';
2819
2820 size_t block_end = line2.find("*/");
2821 while (block_end == string::npos) {
2822 // Didn't find it - look in the next line.
2823 if (std::getline(in, line2)) {
2824 if (!write_line_directive) {
2825 out.put('\n');
2826 }
2827 ++lineno;
2828 block_end = line2.find("*/");
2829 } else {
2830 shader_cat.error()
2831 << "Expected */ before end of file " << fn << "\n";
2832 return false;
2833 }
2834 }
2835
2836 line += line2.substr(block_end + 2);
2837 }
2838
2839 // Strip trailing whitespace.
2840 while (!line.empty() && isspace(line[line.size() - 1])) {
2841 line.resize(line.size() - 1);
2842 }
2843
2844 if (line.empty()) {
2845 if (!write_line_directive) {
2846 out.put('\n');
2847 }
2848 continue;
2849 }
2850
2851 // Check if this line contains a #directive.
2852 char directive[64];
2853 if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) {
2854 // Nope. Just pass the line through unmodified.
2855 if (write_line_directive) {
2856 out << "#line " << lineno << " " << fileno << " // " << fn << "\n";
2857 write_line_directive = false;
2858 }
2859 out << line << "\n";
2860 continue;
2861 }
2862
2863 char pragma[64];
2864 size_t nread = 0;
2865 // What kind of directive is it?
2866 if (strcmp(directive, "pragma") == 0 &&
2867 sscanf(line.c_str(), " # pragma %63s", pragma) == 1) {
2868 if (strcmp(pragma, "include") == 0) {
2869 // Allow both double quotes and angle brackets.
2870 Filename incfn, source_dir;
2871 {
2872 char incfile[2048];
2873 if (sscanf(line.c_str(), " # pragma%*[ \t]include \"%2047[^\"]\" %zn", incfile, &nread) == 1
2874 && nread == line.size()) {
2875 // A regular include, with double quotes. Probably a local file.
2876 source_dir = full_fn.get_dirname();
2877 incfn = incfile;
2878
2879 } else if (sscanf(line.c_str(), " # pragma%*[ \t]include <%2047[^\"]> %zn", incfile, &nread) == 1
2880 && nread == line.size()) {
2881 // Angled includes are also OK, but we don't search in the directory
2882 // of the source file.
2883 incfn = incfile;
2884
2885 } else {
2886 // Couldn't parse it.
2887 shader_cat.error()
2888 << "Malformed #pragma include at line " << lineno
2889 << " of file " << fn << ":\n " << line << "\n";
2890 return false;
2891 }
2892 }
2893
2894 // OK, great. Process the include.
2895 if (!r_preprocess_include(out, incfn, source_dir, once_files, record, depth + 1)) {
2896 // An error occurred. Pass on the failure.
2897 shader_cat.error(false) << "included at line "
2898 << lineno << " of file " << fn << ":\n " << line << "\n";
2899 return false;
2900 }
2901
2902 // Restore the line counter.
2903 write_line_directive = true;
2904 had_include = true;
2905 continue;
2906
2907 } else if (strcmp(pragma, "once") == 0) {
2908 // Do a stricter syntax check, just to be extra safe.
2909 if (sscanf(line.c_str(), " # pragma%*[ \t]once %zn", &nread) != 0 ||
2910 nread != line.size()) {
2911 shader_cat.error()
2912 << "Malformed #pragma once at line " << lineno
2913 << " of file " << fn << ":\n " << line << "\n";
2914 return false;
2915 }
2916
2917 if (fileno == 0) {
2918 shader_cat.warning()
2919 << "#pragma once in main file at line "
2920 << lineno << " of file " << fn
2921 #ifndef NDEBUG
2922 << ":\n " << line
2923 #endif
2924 << "\n";
2925 }
2926
2927 if (!full_fn.empty()) {
2928 once_files.insert(full_fn);
2929 }
2930 continue;
2931 }
2932 // Otherwise, just pass it through to the driver.
2933
2934 } else if (strcmp(directive, "endif") == 0) {
2935 // Check for an #endif after an include. We have to restore the line
2936 // number in case the include happened under an #if block.
2937 if (had_include) {
2938 write_line_directive = true;
2939 }
2940
2941 } else if (strcmp(directive, "version") == 0) {
2942 had_version = true;
2943
2944 } else if (strcmp(directive, "extension") == 0) {
2945 // Check for special preprocessing extensions.
2946 char extension[256];
2947 char behavior[9];
2948 if (sscanf(line.c_str(), " # extension%*[ \t]%255[^: \t] : %8s", extension, behavior) == 2) {
2949 // Parse the behavior string.
2950 int mode;
2951 if (strcmp(behavior, "require") == 0 || strcmp(behavior, "enable") == 0) {
2952 mode = 2;
2953 } else if (strcmp(behavior, "warn") == 0) {
2954 mode = 1;
2955 } else if (strcmp(behavior, "disable") == 0) {
2956 mode = 0;
2957 } else {
2958 shader_cat.error()
2959 << "Extension directive specifies invalid behavior at line "
2960 << lineno << " of file " << fn << ":\n " << line << "\n";
2961 return false;
2962 }
2963
2964 if (strcmp(extension, "all") == 0) {
2965 if (mode == 2) {
2966 shader_cat.error()
2967 << "Extension directive for 'all' may only specify 'warn' or "
2968 "'disable' at line " << lineno << " of file " << fn
2969 << ":\n " << line << "\n";
2970 return false;
2971 }
2972 ext_google_include = mode;
2973 ext_google_line = mode;
2974 // Still pass it through to the driver, so it can enable other
2975 // extensions.
2976
2977 } else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) {
2978 // Enable the Google extension support for #include statements.
2979 // This also implicitly enables GL_GOOGLE_cpp_style_line_directive.
2980 // This matches the behavior of Khronos' glslang reference compiler.
2981 ext_google_include = mode;
2982 ext_google_line = mode;
2983 continue;
2984
2985 } else if (strcmp(extension, "GL_GOOGLE_cpp_style_line_directive") == 0) {
2986 // Enables strings in #line statements.
2987 ext_google_line = mode;
2988 continue;
2989 }
2990 } else {
2991 shader_cat.error()
2992 << "Failed to parse extension directive at line "
2993 << lineno << " of file " << fn << ":\n " << line << "\n";
2994 return false;
2995 }
2996
2997 } else if (ext_google_include > 0 && strcmp(directive, "include") == 0) {
2998 // Warn about extension use if requested.
2999 if (ext_google_include == 1) {
3000 shader_cat.warning()
3001 << "Extension GL_GOOGLE_include_directive is being used at line "
3002 << lineno << " of file " << fn
3003 #ifndef NDEBUG
3004 << ":\n " << line
3005 #endif
3006 << "\n";
3007 }
3008
3009 // This syntax allows only double quotes, not angle brackets.
3010 Filename incfn;
3011 {
3012 char incfile[2048];
3013 if (sscanf(line.c_str(), " # include%*[ \t]\"%2047[^\"]\" %zn", incfile, &nread) != 1
3014 || nread != line.size()) {
3015 // Couldn't parse it.
3016 shader_cat.error()
3017 << "Malformed #include at line " << lineno
3018 << " of file " << fn << ":\n " << line << "\n";
3019 return false;
3020 }
3021 incfn = incfile;
3022 }
3023
3024 // OK, great. Process the include.
3025 Filename source_dir = full_fn.get_dirname();
3026 if (!r_preprocess_include(out, incfn, source_dir, once_files, record, depth + 1)) {
3027 // An error occurred. Pass on the failure.
3028 shader_cat.error(false) << "included at line "
3029 << lineno << " of file " << fn << ":\n " << line << "\n";
3030 return false;
3031 }
3032
3033 // Restore the line counter.
3034 write_line_directive = true;
3035 had_include = true;
3036 continue;
3037
3038 } else if (ext_google_line > 0 && strcmp(directive, "line") == 0) {
3039 // It's a #line directive. See if it uses a string instead of number.
3040 char filestr[2048];
3041 if (sscanf(line.c_str(), " # line%*[ \t]%d%*[ \t]\"%2047[^\"]\" %zn", &lineno, filestr, &nread) == 2
3042 && nread == line.size()) {
3043 // Warn about extension use if requested.
3044 if (ext_google_line == 1) {
3045 shader_cat.warning()
3046 << "Extension GL_GOOGLE_cpp_style_line_directive is being used at line "
3047 << lineno << " of file " << fn
3048 #ifndef NDEBUG
3049 << ":\n " << line
3050 #endif
3051 << "\n";
3052 }
3053
3054 // Replace the string line number with an integer. This is something
3055 // we can substitute later when parsing the GLSL log from the driver.
3056 fileno = 2048 + _included_files.size();
3057 _included_files.push_back(Filename(filestr));
3058
3059 out << "#line " << lineno << " " << fileno << " // " << filestr << "\n";
3060 continue;
3061 }
3062 }
3063
3064 if (write_line_directive) {
3065 out << "#line " << lineno << " " << fileno << " // " << fn << "\n";
3066 write_line_directive = false;
3067 }
3068 out << line << "\n";
3069 }
3070
3071 if (fileno == 0 && !had_version) {
3072 shader_cat.warning()
3073 << "GLSL shader " << fn << " does not contain a #version line!\n";
3074 }
3075
3076 return true;
3077 }
3078
3079 /**
3080 * Checks whether the shader or any of its dependent files were modified on
3081 * disk.
3082 */
3083 bool Shader::
3084 check_modified() const {
3085 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
3086
3087 pvector<Filename>::const_iterator it;
3088 for (it = _source_files.begin(); it != _source_files.end(); ++it) {
3089 const Filename &fn = (*it);
3090
3091 PT(VirtualFile) vfile = vfs->get_file(fn, true);
3092 if (vfile == nullptr || vfile->get_timestamp() > _last_modified) {
3093 return true;
3094 }
3095 }
3096
3097 return false;
3098 }
3099
3100 #ifdef HAVE_CG
3101 /**
3102 * Determines the appropriate active shader profile settings based on any
3103 * profile directives stored within the shader header
3104 */
3105 void Shader::
3106 cg_get_profile_from_header(ShaderCaps& caps) {
3107 // Note this forces profile based on what is specified in the shader header
3108 // string. Should probably be relying on card caps eventually.
3109
3110 string buf;
3111 parse_init();
3112
3113 // Assume that if parse doesn't extend after a parse line then we've reached
3114 // the end of _text
3115 int lastParse;
3116
3117 do {
3118 lastParse = _parse;
3119 parse_line(buf, true, true);
3120 int profilePos = buf.find("//Cg profile");
3121 if (profilePos >= 0) {
3122 // Scan the line for known cg2 vertex program profiles
3123 if ((int)buf.find("gp4vp") >= 0)
3124 caps._active_vprofile = cgGetProfile("gp4vp");
3125
3126 if ((int)buf.find("gp5vp") >= 0)
3127 caps._active_vprofile = cgGetProfile("gp5vp");
3128
3129 if ((int)buf.find("glslv") >= 0)
3130 caps._active_vprofile = cgGetProfile("glslv");
3131
3132 if ((int)buf.find("arbvp1") >= 0)
3133 caps._active_vprofile = cgGetProfile("arbvp1");
3134
3135 if ((int)buf.find("vp40") >= 0)
3136 caps._active_vprofile = cgGetProfile("vp40");
3137
3138 if ((int)buf.find("vp30") >= 0)
3139 caps._active_vprofile = cgGetProfile("vp30");
3140
3141 if ((int)buf.find("vp20") >= 0)
3142 caps._active_vprofile = cgGetProfile("vp20");
3143
3144 if ((int)buf.find("vs_1_1") >= 0)
3145 caps._active_vprofile = cgGetProfile("vs_1_1");
3146
3147 if ((int)buf.find("vs_2_0") >= 0)
3148 caps._active_vprofile = cgGetProfile("vs_2_0");
3149
3150 if ((int)buf.find("vs_2_x") >= 0)
3151 caps._active_vprofile = cgGetProfile("vs_2_x");
3152
3153 if ((int)buf.find("vs_3_0") >= 0)
3154 caps._active_vprofile = cgGetProfile("vs_3_0");
3155
3156 if ((int)buf.find("vs_4_0") >= 0)
3157 caps._active_vprofile = cgGetProfile("vs_4_0");
3158
3159 if ((int)buf.find("vs_5_0") >= 0)
3160 caps._active_vprofile = cgGetProfile("vs_5_0");
3161
3162 // Scan the line for known cg2 fragment program profiles
3163 if ((int)buf.find("gp4fp") >= 0)
3164 caps._active_fprofile = cgGetProfile("gp4fp");
3165
3166 if ((int)buf.find("gp5fp") >= 0)
3167 caps._active_fprofile = cgGetProfile("gp5fp");
3168
3169 if ((int)buf.find("glslf") >= 0)
3170 caps._active_fprofile = cgGetProfile("glslf");
3171
3172 if ((int)buf.find("arbfp1") >= 0)
3173 caps._active_fprofile = cgGetProfile("arbfp1");
3174
3175 if ((int)buf.find("fp40") >= 0)
3176 caps._active_fprofile = cgGetProfile("fp40");
3177
3178 if ((int)buf.find("fp30") >= 0)
3179 caps._active_fprofile = cgGetProfile("fp30");
3180
3181 if ((int)buf.find("fp20") >= 0)
3182 caps._active_fprofile = cgGetProfile("fp20");
3183
3184 if ((int)buf.find("ps_1_1") >= 0)
3185 caps._active_fprofile = cgGetProfile("ps_1_1");
3186
3187 if ((int)buf.find("ps_1_2") >= 0)
3188 caps._active_fprofile = cgGetProfile("ps_1_2");
3189
3190 if ((int)buf.find("ps_1_3") >= 0)
3191 caps._active_fprofile = cgGetProfile("ps_1_3");
3192
3193 if ((int)buf.find("ps_2_0") >= 0)
3194 caps._active_fprofile = cgGetProfile("ps_2_0");
3195
3196 if ((int)buf.find("ps_2_x") >= 0)
3197 caps._active_fprofile = cgGetProfile("ps_2_x");
3198
3199 if ((int)buf.find("ps_3_0") >= 0)
3200 caps._active_fprofile = cgGetProfile("ps_3_0");
3201
3202 if ((int)buf.find("ps_4_0") >= 0)
3203 caps._active_fprofile = cgGetProfile("ps_4_0");
3204
3205 if ((int)buf.find("ps_5_0") >= 0)
3206 caps._active_fprofile = cgGetProfile("ps_5_0");
3207
3208 // Scan the line for known cg2 geometry program profiles
3209 if ((int)buf.find("gp4gp") >= 0)
3210 caps._active_gprofile = cgGetProfile("gp4gp");
3211
3212 if ((int)buf.find("gp5gp") >= 0)
3213 caps._active_gprofile = cgGetProfile("gp5gp");
3214
3215 if ((int)buf.find("glslg") >= 0)
3216 caps._active_gprofile = cgGetProfile("glslg");
3217
3218 if ((int)buf.find("gs_4_0") >= 0)
3219 caps._active_gprofile = cgGetProfile("gs_4_0");
3220
3221 if ((int)buf.find("gs_5_0") >= 0)
3222 caps._active_gprofile = cgGetProfile("gs_5_0");
3223 }
3224 } while(_parse > lastParse);
3225
3226 }
3227 #endif
3228
3229 /**
3230 * Delete the compiled code, if it exists.
3231 */
3232 Shader::
3233 ~Shader() {
3234 release_all();
3235 // Note: don't try to erase ourselves from the table. It currently keeps a
3236 // reference forever, and so the only place where this constructor is called
3237 // is in the destructor of the table itself.
3238 /*if (_loaded) {
3239 _load_table.erase(_filename);
3240 } else {
3241 _make_table.erase(_text);
3242 }*/
3243 }
3244
3245 /**
3246 * Returns the filename of the included shader with the given source file
3247 * index (as recorded in the #line statement in r_preprocess_source). We use
3248 * this to associate error messages with included files.
3249 */
3250 Filename Shader::
3251 get_filename_from_index(int index, ShaderType type) const {
3252 if (index == 0) {
3253 Filename fn = get_filename(type);
3254 if (!fn.empty()) {
3255 return fn;
3256 }
3257 } else if (glsl_preprocess && index >= 2048 &&
3258 (index - 2048) < (int)_included_files.size()) {
3259 return _included_files[(size_t)index - 2048];
3260 }
3261 // Must be a mistake. Quietly put back the integer.
3262 string str = format_string(index);
3263 return Filename(str);
3264 }
3265
3266 /**
3267 * Loads the shader with the given filename.
3268 */
3269 PT(Shader) Shader::
3270 load(const Filename &file, ShaderLanguage lang) {
3271 ShaderFile sfile(file);
3272 ShaderTable::const_iterator i = _load_table.find(sfile);
3273 if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3274 // But check that someone hasn't modified it in the meantime.
3275 if (i->second->check_modified()) {
3276 shader_cat.info()
3277 << "Shader " << file << " was modified on disk, reloading.\n";
3278 } else {
3279 if (shader_cat.is_debug()) {
3280 shader_cat.debug()
3281 << "Shader " << file << " was found in shader cache.\n";
3282 }
3283 return i->second;
3284 }
3285 }
3286
3287 PT(Shader) shader = new Shader(lang);
3288 if (!shader->read(sfile)) {
3289 return nullptr;
3290 }
3291
3292 _load_table[sfile] = shader;
3293
3294 if (cache_generated_shaders) {
3295 ShaderTable::const_iterator i = _make_table.find(shader->_text);
3296 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3297 return i->second;
3298 }
3299 _make_table[shader->_text] = shader;
3300 }
3301 return shader;
3302 }
3303
3304 /**
3305 * This variant of Shader::load loads all shader programs separately.
3306 */
3307 PT(Shader) Shader::
3308 load(ShaderLanguage lang, const Filename &vertex,
3309 const Filename &fragment, const Filename &geometry,
3310 const Filename &tess_control, const Filename &tess_evaluation) {
3311 ShaderFile sfile(vertex, fragment, geometry, tess_control, tess_evaluation);
3312 ShaderTable::const_iterator i = _load_table.find(sfile);
3313 if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3314 // But check that someone hasn't modified it in the meantime.
3315 if (i->second->check_modified()) {
3316 shader_cat.info()
3317 << "Shader was modified on disk, reloading.\n";
3318 } else {
3319 if (shader_cat.is_debug()) {
3320 shader_cat.debug()
3321 << "Shader was found in shader cache.\n";
3322 }
3323 return i->second;
3324 }
3325 }
3326
3327 PT(Shader) shader = new Shader(lang);
3328 if (!shader->read(sfile)) {
3329 return nullptr;
3330 }
3331
3332 _load_table[sfile] = shader;
3333
3334 if (cache_generated_shaders) {
3335 ShaderTable::const_iterator i = _make_table.find(shader->_text);
3336 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3337 return i->second;
3338 }
3339 _make_table[shader->_text] = shader;
3340 }
3341 return shader;
3342 }
3343
3344 /**
3345 * Loads a compute shader.
3346 */
3347 PT(Shader) Shader::
3348 load_compute(ShaderLanguage lang, const Filename &fn) {
3349 if (lang != SL_GLSL) {
3350 shader_cat.error()
3351 << "Only GLSL compute shaders are currently supported.\n";
3352 return nullptr;
3353 }
3354
3355 Filename fullpath(fn);
3356 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
3357 if (!vfs->resolve_filename(fullpath, get_model_path())) {
3358 shader_cat.error()
3359 << "Could not find compute shader file: " << fn << "\n";
3360 return nullptr;
3361 }
3362
3363 ShaderFile sfile;
3364 sfile._separate = true;
3365 sfile._compute = fn;
3366
3367 ShaderTable::const_iterator i = _load_table.find(sfile);
3368 if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3369 // But check that someone hasn't modified it in the meantime.
3370 if (i->second->check_modified()) {
3371 shader_cat.info()
3372 << "Compute shader " << fn << " was modified on disk, reloading.\n";
3373 } else {
3374 if (shader_cat.is_debug()) {
3375 shader_cat.debug()
3376 << "Compute shader " << fn << " was found in shader cache.\n";
3377 }
3378 return i->second;
3379 }
3380 }
3381
3382 BamCache *cache = BamCache::get_global_ptr();
3383 PT(BamCacheRecord) record = cache->lookup(fullpath, "sho");
3384 if (record != nullptr) {
3385 if (record->has_data()) {
3386 shader_cat.info()
3387 << "Compute shader " << fn << " was found in disk cache.\n";
3388
3389 return DCAST(Shader, record->get_data());
3390 }
3391 }
3392
3393 PT(Shader) shader = new Shader(lang);
3394
3395 if (!shader->read(sfile, record)) {
3396 return nullptr;
3397 }
3398 _load_table[sfile] = shader;
3399
3400 if (cache_generated_shaders) {
3401 ShaderTable::const_iterator i = _make_table.find(shader->_text);
3402 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3403 return i->second;
3404 }
3405 _make_table[shader->_text] = shader;
3406 }
3407
3408 // It makes little sense to cache the shader before compilation, so we keep
3409 // the record for when we have the compiled the shader.
3410 std::swap(shader->_record, record);
3411 shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders();
3412 shader->_fullpath = shader->_source_files[0];
3413 return shader;
3414 }
3415
3416 /**
3417 * Loads the shader, using the string as shader body.
3418 */
3419 PT(Shader) Shader::
3420 make(string body, ShaderLanguage lang) {
3421 if (lang == SL_GLSL) {
3422 shader_cat.error()
3423 << "GLSL shaders must have separate shader bodies!\n";
3424 return nullptr;
3425
3426 } else if (lang == SL_none) {
3427 shader_cat.warning()
3428 << "Shader::make() now requires an explicit shader language. Assuming Cg.\n";
3429 lang = SL_Cg;
3430 }
3431 #ifndef HAVE_CG
3432 if (lang == SL_Cg) {
3433 shader_cat.error() << "Support for Cg shaders is not enabled.\n";
3434 return nullptr;
3435 }
3436 #endif
3437
3438 ShaderFile sbody(move(body));
3439
3440 if (cache_generated_shaders) {
3441 ShaderTable::const_iterator i = _make_table.find(sbody);
3442 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3443 // But check that someone hasn't modified its includes in the meantime.
3444 if (!i->second->check_modified()) {
3445 return i->second;
3446 }
3447 }
3448 }
3449
3450 PT(Shader) shader = new Shader(lang);
3451 if (!shader->load(sbody)) {
3452 return nullptr;
3453 }
3454
3455 if (cache_generated_shaders) {
3456 ShaderTable::const_iterator i = _make_table.find(shader->_text);
3457 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3458 shader = i->second;
3459 } else {
3460 _make_table[shader->_text] = shader;
3461 }
3462 _make_table[std::move(sbody)] = shader;
3463 }
3464
3465 if (dump_generated_shaders) {
3466 ostringstream fns;
3467 int index = _shaders_generated ++;
3468 fns << "genshader" << index;
3469 string fn = fns.str();
3470 shader_cat.warning() << "Dumping shader: " << fn << "\n";
3471
3472 pofstream s;
3473 s.open(fn.c_str(), std::ios::out | std::ios::trunc);
3474 s << shader->get_text();
3475 s.close();
3476 }
3477 return shader;
3478 }
3479
3480 /**
3481 * Loads the shader, using the strings as shader bodies.
3482 */
3483 PT(Shader) Shader::
3484 make(ShaderLanguage lang, string vertex, string fragment, string geometry,
3485 string tess_control, string tess_evaluation) {
3486 #ifndef HAVE_CG
3487 if (lang == SL_Cg) {
3488 shader_cat.error() << "Support for Cg shaders is not enabled.\n";
3489 return nullptr;
3490 }
3491 #endif
3492 if (lang == SL_none) {
3493 shader_cat.error()
3494 << "Shader::make() requires an explicit shader language.\n";
3495 return nullptr;
3496 }
3497
3498 ShaderFile sbody(move(vertex), move(fragment), move(geometry),
3499 move(tess_control), move(tess_evaluation));
3500
3501 if (cache_generated_shaders) {
3502 ShaderTable::const_iterator i = _make_table.find(sbody);
3503 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3504 // But check that someone hasn't modified its includes in the meantime.
3505 if (!i->second->check_modified()) {
3506 return i->second;
3507 }
3508 }
3509 }
3510
3511 PT(Shader) shader = new Shader(lang);
3512 shader->_filename = ShaderFile("created-shader");
3513 if (!shader->load(sbody)) {
3514 return nullptr;
3515 }
3516
3517 if (cache_generated_shaders) {
3518 ShaderTable::const_iterator i = _make_table.find(shader->_text);
3519 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3520 shader = i->second;
3521 } else {
3522 _make_table[shader->_text] = shader;
3523 }
3524 _make_table[std::move(sbody)] = shader;
3525 }
3526
3527 return shader;
3528 }
3529
3530 /**
3531 * Loads the compute shader from the given string.
3532 */
3533 PT(Shader) Shader::
3534 make_compute(ShaderLanguage lang, string body) {
3535 if (lang != SL_GLSL) {
3536 shader_cat.error()
3537 << "Only GLSL compute shaders are currently supported.\n";
3538 return nullptr;
3539 }
3540
3541 ShaderFile sbody;
3542 sbody._separate = true;
3543 sbody._compute = move(body);
3544
3545 if (cache_generated_shaders) {
3546 ShaderTable::const_iterator i = _make_table.find(sbody);
3547 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3548 // But check that someone hasn't modified its includes in the meantime.
3549 if (!i->second->check_modified()) {
3550 return i->second;
3551 }
3552 }
3553 }
3554
3555 PT(Shader) shader = new Shader(lang);
3556 shader->_filename = ShaderFile("created-shader");
3557 if (!shader->load(sbody)) {
3558 return nullptr;
3559 }
3560
3561 if (cache_generated_shaders) {
3562 ShaderTable::const_iterator i = _make_table.find(shader->_text);
3563 if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3564 shader = i->second;
3565 } else {
3566 _make_table[shader->_text] = shader;
3567 }
3568 _make_table[std::move(sbody)] = shader;
3569 }
3570
3571 return shader;
3572 }
3573
3574 /**
3575 * Set a 'parse pointer' to the beginning of the shader.
3576 */
3577 void Shader::
3578 parse_init() {
3579 _parse = 0;
3580 }
3581
3582 /**
3583 * Parse a line of text. If 'lt' is true, trim blanks from the left end of
3584 * the line. If 'rt' is true, trim blanks from the right end (the newline is
3585 * always trimmed).
3586 */
3587 void Shader::
3588 parse_line(string &result, bool lt, bool rt) {
3589 nassertv(!_text._separate);
3590 int len = _text._shared.size();
3591 int head = _parse;
3592 int tail = head;
3593 while ((tail < len) && (_text._shared[tail] != '\n')) {
3594 tail++;
3595 }
3596 if (tail < len) {
3597 _parse = tail+1;
3598 } else {
3599 _parse = tail;
3600 }
3601 if (lt) {
3602 while ((head < tail)&&(isspace(_text._shared[head]))) head++;
3603 while ((tail > head)&&(isspace(_text._shared[tail-1]))) tail--;
3604 }
3605 result = _text._shared.substr(head, tail-head);
3606 }
3607
3608 /**
3609 * Parse lines until you read a line that matches the specified pattern.
3610 * Returns all the preceding lines, and if the include flag is set, returns
3611 * the final line as well.
3612 */
3613 void Shader::
3614 parse_upto(string &result, string pattern, bool include) {
3615 nassertv(!_text._separate);
3616 GlobPattern endpat(pattern);
3617 int start = _parse;
3618 int last = _parse;
3619 while (_parse < (int)(_text._shared.size())) {
3620 string t;
3621 parse_line(t, true, true);
3622 if (endpat.matches(t)) break;
3623 last = _parse;
3624 }
3625 if (include) {
3626 result = _text._shared.substr(start, _parse - start);
3627 } else {
3628 result = _text._shared.substr(start, last - start);
3629 }
3630 }
3631
3632 /**
3633 * Returns the rest of the text from the current parse location.
3634 */
3635 void Shader::
3636 parse_rest(string &result) {
3637 nassertv(!_text._separate);
3638 result = _text._shared.substr(_parse, _text._shared.size() - _parse);
3639 }
3640
3641 /**
3642 * Returns true if the parse pointer is at the end of the shader.
3643 */
3644 bool Shader::
3645 parse_eof() {
3646 return (int)_text._shared.size() == _parse;
3647 }
3648
3649 /**
3650 * Indicates that the shader should be enqueued to be prepared in the
3651 * indicated prepared_objects at the beginning of the next frame. This will
3652 * ensure the texture is already loaded into texture memory if it is expected
3653 * to be rendered soon.
3654 *
3655 * Use this function instead of prepare_now() to preload textures from a user
3656 * interface standpoint.
3657 */
3658 PT(AsyncFuture) Shader::
3659 prepare(PreparedGraphicsObjects *prepared_objects) {
3660 return prepared_objects->enqueue_shader_future(this);
3661 }
3662
3663 /**
3664 * Returns true if the shader has already been prepared or enqueued for
3665 * preparation on the indicated GSG, false otherwise.
3666 */
3667 bool Shader::
3668 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
3669 Contexts::const_iterator ci;
3670 ci = _contexts.find(prepared_objects);
3671 if (ci != _contexts.end()) {
3672 return true;
3673 }
3674 return prepared_objects->is_shader_queued(this);
3675 }
3676
3677 /**
3678 * Frees the texture context only on the indicated object, if it exists there.
3679 * Returns true if it was released, false if it had not been prepared.
3680 */
3681 bool Shader::
3682 release(PreparedGraphicsObjects *prepared_objects) {
3683 Contexts::iterator ci;
3684 ci = _contexts.find(prepared_objects);
3685 if (ci != _contexts.end()) {
3686 ShaderContext *sc = (*ci).second;
3687 if (sc != nullptr) {
3688 prepared_objects->release_shader(sc);
3689 } else {
3690 _contexts.erase(ci);
3691 }
3692 return true;
3693 }
3694
3695 // Maybe it wasn't prepared yet, but it's about to be.
3696 return prepared_objects->dequeue_shader(this);
3697 }
3698
3699 /**
3700 * Creates a context for the shader on the particular GSG, if it does not
3701 * already exist. Returns the new (or old) ShaderContext. This assumes that
3702 * the GraphicsStateGuardian is the currently active rendering context and
3703 * that it is ready to accept new textures. If this is not necessarily the
3704 * case, you should use prepare() instead.
3705 *
3706 * Normally, this is not called directly except by the GraphicsStateGuardian;
3707 * a shader does not need to be explicitly prepared by the user before it may
3708 * be rendered.
3709 */
3710 ShaderContext *Shader::
3711 prepare_now(PreparedGraphicsObjects *prepared_objects,
3712 GraphicsStateGuardianBase *gsg) {
3713 Contexts::const_iterator ci;
3714 ci = _contexts.find(prepared_objects);
3715 if (ci != _contexts.end()) {
3716 return (*ci).second;
3717 }
3718
3719 ShaderContext *tc = prepared_objects->prepare_shader_now(this, gsg);
3720 _contexts[prepared_objects] = tc;
3721
3722 return tc;
3723 }
3724
3725 /**
3726 * Removes the indicated PreparedGraphicsObjects table from the Shader's
3727 * table, without actually releasing the texture. This is intended to be
3728 * called only from PreparedGraphicsObjects::release_texture(); it should
3729 * never be called by user code.
3730 */
3731 void Shader::
3732 clear_prepared(PreparedGraphicsObjects *prepared_objects) {
3733 Contexts::iterator ci;
3734 ci = _contexts.find(prepared_objects);
3735 if (ci != _contexts.end()) {
3736 _contexts.erase(ci);
3737 } else {
3738 // If this assertion fails, clear_prepared() was given a prepared_objects
3739 // which the texture didn't know about.
3740 nassert_raise("unknown PreparedGraphicsObjects");
3741 }
3742 }
3743
3744 /**
3745 * Frees the context allocated on all objects for which the texture has been
3746 * declared. Returns the number of contexts which have been freed.
3747 */
3748 int Shader::
3749 release_all() {
3750 // We have to traverse a copy of the _contexts list, because the
3751 // PreparedGraphicsObjects object will call clear_prepared() in response to
3752 // each release_texture(), and we don't want to be modifying the _contexts
3753 // list while we're traversing it.
3754 Contexts temp = _contexts;
3755 int num_freed = (int)_contexts.size();
3756
3757 Contexts::const_iterator ci;
3758 for (ci = temp.begin(); ci != temp.end(); ++ci) {
3759 PreparedGraphicsObjects *prepared_objects = (*ci).first;
3760 ShaderContext *sc = (*ci).second;
3761 if (sc != nullptr) {
3762 prepared_objects->release_shader(sc);
3763 }
3764 }
3765
3766 // There might still be some outstanding contexts in the map, if there were
3767 // any NULL pointers there. Eliminate them.
3768 _contexts.clear();
3769
3770 return num_freed;
3771 }
3772
3773 /**
3774 *
3775 */
3776 void Shader::ShaderCaps::
3777 clear() {
3778 _supports_glsl = false;
3779
3780 #ifdef HAVE_CG
3781 _active_vprofile = CG_PROFILE_UNKNOWN;
3782 _active_fprofile = CG_PROFILE_UNKNOWN;
3783 _active_gprofile = CG_PROFILE_UNKNOWN;
3784 _ultimate_vprofile = CG_PROFILE_UNKNOWN;
3785 _ultimate_fprofile = CG_PROFILE_UNKNOWN;
3786 _ultimate_gprofile = CG_PROFILE_UNKNOWN;
3787 #endif
3788 }
3789
3790 /**
3791 * Tells the BamReader how to create objects of type Shader.
3792 */
3793 void Shader::
3794 register_with_read_factory() {
3795 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
3796 }
3797
3798 /**
3799 * Writes the contents of this object to the datagram for shipping out to a
3800 * Bam file.
3801 */
3802 void Shader::
3803 write_datagram(BamWriter *manager, Datagram &dg) {
3804 dg.add_uint8(_language);
3805 dg.add_bool(_loaded);
3806 _filename.write_datagram(dg);
3807 _text.write_datagram(dg);
3808
3809 dg.add_uint32(_compiled_format);
3810 dg.add_string(_compiled_binary);
3811 }
3812
3813 /**
3814 * This function is called by the BamReader's factory when a new object of
3815 * type Shader is encountered in the Bam file. It should create the Shader
3816 * and extract its information from the file.
3817 */
3818 TypedWritable *Shader::
3819 make_from_bam(const FactoryParams &params) {
3820 Shader *attrib = new Shader(SL_none);
3821 DatagramIterator scan;
3822 BamReader *manager;
3823
3824 parse_params(params, scan, manager);
3825 attrib->fillin(scan, manager);
3826 return attrib;
3827 }
3828
3829 /**
3830 * This internal function is called by make_from_bam to read in all of the
3831 * relevant data from the BamFile for the new Shader.
3832 */
3833 void Shader::
3834 fillin(DatagramIterator &scan, BamReader *manager) {
3835 _language = (ShaderLanguage) scan.get_uint8();
3836 _loaded = scan.get_bool();
3837 _filename.read_datagram(scan);
3838 _text.read_datagram(scan);
3839
3840 _compiled_format = scan.get_uint32();
3841 _compiled_binary = scan.get_string();
3842 }
bamCache.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
AsyncFuture
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
Definition: asyncFuture.h:61
BamCacheRecord
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
Definition: bamCacheRecord.h:36
BamCacheRecord::add_dependent_file
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record.
Definition: bamCacheRecord.cxx:147
BamCacheRecord::get_data
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
Definition: bamCacheRecord.h:77
BamCache
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
BamCache::store
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:194
BamCache::get_global_ptr
static BamCache * get_global_ptr()
Returns a pointer to the global BamCache object, which is used automatically by the ModelPool and Tex...
Definition: bamCache.I:223
BamCache::get_cache_compiled_shaders
get_cache_compiled_shaders
Returns whether compiled shader programs will be stored in the cache, as binary .txo files.
Definition: bamCache.h:94
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
DSearchPath
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
DatagramIterator::get_uint8
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
Definition: datagramIterator.I:72
DatagramIterator::get_uint32
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: datagramIterator.I:164
DatagramIterator::get_bool
bool get_bool()
Extracts a boolean value.
Definition: datagramIterator.I:48
DatagramIterator::get_string
std::string get_string()
Extracts a variable-length string.
Definition: datagramIterator.cxx:26
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Datagram::add_uint32
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
Datagram::add_uint8
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
Datagram::add_bool
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
Datagram::add_string
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Filename::get_dirname
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
GlobPattern
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
GlobPattern::matches
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
GraphicsStateGuardianBase
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Definition: graphicsStateGuardianBase.h:110
InternalName
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
NotifyCategory::error
std::ostream & error(bool prefix=true) const
A shorthand way to write out(NS_error).
Definition: notifyCategory.I:169
PreparedGraphicsObjects
A table of objects that are saved within the graphics context for reference by handle later.
Definition: preparedGraphicsObjects.h:58
PreparedGraphicsObjects::is_shader_queued
bool is_shader_queued(const Shader *shader) const
Returns true if the shader has been queued on this GSG, false otherwise.
Definition: preparedGraphicsObjects.cxx:730
PreparedGraphicsObjects::dequeue_shader
bool dequeue_shader(Shader *shader)
Removes a shader from the queued list of shaders to be prepared.
Definition: preparedGraphicsObjects.cxx:747
PreparedGraphicsObjects::release_shader
void release_shader(ShaderContext *sc)
Indicates that a shader context, created by a previous call to prepare_shader(), is no longer needed.
Definition: preparedGraphicsObjects.cxx:779
PreparedGraphicsObjects::prepare_shader_now
ShaderContext * prepare_shader_now(Shader *shader, GraphicsStateGuardianBase *gsg)
Immediately creates a new ShaderContext for the indicated shader and returns it.
Definition: preparedGraphicsObjects.cxx:867
ShaderContext
The ShaderContext is meant to contain the compiled version of a shader string.
Definition: shaderContext.h:31
Shader::ShaderCaps
Definition: shader.h:450
Shader::ShaderFile::read_datagram
void read_datagram(DatagramIterator &source)
Reads the object from a Datagram.
Definition: shader.I:742
Shader::ShaderFile::write_datagram
void write_datagram(Datagram &dg) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: shader.I:723
Shader
Definition: shader.h:49
Shader::cp_errchk_parameter_varying
bool cp_errchk_parameter_varying(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:152
Shader::cp_parse_eol
bool cp_parse_eol(ShaderArgInfo &arg, vector_string &pieces, int &next)
Make sure the next thing on the word list is EOL.
Definition: shader.cxx:250
Shader::cp_optimize_mat_spec
void cp_optimize_mat_spec(ShaderMatSpec &spec)
Analyzes a ShaderMatSpec and decides what it should use its cache for.
Definition: shader.cxx:508
Shader::get_compiled
bool get_compiled(unsigned int &format, std::string &binary) const
Called by the back-end to retrieve compiled data.
Definition: shader.cxx:1586
Shader::cp_parse_delimiter
bool cp_parse_delimiter(ShaderArgInfo &arg, vector_string &pieces, int &next)
Pop a delimiter ('to' or 'rel') from the word list.
Definition: shader.cxx:262
Shader::get_text
const std::string & get_text(ShaderType type=ST_none) const
Return the Shader's text for the given shader type.
Definition: shader.I:89
Shader::cp_report_error
void cp_report_error(ShaderArgInfo &arg, const std::string &msg)
Generate an error message including a description of the specified parameter.
Definition: shader.cxx:51
Shader::parse_init
void parse_init()
Set a 'parse pointer' to the beginning of the shader.
Definition: shader.cxx:3578
Shader::set_compiled
void set_compiled(unsigned int format, const char *data, size_t length)
Called by the back-end when the shader has compiled data available.
Definition: shader.cxx:1569
Shader::prepare_now
ShaderContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the shader on the particular GSG, if it does not already exist.
Definition: shader.cxx:3711
Shader::compile_parameter
bool compile_parameter(ShaderArgInfo &p, int *arg_dim)
Analyzes a parameter and decides how to bind the parameter to some part of panda's internal state.
Definition: shader.cxx:668
Shader::release_all
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: shader.cxx:3749
Shader::cp_errchk_parameter_in
bool cp_errchk_parameter_in(ShaderArgInfo &arg)
Make sure the provided parameter has the 'in' direction.
Definition: shader.cxx:138
Shader::cp_errchk_parameter_uniform
bool cp_errchk_parameter_uniform(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:166
Shader::cp_parse_coord_sys
bool cp_parse_coord_sys(ShaderArgInfo &arg, vector_string &pieces, int &next, ShaderMatSpec &spec, bool fromflag)
Convert a single-word coordinate system name into a PART/ARG of a ShaderMatSpec.
Definition: shader.cxx:290
Shader::set_default_caps
static void set_default_caps(const ShaderCaps &caps)
Called by the graphics back-end to specify the caps with which we will likely want to be compiling ou...
Definition: shader.cxx:1597
Shader::get_filename_from_index
Filename get_filename_from_index(int index, ShaderType type) const
Returns the filename of the included shader with the given source file index (as recorded in the #lin...
Definition: shader.cxx:3251
Shader::cp_parse_non_delimiter
std::string cp_parse_non_delimiter(vector_string &pieces, int &next)
Pop a non-delimiter word from the word list.
Definition: shader.cxx:276
Shader::parse_line
void parse_line(std::string &result, bool rt, bool lt)
Parse a line of text.
Definition: shader.cxx:3588
Shader::parse_rest
void parse_rest(std::string &result)
Returns the rest of the text from the current parse location.
Definition: shader.cxx:3636
Shader::is_prepared
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the shader has already been prepared or enqueued for preparation on the indicated GSG...
Definition: shader.cxx:3668
Shader::cp_errchk_parameter_float
bool cp_errchk_parameter_float(ShaderArgInfo &arg, int lo, int hi)
Make sure the provided parameter has a floating point type.
Definition: shader.cxx:180
Shader::parse_upto
void parse_upto(std::string &result, std::string pattern, bool include)
Parse lines until you read a line that matches the specified pattern.
Definition: shader.cxx:3614
Shader::register_with_read_factory
static void register_with_read_factory()
Tells the BamReader how to create objects of type Shader.
Definition: shader.cxx:3794
Shader::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: shader.cxx:3803
Shader::cp_errchk_parameter_words
bool cp_errchk_parameter_words(ShaderArgInfo &arg, int len)
Make sure the provided parameter contains the specified number of words.
Definition: shader.cxx:122
Shader::cp_errchk_parameter_sampler
bool cp_errchk_parameter_sampler(ShaderArgInfo &arg)
Make sure the provided parameter has a texture type.
Definition: shader.cxx:231
Shader::parse_eof
bool parse_eof()
Returns true if the parse pointer is at the end of the shader.
Definition: shader.cxx:3645
Shader::get_filename
Filename get_filename(ShaderType type=ST_none) const
Return the Shader's filename for the given shader type.
Definition: shader.I:20
Shader::cp_dependency
int cp_dependency(ShaderMatInput inp)
Given ShaderMatInput, returns an indication of what part or parts of the state_and_transform the Shad...
Definition: shader.cxx:377
Shader::~Shader
~Shader()
Delete the compiled code, if it exists.
Definition: shader.cxx:3233
Shader::release
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: shader.cxx:3682
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
VirtualFileSystem::find_file
PointerTo< VirtualFile > find_file(const Filename &filename, const DSearchPath &searchpath, bool status_only=false) const
Uses the indicated search path to find the file within the file system.
Definition: virtualFileSystem.cxx:543
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:640
VirtualFileSystem::get_file
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
Definition: virtualFileSystem.cxx:515
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
preparedGraphicsObjects.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT
PT(Shader) Shader
Loads the shader with the given filename.
Definition: shader.cxx:3269
shader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
tokenize
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
Definition: string_utils.cxx:170
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Shader::ShaderArgInfo
Definition: shader.h:352
Shader::ShaderMatSpec
Definition: shader.h:412
Shader::ShaderPtrSpec
Definition: shader.h:441
Shader::ShaderTexSpec
Definition: shader.h:424
Shader::ShaderVarSpec
Definition: shader.h:433
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.