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; }
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 }
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 }