1 // Copyright Contributors to the Open Shading Language project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4 
5 /*
6 The code in this file is based somewhat on code released by NVIDIA as
7 part of Gelato (specifically, gsoinfo.cpp).  That code had the following
8 copyright notice:
9 
10    Copyright 2004 NVIDIA Corporation.  All Rights Reserved.
11 
12 and was distributed under the 3-clause BSD license.
13 SPDX-License-Identifier: BSD-3-Clause
14 */
15 
16 /// <doc... oslinfo_source>
17 ///
18 /// Example program using OSLQuery
19 /// ==============================
20 ///
21 /// This is the full text of `oslinfo`, a command-line utility that
22 /// for any shader, will print out its parameters (name, type, default
23 /// values, and metadata).
24 ///
25 /// ~~~~
26 /// <script type="preformatted">
27 #include <cstring>
28 #include <iostream>
29 #include <string>
30 
31 #include <OpenImageIO/argparse.h>
32 #include <OpenImageIO/filesystem.h>
33 #include <OpenImageIO/strutil.h>
34 #include <OpenImageIO/sysutil.h>
35 #include <OpenImageIO/timer.h>
36 
37 #include <OSL/oslquery.h>
38 using namespace OSL;
39 
40 
41 static std::string searchpath;
42 static bool verbose  = false;
43 static bool help     = false;
44 static bool runstats = false;
45 static std::string oneparam;
46 
47 
48 
49 // Print the default values for a parameter built out of integers.
50 static void
print_default_string_vals(const OSLQuery::Parameter * p,bool verbose)51 print_default_string_vals(const OSLQuery::Parameter* p, bool verbose)
52 {
53     size_t ne;
54     if (p->varlenarray || p->type.arraylen < 0)
55         ne = p->sdefault.size();
56     else
57         ne = p->type.numelements();
58     if (verbose) {
59         for (size_t a = 0; a < ne; ++a)
60             std::cout << "\t\tDefault value: \""
61                       << OIIO::Strutil::escape_chars(p->sdefault[a]) << "\"\n";
62     } else {
63         for (size_t a = 0; a < ne; ++a)
64             std::cout << "\"" << OIIO::Strutil::escape_chars(p->sdefault[a])
65                       << "\" ";
66         std::cout << "\n";
67     }
68 }
69 
70 
71 
72 // Print the default values for a parameter built out of floats (including
73 // color, point, etc., or arrays thereof).
74 static void
print_default_int_vals(const OSLQuery::Parameter * p,bool verbose)75 print_default_int_vals(const OSLQuery::Parameter* p, bool verbose)
76 {
77     size_t nf = p->type.aggregate;
78     size_t ne;
79     if (p->varlenarray || p->type.arraylen < 0)
80         ne = p->idefault.size() / nf;
81     else
82         ne = p->type.numelements();
83     if (verbose)
84         std::cout << "\t\tDefault value:";
85     if (p->type.arraylen || nf > 1)
86         std::cout << " [";
87     for (size_t a = 0; a < ne; ++a) {
88         for (size_t f = 0; f < nf; ++f)
89             std::cout << ' ' << p->idefault[a * nf + f];
90     }
91     if (p->type.arraylen || nf > 1)
92         std::cout << " ]";
93     std::cout << std::endl;
94 }
95 
96 
97 
98 // Print the default values for a parameter built out of strings.
99 static void
print_default_float_vals(const OSLQuery::Parameter * p,bool verbose)100 print_default_float_vals(const OSLQuery::Parameter* p, bool verbose)
101 {
102     size_t nf = p->type.aggregate;
103     size_t ne;
104     if (p->varlenarray || p->type.arraylen < 0)
105         ne = p->fdefault.size() / nf;
106     else
107         ne = p->type.numelements();
108     if (verbose)
109         std::cout << "\t\tDefault value:";
110     if (p->type.arraylen || nf > 1)
111         std::cout << " [";
112     for (size_t a = 0; a < ne; ++a) {
113         if (verbose && p->spacename.size() > a && !p->spacename[a].empty())
114             std::cout << " \"" << p->spacename[a] << "\"";
115         for (size_t f = 0; f < nf; ++f)
116             std::cout << ' ' << p->fdefault[a * nf + f];
117     }
118     if (p->type.arraylen || nf > 1)
119         std::cout << " ]";
120     std::cout << std::endl;
121 }
122 
123 
124 
125 // Print all the metadata for a parameter.
126 static void
print_metadata(const OSLQuery::Parameter & m)127 print_metadata(const OSLQuery::Parameter& m)
128 {
129     std::string typestring(m.type.c_str());
130     std::cout << "\t\tmetadata: " << typestring << ' ' << m.name << " =";
131     for (unsigned int d = 0; d < m.idefault.size(); ++d)
132         std::cout << " " << m.idefault[d];
133     for (unsigned int d = 0; d < m.fdefault.size(); ++d)
134         std::cout << " " << m.fdefault[d];
135     for (unsigned int d = 0; d < m.sdefault.size(); ++d)
136         std::cout << " \"" << OIIO::Strutil::escape_chars(m.sdefault[d])
137                   << "\"";
138     std::cout << std::endl;
139 }
140 
141 
142 
143 static void
oslinfo(const std::string & name)144 oslinfo(const std::string& name)
145 {
146     OIIO::Timer t(runstats ? OIIO::Timer::StartNow : OIIO::Timer::DontStartNow);
147     OSLQuery g;
148     g.open(name, searchpath);
149     std::string e = g.geterror();
150     if (!e.empty()) {
151         std::cout << "ERROR opening shader \"" << name << "\" (" << e << ")\n";
152         return;
153     }
154     if (runstats) {
155         // display timings in an easy to sort form
156         std::cout << t.stop() << " sec for " << name << "\n";
157         return;  // don't show anything else, we are just benchmarking
158     }
159 
160     if (oneparam.empty()) {
161         std::cout << g.shadertype() << " \"" << g.shadername() << "\"\n";
162         if (verbose) {
163             for (unsigned int m = 0; m < g.metadata().size(); ++m)
164                 print_metadata(g.metadata()[m]);
165         }
166     }
167 
168     for (size_t i = 0; i < g.nparams(); ++i) {
169         const OSLQuery::Parameter* p = g.getparam(i);
170         if (!p)
171             break;
172         if (oneparam.size() && oneparam != p->name)
173             continue;
174         std::string typestring;
175         if (p->isstruct)
176             typestring = "struct " + p->structname.string();
177         else
178             typestring = p->type.c_str();
179         if (verbose) {
180             std::cout << "    \"" << p->name << "\" \""
181                       << (p->isoutput ? "output " : "") << typestring << "\"\n";
182         } else {
183             std::cout << (p->isoutput ? "output " : "") << typestring << ' '
184                       << p->name << ' ';
185         }
186         if (p->isstruct) {
187             if (verbose)
188                 std::cout << "\t\t";
189             std::cout << "fields: {";
190             for (size_t f = 0; f < p->fields.size(); ++f) {
191                 if (f)
192                     std::cout << ", ";
193                 std::string fieldname = p->name.string() + '.'
194                                         + p->fields[f].string();
195                 const OSLQuery::Parameter* field = g.getparam(fieldname);
196                 if (field)
197                     std::cout << field->type.c_str() << ' ' << p->fields[f];
198                 else
199                     std::cout << "UNKNOWN";
200             }
201             std::cout << "}\n";
202         } else if (!p->validdefault) {
203             if (verbose)
204                 std::cout << "\t\tUnknown default value\n";
205             else
206                 std::cout << "nodefault\n";
207         } else if (p->type.basetype == TypeDesc::STRING)
208             print_default_string_vals(p, verbose);
209         else if (p->type.basetype == TypeDesc::INT)
210             print_default_int_vals(p, verbose);
211         else
212             print_default_float_vals(p, verbose);
213         if (verbose) {
214             for (unsigned int i = 0; i < p->metadata.size(); ++i)
215                 print_metadata(p->metadata[i]);
216         }
217     }
218 }
219 
220 
221 
222 static int
input_file(int argc,const char * argv[])223 input_file(int argc, const char* argv[])
224 {
225     for (int i = 0; i < argc; i++) {
226         oslinfo(argv[i]);
227     }
228     return 0;
229 }
230 
231 
232 
233 int
main(int argc,char * argv[])234 main(int argc, char* argv[])
235 {
236     // Globally force classic "C" locale, and turn off all formatting
237     // internationalization, for the entire oslinfo application.
238     std::locale::global(std::locale::classic());
239 
240 #ifdef OIIO_HAS_STACKTRACE
241     // Helpful for debugging to make sure that any crashes dump a stack
242     // trace.
243     OIIO::Sysutil::setup_crash_stacktrace("stdout");
244 #endif
245 
246     OIIO::Filesystem::convert_native_arguments(argc, (const char**)argv);
247 
248     OIIO::ArgParse ap(argc, (const char**)argv);
249     ap.options(
250         "oslinfo -- list parameters of a compiled OSL shader\n" OSL_INTRO_STRING
251         "\n"
252         "Usage:  oslinfo [options] file0 [file1 ...]\n",
253         "%*", input_file, "", "-h", &help, "Print help message", "--help",
254         &help, "", "-v", &verbose, "Verbose", "--runstats", &runstats,
255         "Benchmark shader loading time for queries", "-p %s", &searchpath,
256         "Set searchpath for shaders", "--param %s", &oneparam,
257         "Output information in just this parameter", NULL);
258 
259     if (ap.parse(argc, (const char**)argv) < 0) {
260         std::cerr << ap.geterror() << std::endl;
261         ap.usage();
262     } else if (help || argc <= 1) {
263         ap.usage();
264     }
265     return EXIT_SUCCESS;
266 }
267 
268 /// </script>
269 /// ~~~~
270