Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions pxr/imaging/hd/sceneIndexAdapterSceneDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,11 +1322,10 @@ _ToMaterialNetworkMap(
}

VtValue
HdSceneIndexAdapterSceneDelegate::GetMaterialResource(SdfPath const & id)
HdSceneIndexAdapterSceneDelegate::GetMaterialResourceFromSceneIndexPrim(
HdSceneIndexPrim& prim, const TfTokenVector& renderContexts)
{
TRACE_FUNCTION();
HF_MALLOC_TAG_FUNCTION();
HdSceneIndexPrim prim = _GetInputPrim(id);

HdMaterialSchema matSchema = HdMaterialSchema::GetFromParent(
prim.dataSource);
Expand All @@ -1335,16 +1334,27 @@ HdSceneIndexAdapterSceneDelegate::GetMaterialResource(SdfPath const & id)
}

// Query for a material network to match the requested render contexts
const TfTokenVector renderContexts =
GetRenderIndex().GetRenderDelegate()->GetMaterialRenderContexts();
HdMaterialNetworkSchema netSchema = matSchema.GetMaterialNetwork(renderContexts);
HdMaterialNetworkSchema netSchema =
matSchema.GetMaterialNetwork(renderContexts);
if (!netSchema.IsDefined()) {
return VtValue();
}

return VtValue(_ToMaterialNetworkMap(netSchema, renderContexts));
}

VtValue
HdSceneIndexAdapterSceneDelegate::GetMaterialResource(SdfPath const & id)
{
TRACE_FUNCTION();
HF_MALLOC_TAG_FUNCTION();

HdSceneIndexPrim prim = _GetInputPrim(id);
return GetMaterialResourceFromSceneIndexPrim(
prim,
GetRenderIndex().GetRenderDelegate()->GetMaterialRenderContexts());
}

static
TfTokenVector
_ToTokenVector(const std::vector<std::string> &strings)
Expand Down
3 changes: 3 additions & 0 deletions pxr/imaging/hd/sceneIndexAdapterSceneDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ class HdSceneIndexAdapterSceneDelegate
// ------------------------------------------------------------------------
// Material API

static HD_API VtValue GetMaterialResourceFromSceneIndexPrim(
HdSceneIndexPrim& prim, const TfTokenVector& renderContexts);

SdfPath GetMaterialId(SdfPath const &id) override;
VtValue GetMaterialResource(SdfPath const &id) override;
HdIdVectorSharedPtr GetCoordSysBindings(SdfPath const &id) override;
Expand Down
1 change: 1 addition & 0 deletions pxr/imaging/hdSt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ if (${PXR_ENABLE_MATERIALX_SUPPORT})
list(APPEND optionalPrivateClasses
materialXFilter
materialXShaderGen
materialXSyncSceneIndex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new scene index implementation, which replaces materialXSyncDispatcher in the original PR.

)
endif()
if (PXR_ENABLE_PTEX_SUPPORT)
Expand Down
94 changes: 72 additions & 22 deletions pxr/imaging/hdSt/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
#include "pxr/imaging/hdSt/tokens.h"
#include "pxr/imaging/hdSt/materialParam.h"

#ifdef PXR_MATERIALX_SUPPORT_ENABLED
#include "pxr/imaging/hdSt/materialXSyncSceneIndex.h"
#include "pxr/imaging/hdSt/renderDelegate.h"
#endif

#include "pxr/imaging/hd/changeTracker.h"
#include "pxr/imaging/hd/tokens.h"

