Panda3D

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