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 }
bamCache.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Shader::get_filename_from_index
Filename get_filename_from_index(int index, ShaderType type) const
Returns the filename of the included shader with the given source file index (as recorded in the #lin...
Definition: shader.cxx:3251
DatagramIterator::get_string
std::string get_string()
Extracts a variable-length string.
Definition: datagramIterator.cxx:26
PreparedGraphicsObjects
A table of objects that are saved within the graphics context for reference by handle later.
Definition: preparedGraphicsObjects.h:58
NotifyCategory::error
std::ostream & error(bool prefix=true) const
A shorthand way to write out(NS_error).
Definition: notifyCategory.I:169
Shader::release_all
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: shader.cxx:3749
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamCacheRecord
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
Definition: bamCacheRecord.h:36
Shader
Definition: shader.h:49
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
tokenize
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
Definition: string_utils.cxx:170
Filename::get_dirname
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Shader::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: shader.cxx:3803
PT
PT(Shader) Shader
Loads the shader with the given filename.
Definition: shader.cxx:3269
Shader::is_prepared
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the shader has already been prepared or enqueued for preparation on the indicated GSG...
Definition: shader.cxx:3668
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamCache::get_global_ptr
static BamCache * get_global_ptr()
Returns a pointer to the global BamCache object, which is used automatically by the ModelPool and Tex...
Definition: bamCache.I:223
Datagram::add_uint8
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
Shader::cp_parse_delimiter
bool cp_parse_delimiter(ShaderArgInfo &arg, vector_string &pieces, int &next)
Pop a delimiter ('to' or 'rel') from the word list.
Definition: shader.cxx:262
VirtualFileSystem::get_file
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
Definition: virtualFileSystem.cxx:515
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
Shader::cp_errchk_parameter_sampler
bool cp_errchk_parameter_sampler(ShaderArgInfo &arg)
Make sure the provided parameter has a texture type.
Definition: shader.cxx:231
preparedGraphicsObjects.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PreparedGraphicsObjects::release_shader
void release_shader(ShaderContext *sc)
Indicates that a shader context, created by a previous call to prepare_shader(), is no longer needed.
Definition: preparedGraphicsObjects.cxx:779
Shader::cp_errchk_parameter_float
bool cp_errchk_parameter_float(ShaderArgInfo &arg, int lo, int hi)
Make sure the provided parameter has a floating point type.
Definition: shader.cxx:180
Shader::cp_errchk_parameter_varying
bool cp_errchk_parameter_varying(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:152
Shader::cp_report_error
void cp_report_error(ShaderArgInfo &arg, const std::string &msg)
Generate an error message including a description of the specified parameter.
Definition: shader.cxx:51
Shader::parse_init
void parse_init()
Set a 'parse pointer' to the beginning of the shader.
Definition: shader.cxx:3578
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
InternalName
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
Shader::parse_line
void parse_line(std::string &result, bool rt, bool lt)
Parse a line of text.
Definition: shader.cxx:3588
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
Shader::parse_upto
void parse_upto(std::string &result, std::string pattern, bool include)
Parse lines until you read a line that matches the specified pattern.
Definition: shader.cxx:3614
GlobPattern::matches
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
Shader::ShaderVarSpec
Definition: shader.h:433
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Shader::~Shader
~Shader()
Delete the compiled code, if it exists.
Definition: shader.cxx:3233
PreparedGraphicsObjects::prepare_shader_now
ShaderContext * prepare_shader_now(Shader *shader, GraphicsStateGuardianBase *gsg)
Immediately creates a new ShaderContext for the indicated shader and returns it.
Definition: preparedGraphicsObjects.cxx:867
AsyncFuture
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
Definition: asyncFuture.h:61
Datagram::add_string
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Shader::cp_parse_coord_sys
bool cp_parse_coord_sys(ShaderArgInfo &arg, vector_string &pieces, int &next, ShaderMatSpec &spec, bool fromflag)
Convert a single-word coordinate system name into a PART/ARG of a ShaderMatSpec.
Definition: shader.cxx:290
Shader::cp_errchk_parameter_words
bool cp_errchk_parameter_words(ShaderArgInfo &arg, int len)
Make sure the provided parameter contains the specified number of words.
Definition: shader.cxx:122
Shader::cp_errchk_parameter_uniform
bool cp_errchk_parameter_uniform(ShaderArgInfo &arg)
Make sure the provided parameter has the correct variance.
Definition: shader.cxx:166
DatagramIterator::get_bool
bool get_bool()
Extracts a boolean value.
Definition: datagramIterator.I:48
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
Shader::ShaderTexSpec
Definition: shader.h:424
ShaderContext
The ShaderContext is meant to contain the compiled version of a shader string.
Definition: shaderContext.h:31
Shader::ShaderPtrSpec
Definition: shader.h:441
DSearchPath
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
Shader::ShaderCaps
Definition: shader.h:450
BamCache::store
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:194
Shader::get_text
const std::string & get_text(ShaderType type=ST_none) const
Return the Shader's text for the given shader type.
Definition: shader.I:89
Shader::cp_errchk_parameter_in
bool cp_errchk_parameter_in(ShaderArgInfo &arg)
Make sure the provided parameter has the 'in' direction.
Definition: shader.cxx:138
VirtualFileSystem::find_file
PointerTo< VirtualFile > find_file(const Filename &filename, const DSearchPath &searchpath, bool status_only=false) const
Uses the indicated search path to find the file within the file system.
Definition: virtualFileSystem.cxx:543
Shader::cp_parse_eol
bool cp_parse_eol(ShaderArgInfo &arg, vector_string &pieces, int &next)
Make sure the next thing on the word list is EOL.
Definition: shader.cxx:250
Shader::ShaderFile::write_datagram
void write_datagram(Datagram &dg) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: shader.I:723
BamCache::get_cache_compiled_shaders
get_cache_compiled_shaders
Returns whether compiled shader programs will be stored in the cache, as binary .txo files.
Definition: bamCache.h:94
PreparedGraphicsObjects::dequeue_shader
bool dequeue_shader(Shader *shader)
Removes a shader from the queued list of shaders to be prepared.
Definition: preparedGraphicsObjects.cxx:747
Shader::cp_optimize_mat_spec
void cp_optimize_mat_spec(ShaderMatSpec &spec)
Analyzes a ShaderMatSpec and decides what it should use its cache for.
Definition: shader.cxx:508
Shader::prepare_now
ShaderContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the shader on the particular GSG, if it does not already exist.
Definition: shader.cxx:3711
Shader::cp_dependency
int cp_dependency(ShaderMatInput inp)
Given ShaderMatInput, returns an indication of what part or parts of the state_and_transform the Shad...
Definition: shader.cxx:377
Shader::ShaderArgInfo
Definition: shader.h:352
Shader::compile_parameter
bool compile_parameter(ShaderArgInfo &p, int *arg_dim)
Analyzes a parameter and decides how to bind the parameter to some part of panda's internal state.
Definition: shader.cxx:668
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
Shader::release
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: shader.cxx:3682
BamCacheRecord::get_data
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
Definition: bamCacheRecord.h:77
Shader::cp_parse_non_delimiter
std::string cp_parse_non_delimiter(vector_string &pieces, int &next)
Pop a non-delimiter word from the word list.
Definition: shader.cxx:276
BamCache
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Datagram::add_uint32
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
Shader::ShaderFile::read_datagram
void read_datagram(DatagramIterator &source)
Reads the object from a Datagram.
Definition: shader.I:742
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
Shader::get_compiled
bool get_compiled(unsigned int &format, std::string &binary) const
Called by the back-end to retrieve compiled data.
Definition: shader.cxx:1586
Datagram::add_bool
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
shader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GraphicsStateGuardianBase
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Definition: graphicsStateGuardianBase.h:110
Shader::set_default_caps
static void set_default_caps(const ShaderCaps &caps)
Called by the graphics back-end to specify the caps with which we will likely want to be compiling ou...
Definition: shader.cxx:1597
PreparedGraphicsObjects::is_shader_queued
bool is_shader_queued(const Shader *shader) const
Returns true if the shader has been queued on this GSG, false otherwise.
Definition: preparedGraphicsObjects.cxx:730
Shader::parse_rest
void parse_rest(std::string &result)
Returns the rest of the text from the current parse location.
Definition: shader.cxx:3636
BamCacheRecord::add_dependent_file
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record.
Definition: bamCacheRecord.cxx:147
Shader::parse_eof
bool parse_eof()
Returns true if the parse pointer is at the end of the shader.
Definition: shader.cxx:3645
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:640
DatagramIterator::get_uint32
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: datagramIterator.I:164
DatagramIterator::get_uint8
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
Definition: datagramIterator.I:72
Shader::set_compiled
void set_compiled(unsigned int format, const char *data, size_t length)
Called by the back-end when the shader has compiled data available.
Definition: shader.cxx:1569
Shader::ShaderMatSpec
Definition: shader.h:412
GlobPattern
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
Shader::register_with_read_factory
static void register_with_read_factory()
Tells the BamReader how to create objects of type Shader.
Definition: shader.cxx:3794
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Shader::get_filename
Filename get_filename(ShaderType type=ST_none) const
Return the Shader's filename for the given shader type.
Definition: shader.I:20