Expand Down Expand Up @@ -172,48 +177,91 @@ HdStMaterial::_ProcessTextureDescriptors(
/* virtual */
void
HdStMaterial::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
HD_TRACE_FUNCTION();
HF_MALLOC_TAG_FUNCTION();

const HdDirtyBits bits = *dirtyBits;

if (!(bits & DirtyResource) && !(bits & DirtyParams)) {
*dirtyBits = Clean;
return;
}

bool processedMaterialNetwork = false;

HdStResourceRegistrySharedPtr const& resourceRegistry =
std::static_pointer_cast<HdStResourceRegistry>(
sceneDelegate->GetRenderIndex().GetResourceRegistry());

HdDirtyBits bits = *dirtyBits;
#ifdef PXR_MATERIALX_SUPPORT_ENABLED
{
HdStRenderDelegate* stormDelegate = static_cast<HdStRenderDelegate*>(
sceneDelegate->GetRenderIndex().GetRenderDelegate());

if (!(bits & DirtyResource) && !(bits & DirtyParams)) {
*dirtyBits = Clean;
return;
HdSt_MaterialFilterTaskSharedPtr filterTask;

// This scene index manages early parallel MaterialX codegen. It exists
// when that optimization is enabled.
if (HdSt_MaterialXSyncSceneIndex* sceneIndex =
stormDelegate->GetMaterialXSyncSceneIndex()) {

// Wait for all early parallel codegen tasks to complete and
// retrieve the state cached for this sprim when codegen was
// started
filterTask = sceneIndex->WaitAndExtractFilterTask(GetId());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait for the codegen tasks to complete. Previously, we were waiting by calling a new render delegate API before syncing the materials: https://github.com/PixarAnimationStudios/OpenUSD/pull/3567/files#r1995705092

Now, we wait for the generator task group in each material's sync. In practice, only the first material sprim would ever wait for any measurable duration, and the overhead of waiting for all subsequent sprims is negligible (around 1 microsecond).

This is also where we get the cached per-material state from the scene index. The filter task is removed from the scene index its ownership is transferred to the local variable.

Reusing the filter task allows us to avoid getting the material resource again below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible instead to have the material prim datasource call wait on the scene index, so that Storm doesn't need to deal with the new scene index at all? I'll try to sketch out a proposal in the scene index code.

}

if (filterTask) {
// We only use filter tasks for non-volume materials
constexpr bool isVolume = false;

_hdStMaterialNetwork.ProcessFilterTask(
GetId(), filterTask,
isVolume, resourceRegistry.get());

processedMaterialNetwork = true;
}
}
#endif

bool markBatchesDirty = false;
// The serial MaterialX codegen code path - executed only if we haven't
// already processed the material network above
if (!processedMaterialNetwork) {
VtValue vtMat = sceneDelegate->GetMaterialResource(GetId());
if (vtMat.IsHolding<HdMaterialNetworkMap>()) {

HdMaterialNetworkMap const& hdNetworkMap =
vtMat.UncheckedGet<HdMaterialNetworkMap>();

if (!hdNetworkMap.terminals.empty() && !hdNetworkMap.map.empty()) {
_hdStMaterialNetwork.ProcessMaterialNetwork(GetId(),
hdNetworkMap, resourceRegistry.get());

processedMaterialNetwork = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the material network has processed the filter task, we don't need the task any more, so we destroy it by letting it go out of scope.

}
}
}

std::string fragmentSource;
std::string displacementSource;
std::string volumeSource;

VtDictionary materialMetadata;
TfToken materialTag = _materialTag;
HdSt_MaterialParamVector params;
HdStMaterialNetwork::TextureDescriptorVector textureDescriptors;

VtValue vtMat = sceneDelegate->GetMaterialResource(GetId());
if (vtMat.IsHolding<HdMaterialNetworkMap>()) {
HdMaterialNetworkMap const& hdNetworkMap =
vtMat.UncheckedGet<HdMaterialNetworkMap>();
if (!hdNetworkMap.terminals.empty() && !hdNetworkMap.map.empty()) {
_networkProcessor.ProcessMaterialNetwork(GetId(), hdNetworkMap,
resourceRegistry.get());
fragmentSource = _networkProcessor.GetFragmentCode();
volumeSource = _networkProcessor.GetVolumeCode();
displacementSource = _networkProcessor.GetDisplacementCode();
materialMetadata = _networkProcessor.GetMetadata();
materialTag = _networkProcessor.GetMaterialTag();
params = _networkProcessor.GetMaterialParams();
textureDescriptors = _networkProcessor.GetTextureDescriptors();
}
if (processedMaterialNetwork) {
fragmentSource = _hdStMaterialNetwork.GetFragmentCode();
volumeSource = _hdStMaterialNetwork.GetVolumeCode();
displacementSource = _hdStMaterialNetwork.GetDisplacementCode();
materialMetadata = _hdStMaterialNetwork.GetMetadata();
materialTag = _hdStMaterialNetwork.GetMaterialTag();
params = _hdStMaterialNetwork.GetMaterialParams();
textureDescriptors = _hdStMaterialNetwork.GetTextureDescriptors();
}

// Use fallback shader when there is no source for
Expand All @@ -226,6 +274,8 @@ HdStMaterial::Sync(HdSceneDelegate *sceneDelegate,
materialMetadata = _fallbackGlslfx->GetMetadata();
}

bool markBatchesDirty = false;

// Update volume material data.
if (_volumeMaterialData.source != volumeSource) {
// If we're updating the volume source, we need to rebatch anything that
Expand Down
4 changes: 3 additions & 1 deletion pxr/imaging/hdSt/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#define PXR_IMAGING_HD_ST_MATERIAL_H

#include "pxr/pxr.h"

#include "pxr/imaging/hdSt/api.h"
#include "pxr/imaging/hdSt/materialNetwork.h"
#include "pxr/imaging/hdSt/shaderCode.h"

#include "pxr/imaging/hd/material.h"
#include "pxr/imaging/hf/perfLog.h"

Expand Down Expand Up @@ -123,7 +125,7 @@ class HdStMaterial final: public HdMaterial
TfToken _materialTag;
size_t _textureHash;

HdStMaterialNetwork _networkProcessor;
HdStMaterialNetwork _hdStMaterialNetwork;
};

inline bool HdStMaterial::HasPtex() const
Expand Down
112 changes: 70 additions & 42 deletions pxr/imaging/hdSt/materialNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ _GetGlslfxForTerminal(
}
}

static HdMaterialNode2 const*
_GetTerminalNode(
HdMaterialNode2 const*
HdSt_GetTerminalNode(
Comment on lines +162 to +163
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now need to call it from outside this source file.

HdMaterialNetwork2 const& network,
TfToken const& terminalName,
SdfPath * terminalNodePath)
Expand Down Expand Up @@ -1100,57 +1100,85 @@ HdStMaterialNetwork::ProcessMaterialNetwork(
{
HD_TRACE_FUNCTION();

_fragmentSource.clear();
_displacementSource.clear();
_materialMetadata.clear();
_materialParams.clear();
_textureDescriptors.clear();
_materialTag = HdStMaterialTagTokens->defaultMaterialTag;
bool isVolume = false;

auto filterTask = std::make_shared<HdSt_MaterialFilterTask>();
filterTask->hdNetwork =
HdConvertToHdMaterialNetwork2(hdNetworkMap, &isVolume);
Comment on lines +1105 to +1107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The state necessary for the codegen process is now encapsulated in the HdSt_MaterialFilterTask class, because it needs to be created earlier than the material sprim and to persist until the parallel codegen task is finished.

HdStMaterialNetwork::ProcessMaterialNetwork is called in the old, synchronous code path, but we create a HdSt_MaterialFilterTask here in order to share code between the two code paths.


// The fragment source comes from the 'surface' network or the
// 'volume' network.
bool isVolume = false;
HdMaterialNetwork2 surfaceNetwork =
HdConvertToHdMaterialNetwork2(hdNetworkMap, &isVolume);
const TfToken &terminalName = (isVolume) ? HdMaterialTerminalTokens->volume
: HdMaterialTerminalTokens->surface;

SdfPath surfTerminalPath;
if (HdMaterialNode2 const* surfTerminal =
_GetTerminalNode(surfaceNetwork, terminalName, &surfTerminalPath)) {
filterTask->terminalNode =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here & material.*, instead of bringing the "filterTask" API into a bunch of files in Storm, you could modularize things better by either:
(1) having the scene index produce a "glsl" material network that can be read by the non-materialx code; or
(2) having the scene index produce a digested material network as code snippets for each stage & params, and pass them through as datasources. If material.cpp finds that, it uses it, and otherwise it falls back to network processing.

This splits the code up a little better, which is nice since the MaterialX code is an external dependency with an evolving API and hidden behind a build flag, so having a big API interface between the two makes me a bit worried.

HdSt_GetTerminalNode(
filterTask->hdNetwork,
terminalName,
&filterTask->terminalNodePath);

if (!filterTask->terminalNode) {
return;
}

ProcessFilterTask(materialId, filterTask, isVolume, resourceRegistry);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProcessMaterialNetwork is now implemented via ProcessFilterTask for code reuse.

}

void
HdStMaterialNetwork::ProcessFilterTask(
SdfPath const& materialId,
HdSt_MaterialFilterTaskSharedPtr filterTask,
bool isVolume,
HdStResourceRegistry *resourceRegistry)
{
HD_TRACE_FUNCTION();

_fragmentSource.clear();
_displacementSource.clear();
_materialMetadata.clear();
_materialParams.clear();
_textureDescriptors.clear();

_materialTag = HdStMaterialTagTokens->defaultMaterialTag;

if (!filterTask || !filterTask->terminalNode) {
return;
}

#ifdef PXR_MATERIALX_SUPPORT_ENABLED
if (!isVolume) {
_materialXGfx = HdSt_ApplyMaterialXFilter(&surfaceNetwork, materialId,
*surfTerminal, surfTerminalPath,
&_materialParams, resourceRegistry);
}
if (!isVolume) {
_materialXGfx = HdSt_ApplyMaterialXFilter(filterTask, materialId,
&_materialParams, resourceRegistry);
}
#endif
// Extract the glslfx and metadata for surface/volume.
_GetGlslfxForTerminal(_surfaceGfx, &_surfaceGfxHash,
surfTerminal->nodeTypeId, resourceRegistry);
if (_surfaceGfx) {

// If the glslfx file is not valid we skip parsing the network.
// This produces no fragmentSource which means Storm's material
// will use the fallback shader.
if (_surfaceGfx->IsValid()) {

_fragmentSource = _surfaceGfx->GetSurfaceSource();
_volumeSource = _surfaceGfx->GetVolumeSource();

_materialMetadata = _surfaceGfx->GetMetadata();
_materialTag = _GetMaterialTag(_materialMetadata, *surfTerminal);
_GatherMaterialParams(surfaceNetwork, *surfTerminal,
&_materialParams, &_textureDescriptors,
_materialTag);

// OSL networks have a displacement network in hdNetworkMap
// under terminal: HdMaterialTerminalTokens->displacement.
// For Storm however we expect the displacement shader to be
// provided via the surface glslfx / terminal.
_displacementSource = _surfaceGfx->GetDisplacementSource();
}
// Extract the glslfx and metadata for surface/volume.
_GetGlslfxForTerminal(_surfaceGfx, &_surfaceGfxHash,
filterTask->terminalNode->nodeTypeId, resourceRegistry);
if (_surfaceGfx) {

// If the glslfx file is not valid we skip parsing the network.
// This produces no fragmentSource which means Storm's material
// will use the fallback shader.
if (_surfaceGfx->IsValid()) {
_fragmentSource = _surfaceGfx->GetSurfaceSource();
_volumeSource = _surfaceGfx->GetVolumeSource();

_materialMetadata = _surfaceGfx->GetMetadata();

_materialTag = _GetMaterialTag(
_surfaceGfx->GetMetadata(), *filterTask->terminalNode);
_GatherMaterialParams(
filterTask->hdNetwork, *filterTask->terminalNode,
&_materialParams, &_textureDescriptors,
_materialTag);

// OSL networks have a displacement network in hdNetworkMap
// under terminal: HdMaterialTerminalTokens->displacement.
// For Storm however we expect the displacement shader to be
// provided via the surface glslfx / terminal.
_displacementSource = _surfaceGfx->GetDisplacementSource();
}
}
}
Expand Down
Loading