Skip to content

Commit

Permalink
Merge pull request #2529 from Autodesk/t_gamaj/fix_assert_on_maya_exit
Browse files Browse the repository at this point in the history
Flush all resource caches on exit
  • Loading branch information
seando-adsk authored Aug 22, 2022
2 parents 927bbaf + 5d7a88a commit a6f0b8d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 25 deletions.
109 changes: 84 additions & 25 deletions lib/mayaUsd/render/vp2RenderDelegate/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <maya/MFragmentManager.h>
#include <maya/MGlobal.h>
#include <maya/MProfiler.h>
#include <maya/MSceneMessage.h>
#include <maya/MShaderManager.h>
#include <maya/MStatus.h>
#include <maya/MString.h>
Expand Down Expand Up @@ -116,6 +117,63 @@ static const std::size_t kRefreshDuration { 1000 };

namespace {

// USD `UsdImagingDelegate::ApplyPendingUpdates()` would request to
// remove the material then recreate, this is causing texture disappearing
// when user manipulating a prim (while holding mouse buttion).
// We hold a copy of the texture info reference, so that the texture will not
// get released immediately along with material removal.
// If the textures would have been requested to reload in `ApplyPendingUpdates()`,
// we could still reuse the loaded one from cache, otherwise the idle task can
// safely release the texture.
class _TransientTexturePreserver
{
public:
static _TransientTexturePreserver& GetInstance()
{
static _TransientTexturePreserver sInstance;
return sInstance;
}

void PreserveTextures(HdVP2LocalTextureMap& localTextureMap)
{
if (_isExiting) {
return;
}

// Locking to avoid race condition for insertion to pendingRemovalTextures
std::lock_guard<std::mutex> lock(_removalTaskMutex);

// Avoid creating multiple idle tasks if there is already one
bool hasRemovalTask = !_pendingRemovalTextures.empty();
for (const auto& info : localTextureMap) {
_pendingRemovalTextures.emplace(info.second);
}

if (!hasRemovalTask) {
// Note that we do not need locking inside idle task since it will
// only be executed serially.
MGlobal::executeTaskOnIdle(
[](void* data) { _TransientTexturePreserver::GetInstance().Clear(); });
}
}

void Clear() { _pendingRemovalTextures.clear(); }

void OnMayaExit()
{
_isExiting = true;
Clear();
}

private:
_TransientTexturePreserver() = default;
~_TransientTexturePreserver() = default;

std::unordered_set<HdVP2TextureInfoSharedPtr> _pendingRemovalTextures;
std::mutex _removalTaskMutex;
bool _isExiting = false;
};

// clang-format off
TF_DEFINE_PRIVATE_TOKENS(
_tokens,
Expand Down Expand Up @@ -1734,32 +1792,8 @@ HdVP2Material::~HdVP2Material()
// Tell pending tasks or running tasks (if any) to terminate
ClearPendingTasks();

// USD `UsdImagingDelegate::ApplyPendingUpdates()` would request to
// remove the material then recreate, this is causing texture disappearing
// when user manipulating a prim (while holding mouse buttion).
// We hold a copy of the texture info reference, so that the texture will not
// get released immediately along with material removal.
// If the textures would have been requested to reload in `ApplyPendingUpdates()`,
// we could still reuse the loaded one from cache, otherwise the idle task can
// safely release the texture.
static std::unordered_set<HdVP2TextureInfoSharedPtr> pendingRemovalTextures;
static std::mutex removalTaskMutex;

if (!_IsDisabledAsyncTextureLoading() && !_localTextureMap.empty()) {
// Locking to avoid race condition for insertion to pendingRemovalTextures
std::lock_guard<std::mutex> lock(removalTaskMutex);

// Avoid creating multiple idle tasks if there is already one
bool hasRemovalTask = !pendingRemovalTextures.empty();
for (const auto& info : _localTextureMap) {
pendingRemovalTextures.emplace(info.second);
}

if (!hasRemovalTask) {
// Note that we do not need locking inside idle task since it will
// only be executed serially.
MGlobal::executeTaskOnIdle([](void* data) { pendingRemovalTextures.clear(); });
}
_TransientTexturePreserver::GetInstance().PreserveTextures(_localTextureMap);
}
}

Expand Down Expand Up @@ -3027,13 +3061,31 @@ void HdVP2Material::_UpdateShaderInstance(
}
}

