Skip to content

Commit

Permalink
Tracking material assignments to shapes and resyncing material IDs if…
Browse files Browse the repository at this point in the history
… terminals have changed. (#753)

Fixes #751
  • Loading branch information
sirpalee authored Apr 20, 2021
1 parent 8746d00 commit 65416a9
Show file tree
Hide file tree
Showing 15 changed files with 316 additions and 14 deletions.
2 changes: 2 additions & 0 deletions render_delegate/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(SRC
instancer.cpp
light.cpp
material.cpp
material_tracker.cpp
mesh.cpp
native_rprim.cpp
openvdb_asset.cpp
Expand All @@ -35,6 +36,7 @@ set(HDR
instancer.h
light.h
material.h
material_tracker.h
mesh.h
native_rprim.h
openvdb_asset.h
Expand Down
1 change: 1 addition & 0 deletions render_delegate/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ source_files = [
'instancer.cpp',
'light.cpp',
'material.cpp',
'material_tracker.cpp',
'mesh.cpp',
'native_rprim.cpp',
'openvdb_asset.cpp',
Expand Down
4 changes: 3 additions & 1 deletion render_delegate/basis_curves.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ void HdArnoldBasisCurves::Sync(

if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
param.Interrupt();
const auto materialId = sceneDelegate->GetMaterialId(id);
_materialTracker.TrackSingleMaterial(GetRenderDelegate(), id, materialId);
const auto* material = reinterpret_cast<const HdArnoldMaterial*>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, sceneDelegate->GetMaterialId(id)));
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, materialId));
if (material != nullptr) {
AiNodeSetPtr(GetArnoldNode(), str::shader, material->GetSurfaceShader());
} else {
Expand Down
17 changes: 16 additions & 1 deletion render_delegate/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ HdArnoldMaterial::HdArnoldMaterial(HdArnoldRenderDelegate* renderDelegate, const

HdArnoldMaterial::~HdArnoldMaterial()
{
_renderDelegate->RemoveMaterial(GetId());
for (auto& node : _nodes) {
AiNodeDestroy(node.second.node);
}
Expand All @@ -452,7 +453,10 @@ void HdArnoldMaterial::Sync(HdSceneDelegate* sceneDelegate, HdRenderParam* rende
auto* param = reinterpret_cast<HdArnoldRenderParam*>(renderParam);
const auto id = GetId();
if ((*dirtyBits & HdMaterial::DirtyResource) && !id.IsEmpty()) {
param->Interrupt();
HdArnoldRenderParamInterrupt param(renderParam);
const auto* oldSurface = _surface;
const auto* oldDisplacement = _displacement;
const auto* oldVolume = _volume;
auto value = sceneDelegate->GetMaterialResource(GetId());
AtNode* surfaceEntry = nullptr;
AtNode* displacementEntry = nullptr;
Expand All @@ -467,6 +471,9 @@ void HdArnoldMaterial::Sync(HdSceneDelegate* sceneDelegate, HdRenderParam* rende
if (network == nullptr) {
return nullptr;
}
// No need to interrupt earlier as we don't know if there is a valid network passed to the function or
// not.
param.Interrupt();
// We are remapping the preview surface nodes to ones that are supported
// in Arnold. This way we can keep the export code untouched,
// and handle connection / node exports separately.
Expand All @@ -482,8 +489,16 @@ void HdArnoldMaterial::Sync(HdSceneDelegate* sceneDelegate, HdRenderParam* rende
_surface = surfaceEntry == nullptr ? _renderDelegate->GetFallbackShader() : surfaceEntry;
_displacement = displacementEntry;
_volume = volumeEntry == nullptr ? _renderDelegate->GetFallbackVolumeShader() : volumeEntry;
// We only mark the material dirty if one of the terminals have changed, but ignore the initial sync, because we
// expect Hydra to do the initial assignment correctly.
if (_wasSyncedOnce) {
if (oldSurface != _surface || oldDisplacement != _displacement || oldVolume != _volume) {
_renderDelegate->DirtyMaterial(id);
}
}
}
*dirtyBits = HdMaterial::Clean;
_wasSyncedOnce = true;
}

HdDirtyBits HdArnoldMaterial::GetInitialDirtyBitsMask() const { return HdMaterial::DirtyResource; }
Expand Down
1 change: 1 addition & 0 deletions render_delegate/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class HdArnoldMaterial : public HdMaterial {
AtNode* _displacement = nullptr;
/// Pointer to the entry point to the Volume Shader Network.
AtNode* _volume = nullptr;
bool _wasSyncedOnce = false; ///< Whether or not the material has been synced at least once.
};

PXR_NAMESPACE_CLOSE_SCOPE
78 changes: 78 additions & 0 deletions render_delegate/material_tracker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2021 Autodesk, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "material_tracker.h"

#include "render_delegate.h"

PXR_NAMESPACE_OPEN_SCOPE

VtArray<SdfPath> HdArnoldMaterialTracker::GetCurrentMaterials(size_t newArraySize)
{
auto currentMaterials = _materials;
if (_materials.size() != newArraySize) {
_materials.resize(newArraySize);
}
return currentMaterials;
}

void HdArnoldMaterialTracker::SetMaterial(const SdfPath& id, size_t arrayId)
{
if (Ai_likely(_materials.size() > arrayId)) {
// cdata is a simple way to access the data without triggering a copy.
if (id != _materials.cdata()[arrayId]) {
// Trigger detaching.
_materials[arrayId] = id;
}
}
}

void HdArnoldMaterialTracker::TrackMaterialChanges(
HdArnoldRenderDelegate* renderDelegate, const SdfPath& shapeId, const VtArray<SdfPath>& oldMaterials)
{
if (!oldMaterials.IsIdentical(_materials)) {
// All the VtArrays are shared, so we don't have to worry about duplicating data here.
// Untracking the old materials.
if (!oldMaterials.empty()) {
renderDelegate->UntrackShapeMaterials(shapeId, oldMaterials);
}
// Tracking the new materials.
renderDelegate->TrackShapeMaterials(shapeId, _materials);
}
}

void HdArnoldMaterialTracker::TrackSingleMaterial(
HdArnoldRenderDelegate* renderDelegate, const SdfPath& shapeId, const SdfPath& materialId)
{
// Initial assignment.
if (_materials.empty()) {
_materials.assign(1, materialId);
renderDelegate->TrackShapeMaterials(shapeId, _materials);
// We already have a single material stored, check if it has changed.
} else {
if (_materials.cdata()[0] != materialId) {
renderDelegate->UntrackShapeMaterials(shapeId, _materials);
_materials[0] = materialId;
renderDelegate->TrackShapeMaterials(shapeId, _materials);
}
}
}

void HdArnoldMaterialTracker::UntrackMaterials(HdArnoldRenderDelegate* renderDelegate, const SdfPath& shapeId)
{
if (!_materials.empty()) {
renderDelegate->UntrackShapeMaterials(shapeId, _materials);
}
}

PXR_NAMESPACE_CLOSE_SCOPE
76 changes: 76 additions & 0 deletions render_delegate/material_tracker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2021 Autodesk, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// @file material_tracker.h
///
/// Utilities for tracking material changes on shapes.
#pragma once

#include "api.h"

#include <pxr/pxr.h>

#include <pxr/base/vt/array.h>

#include <pxr/usd/sdf/path.h>

PXR_NAMESPACE_OPEN_SCOPE

class HdArnoldRenderDelegate;

/// Class to track material assignments to shapes.
class HdArnoldMaterialTracker {
public:
/// Queries the list of current materials.
///
/// @param newArraySize Size of the materials after querying the old array.
/// @return A copy of the current materials.
HDARNOLD_API
VtArray<SdfPath> GetCurrentMaterials(size_t newArraySize);

/// Check if a material has changed and store the new material.
///
/// @param id Path to the new material.
/// @param arrayId Index of the material.
HDARNOLD_API
void SetMaterial(const SdfPath& id, size_t arrayId);

/// Track material changes if materials has been changed.
///
/// @param renderDelegate Pointer to the Arnold Render Delegate.
/// @param shapeId Id of the current shape.
/// @param oldMaterials List of the materials assigned to the shape before assigning materials.
HDARNOLD_API
void TrackMaterialChanges(
HdArnoldRenderDelegate* renderDelegate, const SdfPath& shapeId, const VtArray<SdfPath>& oldMaterials);

/// Track material if there is only a single material assigned to the shape.
///
/// @param renderDelegate Pointer to the Arnold Render Delegate.
/// @param shapeId Id of the current shape.
/// @param materialId The material assigned to the shape.
HDARNOLD_API
void TrackSingleMaterial(HdArnoldRenderDelegate* renderDelegate, const SdfPath& shapeId, const SdfPath& materialId);

/// Untrack all materials assigned to the shape. Typically used when deleting the shape.
///
/// @param renderDelegate Pointer to the Arnold Render Delegate.
/// @param shapeId Id of the current shape.
HDARNOLD_API
void UntrackMaterials(HdArnoldRenderDelegate* renderDelegate, const SdfPath& shapeId);

private:
VtArray<SdfPath> _materials; ///< List of materials currently assigned.
};

PXR_NAMESPACE_CLOSE_SCOPE
5 changes: 5 additions & 0 deletions render_delegate/mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,11 @@ void HdArnoldMesh::Sync(
auto* dispMapArray = AiArrayAllocate(numShaders, 1, AI_TYPE_POINTER);
auto* shader = static_cast<AtNode**>(AiArrayMap(shaderArray));
auto* dispMap = static_cast<AtNode**>(AiArrayMap(dispMapArray));
// We are using VtAray here, so it's going to be COW.
auto oldMaterials = _materialTracker.GetCurrentMaterials(numShaders);

auto setMaterial = [&](const SdfPath& materialId, size_t arrayId) {
_materialTracker.SetMaterial(materialId, arrayId);
const auto* material = reinterpret_cast<const HdArnoldMaterial*>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, materialId));
if (material == nullptr) {
Expand All @@ -295,6 +298,8 @@ void HdArnoldMesh::Sync(
setMaterial(_subsets[subset], subset);
}
setMaterial(sceneDelegate->GetMaterialId(id), numSubsets);
// If there has been a change in data, we already detached materials and the two arrays are different.
_materialTracker.TrackMaterialChanges(GetRenderDelegate(), id, oldMaterials);
AiArrayUnmap(shaderArray);
AiArrayUnmap(dispMapArray);
AiNodeSetArray(GetArnoldNode(), str::shader, shaderArray);
Expand Down
7 changes: 5 additions & 2 deletions render_delegate/native_rprim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ void HdArnoldNativeRprim::Sync(
if (val.IsHolding<ArnoldUsdParamValueList>()) {
const auto* nodeEntry = AiNodeGetNodeEntry(GetArnoldNode());
for (const auto& param : val.UncheckedGet<ArnoldUsdParamValueList>()) {
HdArnoldSetParameter(GetArnoldNode(), AiNodeEntryLookUpParameter(nodeEntry, param.first), param.second);
HdArnoldSetParameter(
GetArnoldNode(), AiNodeEntryLookUpParameter(nodeEntry, param.first), param.second);
}
}
#else
Expand Down Expand Up @@ -81,8 +82,10 @@ void HdArnoldNativeRprim::Sync(

if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
param.Interrupt();
const auto materialId = sceneDelegate->GetMaterialId(id);
_materialTracker.TrackSingleMaterial(GetRenderDelegate(), id, materialId);
const auto* material = reinterpret_cast<const HdArnoldMaterial*>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, sceneDelegate->GetMaterialId(id)));
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, materialId));
if (material != nullptr) {
AiNodeSetPtr(GetArnoldNode(), str::shader, material->GetSurfaceShader());
} else {
Expand Down
4 changes: 3 additions & 1 deletion render_delegate/points.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ void HdArnoldPoints::Sync(

if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
param.Interrupt();
const auto materialId = sceneDelegate->GetMaterialId(id);
_materialTracker.TrackSingleMaterial(GetRenderDelegate(), id, materialId);
const auto* material = reinterpret_cast<const HdArnoldMaterial*>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, sceneDelegate->GetMaterialId(id)));
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, materialId));
if (material != nullptr) {
AiNodeSetPtr(GetArnoldNode(), str::shader, material->GetSurfaceShader());
} else {
Expand Down
56 changes: 54 additions & 2 deletions render_delegate/render_delegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -913,11 +913,49 @@ bool HdArnoldRenderDelegate::ShouldSkipIteration(HdRenderIndex* renderIndex, flo
_shutterClose = shutterClose;
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyTransform | HdChangeTracker::DirtyInstancer;
}
auto skip = false;
if (bits != HdChangeTracker::Clean) {
renderIndex->GetChangeTracker().MarkAllRprimsDirty(bits);
return true;
skip = true;
}
SdfPath id;
// We are first removing materials, to avoid untracking them later.
while (_materialRemovalQueue.try_pop(id)) {
_materialToShapeMap.erase(id);
}
ShapeMaterialChange shapeChange;
while (_shapeMaterialUntrackQueue.try_pop(shapeChange)) {
for (const auto& material : shapeChange.materials) {
auto it = _materialToShapeMap.find(id);
// In case it was already removed.
if (it != _materialToShapeMap.end()) {
it->second.erase(shapeChange.shape);
}
}
}
while (_shapeMaterialTrackQueue.try_pop(shapeChange)) {
for (const auto& material : shapeChange.materials) {
auto it = _materialToShapeMap.find(id);
if (it == _materialToShapeMap.end()) {
_materialToShapeMap.insert({material, {shapeChange.shape}});
} else {
it->second.insert(shapeChange.shape);
}
}
}
auto& changeTracker = renderIndex->GetChangeTracker();
// And at last we are triggering changes.
while (_materialDirtyQueue.try_pop(id)) {
auto it = _materialToShapeMap.find(id);
// There could be cases where the material is not assigned anything tracking, but this should be rare.
if (it != _materialToShapeMap.end()) {
skip = true;
for (const auto& shape : it->second) {
changeTracker.MarkRprimDirty(shape, HdChangeTracker::DirtyMaterialId);
}
}
}
return false;
return skip;
}

bool HdArnoldRenderDelegate::IsPauseSupported() const { return true; }
Expand All @@ -941,4 +979,18 @@ const HdArnoldRenderDelegate::NativeRprimParamList* HdArnoldRenderDelegate::GetN
return it == _nativeRprimParams.end() ? nullptr : &it->second;
}

void HdArnoldRenderDelegate::DirtyMaterial(const SdfPath& id) { _materialDirtyQueue.emplace(id); }

void HdArnoldRenderDelegate::RemoveMaterial(const SdfPath& id) { _materialRemovalQueue.emplace(id); }

void HdArnoldRenderDelegate::TrackShapeMaterials(const SdfPath& shape, const VtArray<SdfPath>& materials)
{
_shapeMaterialTrackQueue.emplace(shape, materials);
}

void HdArnoldRenderDelegate::UntrackShapeMaterials(const SdfPath& shape, const VtArray<SdfPath>& materials)
{
_shapeMaterialUntrackQueue.emplace(shape, materials);
}

PXR_NAMESPACE_CLOSE_SCOPE
Loading

0 comments on commit 65416a9

Please sign in to comment.