Skip to content

Commit fc75eb4

Browse files
adamrwoodburypixar-oss
authored andcommitted
Add OpenExec support for caching simple prim definitions, by introducing a
"composed prim definition" structure that stores computations defined on all ancestor types of the provider prim's schema type. (Internal change: 2366612)
1 parent fd864e3 commit fc75eb4

File tree

3 files changed

+144
-43
lines changed

3 files changed

+144
-43
lines changed

pxr/exec/exec/definitionRegistry.cpp

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -124,35 +124,24 @@ Exec_DefinitionRegistry::GetComputationDefinition(
124124
return nullptr;
125125
}
126126

127-
// Iterate over all ancestor types of the provider's schema type, from
128-
// derived to base, starting with the schema type itself. Look for a
129-
// matching plugin prim computation for the derived-most schema type that
130-
// defines it, or null, if no matching computation can be found.
131-
//
132-
// TODO: Repeatedly traversing the schema type hierarchy like this is
133-
// wasteful and we plan to cache results appropriately. But we still need to
134-
// add support for applied schemas, and we will also need to add the ability
135-
// to compose computation definitions, so for now we are leaving this
136-
// inefficiency in place until more of that functionality lands. The current
137-
// thinking is that exec will cache composed prim definitions that will be
138-
// keyed off of a tuple of typed and applied schemas and will cache the
139-
// resulting set of composed computation definitions. That will enable this
140-
// code to construct a key and do a single lookup into the cache, rather
141-
// than searching to find the computation definition.
142-
143-
TfType foundType;
144-
std::vector<TfType> schemaAncestorTypes;
145-
schemaType.GetAllAncestorTypes(&schemaAncestorTypes);
146-
147-
for (const TfType type : schemaAncestorTypes) {
148-
if (const auto pluginIt = _pluginPrimComputationDefinitions.find(
149-
{type, computationName});
150-
pluginIt != _pluginPrimComputationDefinitions.end()) {
151-
return &pluginIt->second;
152-
}
127+
// Get the composed prim definition, creating it if necesseary, and use it
128+
// to look up the computation, or to determine that the requested
129+
// computation isn't defined for this prim.
130+
auto composedDefIt = _composedPrimDefinitions.find(schemaType);
131+
if (composedDefIt == _composedPrimDefinitions.end()) {
132+
// Note that we allow concurrent callers to race to compose prim
133+
// definitions, since it is safe to do so and we don't expect it to
134+
// happen in the common case.
135+
_ComposedPrimDefinition primDef =
136+
_ComposePrimDefinition(schemaType);
137+
138+
composedDefIt = _composedPrimDefinitions.emplace(
139+
schemaType, std::move(primDef)).first;
153140
}
154141

155-
return nullptr;
142+
const auto &compDefs = composedDefIt->second.primComputationDefinitions;
143+
const auto it = compDefs.find(computationName);
144+
return it == compDefs.end() ? nullptr : it->second;
156145
}
157146

158147
const Exec_ComputationDefinition *
@@ -176,6 +165,52 @@ Exec_DefinitionRegistry::GetComputationDefinition(
176165
return nullptr;
177166
}
178167

