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  */
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  */
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  */
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  */
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  */
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  */
180 cp_errchk_parameter_float(ShaderArgInfo &p, int lo, int hi)
181 {
182  int nfloat;
183  switch (p._type) {
184  case SAT_scalar: nfloat = 1; break;
185  case SAT_vec2: nfloat = 2; break;
186  case SAT_vec3: nfloat = 3; break;
187  case SAT_vec4: nfloat = 4; break;
188  case SAT_mat3x3: nfloat = 9; break;
189  case SAT_mat4x4: nfloat = 16; break;
190  default: nfloat = 0; break;
191  }
192  if ((nfloat < lo)||(nfloat > hi)) {
193  string msg = "wrong type for parameter:";
194  cp_report_error(p, msg);
195  return false;
196  }
197  return true;
198 }
199 
200 /**
201  *
202  */
203 bool Shader::
204 cp_errchk_parameter_ptr(ShaderArgInfo &p) {
205  switch (p._class) {
206  case SAC_scalar: return true;
207  case SAC_vector: return true;
208  case SAC_matrix: return true;
209  case SAC_array:
210  switch (p._subclass) {
211  case SAC_scalar: return true;
212  case SAC_vector: return true;
213  case SAC_matrix: return true;
214  default:
215  string msg = "unsupported array subclass.";
216  cp_report_error(p, msg);
217  return false;
218  }
219  default:
220  string msg = "unsupported class.";
221  cp_report_error(p,msg);
222  return false;
223  }
224 }
225 
226 /**
227  * Make sure the provided parameter has a texture type. If not, print error
228  * message and return false.
229  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
1597 set_default_caps(const ShaderCaps &caps) {
1598  _default_caps = caps;
1599 }
1600 
1601 #ifdef HAVE_CG
1602 /**
1603  *
1604  */
1605 Shader::ShaderArgType Shader::
1606 cg_parameter_type(CGparameter p) {
1607  switch (cgGetParameterClass(p)) {
1608  case CG_PARAMETERCLASS_SCALAR: return SAT_scalar;
1609  case CG_PARAMETERCLASS_VECTOR:
1610  switch (cgGetParameterColumns(p)) {
1611  case 1: return SAT_vec1;
1612  case 2: return SAT_vec2;
1613  case 3: return SAT_vec3;
1614  case 4: return SAT_vec4;
1615  default: return SAT_unknown;
1616  }
1617  case CG_PARAMETERCLASS_MATRIX:
1618  switch (cgGetParameterRows(p)) {
1619  case 1:
1620  switch (cgGetParameterColumns(p)) {
1621  case 1: return SAT_mat1x1;
1622  case 2: return SAT_mat1x2;
1623  case 3: return SAT_mat1x3;
1624  case 4: return SAT_mat1x4;
1625  default: return SAT_unknown;
1626  }
1627  case 2:
1628  switch (cgGetParameterColumns(p)) {
1629  case 1: return SAT_mat2x1;
1630  case 2: return SAT_mat2x2;
1631  case 3: return SAT_mat2x3;
1632  case 4: return SAT_mat2x4;
1633  default: return SAT_unknown;
1634  }
1635  case 3:
1636  switch (cgGetParameterColumns(p)) {
1637  case 1: return SAT_mat3x1;
1638  case 2: return SAT_mat3x2;
1639  case 3: return SAT_mat3x3;
1640  case 4: return SAT_mat3x4;
1641  default: return SAT_unknown;
1642  }
1643  case 4:
1644  switch (cgGetParameterColumns(p)) {
1645  case 1: return SAT_mat4x1;
1646  case 2: return SAT_mat4x2;
1647  case 3: return SAT_mat4x3;
1648  case 4: return SAT_mat4x4;
1649  default: return SAT_unknown;
1650  }
1651  default: return SAT_unknown;
1652  }
1653  case CG_PARAMETERCLASS_SAMPLER:
1654  switch (cgGetParameterType(p)) {
1655  case CG_SAMPLER1D: return Shader::SAT_sampler1d;
1656  case CG_SAMPLER2D: return Shader::SAT_sampler2d;
1657  case CG_SAMPLER3D: return Shader::SAT_sampler3d;
1658  case CG_SAMPLER2DARRAY: return Shader::SAT_sampler2d_array;
1659  case CG_SAMPLERCUBE: return Shader::SAT_sampler_cube;
1660  case CG_SAMPLERBUF: return Shader::SAT_sampler_buffer;
1661  case CG_SAMPLERCUBEARRAY:return Shader::SAT_sampler_cube_array;
1662  // CG_SAMPLER1DSHADOW and CG_SAMPLER2DSHADOW
1663  case 1313: return Shader::SAT_sampler1d;
1664  case 1314: return Shader::SAT_sampler2d;
1665  default: return SAT_unknown;
1666  }
1667  case CG_PARAMETERCLASS_ARRAY: return SAT_unknown;
1668  default:
1669  return SAT_unknown;
1670  }
1671 }
1672 
1673 /**
1674  *
1675  */
1676 Shader::ShaderArgClass Shader::cg_parameter_class(CGparameter p) {
1677  switch (cgGetParameterClass(p)) {
1678  case CG_PARAMETERCLASS_SCALAR: return Shader::SAC_scalar;
1679  case CG_PARAMETERCLASS_VECTOR: return Shader::SAC_vector;
1680  case CG_PARAMETERCLASS_MATRIX: return Shader::SAC_matrix;
1681  case CG_PARAMETERCLASS_SAMPLER: return Shader::SAC_sampler;
1682  case CG_PARAMETERCLASS_ARRAY: return Shader::SAC_array;
1683  default: return Shader::SAC_unknown;
1684  }
1685 }
1686 
1687 /**
1688  *
1689  */
1690 Shader::ShaderArgDir Shader::
1691 cg_parameter_dir(CGparameter p) {
1692  switch (cgGetParameterDirection(p)) {
1693  case CG_IN: return Shader::SAD_in;
1694  case CG_OUT: return Shader::SAD_out;
1695  case CG_INOUT: return Shader::SAD_inout;
1696  default: return Shader::SAD_unknown;
1697  }
1698 }
1699 
1700 /**
1701  * xyz
1702  */
1703 void Shader::
1704 cg_release_resources() {
1705  if (_cg_vprogram != 0) {
1706  cgDestroyProgram(_cg_vprogram);
1707  _cg_vprogram = 0;
1708  }
1709  if (_cg_fprogram != 0) {
1710  cgDestroyProgram(_cg_fprogram);
1711  _cg_fprogram = 0;
1712  }
1713  if (_cg_gprogram != 0) {
1714  cgDestroyProgram(_cg_gprogram);
1715  _cg_gprogram = 0;
1716  }
1717 }
1718 
1719 /**
1720  * xyz
1721  */
1722 CGprogram Shader::
1723 cg_compile_entry_point(const char *entry, const ShaderCaps &caps,
1724  CGcontext context, ShaderType type) {
1725  CGprogram prog;
1726  CGerror err;
1727  const char *compiler_args[100];
1728  const string text = get_text(type);
1729  int nargs = 0;
1730 
1731  int active, ultimate;
1732 
1733  switch (type) {
1734  case ST_vertex:
1735  active = caps._active_vprofile;
1736  ultimate = caps._ultimate_vprofile;
1737  break;
1738 
1739  case ST_fragment:
1740  active = caps._active_fprofile;
1741  ultimate = caps._ultimate_fprofile;
1742  break;
1743 
1744  case ST_geometry:
1745  active = caps._active_gprofile;
1746  ultimate = caps._ultimate_gprofile;
1747  break;
1748 
1749  case ST_tess_evaluation:
1750  case ST_tess_control:
1751  active = caps._active_tprofile;
1752  ultimate = caps._ultimate_tprofile;
1753  break;
1754 
1755  case ST_none:
1756  default:
1757  active = CG_PROFILE_UNKNOWN;
1758  ultimate = CG_PROFILE_UNKNOWN;
1759  };
1760 
1761  if (type == ST_fragment && caps._bug_list.count(SBUG_ati_draw_buffers)) {
1762  compiler_args[nargs++] = "-po";
1763  compiler_args[nargs++] = "ATI_draw_buffers";
1764  }
1765 
1766  string version_arg;
1767  if (!cg_glsl_version.empty() && active != CG_PROFILE_UNKNOWN &&
1768  cgGetProfileProperty((CGprofile) active, CG_IS_GLSL_PROFILE)) {
1769 
1770  version_arg = "version=";
1771  version_arg += cg_glsl_version;
1772 
1773  compiler_args[nargs++] = "-po";
1774  compiler_args[nargs++] = version_arg.c_str();
1775  }
1776 
1777  compiler_args[nargs] = 0;
1778 
1779  cgGetError();
1780 
1781  if ((active != (int)CG_PROFILE_UNKNOWN) && (active != ultimate)) {
1782  // Print out some debug information about what we're doing.
1783  if (shader_cat.is_debug()) {
1784  shader_cat.debug()
1785  << "Compiling Cg shader " << get_filename(type) << " with entry point " << entry
1786  << " and active profile " << cgGetProfileString((CGprofile) active) << "\n";
1787 
1788  if (nargs > 0) {
1789  shader_cat.debug() << "Using compiler arguments:";
1790  for (int i = 0; i < nargs; ++i) {
1791  shader_cat.debug(false) << " " << compiler_args[i];
1792  }
1793  shader_cat.debug(false) << "\n";
1794  }
1795  }
1796 
1797  // Compile the shader with the active profile.
1798  prog = cgCreateProgram(context, CG_SOURCE, text.c_str(),
1799  (CGprofile)active, entry, (const char **)compiler_args);
1800  err = cgGetError();
1801  if (err == CG_NO_ERROR) {
1802  return prog;
1803  }
1804  if (prog != 0) {
1805  cgDestroyProgram(prog);
1806  }
1807  if (shader_cat.is_debug()) {
1808  shader_cat.debug()
1809  << "Compilation with active profile failed: " << cgGetErrorString(err) << "\n";
1810  if (err == CG_COMPILER_ERROR) {
1811  const char *listing = cgGetLastListing(context);
1812  if (listing != nullptr) {
1813  shader_cat.debug(false) << listing;
1814  }
1815  }
1816  }
1817  }
1818 
1819  if (shader_cat.is_debug()) {
1820  shader_cat.debug()
1821  << "Compiling Cg shader " << get_filename(type) << " with entry point " << entry
1822  << " and ultimate profile " << cgGetProfileString((CGprofile) ultimate) << "\n";
1823  }
1824 
1825  // The active profile failed, so recompile it with the ultimate profile.
1826  prog = cgCreateProgram(context, CG_SOURCE, text.c_str(),
1827  (CGprofile)ultimate, entry, nullptr);
1828 
1829  // Extract the output listing.
1830  err = cgGetError();
1831  const char *listing = cgGetLastListing(context);
1832 
1833  if (err == CG_NO_ERROR && listing != nullptr && strlen(listing) > 1) {
1834  shader_cat.warning()
1835  << "Encountered warnings during compilation of " << get_filename(type)
1836  << ":\n" << listing;
1837 
1838  } else if (err == CG_COMPILER_ERROR) {
1839  shader_cat.error()
1840  << "Failed to compile Cg shader " << get_filename(type);
1841  if (listing != nullptr) {
1842  shader_cat.error(false) << ":\n" << listing;
1843  } else {
1844  shader_cat.error(false) << "!\n";
1845  }
1846  }
1847 
1848  if (err == CG_NO_ERROR) {
1849  return prog;
1850  }
1851 
1852  if (shader_cat.is_debug()) {
1853  shader_cat.debug()
1854  << "Compilation with ultimate profile failed: " << cgGetErrorString(err) << "\n";
1855  }
1856 
1857  if (prog != 0) {
1858  cgDestroyProgram(prog);
1859  }
1860  return 0;
1861 }
1862 
1863 /**
1864  * Compiles a Cg shader for a given set of capabilities. If successful, the
1865  * shader is stored in the instance variables _cg_context, _cg_vprogram,
1866  * _cg_fprogram.
1867  */
1868 bool Shader::
1869 cg_compile_shader(const ShaderCaps &caps, CGcontext context) {
1870  _cg_last_caps = caps;
1871 
1872  if (!_text._separate || !_text._vertex.empty()) {
1873  _cg_vprogram = cg_compile_entry_point("vshader", caps, context, ST_vertex);
1874  if (_cg_vprogram == 0) {
1875  cg_release_resources();
1876  return false;
1877  }
1878  _cg_vprofile = cgGetProgramProfile(_cg_vprogram);
1879  }
1880 
1881  if (!_text._separate || !_text._fragment.empty()) {
1882  _cg_fprogram = cg_compile_entry_point("fshader", caps, context, ST_fragment);
1883  if (_cg_fprogram == 0) {
1884  cg_release_resources();
1885  return false;
1886  }
1887  _cg_fprofile = cgGetProgramProfile(_cg_fprogram);
1888  }
1889 
1890  if ((_text._separate && !_text._geometry.empty()) || (!_text._separate && _text._shared.find("gshader") != string::npos)) {
1891  _cg_gprogram = cg_compile_entry_point("gshader", caps, context, ST_geometry);
1892  if (_cg_gprogram == 0) {
1893  cg_release_resources();
1894  return false;
1895  }
1896  _cg_gprofile = cgGetProgramProfile(_cg_gprogram);
1897  }
1898 
1899  if (_cg_vprogram == 0 && _cg_fprogram == 0 && _cg_gprogram == 0) {
1900  shader_cat.error() << "Shader must at least have one program!\n";
1901  cg_release_resources();
1902  return false;
1903  }
1904 
1905  // This is present to work around a bug in the Cg compiler for Direct3D 9.
1906  // It generates "texld_sat" instructions that the result in an
1907  // D3DXERR_INVALIDDATA error when trying to load the shader, since the _sat
1908  // modifier may not be used on tex* instructions.
1909  if (_cg_fprofile == CG_PROFILE_PS_2_0 ||
1910  _cg_fprofile == CG_PROFILE_PS_2_X ||
1911  _cg_fprofile == CG_PROFILE_PS_3_0) {
1912  vector_string lines;
1913  tokenize(cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM), lines, "\n");
1914 
1915  ostringstream out;
1916  int num_modified = 0;
1917 
1918  for (size_t i = 0; i < lines.size(); ++i) {
1919  const string &line = lines[i];
1920 
1921  size_t space = line.find(' ');
1922  if (space == string::npos) {
1923  out << line << '\n';
1924  continue;
1925  }
1926 
1927  string instr = line.substr(0, space);
1928 
1929  // Look for a texld instruction with _sat modifier.
1930  if (instr.compare(0, 5, "texld") == 0 &&
1931  instr.compare(instr.size() - 4, 4, "_sat") == 0) {
1932  // Which destination register are we operating on?
1933  string reg = line.substr(space + 1, line.find(',', space) - space - 1);
1934 
1935  // Move the saturation operation to a separate instruction.
1936  instr.resize(instr.size() - 4);
1937  out << instr << ' ' << line.substr(space + 1) << '\n';
1938  out << "mov_sat " << reg << ", " << reg << '\n';
1939  ++num_modified;
1940  } else {
1941  out << line << '\n';
1942  }
1943  }
1944 
1945  if (num_modified > 0) {
1946  string result = out.str();
1947  CGprogram new_program;
1948  new_program = cgCreateProgram(context, CG_OBJECT, result.c_str(),
1949  (CGprofile)_cg_fprofile, "fshader",
1950  nullptr);
1951  if (new_program) {
1952  cgDestroyProgram(_cg_fprogram);
1953  _cg_fprogram = new_program;
1954 
1955  if (shader_cat.is_debug()) {
1956  shader_cat.debug()
1957  << "Replaced " << num_modified << " invalid texld_sat instruction"
1958  << ((num_modified == 1) ? "" : "s") << " in compiled shader\n";
1959  }
1960  } else {
1961  shader_cat.warning()
1962  << "Failed to load shader with fixed texld_sat instructions: "
1963  << cgGetErrorString(cgGetError()) << "\n";
1964  }
1965  }
1966  }
1967 
1968  // DEBUG: output the generated program
1969  if (shader_cat.is_debug()) {
1970  const char *vertex_program;
1971  const char *fragment_program;
1972  const char *geometry_program;
1973 
1974  if (_cg_vprogram != 0) {
1975  shader_cat.debug()
1976  << "Cg vertex profile: " << cgGetProfileString((CGprofile)_cg_vprofile) << "\n";
1977  vertex_program = cgGetProgramString(_cg_vprogram, CG_COMPILED_PROGRAM);
1978  shader_cat.spam() << vertex_program << "\n";
1979  }
1980  if (_cg_fprogram != 0) {
1981  shader_cat.debug()
1982  << "Cg fragment profile: " << cgGetProfileString((CGprofile)_cg_fprofile) << "\n";
1983  fragment_program = cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM);
1984  shader_cat.spam() << fragment_program << "\n";
1985  }
1986  if (_cg_gprogram != 0) {
1987  shader_cat.debug()
1988  << "Cg geometry profile: " << cgGetProfileString((CGprofile)_cg_gprofile) << "\n";
1989  geometry_program = cgGetProgramString(_cg_gprogram, CG_COMPILED_PROGRAM);
1990  shader_cat.spam() << geometry_program << "\n";
1991  }
1992  }
1993 
1994  return true;
1995 }
1996 
1997 /**
1998  *
1999  */
2000 bool Shader::
2001 cg_analyze_entry_point(CGprogram prog, ShaderType type) {
2002  bool success = true;
2003 
2004  cg_recurse_parameters(cgGetFirstParameter(prog, CG_PROGRAM), type, success);
2005  return success;
2006 }
2007 
2008 /**
2009  * This subroutine analyzes the parameters of a Cg shader. The output is
2010  * stored in instance variables: _mat_spec, _var_spec, and _tex_spec.
2011  *
2012  * In order to do this, it is necessary to compile the shader. It would be a
2013  * waste of CPU time to compile the shader, analyze the parameters, and then
2014  * discard the compiled shader. This would force us to compile it again
2015  * later, when we need to build the ShaderContext. Instead, we cache the
2016  * compiled Cg program in instance variables. Later, a ShaderContext can pull
2017  * the compiled shader from these instance vars.
2018  *
2019  * To compile a shader, you need to first choose a profile. There are two
2020  * contradictory objectives:
2021  *
2022  * 1. If you don't use the gsg's active profile, then the cached compiled
2023  * shader will not be useful to the ShaderContext.
2024  *
2025  * 2. If you use too weak a profile, then the shader may not compile. So to
2026  * guarantee success, you should use the ultimate profile.
2027  *
2028  * To resolve this conflict, we try the active profile first, and if that
2029  * doesn't work, we try the ultimate profile.
2030  *
2031  */
2032 bool Shader::
2033 cg_analyze_shader(const ShaderCaps &caps) {
2034 
2035  // Make sure we have a context for analyzing the shader.
2036  if (_cg_context == 0) {
2037  _cg_context = cgCreateContext();
2038  if (_cg_context == 0) {
2039  shader_cat.error()
2040  << "Could not create a Cg context object: "
2041  << cgGetErrorString(cgGetError()) << "\n";
2042  return false;
2043  }
2044  }
2045 
2046  if (!cg_compile_shader(caps, _cg_context)) {
2047  return false;
2048  }
2049 
2050  if (_cg_fprogram != 0) {
2051  if (!cg_analyze_entry_point(_cg_fprogram, ST_fragment)) {
2052  cg_release_resources();
2053  clear_parameters();
2054  return false;
2055  }
2056  }
2057 
2058  if (_var_spec.size() != 0) {
2059  shader_cat.error() << "Cannot use vtx parameters in an fshader\n";
2060  cg_release_resources();
2061  clear_parameters();
2062  return false;
2063  }
2064 
2065  if (_cg_vprogram != 0) {
2066  if (!cg_analyze_entry_point(_cg_vprogram, ST_vertex)) {
2067  cg_release_resources();
2068  clear_parameters();
2069  return false;
2070  }
2071  }
2072 
2073  if (_cg_gprogram != 0) {
2074  if (!cg_analyze_entry_point(_cg_gprogram, ST_geometry)) {
2075  cg_release_resources();
2076  clear_parameters();
2077  return false;
2078  }
2079  }
2080 
2081  // Assign sequence numbers to all parameters. GLCgShaderContext relies on
2082  // the fact that the varyings start at seqno 0.
2083  int seqno = 0;
2084  for (size_t i = 0; i < _var_spec.size(); ++i) {
2085  _var_spec[i]._id._seqno = seqno++;
2086  }
2087  for (size_t i = 0; i < _mat_spec.size(); ++i) {
2088  _mat_spec[i]._id._seqno = seqno++;
2089  }
2090  for (size_t i = 0; i < _tex_spec.size(); ++i) {
2091  _tex_spec[i]._id._seqno = seqno++;
2092  }
2093 
2094  for (size_t i = 0; i < _ptr_spec.size(); ++i) {
2095  _ptr_spec[i]._id._seqno = seqno++;
2096  _ptr_spec[i]._info._id = _ptr_spec[i]._id;
2097  }
2098 
2099  /*
2100  // The following code is present to work around a bug in the Cg compiler.
2101  // It does not generate correct code for shadow map lookups when using arbfp1.
2102  // This is a particularly onerous limitation, given that arbfp1 is the only
2103  // Cg target that works on radeons. I suspect this is an intentional
2104  // omission on nvidia's part. The following code fetches the output listing,
2105  // detects the error, repairs the code, and resumbits the repaired code to Cg.
2106  if ((_cg_fprofile == CG_PROFILE_ARBFP1) && (gsghint->_supports_shadow_filter)) {
2107  bool shadowunit[32];
2108  bool anyshadow = false;
2109  memset(shadowunit, 0, sizeof(shadowunit));
2110  vector_string lines;
2111  tokenize(cgGetProgramString(_cg_program[SHADER_type_frag],
2112  CG_COMPILED_PROGRAM), lines, "\n");
2113  // figure out which texture units contain shadow maps.
2114  for (int lineno=0; lineno<(int)lines.size(); lineno++) {
2115  if (lines[lineno].compare(0,21,"#var sampler2DSHADOW ")) {
2116  continue;
2117  }
2118  vector_string fields;
2119  tokenize(lines[lineno], fields, ":");
2120  if (fields.size()!=5) {
2121  continue;
2122  }
2123  vector_string words;
2124  tokenize(trim(fields[2]), words, " ");
2125  if (words.size()!=2) {
2126  continue;
2127  }
2128  int unit = atoi(words[1].c_str());
2129  if ((unit < 0)||(unit >= 32)) {
2130  continue;
2131  }
2132  anyshadow = true;
2133  shadowunit[unit] = true;
2134  }
2135  // modify all TEX statements that use the relevant texture units.
2136  if (anyshadow) {
2137  for (int lineno=0; lineno<(int)lines.size(); lineno++) {
2138  if (lines[lineno].compare(0,4,"TEX ")) {
2139  continue;
2140  }
2141  vector_string fields;
2142  tokenize(lines[lineno], fields, ",");
2143  if ((fields.size()!=4)||(trim(fields[3]) != "2D;")) {
2144  continue;
2145  }
2146  vector_string texunitf;
2147  tokenize(trim(fields[2]), texunitf, "[]");
2148  if ((texunitf.size()!=3)||(texunitf[0] != "texture")||(texunitf[2]!="")) {
2149  continue;
2150  }
2151  int unit = atoi(texunitf[1].c_str());
2152  if ((unit < 0) || (unit >= 32) || (shadowunit[unit]==false)) {
2153  continue;
2154  }
2155  lines[lineno] = fields[0]+","+fields[1]+","+fields[2]+", SHADOW2D;";
2156  }
2157  string result = "!!ARBfp1.0\nOPTION ARB_fragment_program_shadow;\n";
2158  for (int lineno=1; lineno<(int)lines.size(); lineno++) {
2159  result += (lines[lineno] + "\n");
2160  }
2161  _cg_program[2] = _cg_program[SHADER_type_frag];
2162  _cg_program[SHADER_type_frag] =
2163  cgCreateProgram(_cg_context, CG_OBJECT, result.c_str(),
2164  _cg_profile[SHADER_type_frag], "fshader", (const char**)NULL);
2165  cg_report_errors(s->get_name(), _cg_context);
2166  if (_cg_program[SHADER_type_frag]==0) {
2167  release_resources();
2168  return false;
2169  }
2170  }
2171  }
2172  */
2173 
2174  cg_release_resources();
2175  return true;
2176 }
2177 
2178 /**
2179  * Returns the CGprogram of the given shadertype that belongs to this shader.
2180  */
2181 CGprogram Shader::
2182 cg_program_from_shadertype(ShaderType type) {
2183  switch (type) {
2184  case ST_vertex:
2185  return _cg_vprogram;
2186 
2187  case ST_fragment:
2188  return _cg_fprogram;
2189 
2190  case ST_geometry:
2191  return _cg_gprogram;
2192 
2193  default:
2194  return 0;
2195  }
2196 }
2197 
2198 /**
2199  * This routine is used by the ShaderContext constructor to compile the
2200  * shader. The CGprogram objects are turned over to the ShaderContext, we no
2201  * longer own them.
2202  */
2203 bool Shader::
2204 cg_compile_for(const ShaderCaps &caps, CGcontext context,
2205  CGprogram &combined_program, pvector<CGparameter> &map) {
2206 
2207  // Initialize the return values to empty.
2208  combined_program = 0;
2209  map.clear();
2210 
2211  // Make sure the shader is compiled for the target caps. Most of the time,
2212  // it will already be - this is usually a no-op.
2213 
2214  _default_caps = caps;
2215  if (!cg_compile_shader(caps, context)) {
2216  return false;
2217  }
2218 
2219  // If the compile routine used the ultimate profile instead of the active
2220  // one, it means the active one isn't powerful enough to compile the shader.
2221  if (_cg_vprogram != 0 && _cg_vprofile != caps._active_vprofile) {
2222  shader_cat.error() << "Cg vertex program not supported by profile "
2223  << cgGetProfileString((CGprofile) caps._active_vprofile) << ": "
2224  << get_filename(ST_vertex) << ". Try choosing a different profile.\n";
2225  return false;
2226  }
2227  if (_cg_fprogram != 0 && _cg_fprofile != caps._active_fprofile) {
2228  shader_cat.error() << "Cg fragment program not supported by profile "
2229  << cgGetProfileString((CGprofile) caps._active_fprofile) << ": "
2230  << get_filename(ST_fragment) << ". Try choosing a different profile.\n";
2231  return false;
2232  }
2233  if (_cg_gprogram != 0 && _cg_gprofile != caps._active_gprofile) {
2234  shader_cat.error() << "Cg geometry program not supported by profile "
2235  << cgGetProfileString((CGprofile) caps._active_gprofile) << ": "
2236  << get_filename(ST_geometry) << ". Try choosing a different profile.\n";
2237  return false;
2238  }
2239 
2240  // Gather the programs we will be combining.
2241  pvector<CGprogram> programs;
2242  if (_cg_vprogram != 0) {
2243  programs.push_back(_cg_vprogram);
2244  }
2245  if (_cg_fprogram != 0) {
2246  programs.push_back(_cg_fprogram);
2247  }
2248  if (_cg_gprogram != 0) {
2249  programs.push_back(_cg_gprogram);
2250  }
2251 
2252  // Combine the programs. This can be more optimal than loading them
2253  // individually, and it is even necessary for some profiles (particularly
2254  // GLSL profiles on non-NVIDIA GPUs).
2255  combined_program = cgCombinePrograms(programs.size(), &programs[0]);
2256 
2257  // Build a parameter map.
2258  size_t n_mat = _mat_spec.size();
2259  size_t n_tex = _tex_spec.size();
2260  size_t n_var = _var_spec.size();
2261  size_t n_ptr = _ptr_spec.size();
2262 
2263  map.resize(n_mat + n_tex + n_var + n_ptr);
2264 
2265  // This is a bit awkward, we have to go in and seperate out the combined
2266  // program, since all the parameter bindings have changed.
2267  CGprogram programs_by_type[ST_COUNT];
2268  for (int i = 0; i < cgGetNumProgramDomains(combined_program); ++i) {
2269  // Conveniently, the CGdomain enum overlaps with ShaderType.
2270  CGprogram program = cgGetProgramDomainProgram(combined_program, i);
2271  programs_by_type[cgGetProgramDomain(program)] = program;
2272  }
2273 
2274  for (size_t i = 0; i < n_mat; ++i) {
2275  const ShaderArgId &id = _mat_spec[i]._id;
2276  map[id._seqno] = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2277 
2278  if (shader_cat.is_debug()) {
2279  const char *resource = cgGetParameterResourceName(map[id._seqno]);
2280  if (resource != nullptr) {
2281  shader_cat.debug() << "Uniform parameter " << id._name
2282  << " is bound to resource " << resource << "\n";
2283  }
2284  }
2285  }
2286 
2287  for (size_t i = 0; i < n_tex; ++i) {
2288  const ShaderArgId &id = _tex_spec[i]._id;
2289  CGparameter p = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2290 
2291  if (shader_cat.is_debug()) {
2292  const char *resource = cgGetParameterResourceName(p);
2293  if (resource != nullptr) {
2294  shader_cat.debug() << "Texture parameter " << id._name
2295  << " is bound to resource " << resource << "\n";
2296  }
2297  }
2298  map[id._seqno] = p;
2299  }
2300 
2301  for (size_t i = 0; i < n_var; ++i) {
2302  const ShaderArgId &id = _var_spec[i]._id;
2303  CGparameter p = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2304 
2305  const char *resource = cgGetParameterResourceName(p);
2306  if (shader_cat.is_debug() && resource != nullptr) {
2307  if (cgGetParameterResource(p) == CG_GLSL_ATTRIB) {
2308  shader_cat.debug()
2309  << "Varying parameter " << id._name << " is bound to GLSL attribute "
2310  << resource << "\n";
2311  } else {
2312  shader_cat.debug()
2313  << "Varying parameter " << id._name << " is bound to resource "
2314  << resource << " (" << cgGetParameterResource(p)
2315  << ", index " << cgGetParameterResourceIndex(p) << ")\n";
2316  }
2317  }
2318 
2319  map[id._seqno] = p;
2320  }
2321 
2322  for (size_t i = 0; i < n_ptr; ++i) {
2323  const ShaderArgId &id = _ptr_spec[i]._id;
2324  map[id._seqno] = cgGetNamedParameter(programs_by_type[id._type], id._name.c_str());
2325 
2326  if (shader_cat.is_debug()) {
2327  const char *resource = cgGetParameterResourceName(map[id._seqno]);
2328  if (resource != nullptr) {
2329  shader_cat.debug() << "Uniform ptr parameter " << id._name
2330  << " is bound to resource " << resource << "\n";
2331  }
2332  }
2333  }
2334 
2335  // Transfer ownership of the compiled shader.
2336  if (_cg_vprogram != 0) {
2337  cgDestroyProgram(_cg_vprogram);
2338  _cg_vprogram = 0;
2339  }
2340  if (_cg_fprogram != 0) {
2341  cgDestroyProgram(_cg_fprogram);
2342  _cg_fprogram = 0;
2343  }
2344  if (_cg_gprogram != 0) {
2345  cgDestroyProgram(_cg_gprogram);
2346  _cg_gprogram = 0;
2347  }
2348 
2349  _cg_last_caps.clear();
2350 
2351  return true;
2352 }
2353 #endif // HAVE_CG
2354 
2355 /**
2356  * Construct a Shader that will be filled in using fillin() or read() later.
2357  */
2358 Shader::
2359 Shader(ShaderLanguage lang) :
2360  _error_flag(false),
2361  _parse(0),
2362  _loaded(false),
2363  _language(lang),
2364  _last_modified(0),
2365  _mat_deps(0),
2366  _cache_compiled_shader(false)
2367 {
2368 #ifdef HAVE_CG
2369  _cg_vprogram = 0;
2370  _cg_fprogram = 0;
2371  _cg_gprogram = 0;
2372  _cg_vprofile = CG_PROFILE_UNKNOWN;
2373  _cg_fprofile = CG_PROFILE_UNKNOWN;
2374  _cg_gprofile = CG_PROFILE_UNKNOWN;
2375  if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) {
2376  if (basic_shaders_only) {
2377  _default_caps._active_vprofile = CG_PROFILE_ARBVP1;
2378  _default_caps._active_fprofile = CG_PROFILE_ARBFP1;
2379  _default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
2380  } else {
2381  _default_caps._active_vprofile = CG_PROFILE_UNKNOWN;
2382  _default_caps._active_fprofile = CG_PROFILE_UNKNOWN;
2383  _default_caps._active_gprofile = CG_PROFILE_UNKNOWN;
2384  }
2385  _default_caps._ultimate_vprofile = cgGetProfile("glslv");
2386  _default_caps._ultimate_fprofile = cgGetProfile("glslf");
2387  _default_caps._ultimate_gprofile = cgGetProfile("glslg");
2388  if (_default_caps._ultimate_gprofile == CG_PROFILE_UNKNOWN) {
2389  _default_caps._ultimate_gprofile = cgGetProfile("gp4gp");
2390  }
2391  }
2392 #endif
2393 }
2394 
2395 /**
2396  * Reads the shader from the given filename(s). Returns a boolean indicating
2397  * success or failure.
2398  */
2399 bool Shader::
2400 read(const ShaderFile &sfile, BamCacheRecord *record) {
2401  _text._separate = sfile._separate;
2402 
2403  if (sfile._separate) {
2404  if (_language == SL_none) {
2405  shader_cat.error()
2406  << "No shader language was specified!\n";
2407  return false;
2408  }
2409 
2410  if (!sfile._vertex.empty() &&
2411  !do_read_source(_text._vertex, sfile._vertex, record)) {
2412  return false;
2413  }
2414  if (!sfile._fragment.empty() &&
2415  !do_read_source(_text._fragment, sfile._fragment, record)) {
2416  return false;
2417  }
2418  if (!sfile._geometry.empty() &&
2419  !do_read_source(_text._geometry, sfile._geometry, record)) {
2420  return false;
2421  }
2422  if (!sfile._tess_control.empty() &&
2423  !do_read_source(_text._tess_control, sfile._tess_control, record)) {
2424  return false;
2425  }
2426  if (!sfile._tess_evaluation.empty() &&
2427  !do_read_source(_text._tess_evaluation, sfile._tess_evaluation, record)) {
2428  return false;
2429  }
2430  if (!sfile._compute.empty() &&
2431  !do_read_source(_text._compute, sfile._compute, record)) {
2432  return false;
2433  }
2434  _filename = sfile;
2435 
2436  } else {
2437  if (!do_read_source(_text._shared, sfile._shared, record)) {
2438  return false;
2439  }
2440  _fullpath = _source_files[0];
2441  _filename = sfile;
2442 
2443  // Determine which language the shader is written in.
2444  if (_language == SL_none) {
2445  string header;
2446  parse_init();
2447  parse_line(header, true, true);
2448  if (header == "//Cg") {
2449  _language = SL_Cg;
2450  } else {
2451  shader_cat.error()
2452  << "Unable to determine shader language of " << sfile._shared << "\n";
2453  return false;
2454  }
2455  } else if (_language == SL_GLSL) {
2456  shader_cat.error()
2457  << "GLSL shaders must have separate shader bodies!\n";
2458  return false;
2459  }
2460 
2461  // Determine which language the shader is written in.
2462  if (_language == SL_Cg) {
2463 #ifdef HAVE_CG
2464  ShaderCaps caps = _default_caps;
2465  cg_get_profile_from_header(caps);
2466 
2467  if (!cg_analyze_shader(caps)) {
2468  shader_cat.error()
2469  << "Shader encountered an error.\n";
2470  return false;
2471  }
2472 #else
2473  shader_cat.error()
2474  << "Tried to load Cg shader, but no Cg support is enabled.\n";
2475 #endif
2476  } else {
2477  shader_cat.error()
2478  << "Shader is not in a supported shader-language.\n";
2479  return false;
2480  }
2481  }
2482 
2483  _loaded = true;
2484  return true;
2485 }
2486 
2487 /**
2488  * Loads the shader from the given string(s). Returns a boolean indicating
2489  * success or failure.
2490  */
2491 bool Shader::
2492 load(const ShaderFile &sbody, BamCacheRecord *record) {
2493  _filename = ShaderFile("created-shader");
2494  _fullpath = Filename();
2495  _text._separate = sbody._separate;
2496 
2497  if (sbody._separate) {
2498  if (_language == SL_none) {
2499  shader_cat.error()
2500  << "No shader language was specified!\n";
2501  return false;
2502  }
2503 
2504  if (!sbody._vertex.empty() &&
2505  !do_load_source(_text._vertex, sbody._vertex, record)) {
2506  return false;
2507  }
2508  if (!sbody._fragment.empty() &&
2509  !do_load_source(_text._fragment, sbody._fragment, record)) {
2510  return false;
2511  }
2512  if (!sbody._geometry.empty() &&
2513  !do_load_source(_text._geometry, sbody._geometry, record)) {
2514  return false;
2515  }
2516  if (!sbody._tess_control.empty() &&
2517  !do_load_source(_text._tess_control, sbody._tess_control, record)) {
2518  return false;
2519  }
2520  if (!sbody._tess_evaluation.empty() &&
2521  !do_load_source(_text._tess_evaluation, sbody._tess_evaluation, record)) {
2522  return false;
2523  }
2524  if (!sbody._compute.empty() &&
2525  !do_load_source(_text._compute, sbody._compute, record)) {
2526  return false;
2527  }
2528 
2529  } else {
2530  if (!do_load_source(_text._shared, sbody._shared, record)) {
2531  return false;
2532  }
2533 
2534  // Determine which language the shader is written in.
2535  if (_language == SL_none) {
2536  string header;
2537  parse_init();
2538  parse_line(header, true, true);
2539  if (header == "//Cg") {
2540  _language = SL_Cg;
2541  } else {
2542  shader_cat.error()
2543  << "Unable to determine shader language of " << sbody._shared << "\n";
2544  return false;
2545  }
2546  } else if (_language == SL_GLSL) {
2547  shader_cat.error()
2548  << "GLSL shaders must have separate shader bodies!\n";
2549  return false;
2550  }
2551 
2552  // Determine which language the shader is written in.
2553  if (_language == SL_Cg) {
2554 #ifdef HAVE_CG
2555  ShaderCaps caps = _default_caps;
2556  cg_get_profile_from_header(caps);
2557 
2558  if (!cg_analyze_shader(caps)) {
2559  shader_cat.error()
2560  << "Shader encountered an error.\n";
2561  return false;
2562  }
2563 #else
2564  shader_cat.error()
2565  << "Tried to load Cg shader, but no Cg support is enabled.\n";
2566 #endif
2567  } else {
2568  shader_cat.error()
2569  << "Shader is not in a supported shader-language.\n";
2570  return false;
2571  }
2572  }
2573 
2574  _loaded = true;
2575  return true;
2576 }
2577 
2578 /**
2579  * Reads the shader file from the given path into the given string.
2580  *
2581  * Returns false if there was an error with this shader bad enough to consider
2582  * it 'invalid'.
2583  */
2584 bool Shader::
2585 do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
2587  PT(VirtualFile) vf = vfs->find_file(fn, get_model_path());
2588  if (vf == nullptr) {
2589  shader_cat.error()
2590  << "Could not find shader file: " << fn << "\n";
2591  return false;
2592  }
2593 
2594  if (_language == SL_GLSL && glsl_preprocess) {
2595  istream *source = vf->open_read_file(true);
2596  if (source == nullptr) {
2597  shader_cat.error()
2598  << "Could not open shader file: " << fn << "\n";
2599  return false;
2600  }
2601 
2602  // Preprocess the GLSL file as we read it.
2603  shader_cat.info()
2604  << "Preprocessing shader file: " << fn << "\n";
2605 
2606  std::set<Filename> open_files;
2607  ostringstream sstr;
2608  if (!r_preprocess_source(sstr, *source, fn, vf->get_filename(), open_files, record)) {
2609  vf->close_read_file(source);
2610  return false;
2611  }
2612  vf->close_read_file(source);
2613  into = sstr.str();
2614 
2615  } else {
2616  shader_cat.info() << "Reading shader file: " << fn << "\n";
2617 
2618  if (!vf->read_file(into, true)) {
2619  shader_cat.error()
2620  << "Could not read shader file: " << fn << "\n";
2621  return false;
2622  }
2623  }
2624 
2625  if (record != nullptr) {
2626  record->add_dependent_file(vf);
2627  }
2628 
2629  _last_modified = std::max(_last_modified, vf->get_timestamp());
2630  _source_files.push_back(vf->get_filename());
2631 
2632  // Strip trailing whitespace.
2633  while (!into.empty() && isspace(into[into.size() - 1])) {
2634  into.resize(into.size() - 1);
2635  }
2636 
2637  // Except add back a newline at the end, which is needed by Intel drivers.
2638  into += "\n";
2639 
2640  return true;
2641 }
2642 
2643 /**
2644  * Loads the shader file from the given string into the given string,
2645  * performing any pre-processing on it that may be necessary.
2646  *
2647  * Returns false if there was an error with this shader bad enough to consider
2648  * it 'invalid'.
2649  */
2650 bool Shader::
2651 do_load_source(string &into, const std::string &source, BamCacheRecord *record) {
2652  if (_language == SL_GLSL && glsl_preprocess) {
2653  // Preprocess the GLSL file as we read it.
2654  std::set<Filename> open_files;
2655  std::ostringstream sstr;
2656  std::istringstream in(source);
2657  if (!r_preprocess_source(sstr, in, Filename("created-shader"), Filename(),
2658  open_files, record)) {
2659  return false;
2660  }
2661  into = sstr.str();
2662 
2663  } else {
2664  into = source;
2665  }
2666 
2667  // Strip trailing whitespace.
2668  while (!into.empty() && isspace(into[into.size() - 1])) {
2669  into.resize(into.size() - 1);
2670  }
2671 
2672  // Except add back a newline at the end, which is needed by Intel drivers.
2673  into += "\n";
2674 
2675  return true;
2676 }
2677 
2678 /**
2679  * Loads a given GLSL file line by line, and processes any #pragma include and
2680  * once statements, as well as removes any comments.
2681  *
2682  * The set keeps track of which files we have already included, for checking
2683  * recursive includes.
2684  */
2685 bool Shader::
2686 r_preprocess_include(ostream &out, const Filename &fn,
2687  const Filename &source_dir,
2688  std::set<Filename> &once_files,
2689  BamCacheRecord *record, int depth) {
2690 
2691  if (depth > glsl_include_recursion_limit) {
2692  shader_cat.error()
2693  << "GLSL includes nested too deeply, raise glsl-include-recursion-limit"
2694  " if necessary\n";
2695  return false;
2696  }
2697 
2698  DSearchPath path(get_model_path());
2699  if (!source_dir.empty()) {
2700  path.prepend_directory(source_dir);
2701  }
2702 
2704  PT(VirtualFile) vf = vfs->find_file(fn, path);
2705  if (vf == nullptr) {
2706  shader_cat.error()
2707  << "Could not find shader include: " << fn << "\n";
2708  return false;
2709  }
2710 
2711  Filename full_fn = vf->get_filename();
2712  if (once_files.find(full_fn) != once_files.end()) {
2713  // If this file had a #pragma once, just move on.
2714  return true;
2715  }
2716 
2717  istream *source = vf->open_read_file(true);
2718  if (source == nullptr) {
2719  shader_cat.error()
2720  << "Could not open shader include: " << fn << "\n";
2721  return false;
2722  }
2723 
2724  if (record != nullptr) {
2725  record->add_dependent_file(vf);
2726  }
2727  _last_modified = std::max(_last_modified, vf->get_timestamp());
2728  _source_files.push_back(full_fn);
2729 
2730  // We give each file an unique index. This is so that we can identify a
2731  // particular shader in the error output. We offset them by 2048 so that
2732  // they are more recognizable. GLSL doesn't give us anything more useful
2733  // than that, unfortunately. Don't do this for the top-level file, though.
2734  // We don't want anything to get in before a potential #version directive.
2735  int fileno = 0;
2736  fileno = 2048 + _included_files.size();
2737 
2738  // Write it into the vector so that we can substitute it later when we are
2739  // parsing the GLSL error log. Don't store the full filename because it
2740  // would just be too long to display.
2741  _included_files.push_back(fn);
2742 
2743  if (shader_cat.is_debug()) {
2744  shader_cat.debug()
2745  << "Preprocessing shader include " << fileno << ": " << fn << "\n";
2746  }
2747 
2748  bool result = r_preprocess_source(out, *source, fn, full_fn, once_files, record, fileno, depth);
2749  vf->close_read_file(source);
2750  return result;
2751 }
2752 
2753 /**
2754  * Loads a given GLSL stream line by line, processing any #pragma include and
2755  * once statements, as well as removing any comments.
2756  *
2757  * The set keeps track of which files we have already included, for checking
2758  * recursive includes.
2759  */
2760 bool Shader::
2761 r_preprocess_source(ostream &out, istream &in, const Filename &fn,
2762  const Filename &full_fn, std::set<Filename> &once_files,
2763  BamCacheRecord *record, int fileno, int depth) {
2764 
2765  // Iterate over the lines for things we may need to preprocess.
2766  string line;
2767  int ext_google_include = 0; // 1 = warn, 2 = enable
2768  int ext_google_line = 0;
2769  bool had_include = false;
2770  bool had_version = false;
2771  int lineno = 0;
2772  bool write_line_directive = (fileno != 0);
2773 
2774  while (std::getline(in, line)) {
2775  ++lineno;
2776 
2777  if (line.empty()) {
2778  // We still write a newline to make sure the line numbering remains
2779  // consistent, unless we are about to write a #line directive anyway.
2780  if (!write_line_directive) {
2781  out.put('\n');
2782  }
2783  continue;
2784  }
2785 
2786  // If the line ends with a backslash, concatenate the following line.
2787  // Preprocessor definitions may be broken up into multiple lines.
2788  while (line[line.size() - 1] == '\\') {
2789  line.resize(line.size() - 1);
2790  string line2;
2791 
2792  if (std::getline(in, line2)) {
2793  line += line2;
2794  if (!write_line_directive) {
2795  out.put('\n');
2796  }
2797  ++lineno;
2798  } else {
2799  break;
2800  }
2801  }
2802 
2803  // Look for comments to strip. This is necessary because comments may
2804  // appear in the middle of or around a preprocessor definition.
2805  size_t line_comment = line.find("//");
2806  size_t block_comment = line.find("/*");
2807  if (line_comment < block_comment) {
2808  // A line comment - strip off the rest of the line.
2809  line.resize(line_comment);
2810 
2811  } else if (block_comment < line_comment) {
2812  // A block comment. Search for closing block.
2813  string line2 = line.substr(block_comment + 2);
2814 
2815  // According to the GLSL specification, a block comment is replaced with
2816  // a single whitespace character.
2817  line.resize(block_comment);
2818  line += ' ';
2819 
2820  size_t block_end = line2.find("*/");
2821  while (block_end == string::npos) {
2822  // Didn't find it - look in the next line.
2823  if (std::getline(in, line2)) {
2824  if (!write_line_directive) {
2825  out.put('\n');
2826  }
2827  ++lineno;
2828  block_end = line2.find("*/");
2829  } else {
2830  shader_cat.error()
2831  << "Expected */ before end of file " << fn << "\n";
2832  return false;
2833  }
2834  }
2835 
2836  line += line2.substr(block_end + 2);
2837  }
2838 
2839  // Strip trailing whitespace.
2840  while (!line.empty() && isspace(line[line.size() - 1])) {
2841  line.resize(line.size() - 1);
2842  }
2843 
2844  if (line.empty()) {
2845  if (!write_line_directive) {
2846  out.put('\n');
2847  }
2848  continue;
2849  }
2850 
2851  // Check if this line contains a #directive.
2852  char directive[64];
2853  if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) {
2854  // Nope. Just pass the line through unmodified.
2855  if (write_line_directive) {
2856  out << "#line " << lineno << " " << fileno << " // " << fn << "\n";
2857  write_line_directive = false;
2858  }
2859  out << line << "\n";
2860  continue;
2861  }
2862 
2863  char pragma[64];
2864  size_t nread = 0;
2865  // What kind of directive is it?
2866  if (strcmp(directive, "pragma") == 0 &&
2867  sscanf(line.c_str(), " # pragma %63s", pragma) == 1) {
2868  if (strcmp(pragma, "include") == 0) {
2869  // Allow both double quotes and angle brackets.
2870  Filename incfn, source_dir;
2871  {
2872  char incfile[2048];
2873  if (sscanf(line.c_str(), " # pragma%*[ \t]include \"%2047[^\"]\" %zn", incfile, &nread) == 1
2874  && nread == line.size()) {
2875  // A regular include, with double quotes. Probably a local file.
2876  source_dir = full_fn.get_dirname();
2877  incfn = incfile;
2878 
2879  } else if (sscanf(line.c_str(), " # pragma%*[ \t]include <%2047[^\"]> %zn", incfile, &nread) == 1
2880  && nread == line.size()) {
2881  // Angled includes are also OK, but we don't search in the directory
2882  // of the source file.
2883  incfn = incfile;
2884 
2885  } else {
2886  // Couldn't parse it.
2887  shader_cat.error()
2888  << "Malformed #pragma include at line " << lineno
2889  << " of file " << fn << ":\n " << line << "\n";
2890  return false;
2891  }
2892  }
2893 
2894  // OK, great. Process the include.
2895  if (!r_preprocess_include(out, incfn, source_dir, once_files, record, depth + 1)) {
2896  // An error occurred. Pass on the failure.
2897  shader_cat.error(false) << "included at line "
2898  << lineno << " of file " << fn << ":\n " << line << "\n";
2899  return false;
2900  }
2901 
2902  // Restore the line counter.
2903  write_line_directive = true;
2904  had_include = true;
2905  continue;
2906 
2907  } else if (strcmp(pragma, "once") == 0) {
2908  // Do a stricter syntax check, just to be extra safe.
2909  if (sscanf(line.c_str(), " # pragma%*[ \t]once %zn", &nread) != 0 ||
2910  nread != line.size()) {
2911  shader_cat.error()
2912  << "Malformed #pragma once at line " << lineno
2913  << " of file " << fn << ":\n " << line << "\n";
2914  return false;
2915  }
2916 
2917  if (fileno == 0) {
2918  shader_cat.warning()
2919  << "#pragma once in main file at line "
2920  << lineno << " of file " << fn
2921 #ifndef NDEBUG
2922  << ":\n " << line
2923 #endif
2924  << "\n";
2925  }
2926 
2927  if (!full_fn.empty()) {
2928  once_files.insert(full_fn);
2929  }
2930  continue;
2931  }
2932  // Otherwise, just pass it through to the driver.
2933 
2934  } else if (strcmp(directive, "endif") == 0) {
2935  // Check for an #endif after an include. We have to restore the line
2936  // number in case the include happened under an #if block.
2937  if (had_include) {
2938  write_line_directive = true;
2939  }
2940 
2941  } else if (strcmp(directive, "version") == 0) {
2942  had_version = true;
2943 
2944  } else if (strcmp(directive, "extension") == 0) {
2945  // Check for special preprocessing extensions.
2946  char extension[256];
2947  char behavior[9];
2948  if (sscanf(line.c_str(), " # extension%*[ \t]%255[^: \t] : %8s", extension, behavior) == 2) {
2949  // Parse the behavior string.
2950  int mode;
2951  if (strcmp(behavior, "require") == 0 || strcmp(behavior, "enable") == 0) {
2952  mode = 2;
2953  } else if (strcmp(behavior, "warn") == 0) {
2954  mode = 1;
2955  } else if (strcmp(behavior, "disable") == 0) {
2956  mode = 0;
2957  } else {
2958  shader_cat.error()
2959  << "Extension directive specifies invalid behavior at line "
2960  << lineno << " of file " << fn << ":\n " << line << "\n";
2961  return false;
2962  }
2963 
2964  if (strcmp(extension, "all") == 0) {
2965  if (mode == 2) {
2966  shader_cat.error()
2967  << "Extension directive for 'all' may only specify 'warn' or "
2968  "'disable' at line " << lineno << " of file " << fn
2969  << ":\n " << line << "\n";
2970  return false;
2971  }
2972  ext_google_include = mode;
2973  ext_google_line = mode;
2974  // Still pass it through to the driver, so it can enable other
2975  // extensions.
2976 
2977  } else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) {
2978  // Enable the Google extension support for #include statements.
2979  // This also implicitly enables GL_GOOGLE_cpp_style_line_directive.
2980  // This matches the behavior of Khronos' glslang reference compiler.
2981  ext_google_include = mode;
2982  ext_google_line = mode;
2983  continue;
2984 
2985  } else if (strcmp(extension, "GL_GOOGLE_cpp_style_line_directive") == 0) {
2986  // Enables strings in #line statements.
2987  ext_google_line = mode;
2988  continue;
2989  }
2990  } else {
2991  shader_cat.error()
2992  << "Failed to parse extension directive at line "
2993  << lineno << " of file " << fn << ":\n " << line << "\n";
2994  return false;
2995  }
2996 
2997  } else if (ext_google_include > 0 && strcmp(directive, "include") == 0) {
2998  // Warn about extension use if requested.
2999  if (ext_google_include == 1) {
3000  shader_cat.warning()
3001  << "Extension GL_GOOGLE_include_directive is being used at line "
3002  << lineno << " of file " << fn
3003 #ifndef NDEBUG
3004  << ":\n " << line
3005 #endif
3006  << "\n";
3007  }
3008 
3009  // This syntax allows only double quotes, not angle brackets.
3010  Filename incfn;
3011  {
3012  char incfile[2048];
3013  if (sscanf(line.c_str(), " # include%*[ \t]\"%2047[^\"]\" %zn", incfile, &nread) != 1
3014  || nread != line.size()) {
3015  // Couldn't parse it.
3016  shader_cat.error()
3017  << "Malformed #include at line " << lineno
3018  << " of file " << fn << ":\n " << line << "\n";
3019  return false;
3020  }
3021  incfn = incfile;
3022  }
3023 
3024  // OK, great. Process the include.
3025  Filename source_dir = full_fn.get_dirname();
3026  if (!r_preprocess_include(out, incfn, source_dir, once_files, record, depth + 1)) {
3027  // An error occurred. Pass on the failure.
3028  shader_cat.error(false) << "included at line "
3029  << lineno << " of file " << fn << ":\n " << line << "\n";
3030  return false;
3031  }
3032 
3033  // Restore the line counter.
3034  write_line_directive = true;
3035  had_include = true;
3036  continue;
3037 
3038  } else if (ext_google_line > 0 && strcmp(directive, "line") == 0) {
3039  // It's a #line directive. See if it uses a string instead of number.
3040  char filestr[2048];
3041  if (sscanf(line.c_str(), " # line%*[ \t]%d%*[ \t]\"%2047[^\"]\" %zn", &lineno, filestr, &nread) == 2
3042  && nread == line.size()) {
3043  // Warn about extension use if requested.
3044  if (ext_google_line == 1) {
3045  shader_cat.warning()
3046  << "Extension GL_GOOGLE_cpp_style_line_directive is being used at line "
3047  << lineno << " of file " << fn
3048 #ifndef NDEBUG
3049  << ":\n " << line
3050 #endif
3051  << "\n";
3052  }
3053 
3054  // Replace the string line number with an integer. This is something
3055  // we can substitute later when parsing the GLSL log from the driver.
3056  fileno = 2048 + _included_files.size();
3057  _included_files.push_back(Filename(filestr));
3058 
3059  out << "#line " << lineno << " " << fileno << " // " << filestr << "\n";
3060  continue;
3061  }
3062  }
3063 
3064  if (write_line_directive) {
3065  out << "#line " << lineno << " " << fileno << " // " << fn << "\n";
3066  write_line_directive = false;
3067  }
3068  out << line << "\n";
3069  }
3070 
3071  if (fileno == 0 && !had_version) {
3072  shader_cat.warning()
3073  << "GLSL shader " << fn << " does not contain a #version line!\n";
3074  }
3075 
3076  return true;
3077 }
3078 
3079 /**
3080  * Checks whether the shader or any of its dependent files were modified on
3081  * disk.
3082  */
3083 bool Shader::
3084 check_modified() const {
3086 
3088  for (it = _source_files.begin(); it != _source_files.end(); ++it) {
3089  const Filename &fn = (*it);
3090 
3091  PT(VirtualFile) vfile = vfs->get_file(fn, true);
3092  if (vfile == nullptr || vfile->get_timestamp() > _last_modified) {
3093  return true;
3094  }
3095  }
3096 
3097  return false;
3098 }
3099 
3100 #ifdef HAVE_CG
3101 /**
3102  * Determines the appropriate active shader profile settings based on any
3103  * profile directives stored within the shader header
3104  */
3105 void Shader::
3106 cg_get_profile_from_header(ShaderCaps& caps) {
3107  // Note this forces profile based on what is specified in the shader header
3108  // string. Should probably be relying on card caps eventually.
3109 
3110  string buf;
3111  parse_init();
3112 
3113  // Assume that if parse doesn't extend after a parse line then we've reached
3114  // the end of _text
3115  int lastParse;
3116 
3117  do {
3118  lastParse = _parse;
3119  parse_line(buf, true, true);
3120  int profilePos = buf.find("//Cg profile");
3121  if (profilePos >= 0) {
3122  // Scan the line for known cg2 vertex program profiles
3123  if ((int)buf.find("gp4vp") >= 0)
3124  caps._active_vprofile = cgGetProfile("gp4vp");
3125 
3126  if ((int)buf.find("gp5vp") >= 0)
3127  caps._active_vprofile = cgGetProfile("gp5vp");
3128 
3129  if ((int)buf.find("glslv") >= 0)
3130  caps._active_vprofile = cgGetProfile("glslv");
3131 
3132  if ((int)buf.find("arbvp1") >= 0)
3133  caps._active_vprofile = cgGetProfile("arbvp1");
3134 
3135  if ((int)buf.find("vp40") >= 0)
3136  caps._active_vprofile = cgGetProfile("vp40");
3137 
3138  if ((int)buf.find("vp30") >= 0)
3139  caps._active_vprofile = cgGetProfile("vp30");
3140 
3141  if ((int)buf.find("vp20") >= 0)
3142  caps._active_vprofile = cgGetProfile("vp20");
3143 
3144  if ((int)buf.find("vs_1_1") >= 0)
3145  caps._active_vprofile = cgGetProfile("vs_1_1");
3146 
3147  if ((int)buf.find("vs_2_0") >= 0)
3148  caps._active_vprofile = cgGetProfile("vs_2_0");
3149 
3150  if ((int)buf.find("vs_2_x") >= 0)
3151  caps._active_vprofile = cgGetProfile("vs_2_x");
3152 
3153  if ((int)buf.find("vs_3_0") >= 0)
3154  caps._active_vprofile = cgGetProfile("vs_3_0");
3155 
3156  if ((int)buf.find("vs_4_0") >= 0)
3157  caps._active_vprofile = cgGetProfile("vs_4_0");
3158 
3159  if ((int)buf.find("vs_5_0") >= 0)
3160  caps._active_vprofile = cgGetProfile("vs_5_0");
3161 
3162  // Scan the line for known cg2 fragment program profiles
3163  if ((int)buf.find("gp4fp") >= 0)
3164  caps._active_fprofile = cgGetProfile("gp4fp");
3165 
3166  if ((int)buf.find("gp5fp") >= 0)
3167  caps._active_fprofile = cgGetProfile("gp5fp");
3168 
3169  if ((int)buf.find("glslf") >= 0)
3170  caps._active_fprofile = cgGetProfile("glslf");
3171 
3172  if ((int)buf.find("arbfp1") >= 0)
3173  caps._active_fprofile = cgGetProfile("arbfp1");
3174 
3175  if ((int)buf.find("fp40") >= 0)
3176  caps._active_fprofile = cgGetProfile("fp40");
3177 
3178  if ((int)buf.find("fp30") >= 0)
3179  caps._active_fprofile = cgGetProfile("fp30");
3180 
3181  if ((int)buf.find("fp20") >= 0)
3182  caps._active_fprofile = cgGetProfile("fp20");
3183 
3184  if ((int)buf.find("ps_1_1") >= 0)
3185  caps._active_fprofile = cgGetProfile("ps_1_1");
3186 
3187  if ((int)buf.find("ps_1_2") >= 0)
3188  caps._active_fprofile = cgGetProfile("ps_1_2");
3189 
3190  if ((int)buf.find("ps_1_3") >= 0)
3191  caps._active_fprofile = cgGetProfile("ps_1_3");
3192 
3193  if ((int)buf.find("ps_2_0") >= 0)
3194  caps._active_fprofile = cgGetProfile("ps_2_0");
3195 
3196  if ((int)buf.find("ps_2_x") >= 0)
3197  caps._active_fprofile = cgGetProfile("ps_2_x");
3198 
3199  if ((int)buf.find("ps_3_0") >= 0)
3200  caps._active_fprofile = cgGetProfile("ps_3_0");
3201 
3202  if ((int)buf.find("ps_4_0") >= 0)
3203  caps._active_fprofile = cgGetProfile("ps_4_0");
3204 
3205  if ((int)buf.find("ps_5_0") >= 0)
3206  caps._active_fprofile = cgGetProfile("ps_5_0");
3207 
3208  // Scan the line for known cg2 geometry program profiles
3209  if ((int)buf.find("gp4gp") >= 0)
3210  caps._active_gprofile = cgGetProfile("gp4gp");
3211 
3212  if ((int)buf.find("gp5gp") >= 0)
3213  caps._active_gprofile = cgGetProfile("gp5gp");
3214 
3215  if ((int)buf.find("glslg") >= 0)
3216  caps._active_gprofile = cgGetProfile("glslg");
3217 
3218  if ((int)buf.find("gs_4_0") >= 0)
3219  caps._active_gprofile = cgGetProfile("gs_4_0");
3220 
3221  if ((int)buf.find("gs_5_0") >= 0)
3222  caps._active_gprofile = cgGetProfile("gs_5_0");
3223  }
3224  } while(_parse > lastParse);
3225 
3226 }
3227 #endif
3228 
3229 /**
3230  * Delete the compiled code, if it exists.
3231  */
3233 ~Shader() {
3234  release_all();
3235  // Note: don't try to erase ourselves from the table. It currently keeps a
3236  // reference forever, and so the only place where this constructor is called
3237  // is in the destructor of the table itself.
3238  /*if (_loaded) {
3239  _load_table.erase(_filename);
3240  } else {
3241  _make_table.erase(_text);
3242  }*/
3243 }
3244 
3245 /**
3246  * Returns the filename of the included shader with the given source file
3247  * index (as recorded in the #line statement in r_preprocess_source). We use
3248  * this to associate error messages with included files.
3249  */
3251 get_filename_from_index(int index, ShaderType type) const {
3252  if (index == 0) {
3253  Filename fn = get_filename(type);
3254  if (!fn.empty()) {
3255  return fn;
3256  }
3257  } else if (glsl_preprocess && index >= 2048 &&
3258  (index - 2048) < (int)_included_files.size()) {
3259  return _included_files[(size_t)index - 2048];
3260  }
3261  // Must be a mistake. Quietly put back the integer.
3262  string str = format_string(index);
3263  return Filename(str);
3264 }
3265 
3266 /**
3267  * Loads the shader with the given filename.
3268  */
3269 PT(Shader) Shader::
3270 load(const Filename &file, ShaderLanguage lang) {
3271  ShaderFile sfile(file);
3272  ShaderTable::const_iterator i = _load_table.find(sfile);
3273  if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3274  // But check that someone hasn't modified it in the meantime.
3275  if (i->second->check_modified()) {
3276  shader_cat.info()
3277  << "Shader " << file << " was modified on disk, reloading.\n";
3278  } else {
3279  if (shader_cat.is_debug()) {
3280  shader_cat.debug()
3281  << "Shader " << file << " was found in shader cache.\n";
3282  }
3283  return i->second;
3284  }
3285  }
3286 
3287  PT(Shader) shader = new Shader(lang);
3288  if (!shader->read(sfile)) {
3289  return nullptr;
3290  }
3291 
3292  _load_table[sfile] = shader;
3293 
3294  if (cache_generated_shaders) {
3295  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3296  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3297  return i->second;
3298  }
3299  _make_table[shader->_text] = shader;
3300  }
3301  return shader;
3302 }
3303 
3304 /**
3305  * This variant of Shader::load loads all shader programs separately.
3306  */
3307 PT(Shader) Shader::
3308 load(ShaderLanguage lang, const Filename &vertex,
3309  const Filename &fragment, const Filename &geometry,
3310  const Filename &tess_control, const Filename &tess_evaluation) {
3311  ShaderFile sfile(vertex, fragment, geometry, tess_control, tess_evaluation);
3312  ShaderTable::const_iterator i = _load_table.find(sfile);
3313  if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3314  // But check that someone hasn't modified it in the meantime.
3315  if (i->second->check_modified()) {
3316  shader_cat.info()
3317  << "Shader was modified on disk, reloading.\n";
3318  } else {
3319  if (shader_cat.is_debug()) {
3320  shader_cat.debug()
3321  << "Shader was found in shader cache.\n";
3322  }
3323  return i->second;
3324  }
3325  }
3326 
3327  PT(Shader) shader = new Shader(lang);
3328  if (!shader->read(sfile)) {
3329  return nullptr;
3330  }
3331 
3332  _load_table[sfile] = shader;
3333 
3334  if (cache_generated_shaders) {
3335  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3336  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3337  return i->second;
3338  }
3339  _make_table[shader->_text] = shader;
3340  }
3341  return shader;
3342 }
3343 
3344 /**
3345  * Loads a compute shader.
3346  */
3347 PT(Shader) Shader::
3348 load_compute(ShaderLanguage lang, const Filename &fn) {
3349  if (lang != SL_GLSL) {
3350  shader_cat.error()
3351  << "Only GLSL compute shaders are currently supported.\n";
3352  return nullptr;
3353  }
3354 
3355  Filename fullpath(fn);
3357  if (!vfs->resolve_filename(fullpath, get_model_path())) {
3358  shader_cat.error()
3359  << "Could not find compute shader file: " << fn << "\n";
3360  return nullptr;
3361  }
3362 
3363  ShaderFile sfile;
3364  sfile._separate = true;
3365  sfile._compute = fn;
3366 
3367  ShaderTable::const_iterator i = _load_table.find(sfile);
3368  if (i != _load_table.end() && (lang == SL_none || lang == i->second->_language)) {
3369  // But check that someone hasn't modified it in the meantime.
3370  if (i->second->check_modified()) {
3371  shader_cat.info()
3372  << "Compute shader " << fn << " was modified on disk, reloading.\n";
3373  } else {
3374  if (shader_cat.is_debug()) {
3375  shader_cat.debug()
3376  << "Compute shader " << fn << " was found in shader cache.\n";
3377  }
3378  return i->second;
3379  }
3380  }
3381 
3383  PT(BamCacheRecord) record = cache->lookup(fullpath, "sho");
3384  if (record != nullptr) {
3385  if (record->has_data()) {
3386  shader_cat.info()
3387  << "Compute shader " << fn << " was found in disk cache.\n";
3388 
3389  return DCAST(Shader, record->get_data());
3390  }
3391  }
3392 
3393  PT(Shader) shader = new Shader(lang);
3394 
3395  if (!shader->read(sfile, record)) {
3396  return nullptr;
3397  }
3398  _load_table[sfile] = shader;
3399 
3400  if (cache_generated_shaders) {
3401  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3402  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3403  return i->second;
3404  }
3405  _make_table[shader->_text] = shader;
3406  }
3407 
3408  // It makes little sense to cache the shader before compilation, so we keep
3409  // the record for when we have the compiled the shader.
3410  std::swap(shader->_record, record);
3411  shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders();
3412  shader->_fullpath = shader->_source_files[0];
3413  return shader;
3414 }
3415 
3416 /**
3417  * Loads the shader, using the string as shader body.
3418  */
3419 PT(Shader) Shader::
3420 make(string body, ShaderLanguage lang) {
3421  if (lang == SL_GLSL) {
3422  shader_cat.error()
3423  << "GLSL shaders must have separate shader bodies!\n";
3424  return nullptr;
3425 
3426  } else if (lang == SL_none) {
3427  shader_cat.warning()
3428  << "Shader::make() now requires an explicit shader language. Assuming Cg.\n";
3429  lang = SL_Cg;
3430  }
3431 #ifndef HAVE_CG
3432  if (lang == SL_Cg) {
3433  shader_cat.error() << "Support for Cg shaders is not enabled.\n";
3434  return nullptr;
3435  }
3436 #endif
3437 
3438  ShaderFile sbody(move(body));
3439 
3440  if (cache_generated_shaders) {
3441  ShaderTable::const_iterator i = _make_table.find(sbody);
3442  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3443  // But check that someone hasn't modified its includes in the meantime.
3444  if (!i->second->check_modified()) {
3445  return i->second;
3446  }
3447  }
3448  }
3449 
3450  PT(Shader) shader = new Shader(lang);
3451  if (!shader->load(sbody)) {
3452  return nullptr;
3453  }
3454 
3455  if (cache_generated_shaders) {
3456  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3457  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3458  shader = i->second;
3459  } else {
3460  _make_table[shader->_text] = shader;
3461  }
3462  _make_table[std::move(sbody)] = shader;
3463  }
3464 
3465  if (dump_generated_shaders) {
3466  ostringstream fns;
3467  int index = _shaders_generated ++;
3468  fns << "genshader" << index;
3469  string fn = fns.str();
3470  shader_cat.warning() << "Dumping shader: " << fn << "\n";
3471 
3472  pofstream s;
3473  s.open(fn.c_str(), std::ios::out | std::ios::trunc);
3474  s << shader->get_text();
3475  s.close();
3476  }
3477  return shader;
3478 }
3479 
3480 /**
3481  * Loads the shader, using the strings as shader bodies.
3482  */
3483 PT(Shader) Shader::
3484 make(ShaderLanguage lang, string vertex, string fragment, string geometry,
3485  string tess_control, string tess_evaluation) {
3486 #ifndef HAVE_CG
3487  if (lang == SL_Cg) {
3488  shader_cat.error() << "Support for Cg shaders is not enabled.\n";
3489  return nullptr;
3490  }
3491 #endif
3492  if (lang == SL_none) {
3493  shader_cat.error()
3494  << "Shader::make() requires an explicit shader language.\n";
3495  return nullptr;
3496  }
3497 
3498  ShaderFile sbody(move(vertex), move(fragment), move(geometry),
3499  move(tess_control), move(tess_evaluation));
3500 
3501  if (cache_generated_shaders) {
3502  ShaderTable::const_iterator i = _make_table.find(sbody);
3503  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3504  // But check that someone hasn't modified its includes in the meantime.
3505  if (!i->second->check_modified()) {
3506  return i->second;
3507  }
3508  }
3509  }
3510 
3511  PT(Shader) shader = new Shader(lang);
3512  shader->_filename = ShaderFile("created-shader");
3513  if (!shader->load(sbody)) {
3514  return nullptr;
3515  }
3516 
3517  if (cache_generated_shaders) {
3518  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3519  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3520  shader = i->second;
3521  } else {
3522  _make_table[shader->_text] = shader;
3523  }
3524  _make_table[std::move(sbody)] = shader;
3525  }
3526 
3527  return shader;
3528 }
3529 
3530 /**
3531  * Loads the compute shader from the given string.
3532  */
3533 PT(Shader) Shader::
3534 make_compute(ShaderLanguage lang, string body) {
3535  if (lang != SL_GLSL) {
3536  shader_cat.error()
3537  << "Only GLSL compute shaders are currently supported.\n";
3538  return nullptr;
3539  }
3540 
3541  ShaderFile sbody;
3542  sbody._separate = true;
3543  sbody._compute = move(body);
3544 
3545  if (cache_generated_shaders) {
3546  ShaderTable::const_iterator i = _make_table.find(sbody);
3547  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3548  // But check that someone hasn't modified its includes in the meantime.
3549  if (!i->second->check_modified()) {
3550  return i->second;
3551  }
3552  }
3553  }
3554 
3555  PT(Shader) shader = new Shader(lang);
3556  shader->_filename = ShaderFile("created-shader");
3557  if (!shader->load(sbody)) {
3558  return nullptr;
3559  }
3560 
3561  if (cache_generated_shaders) {
3562  ShaderTable::const_iterator i = _make_table.find(shader->_text);
3563  if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) {
3564  shader = i->second;
3565  } else {
3566  _make_table[shader->_text] = shader;
3567  }
3568  _make_table[std::move(sbody)] = shader;
3569  }
3570 
3571  return shader;
3572 }
3573 
3574 /**
3575  * Set a 'parse pointer' to the beginning of the shader.
3576  */
3578 parse_init() {
3579  _parse = 0;
3580 }
3581 
3582 /**
3583  * Parse a line of text. If 'lt' is true, trim blanks from the left end of
3584  * the line. If 'rt' is true, trim blanks from the right end (the newline is
3585  * always trimmed).
3586  */
3588 parse_line(string &result, bool lt, bool rt) {
3589  nassertv(!_text._separate);
3590  int len = _text._shared.size();
3591  int head = _parse;
3592  int tail = head;
3593  while ((tail < len) && (_text._shared[tail] != '\n')) {
3594  tail++;
3595  }
3596  if (tail < len) {
3597  _parse = tail+1;
3598  } else {
3599  _parse = tail;
3600  }
3601  if (lt) {
3602  while ((head < tail)&&(isspace(_text._shared[head]))) head++;
3603  while ((tail > head)&&(isspace(_text._shared[tail-1]))) tail--;
3604  }
3605  result = _text._shared.substr(head, tail-head);
3606 }
3607 
3608 /**
3609  * Parse lines until you read a line that matches the specified pattern.
3610  * Returns all the preceding lines, and if the include flag is set, returns
3611  * the final line as well.
3612  */
3614 parse_upto(string &result, string pattern, bool include) {
3615  nassertv(!_text._separate);
3616  GlobPattern endpat(pattern);
3617  int start = _parse;
3618  int last = _parse;
3619  while (_parse < (int)(_text._shared.size())) {
3620  string t;
3621  parse_line(t, true, true);
3622  if (endpat.matches(t)) break;
3623  last = _parse;
3624  }
3625  if (include) {
3626  result = _text._shared.substr(start, _parse - start);
3627  } else {
3628  result = _text._shared.substr(start, last - start);
3629  }
3630 }
3631 
3632 /**
3633  * Returns the rest of the text from the current parse location.
3634  */
3636 parse_rest(string &result) {
3637  nassertv(!_text._separate);
3638  result = _text._shared.substr(_parse, _text._shared.size() - _parse);
3639 }
3640 
3641 /**
3642  * Returns true if the parse pointer is at the end of the shader.
3643  */
3645 parse_eof() {
3646  return (int)_text._shared.size() == _parse;
3647 }
3648 
3649 /**
3650  * Indicates that the shader should be enqueued to be prepared in the
3651  * indicated prepared_objects at the beginning of the next frame. This will
3652  * ensure the texture is already loaded into texture memory if it is expected
3653  * to be rendered soon.
3654  *
3655  * Use this function instead of prepare_now() to preload textures from a user
3656  * interface standpoint.
3657  */
3658 PT(AsyncFuture) Shader::
3659 prepare(PreparedGraphicsObjects *prepared_objects) {
3660  return prepared_objects->enqueue_shader_future(this);
3661 }
3662 
3663 /**
3664  * Returns true if the shader has already been prepared or enqueued for
3665  * preparation on the indicated GSG, false otherwise.
3666  */
3668 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
3669  Contexts::const_iterator ci;
3670  ci = _contexts.find(prepared_objects);
3671  if (ci != _contexts.end()) {
3672  return true;
3673  }
3674  return prepared_objects->is_shader_queued(this);
3675 }
3676 
3677 /**
3678  * Frees the texture context only on the indicated object, if it exists there.
3679  * Returns true if it was released, false if it had not been prepared.
3680  */
3682 release(PreparedGraphicsObjects *prepared_objects) {
3683  Contexts::iterator ci;
3684  ci = _contexts.find(prepared_objects);
3685  if (ci != _contexts.end()) {
3686  ShaderContext *sc = (*ci).second;
3687  if (sc != nullptr) {
3688  prepared_objects->release_shader(sc);
3689  } else {
3690  _contexts.erase(ci);
3691  }
3692  return true;
3693  }
3694 
3695  // Maybe it wasn't prepared yet, but it's about to be.
3696  return prepared_objects->dequeue_shader(this);
3697 }
3698 
3699 /**
3700  * Creates a context for the shader on the particular GSG, if it does not
3701  * already exist. Returns the new (or old) ShaderContext. This assumes that
3702  * the GraphicsStateGuardian is the currently active rendering context and
3703  * that it is ready to accept new textures. If this is not necessarily the
3704  * case, you should use prepare() instead.
3705  *
3706  * Normally, this is not called directly except by the GraphicsStateGuardian;
3707  * a shader does not need to be explicitly prepared by the user before it may
3708  * be rendered.
3709  */
3711 prepare_now(PreparedGraphicsObjects *prepared_objects,
3713  Contexts::const_iterator ci;
3714  ci = _contexts.find(prepared_objects);
3715  if (ci != _contexts.end()) {
3716  return (*ci).second;
3717  }
3718 
3719  ShaderContext *tc = prepared_objects->prepare_shader_now(this, gsg);
3720  _contexts[prepared_objects] = tc;
3721 
3722  return tc;
3723 }
3724 
3725 /**
3726  * Removes the indicated PreparedGraphicsObjects table from the Shader's
3727  * table, without actually releasing the texture. This is intended to be
3728  * called only from PreparedGraphicsObjects::release_texture(); it should
3729  * never be called by user code.
3730  */
3731 void Shader::
3732 clear_prepared(PreparedGraphicsObjects *prepared_objects) {
3733  Contexts::iterator ci;
3734  ci = _contexts.find(prepared_objects);
3735  if (ci != _contexts.end()) {
3736  _contexts.erase(ci);
3737  } else {
3738  // If this assertion fails, clear_prepared() was given a prepared_objects
3739  // which the texture didn't know about.
3740  nassert_raise("unknown PreparedGraphicsObjects");
3741  }
3742 }
3743 
3744 /**
3745  * Frees the context allocated on all objects for which the texture has been
3746  * declared. Returns the number of contexts which have been freed.
3747  */
3749 release_all() {
3750  // We have to traverse a copy of the _contexts list, because the
3751  // PreparedGraphicsObjects object will call clear_prepared() in response to
3752  // each release_texture(), and we don't want to be modifying the _contexts
3753  // list while we're traversing it.
3754  Contexts temp = _contexts;
3755  int num_freed = (int)_contexts.size();
3756 
3757  Contexts::const_iterator ci;
3758  for (ci = temp.begin(); ci != temp.end(); ++ci) {
3759  PreparedGraphicsObjects *prepared_objects = (*ci).first;
3760  ShaderContext *sc = (*ci).second;
3761  if (sc != nullptr) {
3762  prepared_objects->release_shader(sc);
3763  }
3764  }
3765 
3766  // There might still be some outstanding contexts in the map, if there were
3767  // any NULL pointers there. Eliminate them.
3768  _contexts.clear();
3769 
3770  return num_freed;
3771 }
3772 
3773 /**
3774  *
3775  */
3776 void Shader::ShaderCaps::
3777 clear() {
3778  _supports_glsl = false;
3779 
3780 #ifdef HAVE_CG
3781  _active_vprofile = CG_PROFILE_UNKNOWN;
3782  _active_fprofile = CG_PROFILE_UNKNOWN;
3783  _active_gprofile = CG_PROFILE_UNKNOWN;
3784  _ultimate_vprofile = CG_PROFILE_UNKNOWN;
3785  _ultimate_fprofile = CG_PROFILE_UNKNOWN;
3786  _ultimate_gprofile = CG_PROFILE_UNKNOWN;
3787 #endif
3788 }
3789 
3790 /**
3791  * Tells the BamReader how to create objects of type Shader.
3792  */
3795  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
3796 }
3797 
3798 /**
3799  * Writes the contents of this object to the datagram for shipping out to a
3800  * Bam file.
3801  */
3803 write_datagram(BamWriter *manager, Datagram &dg) {
3804  dg.add_uint8(_language);
3805  dg.add_bool(_loaded);
3806  _filename.write_datagram(dg);
3807  _text.write_datagram(dg);
3808 
3809  dg.add_uint32(_compiled_format);
3810  dg.add_string(_compiled_binary);
3811 }
3812 
3813 /**
3814  * This function is called by the BamReader's factory when a new object of
3815  * type Shader is encountered in the Bam file. It should create the Shader
3816  * and extract its information from the file.
3817  */
3818 TypedWritable *Shader::
3819 make_from_bam(const FactoryParams &params) {
3820  Shader *attrib = new Shader(SL_none);
3821  DatagramIterator scan;
3822  BamReader *manager;
3823 
3824  parse_params(params, scan, manager);
3825  attrib->fillin(scan, manager);
3826  return attrib;
3827 }
3828 
3829 /**
3830  * This internal function is called by make_from_bam to read in all of the
3831  * relevant data from the BamFile for the new Shader.
3832  */
3833 void Shader::
3834 fillin(DatagramIterator &scan, BamReader *manager) {
3835  _language = (ShaderLanguage) scan.get_uint8();
3836  _loaded = scan.get_bool();
3837  _filename.read_datagram(scan);
3838  _text.read_datagram(scan);
3839 
3840  _compiled_format = scan.get_uint32();
3841  _compiled_binary = scan.get_string();
3842 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
Definition: asyncFuture.h:61
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
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.
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:194
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
get_cache_compiled_shaders
Returns whether compiled shader programs will be stored in the cache, as binary .txo files.
Definition: bamCache.h:94
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
bool get_bool()
Extracts a boolean value.
std::string get_string()
Extracts a variable-length string.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
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
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
std::ostream & error(bool prefix=true) const
A shorthand way to write out(NS_error).
A table of objects that are saved within the graphics context for reference by handle later.
bool is_shader_queued(const Shader *shader) const
Returns true if the shader has been queued on this GSG, false otherwise.
bool dequeue_shader(Shader *shader)
Removes a shader from the queued list of shaders to be prepared.
void release_shader(ShaderContext *sc)
Indicates that a shader context, created by a previous call to prepare_shader(), is no longer needed.
ShaderContext * prepare_shader_now(Shader *shader, GraphicsStateGuardianBase *gsg)
Immediately creates a new ShaderContext for the indicated shader and returns it.
The ShaderContext is meant to contain the compiled version of a shader string.
Definition: shaderContext.h:31
void read_datagram(DatagramIterator &source)
Reads the object from a Datagram.
Definition: shader.I:742
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
Definition: shader.h:49
bool cp_errchk_parameter_varying(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:152
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
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_parse_delimiter(ShaderArgInfo &arg, vector_string &pieces, int &next)
Pop a delimiter ('to' or 'rel') from the word list.
Definition: shader.cxx:262
const std::string & get_text(ShaderType type=ST_none) const
Return the Shader's text for the given shader type.
Definition: shader.I:89
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
void parse_init()
Set a 'parse pointer' to the beginning of the shader.
Definition: shader.cxx:3578
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
ShaderContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the shader on the particular GSG, if it does not already exist.
Definition: shader.cxx:3711
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
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: shader.cxx:3749
bool cp_errchk_parameter_in(ShaderArgInfo &arg)
Make sure the provided parameter has the 'in' direction.
Definition: shader.cxx:138
bool cp_errchk_parameter_uniform(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:166
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
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
Filename get_filename_from_index(int index, ShaderType type) const
Returns the filename of the included shader with the given source file index (as recorded in the #lin...
Definition: shader.cxx:3251
std::string cp_parse_non_delimiter(vector_string &pieces, int &next)
Pop a non-delimiter word from the word list.
Definition: shader.cxx:276
void parse_line(std::string &result, bool rt, bool lt)
Parse a line of text.
Definition: shader.cxx:3588
void parse_rest(std::string &result)
Returns the rest of the text from the current parse location.
Definition: shader.cxx:3636
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the shader has already been prepared or enqueued for preparation on the indicated GSG...
Definition: shader.cxx:3668
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
void parse_upto(std::string &result, std::string pattern, bool include)
Parse lines until you read a line that matches the specified pattern.
Definition: shader.cxx:3614
static void register_with_read_factory()
Tells the BamReader how to create objects of type Shader.
Definition: shader.cxx:3794
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: shader.cxx:3803
bool cp_errchk_parameter_words(ShaderArgInfo &arg, int len)
Make sure the provided parameter contains the specified number of words.
Definition: shader.cxx:122
bool cp_errchk_parameter_sampler(ShaderArgInfo &arg)
Make sure the provided parameter has a texture type.
Definition: shader.cxx:231
bool parse_eof()
Returns true if the parse pointer is at the end of the shader.
Definition: shader.cxx:3645
Filename get_filename(ShaderType type=ST_none) const
Return the Shader's filename for the given shader type.
Definition: shader.I:20
int cp_dependency(ShaderMatInput inp)
Given ShaderMatInput, returns an indication of what part or parts of the state_and_transform the Shad...
Definition: shader.cxx:377
~Shader()
Delete the compiled code, if it exists.
Definition: shader.cxx:3233
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: shader.cxx:3682
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
A hierarchy of directories and files that appears to be one continuous file system,...
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.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(Shader) Shader
Loads the shader with the given filename.
Definition: shader.cxx:3269
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.