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, &params)) {
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, &paramOrderAny);
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