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::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(); }
void ShaderInstance::parameters (const ParamValueList ¶ms) { // Seed the params with the master's defaults m_iparams = m_master->m_idefaults; m_fparams = m_master->m_fdefaults; m_sparams = m_master->m_sdefaults; m_instoverrides.resize (std::max (0, lastparam())); // Set the initial lockgeom and dataoffset on the instoverrides, based // on the master. for (int i = 0, e = (int)m_instoverrides.size(); i < e; ++i) { Symbol *sym = master()->symbol(i); m_instoverrides[i].lockgeom (sym->lockgeom()); m_instoverrides[i].dataoffset (sym->dataoffset()); } for (auto&& p : params) { if (p.name().size() == 0) continue; // skip empty names int i = findparam (p.name()); if (i >= 0) { // if (shadingsys().debug()) // shadingsys().info (" PARAMETER %s %s", p.name(), p.type()); const Symbol *sm = master()->symbol(i); // This sym in the master SymOverrideInfo *so = &m_instoverrides[i]; // Slot for sym's override info TypeSpec sm_typespec = sm->typespec(); // Type of the master's param if (sm_typespec.is_closure_based()) { // Can't assign a closure instance value. shadingsys().warning ("skipping assignment of closure: %s", sm->name()); continue; } if (sm_typespec.is_structure()) continue; // structs are just placeholders; skip const void *data = p.data(); float tmpdata[3]; // used for inline conversions to float/float[3] // Check type of parameter and matching symbol. Note that the // compatible accounts for indefinite-length arrays. TypeDesc paramtype = sm_typespec.simpletype(); // what the shader writer wants TypeDesc valuetype = p.type(); // what the data provided actually is if (master()->shadingsys().relaxed_param_typecheck()) { // first handle cases where we actually need to modify the data (like setting a float parameter with an int) if ((paramtype == TypeDesc::FLOAT || paramtype.is_vec3()) && valuetype.basetype == TypeDesc::INT && valuetype.basevalues() == 1) { int val = *static_cast<const int*>(p.data()); float conv = float(val); if (val != int(conv)) shadingsys().error ("attempting to set parameter from wrong type would change the value: %s (set %.9g from %d)", sm->name(), conv, val); tmpdata[0] = conv; data = tmpdata; valuetype = TypeDesc::FLOAT; } // Relaxed rules just look to see that the types are isomorphic to each other (ie: same number of base values) // Note that: // * basetypes must match exactly (int vs float vs string) // * valuetype cannot be unsized (we must know the concrete number of values) // * if paramtype is sized (or not an array) just check for the total number of entries // * if paramtype is unsized (shader writer is flexible about how many values come in) -- make sure we are a multiple of the target type // * allow a single float setting a vec3 (or equivalent) if (!( valuetype.basetype == paramtype.basetype && !valuetype.is_unsized_array() && ((!paramtype.is_unsized_array() && valuetype.basevalues() == paramtype.basevalues()) || ( paramtype.is_unsized_array() && valuetype.basevalues() % paramtype.aggregate == 0) || ( paramtype.is_vec3() && valuetype == TypeDesc::FLOAT) ) )) { // We are being very relaxed in this mode, so if the user _still_ got it wrong // something more serious is at play and we should treat it as an error. shadingsys().error ("attempting to set parameter from incompatible type: %s (expected '%s', received '%s')", sm->name(), paramtype, valuetype); continue; } } else if (!compatible_param(paramtype, valuetype)) { shadingsys().warning ("attempting to set parameter with wrong type: %s (expected '%s', received '%s')", sm->name(), paramtype, valuetype); continue; } // Mark that the override as an instance value so->valuesource (Symbol::InstanceVal); // Lock the param against geometric primitive overrides if the // master thinks it was so locked, AND the Parameter() call // didn't specify lockgeom=false (which would be indicated by // the parameter's interpolation being non-CONSTANT). bool lockgeom = (sm->lockgeom() && p.interp() == ParamValue::INTERP_CONSTANT); so->lockgeom (lockgeom); DASSERT (so->dataoffset() == sm->dataoffset()); so->dataoffset (sm->dataoffset()); if (paramtype.is_vec3() && valuetype == TypeDesc::FLOAT) { // Handle the special case of assigning a float for a triple // by replicating it into local memory. tmpdata[0] = *(const float *)data; tmpdata[1] = *(const float *)data; tmpdata[2] = *(const float *)data; data = &tmpdata; valuetype = paramtype; } if (paramtype.arraylen < 0) { // An array of definite size was supplied to a parameter // that was an array of indefinite size. Magic! The trick // here is that we need to allocate paramter space at the // END of the ordinary param storage, since when we assigned // data offsets to each parameter, we didn't know the length // needed to allocate this param in its proper spot. int nelements = valuetype.basevalues(); // Store the actual length in the shader instance parameter // override info. Compute the length this way to account for relaxed // parameter checking (for example passing an array of floats to an array of colors) so->arraylen (nelements / paramtype.aggregate); // Allocate space for the new param size at the end of its // usual parameter area, and set the new dataoffset to that // position. if (paramtype.basetype == TypeDesc::FLOAT) { so->dataoffset((int) m_fparams.size()); expand (m_fparams, nelements); } else if (paramtype.basetype == TypeDesc::INT) { so->dataoffset((int) m_iparams.size()); expand (m_iparams, nelements); } else if (paramtype.basetype == TypeDesc::STRING) { so->dataoffset((int) m_sparams.size()); expand (m_sparams, nelements); } else { ASSERT (0 && "unexpected type"); } // FIXME: There's a tricky case that we overlook here, where // an indefinite-length-array parameter is given DIFFERENT // definite length in subsequent rerenders. Don't do that. } else { // If the instance value is the same as the master's default, // just skip the parameter, let it "keep" the default. // Note that this can't/shouldn't happen for the indefinite- // sized array case, which is why we have it in the 'else' // clause of that test. void *defaultdata = m_master->param_default_storage(i); if (lockgeom && memcmp (defaultdata, data, valuetype.size()) == 0) { // Must reset valuesource to default, in case the parameter // was set already, and now is being changed back to default. so->valuesource (Symbol::DefaultVal); } } // Copy the supplied data into place. memcpy (param_storage(i), data, valuetype.size()); } else { shadingsys().warning ("attempting to set nonexistent parameter: %s", p.name()); } } { // Adjust the stats ShadingSystemImpl &ss (shadingsys()); size_t symmem = vectorbytes(m_instoverrides); size_t parammem = (vectorbytes(m_iparams) + vectorbytes(m_fparams) + vectorbytes(m_sparams)); spin_lock lock (ss.m_stat_mutex); ss.m_stat_mem_inst_syms += symmem; ss.m_stat_mem_inst_paramvals += parammem; ss.m_stat_mem_inst += (symmem+parammem); ss.m_stat_memory += (symmem+parammem); } }