-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Autodesk: [hdSt] Early parallel MaterialX codegen, launched by a scene index #3847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
c521391
e93d4af
0ae0ac5
74ce797
118f955
73c061d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
|
|
||
|
|
@@ -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()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
|
@@ -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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -159,8 +159,8 @@ _GetGlslfxForTerminal( | |
| } | ||
| } | ||
|
|
||
| static HdMaterialNode2 const* | ||
| _GetTerminalNode( | ||
| HdMaterialNode2 const* | ||
| HdSt_GetTerminalNode( | ||
|
Comment on lines
+162
to
+163
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The state necessary for the codegen process is now encapsulated in the
|
||
|
|
||
| // 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 = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
|
|
||
| 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(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
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
materialXSyncDispatcherin the original PR.