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 and primvars.
119 virtual RtPrimVarList
120 _ConvertGeometry(HdPrman_RenderParam *renderParam,
121 HdSceneDelegate *sceneDelegate,
122 const SdfPath &id,
123 RtUString *primType,
124 std::vector<HdGeomSubset> *geomSubsets) = 0;
125
126 // This class does not support copying.
127 HdPrman_Gprim(const HdPrman_Gprim&) = delete;
128 HdPrman_Gprim &operator =(const HdPrman_Gprim&) = delete;
129
130};
131
132template <typename BASE>
133void
135 HdRenderParam* renderParam,
136 HdDirtyBits* dirtyBits,
137 TfToken const &reprToken)
138{
139 HD_TRACE_FUNCTION();
140 HF_MALLOC_TAG_FUNCTION();
141 TF_UNUSED(reprToken);
142
143 // Check if there are any relevant dirtyBits.
144 // (See the HdChangeTracker::MarkRprimDirty() note regarding
145 // internalDirtyBits used for internal signaling in Hydra.)
146 static const HdDirtyBits internalDirtyBits =
147 HdChangeTracker::InitRepr |
148 HdChangeTracker::Varying |
149 HdChangeTracker::NewRepr |
150 HdChangeTracker::CustomBitsMask;
151 // HdPrman does not make use of the repr concept or customBits.
152 *dirtyBits &= ~(HdChangeTracker::NewRepr | HdChangeTracker::CustomBitsMask);
153 // If no relevant dirtyBits remain, return early to avoid acquiring write
154 // access to Riley, which requires a pause and restart of rendering.
155 if (((*dirtyBits & ~internalDirtyBits)
156 & HdChangeTracker::AllSceneDirtyBits) == 0) {
157 return;
158 }
159
160 HdPrman_RenderParam *param =
161 static_cast<HdPrman_RenderParam*>(renderParam);
162
163 // Riley API.
164 riley::Riley *riley = param->AcquireRiley();
165
166 // Update instance bindings.
167 BASE::_UpdateInstancer(sceneDelegate, dirtyBits);
168
169 // Prim id
170 SdfPath const& id = BASE::GetId();
171 SdfPath const& instancerId = BASE::GetInstancerId();
172 const bool isHdInstance = !instancerId.IsEmpty();
173 SdfPath primPath = sceneDelegate->GetScenePrimPath(id, 0, nullptr);
174
175 // Prman has a default value for identifier:id of 0 (in case of ray miss),
176 // while Hydra treats id -1 as the clear value. We map Prman primId as
177 // (Hydra primId + 1) to get around this, here and in
178 // hdPrman/framebuffer.cpp.
179 const int32_t primId = BASE::GetPrimId() + 1;
180
181 // Sample transform
183 sceneDelegate->SampleTransform(id,
184#if HD_API_VERSION >= 68
185 param->GetShutterInterval()[0],
186 param->GetShutterInterval()[1],
187#endif
188 &xf);
189
190 // Update visibility so thet rprim->IsVisible() will work in render pass
191 if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
192 BASE::_UpdateVisibility(sceneDelegate, dirtyBits);
193 }
194
195 // Resolve material binding. Default to fallbackGprimMaterial.
196 if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
197#if HD_API_VERSION < 37
198 BASE::_SetMaterialId(sceneDelegate->GetRenderIndex().GetChangeTracker(),
199 sceneDelegate->GetMaterialId(id));
200#else
201 BASE::SetMaterialId(sceneDelegate->GetMaterialId(id));
202#endif
203 }
204 riley::MaterialId materialId = _GetFallbackMaterial(param);
205 riley::DisplacementId dispId = riley::DisplacementId::InvalidId();
206 const SdfPath & hdMaterialId = BASE::GetMaterialId();
207 HdPrman_ResolveMaterial(sceneDelegate, hdMaterialId, riley, &materialId, &dispId);
208
209 // Convert (and cache) coordinate systems.
210 riley::CoordinateSystemList coordSysList = {0, nullptr};
211 if (HdPrman_RenderParam::RileyCoordSysIdVecRefPtr convertedCoordSys =
212 param->ConvertAndRetainCoordSysBindings(sceneDelegate, id)) {
213 coordSysList.count = convertedCoordSys->size();
214 coordSysList.ids = convertedCoordSys->data();
215 }
216
217 // Hydra dirty bits corresponding to PRMan prototype attributes (also called
218 // "primitive variables" but not synonymous with USD primvars). See prman
219 // docs at https://rmanwiki.pixar.com/display/REN24/Primitive+Variables.
220 static const HdDirtyBits prmanProtoAttrBits =
221 HdChangeTracker::DirtyPoints |
222 HdChangeTracker::DirtyNormals |
223 HdChangeTracker::DirtyWidths |
224 HdChangeTracker::DirtyVolumeField |
225 HdChangeTracker::DirtyTopology |
226 HdChangeTracker::DirtyPrimvar;
227
228 // Hydra dirty bits corresponding to prman instance attributes. See prman
229 // docs at https://rmanwiki.pixar.com/display/REN24/Instance+Attributes.
230 static const HdDirtyBits prmanInstAttrBits =
231 HdChangeTracker::DirtyMaterialId |
232 HdChangeTracker::DirtyTransform |
233 HdChangeTracker::DirtyVisibility |
234 HdChangeTracker::DirtyDoubleSided |
235 HdChangeTracker::DirtySubdivTags |
236 HdChangeTracker::DirtyVolumeField |
237 HdChangeTracker::DirtyCategories |
238 HdChangeTracker::DirtyPrimvar;
239
240 // These two bitmasks intersect, so we check them against dirtyBits
241 // prior to clearing either mask.
242 const bool prmanProtoAttrBitsWereSet(*dirtyBits & prmanProtoAttrBits);
243 const bool prmanInstAttrBitsWereSet(*dirtyBits & prmanInstAttrBits);
244
245 //
246 // Create or modify Riley geometry prototype(s).
247 //
248 std::vector<riley::MaterialId> subsetMaterialIds;
249 std::vector<SdfPath> subsetPaths;
250 {
251 RtUString primType;
252 HdGeomSubsets geomSubsets;
253 RtPrimVarList primvars = _ConvertGeometry(param, sceneDelegate, id,
254 &primType, &geomSubsets);
255
256 // identifier:object is useful for cryptomatte
257 primvars.SetString(RixStr.k_identifier_object,
258 RtUString(id.GetName().c_str()));
259
260// In 2311 and beyond, we can use
261// HdPrman_PreviewSurfacePrimvarsSceneIndexPlugin.
262#if PXR_VERSION < 2311
263 // Transfer material opinions of primvars.
264 HdPrman_TransferMaterialPrimvarOpinions(sceneDelegate, hdMaterialId,
265 primvars);
266#endif // PXR_VERSION < 2311
267
268 // Adjust _prototypeIds array.
269 const size_t oldCount = _prototypeIds.size();
270 const size_t newCount = std::max((size_t) 1, geomSubsets.size());
271 if (newCount != oldCount) {
272 for (const auto &oldPrototypeId: _prototypeIds) {
273 if (oldPrototypeId != riley::GeometryPrototypeId::InvalidId()) {
274 riley->DeleteGeometryPrototype(oldPrototypeId);
275 }
276 }
277 _prototypeIds.resize(newCount,
278 riley::GeometryPrototypeId::InvalidId());
279 }
280
281 // Update Riley geom prototypes.
282 if (geomSubsets.empty()) {
283 // Common case: no subsets.
284 TF_VERIFY(newCount == 1);
285 TF_VERIFY(_prototypeIds.size() == 1);
286 primvars.SetString(RixStr.k_stats_prototypeIdentifier,
287 RtUString(primPath.GetText()));
288 if (_prototypeIds[0] == riley::GeometryPrototypeId::InvalidId()) {
289 TRACE_SCOPE("riley::CreateGeometryPrototype");
290 _prototypeIds[0] = riley->CreateGeometryPrototype(
291 riley::UserId(
292 stats::AddDataLocation(primPath.GetText()).GetValue()),
293 primType, dispId, primvars);
294 } else if (prmanProtoAttrBitsWereSet) {
295 TRACE_SCOPE("riley::ModifyGeometryPrototype");
296 riley->ModifyGeometryPrototype(primType, _prototypeIds[0],
297 &dispId, &primvars);
298 }
299 } else {
300 // Subsets case.
301 // We resolve materials here, and hold them in subsetMaterialIds:
302 // Displacement networks are passed to the geom prototype;
303 // material networks are passed to the instances.
304 subsetMaterialIds.reserve(geomSubsets.size());
305
306 // We also cache the subset paths for re-use when creating the instances
307 subsetPaths.reserve(geomSubsets.size());
308
309 for (size_t j=0; j < geomSubsets.size(); ++j) {
310 auto& prototypeId = _prototypeIds[j];
311 HdGeomSubset &subset = geomSubsets[j];
312
313 // Convert indices to int32_t and set as k_shade_faceset.
314 std::vector<int32_t> int32Indices(subset.indices.cbegin(),
315 subset.indices.cend());
316 primvars.SetIntegerArray(RixStr.k_shade_faceset,
317 int32Indices.data(),
318 int32Indices.size());
319 // Look up material override for the subset (if any)
320 riley::MaterialId subsetMaterialId = materialId;
321 riley::DisplacementId subsetDispId = dispId;
322 if (subset.materialId.IsEmpty()) {
323 subset.materialId = hdMaterialId;
324 }
325 HdPrman_ResolveMaterial(sceneDelegate, subset.materialId,
326 riley, &subsetMaterialId, &subsetDispId);
327 subsetMaterialIds.push_back(subsetMaterialId);
328
329 // Look up the path for the subset
330 SdfPath subsetPath = sceneDelegate->GetScenePrimPath(subset.id, 0, nullptr);
331 subsetPaths.push_back(subsetPath);
332 primvars.SetString(RixStr.k_stats_prototypeIdentifier,
333 RtUString(subsetPath.GetText()));
334
335 if (prototypeId == riley::GeometryPrototypeId::InvalidId()) {
336 TRACE_SCOPE("riley::CreateGeometryPrototype");
337 prototypeId =
338 riley->CreateGeometryPrototype(
339 riley::UserId(
340 stats::AddDataLocation(subsetPath.GetText()).GetValue()),
341 primType, dispId, primvars);
342 } else if (prmanProtoAttrBitsWereSet) {
343 TRACE_SCOPE("riley::ModifyGeometryPrototype");
344 riley->ModifyGeometryPrototype(primType, prototypeId,
345 &subsetDispId, &primvars);
346 }
347 }
348 }
349 *dirtyBits &= ~prmanProtoAttrBits;
350 }
351
352 //
353 // Stop here, or also create geometry instances?
354 //
355 if (_PrototypeOnly()) {
356 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
357 return;
358 }
359
360 //
361 // Create or modify Riley geometry instances.
362 //
363
364 // Resolve attributes.
365 RtParamList attrs = param->ConvertAttributes(sceneDelegate, id, true);
366
367 // Add "identifier:id" with the prim id.
368 attrs.SetInteger(RixStr.k_identifier_id, primId);
369
370 // user:__materialid is useful for cryptomatte
371 if(!hdMaterialId.IsEmpty()) {
372 attrs.SetString(RtUString("user:__materialid"), RtUString(hdMaterialId.GetText()));
373 }
374
375 if (!isHdInstance) {
376 // Simple case: Singleton instance.
377 // Convert transform.
379 for (size_t i=0; i < xf.count; ++i) {
380 xf_rt[i] = HdPrman_Utils::GfMatrixToRtMatrix(xf.values[i]);
381 }
382 const riley::Transform xform = {
383 unsigned(xf.count),
384 xf_rt.data(),
385 xf.times.data()};
386
387 // Add "identifier:id2" with the instance number.
388 // XXX Do we want to distinguish facesets here?
389 attrs.SetInteger(RixStr.k_identifier_id2, 0);
390
391 // Adjust _instanceIds array.
392 const size_t oldCount = _instanceIds.size();
393 const size_t newCount = _prototypeIds.size();
394 if (newCount != oldCount) {
395 for (const auto &oldInstanceId: _instanceIds) {
396 if (oldInstanceId != riley::GeometryInstanceId::InvalidId()) {
397 riley->DeleteGeometryInstance(
398 riley::GeometryPrototypeId::InvalidId(), oldInstanceId);
399 }
400 }
401 _instanceIds.resize(
402 newCount,
403 riley::GeometryInstanceId::InvalidId());
404 }
405
406 // Prepend renderTag to grouping:membership
407 param->AddRenderTagToGroupingMembership(
408 sceneDelegate->GetRenderTag(id), attrs);
409
410 // Create or modify Riley instances corresponding to a
411 // singleton Hydra instance.
412 TF_VERIFY(_instanceIds.size() == _prototypeIds.size());
413 for (size_t j=0; j < _prototypeIds.size(); ++j) {
414 auto const& prototypeId = _prototypeIds[j];
415 auto& instanceId = _instanceIds[j];
416 auto instanceMaterialId = materialId;
417 RtParamList finalAttrs = attrs; // copy
418
419 // If a subset path was cached, use it. If not, use the prim path.
420 SdfPath* subsetPath(&primPath);
421 if (!subsetPaths.empty()) {
422 subsetPath = &subsetPaths[j];
423 }
424
425 finalAttrs.SetString(RixStr.k_identifier_name,
426 RtUString(subsetPath->GetText()));
427
428 // If a valid subset material was bound, use it.
429 if (!subsetMaterialIds.empty()) {
430 TF_VERIFY(j < subsetMaterialIds.size());
431 instanceMaterialId = subsetMaterialIds[j];
432 }
433
434 if (instanceId == riley::GeometryInstanceId::InvalidId()) {
435 TRACE_SCOPE("riley::CreateGeometryInstance");
436 instanceId = riley->CreateGeometryInstance(
437 riley::UserId(
438 stats::AddDataLocation(subsetPath->GetText()).GetValue()),
439 riley::GeometryPrototypeId::InvalidId(), prototypeId,
440 instanceMaterialId, coordSysList, xform, finalAttrs);
441 } else if (prmanInstAttrBitsWereSet) {
442 TRACE_SCOPE("riley::ModifyGeometryInstance");
443 riley->ModifyGeometryInstance(
444 riley::GeometryPrototypeId::InvalidId(),
445 instanceId, &instanceMaterialId, &coordSysList, &xform,
446 &finalAttrs);
447 }
448 }
449 *dirtyBits &= ~prmanInstAttrBits;
450 } else if (prmanInstAttrBitsWereSet
451 || HdChangeTracker::IsInstancerDirty(*dirtyBits, instancerId)) {
452 // This gprim is a prototype of a hydra instancer. (It is not itself an
453 // instancer because it is a gprim.) The riley geometry prototypes have
454 // already been synced above, and those are owned by this gprim instance.
455 // We need to tell the hdprman instancer to sync its riley instances for
456 // these riley prototypes.
457 //
458 // We won't make any riley instances here. The hdprman instancer will
459 // own the riley instances instead.
460 //
461 // We only need to do this if dirtyBits says the instancer is dirty.
462
463 HdRenderIndex &renderIndex = sceneDelegate->GetRenderIndex();
464
465 // first, sync the hydra instancer and its parents, from the bottom up.
466 // (note: this is transitional code, it should be done by the render index...)
467 HdInstancer::_SyncInstancerAndParents(renderIndex, instancerId);
468
469 if (subsetMaterialIds.size() == 0) {
470 subsetMaterialIds.push_back(materialId);
471 }
472 if (subsetPaths.size() == 0) {
473 subsetPaths.push_back(primPath);
474 }
475 TF_VERIFY(_prototypeIds.size() == subsetMaterialIds.size() &&
476 _prototypeIds.size() == subsetPaths.size(),
477 "size mismatch (%lu, %lu, %lu)\n", _prototypeIds.size(),
478 subsetMaterialIds.size(), subsetPaths.size());
479
480 // XXX: To avoid a failed verify inside Populate(), we will check the
481 // prototype ids for validity here. We don't usually do this, relying on
482 // Riley to report invalid prototype ids on instance creation. But
483 // Populate() allows and expects an invalid prototype id when instancing
484 // lights, so doing this check here lets us make a more informative
485 // warning. HYD-3206
486 if (std::any_of(_prototypeIds.begin(), _prototypeIds.end(),
487 [](const auto& id){
488 return id == riley::GeometryPrototypeId::InvalidId();
489 })) {
490 TF_WARN("Riley geometry prototype creation failed for "
491 "instanced gprim <%s>; the prim will not be instanced.",
492 id.GetText());
493 } else {
494 // next, tell the hdprman instancer to sync the riley instances
495 HdPrmanInstancer *instancer = static_cast<HdPrmanInstancer*>(
496 renderIndex.GetInstancer(instancerId));
497 if (instancer) {
498 instancer->Populate(
499 renderParam,
500 dirtyBits,
501 id,
502 _prototypeIds,
503 coordSysList,
504 attrs, xf,
505 subsetMaterialIds,
506 subsetPaths);
507 }
508 }
509 }
510 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
511}
512
513PXR_NAMESPACE_CLOSE_SCOPE
514
515#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.
#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.