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