extComputationUtils.h
1 //
2 // Copyright 2019 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_IMAGING_HD_EXT_COMPUTATION_UTILS_H
25 #define PXR_IMAGING_HD_EXT_COMPUTATION_UTILS_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/imaging/hd/api.h"
29 #include "pxr/imaging/hd/extComputation.h"
30 #include "pxr/imaging/hd/sceneDelegate.h"
31 
32 #include "pxr/base/tf/span.h"
33 #include "pxr/base/tf/token.h"
34 #include "pxr/base/vt/value.h"
35 
36 #include <unordered_map>
37 
38 PXR_NAMESPACE_OPEN_SCOPE
39 
40 using HdExtComputationConstPtr = HdExtComputation const *;
41 using HdExtComputationConstPtrVector = std::vector<HdExtComputationConstPtr>;
42 
43 // This class contains utility methods to allow any Hydra backend to execute
44 // CPU computations via the Hydra ExtComputation framework.
45 //
46 // Note:
47 // The computation execution happens during Rprim sync. This precludes the
48 // use of computations shared by multiple Rprims, since the chain of
49 // computations for a computation primvar is executed for each Rprim.
50 class HdExtComputationUtils {
51 public:
52  using ValueStore =
53  std::unordered_map<TfToken, VtValue, TfToken::HashFunctor>;
54 
55  // Returns a map containing the (token, value) pairs for each "computation
56  // primvar".
57  // The participating computations are ordered based on their dependency
58  // and then, the CPU kernel is executed for each computation.
59  HD_API
60  static ValueStore
61  GetComputedPrimvarValues(
62  HdExtComputationPrimvarDescriptorVector const& compPrimvars,
63  HdSceneDelegate* sceneDelegate);
64 
65  template <unsigned int CAPACITY>
66  using SampledValueStore =
67  std::unordered_map<TfToken, HdTimeSampleArray<VtValue, CAPACITY>,
69 
74  template <unsigned int CAPACITY>
75  static void
76  SampleComputedPrimvarValues(
77  HdExtComputationPrimvarDescriptorVector const& compPrimvars,
78  HdSceneDelegate* sceneDelegate,
79  size_t maxSampleCount,
80  SampledValueStore<CAPACITY> *computedPrimvarValueStore);
81 
82  // Helper methods (these are public for testing purposes)
83  using ComputationDependencyMap =
84  std::unordered_map<HdExtComputation const *,
85  HdExtComputationConstPtrVector>;
86  // Returns true if an ordering of the computations wherein any dependencies
87  // of a given computation come before it is possible, and fills
88  // sortedComps with the ordering.
89  // Returns false otherwise.
90  // The directed graph of a computation (vertex) and its dependencies (edges)
91  // is represented via the ComputationDependencyMap.
92  HD_API
93  static bool
94  DependencySort(ComputationDependencyMap cdm,
95  HdExtComputationConstPtrVector* sortedComps);
96 
97  HD_API
98  static void
99  PrintDependencyMap(ComputationDependencyMap const& cdm);
100 
101 private:
102  HD_API
103  static ComputationDependencyMap
104  _GenerateDependencyMap(
105  HdExtComputationPrimvarDescriptorVector const& compPrimvars,
106  HdSceneDelegate* sceneDelegate);
107 
108  template <unsigned int CAPACITY>
109  static void
110  _ExecuteSampledComputations(
111  HdExtComputationConstPtrVector computations,
112  HdSceneDelegate* sceneDelegate,
113  size_t maxSampleCount,
114  SampledValueStore<CAPACITY>* valueStore);
115 
116  // Limits the list of the computation input time samples to the specified
117  // maximum number of (unique) samples.
118  HD_API
119  static void
120  _LimitTimeSamples(size_t maxSampleCount, std::vector<double>* times);
121 
122  // Internal method to invoke the computation with the specified input
123  // values, storing the output values in the provided buffer. The value
124  // arrays correspond to GetSceneInputNames(), GetComputationInputs(), and
125  // GetComputationOutputs() from the HdExtComputation, respectively, and are
126  // required to have the same lengths.
127  HD_API
128  static bool
129  _InvokeComputation(
130  HdSceneDelegate& sceneDelegate,
131  HdExtComputation const& computation,
132  TfSpan<const VtValue> sceneInputValues,
133  TfSpan<const VtValue> compInputValues,
134  TfSpan<VtValue> compOutputValues);
135 };
136 
137 template <unsigned int CAPACITY>
138 /*static*/ void
139 HdExtComputationUtils::SampleComputedPrimvarValues(
140  HdExtComputationPrimvarDescriptorVector const& compPrimvars,
141  HdSceneDelegate* sceneDelegate,
142  size_t maxSampleCount,
143  SampledValueStore<CAPACITY> *computedPrimvarValueStore
144 )
145 {
146  HD_TRACE_FUNCTION();
147 
148  // Directed graph representation of the participating computations
149  ComputationDependencyMap cdm =
150  _GenerateDependencyMap(compPrimvars, sceneDelegate);
151 
152  // Topological ordering of the computations
153  HdExtComputationConstPtrVector sortedComputations;
154  bool success = DependencySort(cdm, &sortedComputations);
155  if (!success) {
156  return;
157  }
158 
159  // Execution
160  SampledValueStore<CAPACITY> valueStore;
161  _ExecuteSampledComputations<CAPACITY>(sortedComputations, sceneDelegate,
162  maxSampleCount, &valueStore);
163 
164  // Output extraction
165  for (auto const& pv : compPrimvars) {
166  TfToken const& compOutputName = pv.sourceComputationOutputName;
167  (*computedPrimvarValueStore)[pv.name] = valueStore[compOutputName];
168  }
169 }
170 
171 template <unsigned int CAPACITY>
172 /*static*/ void
173 HdExtComputationUtils::_ExecuteSampledComputations(
174  HdExtComputationConstPtrVector computations,
175  HdSceneDelegate* sceneDelegate,
176  size_t maxSampleCount,
177  SampledValueStore<CAPACITY> *valueStore
178 )
179 {
180  HD_TRACE_FUNCTION();
181 
182  for (auto const& comp : computations) {
183  SdfPath const& compId = comp->GetId();
184 
185  TfTokenVector const& sceneInputNames = comp->GetSceneInputNames();
186  HdExtComputationInputDescriptorVector const& compInputs =
187  comp->GetComputationInputs();
188  HdExtComputationOutputDescriptorVector const& compOutputs =
189  comp->GetComputationOutputs();
190 
191  // Add all the scene inputs to the value store
192  std::vector<double> times;
193  for (TfToken const& input : sceneInputNames) {
194  auto &samples = (*valueStore)[input];
195  sceneDelegate->SampleExtComputationInput(compId, input, &samples);
196 
197  for (size_t i = 0; i < samples.count; ++i)
198  times.push_back(samples.times[i]);
199  }
200 
201  if (comp->IsInputAggregation()) {
202  // An aggregator computation produces no output, and thus
203  // doesn't need to be executed.
204  continue;
205  }
206 
207  // Also find all the time samples from the computed inputs.
208  for (auto const& computedInput : compInputs) {
209  auto const& samples =
210  valueStore->at(computedInput.sourceComputationOutputName);
211  for (size_t i = 0; i < samples.count; ++i) {
212  times.push_back(samples.times[i]);
213  }
214  }
215 
216  // Determine the time samples to evaluate the computation at.
217  _LimitTimeSamples(maxSampleCount, &times);
218 
219  // Allocate enough space for the evaluated outputs.
220  for (const TfToken &name : comp->GetOutputNames())
221  {
222  auto &output_samples = (*valueStore)[name];
223  output_samples.Resize(times.size());
224  output_samples.count = 0;
225  }
226 
227  TfSmallVector<VtValue, CAPACITY> sceneInputValues;
228  sceneInputValues.reserve(sceneInputNames.size());
229 
230  TfSmallVector<VtValue, CAPACITY> compInputValues;
231  compInputValues.reserve(compInputs.size());
232 
233  TfSmallVector<VtValue, CAPACITY> compOutputValues;
234 
235  // Evaluate the computation for each time sample.
236  for (double t : times) {
237 
238  // Retrieve all the inputs (scene, computed) from the value store,
239  // resampled to the required time.
240  sceneInputValues.clear();
241  for (auto const& sceneInput : comp->GetSceneInputNames()) {
242  auto const& samples = valueStore->at(sceneInput);
243  sceneInputValues.push_back(samples.Resample(t));
244  }
245 
246  compInputValues.clear();
247  for (auto const& computedInput : compInputs) {
248  auto const& samples =
249  valueStore->at(computedInput.sourceComputationOutputName);
250  compInputValues.push_back(samples.Resample(t));
251  }
252 
253  compOutputValues.resize(compOutputs.size());
254  if (!_InvokeComputation(*sceneDelegate, *comp,
255  TfMakeSpan(sceneInputValues),
256  TfMakeSpan(compInputValues),
257  TfMakeSpan(compOutputValues))) {
258  // We could bail here, or choose to execute other computations.
259  // Choose the latter.
260  continue;
261  }
262 
263  // Add outputs to the value store (subsequent computations may need
264  // them as computation inputs)
265  for (size_t i = 0; i < compOutputValues.size(); ++i) {
266  auto &output_samples = (*valueStore)[compOutputs[i].name];
267 
268  output_samples.times[output_samples.count] = t;
269  output_samples.values[output_samples.count] =
270  std::move(compOutputValues[i]);
271  ++output_samples.count;
272  }
273  }
274 
275  } // for each computation
276 }
277 
278 PXR_NAMESPACE_CLOSE_SCOPE
279 
280 #endif // PXR_IMAGING_HD_EXT_COMPUTATION_UTILS_H
Hydra Representation of a Client defined computation.
void reserve(size_type newCapacity)
Reserve storage for newCapacity entries.
Definition: smallVector.h:443
This is a small-vector class with local storage optimization, the local storage can be specified via ...
Definition: smallVector.h:177
Functor to use for hash maps from tokens to other things.
Definition: token.h:166
virtual HD_API size_t SampleExtComputationInput(SdfPath const &computationId, TfToken const &input, size_t maxSampleCount, float *sampleTimes, VtValue *sampleValues)
Return up to maxSampleCount samples for a given computation id and input token.
Token for efficient comparison, assignment, and hashing of known strings.
Definition: token.h:87
void resize(size_type newSize, const value_type &v=value_type())
Resize the vector to newSize and insert copies of \v.
Definition: smallVector.h:454
Represents a range of contiguous elements.
Definition: span.h:87
Adapter class providing data exchange with the client scene graph.
void clear()
Clear the entries in the vector.
Definition: smallVector.h:474
std::vector< TfToken > TfTokenVector
Convenience types.
Definition: token.h:442
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:290
void push_back(const value_type &v)
Copy an entry to the back of the vector,.
Definition: smallVector.h:511
size_type size() const
Returns the current size of the vector.
Definition: smallVector.h:629
TfSpan< typename Container::value_type > TfMakeSpan(Container &cont)
Helper for constructing a non-const TfSpan from a container.
Definition: span.h:241
TfToken class for efficient string referencing and hashing, plus conversions to and from stl string c...