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 #include <cmath>
7 #include <fstream>
8 #include <iostream>
9 #include <locale>
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include <OpenImageIO/imageio.h>
15 #include <OpenImageIO/imagebuf.h>
16 #include <OpenImageIO/imagebufalgo.h>
17 #include <OpenImageIO/imagebufalgo_util.h>
18 #include <OpenImageIO/imagecache.h>
19 #include <OpenImageIO/argparse.h>
20 #include <OpenImageIO/strutil.h>
21 #include <OpenImageIO/sysutil.h>
22 #include <OpenImageIO/filesystem.h>
23 #include <OpenImageIO/timer.h>
24 
25 #ifdef OSL_USE_OPTIX
26 // purely to get optix version -- once optix 7.0 is required this can go away
27 #include <optix.h>
28 #endif
29 
30 #include <OSL/oslexec.h>
31 #include <OSL/oslcomp.h>
32 #include <OSL/oslquery.h>
33 #include "optixgridrender.h"
34 #include "simplerend.h"
35 
36 using namespace OSL;
37 using OIIO::TypeDesc;
38 using OIIO::ParamValue;
39 using OIIO::ParamValueList;
40 
41 static ShadingSystem *shadingsys = NULL;
42 static std::vector<std::string> shadernames;
43 static std::vector<std::string> outputfiles;
44 static std::vector<std::string> outputvars;
45 static std::vector<ustring> outputvarnames;
46 static std::string dataformatname = "";
47 static std::string shaderpath;
48 static std::vector<std::string> entrylayers;
49 static std::vector<std::string> entryoutputs;
50 static std::vector<int> entrylayer_index;
51 static std::vector<const ShaderSymbol *> entrylayer_symbols;
52 static bool debug1 = false;
53 static bool debug2 = false;
54 static bool llvm_debug = false;
55 static bool verbose = false;
56 static bool runstats = false;
57 static bool vary_Pdxdy = false;
58 static bool vary_udxdy = false;
59 static bool vary_vdxdy = false;
60 static bool saveptx = false;
61 static bool warmup = false;
62 static bool profile = false;
63 static bool O0 = false, O1 = false, O2 = false;
64 static bool pixelcenters = false;
65 static bool debugnan = false;
66 static bool debug_uninit = false;
67 static bool use_group_outputs = false;
68 static bool do_oslquery = false;
69 static bool inbuffer = false;
70 static bool use_shade_image = false;
71 static bool userdata_isconnected = false;
72 static bool print_outputs = false;
73 static bool use_optix = OIIO::Strutil::stoi(OIIO::Sysutil::getenv("TESTSHADE_OPTIX"));
74 static int xres = 1, yres = 1;
75 static int num_threads = 0;
76 static std::string groupname;
77 static std::string groupspec;
78 static std::string layername;
79 static std::vector<std::string> connections;
80 static ParamValueList params;
81 static ParamValueList reparams;
82 static std::string reparam_layer;
83 static ErrorHandler errhandler;
84 static int iters = 1;
85 static std::string raytype = "camera";
86 static bool raytype_opt = false;
87 static std::string extraoptions;
88 static std::string texoptions;
89 static OSL::Matrix44 Mshad;  // "shader" space to "common" space matrix
90 static OSL::Matrix44 Mobj;   // "object" space to "common" space matrix
91 static ShaderGroupRef shadergroup;
92 static std::string archivegroup;
93 static int exprcount = 0;
94 static bool shadingsys_options_set = false;
95 static float uscale = 1, vscale = 1;
96 static float uoffset = 0, voffset = 0;
97 static std::vector<const char*> shader_setup_args;
98 static std::string localename = OIIO::Sysutil::getenv("TESTSHADE_LOCALE");
99 static OIIO::ParamValueList userdata;
100 
101 
102 
103 static void
inject_params()104 inject_params ()
105 {
106     for (auto&& pv : params)
107         shadingsys->Parameter (*shadergroup, pv.name(), pv.type(), pv.data(),
108                                pv.interp() == ParamValue::INTERP_CONSTANT);
109 }
110 
111 
112 
113 // Set shading system global attributes based on command line options.
114 static void
set_shadingsys_options()115 set_shadingsys_options ()
116 {
117     // If benchmarking it isn't necessary to clear the memory. however for
118     // unit tests and tracking down early exit issues we may not want the
119     // previous sample's group data masquerading as correct values for the
120     // next sample, who due to a bug, may not have correct control flow and
121     // not actually write to those values.
122     OSL_DEV_ONLY(shadingsys->attribute ("clearmemory", 1));
123 
124     // Always generate llvm debugging info
125     shadingsys->attribute ("llvm_debugging_symbols", 1);
126 
127     // Always emit llvm Intel profiling events
128     shadingsys->attribute ("llvm_profiling_events", 1);
129 
130     OSL_DEV_ONLY(llvm_debug = true);
131     shadingsys->attribute ("llvm_debug", (llvm_debug ? 2 : 0));
132 
133     shadingsys->attribute ("debug", debug2 ? 2 : (debug1 ? 1 : 0));
134     shadingsys->attribute ("compile_report", debug1|debug2);
135     int opt = 2;  // default
136     if (O0) opt = 0;
137     if (O1) opt = 1;
138     if (O2) opt = 2;
139     if (const char *opt_env = getenv ("TESTSHADE_OPT"))  // overrides opt
140         opt = atoi(opt_env);
141     shadingsys->attribute ("optimize", opt);
142     shadingsys->attribute ("profile", int(profile));
143     shadingsys->attribute ("lockgeom", 1);
144     shadingsys->attribute ("debug_nan", debugnan);
145     shadingsys->attribute ("debug_uninit", debug_uninit);
146     shadingsys->attribute ("userdata_isconnected", userdata_isconnected);
147     if (! shaderpath.empty())
148         shadingsys->attribute ("searchpath:shader", shaderpath);
149     if (extraoptions.size())
150         shadingsys->attribute ("options", extraoptions);
151     if (texoptions.size())
152         shadingsys->texturesys()->attribute ("options", texoptions);
153     shadingsys_options_set = true;
154 }
155 
156 
157 
158 static void
compile_buffer(const std::string & sourcecode,const std::string & shadername)159 compile_buffer (const std::string &sourcecode,
160                 const std::string &shadername)
161 {
162     // std::cout << "source was\n---\n" << sourcecode << "---\n\n";
163     std::string osobuffer;
164     OSLCompiler compiler;
165     std::vector<std::string> options;
166 
167     if (! compiler.compile_buffer (sourcecode, osobuffer, options)) {
168         std::cerr << "Could not compile \"" << shadername << "\"\n";
169         exit (EXIT_FAILURE);
170     }
171     // std::cout << "Compiled to oso:\n---\n" << osobuffer << "---\n\n";
172 
173     if (! shadingsys->LoadMemoryCompiledShader (shadername, osobuffer)) {
174         std::cerr << "Could not load compiled buffer from \""
175                   << shadername << "\"\n";
176         exit (EXIT_FAILURE);
177     }
178 }
179 
180 
181 
182 static void
shader_from_buffers(std::string shadername)183 shader_from_buffers (std::string shadername)
184 {
185     std::string oslfilename = shadername;
186     if (! OIIO::Strutil::ends_with (oslfilename, ".osl"))
187         oslfilename += ".osl";
188     std::string sourcecode;
189     if (! OIIO::Filesystem::read_text_file (oslfilename, sourcecode)) {
190         std::cerr << "Could not open \"" << oslfilename << "\"\n";
191         exit (EXIT_FAILURE);
192     }
193 
194     compile_buffer (sourcecode, shadername);
195     // std::cout << "Read and compiled " << shadername << "\n";
196 }
197 
198 
199 
200 static int
add_shader(int argc,const char * argv[])201 add_shader (int argc, const char *argv[])
202 {
203     OSL_DASSERT(argc == 1);
204     string_view shadername (argv[0]);
205 
206     set_shadingsys_options ();
207 
208     if (inbuffer)  // Request to exercise the buffer-based API calls
209         shader_from_buffers (shadername);
210 
211     for (int i = 0;  i < argc;  i++) {
212         inject_params ();
213         shadernames.push_back (shadername);
214         shadingsys->Shader (*shadergroup, "surface", shadername, layername);
215         layername.clear ();
216         params.clear ();
217     }
218     return 0;
219 }
220 
221 
222 
223 static void
action_shaderdecl(int,const char * argv[])224 action_shaderdecl (int /*argc*/, const char *argv[])
225 {
226     // `--shader shadername layername` is exactly equivalent to:
227     // `--layer layername` followed by naming the shader.
228     layername = argv[2];
229     add_shader (1, argv+1);
230 }
231 
232 
233 
234 // The --expr ARG command line option will take ARG that is a snipped of
235 // OSL source code, embed it in some boilerplate shader wrapper, compile
236 // it from memory, and run that in the same way that would have been done
237 // if it were a compiled shader on disk. The boilerplate assumes that there
238 // are two output parameters for the shader: color result, and float alpha.
239 //
240 // Example use:
241 //   testshade -v -g 64 64 -o result out.exr -expr 'result=color(u,v,0);'
242 //
243 static void
specify_expr(int argc OSL_MAYBE_UNUSED,const char * argv[])244 specify_expr (int argc OSL_MAYBE_UNUSED, const char *argv[])
245 {
246     OSL_DASSERT(argc == 2);
247     std::string shadername = OIIO::Strutil::sprintf("expr_%d", exprcount++);
248     std::string sourcecode =
249         "shader " + shadername + " (\n"
250         "    float s = u [[ int lockgeom=0 ]],\n"
251         "    float t = v [[ int lockgeom=0 ]],\n"
252         "    output color result = 0,\n"
253         "    output float alpha = 1,\n"
254         "  )\n"
255         "{\n"
256         "    " + std::string(argv[1]) + "\n"
257         "    ;\n"
258         "}\n";
259     if (verbose)
260         std::cout << "Expression-based shader text is:\n---\n"
261                   << sourcecode << "---\n";
262 
263     set_shadingsys_options ();
264 
265     compile_buffer (sourcecode, shadername);
266 
267     inject_params ();
268     shadernames.push_back (shadername);
269     shadingsys->Shader (*shadergroup, "surface", shadername, layername);
270     layername.clear ();
271     params.clear ();
272 }
273 
274 
275 
276 // Utility: Add {paramname, stringval} to the given parameter list.
277 static void
add_param(ParamValueList & params,string_view command,string_view paramname,string_view stringval)278 add_param (ParamValueList& params, string_view command,
279            string_view paramname, string_view stringval)
280 {
281     TypeDesc type = TypeDesc::UNKNOWN;
282     bool unlockgeom = false;
283     float f[16];
284 
285     size_t pos;
286     while ((pos = command.find_first_of(":")) != std::string::npos) {
287         command = command.substr (pos+1, std::string::npos);
288         std::vector<std::string> splits;
289         OIIO::Strutil::split (command, splits, ":", 1);
290         if (splits.size() < 1) {}
291         else if (OIIO::Strutil::istarts_with(splits[0],"type="))
292             type.fromstring (splits[0].c_str()+5);
293         else if (OIIO::Strutil::istarts_with(splits[0],"lockgeom="))
294             unlockgeom = (OIIO::Strutil::from_string<int> (splits[0]) == 0);
295     }
296 
297     // If it is or might be a matrix, look for 16 comma-separated floats
298     if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeMatrix)
299         && sscanf (stringval.c_str(),
300                    "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f",
301                    &f[0], &f[1], &f[2], &f[3],
302                    &f[4], &f[5], &f[6], &f[7], &f[8], &f[9], &f[10], &f[11],
303                    &f[12], &f[13], &f[14], &f[15]) == 16) {
304         params.emplace_back (paramname, TypeDesc::TypeMatrix, 1, f);
305         if (unlockgeom)
306             params.back().interp (ParamValue::INTERP_VERTEX);
307         return;
308     }
309     // If it is or might be a vector type, look for 3 comma-separated floats
310     if ((type == TypeDesc::UNKNOWN || equivalent(type,TypeDesc::TypeVector))
311         && sscanf (stringval.c_str(), "%g, %g, %g", &f[0], &f[1], &f[2]) == 3) {
312         if (type == TypeDesc::UNKNOWN)
313             type = TypeDesc::TypeVector;
314         params.emplace_back (paramname, type, 1, f);
315         if (unlockgeom)
316             params.back().interp (ParamValue::INTERP_VERTEX);
317         return;
318     }
319     // If it is or might be an int, look for an int that takes up the whole
320     // string.
321     if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeInt)
322           && OIIO::Strutil::string_is<int>(stringval)) {
323         params.emplace_back (paramname, OIIO::Strutil::from_string<int>(stringval));
324         if (unlockgeom)
325             params.back().interp (ParamValue::INTERP_VERTEX);
326         return;
327     }
328     // If it is or might be an float, look for a float that takes up the
329     // whole string.
330     if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeFloat)
331           && OIIO::Strutil::string_is<float>(stringval)) {
332         params.emplace_back (paramname, OIIO::Strutil::from_string<float>(stringval));
333         if (unlockgeom)
334             params.back().interp (ParamValue::INTERP_VERTEX);
335         return;
336     }
337 
338     // Catch-all for float types and arrays
339     if (type.basetype == TypeDesc::FLOAT) {
340         int n = type.aggregate * type.numelements();
341         std::vector<float> vals (n);
342         for (int i = 0;  i < n;  ++i) {
343             OIIO::Strutil::parse_float (stringval, vals[i]);
344             OIIO::Strutil::parse_char (stringval, ',');
345         }
346         params.emplace_back (paramname, type, 1, &vals[0]);
347         if (unlockgeom)
348             params.back().interp (ParamValue::INTERP_VERTEX);
349         return;
350     }
351 
352     // Catch-all for int types and arrays
353     if (type.basetype == TypeDesc::INT) {
354         int n = type.aggregate * type.numelements();
355         std::vector<int> vals (n);
356         for (int i = 0;  i < n;  ++i) {
357             OIIO::Strutil::parse_int (stringval, vals[i]);
358             OIIO::Strutil::parse_char (stringval, ',');
359         }
360         params.emplace_back (paramname, type, 1, &vals[0]);
361         if (unlockgeom)
362             params.back().interp (ParamValue::INTERP_VERTEX);
363         return;
364     }
365 
366     // String arrays are slightly tricky
367     if (type.basetype == TypeDesc::STRING && type.is_array()) {
368         std::vector<string_view> splitelements;
369         OIIO::Strutil::split (stringval, splitelements, ",", type.arraylen);
370         splitelements.resize (type.arraylen);
371         std::vector<ustring> strelements;
372         for (auto&& s : splitelements)
373             strelements.push_back (ustring(s));
374         params.emplace_back (paramname, type, 1, &strelements[0]);
375         if (unlockgeom)
376             params.back().interp (ParamValue::INTERP_VERTEX);
377         return;
378     }
379 
380     // All remaining cases -- it's a string
381     const char *s = stringval.c_str();
382     params.emplace_back (paramname, TypeDesc::TypeString, 1, &s);
383     if (unlockgeom)
384         params.back().interp (ParamValue::INTERP_VERTEX);
385 }
386 
387 
388 
389 static void
action_param(int,const char * argv[])390 action_param(int /*argc*/, const char *argv[])
391 {
392     std::string command = argv[0];
393     bool use_reparam = false;
394     if (OIIO::Strutil::istarts_with(command, "--reparam") ||
395         OIIO::Strutil::istarts_with(command, "-reparam"))
396         use_reparam = true;
397     ParamValueList &params (use_reparam ? reparams : (::params));
398 
399     add_param(params, command, argv[1], argv[2]);
400 }
401 
402 
403 
404 // reparam -- just set reparam_layer and then let action_param do all the
405 // hard work.
406 static void
action_reparam(int,const char * argv[])407 action_reparam (int /*argc*/, const char *argv[])
408 {
409     reparam_layer = argv[1];
410     const char *newargv[] = { argv[0], argv[2], argv[3] };
411     action_param (3, newargv);
412 }
413 
414 
415 
416 static void
action_groupspec(int,const char * argv[])417 action_groupspec (int /*argc*/, const char *argv[])
418 {
419     shadingsys->ShaderGroupEnd (*shadergroup);
420     std::string groupspec (argv[1]);
421     if (OIIO::Filesystem::exists (groupspec)) {
422         // If it names a file, use the contents of the file as the group
423         // specification.
424         OIIO::Filesystem::read_text_file (groupspec, groupspec);
425     }
426     set_shadingsys_options ();
427     if (verbose)
428         std::cout << "Processing group specification:\n---\n"
429                   << groupspec << "\n---\n";
430     shadergroup = shadingsys->ShaderGroupBegin (groupname, "surface", groupspec);
431 }
432 
433 
434 
435 static void
stash_shader_arg(int argc,const char * argv[])436 stash_shader_arg (int argc, const char* argv[])
437 {
438     for (int i = 0; i < argc; ++i)
439         shader_setup_args.push_back (argv[i]);
440 }
441 
442 
443 
444 static void
stash_userdata(int argc,const char * argv[])445 stash_userdata(int argc, const char* argv[])
446 {
447     add_param(userdata, argv[0], argv[1], argv[2]);
448 }
449 
450 
451 
452 void
print_info()453 print_info()
454 {
455     ErrorHandler errhandler;
456     SimpleRenderer* rend = nullptr;
457 #ifdef OSL_USE_OPTIX
458     if (use_optix)
459         rend = new OptixGridRenderer;
460     else
461 #endif
462         rend = new SimpleRenderer;
463     TextureSystem *texturesys = TextureSystem::create();
464     shadingsys = new ShadingSystem(rend, texturesys, &errhandler);
465     rend->init_shadingsys(shadingsys);
466 
467     std::cout << "\n" << shadingsys->getstats (5) << "\n";
468 
469     delete shadingsys;
470     delete rend;
471 }
472 
473 
474 
475 static void
getargs(int argc,const char * argv[])476 getargs (int argc, const char *argv[])
477 {
478     static bool help = false;
479 
480     // We have a bit of a chicken-and-egg problem here, where some arguments
481     // set up the shader instances, but other args and housekeeping are
482     // needed first. Untangle by just storing the shader setup args until
483     // they can be later processed in full.
484     shader_setup_args.clear();
485     shader_setup_args.push_back("testshade"); // seed with 'program'
486 
487     OIIO::ArgParse ap;
488     ap.options ("Usage:  testshade [options] shader...",
489                 "%*", stash_shader_arg, "",
490                 "--help", &help, "Print help message",
491                 "-v", &verbose, "Verbose messages",
492                 "-t %d", &num_threads, "Render using N threads (default: auto-detect)",
493                 "--optix", &use_optix, "Use OptiX if available",
494                 "--debug", &debug1, "Lots of debugging info",
495                 "--debug2", &debug2, "Even more debugging info",
496                 "--llvm_debug", &llvm_debug, "Turn on LLVM debugging info",
497                 "--runstats", &runstats, "Print run statistics",
498                 "--stats", &runstats, "",  // DEPRECATED 1.7
499                 "--vary_pdxdy", &vary_Pdxdy, "populate Dx(P) & Dy(P) with varying values (vs. uniform)",
500                 "--vary_udxdy", &vary_udxdy, "populate Dx(u) & Dy(u) with varying values (vs. uniform)",
501                 "--vary_vdxdy", &vary_vdxdy, "populate Dx(v) & Dy(v) with varying values (vs. uniform)",
502                 "--profile", &profile, "Print profile information",
503                 "--saveptx", &saveptx, "Save the generated PTX (OptiX mode only)",
504                 "--warmup", &warmup, "Perform a warmup launch",
505                 "--path %s", &shaderpath, "Specify oso search path",
506                 "--res %d %d", &xres, &yres, "Make an W x H image",
507                 "-g %d %d", &xres, &yres, "", // synonym for -res
508                 "--options %s", &extraoptions, "Set extra OSL options",
509                 "--texoptions %s", &texoptions, "Set extra TextureSystem options",
510                 "-o %L %L", &outputvars, &outputfiles,
511                         "Output (variable, filename)   [filename='null' means don't save]",
512                 "-d %s", &dataformatname, "Set the output data format to one of: "
513                         "uint8, half, float",
514                 "-od %s", &dataformatname, "", // old name
515                 "--print", &print_outputs, "Print values of all -o outputs to console instead of saving images",
516                 "--groupname %s", &groupname, "Set shader group name",
517                 "--layer %@ %s", stash_shader_arg, NULL, "Set next layer name",
518                 "--param %@ %s %s", stash_shader_arg, NULL, NULL,
519                         "Add a parameter (args: name value) (options: type=%s, lockgeom=%d)",
520                 "--shader %@ %s %s", stash_shader_arg, NULL, NULL,
521                         "Declare a shader node (args: shader layername)",
522                 "--connect %@ %s %s %s %s",
523                     stash_shader_arg, NULL, NULL, NULL, NULL,
524                     "Connect fromlayer fromoutput tolayer toinput",
525                 "--reparam %@ %s %s %s", stash_shader_arg, NULL, NULL, NULL,
526                         "Change a parameter (args: layername paramname value) (options: type=%s)",
527                 "--group %@ %s", stash_shader_arg, NULL,
528                         "Specify a full group command",
529                 "--archivegroup %s", &archivegroup,
530                         "Archive the group to a given filename",
531                 "--raytype %s", &raytype, "Set the raytype",
532                 "--raytype_opt", &raytype_opt, "Specify ray type mask for optimization",
533                 "--iters %d", &iters, "Number of iterations",
534                 "-O0", &O0, "Do no runtime shader optimization",
535                 "-O1", &O1, "Do a little runtime shader optimization",
536                 "-O2", &O2, "Do lots of runtime shader optimization",
537                 "--entry %L", &entrylayers, "Add layer to the list of entry points",
538                 "--entryoutput %L", &entryoutputs, "Add output symbol to the list of entry points",
539                 "--center", &pixelcenters, "Shade at output pixel 'centers' rather than corners",
540                 "--debugnan", &debugnan, "Turn on 'debug_nan' mode",
541                 "--debuguninit", &debug_uninit, "Turn on 'debug_uninit' mode",
542                 "--groupoutputs", &use_group_outputs, "Specify group outputs, not global outputs",
543                 "--oslquery", &do_oslquery, "Test OSLQuery at runtime",
544                 "--inbuffer", &inbuffer, "Compile osl source from and to buffer",
545                 "--shadeimage", &use_shade_image, "Use shade_image utility",
546                 "--noshadeimage %!", &use_shade_image, "Don't use shade_image utility",
547                 "--expr %@ %s", stash_shader_arg, NULL, "Specify an OSL expression to evaluate",
548                 "--offsetuv %f %f", &uoffset, &voffset, "Offset s & t texture coordinates (default: 0 0)",
549                 "--offsetst %f %f", &uoffset, &voffset, "", // old name
550                 "--scaleuv %f %f", &uscale, &vscale, "Scale s & t texture lookups (default: 1, 1)",
551                 "--scalest %f %f", &uscale, &vscale, "", // old name
552                 "--userdata %@ %s %s", stash_userdata, nullptr, nullptr,
553                         "Add userdata (args: name value) (options: type=%s)",
554                 "--userdata_isconnected", &userdata_isconnected, "Consider lockgeom=0 to be isconnected()",
555                 "--locale %s", &localename, "Set a different locale",
556                 NULL);
557     if (ap.parse(argc, argv) < 0 /*|| (shadernames.empty() && groupspec.empty())*/) {
558         std::cerr << ap.geterror() << std::endl;
559         ap.usage ();
560         exit (EXIT_FAILURE);
561     }
562     if (help) {
563         std::cout << "testshade -- Test Open Shading Language\n"
564                      OSL_COPYRIGHT_STRING "\n";
565         ap.usage ();
566         print_info();
567         exit (EXIT_SUCCESS);
568     }
569 }
570 
571 
572 
573 static void
process_shader_setup_args(int argc,const char * argv[])574 process_shader_setup_args (int argc, const char *argv[])
575 {
576     OIIO::ArgParse ap;
577     ap.options ("Usage:  testshade [options] shader...",
578                 "%*", add_shader, "",
579                 "--layer %s", &layername, "Set next layer name",
580                 "--param %@ %s %s", &action_param, NULL, NULL,
581                         "Add a parameter (args: name value) (options: type=%s, lockgeom=%d)",
582                 "--shader %@ %s %s", &action_shaderdecl, NULL, NULL,
583                         "Declare a shader node (args: shader layername)",
584                 "--connect %L %L %L %L",
585                     &connections, &connections, &connections, &connections,
586                     "Connect fromlayer fromoutput tolayer toinput",
587                 "--reparam %@ %s %s %s", &action_reparam, NULL, NULL, NULL,
588                         "Change a parameter (args: layername paramname value) (options: type=%s)",
589                 "--group %@ %s", &action_groupspec, &groupspec,
590                         "Specify a full group command",
591                 "--expr %@ %s", &specify_expr, NULL, "Specify an OSL expression to evaluate",
592                 NULL);
593     if (ap.parse(argc, argv) < 0 || (shadernames.empty() && groupspec.empty())) {
594         std::cerr << "ERROR: No shader or group was specified.\n";
595         std::cerr << ap.geterror() << std::endl;
596         std::cerr << "Try `testshade --help` for an explanation of all arguments\n";
597         exit (EXIT_FAILURE);
598     }
599 }
600 
601 
602 
603 // Here we set up transformations.  These are just examples, set up so
604 // that our unit tests can transform among spaces in ways that we will
605 // recognize as correct.  The "shader" and "object" spaces are required
606 // by OSL and the ShaderGlobals will need to have references to them.
607 // For good measure, we also set up a "myspace" space, registering it
608 // with the RendererServices.
609 //
610 static void
setup_transformations(SimpleRenderer & rend,OSL::Matrix44 & Mshad,OSL::Matrix44 & Mobj)611 setup_transformations (SimpleRenderer &rend, OSL::Matrix44 &Mshad,
612                        OSL::Matrix44 &Mobj)
613 {
614     Matrix44 M (1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1);
615     rend.camera_params (M, ustring("perspective"), 90.0f,
616                         0.1f, 1000.0f, xres, yres);
617 
618     // Make a "shader" space that is translated one unit in x and rotated
619     // 45deg about the z axis.
620     Mshad.makeIdentity ();
621     Mshad.translate (OSL::Vec3 (1.0, 0.0, 0.0));
622     Mshad.rotate (OSL::Vec3 (0.0, 0.0, M_PI_4));
623     // std::cout << "shader-to-common matrix: " << Mshad << "\n";
624 
625     // Make an object space that is translated one unit in y and rotated
626     // 90deg about the z axis.
627     Mobj.makeIdentity ();
628     Mobj.translate (OSL::Vec3 (0.0, 1.0, 0.0));
629     Mobj.rotate (OSL::Vec3 (0.0, 0.0, M_PI_2));
630     // std::cout << "object-to-common matrix: " << Mobj << "\n";
631 
632     OSL::Matrix44 Mmyspace;
633     Mmyspace.scale (OSL::Vec3 (1.0, 2.0, 1.0));
634     // std::cout << "myspace-to-common matrix: " << Mmyspace << "\n";
635     rend.name_transform ("myspace", Mmyspace);
636 }
637 
638 
639 
640 // Set up the ShaderGlobals fields for pixel (x,y).
641 static void
setup_shaderglobals(ShaderGlobals & sg,ShadingSystem * shadingsys,int x,int y)642 setup_shaderglobals (ShaderGlobals &sg, ShadingSystem *shadingsys,
643                      int x, int y)
644 {
645     // Just zero the whole thing out to start
646     memset ((char *)&sg, 0, sizeof(ShaderGlobals));
647 
648     // In our SimpleRenderer, the "renderstate" itself just a pointer to
649     // the ShaderGlobals.
650     sg.renderstate = &sg;
651 
652     // Set "shader" space to be Mshad.  In a real renderer, this may be
653     // different for each shader group.
654     sg.shader2common = OSL::TransformationPtr (&Mshad);
655 
656     // Set "object" space to be Mobj.  In a real renderer, this may be
657     // different for each object.
658     sg.object2common = OSL::TransformationPtr (&Mobj);
659 
660     // Just make it look like all shades are the result of 'raytype' rays.
661     sg.raytype = shadingsys->raytype_bit (ustring (raytype));
662 
663     // Set up u,v to vary across the "patch", and also their derivatives.
664     // Note that since u & x, and v & y are aligned, we only need to set
665     // values for dudx and dvdy, we can use the memset above to have set
666     // dvdx and dudy to 0.
667     if (pixelcenters) {
668         // Our patch is like an "image" with shading samples at the
669         // centers of each pixel.
670         sg.u = uscale * (float)(x+0.5f) / xres + uoffset;
671         sg.v = vscale * (float)(y+0.5f) / yres + voffset;
672         if (vary_udxdy) {
673             sg.dudx = 1.0f - sg.u;
674             sg.dudy = sg.u;
675         } else {
676             sg.dudx = uscale / xres;
677         }
678         if (vary_vdxdy) {
679             sg.dvdx = 1.0f - sg.v;
680             sg.dvdy = sg.v;
681         } else {
682             sg.dvdy = vscale / yres;
683         }
684     } else {
685         // Our patch is like a Reyes grid of points, with the border
686         // samples being exactly on u,v == 0 or 1.
687         sg.u = uscale * ((xres == 1) ? 0.5f : (float) x / (xres - 1)) + uoffset;
688         sg.v = vscale * ((yres == 1) ? 0.5f : (float) y / (yres - 1)) + voffset;
689         if (vary_udxdy) {
690             sg.dudx = 1.0f - sg.u;
691             sg.dudy = sg.u;
692         } else {
693             sg.dudx = uscale / std::max (1, xres-1);
694         }
695         if (vary_vdxdy) {
696             sg.dvdx = 1.0f - sg.v;
697             sg.dvdy = sg.v;
698         } else {
699             sg.dvdy = vscale / std::max (1, yres-1);
700         }
701     }
702 
703     // Assume that position P is simply (u,v,1), that makes the patch lie
704     // on [0,1] at z=1.
705     sg.P = Vec3 (sg.u, sg.v, 1.0f);
706     // Derivatives with respect to x,y
707     if (vary_Pdxdy) {
708         sg.dPdx = Vec3 (1.0f - sg.u, 1.0f - sg.v, sg.u*0.5);
709         sg.dPdy = Vec3 (1.0f - sg.v, 1.0f - sg.u, sg.v*0.5);
710     } else {
711         sg.dPdx = Vec3 (uscale / std::max (1, xres-1), 0.0f, 0.0f);
712         sg.dPdy = Vec3 (0.0f, vscale / std::max (1, yres-1), 0.0f);
713     }
714     sg.dPdz = Vec3 (0.0f, 0.0f, 0.0f);  // just use 0 for volume tangent
715     // Tangents of P with respect to surface u,v
716     sg.dPdu = Vec3 (1.0f, 0.0f, 0.0f);
717     sg.dPdv = Vec3 (0.0f, 1.0f, 0.0f);
718     // That also implies that our normal points to (0,0,1)
719     sg.N    = Vec3 (0, 0, 1);
720     sg.Ng   = Vec3 (0, 0, 1);
721 
722     // Set the surface area of the patch to 1 (which it is).  This is
723     // only used for light shaders that call the surfacearea() function.
724     sg.surfacearea = 1;
725 }
726 
727 
728 
729 static void
setup_output_images(SimpleRenderer * rend,ShadingSystem * shadingsys,ShaderGroupRef & shadergroup)730 setup_output_images (SimpleRenderer *rend, ShadingSystem *shadingsys,
731                      ShaderGroupRef &shadergroup)
732 {
733     // Tell the shading system which outputs we want
734     if (outputvars.size()) {
735         std::vector<const char *> aovnames (outputvars.size());
736         for (size_t i = 0; i < outputvars.size(); ++i) {
737             ustring varname (outputvars[i]);
738             aovnames[i] = varname.c_str();
739             size_t dot = varname.find('.');
740             if (dot != ustring::npos) {
741                 // If the name contains a dot, it's intended to be layer.symbol
742                 varname = ustring (varname, dot+1);
743             }
744         }
745         shadingsys->attribute (use_group_outputs ? shadergroup.get() : NULL,
746                                "renderer_outputs",
747                                TypeDesc(TypeDesc::STRING,(int)aovnames.size()),
748                                &aovnames[0]);
749         if (use_group_outputs)
750             std::cout << "Marking group outputs, not global renderer outputs.\n";
751     }
752 
753     if (entrylayers.size()) {
754         std::vector<const char *> layers;
755         std::cout << "Entry layers:";
756         for (size_t i = 0; i < entrylayers.size(); ++i) {
757             ustring layername (entrylayers[i]);  // convert to ustring
758             int layid = shadingsys->find_layer (*shadergroup, layername);
759             layers.push_back (layername.c_str());
760             entrylayer_index.push_back (layid);
761             std::cout << ' ' << entrylayers[i] << "(" << layid << ")";
762         }
763         std::cout << "\n";
764         shadingsys->attribute (shadergroup.get(), "entry_layers",
765                                TypeDesc(TypeDesc::STRING,(int)entrylayers.size()),
766                                &layers[0]);
767     }
768 
769     OSL::PerThreadInfo *thread_info = shadingsys->create_thread_info();
770     ShadingContext *ctx = shadingsys->get_context(thread_info);
771     // Because we can only call find_symbol or get_symbol on something that
772     // has been set up to shade (or executed), we call execute() but tell it
773     // not to actually run the shader.
774     ShaderGlobals sg;
775     setup_shaderglobals (sg, shadingsys, 0, 0);
776 
777     int raytype_bit = shadingsys->raytype_bit (ustring (raytype));
778     if (raytype_opt)
779         shadingsys->optimize_group (shadergroup.get(), raytype_bit, ~raytype_bit, ctx);
780     shadingsys->execute (*ctx, *shadergroup, sg, false);
781 
782     if (entryoutputs.size()) {
783         std::cout << "Entry outputs:";
784         for (size_t i = 0; i < entryoutputs.size(); ++i) {
785             ustring name (entryoutputs[i]);  // convert to ustring
786             const ShaderSymbol *sym = shadingsys->find_symbol (*shadergroup, name);
787             if (!sym) {
788                 std::cout << "\nEntry output " << entryoutputs[i] << " not found. Abording.\n";
789                 exit (EXIT_FAILURE);
790             }
791             entrylayer_symbols.push_back (sym);
792             std::cout << ' ' << entryoutputs[i];
793         }
794         std::cout << "\n";
795     }
796 
797     // For each output file specified on the command line...
798     for (size_t i = 0;  i < outputfiles.size();  ++i) {
799         // Make a ustring version of the output name, for fast manipulation
800         outputvarnames.emplace_back(outputvars[i]);
801 
802         // Ask for a pointer to the symbol's data, as computed by this
803         // shader.
804         TypeDesc t;
805         const void *data = shadingsys->get_symbol (*ctx, outputvarnames[i], t);
806         if (!data) {
807             std::cout << "Output " << outputvars[i]
808                       << " not found, skipping.\n";
809             continue;  // Skip if symbol isn't found
810         }
811         std::cout << "Output " << outputvars[i] << " to "
812                   << outputfiles[i] << "\n";
813 
814         if (outputfiles[i] == "null") {
815             // Filename "null" means to consider this a "renderer output",
816             // but not save it in an image file.
817             continue;
818         }
819 
820         // And the "base" type, i.e. the type of each element or channel
821         TypeDesc tbase = TypeDesc ((TypeDesc::BASETYPE)t.basetype);
822 
823         // But which type are we going to write?  Use the true data type
824         // from OSL, unless the command line options indicated that
825         // something else was desired.
826         TypeDesc outtypebase = tbase;
827         if (dataformatname == "uint8")
828             outtypebase = TypeDesc::UINT8;
829         else if (dataformatname == "half")
830             outtypebase = TypeDesc::HALF;
831         else if (dataformatname == "float")
832             outtypebase = TypeDesc::FLOAT;
833 
834         // Number of channels to write to the image is the number of (array)
835         // elements times the number of channels (e.g. 1 for scalar, 3 for
836         // vector, etc.)
837         int nchans = t.numelements() * t.aggregate;
838 
839         // Make an ImageBuf of the right type and size to hold this
840         // symbol's output, and initially clear it to all black pixels.
841         rend->add_output (outputvars[i], outputfiles[i], tbase, nchans);
842     }
843 
844     if (! rend->noutputs()) {
845         rend->add_output ("Cout", "Cout.tif", OIIO::TypeFloat, 3);
846     }
847 
848     shadingsys->release_context (ctx);  // don't need this anymore for now
849     shadingsys->destroy_thread_info(thread_info);
850 }
851 
852 
853 
854 // For pixel (x,y) that was just shaded by the given shading context,
855 // save each of the requested outputs to the corresponding output
856 // ImageBuf.
857 //
858 // In a real renderer, this is illustrative of how you would pull shader
859 // outputs into "AOV's" (arbitrary output variables, or additional
860 // renderer outputs).  You would, of course, also grab the closure Ci
861 // and integrate the lights using that BSDF to determine the radiance
862 // in the direction of the camera for that pixel.
863 static void
save_outputs(SimpleRenderer * rend,ShadingSystem * shadingsys,ShadingContext * ctx,int x,int y)864 save_outputs (SimpleRenderer *rend, ShadingSystem *shadingsys,
865               ShadingContext *ctx, int x, int y)
866 {
867     if (print_outputs)
868         printf ("Pixel (%d, %d):\n", x, y);
869     // For each output requested on the command line...
870     for (size_t i = 0, e = rend->noutputs();  i < e;  ++i) {
871         // Skip if we couldn't open the image or didn't match a known output
872         OIIO::ImageBuf* outputimg = rend->outputbuf(i);
873         if (! outputimg)
874             continue;
875 
876         // Ask for a pointer to the symbol's data, as computed by this
877         // shader.
878         TypeDesc t;
879         const void *data = shadingsys->get_symbol (*ctx, rend->outputname(i), t);
880         if (!data)
881             continue;  // Skip if symbol isn't found
882 
883         int nchans = outputimg->nchannels();
884         if (t.basetype == TypeDesc::FLOAT) {
885             // If the variable we are outputting is float-based, set it
886             // directly in the output buffer.
887             outputimg->setpixel (x, y, (const float *)data);
888             if (print_outputs) {
889                 printf ("  %s :", outputvarnames[i].c_str());
890                 for (int c = 0; c < nchans; ++c)
891                     printf (" %g", ((const float *)data)[c]);
892                 printf ("\n");
893             }
894         } else if (t.basetype == TypeDesc::INT) {
895             // We are outputting an integer variable, so we need to
896             // convert it to floating point.
897             float *pixel = OIIO_ALLOCA(float, nchans);
898             OIIO::convert_types (TypeDesc::BASETYPE(t.basetype), data,
899                                  TypeDesc::FLOAT, pixel, nchans);
900             outputimg->setpixel (x, y, &pixel[0]);
901             if (print_outputs) {
902                 printf ("  %s :", outputvarnames[i].c_str());
903                 for (int c = 0; c < nchans; ++c)
904                     printf (" %d", ((const int *)data)[c]);
905                 printf ("\n");
906             }
907         }
908         // N.B. Drop any outputs that aren't float- or int-based
909     }
910 }
911 
912 
913 
914 static void
test_group_attributes(ShaderGroup * group)915 test_group_attributes (ShaderGroup *group)
916 {
917     int nt = 0;
918     if (shadingsys->getattribute (group, "num_textures_needed", nt)) {
919         std::cout << "Need " << nt << " textures:\n";
920         ustring *tex = NULL;
921         shadingsys->getattribute (group, "textures_needed",
922                                   TypeDesc::PTR, &tex);
923         for (int i = 0; i < nt; ++i)
924             std::cout << "    " << tex[i] << "\n";
925         int unk = 0;
926         shadingsys->getattribute (group, "unknown_textures_needed", unk);
927         if (unk)
928             std::cout << "    and unknown textures\n";
929     }
930     int nclosures = 0;
931     if (shadingsys->getattribute (group, "num_closures_needed", nclosures)) {
932         std::cout << "Need " << nclosures << " closures:\n";
933         ustring *closures = NULL;
934         shadingsys->getattribute (group, "closures_needed",
935                                   TypeDesc::PTR, &closures);
936         for (int i = 0; i < nclosures; ++i)
937             std::cout << "    " << closures[i] << "\n";
938         int unk = 0;
939         shadingsys->getattribute (group, "unknown_closures_needed", unk);
940         if (unk)
941             std::cout << "    and unknown closures\n";
942     }
943     int nglobals = 0;
944     if (shadingsys->getattribute (group, "num_globals_needed", nglobals)) {
945         std::cout << "Need " << nglobals << " globals: ";
946         ustring *globals = NULL;
947         shadingsys->getattribute (group, "globals_needed",
948                                   TypeDesc::PTR, &globals);
949         for (int i = 0; i < nglobals; ++i)
950             std::cout << " " << globals[i];
951         std::cout << "\n";
952     }
953 
954     int globals_read = 0;
955     int globals_write = 0;
956     shadingsys->getattribute (group, "globals_read", globals_read);
957     shadingsys->getattribute (group, "globals_write", globals_write);
958     std::cout << "Globals read: (" << globals_read << ") ";
959     for (int i = 1; i < int(SGBits::last); i <<= 1)
960         if (globals_read & i)
961             std::cout << ' ' << shadingsys->globals_name (SGBits(i));
962     std::cout << "\nGlobals written: (" << globals_write << ") ";
963     for (int i = 1; i < int(SGBits::last); i <<= 1)
964         if (globals_write & i)
965             std::cout << ' ' << shadingsys->globals_name (SGBits(i));
966     std::cout << "\n";
967 
968     int nuser = 0;
969     if (shadingsys->getattribute (group, "num_userdata", nuser) && nuser) {
970         std::cout << "Need " << nuser << " user data items:\n";
971         ustring *userdata_names = NULL;
972         TypeDesc *userdata_types = NULL;
973         int *userdata_offsets = NULL;
974         bool *userdata_derivs = NULL;
975         shadingsys->getattribute (group, "userdata_names",
976                                   TypeDesc::PTR, &userdata_names);
977         shadingsys->getattribute (group, "userdata_types",
978                                   TypeDesc::PTR, &userdata_types);
979         shadingsys->getattribute (group, "userdata_offsets",
980                                   TypeDesc::PTR, &userdata_offsets);
981         shadingsys->getattribute (group, "userdata_derivs",
982                                   TypeDesc::PTR, &userdata_derivs);
983         OSL_DASSERT(userdata_names && userdata_types && userdata_offsets);
984         for (int i = 0; i < nuser; ++i)
985             std::cout << "    " << userdata_names[i] << ' '
986                       << userdata_types[i] << "  offset="
987                       << userdata_offsets[i] << " deriv="
988                       << userdata_derivs[i] << "\n";
989     }
990     int nattr = 0;
991     if (shadingsys->getattribute (group, "num_attributes_needed", nattr) && nattr) {
992         std::cout << "Need " << nattr << " attributes:\n";
993         ustring *names = NULL;
994         ustring *scopes = NULL;
995         shadingsys->getattribute (group, "attributes_needed",
996                                   TypeDesc::PTR, &names);
997         shadingsys->getattribute (group, "attribute_scopes",
998                                   TypeDesc::PTR, &scopes);
999         OSL_DASSERT(names && scopes);
1000         for (int i = 0; i < nattr; ++i)
1001             std::cout << "    " << names[i] << ' '
1002                       << scopes[i] << "\n";
1003 
1004         int unk = 0;
1005         shadingsys->getattribute (group, "unknown_attributes_needed", unk);
1006         if (unk)
1007             std::cout << "    and unknown attributes\n";
1008     }
1009     int raytype_queries = 0;
1010     shadingsys->getattribute (group, "raytype_queries", raytype_queries);
1011     std::cout << "raytype() query mask: " << raytype_queries << "\n";
1012 }
1013 
1014 
1015 
1016 void
shade_region(SimpleRenderer * rend,ShaderGroup * shadergroup,OIIO::ROI roi,bool save)1017 shade_region (SimpleRenderer *rend, ShaderGroup *shadergroup,
1018               OIIO::ROI roi, bool save)
1019 {
1020     // Request an OSL::PerThreadInfo for this thread.
1021     OSL::PerThreadInfo *thread_info = shadingsys->create_thread_info();
1022 
1023     // Request a shading context so that we can execute the shader.
1024     // We could get_context/release_context for each shading point,
1025     // but to save overhead, it's more efficient to reuse a context
1026     // within a thread.
1027     ShadingContext *ctx = shadingsys->get_context (thread_info);
1028 
1029     // Set up shader globals and a little test grid of points to shade.
1030     ShaderGlobals shaderglobals;
1031 
1032     // Loop over all pixels in the image (in x and y)...
1033     for (int y = roi.ybegin;  y < roi.yend;  ++y) {
1034         for (int x = roi.xbegin;  x < roi.xend;  ++x) {
1035             // In a real renderer, this is where you would figure
1036             // out what object point is visible in this pixel (or
1037             // this sample, for antialiasing).  Once determined,
1038             // you'd set up a ShaderGlobals that contained the vital
1039             // information about that point, such as its location,
1040             // the normal there, the u and v coordinates on the
1041             // surface, the transformation of that object, and so
1042             // on.
1043             //
1044             // This test app is not a real renderer, so we just
1045             // set it up rigged to look like we're rendering a single
1046             // quadrilateral that exactly fills the viewport, and that
1047             // setup is done in the following function call:
1048             setup_shaderglobals (shaderglobals, shadingsys, x, y);
1049 
1050             // Actually run the shader for this point
1051             if (entrylayer_index.empty()) {
1052                 // Sole entry point for whole group, default behavior
1053                 shadingsys->execute (*ctx, *shadergroup, shaderglobals);
1054             } else {
1055                 // Explicit list of entries to call in order
1056                 shadingsys->execute_init (*ctx, *shadergroup, shaderglobals);
1057                 if (entrylayer_symbols.size()) {
1058                     for (size_t i = 0, e = entrylayer_symbols.size(); i < e; ++i)
1059                         shadingsys->execute_layer (*ctx, shaderglobals, entrylayer_symbols[i]);
1060                 } else {
1061                     for (size_t i = 0, e = entrylayer_index.size(); i < e; ++i)
1062                         shadingsys->execute_layer (*ctx, shaderglobals, entrylayer_index[i]);
1063                 }
1064                 shadingsys->execute_cleanup (*ctx);
1065             }
1066 
1067             // Save all the designated outputs.  But only do so if we
1068             // are on the last iteration requested, so that if we are
1069             // doing a bunch of iterations for time trials, we only
1070             // including the output pixel copying once in the timing.
1071             if (save)
1072                 save_outputs (rend, shadingsys, ctx, x, y);
1073         }
1074     }
1075 
1076     // We're done shading with this context.
1077     shadingsys->release_context (ctx);
1078     shadingsys->destroy_thread_info(thread_info);
1079 }
1080 
1081 
synchio()1082 static void synchio() {
1083     // Synch all writes to stdout & stderr now (mostly for Windows)
1084     std::cout.flush();
1085     std::cerr.flush();
1086     fflush(stdout);
1087     fflush(stderr);
1088 }
1089 
1090 extern "C" OSL_DLL_EXPORT int
test_shade(int argc,const char * argv[])1091 test_shade (int argc, const char *argv[])
1092 {
1093     OIIO::Timer timer;
1094 
1095     // Get the command line arguments.  Those that set up the shader
1096     // instances are queued up in shader_setup_args for later handling.
1097     getargs (argc, argv);
1098 
1099     // For testing purposes, allow user to set global locale
1100     if (localename.size()) {
1101         std::locale::global (std::locale(localename.c_str()));
1102         if (debug1 || verbose)
1103             printf("testshade: locale '%s', floats look like: %g\n",
1104                    localename.c_str(), 3.5);
1105     }
1106 
1107     SimpleRenderer *rend = nullptr;
1108 #ifdef OSL_USE_OPTIX
1109     if (use_optix)
1110         rend = new OptixGridRenderer;
1111     else
1112 #endif
1113         rend = new SimpleRenderer;
1114 
1115     // Other renderer and global options
1116     if (debug1 || verbose)
1117         rend->errhandler().verbosity (ErrorHandler::VERBOSE);
1118     rend->attribute("saveptx", (int)saveptx);
1119 
1120     // Hand the userdata options from the command line over to the renderer
1121 #if OIIO_VERSION >= 20202
1122     rend->userdata.merge(userdata);
1123 #else
1124     rend->userdata = userdata;
1125 #endif
1126 
1127     // Request a TextureSystem (by default it will be the global shared
1128     // one). This isn't strictly necessary, if you pass nullptr to
1129     // ShadingSystem ctr, it will ask for the shared one internally.
1130     TextureSystem *texturesys = TextureSystem::create();
1131 
1132     // Create a new shading system.  We pass it the RendererServices
1133     // object that services callbacks from the shading system, the
1134     // TextureSystem (note: passing nullptr just makes the ShadingSystem
1135     // make its own TS), and an error handler.
1136     shadingsys = new ShadingSystem (rend, texturesys, &errhandler);
1137     rend->init_shadingsys (shadingsys);
1138 
1139     // Register the layout of all closures known to this renderer
1140     // Any closure used by the shader which is not registered, or
1141     // registered with a different number of arguments will lead
1142     // to a runtime error.
1143     register_closures(shadingsys);
1144 
1145     // Remember that each shader parameter may optionally have a
1146     // metadata hint [[int lockgeom=...]], where 0 indicates that the
1147     // parameter may be overridden by the geometry itself, for example
1148     // with data interpolated from the mesh vertices, and a value of 1
1149     // means that it is "locked" with respect to the geometry (i.e. it
1150     // will not be overridden with interpolated or
1151     // per-geometric-primitive data).
1152     //
1153     // In order to most fully optimize the shader, we typically want any
1154     // shader parameter not explicitly specified to default to being
1155     // locked (i.e. no per-geometry override):
1156     shadingsys->attribute("lockgeom", 1);
1157 
1158     // Now we declare our shader.
1159     //
1160     // Each material in the scene is comprised of a "shader group."
1161     // Each group is comprised of one or more "layers" (a.k.a. shader
1162     // instances) with possible connections from outputs of
1163     // upstream/early layers into the inputs of downstream/later layers.
1164     // A shader instance is the combination of a reference to a shader
1165     // master and its parameter values that may override the defaults in
1166     // the shader source and may be particular to this instance (versus
1167     // all the other instances of the same shader).
1168     //
1169     // A shader group declaration typically looks like this:
1170     //
1171     //   ShaderGroupRef group = ss->ShaderGroupBegin ();
1172     //   ss->Parameter (*group, "paramname", TypeDesc paramtype, void *value);
1173     //      ... and so on for all the other parameters of...
1174     //   ss->Shader (*group, "shadertype", "shadername", "layername");
1175     //      The Shader() call creates a new instance, which gets
1176     //      all the pending Parameter() values made right before it.
1177     //   ... and other shader instances in this group, interspersed with...
1178     //   ss->ConnectShaders (*group, "layer1", "param1", "layer2", "param2");
1179     //   ... and other connections ...
1180     //   ss->ShaderGroupEnd (*group);
1181     //
1182     // It looks so simple, and it really is, except that the way this
1183     // testshade program works is that all the Parameter() and Shader()
1184     // calls are done inside getargs(), as it walks through the command
1185     // line arguments, whereas the connections accumulate and have
1186     // to be processed at the end.  Bear with us.
1187 
1188     // Start the shader group and grab a reference to it.
1189     shadergroup = shadingsys->ShaderGroupBegin (groupname);
1190 
1191     // Revisit the command line arguments that we stashed to set up the
1192     // shader itself.
1193     process_shader_setup_args ((int)shader_setup_args.size(),
1194                                shader_setup_args.data());
1195     if (params.size()) {
1196         std::cerr << "ERROR: Pending parameters without a shader:";
1197         for (auto&& pv : params)
1198             std::cerr << " " << pv.name();
1199         std::cerr << "\n";
1200         std::cerr << "Did you mistakenly put --param after the shader declaration?\n";
1201         return EXIT_FAILURE;
1202     }
1203 
1204     if (! shadergroup) {
1205         std::cerr << "ERROR: Invalid shader group. Exiting testshade.\n";
1206         return EXIT_FAILURE;
1207     }
1208 
1209     // Set shading sys options again, in case late-encountered command line
1210     // options change their values.
1211     set_shadingsys_options ();
1212 
1213     if (groupname.size())
1214         shadingsys->attribute (shadergroup.get(), "groupname", groupname);
1215 
1216     // Now set up the connections
1217     for (size_t i = 0;  i < connections.size();  i += 4) {
1218         if (i+3 < connections.size()) {
1219             std::cout << "Connect "
1220                       << connections[i] << "." << connections[i+1]
1221                       << " to " << connections[i+2] << "." << connections[i+3]
1222                       << "\n";
1223             synchio();
1224             bool ok = shadingsys->ConnectShaders (*shadergroup,
1225                                                   connections[i],
1226                                                   connections[i+1],
1227                                                   connections[i+2],
1228                                                   connections[i+3]);
1229             if (!ok) {
1230                 return EXIT_FAILURE;
1231             }
1232         }
1233     }
1234 
1235     // End the group
1236     shadingsys->ShaderGroupEnd (*shadergroup);
1237 
1238     if (verbose || do_oslquery) {
1239         std::string pickle;
1240         shadingsys->getattribute (shadergroup.get(), "pickle", pickle);
1241         std::cout << "Shader group:\n---\n" << pickle << "\n---\n";
1242         std::cout << "\n";
1243         ustring groupname;
1244         shadingsys->getattribute (shadergroup.get(), "groupname", groupname);
1245         std::cout << "Shader group \"" << groupname << "\" layers are:\n";
1246         int num_layers = 0;
1247         shadingsys->getattribute (shadergroup.get(), "num_layers", num_layers);
1248         if (num_layers > 0) {
1249             std::vector<const char *> layers (size_t(num_layers), NULL);
1250             shadingsys->getattribute (shadergroup.get(), "layer_names",
1251                                       TypeDesc(TypeDesc::STRING, num_layers),
1252                                       &layers[0]);
1253             for (int i = 0; i < num_layers; ++i) {
1254                 std::cout << "    " << (layers[i] ? layers[i] : "<unnamed>") << "\n";
1255                 if (do_oslquery) {
1256                     OSLQuery q;
1257                     q.init (shadergroup.get(), i);
1258                     for (size_t p = 0;  p < q.nparams(); ++p) {
1259                         const OSLQuery::Parameter *param = q.getparam(p);
1260                         std::cout << "\t" << (param->isoutput ? "output "  : "")
1261                                   << param->type << ' ' << param->name << "\n";
1262                     }
1263                 }
1264             }
1265         }
1266         std::cout << "\n";
1267     }
1268     if (archivegroup.size())
1269         shadingsys->archive_shadergroup (shadergroup.get(), archivegroup);
1270 
1271     if (outputfiles.size() != 0)
1272         std::cout << "\n";
1273 
1274     rend->shaders().push_back (shadergroup);
1275 
1276     // Set up the named transformations, including shader and object.
1277     // For this test application, we just do this statically; in a real
1278     // renderer, the global named space (like "myspace") would probably
1279     // be static, but shader and object spaces may be different for each
1280     // object.
1281     setup_transformations (*rend, Mshad, Mobj);
1282 
1283 #ifdef OSL_USE_OPTIX
1284 #if (OPTIX_VERSION >= 70000)
1285     if (use_optix)
1286         reinterpret_cast<OptixGridRenderer *> (rend)->synch_attributes();
1287 #endif
1288 #endif
1289 
1290     // Set up the image outputs requested on the command line
1291     setup_output_images (rend, shadingsys, shadergroup);
1292 
1293     if (debug1)
1294         test_group_attributes (shadergroup.get());
1295 
1296     if (num_threads < 1)
1297         num_threads = OIIO::Sysutil::hardware_concurrency();
1298 
1299     synchio();
1300 
1301     rend->prepare_render ();
1302 
1303     double setuptime = timer.lap ();
1304 
1305     if (warmup)
1306         rend->warmup();
1307     double warmuptime = timer.lap ();
1308 
1309     // Allow a settable number of iterations to "render" the whole image,
1310     // which is useful for time trials of things that would be too quick
1311     // to accurately time for a single iteration
1312     for (int iter = 0;  iter < iters;  ++iter) {
1313         OIIO::ROI roi (0, xres, 0, yres);
1314 
1315         if (use_optix) {
1316             rend->render (xres, yres);
1317         } else if (use_shade_image) {
1318             OSL::shade_image (*shadingsys, *shadergroup, NULL,
1319                               *rend->outputbuf(0), outputvarnames,
1320                               pixelcenters ? ShadePixelCenters : ShadePixelGrid,
1321                               roi, num_threads);
1322         } else {
1323             bool save = (iter == (iters-1));   // save on last iteration
1324 #if 0
1325             shade_region (rend, shadergroup.get(), roi, save);
1326 #else
1327             OIIO::ImageBufAlgo::parallel_image (roi, num_threads,
1328                                                 std::bind (shade_region, rend, shadergroup.get(), std::placeholders::_1, save));
1329 #endif
1330         }
1331 
1332         // If any reparam was requested, do it now
1333         if (reparams.size() && reparam_layer.size()) {
1334             for (size_t p = 0;  p < reparams.size();  ++p) {
1335                 const ParamValue &pv (reparams[p]);
1336                 shadingsys->ReParameter (*shadergroup, reparam_layer.c_str(),
1337                                          pv.name().c_str(), pv.type(),
1338                                          pv.data());
1339             }
1340         }
1341     }
1342     double runtime = timer.lap();
1343 
1344     if (outputfiles.size() == 0)
1345         std::cout << "\n";
1346 
1347     // Write the output images to disk
1348     rend->finalize_pixel_buffer ();
1349     for (size_t i = 0;  i < rend->noutputs();  ++i) {
1350         OIIO::ImageBuf* outputimg = rend->outputbuf(i);
1351         if (outputimg) {
1352             if (! print_outputs) {
1353                 std::string filename = outputimg->name();
1354                 TypeDesc datatype = outputimg->spec().format;
1355                 if (dataformatname == "uint8")
1356                     datatype = TypeDesc::UINT8;
1357                 else if (dataformatname == "half")
1358                     datatype = TypeDesc::HALF;
1359                 else if (dataformatname == "float")
1360                     datatype = TypeDesc::FLOAT;
1361 
1362                 // JPEG, GIF, and PNG images should be automatically saved
1363                 // as sRGB because they are almost certainly supposed to
1364                 // be displayed on web pages.
1365                 using namespace OIIO;
1366                 if (Strutil::iends_with (filename, ".jpg") ||
1367                     Strutil::iends_with (filename, ".jpeg") ||
1368                     Strutil::iends_with (filename, ".gif") ||
1369                     Strutil::iends_with (filename, ".png")) {
1370                     ImageBuf ccbuf;
1371                     ImageBufAlgo::colorconvert (ccbuf, *outputimg,
1372                                                 "linear", "sRGB", false,
1373                                                 "", "");
1374                     ccbuf.set_write_format (datatype);
1375                     ccbuf.write (filename);
1376                 } else {
1377                     outputimg->set_write_format (datatype);
1378                     outputimg->write (filename);
1379                 }
1380             }
1381             // outputimg->reset();
1382         }
1383     }
1384 
1385     // Print some debugging info
1386     if (debug1 || runstats || profile) {
1387         double writetime = timer.lap();
1388         std::cout << "\n";
1389         std::cout << "Setup : " << OIIO::Strutil::timeintervalformat (setuptime,4) << "\n";
1390         std::cout << "Warmup: " << OIIO::Strutil::timeintervalformat (warmuptime,4) << "\n";
1391         std::cout << "Run   : " << OIIO::Strutil::timeintervalformat (runtime,4) << "\n";
1392         std::cout << "Write : " << OIIO::Strutil::timeintervalformat (writetime,4) << "\n";
1393         std::cout << "\n";
1394         std::cout << shadingsys->getstats (5) << "\n";
1395         OIIO::TextureSystem *texturesys = shadingsys->texturesys();
1396         if (texturesys)
1397             std::cout << texturesys->getstats (5) << "\n";
1398         std::cout << ustring::getstats() << "\n";
1399     }
1400 
1401     // Give the renderer a chance to do initial cleanup while everything is still alive
1402     rend->clear();
1403 
1404     // We're done with the shading system now, destroy it
1405     shadergroup.reset ();  // Must release this before destroying shadingsys
1406 
1407     delete shadingsys;
1408     int retcode = EXIT_SUCCESS;
1409 
1410     // Double check that there were no uncaught errors in the texture
1411     // system and image cache.
1412     std::string err = texturesys->geterror();
1413     if (!err.empty()) {
1414         std::cout << "ERRORS left in TextureSystem:\n" << err << "\n";
1415         retcode = EXIT_FAILURE;
1416     }
1417     OIIO::ImageCache *ic = texturesys->imagecache();
1418     err = ic ? ic->geterror() : std::string();
1419     if (!err.empty()) {
1420         std::cout << "ERRORS left in ImageCache:\n" << err << "\n";
1421         retcode = EXIT_FAILURE;
1422     }
1423 
1424     delete rend;
1425 
1426     return retcode;
1427 }
1428