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