Loading...
Searching...
No Matches
gprim.h
1//
2// Copyright 2019 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef EXT_RMANPKG_25_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
8#define EXT_RMANPKG_25_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
9
10#include "pxr/pxr.h"
11#include "pxr/usd/sdf/types.h"
13
14#include "hdPrman/gprimbase.h"
15#include "hdPrman/renderParam.h"
16#include "hdPrman/instancer.h"
17#include "hdPrman/material.h"
18#include "hdPrman/rixStrings.h"
19#include "hdPrman/utils.h"
20
21#include "Riley.h"
22
23PXR_NAMESPACE_OPEN_SCOPE
24
27template <typename BASE>
28class HdPrman_Gprim : public BASE, public HdPrman_GprimBase
29{
30public:
31 using BaseType = BASE;
32
33 HdPrman_Gprim(SdfPath const& id)
34 : BaseType(id)
35 {
36 }
37
38 ~HdPrman_Gprim() override = default;
39
40 void
41 Finalize(HdRenderParam *renderParam) override
42 {
43 HdPrman_RenderParam *param =
44 static_cast<HdPrman_RenderParam*>(renderParam);
45 const SdfPath& id = BASE::GetId();
46 riley::Riley *riley = param->AcquireRiley();
47
48 // Release retained conversions of coordSys bindings.
49 param->ReleaseCoordSysBindings(id);
50
51 // Delete instances before deleting the prototypes they use.
52 for (const auto &instId: _instanceIds) {
53 if (instId != riley::GeometryInstanceId::InvalidId()) {
54 riley->DeleteGeometryInstance(
55 riley::GeometryPrototypeId::InvalidId(), instId);
56 }
57 }
58 _instanceIds.clear();
59
60 // delete instances owned by the instancer.
61 if (HdPrmanInstancer* instancer = param->GetInstancer(
62 BASE::GetInstancerId())) {
63 instancer->Depopulate(renderParam, id);
64 }
65
66 for (const auto &protoId: _prototypeIds) {
67 if (protoId != riley::GeometryPrototypeId::InvalidId()) {
68 riley->DeleteGeometryPrototype(protoId);
69 }
70 }
71 _prototypeIds.clear();
72 }
73
74 void Sync(HdSceneDelegate* sceneDelegate,
75 HdRenderParam* renderParam,
76 HdDirtyBits* dirtyBits,
77 TfToken const &reprToken) override;
78
79protected:
80 HdDirtyBits GetInitialDirtyBitsMask() const override = 0;
81
82 HdDirtyBits
83 _PropagateDirtyBits(HdDirtyBits bits) const override
84 {
85 // By default, just return the same dirty bits we recieved.
86 return bits;
87 }
88
89 void
90 _InitRepr(TfToken const &reprToken,
91 HdDirtyBits *dirtyBits) override
92 {
93 TF_UNUSED(reprToken);
94 TF_UNUSED(dirtyBits);
95 // No-op
96 }
97
98 // We override this member function in mesh.cpp to support the creation
99 // of mesh light prototype geometry.
100 virtual bool
101 _PrototypeOnly()
102 {
103 return false;
104 }
105
106 // Provide a fallback material. Default grabs _fallbackMaterial
107 // from the context.
108 virtual riley::MaterialId
109 _GetFallbackMaterial(HdPrman_RenderParam *renderParam)
110 {
111 return renderParam->GetFallbackMaterialId();
112 }
113
114 // Populate primType and primvars.
115 virtual RtPrimVarList
116 _ConvertGeometry(HdPrman_RenderParam *renderParam,
117 HdSceneDelegate *sceneDelegate,
118 const SdfPath &id,
119 RtUString *primType,
120 std::vector<HdGeomSubset> *geomSubsets) = 0;
121
122 // This class does not support copying.
123 HdPrman_Gprim(const HdPrman_Gprim&) = delete;
124 HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
125
126};
127
128template <typename BASE>
129void
131 HdRenderParam* renderParam,
132 HdDirtyBits* dirtyBits,
133 TfToken const &reprToken)
134{
135 HD_TRACE_FUNCTION();
136 HF_MALLOC_TAG_FUNCTION();
137 TF_UNUSED(reprToken);
138
139 // Check if there are any relevant dirtyBits.
140 // (See the HdChangeTracker::MarkRprimDirty() note regarding
141 // internalDirtyBits used for internal signaling in Hydra.)
142 static const HdDirtyBits internalDirtyBits =
143 HdChangeTracker::InitRepr |
144 HdChangeTracker::Varying |
145 HdChangeTracker::NewRepr |
146 HdChangeTracker::CustomBitsMask;
147 // HdPrman does not make use of the repr concept or customBits.
148 *dirtyBits &= ~(HdChangeTracker::NewRepr | HdChangeTracker::CustomBitsMask);
149 // If no relevant dirtyBits remain, return early to avoid acquiring write
150 // access to Riley, which requires a pause and restart of rendering.
151 if (((*dirtyBits & ~internalDirtyBits)
152 & HdChangeTracker::AllSceneDirtyBits) == 0) {
153 return;
154 }
155
156 HdPrman_RenderParam *param =
157 static_cast<HdPrman_RenderParam*>(renderParam);
158
159 // Riley API.
160 riley::Riley *riley = param->AcquireRiley();
161
162 // Update instance bindings.
163 BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
164
165 // Prim id
166 SdfPath const& id = BASE::GetId();
167 SdfPath const& instancerId = BASE::GetInstancerId();
168 const bool isHdInstance = !instancerId.IsEmpty();
169 SdfPath primPath = sceneDelegate->GetScenePrimPath(id, 0, nullptr);
170
171 // Prman has a default value for identifier:id of 0 (in case of ray miss),
172 // while Hydra treats id -1 as the clear value. We map Prman primId as
173 // (Hydra primId + 1) to get around this, here and in
174 // hdPrman/framebuffer.cpp.
175 const int32_t primId = BASE::GetPrimId() + 1;
176
177 // Sample transform
179 sceneDelegate->SampleTransform(id, &xf);
180
181 // Update visibility so thet rprim->IsVisible() will work in render pass
182 if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
183 BASE::_UpdateVisibility(sceneDelegate, dirtyBits);
184 }
185
186 // Resolve material binding. Default to fallbackGprimMaterial.
187 if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
188#if HD_API_VERSION < 37
189 BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
190 sceneDelegate->GetMaterialId(id));
191#else
192 BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
193#endif
194 }
195 riley::MaterialId materialId = _GetFallbackMaterial(param);
196 riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
197 const SdfPath & hdMaterialId = BASE::GetMaterialId();
198 HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, riley, &materialId, &dispId);
199
200 // Convert (and cache) coordinate systems.
201 riley::CoordinateSystemList coordSysList = {0, nullptr};
202 if (HdPrman_RenderParam::RileyCoordSysIdVecRefPtr convertedCoordSys =
203 param->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
204 coordSysList.count = convertedCoordSys->size();
205 coordSysList.ids = convertedCoordSys->data();
206 }
207
208 // Hydra dirty bits corresponding to PRMan prototype attributes (also called
209 // "primitive variables" but not synonymous with USD primvars). See prman
210 // docs at https://rmanwiki.pixar.com/display/REN24/Primitive+Variables.
211 static const HdDirtyBits prmanProtoAttrBits =
212 HdChangeTracker::DirtyPoints |
213 HdChangeTracker::DirtyNormals |
214 HdChangeTracker::DirtyWidths |
215 HdChangeTracker::DirtyTopology;
216
217 // Hydra dirty bits corresponding to prman instance attributes. See prman
218 // docs at https://rmanwiki.pixar.com/display/REN24/Instance+Attributes.
219 static const HdDirtyBits prmanInstAttrBits =
220 HdChangeTracker::DirtyMaterialId |
221 HdChangeTracker::DirtyTransform |
222 HdChangeTracker::DirtyVisibility |
223 HdChangeTracker::DirtyDoubleSided |
224 HdChangeTracker::DirtySubdivTags |
225 HdChangeTracker::DirtyVolumeField |
226 HdChangeTracker::DirtyCategories |
227 HdChangeTracker::DirtyPrimvar;
228
229 //
230 // Create or modify Riley geometry prototype(s).
231 //
232 std::vector<riley::MaterialId> subsetMaterialIds;
233 std::vector<SdfPath> subsetPaths;
234 {
235 RtUString primType;
236 HdGeomSubsets geomSubsets;
237 RtPrimVarList primvars = _ConvertGeometry(param, sceneDelegate, id,
238 &primType, &geomSubsets);
239
240 // Transfer material opinions of primvars.
241 HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
242 primvars);
243
244 // Adjust _prototypeIds array.
245 const size_t oldCount = _prototypeIds.size();
246 const size_t newCount = std::max((size_t) 1, geomSubsets.size());
247 if (newCount != oldCount) {
248 for (const auto &oldPrototypeId: _prototypeIds) {
249 if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
250 riley->DeleteGeometryPrototype(oldPrototypeId);
251 }
252 }
253 _prototypeIds.resize(newCount,
254 riley::GeometryPrototypeId::InvalidId());
255 }
256
257 // Update Riley geom prototypes.
258 if (geomSubsets.empty()) {
259 // Common case: no subsets.
260 TF_VERIFY(newCount == 1);
261 TF_VERIFY(_prototypeIds.size() == 1);
262 primvars.SetString(RixStr.k_stats_prototypeIdentifier,
263 RtUString(primPath.GetText()));
264 if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
265 TRACE_SCOPE("riley::CreateGeometryPrototype");
266 _prototypeIds[0] = riley->CreateGeometryPrototype(
267 riley::UserId(
268 stats::AddDataLocation(primPath.GetText()).GetValue()),
269 primType, dispId, primvars);
270 } else if (*dirtyBits & prmanProtoAttrBits) {
271 TRACE_SCOPE("riley::ModifyGeometryPrototype");
272 riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
273 &dispId, &primvars);
274 }
275 } else {
276 // Subsets case.
277 // We resolve materials here, and hold them in subsetMaterialIds:
278 // Displacement networks are passed to the geom prototype;
279 // material networks are passed to the instances.
280 subsetMaterialIds.reserve(geomSubsets.size());
281
282 // We also cache the subset paths for re-use when creating the instances
283 subsetPaths.reserve(geomSubsets.size());
284
285 for (size_t j=0; j < geomSubsets.size(); ++j) {
286 auto& prototypeId = _prototypeIds[j];
287 HdGeomSubset &subset = geomSubsets[j];
288
289 // Convert indices to int32_t and set as k_shade_faceset.
290 std::vector<int32_t> int32Indices(subset.indices.cbegin(),
291 subset.indices.cend());
292 primvars.SetIntegerArray(RixStr.k_shade_faceset,
293 int32Indices.data(),
294 int32Indices.size());
295 // Look up material override for the subset (if any)
296 riley::MaterialId subsetMaterialId = materialId;
297 riley::DisplacementId subsetDispId = dispId;
298 if (subset.materialId.IsEmpty()) {
299 subset.materialId = hdMaterialId;
300 }
301 HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
302 riley, &subsetMaterialId, &subsetDispId);
303 subsetMaterialIds.push_back(subsetMaterialId);
304
305 // Look up the path for the subset
306 SdfPath subsetPath = sceneDelegate->GetScenePrimPath(subset.id, 0, nullptr);
307 subsetPaths.push_back(subsetPath);
308 primvars.SetString(RixStr.k_stats_prototypeIdentifier,
309 RtUString(subsetPath.GetText()));
310
311 if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
312 TRACE_SCOPE("riley::CreateGeometryPrototype");
313 prototypeId =
314 riley->CreateGeometryPrototype(
315 riley::UserId(
316 stats::AddDataLocation(subsetPath.GetText()).GetValue()),
317 primType, dispId, primvars);
318 } else if (*dirtyBits & prmanProtoAttrBits) {
319 TRACE_SCOPE("riley::ModifyGeometryPrototype");
320 riley->ModifyGeometryPrototype(primType, prototypeId,
321 &subsetDispId, &primvars);
322 }
323 }
324 }
325 *dirtyBits &= ~prmanProtoAttrBits;
326 }
327
328 //
329 // Stop here, or also create geometry instances?
330 //
331 if (_PrototypeOnly()) {
332 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
333 return;
334 }
335
336 //
337 // Create or modify Riley geometry instances.
338 //
339
340 // Resolve attributes.
341 RtParamList attrs = param->ConvertAttributes(sceneDelegate, id, true);
342
343 // Add "identifier:id" with the prim id.
344 attrs.SetInteger(RixStr.k_identifier_id, primId);
345
346 if (!isHdInstance) {
347 // Simple case: Singleton instance.
348 // Convert transform.
350 for (size_t i=0; i < xf.count; ++i) {
351 xf_rt[i] = HdPrman_Utils::GfMatrixToRtMatrix(xf.values[i]);
352 }
353 const riley::Transform xform = {
354 unsigned(xf.count),
355 xf_rt.data(),
356 xf.times.data()};
357
358 // Add "identifier:id2" with the instance number.
359 // XXX Do we want to distinguish facesets here?
360 attrs.SetInteger(RixStr.k_identifier_id2, 0);
361
362 // Adjust _instanceIds array.
363 const size_t oldCount = _instanceIds.size();
364 const size_t newCount = _prototypeIds.size();
365 if (newCount != oldCount) {
366 for (const auto &oldInstanceId: _instanceIds) {
367 if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
368 riley->DeleteGeometryInstance(
369 riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
370 }
371 }
372 _instanceIds.resize(
373 newCount,
374 riley::GeometryInstanceId::InvalidId());
375 }
376
377 // Create or modify Riley instances corresponding to a
378 // singleton Hydra instance.
379 TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
380 for (size_t j=0; j < _prototypeIds.size(); ++j) {
381 auto const& prototypeId = _prototypeIds[j];
382 auto& instanceId = _instanceIds[j];
383 auto instanceMaterialId = materialId;
384 RtParamList finalAttrs = attrs; // copy
385
386 // If a subset path was cached, use it. If not, use the prim path.
387 SdfPath* subsetPath(&primPath);
388 if (!subsetPaths.empty()) {
389 subsetPath = &subsetPaths[j];
390 }
391
392 finalAttrs.SetString(RixStr.k_identifier_name,
393 RtUString(subsetPath->GetText()));
394
395 // If a valid subset material was bound, use it.
396 if (!subsetMaterialIds.empty()) {
397 TF_VERIFY(j < subsetMaterialIds.size());
398 instanceMaterialId = subsetMaterialIds[j];
399 }
400
401 // Very last thing: prepend renderTag to grouping:membership
402 param->AddRenderTagToGroupingMembership(
403 sceneDelegate->GetRenderTag(id), finalAttrs);
404
405 if (instanceId == riley::GeometryInstanceId::InvalidId()) {
406 TRACE_SCOPE("riley::CreateGeometryInstance");
407 instanceId = riley->CreateGeometryInstance(
408 riley::UserId(
409 stats::AddDataLocation(subsetPath->GetText()).GetValue()),
410 riley::GeometryPrototypeId::InvalidId(), prototypeId,
411 instanceMaterialId, coordSysList, xform, finalAttrs);
412 } else if (*dirtyBits & prmanInstAttrBits) {
413 TRACE_SCOPE("riley::ModifyGeometryInstance");
414 riley->ModifyGeometryInstance(
415 riley::GeometryPrototypeId::InvalidId(),
416 instanceId, &instanceMaterialId, &coordSysList, &xform,
417 &finalAttrs);
418 }
419 }
420 *dirtyBits &= ~prmanInstAttrBits;
421 } else if ((*dirtyBits & prmanInstAttrBits)
422 || HdChangeTracker::IsInstancerDirty(*dirtyBits, instancerId)) {
423 // This gprim is a prototype of a hydra instancer. (It is not itself an
424 // instancer because it is a gprim.) The riley geometry prototypes have
425 // already been synced above, and those are owned by this gprim instance.
426 // We need to tell the hdprman instancer to sync its riley instances for
427 // these riley prototypes.
428 //
429 // We won't make any riley instances here. The hdprman instancer will
430 // own the riley instances instead.
431 //
432 // We only need to do this if dirtyBits says the instancer is dirty.
433
434 HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
435
436 // first, sync the hydra instancer and its parents, from the bottom up.
437 // (note: this is transitional code, it should be done by the render index...)
438 HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
439
440 if (subsetMaterialIds.size() == 0) {
441 subsetMaterialIds.push_back(materialId);
442 }
443 if (subsetPaths.size() == 0) {
444 subsetPaths.push_back(primPath);
445 }
446 TF_VERIFY(_prototypeIds.size() == subsetMaterialIds.size() &&
447 _prototypeIds.size() == subsetPaths.size(),
448 "size mismatch (%lu, %lu, %lu)\n", _prototypeIds.size(),
449 subsetMaterialIds.size(), subsetPaths.size());
450
451 // next, tell the hdprman instancer to sync the riley instances
452 HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
453 renderIndex.GetInstancer(instancerId));
454 if (instancer) {
455 instancer->Populate(
456 renderParam,
457 dirtyBits,
458 id,
459 _prototypeIds,
460 coordSysList,
461 attrs, xf,
462 subsetMaterialIds,
463 subsetPaths);
464 }
465 }
466 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
467}
468
469PXR_NAMESPACE_CLOSE_SCOPE
470
471#endif // EXT_RMANPKG_25_0_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
Tracks changes from the HdSceneDelegate, providing invalidation cues to the render engine.
Definition: changeTracker.h:35
static HD_API bool IsInstancerDirty(HdDirtyBits dirtyBits, SdfPath const &id)
Returns true if the dirtyBits has a dirty instancer. id is for perflog.
HD_API bool IsVisibilityDirty(SdfPath const &id)
Returns true if the rprim identified by id has dirty visibility.
A common base class for HdPrman_Gprim types.
Definition: gprimbase.h:21
A mix-in template that adds shared gprim behavior to support various HdRprim types.
Definition: gprim.h:29
The Hydra render index is a flattened representation of the client scene graph, which may be composed...
Definition: renderIndex.h:104
HD_API HdInstancer * GetInstancer(SdfPath const &id) const
Returns the instancer of id.
The HdRenderParam is an opaque (to core Hydra) handle, to an object that is obtained from the render ...
Adapter class providing data exchange with the client scene graph.
virtual HD_API TfToken GetRenderTag(SdfPath const &id)
Returns the render tag that will be used to bucket prims during render pass bucketing.
virtual HD_API SdfPath GetMaterialId(SdfPath const &rprimId)
Returns the material ID bound to the rprim rprimId.
virtual HD_API size_t SampleTransform(SdfPath const &id, size_t maxSampleCount, float *sampleTimes, GfMatrix4d *sampleValues)
Store up to maxSampleCount transform samples in *sampleValues.
HdRenderIndex & GetRenderIndex()
Returns the RenderIndex owned by this delegate.
virtual HD_API SdfPath GetScenePrimPath(SdfPath const &rprimId, int instanceIndex, HdInstancerContext *instancerContext=nullptr)
Returns the scene address of the prim corresponding to the given rprim/instance index.
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:274
SDF_API const char * GetText() const
Returns the string representation of this path as a c string.
bool IsEmpty() const noexcept
Returns true if this is the empty path (SdfPath::EmptyPath()).
Definition: path.h:398
This is a small-vector class with local storage optimization, the local storage can be specified via ...
Definition: smallVector.h:155
value_type * data()
Direct access to the underlying array.
Definition: smallVector.h:733
Token for efficient comparison, assignment, and hashing of known strings.
Definition: token.h:71
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:266
#define TF_UNUSED(x)
Stops compiler from producing unused argument or variable warnings.
Definition: tf.h:168
Describes a subset of a piece of geometry as a set of indices.
Definition: geomSubset.h:23
VtIntArray indices
The list of element indices contained in the subset.
Definition: geomSubset.h:39
SdfPath id
The path used to identify this subset in the scene.
Definition: geomSubset.h:35
SdfPath materialId
The path used to identify this material bound to the subset.
Definition: geomSubset.h:37
An array of a value sampled over time, in struct-of-arrays layout.
#define TRACE_SCOPE(name)
Records a timestamp when constructed and a timespan event when destructed, using name as the key.
Definition: trace.h:31
Basic Sdf data types.