1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #include "pxr/imaging/hio/glslfxConfig.h"
25 #include "pxr/imaging/hio/debugCodes.h"
26 #include "pxr/imaging/hio/dictionary.h"
27
28 #include "pxr/base/tf/envSetting.h"
29 #include "pxr/base/tf/staticTokens.h"
30 #include "pxr/base/tf/stl.h"
31 #include "pxr/base/tf/type.h"
32 #include "pxr/base/trace/trace.h"
33
34 PXR_NAMESPACE_OPEN_SCOPE
35
36
37 using std::string;
38 using std::vector;
39
40 TF_DEFINE_PRIVATE_TOKENS(
41 _tokens,
42 (attributes)
43 (techniques)
44 (metadata)
45 (parameters)
46 (parameterOrder)
47 (textures)
48 (documentation)
49 (role)
50 (color)
51 ((defVal, "default"))
52 (source)
53 (type)
54 );
55
56 TF_DEFINE_ENV_SETTING(HIO_GLSLFX_DEFAULT_VALUE_VALIDATION, true,
57 "If true, there is no check that the default value of an attribute matches "
58 "the type declared in the glslfx config section.");
59
60 static
61 bool
_IsFloatOrDouble(const VtValue & v)62 _IsFloatOrDouble(const VtValue &v)
63 {
64 return v.IsHolding<float>() || v.IsHolding<double>();
65 }
66
67 // Is VtValue holding a vector of floats or doubles of length n.
68 template<size_t n>
69 static
70 bool
_IsVec(const VtValue & v)71 _IsVec(const VtValue &v)
72 {
73 if (!v.IsHolding<std::vector<VtValue>>()) {
74 return false;
75 }
76
77 const std::vector<VtValue> &vec = v.UncheckedGet<std::vector<VtValue>>();
78 if (vec.size() != n) {
79 return false;
80 }
81
82 for (size_t i = 0; i < n; i++) {
83 if (!_IsFloatOrDouble(vec[i])) {
84 return false;
85 }
86 }
87
88 return true;
89 }
90
91 // Extract default value from the dictionary.
92 //
93 // This looks at the default and type key. If a default is given, it is used
94 // if it is matching the type. Otherwise, a default value for that type is
95 // constructed.
96 //
97 // errorStr is set if the given type is invalid or does not match the given
98 // default value.
99 //
100 // We would like the 'attribute' section to start using 'default:' to
101 // describe the value type of primvar inputs, but currently they often
102 // use 'type: "vec4"'.
103 // The awkward looking 'std::vector<VtValue>(...)' usage below is to
104 // match the json parser return for "default: (0,0,0)".
105 static
106 VtValue
_GetDefaultValue(const std::string & attributeName,const VtDictionary & attributeDataDict,std::string * const errorStr)107 _GetDefaultValue(
108 const std::string &attributeName,
109 const VtDictionary &attributeDataDict,
110 std::string * const errorStr)
111 {
112 TRACE_FUNCTION();
113
114 // Get default key
115 VtValue defaultValue;
116 const bool hasDefaultValue =
117 TfMapLookup(attributeDataDict, _tokens->defVal, &defaultValue);
118
119 // Old behavior - so that assets where the default value and the
120 // type do not match still work.
121 if (hasDefaultValue &&
122 !TfGetEnvSetting(HIO_GLSLFX_DEFAULT_VALUE_VALIDATION)) {
123 return defaultValue;
124 }
125
126 // Get type key
127 VtValue typeNameValue;
128 const bool hasTypeNameValue =
129 TfMapLookup(attributeDataDict, _tokens->type, &typeNameValue);
130
131 if (!hasTypeNameValue) {
132 if (hasDefaultValue) {
133 // If value but not type specified, just use it.
134 return defaultValue;
135 }
136 *errorStr = TfStringPrintf("No type or default value for %s",
137 attributeName.c_str());
138 return VtValue(std::vector<float>(4, 0.0f));
139 }
140
141 if (!typeNameValue.IsHolding<std::string>()) {
142 *errorStr = TfStringPrintf("Type name for %s is not a string",
143 attributeName.c_str());
144 if (hasDefaultValue) {
145 return defaultValue;
146 }
147 return VtValue(std::vector<float>(4, 0.0f));
148 }
149
150 struct TypeInfo {
151 std::string name;
152 VtValue defaultValue;
153 // Is VtValue of given type?
154 bool (*predicate)(const VtValue &);
155 };
156
157 static const TypeInfo typeInfos[] = {
158 { "float",
159 VtValue(0.0f),
160 _IsFloatOrDouble },
161 { "double",
162 VtValue(0.0),
163 _IsFloatOrDouble },
164 { "vec2",
165 VtValue(std::vector<VtValue>(2, VtValue(0.0f))),
166 _IsVec<2> },
167 { "vec3",
168 VtValue(std::vector<VtValue>(3, VtValue(0.0f))),
169 _IsVec<3> },
170 { "vec4",
171 VtValue(std::vector<VtValue>(4, VtValue(0.0f))),
172 _IsVec<4> }
173 };
174
175 std::string const& typeName = typeNameValue.UncheckedGet<std::string>();
176
177 // Find respective typeInfo
178 for (TypeInfo const& typeInfo : typeInfos) {
179 if (typeInfo.name == typeName) {
180 if (hasDefaultValue) {
181 // Check that our default value matches
182 if (typeInfo.predicate(defaultValue)) {
183 return defaultValue;
184 }
185 *errorStr = TfStringPrintf(
186 "Default value for %s is not of type %s",
187 attributeName.c_str(), typeName.c_str());
188 }
189 // If no default value, use one based on the type.
190 return typeInfo.defaultValue;
191 }
192 }
193
194 // Invalid type name, use or construct default value.
195 if (hasDefaultValue) {
196 *errorStr = TfStringPrintf(
197 "Invalid type %s for %s",
198 typeName.c_str(), attributeName.c_str());
199 return defaultValue;
200 }
201
202 *errorStr = TfStringPrintf(
203 "Invalid type and no default value for %s",
204 attributeName.c_str());
205 return VtValue(std::vector<float>(4, 0.0f));
206 }
207
208 HioGlslfxConfig *
Read(TfToken const & technique,string const & input,string const & filename,string * errorStr)209 HioGlslfxConfig::Read(TfToken const & technique,
210 string const & input,
211 string const & filename,
212 string *errorStr)
213 {
214 return new HioGlslfxConfig(technique,
215 Hio_GetDictionaryFromInput(input, filename, errorStr), errorStr );
216 }
217
HioGlslfxConfig(TfToken const & technique,VtDictionary const & dict,string * errors)218 HioGlslfxConfig::HioGlslfxConfig(TfToken const & technique,
219 VtDictionary const & dict,
220 string * errors)
221 : _technique(technique)
222 {
223 _Init(dict, errors);
224 }
225
226 void
_Init(VtDictionary const & dict,string * errors)227 HioGlslfxConfig::_Init(VtDictionary const & dict, string * errors)
228 {
229 TRACE_FUNCTION();
230
231 _params = _GetParameters(dict, errors);
232 _textures = _GetTextures(dict, errors);
233 _attributes = _GetAttributes(dict, errors);
234 _metadata = _GetMetadata(dict, errors);
235 _sourceKeyMap = _GetSourceKeyMap(dict, errors);
236 }
237
238 HioGlslfxConfig::SourceKeys
GetSourceKeys(TfToken const & shaderStageKey) const239 HioGlslfxConfig::GetSourceKeys(TfToken const & shaderStageKey) const
240 {
241 HioGlslfxConfig::SourceKeys ret;
242 TfMapLookup(_sourceKeyMap, shaderStageKey, &ret);
243 return ret;
244 }
245
246 HioGlslfxConfig::_SourceKeyMap
_GetSourceKeyMap(VtDictionary const & dict,string * errorStr) const247 HioGlslfxConfig::_GetSourceKeyMap(VtDictionary const & dict,
248 string *errorStr) const
249 {
250 // XXX as we implement more public API for this thing, some better structure
251 // in the internal API we use to access parts of this graph would
252 // be nice. perhaps even our own variant type instead of VtDictionary?
253 _SourceKeyMap ret;
254
255 VtValue techniques;
256
257 // verify that techiniques is specified
258 if (!TfMapLookup(dict, _tokens->techniques, &techniques)) {
259 *errorStr = TfStringPrintf("Configuration does not specify %s",
260 _tokens->techniques.GetText());
261 return ret;
262 }
263
264 // verify that it holds a VtDictionary
265 if (!techniques.IsHolding<VtDictionary>()) {
266 *errorStr = TfStringPrintf("%s declaration expects a dictionary value",
267 _tokens->techniques.GetText());
268 return ret;
269 }
270
271 // allow only one technique for now, but we plan on supporting more in
272 // the future
273 const VtDictionary& techniquesDict =
274 techniques.UncheckedGet<VtDictionary>();
275
276 if (techniquesDict.size() == 0) {
277 *errorStr = TfStringPrintf("No %s specified",
278 _tokens->techniques.GetText());
279 return ret;
280 }
281
282 VtDictionary::const_iterator entry = techniquesDict.find(_technique);
283 if (entry == techniquesDict.end()) {
284 *errorStr = TfStringPrintf("No entry for %s: %s",
285 _tokens->techniques.GetText(),
286 _technique.GetText());
287 return ret;
288 }
289
290 // get the value of the technique spec
291 VtValue techniqueSpec = entry->second;
292
293 // verify that it also holds a VtDictionary
294 if (!techniqueSpec.IsHolding<VtDictionary>()) {
295 *errorStr = TfStringPrintf("%s spec for %s expects a dictionary value",
296 _tokens->techniques.GetText(),
297 entry->first.c_str());
298 return ret;
299 }
300
301 const VtDictionary& specDict = techniqueSpec.UncheckedGet<VtDictionary>();
302 // get all of the shader stages specified in the spec
303 for (const std::pair<std::string, VtValue>& p : specDict) {
304 const string& shaderStageKey = p.first;
305 const VtValue& shaderStageSpec = p.second;
306
307 // verify that the shaderStageSpec also holds a VtDictionary
308 if (!shaderStageSpec.IsHolding<VtDictionary>()) {
309 *errorStr = TfStringPrintf("%s spec for %s expects a dictionary "
310 "value",
311 entry->first.c_str(),
312 shaderStageKey.c_str());
313 return ret;
314 }
315
316 // get the source value for the shader stage
317 const VtDictionary& shaderStageDict =
318 shaderStageSpec.UncheckedGet<VtDictionary>();
319 VtValue source;
320 if (!TfMapLookup(shaderStageDict, _tokens->source, &source)) {
321 *errorStr = TfStringPrintf("%s spec doesn't define %s for %s",
322 entry->first.c_str(),
323 _tokens->source.GetText(),
324 shaderStageKey.c_str());
325 return ret;
326 }
327
328 // verify that source holds a list
329 if (!source.IsHolding<vector<VtValue> >()) {
330 *errorStr = TfStringPrintf("%s of %s for spec %s expects a list",
331 _tokens->source.GetText(),
332 shaderStageKey.c_str(),
333 entry->first.c_str());
334 return ret;
335 }
336
337 vector<VtValue> sourceList = source.UncheckedGet<vector<VtValue>>();
338 for (VtValue const& val : sourceList) {
339 // verify that this value is a string
340 if (!val.IsHolding<string>()) {
341 *errorStr = TfStringPrintf("%s of %s for spec %s expects a "
342 "list of strings",
343 _tokens->source.GetText(),
344 shaderStageKey.c_str(),
345 entry->first.c_str());
346 return ret;
347 }
348
349 ret[shaderStageKey].push_back(val.UncheckedGet<string>());
350 }
351 }
352
353 return ret;
354 }
355
356 static HioGlslfxConfig::Role
_GetRoleFromString(string const & roleString,string * errorStr)357 _GetRoleFromString(string const & roleString, string *errorStr)
358 {
359 if (roleString == _tokens->color) {
360 return HioGlslfxConfig::RoleColor;
361 }
362
363 *errorStr = TfStringPrintf("Unknown role specification: %s",
364 roleString.c_str());
365 return HioGlslfxConfig::RoleNone;
366 }
367
368
369 HioGlslfxConfig::Parameters
GetParameters() const370 HioGlslfxConfig::GetParameters() const
371 {
372 return _params;
373 }
374
375 HioGlslfxConfig::Parameters
_GetParameters(VtDictionary const & dict,string * errorStr) const376 HioGlslfxConfig::_GetParameters(VtDictionary const & dict,
377 string *errorStr) const
378 {
379 Parameters ret;
380
381 VtValue params;
382
383 // look for the params section
384 if (!TfMapLookup(dict, _tokens->parameters, ¶ms)) {
385 return ret;
386 }
387
388 // verify that it holds a VtDictionary
389 if (!params.IsHolding<VtDictionary>()) {
390 *errorStr = TfStringPrintf("%s declaration expects a dictionary value",
391 _tokens->parameters.GetText());
392 return ret;
393 }
394
395 // look for the parameterOrder section:
396 vector<string> paramOrder;
397 VtValue paramOrderAny;
398 TfMapLookup(dict, _tokens->parameterOrder, ¶mOrderAny);
399
400 if (!paramOrderAny.IsEmpty()) {
401 // verify the type
402 if (!paramOrderAny.IsHolding<vector<VtValue> >()) {
403 *errorStr =
404 TfStringPrintf("%s declaration expects a list of strings",
405 _tokens->parameterOrder.GetText());
406 return ret;
407 }
408
409 const vector<VtValue>& paramOrderList =
410 paramOrderAny.UncheckedGet<vector<VtValue> >();
411 for (VtValue const& val : paramOrderList) {
412 // verify that this value is a string
413 if (!val.IsHolding<string>()) {
414 *errorStr = TfStringPrintf("%s declaration expects a list of "
415 "strings",
416 _tokens->parameterOrder.GetText());
417 return ret;
418 }
419
420 const string& paramName = val.UncheckedGet<string>();
421 if (std::find(paramOrder.begin(), paramOrder.end(), paramName) ==
422 paramOrder.end()) {
423 paramOrder.push_back(paramName);
424 }
425 }
426 }
427
428
429 const VtDictionary& paramsDict = params.UncheckedGet<VtDictionary>();
430 // pre-process the paramsDict in order to get the merged ordering
431 for (const std::pair<std::string, VtValue>& p : paramsDict) {
432 string paramName = p.first;
433 if (std::find(paramOrder.begin(), paramOrder.end(), paramName) ==
434 paramOrder.end()) {
435 paramOrder.push_back(paramName);
436 }
437 }
438
439 // now go through the params in the specified order
440 for (std::string const& paramName : paramOrder) {
441 // ignore anything specified in the order that isn't in the actual dict
442 VtDictionary::const_iterator dictIt = paramsDict.find(paramName);
443 if (dictIt == paramsDict.end()) {
444 continue;
445 }
446
447 const VtValue& paramData = dictIt->second;
448
449 if (!paramData.IsHolding<VtDictionary>()) {
450 *errorStr = TfStringPrintf("%s declaration for %s expects a "
451 "dictionary value",
452 _tokens->parameters.GetText(),
453 paramName.c_str());
454 return ret;
455 }
456
457 // get the default value out
458 const VtDictionary& paramDataDict =
459 paramData.UncheckedGet<VtDictionary>();
460 VtValue defVal;
461 if (!TfMapLookup(paramDataDict, _tokens->defVal, &defVal)) {
462 *errorStr = TfStringPrintf("%s declaration for %s must specify "
463 "a default value",
464 _tokens->parameters.GetText(),
465 paramName.c_str());
466 return ret;
467 }
468
469 // optional documentation string
470 VtValue docVal;
471 string docString;
472 if (TfMapLookup(paramDataDict, _tokens->documentation, &docVal)) {
473 if (!docVal.IsHolding<string>()) {
474 *errorStr = TfStringPrintf("Value for %s for %s is not a "
475 "string",
476 _tokens->documentation.GetText(),
477 paramName.c_str());
478 return ret;
479 }
480
481 docString = docVal.UncheckedGet<string>();
482 }
483 // optional role specification
484 VtValue roleVal;
485 Role role = RoleNone;
486 if (TfMapLookup(paramDataDict, _tokens->role, &roleVal)) {
487 if (!roleVal.IsHolding<string>()) {
488 *errorStr = TfStringPrintf("Value for %s for %s is not a "
489 "string",
490 _tokens->role.GetText(),
491 paramName.c_str());
492 return ret;
493 }
494
495 const string& roleString = roleVal.UncheckedGet<string>();
496 role = _GetRoleFromString(roleString, errorStr);
497 if (!errorStr->empty()) {
498 return ret;
499 }
500 }
501
502 TF_DEBUG(HIO_DEBUG_GLSLFX).Msg(" param: %s\n",
503 paramName.c_str());
504
505 ret.push_back(Parameter(paramName, defVal, docString, role));
506 }
507
508 return ret;
509 }
510
511
512 HioGlslfxConfig::Textures
GetTextures() const513 HioGlslfxConfig::GetTextures() const
514 {
515 return _textures;
516 }
517
518 HioGlslfxConfig::Textures
_GetTextures(VtDictionary const & dict,string * errorStr) const519 HioGlslfxConfig::_GetTextures(VtDictionary const & dict,
520 string *errorStr) const
521 {
522 Textures ret;
523
524 VtValue textures;
525
526 // look for the params section
527 if (!TfMapLookup(dict, _tokens->textures, &textures)) {
528 return ret;
529 }
530
531 // verify that it holds a VtDictionary
532 if (!textures.IsHolding<VtDictionary>()) {
533 *errorStr = TfStringPrintf("%s declaration expects a dictionary value",
534 _tokens->textures.GetText());
535 return ret;
536 }
537
538 const VtDictionary& texturesDict = textures.UncheckedGet<VtDictionary>();
539 for (const std::pair<std::string, VtValue>& p : texturesDict) {
540 const string& textureName = p.first;
541 const VtValue& textureData = p.second;
542 if (!textureData.IsHolding<VtDictionary>()) {
543 *errorStr = TfStringPrintf("%s declaration for %s expects a "
544 "dictionary value",
545 _tokens->textures.GetText(),
546 textureName.c_str());
547 return ret;
548 }
549
550
551 const VtDictionary& textureDataDict =
552 textureData.UncheckedGet<VtDictionary>();
553
554 // optional default color
555 VtValue defVal;
556 TfMapLookup(textureDataDict, _tokens->defVal, &defVal);
557
558 // optional documentation string
559 VtValue docVal;
560 string docString;
561 if (TfMapLookup(textureDataDict, _tokens->documentation, &docVal)) {
562 if (!docVal.IsHolding<string>()) {
563 *errorStr = TfStringPrintf("Value for %s for %s is not a "
564 "string",
565 _tokens->documentation.GetText(),
566 textureName.c_str());
567 return ret;
568 }
569
570 docString = docVal.UncheckedGet<string>();
571 }
572
573 TF_DEBUG(HIO_DEBUG_GLSLFX).Msg(" texture: %s\n",
574 textureName.c_str());
575
576 ret.push_back(Texture(textureName, defVal, docString));
577 }
578
579 return ret;
580 }
581
582 HioGlslfxConfig::Attributes
GetAttributes() const583 HioGlslfxConfig::GetAttributes() const
584 {
585 return _attributes;
586 }
587
588 HioGlslfxConfig::Attributes
_GetAttributes(VtDictionary const & dict,string * errorStr) const589 HioGlslfxConfig::_GetAttributes(VtDictionary const & dict,
590 string *errorStr) const
591 {
592 Attributes ret;
593
594 VtValue attributes;
595
596 // look for the attribute section
597 if (!TfMapLookup(dict, _tokens->attributes, &attributes)) {
598 return ret;
599 }
600
601 // verify that it holds a VtDictionary
602 if (!attributes.IsHolding<VtDictionary>()) {
603 *errorStr = TfStringPrintf("%s declaration expects a dictionary value",
604 _tokens->attributes.GetText());
605 return ret;
606 }
607
608 const VtDictionary& attributesDict =
609 attributes.UncheckedGet<VtDictionary>();
610 for (const std::pair<std::string, VtValue>& p : attributesDict) {
611 const string& attributeName = p.first;
612 const VtValue& attributeData = p.second;
613 if (!attributeData.IsHolding<VtDictionary>()) {
614 *errorStr = TfStringPrintf("%s declaration for %s expects a "
615 "dictionary value",
616 _tokens->attributes.GetText(),
617 attributeName.c_str());
618 return ret;
619 }
620
621 const VtDictionary& attributeDataDict =
622 attributeData.UncheckedGet<VtDictionary>();
623
624
625 // optional documentation string
626 VtValue docVal;
627 string docString;
628 if (TfMapLookup(attributeDataDict, _tokens->documentation, &docVal)) {
629 if (!docVal.IsHolding<string>()) {
630 *errorStr = TfStringPrintf("Value for %s for %s is not a "
631 "string",
632 _tokens->documentation.GetText(),
633 attributeName.c_str());
634 return ret;
635 }
636
637 docString = docVal.UncheckedGet<string>();
638 }
639
640 TF_DEBUG(HIO_DEBUG_GLSLFX).Msg(" attribute: %s\n",
641 attributeName.c_str());
642
643 ret.push_back(
644 Attribute(attributeName,
645 _GetDefaultValue(attributeName,
646 attributeDataDict,
647 errorStr),
648 docString));
649 }
650
651 return ret;
652 }
653
654 HioGlslfxConfig::MetadataDictionary
GetMetadata() const655 HioGlslfxConfig::GetMetadata() const
656 {
657 return _metadata;
658 }
659
660 HioGlslfxConfig::MetadataDictionary
_GetMetadata(VtDictionary const & dict,string * errorStr) const661 HioGlslfxConfig::_GetMetadata(VtDictionary const & dict,
662 string *errorStr) const
663 {
664 MetadataDictionary ret;
665
666 VtValue metadata;
667
668 // look for the metadata section
669 if (!TfMapLookup(dict, _tokens->metadata, &metadata)) {
670 return ret;
671 }
672
673 // verify that it holds a VtDictionary
674 if (!metadata.IsHolding<VtDictionary>()) {
675 *errorStr = TfStringPrintf("%s declaration expects a dictionary value",
676 _tokens->metadata.GetText());
677 return ret;
678 }
679
680 return metadata.UncheckedGet<VtDictionary>();
681 }
682
683 PXR_NAMESPACE_CLOSE_SCOPE
684
685