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_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
8#define EXT_RMANPKG_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_GPRIM_H
9
10#include "pxr/pxr.h"
11#include "pxr/imaging/hd/version.h"
12#include "pxr/usd/sdf/types.h"
14
15#include "hdPrman/gprimbase.h"
16#include "hdPrman/renderParam.h"
17#include "hdPrman/instancer.h"
18#include "hdPrman/material.h"
19#include "hdPrman/rixStrings.h"
20#include "hdPrman/utils.h"
21
22#include "Riley.h"
23
24PXR_NAMESPACE_OPEN_SCOPE
25
28template <typename BASE>
29class HdPrman_Gprim : public BASE, public HdPrman_GprimBase
30{
31public:
32 using BaseType = BASE;
33
34 HdPrman_Gprim(SdfPath const& id)
35 : BaseType(id)
36 {
37 }
38
39 ~HdPrman_Gprim() override = default;
40
41 void
42 Finalize(HdRenderParam *renderParam) override
43 {
44 HdPrman_RenderParam *param =
45 static_cast<HdPrman_RenderParam*>(renderParam);
46 const SdfPath& id = BASE::GetId();
47 riley::Riley *riley = param->AcquireRiley();
48 if (!riley) {
49 return;
50 }
51
52 // Release retained conversions of coordSys bindings.
53 param->ReleaseCoordSysBindings(id);
54
55 // Delete instances before deleting the prototypes they use.
56 for (const auto &instId: _instanceIds) {
57 if (instId != riley::GeometryInstanceId::InvalidId()) {
58 riley->DeleteGeometryInstance(
59 riley::GeometryPrototypeId::InvalidId(), instId);
60 }
61 }
62 _instanceIds.clear();
63
64 // delete instances owned by the instancer.
65 if (HdPrmanInstancer* instancer = param->GetInstancer(
66 BASE::GetInstancerId())) {
67 instancer->Depopulate(renderParam, id);
68 }
69
70 for (const auto &protoId: _prototypeIds) {
71 if (protoId != riley::GeometryPrototypeId::InvalidId()) {
72 riley->DeleteGeometryPrototype(protoId);
73 }
74 }
75 _prototypeIds.clear();
76 }
77
78 void Sync(HdSceneDelegate* sceneDelegate,
79 HdRenderParam* renderParam,
80 HdDirtyBits* dirtyBits,
81 TfToken const &reprToken) override;
82
83protected:
84 HdDirtyBits GetInitialDirtyBitsMask() const override = 0;
85
86 HdDirtyBits
87 _PropagateDirtyBits(HdDirtyBits bits) const override
88 {
89 // By default, just return the same dirty bits we recieved.
90 return bits;
91 }
92
93 void
94 _InitRepr(TfToken const &reprToken,
95 HdDirtyBits *dirtyBits) override
96 {
97 TF_UNUSED(reprToken);
98 TF_UNUSED(dirtyBits);
99 // No-op
100 }
101
102 // We override this member function in mesh.cpp to support the creation
103 // of mesh light prototype geometry.
104 virtual bool
105 _PrototypeOnly()
106 {
107 return false;
108 }
109
110 // Provide a fallback material. Default grabs _fallbackMaterial
111 // from the context.
112 virtual riley::MaterialId
113 _GetFallbackMaterial(HdPrman_RenderParam *renderParam)
114 {
115 return renderParam->GetFallbackMaterialId();
116 }
117
118 // Populate primType, primvars, and geometry subsets.
119 // Returns true if successful.
120 virtual bool
121 _ConvertGeometry(
122 HdPrman_RenderParam *renderParam,
123 HdSceneDelegate *sceneDelegate,
124 const SdfPath &id,
125 RtUString *primType,
126 RtPrimVarList *primvars,
127 std::vector<HdGeomSubset> *geomSubsets,
128 std::vector<RtPrimVarList> *geomSubsetPrimvars) = 0;
129
130 // This class does not support copying.
131 HdPrman_Gprim(const HdPrman_Gprim&) = delete;
132 HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
133
134};
135
136template <typename BASE>
137void
139 HdRenderParam* renderParam,
140 HdDirtyBits* dirtyBits,
141 TfToken const &reprToken)
142{
143 HD_TRACE_FUNCTION();
144 HF_MALLOC_TAG_FUNCTION();
145 TF_UNUSED(reprToken);
146
147 // Check if there are any relevant dirtyBits.
148 // (See the HdChangeTracker::MarkRprimDirty() note regarding
149 // internalDirtyBits used for internal signaling in Hydra.)
150 static const HdDirtyBits internalDirtyBits =
151 HdChangeTracker::InitRepr |
152 HdChangeTracker::Varying |
153 HdChangeTracker::NewRepr |
154 HdChangeTracker::CustomBitsMask;
155 // HdPrman does not make use of the repr concept or customBits.
156 *dirtyBits &= ~(HdChangeTracker::NewRepr | HdChangeTracker::CustomBitsMask);
157 // If no relevant dirtyBits remain, return early to avoid acquiring write
158 // access to Riley, which requires a pause and restart of rendering.
159 if (((*dirtyBits & ~internalDirtyBits)
160 & HdChangeTracker::AllSceneDirtyBits) == 0) {
161 return;
162 }
163
164 HdPrman_RenderParam *param =
165 static_cast<HdPrman_RenderParam*>(renderParam);
166
167 // Riley API.
168 riley::Riley *riley = param->AcquireRiley();
169
170 // Update instance bindings.
171 BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
172
173 // Prim id
174 SdfPath const& id = BASE::GetId();
175 SdfPath const& instancerId = BASE::GetInstancerId();
176 const bool isHdInstance = !instancerId.IsEmpty();
177 SdfPath primPath = sceneDelegate->GetScenePrimPath(id, 0, nullptr);
178
179 // Prman has a default value for identifier:id of 0 (in case of ray miss),
180 // while Hydra treats id -1 as the clear value. We map Prman primId as
181 // (Hydra primId + 1) to get around this, here and in
182 // hdPrman/framebuffer.cpp.
183 const int32_t primId = BASE::GetPrimId() + 1;
184
185 // Sample transform
187 sceneDelegate->SampleTransform(id,
188#if HD_API_VERSION >= 68
189 param->GetShutterInterval()[0],
190 param->GetShutterInterval()[1],
191#endif
192 &xf);
193
194 // Update visibility so thet rprim->IsVisible() will work in render pass
195 if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
196 BASE::_UpdateVisibility(sceneDelegate, dirtyBits);
197 }
198
199 // Resolve material binding. Default to fallbackGprimMaterial.
200 if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
201#if HD_API_VERSION < 37
202 BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
203 sceneDelegate->GetMaterialId(id));
204#else
205 BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
206#endif
207 }
208 riley::MaterialId materialId = _GetFallbackMaterial(param);
209 riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
210 const SdfPath & hdMaterialId = BASE::GetMaterialId();
211 HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, riley, &materialId, &dispId);
212
213 // Convert (and cache) coordinate systems.
214 riley::CoordinateSystemList coordSysList = {0, nullptr};
215 if (HdPrman_RenderParam::RileyCoordSysIdVecRefPtr convertedCoordSys =
216 param->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
217 coordSysList.count = convertedCoordSys->size();
218 coordSysList.ids = convertedCoordSys->data();
219 }
220
221 // Hydra dirty bits corresponding to PRMan prototype attributes (also called
222 // "primitive variables" but not synonymous with USD primvars). See prman
223 // docs at https://rmanwiki.pixar.com/display/REN24/Primitive+Variables.
224 static const HdDirtyBits prmanProtoAttrBits =
225 HdChangeTracker::DirtyPoints |
226 HdChangeTracker::DirtyNormals |
227 HdChangeTracker::DirtyWidths |
228 HdChangeTracker::DirtyVolumeField |
229 HdChangeTracker::DirtyTopology |
230 HdChangeTracker::DirtyPrimvar;
231
232 // Hydra dirty bits corresponding to prman instance attributes. See prman
233 // docs at https://rmanwiki.pixar.com/display/REN24/Instance+Attributes.
234 static const HdDirtyBits prmanInstAttrBits =
235 HdChangeTracker::DirtyMaterialId |
236 HdChangeTracker::DirtyTransform |
237 HdChangeTracker::DirtyVisibility |
238 HdChangeTracker::DirtyDoubleSided |
239 HdChangeTracker::DirtySubdivTags |
240 HdChangeTracker::DirtyVolumeField |
241 HdChangeTracker::DirtyCategories |
242 HdChangeTracker::DirtyPrimvar;
243
244 // These two bitmasks intersect, so we check them against dirtyBits
245 // prior to clearing either mask.
246 const bool prmanProtoAttrBitsWereSet(*dirtyBits & prmanProtoAttrBits);
247 const bool prmanInstAttrBitsWereSet(*dirtyBits & prmanInstAttrBits);
248
249 //
250 // Create or modify Riley geometry prototype(s).
251 //
252 std::vector<riley::MaterialId> subsetMaterialIds;
253 std::vector<SdfPath> subsetPaths;
254 {
255 RtUString primType;
256 RtPrimVarList primvars;
257 HdGeomSubsets geomSubsets;
258 std::vector<RtPrimVarList> geomSubsetPrimvars;
259 bool ok = _ConvertGeometry(param, sceneDelegate, id,
260 &primType, &primvars,
261 &geomSubsets, &geomSubsetPrimvars);
262 if (!ok) {
263 // We expect a specific error will have already been issued.
264 return;
265 }
266
267 // identifier:object is useful for cryptomatte
268 primvars.SetString(RixStr.k_identifier_object,
269 RtUString(id.GetName().c_str()));
270 for (size_t i=0, n=geomSubsets.size(); i<n; ++i) {
271 primvars.SetString(RixStr.k_identifier_object,
272 RtUString(geomSubsets[i].id.GetName().c_str()));
273 }
274
275// In 2311 and beyond, we can use
276// HdPrman_PreviewSurfacePrimvarsSceneIndexPlugin.
277#if PXR_VERSION < 2311
278 // Transfer material opinions of primvars.
279 HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
280 primvars);
281#endif // PXR_VERSION < 2311
282
283 // Adjust _prototypeIds array.
284 const size_t oldCount = _prototypeIds.size();
285 const size_t newCount = std::max((size_t) 1, geomSubsets.size());
286 if (newCount != oldCount) {
287 for (const auto &oldPrototypeId: _prototypeIds) {
288 if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
289 riley->DeleteGeometryPrototype(oldPrototypeId);
290 }
291 }
292 _prototypeIds.resize(newCount,
293 riley::GeometryPrototypeId::InvalidId());
294 }
295
296 // Update Riley geom prototypes.
297 if (geomSubsets.empty()) {
298 // Common case: no subsets.
299 TF_VERIFY(newCount == 1);
300 TF_VERIFY(_prototypeIds.size() == 1);
301 primvars.SetString(RixStr.k_stats_prototypeIdentifier,
302 RtUString(primPath.GetText()));
303 if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
304 TRACE_SCOPE("riley::CreateGeometryPrototype");
305 _prototypeIds[0] = riley->CreateGeometryPrototype(
306 riley::UserId(
307 stats::AddDataLocation(primPath.GetText()).GetValue()),
308 primType, dispId, primvars);
309 } else if (prmanProtoAttrBitsWereSet) {
310 TRACE_SCOPE("riley::ModifyGeometryPrototype");
311 riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
312 &dispId, &primvars);
313 }
314 } else {
315 // Subsets case.
316 // We resolve materials here, and hold them in subsetMaterialIds:
317 // Displacement networks are passed to the geom prototype;
318 // material networks are passed to the instances.
319 subsetMaterialIds.reserve(geomSubsets.size());
320
321 // We also cache the subset paths for re-use when creating
322 // the instances
323 subsetPaths.reserve(geomSubsets.size());
324
325 for (size_t j=0; j < geomSubsets.size(); ++j) {
326 auto& prototypeId = _prototypeIds[j];
327 HdGeomSubset &subset = geomSubsets[j];
328 RtPrimVarList &subsetPrimvars = geomSubsetPrimvars[j];
329
330 // Convert indices to int32_t and set as k_shade_faceset.
331 std::vector<int32_t> int32Indices(subset.indices.cbegin(),
332 subset.indices.cend());
333 subsetPrimvars.SetIntegerArray(RixStr.k_shade_faceset,
334 int32Indices.data(),
335 int32Indices.size());
336 // Look up material override for the subset (if any)
337 riley::MaterialId subsetMaterialId = materialId;
338 riley::DisplacementId subsetDispId = dispId;
339 if (subset.materialId.IsEmpty()) {
340 subset.materialId = hdMaterialId;
341 }
342 HdPrman_ResolveMaterial(
343 sceneDelegate, subset.materialId,
344 riley, &subsetMaterialId, &subsetDispId);
345 subsetMaterialIds.push_back(subsetMaterialId);
346
347 // Look up the path for the subset
348 const SdfPath subsetPath =
349 sceneDelegate->GetScenePrimPath(subset.id, 0, nullptr);
350 subsetPaths.push_back(subsetPath);
351 subsetPrimvars.SetString(
352 RixStr.k_stats_prototypeIdentifier,
353 RtUString(subsetPath.GetText()));
354
355 if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
356 TRACE_SCOPE("riley::CreateGeometryPrototype");
357 prototypeId =
358 riley->CreateGeometryPrototype(
359 riley::UserId(
360 stats::AddDataLocation(
361 subsetPath.GetText()).GetValue()),
362 primType, subsetDispId, subsetPrimvars);
363 } else if (prmanProtoAttrBitsWereSet) {
364 TRACE_SCOPE("riley::ModifyGeometryPrototype");
365 riley->ModifyGeometryPrototype(
366 primType, prototypeId,
367 &subsetDispId, &subsetPrimvars);
368 }
369 }
370 }
371 *dirtyBits &= ~prmanProtoAttrBits;
372 }
373
374 //
375 // Stop here, or also create geometry instances?
376 //
377 if (_PrototypeOnly()) {
378 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
379 return;
380 }
381
382 //
383 // Create or modify Riley geometry instances.
384 //
385
386 // Resolve attributes.
387 RtParamList attrs = param->ConvertAttributes(sceneDelegate, id, true);
388
389 // Add "identifier:id" with the prim id.
390 attrs.SetInteger(RixStr.k_identifier_id, primId);
391
392 // user:__materialid is useful for cryptomatte
393 if(!hdMaterialId.IsEmpty()) {
394 attrs.SetString(RtUString("user:__materialid"), RtUString(hdMaterialId.GetText()));
395 }
396
397 if (!isHdInstance) {
398 // Simple case: Singleton instance.
399 // Convert transform.
401 for (size_t i=0; i < xf.count; ++i) {
402 xf_rt[i] = HdPrman_Utils::GfMatrixToRtMatrix(xf.values[i]);
403 }
404 const riley::Transform xform = {
405 unsigned(xf.count),
406 xf_rt.data(),
407 xf.times.data()};
408
409 // Add "identifier:id2" with the instance number.
410 // XXX Do we want to distinguish facesets here?
411 attrs.SetInteger(RixStr.k_identifier_id2, 0);
412
413 // Adjust _instanceIds array.
414 const size_t oldCount = _instanceIds.size();
415 const size_t newCount = _prototypeIds.size();
416 if (newCount != oldCount) {
417 for (const auto &oldInstanceId: _instanceIds) {
418 if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
419 riley->DeleteGeometryInstance(
420 riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
421 }
422 }
423 _instanceIds.resize(
424 newCount,
425 riley::GeometryInstanceId::InvalidId());
426 }
427
428 // Prepend renderTag to grouping:membership
429 param->AddRenderTagToGroupingMembership(
430 sceneDelegate->GetRenderTag(id), attrs);
431
432 // Create or modify Riley instances corresponding to a
433 // singleton Hydra instance.
434 TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
435 for (size_t j=0; j < _prototypeIds.size(); ++j) {
436 auto const& prototypeId = _prototypeIds[j];
437 auto& instanceId = _instanceIds[j];
438 auto instanceMaterialId = materialId;
439 RtParamList finalAttrs = attrs; // copy
440
441 // If a subset path was cached, use it. If not, use the prim path.
442 SdfPath* subsetPath(&primPath);
443 if (!subsetPaths.empty()) {
444 subsetPath = &subsetPaths[j];
445 }
446
447 finalAttrs.SetString(RixStr.k_identifier_name,
448 RtUString(subsetPath->GetText()));
449
450 // If a valid subset material was bound, use it.
451 if (!subsetMaterialIds.empty()) {
452 TF_VERIFY(j < subsetMaterialIds.size());
453 instanceMaterialId = subsetMaterialIds[j];
454 }
455
456 if (instanceId == riley::GeometryInstanceId::InvalidId()) {
457 TRACE_SCOPE("riley::CreateGeometryInstance");
458 instanceId = riley->CreateGeometryInstance(
459 riley::UserId(
460 stats::AddDataLocation(subsetPath->GetText()).GetValue()),
461 riley::GeometryPrototypeId::InvalidId(), prototypeId,
462 instanceMaterialId, coordSysList, xform, finalAttrs);
463 } else if (prmanInstAttrBitsWereSet) {
464 TRACE_SCOPE("riley::ModifyGeometryInstance");
465 riley->ModifyGeometryInstance(
466 riley::GeometryPrototypeId::InvalidId(),
467 instanceId, &instanceMaterialId, &coordSysList, &xform,
468 &finalAttrs);
469 }
470 }
471 *dirtyBits &= ~prmanInstAttrBits;
472 } else if (prmanInstAttrBitsWereSet
473 || HdChangeTracker::IsInstancerDirty(*dirtyBits, instancerId)) {
474 // This gprim is a prototype of a hydra instancer. (It is not itself an
475 // instancer because it is a gprim.) The riley geometry prototypes have
476 // already been synced above, and those are owned by this gprim instance.
477 // We need to tell the hdprman instancer to sync its riley instances for
478 // these riley prototypes.
479 //
480 // We won't make any riley instances here. The hdprman instancer will
481 // own the riley instances instead.
482 //
483 // We only need to do this if dirtyBits says the instancer is dirty.
484
485 HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
486
487 // first, sync the hydra instancer and its parents, from the bottom up.
488 // (note: this is transitional code, it should be done by the render index...)
489 HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
490
491 if (subsetMaterialIds.size() == 0) {
492 subsetMaterialIds.push_back(materialId);
493 }
494 if (subsetPaths.size() == 0) {
495 subsetPaths.push_back(primPath);
496 }
497 TF_VERIFY(_prototypeIds.size() == subsetMaterialIds.size() &&
498 _prototypeIds.size() == subsetPaths.size(),
499 "size mismatch (%lu, %lu, %lu)\n", _prototypeIds.size(),
500 subsetMaterialIds.size(), subsetPaths.size());
501
502 // XXX: To avoid a failed verify inside Populate(), we will check the
503 // prototype ids for validity here. We don't usually do this, relying on
504 // Riley to report invalid prototype ids on instance creation. But
505 // Populate() allows and expects an invalid prototype id when instancing
506 // lights, so doing this check here lets us make a more informative
507 // warning. HYD-3206
508 if (std::any_of(_prototypeIds.begin(), _prototypeIds.end(),
509 [](const auto& id){
510 return id == riley::GeometryPrototypeId::InvalidId();
511 })) {
512 TF_WARN("Riley geometry prototype creation failed for "
513 "instanced gprim <%s>; the prim will not be instanced.",
514 id.GetText());
515 } else {
516 // next, tell the hdprman instancer to sync the riley instances
517 HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
518 renderIndex.GetInstancer(instancerId));
519 if (instancer) {
520 instancer->Populate(
521 renderParam,
522 dirtyBits,
523 id,
524 _prototypeIds,
525 coordSysList,
526 attrs, xf,
527 subsetMaterialIds,
528 subsetPaths);
529 }
530 }
531 }
532 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
533}
534
535PXR_NAMESPACE_CLOSE_SCOPE
536
537#endif // EXT_RMANPKG_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:22
A mix-in template that adds shared gprim behavior to support various HdRprim types.
Definition: gprim.h:30
The Hydra render index is a flattened representation of the client scene graph, which may be composed...
Definition: renderIndex.h:105
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:157
Token for efficient comparison, assignment, and hashing of known strings.
Definition: token.h:71
#define TF_WARN(...)
Issue a warning, but continue execution.
Definition: diagnostic.h:132
#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.
Basic Sdf data types.