From 344b34e54864e612b466c5be82a34986649b9b14 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Thu, 4 Aug 2022 16:19:04 -0400 Subject: [PATCH 1/2] Flush all resource caches on exit This makes sure textures and shaders are freed before VP2 shuts down. --- .../render/vp2RenderDelegate/material.cpp | 48 ++++++++++++++----- .../render/vp2RenderDelegate/material.h | 2 + .../vp2RenderDelegate/render_delegate.cpp | 23 +++++++++ .../vp2RenderDelegate/render_delegate.h | 2 + 4 files changed, 64 insertions(+), 11 deletions(-) diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp index 3e43bdf37f..3fd75ea694 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,16 @@ 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. +std::unordered_set pendingRemovalTextures; + // clang-format off TF_DEFINE_PRIVATE_TOKENS( _tokens, @@ -1734,18 +1745,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 pendingRemovalTextures; - static std::mutex removalTaskMutex; - if (!_IsDisabledAsyncTextureLoading() && !_localTextureMap.empty()) { + std::mutex removalTaskMutex; // Locking to avoid race condition for insertion to pendingRemovalTextures std::lock_guard lock(removalTaskMutex); @@ -3027,6 +3028,19 @@ 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( @@ -3034,6 +3048,11 @@ const HdVP2TextureInfo& HdVP2Material::_AcquireTexture( 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()) { @@ -3226,4 +3245,11 @@ void HdVP2Material::_MaterialChanged(HdSceneDelegate* sceneDelegate) #endif +void HdVP2Material::OnMayaExit() +{ + pendingRemovalTextures.clear(); + _globalTextureMap.clear(); + HdVP2RenderDelegate::OnMayaExit(); +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.h b/lib/mayaUsd/render/vp2RenderDelegate/material.h index c52d0f1d91..d640606d9a 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.h @@ -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 diff --git a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp index 04c9a865e1..73de8b6624 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.cpp @@ -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 @@ -588,6 +605,10 @@ HdVP2RenderDelegate::HdVP2RenderDelegate(ProxyRenderDelegate& drawScene) */ HdVP2RenderDelegate::~HdVP2RenderDelegate() { + CleanupMaterials(); + // Release Textures and Shader resources: + _materialSprims.clear(); + std::lock_guard guard(_renderDelegateMutex); if (_renderDelegateCounter.fetch_sub(1) == 1) { _resourceRegistry.reset(); @@ -1141,4 +1162,6 @@ void HdVP2RenderDelegate::CleanupMaterials() } } +void HdVP2RenderDelegate::OnMayaExit() { sShaderCache.OnMayaExit(); } + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h index 1fe4b64714..d677a9b02c 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/render_delegate.h @@ -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; From 5d7a88a5949785751a043141d0668836ee748e01 Mon Sep 17 00:00:00 2001 From: Jerry Gamache Date: Fri, 5 Aug 2022 09:49:55 -0400 Subject: [PATCH 2/2] Keep texture preservation code grouped --- .../render/vp2RenderDelegate/material.cpp | 67 ++++++++++++++----- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp index 3fd75ea694..01d4f003a2 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp @@ -125,7 +125,54 @@ namespace { // 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. -std::unordered_set pendingRemovalTextures; +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 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 _pendingRemovalTextures; + std::mutex _removalTaskMutex; + bool _isExiting = false; +}; // clang-format off TF_DEFINE_PRIVATE_TOKENS( @@ -1746,21 +1793,7 @@ HdVP2Material::~HdVP2Material() ClearPendingTasks(); if (!_IsDisabledAsyncTextureLoading() && !_localTextureMap.empty()) { - std::mutex removalTaskMutex; - // Locking to avoid race condition for insertion to pendingRemovalTextures - std::lock_guard 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); } } @@ -3247,7 +3280,7 @@ void HdVP2Material::_MaterialChanged(HdSceneDelegate* sceneDelegate) void HdVP2Material::OnMayaExit() { - pendingRemovalTextures.clear(); + _TransientTexturePreserver::GetInstance().OnMayaExit(); _globalTextureMap.clear(); HdVP2RenderDelegate::OnMayaExit(); }