static void setup_output_images (ShadingSystem *shadingsys, ShaderGroupRef &shadergroup) { // Tell the shading system which outputs we want if (outputvars.size()) { std::vector<const char *> aovnames (outputvars.size()); for (size_t i = 0; i < outputvars.size(); ++i) aovnames[i] = outputvars[i].c_str(); shadingsys->attribute ("renderer_outputs", TypeDesc(TypeDesc::STRING,(int)aovnames.size()), &aovnames[0]); } if (extraoptions.size()) shadingsys->attribute ("options", extraoptions); ShadingContext *ctx = shadingsys->get_context (); // Because we can only call get_symbol on something that has been // set up to shade (or executed), we call execute() but tell it not // to actually run the shader. ShaderGlobals sg; setup_shaderglobals (sg, shadingsys, 0, 0); shadingsys->execute (*ctx, *shadergroup, sg, false); // For each output file specified on the command line... for (size_t i = 0; i < outputfiles.size(); ++i) { // Make a ustring version of the output name, for fast manipulation outputvarnames.push_back (ustring(outputvars[i])); // Start with a NULL ImageBuf pointer outputimgs.push_back (NULL); // Ask for a pointer to the symbol's data, as computed by this // shader. TypeDesc t; const void *data = shadingsys->get_symbol (*ctx, outputvarnames[i], t); if (!data) { std::cout << "Output " << outputvars[i] << " not found, skipping.\n"; continue; // Skip if symbol isn't found } std::cout << "Output " << outputvars[i] << " to " << outputfiles[i] << "\n"; // And the "base" type, i.e. the type of each element or channel TypeDesc tbase = TypeDesc ((TypeDesc::BASETYPE)t.basetype); // But which type are we going to write? Use the true data type // from OSL, unless the command line options indicated that // something else was desired. TypeDesc outtypebase = tbase; if (dataformatname == "uint8") outtypebase = TypeDesc::UINT8; else if (dataformatname == "half") outtypebase = TypeDesc::HALF; else if (dataformatname == "float") outtypebase = TypeDesc::FLOAT; // Number of channels to write to the image is the number of (array) // elements times the number of channels (e.g. 1 for scalar, 3 for // vector, etc.) int nchans = t.numelements() * t.aggregate; // Make an ImageBuf of the right type and size to hold this // symbol's output, and initially clear it to all black pixels. OIIO::ImageSpec spec (xres, yres, nchans, outtypebase); outputimgs[i] = new OIIO::ImageBuf(outputfiles[i], spec); OIIO::ImageBufAlgo::zero (*outputimgs[i]); } shadingsys->release_context (ctx); // don't need this anymore for now }
int StringTable::getOffset (const std::string& str) const { auto it = m_offset_map.find (ustring(str)); return (it != m_offset_map.end()) ? it->second : -1; }
static void action_param (int argc, const char *argv[]) { std::string command = argv[0]; bool use_reparam = false; if (OIIO::Strutil::istarts_with(command, "--reparam") || OIIO::Strutil::istarts_with(command, "-reparam")) use_reparam = true; ParamValueList ¶ms (use_reparam ? reparams : (::params)); std::string paramname = argv[1]; std::string stringval = argv[2]; TypeDesc type = TypeDesc::UNKNOWN; bool unlockgeom = false; float f[16]; size_t pos; while ((pos = command.find_first_of(":")) != std::string::npos) { command = command.substr (pos+1, std::string::npos); std::vector<std::string> splits; OIIO::Strutil::split (command, splits, ":", 1); if (splits.size() < 1) {} else if (OIIO::Strutil::istarts_with(splits[0],"type=")) type.fromstring (splits[0].c_str()+5); else if (OIIO::Strutil::istarts_with(splits[0],"lockgeom=")) unlockgeom = (strtol (splits[0].c_str()+9, NULL, 10) == 0); } // If it is or might be a matrix, look for 16 comma-separated floats if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeMatrix) && sscanf (stringval.c_str(), "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9], &f[10], &f[11], &f[12], &f[13], &f[14], &f[15]) == 16) { params.push_back (ParamValue()); params.back().init (paramname, TypeDesc::TypeMatrix, 1, f); if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // If it is or might be a vector type, look for 3 comma-separated floats if ((type == TypeDesc::UNKNOWN || equivalent(type,TypeDesc::TypeVector)) && sscanf (stringval.c_str(), "%g, %g, %g", &f[0], &f[1], &f[2]) == 3) { if (type == TypeDesc::UNKNOWN) type = TypeDesc::TypeVector; params.push_back (ParamValue()); params.back().init (paramname, type, 1, f); if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // If it is or might be an int, look for an int that takes up the whole // string. if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeInt)) { char *endptr = NULL; int ival = strtol(stringval.c_str(),&endptr,10); if (endptr && *endptr == 0) { params.push_back (ParamValue()); params.back().init (paramname, TypeDesc::TypeInt, 1, &ival); if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } } // If it is or might be an float, look for a float that takes up the // whole string. if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeFloat)) { char *endptr = NULL; float fval = (float) strtod(stringval.c_str(),&endptr); if (endptr && *endptr == 0) { params.push_back (ParamValue()); params.back().init (paramname, TypeDesc::TypeFloat, 1, &fval); if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } } // All remaining cases -- it's a string const char *s = stringval.c_str(); params.push_back (ParamValue()); params.back().init (paramname, TypeDesc::TypeString, 1, &s); if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); }
static void setup_output_images (ShadingSystem *shadingsys, ShaderGroupRef &shadergroup) { // Tell the shading system which outputs we want if (outputvars.size()) { std::vector<const char *> aovnames (outputvars.size()); for (size_t i = 0; i < outputvars.size(); ++i) { ustring varname (outputvars[i]); aovnames[i] = varname.c_str(); size_t dot = varname.find('.'); if (dot != ustring::npos) { // If the name contains a dot, it's intended to be layer.symbol varname = ustring (varname, dot+1); } } shadingsys->attribute (use_group_outputs ? shadergroup.get() : NULL, "renderer_outputs", TypeDesc(TypeDesc::STRING,(int)aovnames.size()), &aovnames[0]); if (use_group_outputs) std::cout << "Marking group outputs, not global renderer outputs.\n"; } if (entrylayers.size()) { std::vector<const char *> layers; std::cout << "Entry layers:"; for (size_t i = 0; i < entrylayers.size(); ++i) { ustring layername (entrylayers[i]); // convert to ustring int layid = shadingsys->find_layer (*shadergroup, layername); layers.push_back (layername.c_str()); entrylayer_index.push_back (layid); std::cout << ' ' << entrylayers[i] << "(" << layid << ")"; } std::cout << "\n"; shadingsys->attribute (shadergroup.get(), "entry_layers", TypeDesc(TypeDesc::STRING,(int)entrylayers.size()), &layers[0]); } if (extraoptions.size()) shadingsys->attribute ("options", extraoptions); ShadingContext *ctx = shadingsys->get_context (); // Because we can only call find_symbol or get_symbol on something that // has been set up to shade (or executed), we call execute() but tell it // not to actually run the shader. ShaderGlobals sg; setup_shaderglobals (sg, shadingsys, 0, 0); if (raytype_opt) shadingsys->optimize_group (shadergroup.get(), raytype_bit, ~raytype_bit); shadingsys->execute (ctx, *shadergroup, sg, false); if (entryoutputs.size()) { std::cout << "Entry outputs:"; for (size_t i = 0; i < entryoutputs.size(); ++i) { ustring name (entryoutputs[i]); // convert to ustring const ShaderSymbol *sym = shadingsys->find_symbol (*shadergroup, name); if (!sym) { std::cout << "\nEntry output " << entryoutputs[i] << " not found. Abording.\n"; exit (EXIT_FAILURE); } entrylayer_symbols.push_back (sym); std::cout << ' ' << entryoutputs[i]; } std::cout << "\n"; } // For each output file specified on the command line... for (size_t i = 0; i < outputfiles.size(); ++i) { // Make a ustring version of the output name, for fast manipulation outputvarnames.push_back (ustring(outputvars[i])); // Start with a NULL ImageBuf pointer outputimgs.push_back (NULL); // Ask for a pointer to the symbol's data, as computed by this // shader. TypeDesc t; const void *data = shadingsys->get_symbol (*ctx, outputvarnames[i], t); if (!data) { std::cout << "Output " << outputvars[i] << " not found, skipping.\n"; continue; // Skip if symbol isn't found } std::cout << "Output " << outputvars[i] << " to " << outputfiles[i] << "\n"; // And the "base" type, i.e. the type of each element or channel TypeDesc tbase = TypeDesc ((TypeDesc::BASETYPE)t.basetype); // But which type are we going to write? Use the true data type // from OSL, unless the command line options indicated that // something else was desired. TypeDesc outtypebase = tbase; if (dataformatname == "uint8") outtypebase = TypeDesc::UINT8; else if (dataformatname == "half") outtypebase = TypeDesc::HALF; else if (dataformatname == "float") outtypebase = TypeDesc::FLOAT; // Number of channels to write to the image is the number of (array) // elements times the number of channels (e.g. 1 for scalar, 3 for // vector, etc.) int nchans = t.numelements() * t.aggregate; // Make an ImageBuf of the right type and size to hold this // symbol's output, and initially clear it to all black pixels. OIIO::ImageSpec spec (xres, yres, nchans, TypeDesc::FLOAT); outputimgs[i] = new OIIO::ImageBuf(outputfiles[i], spec); outputimgs[i]->set_write_format (outtypebase); OIIO::ImageBufAlgo::zero (*outputimgs[i]); } if (outputimgs.empty()) { OIIO::ImageSpec spec (xres, yres, 3, TypeDesc::FLOAT); outputimgs.push_back(new OIIO::ImageBuf(spec)); } shadingsys->release_context (ctx); // don't need this anymore for now }
extern "C" OSL_DLL_EXPORT int test_shade (int argc, const char *argv[]) { OIIO::Timer timer; // Create a new shading system. We pass it the RendererServices // object that services callbacks from the shading system, NULL for // the TextureSystem (that just makes 'create' make its own TS), and // an error handler. shadingsys = new ShadingSystem (&rend, NULL, &errhandler); // Register the layout of all closures known to this renderer // Any closure used by the shader which is not registered, or // registered with a different number of arguments will lead // to a runtime error. register_closures(shadingsys); // Remember that each shader parameter may optionally have a // metadata hint [[int lockgeom=...]], where 0 indicates that the // parameter may be overridden by the geometry itself, for example // with data interpolated from the mesh vertices, and a value of 1 // means that it is "locked" with respect to the geometry (i.e. it // will not be overridden with interpolated or // per-geometric-primitive data). // // In order to most fully optimize shader, we typically want any // shader parameter not explicitly specified to default to being // locked (i.e. no per-geometry override): shadingsys->attribute("lockgeom", 1); // Now we declare our shader. // // Each material in the scene is comprised of a "shader group." // Each group is comprised of one or more "layers" (a.k.a. shader // instances) with possible connections from outputs of // upstream/early layers into the inputs of downstream/later layers. // A shader instance is the combination of a reference to a shader // master and its parameter values that may override the defaults in // the shader source and may be particular to this instance (versus // all the other instances of the same shader). // // A shader group declaration typically looks like this: // // ShaderGroupRef shadergroup = ss->ShaderGroupBegin (); // ss->Parameter ("paramname", TypeDesc paramtype, void *value); // ... and so on for all the other parameters of... // ss->Shader ("shadertype", "shadername", "layername"); // The Shader() call creates a new instance, which gets // all the pending Parameter() values made right before it. // ... and other shader instances in this group, interspersed with... // ss->ConnectShaders ("layer1", "param1", "layer2", "param2"); // ... and other connections ... // ss->ShaderGroupEnd (); // // It looks so simple, and it really is, except that the way this // testshade program works is that all the Parameter() and Shader() // calls are done inside getargs(), as it walks through the command // line arguments, whereas the connections accumulate and have // to be processed at the end. Bear with us. // Start the shader group and grab a reference to it. shadergroup = shadingsys->ShaderGroupBegin (groupname); // Get the command line arguments. That will set up all the shader // instances and their parameters for the group. getargs (argc, argv); if (! shadergroup) { std::cerr << "ERROR: Invalid shader group. Exiting testshade.\n"; return EXIT_FAILURE; } shadingsys->attribute (shadergroup.get(), "groupname", groupname); // Now set up the connections for (size_t i = 0; i < connections.size(); i += 4) { if (i+3 < connections.size()) { std::cout << "Connect " << connections[i] << "." << connections[i+1] << " to " << connections[i+2] << "." << connections[i+3] << "\n"; shadingsys->ConnectShaders (connections[i].c_str(), connections[i+1].c_str(), connections[i+2].c_str(), connections[i+3].c_str()); } } // End the group shadingsys->ShaderGroupEnd (); if (verbose || do_oslquery) { std::string pickle; shadingsys->getattribute (shadergroup.get(), "pickle", pickle); std::cout << "Shader group:\n---\n" << pickle << "\n---\n"; std::cout << "\n"; ustring groupname; shadingsys->getattribute (shadergroup.get(), "groupname", groupname); std::cout << "Shader group \"" << groupname << "\" layers are:\n"; int num_layers = 0; shadingsys->getattribute (shadergroup.get(), "num_layers", num_layers); if (num_layers > 0) { std::vector<const char *> layers (size_t(num_layers), NULL); shadingsys->getattribute (shadergroup.get(), "layer_names", TypeDesc(TypeDesc::STRING, num_layers), &layers[0]); for (int i = 0; i < num_layers; ++i) { std::cout << " " << (layers[i] ? layers[i] : "<unnamed>") << "\n"; if (do_oslquery) { OSLQuery q; q.init (shadergroup.get(), i); for (size_t p = 0; p < q.nparams(); ++p) { const OSLQuery::Parameter *param = q.getparam(p); std::cout << "\t" << (param->isoutput ? "output " : "") << param->type << ' ' << param->name << "\n"; } } } } std::cout << "\n"; } if (archivegroup.size()) shadingsys->archive_shadergroup (shadergroup.get(), archivegroup); if (outputfiles.size() != 0) std::cout << "\n"; // Set up the named transformations, including shader and object. // For this test application, we just do this statically; in a real // renderer, the global named space (like "myspace") would probably // be static, but shader and object spaces may be different for each // object. setup_transformations (rend, Mshad, Mobj); // Set up the image outputs requested on the command line setup_output_images (shadingsys, shadergroup); if (debug) test_group_attributes (shadergroup.get()); if (num_threads < 1) num_threads = boost::thread::hardware_concurrency(); double setuptime = timer.lap (); // Allow a settable number of iterations to "render" the whole image, // which is useful for time trials of things that would be too quick // to accurately time for a single iteration for (int iter = 0; iter < iters; ++iter) { OIIO::ROI roi (0, xres, 0, yres); if (use_shade_image) OSL::shade_image (*shadingsys, *shadergroup, NULL, *outputimgs[0], outputvarnames, pixelcenters ? ShadePixelCenters : ShadePixelGrid, roi, num_threads); else { bool save = (iter == (iters-1)); // save on last iteration #if 0 shade_region (shadergroup.get(), roi, save); #else OIIO::ImageBufAlgo::parallel_image ( boost::bind (shade_region, shadergroup.get(), _1, save), roi, num_threads); #endif } // If any reparam was requested, do it now if (reparams.size() && reparam_layer.size()) { for (size_t p = 0; p < reparams.size(); ++p) { const ParamValue &pv (reparams[p]); shadingsys->ReParameter (*shadergroup, reparam_layer.c_str(), pv.name().c_str(), pv.type(), pv.data()); } } } double runtime = timer.lap(); if (outputfiles.size() == 0) std::cout << "\n"; // Write the output images to disk for (size_t i = 0; i < outputimgs.size(); ++i) { if (outputimgs[i]) { if (! print_outputs) { std::string filename = outputimgs[i]->name(); // JPEG, GIF, and PNG images should be automatically saved // as sRGB because they are almost certainly supposed to // be displayed on web pages. using namespace OIIO; if (Strutil::iends_with (filename, ".jpg") || Strutil::iends_with (filename, ".jpeg") || Strutil::iends_with (filename, ".gif") || Strutil::iends_with (filename, ".png")) { ImageBuf ccbuf; ImageBufAlgo::colorconvert (ccbuf, *outputimgs[i], "linear", "sRGB", false, "", ""); ccbuf.set_write_format (outputimgs[i]->spec().format); ccbuf.write (filename); } else { outputimgs[i]->write (filename); } } delete outputimgs[i]; outputimgs[i] = NULL; } } // Print some debugging info if (debug || runstats || profile) { double writetime = timer.lap(); std::cout << "\n"; std::cout << "Setup: " << OIIO::Strutil::timeintervalformat (setuptime,2) << "\n"; std::cout << "Run : " << OIIO::Strutil::timeintervalformat (runtime,2) << "\n"; std::cout << "Write: " << OIIO::Strutil::timeintervalformat (writetime,2) << "\n"; std::cout << "\n"; std::cout << shadingsys->getstats (5) << "\n"; OIIO::TextureSystem *texturesys = shadingsys->texturesys(); if (texturesys) std::cout << texturesys->getstats (5) << "\n"; std::cout << ustring::getstats() << "\n"; } // We're done with the shading system now, destroy it shadergroup.reset (); // Must release this before destroying shadingsys delete shadingsys; return EXIT_SUCCESS; }
OSL_SHADEOP void osl_trunc_vv (void *r, void *x_) { const Vec3 &x (VEC(x_)); VEC(r).setValue (truncf(x[0]), truncf(x[1]), truncf(x[2])); }
OSL_SHADEOP float osl_trunc_ff (float x) { return truncf(x); }
OSL_SHADEOP void osl_round_vv (void *r, void *x_) { const Vec3 &x (VEC(x_)); VEC(r).setValue (roundf(x[0]), roundf(x[1]), roundf(x[2])); }
OSL_SHADEOP float osl_round_ff (float x) { return roundf(x); }
static void action_param (int argc, const char *argv[]) { std::string command = argv[0]; bool use_reparam = false; if (OIIO::Strutil::istarts_with(command, "--reparam") || OIIO::Strutil::istarts_with(command, "-reparam")) use_reparam = true; ParamValueList ¶ms (use_reparam ? reparams : (::params)); string_view paramname (argv[1]); string_view stringval (argv[2]); TypeDesc type = TypeDesc::UNKNOWN; bool unlockgeom = false; float f[16]; size_t pos; while ((pos = command.find_first_of(":")) != std::string::npos) { command = command.substr (pos+1, std::string::npos); std::vector<std::string> splits; OIIO::Strutil::split (command, splits, ":", 1); if (splits.size() < 1) {} else if (OIIO::Strutil::istarts_with(splits[0],"type=")) type.fromstring (splits[0].c_str()+5); else if (OIIO::Strutil::istarts_with(splits[0],"lockgeom=")) unlockgeom = (OIIO::Strutil::from_string<int> (splits[0]) == 0); } // If it is or might be a matrix, look for 16 comma-separated floats if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeMatrix) && sscanf (stringval.c_str(), "%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f", &f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9], &f[10], &f[11], &f[12], &f[13], &f[14], &f[15]) == 16) { #if OIIO_VERSION >= 10804 params.emplace_back (paramname, TypeDesc::TypeMatrix, 1, f); #else params.push_back (ParamValue()); params.back().init (paramname, TypeDesc::TypeMatrix, 1, f); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // If it is or might be a vector type, look for 3 comma-separated floats if ((type == TypeDesc::UNKNOWN || equivalent(type,TypeDesc::TypeVector)) && sscanf (stringval.c_str(), "%g, %g, %g", &f[0], &f[1], &f[2]) == 3) { if (type == TypeDesc::UNKNOWN) type = TypeDesc::TypeVector; #if OIIO_VERSION >= 10804 params.emplace_back (paramname, type, 1, f); #else params.push_back (ParamValue()); params.back().init (paramname, type, 1, f); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // If it is or might be an int, look for an int that takes up the whole // string. if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeInt) && OIIO::Strutil::string_is<int>(stringval)) { #if OIIO_VERSION >= 10804 params.emplace_back (paramname, OIIO::Strutil::from_string<int>(stringval)); #else params.push_back (ParamValue()); int i = OIIO::Strutil::from_string<int>(stringval); params.back().init (paramname, TypeDesc::TypeInt, 1, &i); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // If it is or might be an float, look for a float that takes up the // whole string. if ((type == TypeDesc::UNKNOWN || type == TypeDesc::TypeFloat) && OIIO::Strutil::string_is<float>(stringval)) { #if OIIO_VERSION >= 10804 params.emplace_back (paramname, OIIO::Strutil::from_string<float>(stringval)); #else params.push_back (ParamValue()); float f = OIIO::Strutil::from_string<float>(stringval); params.back().init (paramname, TypeDesc::TypeFloat, 1, &f); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // Catch-all for float types and arrays if (type.basetype == TypeDesc::FLOAT) { int n = type.aggregate * type.numelements(); std::vector<float> vals (n); for (int i = 0; i < n; ++i) { OIIO::Strutil::parse_float (stringval, vals[i]); OIIO::Strutil::parse_char (stringval, ','); } #if OIIO_VERSION >= 10804 params.emplace_back (paramname, type, 1, &vals[0]); #else params.push_back (ParamValue()); params.back().init (paramname, type, 1, &vals[0]); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // Catch-all for int types and arrays if (type.basetype == TypeDesc::INT) { int n = type.aggregate * type.numelements(); std::vector<int> vals (n); for (int i = 0; i < n; ++i) { OIIO::Strutil::parse_int (stringval, vals[i]); OIIO::Strutil::parse_char (stringval, ','); } #if OIIO_VERSION >= 10804 params.emplace_back (paramname, type, 1, &vals[0]); #else params.push_back (ParamValue()); params.back().init (paramname, type, 1, &vals[0]); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // String arrays are slightly tricky if (type.basetype == TypeDesc::STRING && type.is_array()) { std::vector<string_view> splitelements; OIIO::Strutil::split (stringval, splitelements, ",", type.arraylen); splitelements.resize (type.arraylen); std::vector<ustring> strelements; for (auto&& s : splitelements) strelements.push_back (ustring(s)); #if OIIO_VERSION >= 10804 params.emplace_back (paramname, type, 1, &strelements[0]); #else params.push_back (ParamValue()); params.back().init (paramname, type, 1, &strelements[0]); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); return; } // All remaining cases -- it's a string const char *s = stringval.c_str(); #if OIIO_VERSION >= 10804 params.emplace_back (paramname, TypeDesc::TypeString, 1, &s); #else params.push_back (ParamValue()); params.back().init (paramname, TypeDesc::TypeString, 1, &s); #endif if (unlockgeom) params.back().interp (ParamValue::INTERP_VERTEX); }