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); } }
// Can a parameter with type 'a' be bound to a value of type b? // Requires matching types (and if arrays, matching lengths or for // a's length to be undetermined), or it's also ok to bind a single float to // a non-array triple. All triples are considered equivalent for this test. inline bool compatible_param (const TypeDesc& a, const TypeDesc& b) { return equivalent (a, b) || (a.is_vec3() && b == TypeDesc::FLOAT); }