From 35aa264dc8435327af4d2dc3680d65974e9b8093 Mon Sep 17 00:00:00 2001 From: Curtis Black Date: Tue, 11 Apr 2023 11:38:59 +1000 Subject: [PATCH] Add type information for needed attributes. (#1650) We can currently query which attribute names and scopes are requested in a shader. This PR extends that idea to also allow querying the attribute types. This now more closely matches the equivalent available queries for user data. For example, this shader: ``` shader Shader { color c; getattribute("user_color", c); string s; getattribute("user_string", s); } ``` allows us to do this: ``` int nattr = 0; if (shadingSys->getattribute(shaderRef.get(), "num_attributes_needed", nattr) && nattr) { // this already works OIIO::ustring* names = nullptr; shadingSys->getattribute(shaderRef.get(), "attributes_needed", OSL::TypeDesc::PTR, &names); // this is the added feature OIIO::TypeDesc* types = nullptr; shadingSys->getattribute(shaderRef.get(), "attribute_types", OSL::TypeDesc::PTR, &types); // nattr: 2 // names: ["user_color", "user_string"] // types: [OIIO::TypeColor, OIIO::TypeString] } ``` The motivation behind this feature is to perform additional type validation at material compile time when determining what primvars can be provided to the shader. One side-effect of this change to be aware of is that, as mentioned in the code comments, if the same name is requested multiple times with different types, it will now be reported multiple times. Hopefully this is not too much of an issue as the same behaviour also occurs when an attribute is requested in multiple scopes, or when user data is requested with multiple types. Signed-off-by: Curtis Black --- src/include/OSL/oslexec.h | 7 ++++-- src/liboslexec/oslexec_pvt.h | 16 +++++++++++-- src/liboslexec/runtimeoptimize.cpp | 38 ++++++++++++++++++++++-------- src/liboslexec/shadingsys.cpp | 6 +++++ src/testshade/testshade.cpp | 8 +++++-- 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/src/include/OSL/oslexec.h b/src/include/OSL/oslexec.h index 1d544ff85..3a6b849bb 100644 --- a/src/include/OSL/oslexec.h +++ b/src/include/OSL/oslexec.h @@ -432,12 +432,15 @@ class OSLEXECPUBLIC ShadingSystem { /// ptr attributes_needed Retrieves a pointer to the ustring array /// containing the names of the needed attributes. /// Note that if the same attribute - /// is requested in multiple scopes, it will + /// is requested in multiple scopes, or with + /// multiple different types, it will /// appear in the array multiple times - once for - /// each scope in which is is queried. + /// each scope/type in which is is queried. /// ptr attribute_scopes Retrieves a pointer to a ustring array containing /// the scopes associated with each attribute query /// in the attributes_needed array. + /// ptr attribute_types Retrieves a pointer to the array of + /// TypeDesc describing the attributes. /// int unknown_attributes_needed Nonzero if additional attributes may be /// needed, whose names will not be known /// until the shader actually runs. diff --git a/src/liboslexec/oslexec_pvt.h b/src/liboslexec/oslexec_pvt.h index fb293fbc2..fa5f90b16 100644 --- a/src/liboslexec/oslexec_pvt.h +++ b/src/liboslexec/oslexec_pvt.h @@ -211,9 +211,11 @@ struct UserDataNeeded { struct AttributeNeeded { ustring name; ustring scope; + TypeDesc type; - AttributeNeeded(ustring name, ustring scope = ustring()) - : name(name), scope(scope) + AttributeNeeded(ustring name, ustring scope = ustring(), + TypeDesc type = TypeUnknown) + : name(name), scope(scope), type(type) { } @@ -223,6 +225,15 @@ struct AttributeNeeded { return a.name < b.name; if (a.scope != b.scope) return a.scope < b.scope; + if (a.type.basetype != b.type.basetype) + return a.type.basetype < b.type.basetype; + if (a.type.aggregate != b.type.aggregate) + return a.type.aggregate < b.type.aggregate; + if (a.type.arraylen != b.type.arraylen) + return a.type.arraylen < b.type.arraylen; + // Ignore vector semantics + // if (a.type.vecsemantics != b.type.vecsemantics) + // return a.type.vecsemantics < b.type.vecsemantics; return false; // they are equal } }; @@ -1919,6 +1930,7 @@ class ShaderGroup { std::vector m_userdata_init_vals; std::vector m_attributes_needed; std::vector m_attribute_scopes; + std::vector m_attribute_types; std::vector m_renderer_outputs; ///< Names of renderer outputs std::vector m_symlocs; ///< SORTED!! bool m_unknown_textures_needed; diff --git a/src/liboslexec/runtimeoptimize.cpp b/src/liboslexec/runtimeoptimize.cpp index 5aec23f09..1abacb8c6 100644 --- a/src/liboslexec/runtimeoptimize.cpp +++ b/src/liboslexec/runtimeoptimize.cpp @@ -3331,27 +3331,44 @@ RuntimeOptimizer::run() OSL_DASSERT(sym1 && sym1->typespec().is_string()); if (sym1->is_constant()) { if (op.nargs() == 3) { + Symbol* sym2 = opargsym(op, 2); // getattribute( attributename, result ) m_attributes_needed.insert( - AttributeNeeded(sym1->get_string())); - } else { - OSL_DASSERT(op.nargs() == 4 || op.nargs() == 5); + AttributeNeeded(sym1->get_string(), ustring(), + sym2->typespec().simpletype())); + } else if (op.nargs() == 4) { Symbol* sym2 = opargsym(op, 2); + Symbol* sym3 = opargsym(op, 3); if (sym2->typespec().is_string()) { - // getattribute( scopename, attributename, result ) or - // getattribute( scopename, attributename, arrayindex, result ) + // getattribute( scopename, attributename, result ) if (sym2->is_constant()) { - m_attributes_needed.insert( - AttributeNeeded(sym2->get_string(), - sym1->get_string())); + m_attributes_needed.insert(AttributeNeeded( + sym2->get_string(), sym1->get_string(), + sym3->typespec().simpletype())); } else { m_unknown_attributes_needed = true; } } else { // getattribute( attributename, arrayindex, result ) m_attributes_needed.insert( - AttributeNeeded(sym1->get_string())); + AttributeNeeded(sym1->get_string(), ustring(), + sym3->typespec().simpletype())); + } + } else if (op.nargs() == 5) { + Symbol* sym2 = opargsym(op, 2); + Symbol* sym4 = opargsym(op, 4); + if (sym2->typespec().is_string()) { + // getattribute( scopename, attributename, arrayindex, result ) + if (sym2->is_constant()) { + m_attributes_needed.insert(AttributeNeeded( + sym2->get_string(), sym1->get_string(), + sym4->typespec().simpletype())); + } else { + m_unknown_attributes_needed = true; + } } + } else { + OSL_DASSERT(false); } } else { // sym1 not constant m_unknown_attributes_needed = true; @@ -3403,7 +3420,8 @@ RuntimeOptimizer::run() if (m_attributes_needed.size()) { shadingcontext()->infofmt("Group needs attributes:"); for (auto&& f : m_attributes_needed) - shadingcontext()->infofmt(" {} {}", f.name, f.scope); + shadingcontext()->infofmt(" {} {} {}", f.name, f.scope, + f.type); if (m_unknown_attributes_needed) shadingcontext()->infofmt( " Also may construct attribute names on the fly."); diff --git a/src/liboslexec/shadingsys.cpp b/src/liboslexec/shadingsys.cpp index b2219ec36..543f3de64 100644 --- a/src/liboslexec/shadingsys.cpp +++ b/src/liboslexec/shadingsys.cpp @@ -2102,6 +2102,11 @@ ShadingSystemImpl::getattribute(ShaderGroup* group, string_view name, *(ustring**)val = n ? &group->m_attribute_scopes[0] : NULL; return true; } + if (name == "attribute_types" && type.basetype == TypeDesc::PTR) { + size_t n = group->m_attribute_types.size(); + *(TypeDesc**)val = n ? &group->m_attribute_types[0] : NULL; + return true; + } if (name == "unknown_attributes_needed" && type == TypeDesc::TypeInt) { *(int*)val = (int)group->m_unknown_attributes_needed; return true; @@ -3560,6 +3565,7 @@ ShadingSystemImpl::optimize_group(ShaderGroup& group, ShadingContext* ctx, for (auto&& f : rop.m_attributes_needed) { group.m_attributes_needed.push_back(f.name); group.m_attribute_scopes.push_back(f.scope); + group.m_attribute_types.push_back(f.type); } group.m_optimized = true; diff --git a/src/testshade/testshade.cpp b/src/testshade/testshade.cpp index 98c1a20c8..fbfbcaf32 100644 --- a/src/testshade/testshade.cpp +++ b/src/testshade/testshade.cpp @@ -1468,13 +1468,17 @@ test_group_attributes(ShaderGroup* group) std::cout << "Need " << nattr << " attributes:\n"; ustring* names = NULL; ustring* scopes = NULL; + TypeDesc* types = NULL; shadingsys->getattribute(group, "attributes_needed", TypeDesc::PTR, &names); shadingsys->getattribute(group, "attribute_scopes", TypeDesc::PTR, &scopes); - OSL_DASSERT(names && scopes); + shadingsys->getattribute(group, "attribute_types", TypeDesc::PTR, + &types); + OSL_DASSERT(names && scopes && types); for (int i = 0; i < nattr; ++i) - std::cout << " " << names[i] << ' ' << scopes[i] << "\n"; + std::cout << " " << names[i] << ' ' << scopes[i] << ' ' + << types[i] << "\n"; int unk = 0; shadingsys->getattribute(group, "unknown_attributes_needed", unk);