bool oiio_attribute_tuple_typed (const std::string &name, TypeDesc type, tuple &obj) { if (type.basetype == TypeDesc::INT) { std::vector<int> vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) return OIIO::attribute (name, type, &vals[0]); return false; } if (type.basetype == TypeDesc::FLOAT) { std::vector<float> vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) return OIIO::attribute (name, type, &vals[0]); return false; } if (type.basetype == TypeDesc::STRING) { std::vector<std::string> vals; py_to_stdvector (vals, obj); if (vals.size() == type.numelements()*type.aggregate) { std::vector<ustring> u; for (size_t i = 0, e = vals.size(); i < e; ++i) u.push_back (ustring(vals[i])); return OIIO::attribute (name, type, &u[0]); } return false; } return false; }
void OSOReaderToMaster::symbol (SymType symtype, TypeSpec typespec, const char *name_) { ustring name(name_); Symbol sym (name, typespec, symtype); TypeDesc t = typespec.simpletype(); int nvals = t.aggregate * (t.is_unsized_array() ? 1 : t.numelements()); if (sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam) { // Skip structs for now, they're just placeholders if (typespec.is_structure()) { } else if (typespec.simpletype().basetype == TypeDesc::FLOAT) { sym.dataoffset ((int) m_master->m_fdefaults.size()); expand (m_master->m_fdefaults, nvals); } else if (typespec.simpletype().basetype == TypeDesc::INT) { sym.dataoffset ((int) m_master->m_idefaults.size()); expand (m_master->m_idefaults, nvals); } else if (typespec.simpletype().basetype == TypeDesc::STRING) { sym.dataoffset ((int) m_master->m_sdefaults.size()); expand (m_master->m_sdefaults, nvals); } else if (typespec.is_closure_based()) { // Closures are pointers, so we allocate a string default taking // adventage of their default being NULL as well. sym.dataoffset ((int) m_master->m_sdefaults.size()); expand (m_master->m_sdefaults, nvals); } else { ASSERT (0 && "unexpected type"); } } if (sym.symtype() == SymTypeConst) { if (typespec.simpletype().basetype == TypeDesc::FLOAT) { sym.dataoffset ((int) m_master->m_fconsts.size()); expand (m_master->m_fconsts, nvals); } else if (typespec.simpletype().basetype == TypeDesc::INT) { sym.dataoffset ((int) m_master->m_iconsts.size()); expand (m_master->m_iconsts, nvals); } else if (typespec.simpletype().basetype == TypeDesc::STRING) { sym.dataoffset ((int) m_master->m_sconsts.size()); expand (m_master->m_sconsts, nvals); } else { ASSERT (0 && "unexpected type"); } } #if 0 // FIXME -- global_heap_offset is quite broken. But also not necessary. // We made need to fix this later. if (sym.symtype() == SymTypeGlobal) { sym.dataoffset (m_shadingsys.global_heap_offset (sym.name())); } #endif sym.lockgeom (m_shadingsys.lockgeom_default()); m_master->m_symbols.push_back (sym); m_symmap[name] = int(m_master->m_symbols.size()) - 1; // Start the index at which we add specified defaults m_sym_default_index = 0; }
std::ostream & Symbol::print_vals (std::ostream &out, int maxvals) const { if (! data()) return out; TypeDesc t = typespec().simpletype(); int n = std::min (int(t.aggregate * t.numelements()), maxvals); if (t.basetype == TypeDesc::FLOAT) { for (int j = 0; j < n; ++j) out << (j ? " " : "") << ((float *)data())[j]; } else if (t.basetype == TypeDesc::INT) { for (int j = 0; j < n; ++j) out << (j ? " " : "") << ((int *)data())[j]; } else if (t.basetype == TypeDesc::STRING) { for (int j = 0; j < n; ++j) out << (j ? " " : "") << "\"" << Strutil::escape_chars(((ustring *)data())[j].string()) << "\""; } if (int(t.aggregate * t.numelements()) > maxvals) out << "..."; return out; }
void BackendLLVM::llvm_generate_debug_uninit (const Opcode &op) { for (int i = 0; i < op.nargs(); ++i) { Symbol &sym (*opargsym (op, i)); if (! op.argread(i)) continue; if (sym.typespec().is_closure_based()) continue; TypeDesc t = sym.typespec().simpletype(); if (t.basetype != TypeDesc::FLOAT && t.basetype != TypeDesc::INT && t.basetype != TypeDesc::STRING) continue; // just check float, int, string based types llvm::Value *ncheck = ll.constant (int(t.numelements() * t.aggregate)); llvm::Value *offset = ll.constant(0); // Some special cases... if (op.opname() == Strings::op_for && i == 0) { // The first argument of 'for' is the condition temp, but // note that it may not have had its initializer run yet, so // don't generate uninit test code for it. continue; } if (op.opname() == op_aref && i == 1) { // Special case -- array assignment -- only check one element llvm::Value *ind = llvm_load_value (*opargsym (op, 2)); llvm::Value *agg = ll.constant(t.aggregate); offset = t.aggregate == 1 ? ind : ll.op_mul (ind, agg); ncheck = agg; } else if (op.opname() == op_compref && i == 1) { // Special case -- component assignment -- only check one channel llvm::Value *ind = llvm_load_value (*opargsym (op, 2)); offset = ind; ncheck = ll.constant(1); } llvm::Value *args[] = { ll.constant(t), llvm_void_ptr(sym), sg_void_ptr(), ll.constant(op.sourcefile()), ll.constant(op.sourceline()), ll.constant(sym.name()), offset, ncheck }; ll.call_function ("osl_uninit_check", args, 8); } }
inline std::string print_vals (const Symbol &s) { std::stringstream out; TypeDesc t = s.typespec().simpletype(); int n = t.aggregate * t.numelements(); if (t.basetype == TypeDesc::FLOAT) { for (int j = 0; j < n; ++j) out << (j ? " " : "") << ((float *)s.data())[j]; } else if (t.basetype == TypeDesc::INT) { for (int j = 0; j < n; ++j) out << (j ? " " : "") << ((int *)s.data())[j]; } else if (t.basetype == TypeDesc::STRING) { for (int j = 0; j < n; ++j) out << (j ? " " : "") << "\"" << ((ustring *)s.data())[j] << "\""; } return out.str(); }
void BackendLLVM::llvm_generate_debugnan (const Opcode &op) { for (int i = 0; i < op.nargs(); ++i) { Symbol &sym (*opargsym (op, i)); if (! op.argwrite(i)) continue; TypeDesc t = sym.typespec().simpletype(); if (t.basetype != TypeDesc::FLOAT) continue; // just check float-based types llvm::Value *ncomps = ll.constant (int(t.numelements() * t.aggregate)); llvm::Value *offset = ll.constant(0); llvm::Value *ncheck = ncomps; if (op.opname() == op_aassign) { // Special case -- array assignment -- only check one element ASSERT (i == 0 && "only arg 0 is written for aassign"); llvm::Value *ind = llvm_load_value (*opargsym (op, 1)); llvm::Value *agg = ll.constant(t.aggregate); offset = t.aggregate == 1 ? ind : ll.op_mul (ind, agg); ncheck = agg; } else if (op.opname() == op_compassign) { // Special case -- component assignment -- only check one channel ASSERT (i == 0 && "only arg 0 is written for compassign"); llvm::Value *ind = llvm_load_value (*opargsym (op, 1)); offset = ind; ncheck = ll.constant(1); } llvm::Value *args[] = { ncomps, llvm_void_ptr(sym), ll.constant((int)sym.has_derivs()), sg_void_ptr(), ll.constant(op.sourcefile()), ll.constant(op.sourceline()), ll.constant(sym.name()), offset, ncheck, ll.constant(op.opname()) }; ll.call_function ("osl_naninf_check", args, 10); } }
static void parse_elements (string_view name, TypeDesc type, const char *type_code, string_view value, ImageIOParameter ¶m) { int num_items = type.numelements() * type.aggregate; T *data = (T*) param.data(); // Erase any leading whitespace value.remove_prefix (value.find_first_not_of (" \t")); for (int i = 0; i < num_items; ++i) { // Make a temporary copy so we for sure have a 0-terminated string. std::string temp = value; // Grab the first value from it sscanf (temp.c_str(), type_code, &data[i]); // Skip the value (eat until we find a delimiter -- space, comma, tab) value.remove_prefix (value.find_first_of (" ,\t")); // Skip the delimiter value.remove_prefix (value.find_first_not_of (" ,\t")); if (value.empty()) break; // done if nothing left to parse } }
void RuntimeOptimizer::llvm_generate_debugnan (const Opcode &op) { for (int i = 0; i < op.nargs(); ++i) { Symbol &sym (*opargsym (op, i)); if (! op.argwrite(i)) continue; TypeDesc t = sym.typespec().simpletype(); if (t.basetype != TypeDesc::FLOAT) continue; // just check float-based types int ncomps = t.numelements() * t.aggregate; llvm::Value *args[] = { llvm_constant(ncomps), llvm_void_ptr(sym), llvm_constant((int)sym.has_derivs()), sg_void_ptr(), llvm_constant(op.sourcefile()), llvm_constant(op.sourceline()), llvm_constant(sym.name()) }; llvm_call_function ("osl_naninf_check", args, 7); } }
llvm::Function* BackendLLVM::build_llvm_instance (bool groupentry) { // Make a layer function: void layer_func(ShaderGlobals*, GroupData*) // Note that the GroupData* is passed as a void*. std::string unique_layer_name = Strutil::format ("%s_%d", inst()->layername(), inst()->id()); ll.current_function ( ll.make_function (unique_layer_name, !groupentry, // fastcall for non-entry layer functions ll.type_void(), // return type llvm_type_sg_ptr(), llvm_type_groupdata_ptr())); // Get shader globals and groupdata pointers m_llvm_shaderglobals_ptr = ll.current_function_arg(0); //arg_it++; m_llvm_groupdata_ptr = ll.current_function_arg(1); //arg_it++; llvm::BasicBlock *entry_bb = ll.new_basic_block (unique_layer_name); m_exit_instance_block = NULL; // Set up a new IR builder ll.new_builder (entry_bb); #if 0 /* helpful for debuggin */ if (llvm_debug() && groupentry) llvm_gen_debug_printf (Strutil::format("\n\n\n\nGROUP! %s",group().name())); if (llvm_debug()) llvm_gen_debug_printf (Strutil::format("enter layer %s %s", inst()->layername(), inst()->shadername())); #endif if (shadingsys().countlayerexecs()) ll.call_function ("osl_incr_layers_executed", sg_void_ptr()); if (groupentry) { if (m_num_used_layers > 1) { // If this is the group entry point, clear all the "layer // executed" bits. If it's not the group entry (but rather is // an upstream node), then set its bit! int sz = (m_num_used_layers + 3) & (~3); // round up to 32 bits ll.op_memset (ll.void_ptr(layer_run_ptr(0)), 0, sz, 4 /*align*/); } // Group entries also need to allot space for ALL layers' params // that are closures (to avoid weird order of layer eval problems). for (int i = 0; i < group().nlayers(); ++i) { ShaderInstance *gi = group()[i]; if (gi->unused()) continue; FOREACH_PARAM (Symbol &sym, gi) { if (sym.typespec().is_closure_based()) { int arraylen = std::max (1, sym.typespec().arraylength()); llvm::Value *val = ll.constant_ptr(NULL, ll.type_void_ptr()); for (int a = 0; a < arraylen; ++a) { llvm::Value *arrind = sym.typespec().is_array() ? ll.constant(a) : NULL; llvm_store_value (val, sym, 0, arrind, 0); } } } // Unconditionally execute earlier layers that are not lazy if (! gi->run_lazily() && i < group().nlayers()-1) llvm_call_layer (i, true /* unconditionally run */); } } // Setup the symbols m_named_values.clear (); BOOST_FOREACH (Symbol &s, inst()->symbols()) { // Skip constants -- we always inline scalar constants, and for // array constants we will just use the pointers to the copy of // the constant that belongs to the instance. if (s.symtype() == SymTypeConst) continue; // Skip structure placeholders if (s.typespec().is_structure()) continue; // Allocate space for locals, temps, aggregate constants if (s.symtype() == SymTypeLocal || s.symtype() == SymTypeTemp || s.symtype() == SymTypeConst) getOrAllocateLLVMSymbol (s); // Set initial value for constants, closures, and strings that are // not parameters. if (s.symtype() != SymTypeParam && s.symtype() != SymTypeOutputParam && s.symtype() != SymTypeGlobal && (s.is_constant() || s.typespec().is_closure_based() || s.typespec().is_string_based() || ((s.symtype() == SymTypeLocal || s.symtype() == SymTypeTemp) && shadingsys().debug_uninit()))) llvm_assign_initial_value (s); // If debugnan is turned on, globals check that their values are ok if (s.symtype() == SymTypeGlobal && shadingsys().debug_nan()) { TypeDesc t = s.typespec().simpletype(); if (t.basetype == TypeDesc::FLOAT) { // just check float-based types int ncomps = t.numelements() * t.aggregate; llvm::Value *args[] = { ll.constant(ncomps), llvm_void_ptr(s), ll.constant((int)s.has_derivs()), sg_void_ptr(), ll.constant(ustring(inst()->shadername())), ll.constant(0), ll.constant(s.name()), ll.constant(0), ll.constant(ncomps), ll.constant("<none>") }; ll.call_function ("osl_naninf_check", args, 10); } } } // make a second pass for the parameters (which may make use of // locals and constants from the first pass) FOREACH_PARAM (Symbol &s, inst()) { // Skip structure placeholders if (s.typespec().is_structure()) continue; // Skip if it's never read and isn't connected if (! s.everread() && ! s.connected_down() && ! s.connected() && ! shadingsys().is_renderer_output(s.name())) continue; // Set initial value for params (may contain init ops) llvm_assign_initial_value (s); } // All the symbols are stack allocated now. // Mark all the basic blocks, including allocating llvm::BasicBlock // records for each. find_basic_blocks (); find_conditionals (); m_layers_already_run.clear (); build_llvm_code (inst()->maincodebegin(), inst()->maincodeend()); if (llvm_has_exit_instance_block()) ll.op_branch (m_exit_instance_block); // also sets insert point // Transfer all of this layer's outputs into the downstream shader's // inputs. for (int layer = this->layer()+1; layer < group().nlayers(); ++layer) { ShaderInstance *child = group()[layer]; for (int c = 0; c < child->nconnections(); ++c) { const Connection &con (child->connection (c)); if (con.srclayer == this->layer()) { ASSERT (con.src.arrayindex == -1 && con.src.channel == -1 && con.dst.arrayindex == -1 && con.dst.channel == -1 && "no support for individual element/channel connection"); Symbol *srcsym (inst()->symbol (con.src.param)); Symbol *dstsym (child->symbol (con.dst.param)); llvm_run_connected_layers (*srcsym, con.src.param); // FIXME -- I'm not sure I understand this. Isn't this // unnecessary if we wrote to the parameter ourself? llvm_assign_impl (*dstsym, *srcsym); } } } // llvm_gen_debug_printf ("done copying connections"); // All done #if 0 /* helpful for debugging */ if (llvm_debug()) llvm_gen_debug_printf (Strutil::format("exit layer %s %s", inst()->layername(), inst()->shadername())); #endif ll.op_return(); if (llvm_debug()) std::cout << "layer_func (" << unique_layer_name << ") "<< this->layer() << "/" << group().nlayers() << " after llvm = " << ll.bitcode_string(ll.current_function()) << "\n"; ll.end_builder(); // clear the builder return ll.current_function(); }
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 }
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 = (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; } } // 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, ','); } params.push_back (ParamValue()); params.back().init (paramname, type, 1, &vals[0]); 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, ','); } params.push_back (ParamValue()); params.back().init (paramname, type, 1, &vals[0]); 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); }
llvm::Type * BackendLLVM::llvm_type_groupdata () { // If already computed, return it if (m_llvm_type_groupdata) return m_llvm_type_groupdata; std::vector<llvm::Type*> fields; int offset = 0; int order = 0; if (llvm_debug() >= 2) std::cout << "Group param struct:\n"; // First, add the array that tells if each layer has run. But only make // slots for the layers that may be called/used. if (llvm_debug() >= 2) std::cout << " layers run flags: " << m_num_used_layers << " at offset " << offset << "\n"; int sz = (m_num_used_layers + 3) & (~3); // Round up to 32 bit boundary fields.push_back (ll.type_array (ll.type_bool(), sz)); offset += sz * sizeof(bool); ++order; // Now add the array that tells which userdata have been initialized, // and the space for the userdata values. int nuserdata = (int) group().m_userdata_names.size(); if (nuserdata) { if (llvm_debug() >= 2) std::cout << " userdata initialized flags: " << nuserdata << " at offset " << offset << ", field " << order << "\n"; ustring *names = & group().m_userdata_names[0]; TypeDesc *types = & group().m_userdata_types[0]; int *offsets = & group().m_userdata_offsets[0]; int sz = (nuserdata + 3) & (~3); fields.push_back (ll.type_array (ll.type_bool(), sz)); offset += nuserdata * sizeof(bool); ++order; for (int i = 0; i < nuserdata; ++i) { TypeDesc type = types[i]; int n = type.numelements() * 3; // always make deriv room type.arraylen = n; fields.push_back (llvm_type (type)); // Alignment int align = type.basesize(); offset = OIIO::round_to_multiple_of_pow2 (offset, align); if (llvm_debug() >= 2) std::cout << " userdata " << names[i] << ' ' << type << ", field " << order << ", offset " << offset << "\n"; offsets[i] = offset; offset += int(type.size()); ++order; } } // For each layer in the group, add entries for all params that are // connected or interpolated, and output params. Also mark those // symbols with their offset within the group struct. m_param_order_map.clear (); for (int layer = 0; layer < group().nlayers(); ++layer) { ShaderInstance *inst = group()[layer]; if (inst->unused()) continue; FOREACH_PARAM (Symbol &sym, inst) { TypeSpec ts = sym.typespec(); if (ts.is_structure()) // skip the struct symbol itself continue; const int arraylen = std::max (1, sym.typespec().arraylength()); const int derivSize = (sym.has_derivs() ? 3 : 1); ts.make_array (arraylen * derivSize); fields.push_back (llvm_type (ts)); // Alignment size_t align = sym.typespec().is_closure_based() ? sizeof(void*) : sym.typespec().simpletype().basesize(); if (offset & (align-1)) offset += align - (offset & (align-1)); if (llvm_debug() >= 2) std::cout << " " << inst->layername() << " (" << inst->id() << ") " << sym.mangled() << " " << ts.c_str() << ", field " << order << ", size " << derivSize * int(sym.size()) << ", offset " << offset << std::endl; sym.dataoffset ((int)offset); offset += derivSize* int(sym.size()); m_param_order_map[&sym] = order; ++order; } }
// Add the attribute -- figure out the type void parse_param(string_view paramname, string_view val, ImageSpec& spec) { TypeDesc type; // start out unknown // If the param string starts with a type name, that's what it is if (size_t typeportion = type.fromstring(paramname)) { paramname.remove_prefix(typeportion); Strutil::skip_whitespace(paramname); } // If the value string starts with a type name, that's what it is else if (size_t typeportion = type.fromstring(val)) { val.remove_prefix(typeportion); Strutil::skip_whitespace(val); } if (type.basetype == TypeDesc::UNKNOWN) { // If we didn't find a type name, try to guess if (val.size() >= 2 && val.front() == '\"' && val.back() == '\"') { // Surrounded by quotes? it's a string (strip off the quotes) val.remove_prefix(1); val.remove_suffix(1); type = TypeDesc::TypeString; } else if (Strutil::string_is<int>(val)) { // Looks like an int, is an int type = TypeDesc::TypeInt; } else if (Strutil::string_is<float>(val)) { // Looks like a float, is a float type = TypeDesc::TypeFloat; } else { // Everything else is assumed a string type = TypeDesc::TypeString; } } // Read the values and set the attribute int n = type.numelements() * type.aggregate; if (type.basetype == TypeDesc::INT) { std::vector<int> values(n); for (int i = 0; i < n; ++i) { Strutil::parse_int(val, values[i]); Strutil::parse_char(val, ','); // optional } if (n > 0) spec.attribute(paramname, type, &values[0]); } if (type.basetype == TypeDesc::FLOAT) { std::vector<float> values(n); for (int i = 0; i < n; ++i) { Strutil::parse_float(val, values[i]); Strutil::parse_char(val, ','); // optional } if (n > 0) spec.attribute(paramname, type, &values[0]); } else if (type.basetype == TypeDesc::STRING) { std::vector<ustring> values(n); for (int i = 0; i < n; ++i) { string_view v; Strutil::parse_string(val, v); Strutil::parse_char(val, ','); // optional values[i] = v; } if (n > 0) spec.attribute(paramname, type, &values[0]); } }
int Dictionary::dict_value (int nodeID, ustring attribname, TypeDesc type, void *data) { if (nodeID <= 0 || nodeID >= (int)m_nodes.size()) return 0; // invalid node ID const Dictionary::Node &node (m_nodes[nodeID]); Dictionary::Query q (node.document, nodeID, attribname, type); Dictionary::QueryMap::iterator qfound = m_cache.find (q); if (qfound != m_cache.end()) { // previously found int offset = qfound->second.valueoffset; int n = type.numelements() * type.aggregate; if (type.basetype == TypeDesc::STRING) { ASSERT (n == 1 && "no string arrays in XML"); ((ustring *)data)[0] = m_stringdata[offset]; return 1; } if (type.basetype == TypeDesc::INT) { for (int i = 0; i < n; ++i) ((int *)data)[i] = m_intdata[offset++]; return 1; } if (type.basetype == TypeDesc::FLOAT) { for (int i = 0; i < n; ++i) ((float *)data)[i] = m_floatdata[offset++]; return 1; } return 0; // Unknown type } // OK, the entry wasn't in the cache, we need to decode it and cache it. const char *val = NULL; if (attribname.empty()) { val = node.node.value(); } else { for (pugi::xml_attribute_iterator ait = node.node.attributes_begin(); ait != node.node.attributes_end(); ++ait) { if (ait->name() == attribname) { val = ait->value(); break; } } } if (val == NULL) return 0; // not found Dictionary::QueryResult r (false, 0); int n = type.numelements() * type.aggregate; if (type.basetype == TypeDesc::STRING && n == 1) { r.valueoffset = (int) m_stringdata.size(); ustring s (val); m_stringdata.push_back (s); ((ustring *)data)[0] = s; m_cache[q] = r; return 1; } if (type.basetype == TypeDesc::INT) { r.valueoffset = (int) m_intdata.size(); for (int i = 0; i < n; ++i) { int v = (int) strtol (val, (char **)&val, 10); while (isspace(*val) || *val == ',') ++val; m_intdata.push_back (v); ((int *)data)[i] = v; } m_cache[q] = r; return 1; } if (type.basetype == TypeDesc::FLOAT) { r.valueoffset = (int) m_floatdata.size(); for (int i = 0; i < n; ++i) { float v = (float) strtod (val, (char **)&val); while (isspace(*val) || *val == ',') ++val; m_floatdata.push_back (v); ((float *)data)[i] = v; } m_cache[q] = r; return 1; } // Anything that's left is an unsupported type return 0; }
int main (int argc, const char *argv[]) { // Create a new shading system. Timer timer; SimpleRenderer rend; shadingsys = ShadingSystem::create (&rend, NULL, &errhandler); shadingsys->attribute("lockgeom", 1); shadingsys->ShaderGroupBegin (); getargs (argc, argv); if (debug || verbose) errhandler.verbosity (ErrorHandler::VERBOSE); 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()); } } shadingsys->ShaderGroupEnd (); // getargs called 'add_shader' for each shader mentioned on the command // line. So now we should have a valid shading state. ShadingAttribStateRef shaderstate = shadingsys->state (); // Set up shader globals and a little test grid of points to shade. ShaderGlobals shaderglobals; memset(&shaderglobals, 0, sizeof(ShaderGlobals)); // Make a shader space that is translated one unit in x and rotated // 45deg about the z axis. OSL::Matrix44 Mshad; Mshad.translate (OSL::Vec3 (1.0, 0.0, 0.0)); Mshad.rotate (OSL::Vec3 (0.0, 0.0, M_PI_4)); // std::cout << "shader-to-common matrix: " << Mshad << "\n"; OSL::TransformationPtr Mshadptr (&Mshad); shaderglobals.shader2common = Mshadptr; // Make an object space that is translated one unit in y and rotated // 90deg about the z axis. OSL::Matrix44 Mobj; Mobj.translate (OSL::Vec3 (0.0, 1.0, 0.0)); Mobj.rotate (OSL::Vec3 (0.0, 0.0, M_PI_2)); // std::cout << "object-to-common matrix: " << Mobj << "\n"; OSL::TransformationPtr Mobjptr (&Mobj); shaderglobals.object2common = Mobjptr; // Make a 'myspace that is non-uniformly scaled OSL::Matrix44 Mmyspace; Mmyspace.scale (OSL::Vec3 (1.0, 2.0, 1.0)); // std::cout << "myspace-to-common matrix: " << Mmyspace << "\n"; rend.name_transform ("myspace", Mmyspace); shaderglobals.dudx = 1.0f / xres; shaderglobals.dvdy = 1.0f / yres; shaderglobals.raytype = ((ShadingSystemImpl *)shadingsys)->raytype_bit (ustring(raytype)); double setuptime = timer (); double runtime = 0; std::vector<float> pixel; if (outputfiles.size() != 0) std::cout << "\n"; // grab this once since we will be shading several points ShadingSystemImpl *ssi = (ShadingSystemImpl *)shadingsys; void* thread_info = ssi->create_thread_info(); for (int iter = 0; iter < iters; ++iter) { for (int y = 0, n = 0; y < yres; ++y) { for (int x = 0; x < xres; ++x, ++n) { shaderglobals.u = (xres == 1) ? 0.5f : (float) x / (xres - 1); shaderglobals.v = (yres == 1) ? 0.5f : (float) y / (yres - 1); shaderglobals.P = Vec3 (shaderglobals.u, shaderglobals.v, 1.0f); shaderglobals.dPdx = Vec3 (shaderglobals.dudx, shaderglobals.dudy, 0.0f); shaderglobals.dPdy = Vec3 (shaderglobals.dvdx, shaderglobals.dvdy, 0.0f); shaderglobals.N = Vec3 (0, 0, 1); shaderglobals.Ng = Vec3 (0, 0, 1); shaderglobals.dPdu = Vec3 (1.0f, 0.0f, 0.0f); shaderglobals.dPdv = Vec3 (0.0f, 1.0f, 0.0f); shaderglobals.surfacearea = 1; // Request a shading context, bind it, execute the shaders. // FIXME -- this will eventually be replaced with a public // ShadingSystem call that encapsulates it. ShadingContext *ctx = ssi->get_context (thread_info); timer.reset (); timer.start (); // run shader for this point ctx->execute (ShadUseSurface, *shaderstate, shaderglobals); runtime += timer (); if (iter == (iters - 1)) { // extract any output vars into images (on last iteration only) for (size_t i = 0; i < outputfiles.size(); ++i) { Symbol *sym = ctx->symbol (ShadUseSurface, ustring(outputvars[i])); if (! sym) { if (n == 0) { std::cout << "Output " << outputvars[i] << " not found, skipping.\n"; outputimgs.push_back(0); // invalid image } continue; } if (n == 0) std::cout << "Output " << outputvars[i] << " to " << outputfiles[i]<< "\n"; TypeDesc t = sym->typespec().simpletype(); TypeDesc tbase = TypeDesc ((TypeDesc::BASETYPE)t.basetype); TypeDesc outtypebase = tbase; if (dataformatname == "uint8") outtypebase = TypeDesc::UINT8; else if (dataformatname == "half") outtypebase = TypeDesc::HALF; else if (dataformatname == "float") outtypebase = TypeDesc::FLOAT; int nchans = t.numelements() * t.aggregate; pixel.resize (nchans); if (n == 0) { OIIO::ImageSpec spec (xres, yres, nchans, outtypebase); OIIO::ImageBuf* img = new OIIO::ImageBuf(outputfiles[i], spec); #if OPENIMAGEIO_VERSION >= 900 /* 0.9.0 */ OIIO::ImageBufAlgo::zero (*img); #else img->zero (); #endif outputimgs.push_back(img); } OIIO::convert_types (tbase, ctx->symbol_data (*sym, 0), TypeDesc::FLOAT, &pixel[0], nchans); outputimgs[i]->setpixel (x, y, &pixel[0]); } } ssi->release_context (ctx, thread_info); } } } ssi->destroy_thread_info(thread_info); if (outputfiles.size() == 0) std::cout << "\n"; // write any images to disk for (size_t i = 0; i < outputimgs.size(); ++i) { if (outputimgs[i]) { outputimgs[i]->save(); delete outputimgs[i]; } } if (debug || stats) { std::cout << "\n"; std::cout << "Setup: " << Strutil::timeintervalformat (setuptime,2) << "\n"; std::cout << "Run : " << Strutil::timeintervalformat (runtime,2) << "\n"; std::cout << "\n"; std::cout << shadingsys->getstats (5) << "\n"; } ShadingSystem::destroy (shadingsys); return EXIT_SUCCESS; }
llvm::Function* RuntimeOptimizer::build_llvm_instance (bool groupentry) { // Make a layer function: void layer_func(ShaderGlobals*, GroupData*) // Note that the GroupData* is passed as a void*. std::string unique_layer_name = Strutil::format ("%s_%d", inst()->layername().c_str(), inst()->id()); m_layer_func = llvm::cast<llvm::Function>(m_llvm_module->getOrInsertFunction(unique_layer_name, llvm_type_void(), llvm_type_sg_ptr(), llvm_type_groupdata_ptr(), NULL)); // Use fastcall for non-entry layer functions to encourage register calling if (!groupentry) m_layer_func->setCallingConv(llvm::CallingConv::Fast); llvm::Function::arg_iterator arg_it = m_layer_func->arg_begin(); // Get shader globals pointer m_llvm_shaderglobals_ptr = arg_it++; m_llvm_groupdata_ptr = arg_it++; llvm::BasicBlock *entry_bb = llvm_new_basic_block (unique_layer_name); // Set up a new IR builder delete m_builder; m_builder = new llvm::IRBuilder<> (entry_bb); // llvm_gen_debug_printf (std::string("enter layer ")+inst()->shadername()); if (groupentry) { if (m_num_used_layers > 1) { // If this is the group entry point, clear all the "layer // executed" bits. If it's not the group entry (but rather is // an upstream node), then set its bit! int sz = (m_num_used_layers + 3) & (~3); // round up to 32 bits llvm_memset (llvm_void_ptr(layer_run_ptr(0)), 0, sz, 4 /*align*/); } // Group entries also need to allot space for ALL layers' params // that are closures (to avoid weird order of layer eval problems). for (int i = 0; i < group().nlayers(); ++i) { ShaderInstance *gi = group()[i]; if (gi->unused()) continue; FOREACH_PARAM (Symbol &sym, gi) { if (sym.typespec().is_closure_based()) { int arraylen = std::max (1, sym.typespec().arraylength()); llvm::Value *val = llvm_constant_ptr(NULL, llvm_type_void_ptr()); for (int a = 0; a < arraylen; ++a) { llvm::Value *arrind = sym.typespec().is_array() ? llvm_constant(a) : NULL; llvm_store_value (val, sym, 0, arrind, 0); } } } // Unconditionally execute earlier layers that are not lazy if (! gi->run_lazily() && i < group().nlayers()-1) llvm_call_layer (i, true /* unconditionally run */); } } // Setup the symbols m_named_values.clear (); BOOST_FOREACH (Symbol &s, inst()->symbols()) { // Skip non-array constants -- we always inline them if (s.symtype() == SymTypeConst && !s.typespec().is_array()) continue; // Skip structure placeholders if (s.typespec().is_structure()) continue; // Allocate space for locals, temps, aggregate constants if (s.symtype() == SymTypeLocal || s.symtype() == SymTypeTemp || s.symtype() == SymTypeConst) getOrAllocateLLVMSymbol (s); // Set initial value for constants, closures, and strings that are // not parameters. if (s.symtype() != SymTypeParam && s.symtype() != SymTypeOutputParam && (s.is_constant() || s.typespec().is_closure_based() || s.typespec().is_string_based())) llvm_assign_initial_value (s); // If debugnan is turned on, globals check that their values are ok if (s.symtype() == SymTypeGlobal && m_shadingsys.debug_nan()) { TypeDesc t = s.typespec().simpletype(); if (t.basetype == TypeDesc::FLOAT) { // just check float-based types int ncomps = t.numelements() * t.aggregate; llvm::Value *args[] = { llvm_constant(ncomps), llvm_void_ptr(s), llvm_constant((int)s.has_derivs()), sg_void_ptr(), llvm_constant(ustring(inst()->shadername())), llvm_constant(0), llvm_constant(s.name()) }; llvm_call_function ("osl_naninf_check", args, 7); } } } // make a second pass for the parameters (which may make use of // locals and constants from the first pass) FOREACH_PARAM (Symbol &s, inst()) { // Skip structure placeholders if (s.typespec().is_structure()) continue; // Skip if it's never read and isn't connected if (! s.everread() && ! s.connected_down() && ! s.connected()) continue; // Set initial value for params (may contain init ops) llvm_assign_initial_value (s); } // All the symbols are stack allocated now. // Mark all the basic blocks, including allocating llvm::BasicBlock // records for each. find_basic_blocks (true); find_conditionals (); m_layers_already_run.clear (); build_llvm_code (inst()->maincodebegin(), inst()->maincodeend()); // Transfer all of this layer's outputs into the downstream shader's // inputs. for (int layer = m_layer+1; layer < group().nlayers(); ++layer) { ShaderInstance *child = m_group[layer]; for (int c = 0; c < child->nconnections(); ++c) { const Connection &con (child->connection (c)); if (con.srclayer == m_layer) { ASSERT (con.src.arrayindex == -1 && con.src.channel == -1 && con.dst.arrayindex == -1 && con.dst.channel == -1 && "no support for individual element/channel connection"); Symbol *srcsym (inst()->symbol (con.src.param)); Symbol *dstsym (child->symbol (con.dst.param)); llvm_run_connected_layers (*srcsym, con.src.param); // FIXME -- I'm not sure I understand this. Isn't this // unnecessary if we wrote to the parameter ourself? llvm_assign_impl (*dstsym, *srcsym); } } } // llvm_gen_debug_printf ("done copying connections"); // All done // llvm_gen_debug_printf (std::string("exit layer ")+inst()->shadername()); builder().CreateRetVoid(); if (shadingsys().llvm_debug()) llvm::outs() << "layer_func (" << unique_layer_name << ") after llvm = " << *m_layer_func << "\n"; delete m_builder; m_builder = NULL; return m_layer_func; }
std::string ShaderGroup::serialize () const { std::ostringstream out; out.imbue (std::locale::classic()); // force C locale out.precision (9); lock_guard lock (m_mutex); for (int i = 0, nl = nlayers(); i < nl; ++i) { const ShaderInstance *inst = m_layers[i].get(); bool dstsyms_exist = inst->symbols().size(); for (int p = 0; p < inst->lastparam(); ++p) { const Symbol *s = dstsyms_exist ? inst->symbol(p) : inst->mastersymbol(p); ASSERT (s); if (s->symtype() != SymTypeParam && s->symtype() != SymTypeOutputParam) continue; Symbol::ValueSource vs = dstsyms_exist ? s->valuesource() : inst->instoverride(p)->valuesource(); if (vs == Symbol::InstanceVal) { TypeDesc type = s->typespec().simpletype(); int offset = s->dataoffset(); if (type.is_unsized_array() && ! dstsyms_exist) { // If we're being asked to serialize a group that isn't // yet optimized, any "unsized" arrays will have their // concrete length and offset in the SymOverrideInfo, // not in the Symbol belonging to the instance. type.arraylen = inst->instoverride(p)->arraylen(); offset = inst->instoverride(p)->dataoffset(); } out << "param " << type << ' ' << s->name(); int nvals = type.numelements() * type.aggregate; if (type.basetype == TypeDesc::INT) { const int *vals = &inst->m_iparams[offset]; for (int i = 0; i < nvals; ++i) out << ' ' << vals[i]; } else if (type.basetype == TypeDesc::FLOAT) { const float *vals = &inst->m_fparams[offset]; for (int i = 0; i < nvals; ++i) out << ' ' << vals[i]; } else if (type.basetype == TypeDesc::STRING) { const ustring *vals = &inst->m_sparams[offset]; for (int i = 0; i < nvals; ++i) out << ' ' << '\"' << Strutil::escape_chars(vals[i]) << '\"'; } else { ASSERT_MSG (0, "unknown type for serialization: %s (%s)", type.c_str(), s->typespec().c_str()); } bool lockgeom = dstsyms_exist ? s->lockgeom() : inst->instoverride(p)->lockgeom(); if (! lockgeom) out << Strutil::sprintf (" [[int lockgeom=%d]]", lockgeom); out << " ;\n"; } } out << "shader " << inst->shadername() << ' ' << inst->layername() << " ;\n"; for (int c = 0, nc = inst->nconnections(); c < nc; ++c) { const Connection &con (inst->connection(c)); ASSERT (con.srclayer >= 0); const ShaderInstance *srclayer = m_layers[con.srclayer].get(); ASSERT (srclayer); ustring srclayername = srclayer->layername(); ASSERT (con.src.param >= 0 && con.dst.param >= 0); bool srcsyms_exist = srclayer->symbols().size(); ustring srcparam = srcsyms_exist ? srclayer->symbol(con.src.param)->name() : srclayer->mastersymbol(con.src.param)->name(); ustring dstparam = dstsyms_exist ? inst->symbol(con.dst.param)->name() : inst->mastersymbol(con.dst.param)->name(); // FIXME: Assertions to be sure we don't yet support individual // channel or array element connections. Fix eventually. ASSERT (con.src.arrayindex == -1 && con.src.channel == -1); ASSERT (con.dst.arrayindex == -1 && con.dst.channel == -1); out << "connect " << srclayername << '.' << srcparam << ' ' << inst->layername() << '.' << dstparam << " ;\n"; } } return out.str(); }
static void setup_output_images (ShadingSystem *shadingsys, ShadingAttribStateRef &shaderstate) { // 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, *shaderstate, 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 bool grep_file (const std::string &filename, boost::regex &re, bool ignore_nonimage_files=false) { if (! Filesystem::exists (filename)) { std::cerr << "igrep: " << filename << ": No such file or directory\n"; return false; } if (Filesystem::is_directory (filename)) { if (! recursive) return false; if (print_dirs) { std::cout << "(" << filename << "/)\n"; std::cout.flush(); } bool r = false; std::vector<std::string> directory_entries; Filesystem::get_directory_entries (filename, directory_entries); for (size_t i = 0, e = directory_entries.size(); i < e; ++i) r |= grep_file (directory_entries[i], re, true); return r; } std::unique_ptr<ImageInput> in (ImageInput::open (filename.c_str())); if (! in.get()) { if (! ignore_nonimage_files) std::cerr << geterror() << "\n"; return false; } ImageSpec spec = in->spec(); if (file_match) { bool match = boost::regex_search (filename, re); if (match && ! invert_match) { std::cout << filename << "\n"; return true; } } bool found = false; int subimage = 0; do { if (!all_subimages && subimage > 0) break; for (auto&& p : spec.extra_attribs) { TypeDesc t = p.type(); if (t.elementtype() == TypeDesc::STRING) { int n = t.numelements(); for (int i = 0; i < n; ++i) { bool match = boost::regex_search (((const char **)p.data())[i], re); found |= match; if (match && ! invert_match) { if (list_files) { std::cout << filename << "\n"; return found; } std::cout << filename << ": " << p.name() << " = " << ((const char **)p.data())[i] << "\n"; } } } } } while (in->seek_subimage (++subimage, 0, spec)); if (invert_match) { found = !found; if (found) std::cout << filename << "\n"; } return found; }