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"
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::
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::
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::
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::
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::
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::
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::
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::
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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)) ||
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 
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::
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  cg_get_profile_from_header(_default_caps);
2465 
2466  if (!cg_analyze_shader(_default_caps)) {
2467  shader_cat.error()
2468  << "Shader encountered an error.\n";
2469  return false;
2470  }
2471 #else
2472  shader_cat.error()
2473  << "Tried to load Cg shader, but no Cg support is enabled.\n";
2474 #endif
2475  } else {
2476  shader_cat.error()
2477  << "Shader is not in a supported shader-language.\n";
2478  return false;
2479  }
2480  }
2481 
2482  _loaded = true;
2483  return true;
2484 }
2485 
2486 /**
2487  * Loads the shader from the given string(s). Returns a boolean indicating
2488  * success or failure.
2489  */
2490 bool Shader::
2491 load(const ShaderFile &sbody, BamCacheRecord *record) {
2492  _filename = ShaderFile("created-shader");
2493  _fullpath = Filename();
2494  _text._separate = sbody._separate;
2495 
2496  if (sbody._separate) {
2497  if (_language == SL_none) {
2498  shader_cat.error()
2499  << "No shader language was specified!\n";
2500  return false;
2501  }
2502 
2503  if (!sbody._vertex.empty() &&
2504  !do_load_source(_text._vertex, sbody._vertex, record)) {
2505  return false;
2506  }
2507  if (!sbody._fragment.empty() &&
2508  !do_load_source(_text._fragment, sbody._fragment, record)) {
2509  return false;
2510  }
2511  if (!sbody._geometry.empty() &&
2512  !do_load_source(_text._geometry, sbody._geometry, record)) {
2513  return false;
2514  }
2515  if (!sbody._tess_control.empty() &&
2516  !do_load_source(_text._tess_control, sbody._tess_control, record)) {
2517  return false;
2518  }
2519  if (!sbody._tess_evaluation.empty() &&
2520  !do_load_source(_text._tess_evaluation, sbody._tess_evaluation, record)) {
2521  return false;
2522  }
2523  if (!sbody._compute.empty() &&
2524  !do_load_source(_text._compute, sbody._compute, record)) {
2525  return false;
2526  }
2527 
2528  } else {
2529  if (!do_load_source(_text._shared, sbody._shared, record)) {
2530  return false;
2531  }
2532 
2533  // Determine which language the shader is written in.
2534  if (_language == SL_none) {
2535  string header;
2536  parse_init();
2537  parse_line(header, true, true);
2538  if (header == "//Cg") {
2539  _language = SL_Cg;
2540  } else {
2541  shader_cat.error()
2542  << "Unable to determine shader language of " << sbody._shared << "\n";
2543  return false;
2544  }
2545  } else if (_language == SL_GLSL) {
2546  shader_cat.error()
2547  << "GLSL shaders must have separate shader bodies!\n";
2548  return false;
2549  }
2550 
2551  // Determine which language the shader is written in.
2552  if (_language == SL_Cg) {
2553 #ifdef HAVE_CG
2554  cg_get_profile_from_header(_default_caps);
2555 
2556  if (!cg_analyze_shader(_default_caps)) {
2557  shader_cat.error()
2558  << "Shader encountered an error.\n";
2559  return false;
2560  }
2561 #else
2562  shader_cat.error()
2563  << "Tried to load Cg shader, but no Cg support is enabled.\n";
2564 #endif
2565  } else {
2566  shader_cat.error()
2567  << "Shader is not in a supported shader-language.\n";
2568  return false;
2569  }
2570  }
2571 
2572  _loaded = true;
2573  return true;
2574 }
2575 
2576 /**
2577  * Reads the shader file from the given path into the given string.
2578  *
2579  * Returns false if there was an error with this shader bad enough to consider
2580  * it 'invalid'.
2581  */
2582 bool Shader::
2583 do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
2585  PT(VirtualFile) vf = vfs->find_file(fn, get_model_path());
2586  if (vf == nullptr) {
2587  shader_cat.error()
2588  << "Could not find shader file: " << fn << "\n";
2589  return false;
2590  }
2591 
2592  if (_language == SL_GLSL && glsl_preprocess) {
2593  istream *source = vf->open_read_file(true);
2594  if (source == nullptr) {
2595  shader_cat.error()
2596  << "Could not open shader file: " << fn << "\n";
2597  return false;
2598  }
2599 
2600  // Preprocess the GLSL file as we read it.
2601  shader_cat.info()
2602  << "Preprocessing shader file: " << fn << "\n";
2603 
2604  std::set<Filename> open_files;
2605  ostringstream sstr;
2606  if (!r_preprocess_source(sstr, *source, fn, vf->get_filename(), open_files, record)) {
2607  vf->close_read_file(source);
2608  return false;
2609  }
2610  vf->close_read_file(source);
2611  into = sstr.str();
2612 
2613  } else {
2614  shader_cat.info() << "Reading shader file: " << fn << "\n";
2615 
2616  if (!vf->read_file(into, true)) {
2617  shader_cat.error()
2618  << "Could not read shader file: " << fn << "\n";
2619  return false;
2620  }
2621  }
2622 
2623  if (record != nullptr) {
2624  record->add_dependent_file(vf);
2625  }
2626 
2627  _last_modified = std::max(_last_modified, vf->get_timestamp());
2628  _source_files.push_back(vf->get_filename());
2629 
2630  // Strip trailing whitespace.
2631  while (!into.empty() && isspace(into[into.size() - 1])) {
2632  into.resize(into.size() - 1);
2633  }
2634 
2635  // Except add back a newline at the end, which is needed by Intel drivers.
2636  into += "\n";
2637 
2638  return true;
2639 }
2640 
2641 /**
2642  * Loads the shader file from the given string into the given string,
2643  * performing any pre-processing on it that may be necessary.
2644  *
2645  * Returns false if there was an error with this shader bad enough to consider
2646  * it 'invalid'.
2647  */
2648 bool Shader::
2649 do_load_source(string &into, const std::string &source, BamCacheRecord *record) {
2650  if (_language == SL_GLSL && glsl_preprocess) {
2651  // Preprocess the GLSL file as we read it.
2652  std::set<Filename> open_files;
2653  std::ostringstream sstr;
2654  std::istringstream in(source);
2655  if (!r_preprocess_source(sstr, in, Filename("created-shader"), Filename(),
2656  open_files, record)) {
2657  return false;
2658  }
2659  into = sstr.str();
2660 
2661  } else {
2662  into = source;
2663  }
2664 
2665  // Strip trailing whitespace.
2666  while (!into.empty() && isspace(into[into.size() - 1])) {
2667  into.resize(into.size() - 1);
2668  }
2669 
2670  // Except add back a newline at the end, which is needed by Intel drivers.
2671  into += "\n";
2672 
2673  return true;
2674 }
2675 
2676 /**
2677  * Loads a given GLSL file line by line, and processes any #pragma include and
2678  * once statements, as well as removes any comments.
2679  *
2680  * The set keeps track of which files we have already included, for checking
2681  * recursive includes.
2682  */
2683 bool Shader::
2684 r_preprocess_include(ostream &out, const Filename &fn,
2685  const Filename &source_dir,
2686  std::set<Filename> &once_files,
2687  BamCacheRecord *record, int depth) {
2688 
2689  if (depth > glsl_include_recursion_limit) {
2690  shader_cat.error()
2691  << "GLSL includes nested too deeply, raise glsl-include-recursion-limit"
2692  " if necessary\n";
2693  return false;
2694  }
2695 
2696  DSearchPath path(get_model_path());
2697  if (!source_dir.empty()) {
2698  path.prepend_directory(source_dir);
2699  }
2700 
2702  PT(VirtualFile) vf = vfs->find_file(fn, path);
2703  if (vf == nullptr) {
2704  shader_cat.error()
2705  << "Could not find shader include: " << fn << "\n";
2706  return false;
2707  }
2708 
2709  Filename full_fn = vf->get_filename();
2710  if (once_files.find(full_fn) != once_files.end()) {
2711  // If this file had a #pragma once, just move on.
2712  return true;
2713  }
2714 
2715  istream *source = vf->open_read_file(true);
2716  if (source == nullptr) {
2717  shader_cat.error()
2718  << "Could not open shader include: " << fn << "\n";
2719  return false;
2720  }
2721 
2722  if (record != nullptr) {
2723  record->add_dependent_file(vf);
2724  }
2725  _last_modified = std::max(_last_modified, vf->get_timestamp());
2726  _source_files.push_back(full_fn);
2727 
2728  // We give each file an unique index. This is so that we can identify a
2729  // particular shader in the error output. We offset them by 2048 so that
2730  // they are more recognizable. GLSL doesn't give us anything more useful
2731  // than that, unfortunately. Don't do this for the top-level file, though.
2732  // We don't want anything to get in before a potential #version directive.
2733  int fileno = 0;
2734  fileno = 2048 + _included_files.size();
2735 
2736  // Write it into the vector so that we can substitute it later when we are
2737  // parsing the GLSL error log. Don't store the full filename because it
2738  // would just be too long to display.
2739  _included_files.push_back(fn);
2740 
2741  if (shader_cat.is_debug()) {
2742  shader_cat.debug()
2743  << "Preprocessing shader include " << fileno << ": " << fn << "\n";
2744  }
2745 
2746  bool result = r_preprocess_source(out, *source, fn, full_fn, once_files, record, fileno, depth);
2747  vf->close_read_file(source);
2748  return result;
2749 }
2750 
2751 /**
2752  * Loads a given GLSL stream line by line, processing any #pragma include and
2753  * once statements, as well as removing any comments.
2754  *
2755  * The set keeps track of which files we have already included, for checking
2756  * recursive includes.
2757  */
2758 bool Shader::
2759 r_preprocess_source(ostream &out, istream &in, const Filename &fn,
2760  const Filename &full_fn, std::set<Filename> &once_files,
2761  BamCacheRecord *record, int fileno, int depth) {
2762 
2763  // Iterate over the lines for things we may need to preprocess.
2764  string line;
2765  int ext_google_include = 0; // 1 = warn, 2 = enable
2766  int ext_google_line = 0;
2767  bool had_include = false;
2768  bool had_version = false;
2769  int lineno = 0;
2770  bool write_line_directive = (fileno != 0);
2771 
2772  while (std::getline(in, line)) {
2773  ++lineno;
2774 
2775  if (line.empty()) {
2776  // We still write a newline to make sure the line numbering remains
2777  // consistent, unless we are about to write a #line directive anyway.
2778  if (!write_line_directive) {
2779  out.put('\n');
2780  }
2781  continue;
2782  }
2783 
2784  // If the line ends with a backslash, concatenate the following line.
2785  // Preprocessor definitions may be broken up into multiple lines.
2786  while (line[line.size() - 1] == '\\') {
2787  line.resize(line.size() - 1);
2788  string line2;
2789 
2790  if (std::getline(in, line2)) {
2791  line += line2;
2792  if (!write_line_directive) {
2793  out.put('\n');
2794  }
2795  ++lineno;
2796  } else {
2797  break;
2798  }
2799  }
2800 
2801  // Look for comments to strip. This is necessary because comments may
2802  // appear in the middle of or around a preprocessor definition.
2803  size_t line_comment = line.find("//");
2804  size_t block_comment = line.find("/*");
2805  if (line_comment < block_comment) {
2806  // A line comment - strip off the rest of the line.
2807  line.resize(line_comment);
2808 
2809  } else if (block_comment < line_comment) {
2810  // A block comment. Search for closing block.
2811  string line2 = line.substr(block_comment + 2);
2812 
2813  // According to the GLSL specification, a block comment is replaced with
2814  // a single whitespace character.
2815  line.resize(block_comment);
2816  line += ' ';
2817 
2818  size_t block_end = line2.find("*/");
2819  while (block_end == string::npos) {
2820  // Didn't find it - look in the next line.
2821  if (std::getline(in, line2)) {
2822  if (!write_line_directive) {
2823  out.put('\n');
2824  }
2825  ++lineno;
2826  block_end = line2.find("*/");
2827  } else {
2828  shader_cat.error()
2829  << "Expected */ before end of file " << fn << "\n";
2830  return false;
2831  }
2832  }
2833 
2834  line += line2.substr(block_end + 2);
2835  }
2836 
2837  // Strip trailing whitespace.
2838  while (!line.empty() && isspace(line[line.size() - 1])) {
2839  line.resize(line.size() - 1);
2840  }
2841 
2842  if (line.empty()) {
2843  if (!write_line_directive) {
2844  out.put('\n');
2845  }
2846  continue;
2847  }
2848 
2849  // Check if this line contains a #directive.
2850  char directive[64];
2851  if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) {
2852  // Nope. Just pass the line through unmodified.
2853  if (write_line_directive) {
2854  out << "#line " << lineno << " " << fileno << " // " << fn << "\n";
2855  write_line_directive = false;
2856  }
2857  out << line << "\n";
2858  continue;
2859  }
2860 
2861  char pragma[64];
2862  size_t nread = 0;
2863  // What kind of directive is it?
2864  if (strcmp(directive, "pragma") == 0 &&
2865  sscanf(line.c_str(), " # pragma %63s", pragma) == 1) {
2866  if (strcmp(pragma, "include") == 0) {
2867  // Allow both double quotes and angle brackets.
2868  Filename incfn, source_dir;
2869  {
2870  char incfile[2048];
2871  if (sscanf(line.c_str(), " # pragma%*[ \t]include \"%2047[^\"]\" %zn", incfile, &nread) == 1
2872  && nread == line.size()) {
2873  // A regular include, with double quotes. Probably a local file.
2874  source_dir = full_fn.get_dirname();
2875  incfn = incfile;
2876 
2877  } else if (sscanf(line.c_str(), " # pragma%*[ \t]include <%2047[^\"]> %zn", incfile, &nread) == 1
2878  && nread == line.size()) {
2879  // Angled includes are also OK, but we don't search in the directory
2880  // of the source file.
2881  incfn = incfile;
2882 
2883  } else {
2884  // Couldn't parse it.
2885  shader_cat.error()
2886  << "Malformed #pragma include at line " << lineno
2887  << " of file " << fn << ":\n " << line << "\n";
2888  return false;
2889  }
2890  }
2891 
2892  // OK, great. Process the include.
2893  if (!r_preprocess_include(out, incfn, source_dir, once_files, record, depth + 1)) {
2894  // An error occurred. Pass on the failure.
2895  shader_cat.error(false) << "included at line "
2896  << lineno << " of file " << fn << ":\n " << line << "\n";
2897  return false;
2898  }
2899 
2900  // Restore the line counter.
2901  write_line_directive = true;
2902  had_include = true;
2903  continue;
2904 
2905  } else if (strcmp(pragma, "once") == 0) {
2906  // Do a stricter syntax check, just to be extra safe.
2907  if (sscanf(line.c_str(), " # pragma%*[ \t]once %zn", &nread) != 0 ||
2908  nread != line.size()) {
2909  shader_cat.error()
2910  << "Malformed #pragma once at line " << lineno
2911  << " of file " << fn << ":\n " << line << "\n";
2912  return false;
2913  }
2914 
2915  if (fileno == 0) {
2916  shader_cat.warning()
2917  << "#pragma once in main file at line "
2918  << lineno << " of file " << fn
2919 #ifndef NDEBUG
2920  << ":\n " << line
2921 #endif
2922  << "\n";
2923  }
2924 
2925  if (!full_fn.empty()) {
2926  once_files.insert(full_fn);
2927  }
2928  continue;
2929  }
2930  // Otherwise, just pass it through to the driver.
2931 
2932  } else if (strcmp(directive, "endif") == 0) {
2933  // Check for an #endif after an include. We have to restore the line
2934  // number in case the include happened under an #if block.
2935  if (had_include) {
2936  write_line_directive = true;
2937  }
2938 
2939  } else if (strcmp(directive, "version") == 0) {
2940  had_version = true;
2941 
2942  } else if (strcmp(directive, "extension") == 0) {
2943  // Check for special preprocessing extensions.
2944  char extension[256];
2945  char behavior[9];
2946  if (sscanf(line.c_str(), " # extension%*[ \t]%255[^: \t] : %8s", extension, behavior) == 2) {
2947  // Parse the behavior string.
2948  int mode;
2949  if (strcmp(behavior, "require") == 0 || strcmp(behavior, "enable") == 0) {
2950  mode = 2;
2951  } else if (strcmp(behavior, "warn") == 0) {
2952  mode = 1;
2953  } else if (strcmp(behavior, "disable") == 0) {
2954  mode = 0;
2955  } else {
2956  shader_cat.error()
2957  << "Extension directive specifies invalid behavior at line "
2958  << lineno << " of file " << fn << ":\n " << line << "\n";
2959  return false;
2960  }
2961 
2962  if (strcmp(extension, "all") == 0) {
2963  if (mode == 2) {
2964  shader_cat.error()
2965  << "Extension directive for 'all' may only specify 'warn' or "
2966  "'disable' at line " << lineno << " of file " << fn
2967  << ":\n " << line << "\n";
2968  return false;
2969  }
2970  ext_google_include = mode;
2971  ext_google_line = mode;
2972  // Still pass it through to the driver, so it can enable other
2973  // extensions.
2974 
2975  } else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) {
2976  // Enable the Google extension support for #include statements.
2977  // This also implicitly enables GL_GOOGLE_cpp_style_line_directive.
2978  // This matches the behavior of Khronos' glslang reference compiler.
2979  ext_google_include = mode;
2980  ext_google_line = mode;
2981  continue;
2982 
2983  } else if (strcmp(extension, "GL_GOOGLE_cpp_style_line_directive") == 0) {
2984  // Enables strings in #line statements.
2985  ext_google_line = mode;
2986  continue;
2987  }
2988  } else {
2989  shader_cat.error()
2990  << "Failed to parse extension directive at line "
2991  << lineno << " of file " << fn << ":\n " << line << "\n";
2992  return false;
2993  }
2994 
2995  } else if (ext_google_include > 0 && strcmp(directive, "include") == 0) {
2996  // Warn about extension use if requested.
2997  if (ext_google_include == 1) {
2998  shader_cat.warning()
2999  << "Extension GL_GOOGLE_include_directive is being used at line "
3000  << lineno << " of file " << fn
3001 #ifndef NDEBUG
3002  << ":\n " << line
3003 #endif
3004  << "\n";
3005  }
3006 
3007  // This syntax allows only double quotes, not angle brackets.
3008  Filename incfn;
3009  {
3010  char incfile[2048];
3011  if (sscanf(line.c_str(), " # include%*[ \t]\"%2047[^\"]\" %zn", incfile, &nread) != 1
3012  || nread != line.size()) {
3013  // Couldn't parse it.
3014  shader_cat.error()
3015  << "Malformed #include at line " << lineno
3016  << " of file " << fn << ":\n " << line << "\n";
3017  return false;
3018  }
3019  incfn = incfile;
3020  }
3021 
3022  // OK, great. Process the include.
3023  Filename source_dir = full_fn.get_dirname();
3024  if (!r_preprocess_include(out, incfn, source_dir, once_files, record, depth + 1)) {
3025  // An error occurred. Pass on the failure.
3026  shader_cat.error(false) << "included at line "
3027  << lineno << " of file " << fn << ":\n " << line << "\n";
3028  return false;
3029  }
3030 
3031  // Restore the line counter.
3032  write_line_directive = true;
3033  had_include = true;
3034  continue;
3035 
3036  } else if (ext_google_line > 0 && strcmp(directive, "line") == 0) {
3037  // It's a #line directive. See if it uses a string instead of number.
3038  char filestr[2048];
3039  if (sscanf(line.c_str(), " # line%*[ \t]%d%*[ \t]\"%2047[^\"]\" %zn", &lineno, filestr, &nread) == 2
3040  && nread == line.size()) {
3041  // Warn about extension use if requested.
3042  if (ext_google_line == 1) {
3043  shader_cat.warning()
3044  << "Extension GL_GOOGLE_cpp_style_line_directive is being used at line "
3045  << lineno << " of file " << fn
3046 #ifndef NDEBUG
3047  << ":\n " << line
3048 #endif
3049  << "\n";
3050  }
3051 
3052  // Replace the string line number with an integer. This is something
3053  // we can substitute later when parsing the GLSL log from the driver.
3054  fileno = 2048 + _included_files.size();
3055  _included_files.push_back(Filename(filestr));
3056 
3057  out << "#line " << lineno << " " << fileno << " // " << filestr << "\n";
3058  continue;
3059  }
3060  }
3061 
3062  if (write_line_directive) {
3063  out << "#line " << lineno << " " << fileno << " // " << fn << "\n";
3064  write_line_directive = false;
3065  }
3066  out << line << "\n";
3067  }
3068 
3069  if (fileno == 0 && !had_version) {
3070  shader_cat.warning()
3071  << "GLSL shader " << fn << " does not contain a #version line!\n";
3072  }
3073 
3074  return true;
3075 }
3076 
3077 /**
3078  * Checks whether the shader or any of its dependent files were modified on
3079  * disk.
3080  */
3081 bool Shader::
3082 check_modified() const {
3084 
3086  for (it = _source_files.begin(); it != _source_files.end(); ++it) {
3087  const Filename &fn = (*it);
3088 
3089  PT(VirtualFile) vfile = vfs->get_file(fn, true);
3090  if (vfile == nullptr || vfile->get_timestamp() > _last_modified) {
3091  return true;
3092  }
3093  }
3094 
3095  return false;
3096 }
3097 
3098 #ifdef HAVE_CG
3099 /**
3100  * Determines the appropriate active shader profile settings based on any
3101  * profile directives stored within the shader header
3102  */
3103 void Shader::
3104 cg_get_profile_from_header(ShaderCaps& caps) {
3105  // Note this forces profile based on what is specified in the shader header
3106  // string. Should probably be relying on card caps eventually.
3107 
3108  string buf;
3109  parse_init();
3110 
3111  // Assume that if parse doesn't extend after a parse line then we've reached
3112  // the end of _text
3113  int lastParse;
3114 
3115  do {
3116  lastParse = _parse;
3117  parse_line(buf, true, true);
3118  int profilePos = buf.find("//Cg profile");
3119  if (profilePos >= 0) {
3120  // Scan the line for known cg2 vertex program profiles
3121  if ((int)buf.find("gp4vp") >= 0)
3122  caps._active_vprofile = cgGetProfile("gp4vp");
3123 
3124  if ((int)buf.find("gp5vp") >= 0)
3125  caps._active_vprofile = cgGetProfile("gp5vp");
3126 
3127  if ((int)buf.find("glslv") >= 0)
3128  caps._active_vprofile = cgGetProfile("glslv");
3129 
3130  if ((int)buf.find("arbvp1") >= 0)
3131  caps._active_vprofile = cgGetProfile("arbvp1");
3132 
3133  if ((int)buf.find("vp40") >= 0)
3134  caps._active_vprofile = cgGetProfile("vp40");
3135 
3136  if ((int)buf.find("vp30") >= 0)
3137  caps._active_vprofile = cgGetProfile("vp30");
3138 
3139  if ((int)buf.find("vp20") >= 0)
3140  caps._active_vprofile = cgGetProfile("vp20");
3141 
3142  if ((int)buf.find("vs_1_1") >= 0)
3143  caps._active_vprofile = cgGetProfile("vs_1_1");
3144 
3145  if ((int)buf.find("vs_2_0") >= 0)
3146  caps._active_vprofile = cgGetProfile("vs_2_0");
3147 
3148  if ((int)buf.find("vs_2_x") >= 0)
3149  caps._active_vprofile = cgGetProfile("vs_2_x");
3150 
3151  if ((int)buf.find("vs_3_0") >= 0)
3152  caps._active_vprofile = cgGetProfile("vs_3_0");
3153 
3154  if ((int)buf.find("vs_4_0") >= 0)
3155  caps._active_vprofile = cgGetProfile("vs_4_0");
3156 
3157  if ((int)buf.find("vs_5_0") >= 0)
3158  caps._active_vprofile = cgGetProfile("vs_5_0");
3159 
3160  // Scan the line for known cg2 fragment program profiles
3161  if ((int)buf.find("gp4fp") >= 0)
3162  caps._active_fprofile = cgGetProfile("gp4fp");
3163 
3164  if ((int)buf.find("gp5fp") >= 0)
3165  caps._active_fprofile = cgGetProfile("gp5fp");
3166 
3167  if ((int)buf.find("glslf") >= 0)
3168  caps._active_fprofile = cgGetProfile("glslf");
3169 
3170  if ((int)buf.find("arbfp1") >= 0)
3171  caps._active_fprofile = cgGetProfile("arbfp1");
3172 
3173  if ((int)buf.find("fp40") >= 0)
3174  caps._active_fprofile = cgGetProfile("fp40");
3175 
3176  if ((int)buf.find("fp30") >= 0)
3177  caps._active_fprofile = cgGetProfile("fp30");
3178 
3179  if ((int)buf.find("fp20") >= 0)
3180  caps._active_fprofile = cgGetProfile("fp20");
3181 
3182  if ((int)buf.find("ps_1_1") >= 0)
3183  caps._active_fprofile = cgGetProfile("ps_1_1");
3184 
3185  if ((int)buf.find("ps_1_2") >= 0)
3186  caps._active_fprofile = cgGetProfile("ps_1_2");
3187 
3188  if ((int)buf.find("ps_1_3") >= 0)
3189  caps._active_fprofile = cgGetProfile("ps_1_3");
3190 
3191  if ((int)buf.find("ps_2_0") >= 0)
3192  caps._active_fprofile = cgGetProfile("ps_2_0");
3193 
3194  if ((int)buf.find("ps_2_x") >= 0)
3195  caps._active_fprofile = cgGetProfile("ps_2_x");
3196 
3197  if ((int)buf.find("ps_3_0") >= 0)
3198  caps._active_fprofile = cgGetProfile("ps_3_0");
3199 
3200  if ((int)buf.find("ps_4_0") >= 0)
3201  caps._active_fprofile = cgGetProfile("ps_4_0");
3202 
3203  if ((int)buf.find("ps_5_0") >= 0)
3204  caps._active_fprofile = cgGetProfile("ps_5_0");
3205 
3206  // Scan the line for known cg2 geometry program profiles
3207  if ((int)buf.find("gp4gp") >= 0)
3208  caps._active_gprofile = cgGetProfile("gp4gp");
3209 
3210  if ((int)buf.find("gp5gp") >= 0)
3211  caps._active_gprofile = cgGetProfile("gp5gp");
3212 
3213  if ((int)buf.find("glslg") >= 0)
3214  caps._active_gprofile = cgGetProfile("glslg");
3215 
3216  if ((int)buf.find("gs_4_0") >= 0)
3217  caps._active_gprofile = cgGetProfile("gs_4_0");
3218 
3219  if ((int)buf.find("gs_5_0") >= 0)
3220  caps._active_gprofile = cgGetProfile("gs_5_0");
3221  }
3222  } while(_parse > lastParse);
3223 
3224 }
3225 #endif
3226 
3227 /**
3228  * Delete the compiled code, if it exists.
3229  */
3230 Shader::
3232  release_all();
3233  // Note: don't try to erase ourselves from the table. It currently keeps a
3234  // reference forever, and so the only place where this constructor is called
3235  // is in the destructor of the table itself.
3236  /*if (_loaded) {
3237  _load_table.erase(_filename);
3238  } else {
3239  _make_table.erase(_text);
3240  }*/
3241 }
3242 
3243 /**
3244  * Returns the filename of the included shader with the given source file
3245  * index (as recorded in the #line statement in r_preprocess_source). We use
3246  * this to associate error messages with included files.
3247  */
3249 get_filename_from_index(int index, ShaderType type) const {
3250  if (index == 0) {
3251  Filename fn = get_filename(type);
3252  if (!fn.empty()) {
3253  return fn;
3254  }
3255  } else if (glsl_preprocess && index >= 2048 &&
3256  (index - 2048) < (int)_included_files.size()) {
3257  return _included_files[(size_t)index - 2048];
3258  }
3259  // Must be a mistake. Quietly put back the integer.
3260  string str = format_string(index);
3261  return Filename(str);
3262 }
3263 
3264 /**
3265  * Loads the shader with the given filename.
3266  */
3267 PT(Shader) Shader::
3268 load(const Filename &file, ShaderLanguage lang) {
3269  ShaderFile sfile(file);
3270  ShaderTable::const_iterator i = _load_table.find(sfile);
3271  if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3272  // But check that someone hasn't modified it in the meantime.
3273  if (i->second->check_modified()) {
3274  shader_cat.info()
3275  << "Shader " << file << " was modified on disk, reloading.\n";
3276  } else {
3277  shader_cat.debug()
3278  << "Shader " << file << " was found in shader cache.\n";
3279  return i->second;
3280  }
3281  }
3282 
3283  PT(Shader) shader = new Shader(lang);
3284  if (!shader->read(sfile)) {
3285  return nullptr;
3286  }
3287 
3288  _load_table[sfile] = shader;
3289 
3290  if (cache_generated_shaders) {
3291  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3292  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3293  return i->second;
3294  }
3295  _make_table[shader->_text] = shader;
3296  }
3297  return shader;
3298 }
3299 
3300 /**
3301  * This variant of Shader::load loads all shader programs separately.
3302  */
3303 PT(Shader) Shader::
3304 load(ShaderLanguage lang, const Filename &vertex,
3305  const Filename &fragment, const Filename &geometry,
3306  const Filename &tess_control, const Filename &tess_evaluation) {
3307  ShaderFile sfile(vertex, fragment, geometry, tess_control, tess_evaluation);
3308  ShaderTable::const_iterator i = _load_table.find(sfile);
3309  if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3310  // But check that someone hasn't modified it in the meantime.
3311  if (i->second->check_modified()) {
3312  shader_cat.info()
3313  << "Shader was modified on disk, reloading.\n";
3314  } else {
3315  shader_cat.debug()
3316  << "Shader was found in shader cache.\n";
3317  return i->second;
3318  }
3319  }
3320 
3321  PT(Shader) shader = new Shader(lang);
3322  if (!shader->read(sfile)) {
3323  return nullptr;
3324  }
3325 
3326  _load_table[sfile] = shader;
3327 
3328  if (cache_generated_shaders) {
3329  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3330  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3331  return i->second;
3332  }
3333  _make_table[shader->_text] = shader;
3334  }
3335  return shader;
3336 }
3337 
3338 /**
3339  * Loads a compute shader.
3340  */
3341 PT(Shader) Shader::
3342 load_compute(ShaderLanguage lang, const Filename &fn) {
3343  if (lang != SL_GLSL) {
3344  shader_cat.error()
3345  << "Only GLSL compute shaders are currently supported.\n";
3346  return nullptr;
3347  }
3348 
3349  Filename fullpath(fn);
3351  if (!vfs->resolve_filename(fullpath, get_model_path())) {
3352  shader_cat.error()
3353  << "Could not find compute shader file: " << fn << "\n";
3354  return nullptr;
3355  }
3356 
3357  ShaderFile sfile;
3358  sfile._separate = true;
3359  sfile._compute = fn;
3360 
3361  ShaderTable::const_iterator i = _load_table.find(sfile);
3362  if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3363  // But check that someone hasn't modified it in the meantime.
3364  if (i->second->check_modified()) {
3365  shader_cat.info()
3366  << "Compute shader " << fn << " was modified on disk, reloading.\n";
3367  } else {
3368  shader_cat.debug()
3369  << "Compute shader " << fn << " was found in shader cache.\n";
3370  return i->second;
3371  }
3372  }
3373 
3375  PT(BamCacheRecord) record = cache->lookup(fullpath, "sho");
3376  if (record != nullptr) {
3377  if (record->has_data()) {
3378  shader_cat.info()
3379  << "Compute shader " << fn << " was found in disk cache.\n";
3380 
3381  return DCAST(Shader, record->get_data());
3382  }
3383  }
3384 
3385  PT(Shader) shader = new Shader(lang);
3386 
3387  if (!shader->read(sfile, record)) {
3388  return nullptr;
3389  }
3390  _load_table[sfile] = shader;
3391 
3392  if (cache_generated_shaders) {
3393  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3394  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3395  return i->second;
3396  }
3397  _make_table[shader->_text] = shader;
3398  }
3399 
3400  // It makes little sense to cache the shader before compilation, so we keep
3401  // the record for when we have the compiled the shader.
3402  std::swap(shader->_record, record);
3403  shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders();
3404  shader->_fullpath = shader->_source_files[0];
3405  return shader;
3406 }
3407 
3408 /**
3409  * Loads the shader, using the string as shader body.
3410  */
3411 PT(Shader) Shader::
3412 make(string body, ShaderLanguage lang) {
3413  if (lang == SL_GLSL) {
3414  shader_cat.error()
3415  << "GLSL shaders must have separate shader bodies!\n";
3416  return nullptr;
3417 
3418  } else if (lang == SL_none) {
3419  shader_cat.warning()
3420  << "Shader::make() now requires an explicit shader language. Assuming Cg.\n";
3421  lang = SL_Cg;
3422  }
3423 #ifndef HAVE_CG
3424  if (lang == SL_Cg) {
3425  shader_cat.error() << "Support for Cg shaders is not enabled.\n";
3426  return nullptr;
3427  }
3428 #endif
3429 
3430  ShaderFile sbody(move(body));
3431 
3432  if (cache_generated_shaders) {
3433  ShaderTable::const_iterator i = _make_table.find(sbody);
3434  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3435  // But check that someone hasn't modified its includes in the meantime.
3436  if (!i->second->check_modified()) {
3437  return i->second;
3438  }
3439  }
3440  }
3441 
3442  PT(Shader) shader = new Shader(lang);
3443  if (!shader->load(sbody)) {
3444  return nullptr;
3445  }
3446 
3447  if (cache_generated_shaders) {
3448  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3449  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3450  shader = i->second;
3451  } else {
3452  _make_table[shader->_text] = shader;
3453  }
3454  _make_table[std::move(sbody)] = shader;
3455  }
3456 
3457  if (dump_generated_shaders) {
3458  ostringstream fns;
3459  int index = _shaders_generated ++;
3460  fns << "genshader" << index;
3461  string fn = fns.str();
3462  shader_cat.warning() << "Dumping shader: " << fn << "\n";
3463 
3464  pofstream s;
3465  s.open(fn.c_str(), std::ios::out | std::ios::trunc);
3466  s << shader->get_text();
3467  s.close();
3468  }
3469  return shader;
3470 }
3471 
3472 /**
3473  * Loads the shader, using the strings as shader bodies.
3474  */
3475 PT(Shader) Shader::
3476 make(ShaderLanguage lang, string vertex, string fragment, string geometry,
3477  string tess_control, string tess_evaluation) {
3478 #ifndef HAVE_CG
3479  if (lang == SL_Cg) {
3480  shader_cat.error() << "Support for Cg shaders is not enabled.\n";
3481  return nullptr;
3482  }
3483 #endif
3484  if (lang == SL_none) {
3485  shader_cat.error()
3486  << "Shader::make() requires an explicit shader language.\n";
3487  return nullptr;
3488  }
3489 
3490  ShaderFile sbody(move(vertex), move(fragment), move(geometry),
3491  move(tess_control), move(tess_evaluation));
3492 
3493  if (cache_generated_shaders) {
3494  ShaderTable::const_iterator i = _make_table.find(sbody);
3495  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3496  // But check that someone hasn't modified its includes in the meantime.
3497  if (!i->second->check_modified()) {
3498  return i->second;
3499  }
3500  }
3501  }
3502 
3503  PT(Shader) shader = new Shader(lang);
3504  shader->_filename = ShaderFile("created-shader");
3505  if (!shader->load(sbody)) {
3506  return nullptr;
3507  }
3508 
3509  if (cache_generated_shaders) {
3510  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3511  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3512  shader = i->second;
3513  } else {
3514  _make_table[shader->_text] = shader;
3515  }
3516  _make_table[std::move(sbody)] = shader;
3517  }
3518 
3519  return shader;
3520 }
3521 
3522 /**
3523  * Loads the compute shader from the given string.
3524  */
3525 PT(Shader) Shader::
3526 make_compute(ShaderLanguage lang, string body) {
3527  if (lang != SL_GLSL) {
3528  shader_cat.error()
3529  << "Only GLSL compute shaders are currently supported.\n";
3530  return nullptr;
3531  }
3532 
3533  ShaderFile sbody;
3534  sbody._separate = true;
3535  sbody._compute = move(body);
3536 
3537  if (cache_generated_shaders) {
3538  ShaderTable::const_iterator i = _make_table.find(sbody);
3539  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3540  // But check that someone hasn't modified its includes in the meantime.
3541  if (!i->second->check_modified()) {
3542  return i->second;
3543  }
3544  }
3545  }
3546 
3547  PT(Shader) shader = new Shader(lang);
3548  shader->_filename = ShaderFile("created-shader");
3549  if (!shader->load(sbody)) {
3550  return nullptr;
3551  }
3552 
3553  if (cache_generated_shaders) {
3554  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3555  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3556  shader = i->second;
3557  } else {
3558  _make_table[shader->_text] = shader;
3559  }
3560  _make_table[std::move(sbody)] = shader;
3561  }
3562 
3563  return shader;
3564 }
3565 
3566 /**
3567  * Set a 'parse pointer' to the beginning of the shader.
3568  */
3569 void Shader::
3571  _parse = 0;
3572 }
3573 
3574 /**
3575  * Parse a line of text. If 'lt' is true, trim blanks from the left end of
3576  * the line. If 'rt' is true, trim blanks from the right end (the newline is
3577  * always trimmed).
3578  */
3579 void Shader::
3580 parse_line(string &result, bool lt, bool rt) {
3581  nassertv(!_text._separate);
3582  int len = _text._shared.size();
3583  int head = _parse;
3584  int tail = head;
3585  while ((tail < len) && (_text._shared[tail] != '\n')) {
3586  tail++;
3587  }
3588  if (tail < len) {
3589  _parse = tail+1;
3590  } else {
3591  _parse = tail;
3592  }
3593  if (lt) {
3594  while ((head < tail)&&(isspace(_text._shared[head]))) head++;
3595  while ((tail > head)&&(isspace(_text._shared[tail-1]))) tail--;
3596  }
3597  result = _text._shared.substr(head, tail-head);
3598 }
3599 
3600 /**
3601  * Parse lines until you read a line that matches the specified pattern.
3602  * Returns all the preceding lines, and if the include flag is set, returns
3603  * the final line as well.
3604  */
3605 void Shader::
3606 parse_upto(string &result, string pattern, bool include) {
3607  nassertv(!_text._separate);
3608  GlobPattern endpat(pattern);
3609  int start = _parse;
3610  int last = _parse;
3611  while (_parse < (int)(_text._shared.size())) {
3612  string t;
3613  parse_line(t, true, true);
3614  if (endpat.matches(t)) break;
3615  last = _parse;
3616  }
3617  if (include) {
3618  result = _text._shared.substr(start, _parse - start);
3619  } else {
3620  result = _text._shared.substr(start, last - start);
3621  }
3622 }
3623 
3624 /**
3625  * Returns the rest of the text from the current parse location.
3626  */
3627 void Shader::
3628 parse_rest(string &result) {
3629  nassertv(!_text._separate);
3630  result = _text._shared.substr(_parse, _text._shared.size() - _parse);
3631 }
3632 
3633 /**
3634  * Returns true if the parse pointer is at the end of the shader.
3635  */
3636 bool Shader::
3638  return (int)_text._shared.size() == _parse;
3639 }
3640 
3641 /**
3642  * Indicates that the shader should be enqueued to be prepared in the
3643  * indicated prepared_objects at the beginning of the next frame. This will
3644  * ensure the texture is already loaded into texture memory if it is expected
3645  * to be rendered soon.
3646  *
3647  * Use this function instead of prepare_now() to preload textures from a user
3648  * interface standpoint.
3649  */
3650 PT(AsyncFuture) Shader::
3651 prepare(PreparedGraphicsObjects *prepared_objects) {
3652  return prepared_objects->enqueue_shader_future(this);
3653 }
3654 
3655 /**
3656  * Returns true if the shader has already been prepared or enqueued for
3657  * preparation on the indicated GSG, false otherwise.
3658  */
3659 bool Shader::
3660 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
3661  Contexts::const_iterator ci;
3662  ci = _contexts.find(prepared_objects);
3663  if (ci != _contexts.end()) {
3664  return true;
3665  }
3666  return prepared_objects->is_shader_queued(this);
3667 }
3668 
3669 /**
3670  * Frees the texture context only on the indicated object, if it exists there.
3671  * Returns true if it was released, false if it had not been prepared.
3672  */
3673 bool Shader::
3674 release(PreparedGraphicsObjects *prepared_objects) {
3675  Contexts::iterator ci;
3676  ci = _contexts.find(prepared_objects);
3677  if (ci != _contexts.end()) {
3678  ShaderContext *sc = (*ci).second;
3679  if (sc != nullptr) {
3680  prepared_objects->release_shader(sc);
3681  } else {
3682  _contexts.erase(ci);
3683  }
3684  return true;
3685  }
3686 
3687  // Maybe it wasn't prepared yet, but it's about to be.
3688  return prepared_objects->dequeue_shader(this);
3689 }
3690 
3691 /**
3692  * Creates a context for the shader on the particular GSG, if it does not
3693  * already exist. Returns the new (or old) ShaderContext. This assumes that
3694  * the GraphicsStateGuardian is the currently active rendering context and
3695  * that it is ready to accept new textures. If this is not necessarily the
3696  * case, you should use prepare() instead.
3697  *
3698  * Normally, this is not called directly except by the GraphicsStateGuardian;
3699  * a shader does not need to be explicitly prepared by the user before it may
3700  * be rendered.
3701  */
3705  Contexts::const_iterator ci;
3706  ci = _contexts.find(prepared_objects);
3707  if (ci != _contexts.end()) {
3708  return (*ci).second;
3709  }
3710 
3711  ShaderContext *tc = prepared_objects->prepare_shader_now(this, gsg);
3712  _contexts[prepared_objects] = tc;
3713 
3714  return tc;
3715 }
3716 
3717 /**
3718  * Removes the indicated PreparedGraphicsObjects table from the Shader's
3719  * table, without actually releasing the texture. This is intended to be
3720  * called only from PreparedGraphicsObjects::release_texture(); it should
3721  * never be called by user code.
3722  */
3723 void Shader::
3724 clear_prepared(PreparedGraphicsObjects *prepared_objects) {
3725  Contexts::iterator ci;
3726  ci = _contexts.find(prepared_objects);
3727  if (ci != _contexts.end()) {
3728  _contexts.erase(ci);
3729  } else {
3730  // If this assertion fails, clear_prepared() was given a prepared_objects
3731  // which the texture didn't know about.
3732  nassert_raise("unknown PreparedGraphicsObjects");
3733  }
3734 }
3735 
3736 /**
3737  * Frees the context allocated on all objects for which the texture has been
3738  * declared. Returns the number of contexts which have been freed.
3739  */
3740 int Shader::
3742  // We have to traverse a copy of the _contexts list, because the
3743  // PreparedGraphicsObjects object will call clear_prepared() in response to
3744  // each release_texture(), and we don't want to be modifying the _contexts
3745  // list while we're traversing it.
3746  Contexts temp = _contexts;
3747  int num_freed = (int)_contexts.size();
3748 
3749  Contexts::const_iterator ci;
3750  for (ci = temp.begin(); ci != temp.end(); ++ci) {
3751  PreparedGraphicsObjects *prepared_objects = (*ci).first;
3752  ShaderContext *sc = (*ci).second;
3753  if (sc != nullptr) {
3754  prepared_objects->release_shader(sc);
3755  }
3756  }
3757 
3758  // There might still be some outstanding contexts in the map, if there were
3759  // any NULL pointers there. Eliminate them.
3760  _contexts.clear();
3761 
3762  return num_freed;
3763 }
3764 
3765 /**
3766  *
3767  */
3768 void Shader::ShaderCaps::
3769 clear() {
3770  _supports_glsl = false;
3771 
3772 #ifdef HAVE_CG
3773  _active_vprofile = CG_PROFILE_UNKNOWN;
3774  _active_fprofile = CG_PROFILE_UNKNOWN;
3775  _active_gprofile = CG_PROFILE_UNKNOWN;
3776  _ultimate_vprofile = CG_PROFILE_UNKNOWN;
3777  _ultimate_fprofile = CG_PROFILE_UNKNOWN;
3778  _ultimate_gprofile = CG_PROFILE_UNKNOWN;
3779 #endif
3780 }
3781 
3782 /**
3783  * Tells the BamReader how to create objects of type Shader.
3784  */
3785 void Shader::
3787  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
3788 }
3789 
3790 /**
3791  * Writes the contents of this object to the datagram for shipping out to a
3792  * Bam file.
3793  */
3794 void Shader::
3796  dg.add_uint8(_language);
3797  dg.add_bool(_loaded);
3798  _filename.write_datagram(dg);
3799  _text.write_datagram(dg);
3800 
3801  dg.add_uint32(_compiled_format);
3802  dg.add_string(_compiled_binary);
3803 }
3804 
3805 /**
3806  * This function is called by the BamReader's factory when a new object of
3807  * type Shader is encountered in the Bam file. It should create the Shader
3808  * and extract its information from the file.
3809  */
3810 TypedWritable *Shader::
3811 make_from_bam(const FactoryParams &params) {
3812  Shader *attrib = new Shader(SL_none);
3813  DatagramIterator scan;
3814  BamReader *manager;
3815 
3816  parse_params(params, scan, manager);
3817  attrib->fillin(scan, manager);
3818  return attrib;
3819 }
3820 
3821 /**
3822  * This internal function is called by make_from_bam to read in all of the
3823  * relevant data from the BamFile for the new Shader.
3824  */
3825 void Shader::
3826 fillin(DatagramIterator &scan, BamReader *manager) {
3827  _language = (ShaderLanguage) scan.get_uint8();
3828  _loaded = scan.get_bool();
3829  _filename.read_datagram(scan);
3830  _text.read_datagram(scan);
3831 
3832  _compiled_format = scan.get_uint32();
3833  _compiled_binary = scan.get_string();
3834 }
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.
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
void parse_rest(std::string &result)
Returns the rest of the text from the current parse location.
Definition: shader.cxx:3628
void release_shader(ShaderContext *sc)
Indicates that a shader context, created by a previous call to prepare_shader(), is no longer needed.
bool cp_errchk_parameter_varying(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:152
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
Definition: asyncFuture.h:61
bool get_bool()
Extracts a boolean value.
static void register_with_read_factory()
Tells the BamReader how to create objects of type Shader.
Definition: shader.cxx:3786
std::ostream & error(bool prefix=true) const
A shorthand way to write out(NS_error).
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
A hierarchy of directories and files that appears to be one continuous file system,...
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
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Definition: shader.h:49
const std::string & get_text(ShaderType type=ST_none) const
Return the Shader's text for the given shader type.
Definition: shader.I:89
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
Filename get_filename(ShaderType type=ST_none) const
Return the Shader's filename for the given shader type.
Definition: shader.I:20
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
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
bool cp_errchk_parameter_sampler(ShaderArgInfo &arg)
Make sure the provided parameter has a texture type.
Definition: shader.cxx:231
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
A table of objects that are saved within the graphics context for reference by handle later.
PT(Shader) Shader
Loads the shader with the given filename.
Definition: shader.cxx:3267
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_init()
Set a 'parse pointer' to the beginning of the shader.
Definition: shader.cxx:3570
std::string get_string()
Extracts a variable-length string.
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
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:3249
get_cache_compiled_shaders
Returns whether compiled shader programs will be stored in the cache, as binary .txo files.
Definition: bamCache.h:94
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
void cp_optimize_mat_spec(ShaderMatSpec &spec)
Analyzes a ShaderMatSpec and decides what it should use its cache for.
Definition: shader.cxx:508
The ShaderContext is meant to contain the compiled version of a shader string.
Definition: shaderContext.h:31
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
void parse_line(std::string &result, bool rt, bool lt)
Parse a line of text.
Definition: shader.cxx:3580
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
ShaderContext * prepare_shader_now(Shader *shader, GraphicsStateGuardianBase *gsg)
Immediately creates a new ShaderContext for the indicated shader and returns it.
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:188
void read_datagram(DatagramIterator &source)
Reads the object from a Datagram.
Definition: shader.I:742
bool get_compiled(unsigned int &format, std::string &binary) const
Called by the back-end to retrieve compiled data.
Definition: shader.cxx:1586
bool cp_errchk_parameter_uniform(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:166
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
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
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:3795
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
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
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
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
~Shader()
Delete the compiled code, if it exists.
Definition: shader.cxx:3231
bool dequeue_shader(Shader *shader)
Removes a shader from the queued list of shaders to be prepared.
std::string cp_parse_non_delimiter(vector_string &pieces, int &next)
Pop a non-delimiter word from the word list.
Definition: shader.cxx:276
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
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.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: shader.cxx:3741
bool cp_errchk_parameter_in(ShaderArgInfo &arg)
Make sure the provided parameter has the 'in' direction.
Definition: shader.cxx:138
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:3660
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: shader.cxx:3674
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
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.
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool parse_eof()
Returns true if the parse pointer is at the end of the shader.
Definition: shader.cxx:3637
bool cp_errchk_parameter_words(ShaderArgInfo &arg, int len)
Make sure the provided parameter contains the specified number of words.
Definition: shader.cxx:122
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
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:3703
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
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
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
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
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:3606
bool is_shader_queued(const Shader *shader) const
Returns true if the shader has been queued on this GSG, false otherwise.
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