Skip to content
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

Reuse shader effect for duplicate material networks. #950

Merged
Merged
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
1 change: 1 addition & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ target_sources(${PROJECT_NAME}
render_delegate.cpp
render_param.cpp
sampler.cpp
shader.cpp
tokens.cpp
)

Expand Down
158 changes: 121 additions & 37 deletions lib/mayaUsd/render/vp2RenderDelegate/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,35 +113,106 @@ inline bool _IsUsdUVTexture(const HdMaterialNode& node)
return (node.identifier == UsdImagingTokens->UsdUVTexture);
}

//! Helper utility function to print nodes, connections and primvars in the
//! Helper function to generate a XML string about nodes, relationships and primvars in the
//! specified material network.
void _PrintMaterialNetwork(
const std::string& label,
const SdfPath& id,
const HdMaterialNetwork& mat)
std::string _GenerateXMLString(const HdMaterialNetwork& materialNetwork, bool includeParams = true)
{
std::cout << label << " material network for " << id << "\n";
std::string result;

std::cout << " --Node--\n";
for (HdMaterialNode const& node : mat.nodes) {
std::cout << " " << node.path << "\n";
std::cout << " " << node.identifier << "\n";
if (ARCH_LIKELY(!materialNetwork.nodes.empty())) {
const size_t numNodes = materialNetwork.nodes.size();

for (auto const& entry : node.parameters) {
std::cout << " param " << entry.first << ": " << TfStringify(entry.second) << "\n";
SdfPathVector paths;
paths.reserve(numNodes);

for (size_t i = 0; i < numNodes; i++) {
paths.push_back(materialNetwork.nodes[i].path);
}
}

std::cout << " --Connections--\n";
for (const HdMaterialRelationship& rel : mat.relationships) {
std::cout << " " << rel.inputId << "." << rel.inputName << "->" << rel.outputId << "."
<< rel.outputName << "\n";
}
// We use concise relative paths to generate the same string for duplicate material
// networks.
const SdfPathVector conciseRelativePaths = SdfPath::GetConciseRelativePaths(paths);

// Reserve enough memory to avoid memory reallocation.
result.reserve(1024);

result += "<nodes>\n";

if (includeParams) {
for (size_t i = 0; i < numNodes; i++) {
const HdMaterialNode& node = materialNetwork.nodes[i];

result += " <node path=\"";
result += conciseRelativePaths[i].GetString();
result += "\" id=\"";
result += node.identifier;
result += "\">\n";

result += " <params>\n";

for (auto const& parameter : node.parameters) {
result += " <param name=\"";
result += parameter.first;
result += "\" value=\"";
result += TfStringify(parameter.second);
result += "\"/>\n";
}

result += " </params>\n";

result += " </node>\n";
}
} else {
for (size_t i = 0; i < numNodes; i++) {
const HdMaterialNode& node = materialNetwork.nodes[i];

result += " <node path=\"";
result += conciseRelativePaths[i].GetString();
result += "\" id=\"";
result += node.identifier;
result += "\"/>\n";
}
}

result += "</nodes>\n";

if (!materialNetwork.relationships.empty()) {
std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> pathMappings;
for (size_t i = 0; i < numNodes; i++) {
pathMappings[paths[i]] = conciseRelativePaths[i];
}

std::cout << " --Primvars--\n";
for (TfToken const& primvar : mat.primvars) {
std::cout << " " << primvar << "\n";
result += "<relationships>\n";

for (const HdMaterialRelationship& rel : materialNetwork.relationships) {
result += " <rel from=\"";
result += pathMappings[rel.inputId].GetString();
result += ".";
result += rel.inputName;
result += "\" to=\"";
result += pathMappings[rel.outputId].GetString();
result += ".";
result += rel.outputName;
result += "\"/>\n";
}

result += "</relationships>\n";
}

if (!materialNetwork.primvars.empty()) {
result += "<primvars>\n";

for (TfToken const& primvar : materialNetwork.primvars) {
result += " <primvar name=\"";
result += primvar;
result += "\"/>\n";
}

result += "</primvars>\n";
}
}

return result;
}

//! Helper utility function to apply VP2-specific fixes to the material network.
Expand Down Expand Up @@ -591,17 +662,6 @@ _LoadTexture(const std::string& path, bool& isColorSpaceSRGB, MFloatArray& uvSca

} // anonymous namespace

/*! \brief Releases the reference to the shader owned by a smart pointer.
*/
void HdVP2ShaderDeleter::operator()(MHWRender::MShaderInstance* shader)
{
MRenderer* const renderer = MRenderer::theRenderer();
const MShaderManager* const shaderMgr = renderer ? renderer->getShaderManager() : nullptr;
if (TF_VERIFY(shaderMgr)) {
shaderMgr->releaseShader(shader);
}
}

/*! \brief Releases the reference to the texture owned by a smart pointer.
*/
void HdVP2TextureDeleter::operator()(MHWRender::MTexture* texture)
Expand Down Expand Up @@ -647,13 +707,37 @@ void HdVP2Material::Sync(
HdMaterialNetwork vp2BxdfNet;
_ApplyVP2Fixes(vp2BxdfNet, bxdfNet);

// Create a shader instance for the material network.
_surfaceShader.reset(_CreateShaderInstance(vp2BxdfNet));
// Generate a XML string from the material network and convert it to a token for
// faster hashing and comparison.
const TfToken bxdfNetToken(_GenerateXMLString(bxdfNet, false));

// Acquire a shader instance from the shader cache. If a shader instance has been
// cached with the same token, a clone of the shader instance will be returned.
// Multiple clones of a shader instance will share the same shader effect, thus
// reduce compilation overhead and enable MDI consolidation.
MHWRender::MShaderInstance* shader
= _renderDelegate->GetShaderFromCache(bxdfNetToken);

// If the shader instance is not found in the cache, create one from the material
// network and add a clone to the cache for reuse.
if (!shader) {
shader = _CreateShaderInstance(vp2BxdfNet);

if (shader) {
_renderDelegate->AddShaderToCache(bxdfNetToken, *shader);
}
}

// The shader instance is owned by the material solely.
_surfaceShader.reset(shader);

if (TfDebug::IsEnabled(HDVP2_DEBUG_MATERIAL)) {
_PrintMaterialNetwork("BXDF", id, bxdfNet);
_PrintMaterialNetwork("BXDF (with VP2 fixes)", id, vp2BxdfNet);
_PrintMaterialNetwork("Displacement", id, dispNet);
std::cout << "BXDF material network for " << id << ":\n"
<< _GenerateXMLString(bxdfNet) << "\n"
<< "BXDF (with VP2 fixes) material network for " << id << ":\n"
<< _GenerateXMLString(vp2BxdfNet) << "\n"
<< "Displacement material network for " << id << ":\n"
<< _GenerateXMLString(dispNet) << "\n";

if (_surfaceShader) {
auto tmpDir = boost::filesystem::temp_directory_path();
Expand Down
13 changes: 2 additions & 11 deletions lib/mayaUsd/render/vp2RenderDelegate/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#ifndef HD_VP2_MATERIAL
#define HD_VP2_MATERIAL

#include "shader.h"

#include <pxr/base/gf/vec2f.h>
#include <pxr/imaging/hd/material.h>
#include <pxr/pxr.h>
Expand All @@ -29,17 +31,6 @@ PXR_NAMESPACE_OPEN_SCOPE
class HdSceneDelegate;
class HdVP2RenderDelegate;

/*! \brief A deleter for MShaderInstance, for use with smart pointers.
*/
struct HdVP2ShaderDeleter
{
void operator()(MHWRender::MShaderInstance*);
};

/*! \brief A MShaderInstance owned by a std unique pointer.
*/
using HdVP2ShaderUniquePtr = std::unique_ptr<MHWRender::MShaderInstance, HdVP2ShaderDeleter>;

/*! \brief A deleter for MTexture, for use with smart pointers.
*/
struct HdVP2TextureDeleter
Expand Down
31 changes: 31 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,37 @@ MString HdVP2RenderDelegate::GetLocalNodeName(const MString& name) const
return MString(_id.AppendChild(TfToken(name.asChar())).GetText());
}

/*! \brief Returns a clone of the shader entry stored in the cache with the specified id.
*/
MHWRender::MShaderInstance* HdVP2RenderDelegate::GetShaderFromCache(const TfToken& id)
{
tbb::spin_rw_mutex::scoped_lock lock(_shaderCache._mutex, false /*write*/);

const auto it = _shaderCache._map.find(id);

const MHWRender::MShaderInstance* shader
= (it != _shaderCache._map.cend() ? it->second.get() : nullptr);
return (shader ? shader->clone() : nullptr);
}

/*! \brief Adds a clone of the shader to the cache with the specified id if it doesn't exist.
*/
bool HdVP2RenderDelegate::AddShaderToCache(
const TfToken& id,
const MHWRender::MShaderInstance& shader)
{
tbb::spin_rw_mutex::scoped_lock lock(_shaderCache._mutex, false /*write*/);

const auto it = _shaderCache._map.find(id);
if (it != _shaderCache._map.cend()) {
return false;
}

lock.upgrade_to_writer();
_shaderCache._map[id].reset(shader.clone());
return true;
}

/*! \brief Returns a fallback shader instance when no material is bound.

This method is keeping registry of all fallback shaders generated, allowing only
Expand Down
8 changes: 7 additions & 1 deletion lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "render_param.h"
#include "resource_registry.h"
#include "shader.h"

#include <pxr/imaging/hd/renderDelegate.h>
#include <pxr/imaging/hd/resourceRegistry.h>
Expand Down Expand Up @@ -136,6 +137,9 @@ class HdVP2RenderDelegate final : public HdRenderDelegate
MHWRender::MShaderInstance*
GetBasisCurvesCPVShader(const TfToken& curveType, const TfToken& curveBasis) const;

MHWRender::MShaderInstance* GetShaderFromCache(const TfToken& id);
bool AddShaderToCache(const TfToken& id, const MHWRender::MShaderInstance& shader);

const MHWRender::MSamplerState* GetSamplerState(const MHWRender::MSamplerStateDesc& desc) const;

const HdVP2BBoxGeom& GetSharedBBoxGeom() const;
Expand All @@ -156,9 +160,11 @@ class HdVP2RenderDelegate final : public HdRenderDelegate

std::unique_ptr<HdVP2RenderParam>
_renderParam; //!< Render param used to provided access to VP2 during prim synchronization
SdfPath _id; //!< Render delegate IDs
SdfPath _id; //!< Render delegate ID
HdVP2ResourceRegistry
_resourceRegistryVP2; //!< VP2 resource registry used for enqueue and execution of commits

HdVP2ShaderCache _shaderCache; //!< A thread-safe cache of named shaders.
};

PXR_NAMESPACE_CLOSE_SCOPE
Expand Down
34 changes: 34 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/shader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright 2020 Autodesk
//
// 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 "shader.h"

#include <pxr/base/tf/diagnostic.h>

PXR_NAMESPACE_OPEN_SCOPE

/*! \brief Releases the reference to the shader owned by a smart pointer.
*/
void HdVP2ShaderDeleter::operator()(MHWRender::MShaderInstance* shader)
{
MRenderer* const renderer = MRenderer::theRenderer();

const MShaderManager* const shaderMgr = renderer ? renderer->getShaderManager() : nullptr;
if (TF_VERIFY(shaderMgr)) {
shaderMgr->releaseShader(shader);
}
}

PXR_NAMESPACE_CLOSE_SCOPE
55 changes: 55 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/shader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// Copyright 2020 Autodesk
//
// 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.
//
#ifndef HD_VP2_SHADER
#define HD_VP2_SHADER

#include <pxr/base/tf/token.h>
#include <pxr/pxr.h>

#include <maya/MShaderManager.h>

#include <tbb/spin_rw_mutex.h>

#include <memory>
#include <unordered_map>

PXR_NAMESPACE_OPEN_SCOPE

/*! \brief A deleter for MShaderInstance, for use with smart pointers.
*/
struct HdVP2ShaderDeleter
{
void operator()(MHWRender::MShaderInstance*);
};

/*! \brief A MShaderInstance owned by a std::unique_ptr.
*/
using HdVP2ShaderUniquePtr = std::unique_ptr<MHWRender::MShaderInstance, HdVP2ShaderDeleter>;

/*! \brief Thread-safe cache of named shaders.
*/
struct HdVP2ShaderCache
{
//! Shader registry
std::unordered_map<TfToken, HdVP2ShaderUniquePtr, TfToken::HashFunctor> _map;

//! Synchronization used to protect concurrent read from serial writes
tbb::spin_rw_mutex _mutex;
};

PXR_NAMESPACE_CLOSE_SCOPE

#endif // HD_VP2_SHADER