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