168+
Exec_DefinitionRegistry::_ComposedPrimDefinition
169+
Exec_DefinitionRegistry::_ComposePrimDefinition(
170+
const TfType schemaType) const
171+
{
172+
TRACE_FUNCTION();
173+
174+
// Iterate over all ancestor types of the provider's schema type, from
175+
// derived to base, starting with the schema type itself. Ensure that plugin
176+
// computations have been loaded for each schema type for which they are
177+
// registered. Add all plugin computations registered for each type to the
178+
// composed prim definition.
179+
//
180+
// TODO: Add support for computations that are registered for applied
181+
// schemas. To do that, instead of keying off the schema type we will need
182+
// to use a "configuration key" that combines the typed schema with applied
183+
// schemas. We will also need to search through all applied schemas, in
184+
// strength order, in addition to searching up the typed schema type
185+
// hierarchy.
186+
187+
std::vector<TfType> schemaAncestorTypes;
188+
schemaType.GetAllAncestorTypes(&schemaAncestorTypes);
189+
190+
// Build up the composed prim definition.
191+
_ComposedPrimDefinition primDef;
192+
193+
for (const TfType type : schemaAncestorTypes) {
194+
195+
// TODO: For all but the first type, it makes sense to look in
196+
// _composedPrimDefinitions to see if we have already composed the base
197+
// type, and then to merge, rather than keep searching up the type
198+
// hierarchy.
199+
200+
if (const auto pluginIt = _pluginPrimComputationDefinitions.find(type);
201+
pluginIt != _pluginPrimComputationDefinitions.end()) {
202+
for (const Exec_PluginComputationDefinition &computationDef :
203+
pluginIt->second) {
204+
primDef.primComputationDefinitions.emplace(
205+
computationDef.GetComputationName(),
206+
&computationDef);
207+
}
208+
}
209+
}
210+
211+
return primDef;
212+
}
213+
179214
void
180215
Exec_DefinitionRegistry::_RegisterPrimComputation(
181216
TfType schemaType,
@@ -203,14 +238,11 @@ Exec_DefinitionRegistry::_RegisterPrimComputation(
203238
}
204239

205240
const bool emplaced =
206-
_pluginPrimComputationDefinitions.emplace(
207-
std::piecewise_construct,
208-
std::forward_as_tuple(schemaType, computationName),
209-
std::forward_as_tuple(
210-
resultType,
211-
computationName,
212-
std::move(callback),
213-
std::move(inputKeys))).second;
241+
_pluginPrimComputationDefinitions[schemaType].emplace(
242+
resultType,
243+
computationName,
244+
std::move(callback),
245+
std::move(inputKeys)).second;
214246

215247
if (!emplaced) {
216248
TF_CODING_ERROR(

pxr/exec/exec/definitionRegistry.h

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
#include "pxr/base/tf/type.h"
2121
#include "pxr/base/tf/weakBase.h"
2222

23+
#include "tbb/concurrent_unordered_map.h"
24+
2325
#include <memory>
26+
#include <set>
2427
#include <tuple>
2528
#include <unordered_map>
2629
#include <utility>
@@ -97,10 +100,32 @@ class Exec_DefinitionRegistry : public TfWeakBase
97100
Exec_DefinitionRegistry();
98101

99102
// Returns a reference to the singleton that is suitable for registering
100-
// new computations. This instance cannot be used to look up computations.
103+
// new computations.
104+
//
105+
// The returned instance cannot be used to look up computations.
106+
//
101107
EXEC_API
102108
static Exec_DefinitionRegistry& _GetInstanceForRegistration();
103109

110+
// a structure that contains the definitions for all computations that can
111+
// be found on a prim of a given type.
112+
//
113+
struct _ComposedPrimDefinition {
114+
// Map from computation name to plugin prim computation definition.
115+
std::unordered_map<
116+
TfToken,
117+
const Exec_PluginComputationDefinition *,
118+
TfHash>
119+
primComputationDefinitions;
120+
121+
// TODO: Add plugin attribute computation definitions.
122+
};
123+
124+
// Creates and returns the composed prim definition for a prim with type
125+
// \p schemaType.
126+
//
127+
_ComposedPrimDefinition _ComposePrimDefinition(TfType schemaType) const;
128+
104129
void _RegisterPrimComputation(
105130
TfType schemaType,
106131
const TfToken &computationName,
@@ -130,17 +155,34 @@ class Exec_DefinitionRegistry : public TfWeakBase
130155
// functions.
131156
std::unique_ptr<Exec_RegistrationBarrier> _registrationBarrier;
132157

133-
// Map from (schemaType, computationName) to plugin prim computation
134-
// definition.
135-
//
136-
// TODO: When we add support for loading plugins, we will need to think about
137-
// how to provide thread-safe access to this map.
158+
// Comparator for ordering plugin computation definitions in a set.
159+
struct _PluginComputationDefinitionComparator {
160+
bool operator()(
161+
const Exec_PluginComputationDefinition &a,
162+
const Exec_PluginComputationDefinition &b) const {
163+
return a.GetComputationName() < b.GetComputationName();
164+
}
165+
};
166+
167+
// Map from schemaType to plugin prim computation definitions.
138168
std::unordered_map<
139-
std::tuple<TfType, TfToken>,
140-
Exec_PluginComputationDefinition,
169+
TfType,
170+
std::set<
171+
Exec_PluginComputationDefinition,
172+
_PluginComputationDefinitionComparator>,
141173
TfHash>
142174
_pluginPrimComputationDefinitions;
143175

176+
// Map from schemaType to composed prim exec definition.
177+
//
178+
// This is a concurrent map to allow computation lookup to happen in
179+
// parallel with lazy caching of composed prim definitions.
180+
mutable tbb::concurrent_unordered_map<
181+
TfType,
182+
_ComposedPrimDefinition,
183+
TfHash>
184+
_composedPrimDefinitions;
185+
144186
// Map from computationName to builtin stage computation
145187
// definition.
146188
std::unordered_map<

pxr/exec/exec/testenv/testExecComputationRegistration.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ TF_DEFINE_PRIVATE_TOKENS(
4040
(attributeComputation)
4141
(attributeComputedValueComputation)
4242
(attributeName)
43+
(baseAndDerivedSchemaComputation)
4344
(derivedSchemaComputation)
4445
(emptyComputation)
4546
(missingComputation)
@@ -105,12 +106,23 @@ EXEC_REGISTER_SCHEMA(TestExecComputationRegistrationCustomSchema)
105106
Attribute(_tokens->attr)
106107
.Computation<double>(ExecBuiltinComputations->computeValue)
107108
);
109+
110+
self.PrimComputation(_tokens->baseAndDerivedSchemaComputation)
111+
.Callback(+[](const VdfContext &) { return 1.0; });
108112
}
109113

110114
EXEC_REGISTER_SCHEMA(TestExecComputationRegistrationDerivedCustomSchema)
111115
{
112116
self.PrimComputation(_tokens->derivedSchemaComputation)
113117
.Callback(+[](const VdfContext &) { return 1.0; });
118+
119+
// This overrides the computation of the same name on the base schema.
120+
// (We add an input here so we can verify this definition is stronger.)
121+
self.PrimComputation(_tokens->baseAndDerivedSchemaComputation)
122+
.Callback(+[](const VdfContext &) { return 1.0; })
123+
.Inputs(
124+
AttributeValue<int>(_tokens->attributeName)
125+
);
114126
}
115127

116128
// XXX:TODO
@@ -461,6 +473,21 @@ TestDerivedSchemaComputationRegistration()
461473
TF_AXIOM(primCompDef);
462474
}
463475

476+
{
477+
// Look up a computation registered for the base and derived schema
478+
// types.
479+
const Exec_ComputationDefinition *const primCompDef =
480+
reg.GetComputationDefinition(
481+
*prim, _tokens->baseAndDerivedSchemaComputation, nullJournal);
482+
TF_AXIOM(primCompDef);
483+
484+
// Make sure we got the definition from the derived schema (i.e., the
485+
// stronger one).
486+
const auto inputKeys =
487+
primCompDef->GetInputKeys(*prim, nullJournal);
488+
ASSERT_EQ(inputKeys.size(), 1);
489+
}
490+
464491
{
465492
// Look up a computation registered for the base schema type.
466493
const Exec_ComputationDefinition *const primCompDef =

0 commit comments

Comments
 (0)