1 //
2 // Copyright 2018 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 
25 #include "pxr/imaging/hdSt/extComputation.h"
26 #include "pxr/imaging/hdSt/primUtils.h"
27 #include "pxr/imaging/hdSt/resourceRegistry.h"
28 #include "pxr/imaging/hdSt/renderParam.h"
29 #include "pxr/imaging/hd/extComputationContext.h"
30 #include "pxr/imaging/hd/compExtCompInputSource.h"
31 #include "pxr/imaging/hd/perfLog.h"
32 #include "pxr/imaging/hd/sceneDelegate.h"
33 #include "pxr/imaging/hd/vtBufferSource.h"
34 
35 #include "pxr/base/arch/hash.h"
36 
37 PXR_NAMESPACE_OPEN_SCOPE
38 
HdStExtComputation(SdfPath const & id)39 HdStExtComputation::HdStExtComputation(SdfPath const &id)
40  : HdExtComputation(id)
41  , _inputRange()
42 {
43 }
44 
45 HdStExtComputation::~HdStExtComputation() = default;
46 
47 //
48 // De-duplicating and sharing of ExtComputation data.
49 //
50 // This is similar to sharing of primvar data. We identify
51 // data by computing a hash of the sources of the data. For
52 // now, buffer data allocated here is read-only and is never
53 // mutated. If that changes, then we will have to deal with
54 // migrating shared data to a non-shared buffer so that it
55 // can be modified safely.
56 //
57 static uint64_t
_ComputeSharedComputationInputId(uint64_t baseId,HdBufferSourceSharedPtrVector const & sources)58 _ComputeSharedComputationInputId(uint64_t baseId,
59                                  HdBufferSourceSharedPtrVector const &sources)
60 {
61     size_t inputId = baseId;
62     for (HdBufferSourceSharedPtr const &bufferSource : sources) {
63         size_t sourceId = bufferSource->ComputeHash();
64         inputId = ArchHash64((const char*)&sourceId,
65                                sizeof(sourceId), inputId);
66     }
67     return inputId;
68 }
69 
70 static HdBufferArrayRangeSharedPtr
_AllocateComputationDataRange(HdBufferSourceSharedPtrVector && inputs,HdStResourceRegistrySharedPtr const & resourceRegistry)71 _AllocateComputationDataRange(
72         HdBufferSourceSharedPtrVector && inputs,
73         HdStResourceRegistrySharedPtr const & resourceRegistry)
74 {
75     HdBufferSpecVector bufferSpecs;
76     HdBufferSpec::GetBufferSpecs(inputs, &bufferSpecs);
77 
78     HdBufferArrayRangeSharedPtr inputRange =
79         resourceRegistry->AllocateShaderStorageBufferArrayRange(
80                                               HdPrimTypeTokens->extComputation,
81                                               bufferSpecs,
82                                               HdBufferArrayUsageHint());
83     resourceRegistry->AddSources(inputRange, std::move(inputs));
84 
85     return inputRange;
86 }
87 
88 void
Sync(HdSceneDelegate * sceneDelegate,HdRenderParam * renderParam,HdDirtyBits * dirtyBits)89 HdStExtComputation::Sync(HdSceneDelegate *sceneDelegate,
90                          HdRenderParam   *renderParam,
91                          HdDirtyBits     *dirtyBits)
92 {
93     HD_TRACE_FUNCTION();
94     HF_MALLOC_TAG_FUNCTION();
95 
96     HdExtComputation::_Sync(sceneDelegate, renderParam, dirtyBits);
97 
98     TF_DEBUG(HD_EXT_COMPUTATION_UPDATED).Msg(
99         "HdStExtComputation::Sync for %s (dirty bits = 0x%x)\n",
100         GetId().GetText(), *dirtyBits);
101 
102     // During Sprim sync, we only commit GPU resources when directly executing a
103     // GPU computation or when aggregating inputs for a downstream computation.
104     // Note: For CPU computations, we pull the inputs when we create the
105     // HdExtCompCpuComputation, which happens during Rprim sync.
106     if (GetGpuKernelSource().empty() && !IsInputAggregation()) {
107         return;
108     }
109 
110     if (!(*dirtyBits & DirtySceneInput)) {
111         // No scene inputs to sync. All other computation dirty bits (barring
112         // DirtyCompInput) are sync'd in HdExtComputation::_Sync.
113         return;
114     }
115 
116     HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
117     HdStResourceRegistrySharedPtr const & resourceRegistry =
118         std::dynamic_pointer_cast<HdStResourceRegistry>(
119                               renderIndex.GetResourceRegistry());
120 
121     HdBufferSourceSharedPtrVector inputs;
122     for (TfToken const & inputName: GetSceneInputNames()) {
123         VtValue inputValue = sceneDelegate->GetExtComputationInput(
124                                                 GetId(), inputName);
125         size_t arraySize =
126             inputValue.IsArrayValued() ? inputValue.GetArraySize() : 1;
127         HdBufferSourceSharedPtr inputSource = std::make_shared<HdVtBufferSource>
128             (inputName, inputValue, arraySize);
129         if (inputSource->IsValid()) {
130             inputs.push_back(inputSource);
131         } else {
132             TF_WARN("Unsupported type %s for source %s in extComputation %s.",
133                     inputValue.GetType().GetTypeName().c_str(),
134                     inputName.GetText(), GetId().GetText());
135         }
136     }
137 
138     // Store the current range to know if garbage collection  is necessary.
139     HdBufferArrayRangeSharedPtr const prevRange = _inputRange;
140 
141     if (!inputs.empty()) {
142         if (_IsEnabledSharedExtComputationData() && IsInputAggregation()) {
143             uint64_t inputId = _ComputeSharedComputationInputId(0, inputs);
144 
145             HdInstance<HdBufferArrayRangeSharedPtr> barInstance =
146                 resourceRegistry->RegisterExtComputationDataRange(inputId);
147 
148             if (barInstance.IsFirstInstance()) {
149                 // Allocate the first buffer range for this input key
150                 _inputRange = _AllocateComputationDataRange(std::move(inputs),
151                                                             resourceRegistry);
152                 barInstance.SetValue(_inputRange);
153 
154                 TF_DEBUG(HD_SHARED_EXT_COMPUTATION_DATA).Msg(
155                     "Allocated shared ExtComputation buffer range: %s: %p\n",
156                     GetId().GetText(), (void *)_inputRange.get());
157             } else {
158                 // Share the existing buffer range for this input key
159                 _inputRange = barInstance.GetValue();
160 
161                 TF_DEBUG(HD_SHARED_EXT_COMPUTATION_DATA).Msg(
162                     "Reused shared ExtComputation buffer range: %s: %p\n",
163                     GetId().GetText(), (void *)_inputRange.get());
164             }
165 
166         } else {
167             // We're not sharing.
168 
169             // We don't yet have the ability to track dirtiness per scene input.
170             // Each time DirtySceneInput is set, we pull and upload _all_ the
171             // scene inputs.
172             // This means that BAR migration isn't necessary, and so we avoid
173             // using the Update*BufferArrayRange flavor of methods in
174             // HdStResourceRegistry and handle allocation/upload manually.
175 
176             if (!_inputRange || !_inputRange->IsValid()) {
177                 // Allocate a new BAR if we haven't already.
178                 _inputRange = _AllocateComputationDataRange(
179                     std::move(inputs), resourceRegistry);
180                 TF_DEBUG(HD_SHARED_EXT_COMPUTATION_DATA).Msg(
181                     "Allocated unshared ExtComputation buffer range: %s: %p\n",
182                     GetId().GetText(), (void *)_inputRange.get());
183 
184             } else {
185                 HdBufferSpecVector inputSpecs;
186                 HdBufferSpec::GetBufferSpecs(inputs, &inputSpecs);
187                 HdBufferSpecVector barSpecs;
188                 _inputRange->GetBufferSpecs(&barSpecs);
189 
190                 bool useExistingRange =
191                     HdBufferSpec::IsSubset(/*subset*/inputSpecs,
192                                            /*superset*/barSpecs);
193                 if (useExistingRange) {
194                     resourceRegistry->AddSources(
195                         _inputRange, std::move(inputs));
196 
197                     TF_DEBUG(HD_SHARED_EXT_COMPUTATION_DATA).Msg(
198                         "Reused unshared ExtComputation buffer range: "
199                         "%s: %p\n",
200                         GetId().GetText(), (void *)_inputRange.get());
201 
202                 } else {
203                     _inputRange = _AllocateComputationDataRange(
204                         std::move(inputs), resourceRegistry);
205                     TF_DEBUG(HD_SHARED_EXT_COMPUTATION_DATA).Msg(
206                         "Couldn't reuse existing unshared range. Allocated a "
207                         "new one.%s: %p\n",
208                         GetId().GetText(), (void *)_inputRange.get());
209                 }
210             }
211         }
212 
213         if (prevRange && (prevRange != _inputRange)) {
214             // Make sure that we also release any stale input range data
215             HdStMarkGarbageCollectionNeeded(renderParam);
216         }
217     }
218 
219     *dirtyBits &= ~DirtySceneInput;
220 }
221 
222 void
Finalize(HdRenderParam * renderParam)223 HdStExtComputation::Finalize(HdRenderParam *renderParam)
224 {
225     // Release input range data.
226     HdStMarkGarbageCollectionNeeded(renderParam);
227 }
228 
229 PXR_NAMESPACE_CLOSE_SCOPE
230