namespace {

void exitingCallback(void* /* unusedData */)
{
// Maya does not unload plugins on exit. Make sure we perform an orderly
// cleanup of the texture cache. Otherwise the cache will clear after VP2
// has shut down.
HdVP2Material::OnMayaExit();
}

MCallbackId gExitingCbId = 0;
} // namespace

/*! \brief Acquires a texture for the given image path.
*/
const HdVP2TextureInfo& HdVP2Material::_AcquireTexture(
HdSceneDelegate* sceneDelegate,
const std::string& path,
const HdMaterialNode& node)
{
// Register for the Maya exit message
if (!gExitingCbId) {
gExitingCbId = MSceneMessage::addCallback(MSceneMessage::kMayaExiting, exitingCallback);
}

// see if we already have the texture loaded.
const auto it = _globalTextureMap.find(path);
if (it != _globalTextureMap.end()) {
Expand Down Expand Up @@ -3226,4 +3278,11 @@ void HdVP2Material::_MaterialChanged(HdSceneDelegate* sceneDelegate)

#endif

void HdVP2Material::OnMayaExit()
{
_TransientTexturePreserver::GetInstance().OnMayaExit();
_globalTextureMap.clear();
HdVP2RenderDelegate::OnMayaExit();
}

PXR_NAMESPACE_CLOSE_SCOPE
2 changes: 2 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ class HdVP2Material final : public HdMaterial
class TextureLoadingTask;
friend class TextureLoadingTask;

static void OnMayaExit();

private:
void _ApplyVP2Fixes(HdMaterialNetwork& outNet, const HdMaterialNetwork& inNet);
#ifdef WANT_MATERIALX_BUILD
Expand Down
23 changes: 23 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,23 @@ class MShaderCache final
}
#endif

void OnMayaExit()
{
if (_isInitialized) {
for (size_t i = 0; i < FallbackShaderTypeCount; ++i) {
_fallbackShaders[i]._map.clear();
_fallbackCPVShaders[i] = nullptr;
}
_3dSolidShaders._map.clear();
_3dFatPointShaders._map.clear();
_userCache._map.clear();
_3dDefaultMaterialShader = nullptr;
_3dCPVSolidShader = nullptr;
_3dCPVFatPointShader = nullptr;
}
_isInitialized = false;
}

private:
bool _isInitialized { false }; //!< Whether the shader cache is initialized

Expand Down Expand Up @@ -588,6 +605,10 @@ HdVP2RenderDelegate::HdVP2RenderDelegate(ProxyRenderDelegate& drawScene)
*/
HdVP2RenderDelegate::~HdVP2RenderDelegate()
{
CleanupMaterials();
// Release Textures and Shader resources:
_materialSprims.clear();

std::lock_guard<std::mutex> guard(_renderDelegateMutex);
if (_renderDelegateCounter.fetch_sub(1) == 1) {
_resourceRegistry.reset();
Expand Down Expand Up @@ -1141,4 +1162,6 @@ void HdVP2RenderDelegate::CleanupMaterials()
}
}

void HdVP2RenderDelegate::OnMayaExit() { sShaderCache.OnMayaExit(); }

PXR_NAMESPACE_CLOSE_SCOPE
2 changes: 2 additions & 0 deletions lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class HdVP2RenderDelegate final : public HdRenderDelegate

static const int sProfilerCategory; //!< Profiler category

static void OnMayaExit();

private:
HdVP2RenderDelegate(const HdVP2RenderDelegate&) = delete;
HdVP2RenderDelegate& operator=(const HdVP2RenderDelegate&) = delete;
Expand Down

0 comments on commit a6f0b8d

Please sign in to comment.