fix: performance issues when RO Simulator is open for too long (#1101)

Also fix an issue where the clear overrides button doesn't work for menu item overrides.

Closes: #1100
This commit is contained in:
bd_ 2024-09-04 12:40:48 -07:00 committed by GitHub
parent 422ed5cfb1
commit 389ae4f2cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 61 additions and 40 deletions

View File

@ -93,6 +93,17 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
private Dictionary<(int, string), bool> foldoutState = new(); private Dictionary<(int, string), bool> foldoutState = new();
private Button _btn_clear; private Button _btn_clear;
private bool _refreshPending;
private void RequestRefresh()
{
if (_refreshPending) return;
_refreshPending = true;
EditorApplication.delayCall += RefreshUI;
}
private void UpdatePropertyOverride(string prop, bool? enable, float f_val) private void UpdatePropertyOverride(string prop, bool? enable, float f_val)
{ {
if (enable == null) if (enable == null)
@ -107,7 +118,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
PropertyOverrides.Value = PropertyOverrides.Value.SetItem(prop, 0f); PropertyOverrides.Value = PropertyOverrides.Value.SetItem(prop, 0f);
} }
EditorApplication.delayCall += RefreshUI; RequestRefresh();
} }
private void UpdateMenuItemOverride(string prop, ModularAvatarMenuItem item, bool? value) private void UpdateMenuItemOverride(string prop, ModularAvatarMenuItem item, bool? value)
@ -126,21 +137,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
MenuItemOverrides.Value = MenuItemOverrides.Value.SetItem(prop, null); MenuItemOverrides.Value = MenuItemOverrides.Value.SetItem(prop, null);
} }
EditorApplication.delayCall += RefreshUI; RequestRefresh();
}
private void UpdatePropertyOverride(string prop, bool? value)
{
if (value == null)
{
PropertyOverrides.Value = PropertyOverrides.Value.Remove(prop);
}
else
{
PropertyOverrides.Value = PropertyOverrides.Value.SetItem(prop, value.Value ? 1f : 0f);
}
RefreshUI();
} }
private void ShowButton(Rect rect) private void ShowButton(Rect rect)
@ -194,7 +191,8 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
_btn_clear.clickable.clicked += () => _btn_clear.clickable.clicked += () =>
{ {
PropertyOverrides.Value = ImmutableDictionary<string, float>.Empty; PropertyOverrides.Value = ImmutableDictionary<string, float>.Empty;
RefreshUI(); MenuItemOverrides.Value = ImmutableDictionary<string, ModularAvatarMenuItem>.Empty;
RequestRefresh();
}; };
e_debugInfo = root.Q<VisualElement>("debug-info"); e_debugInfo = root.Q<VisualElement>("debug-info");
@ -209,11 +207,13 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
currentSelection = locked ? f_inspecting.value as GameObject : Selection.activeGameObject; currentSelection = locked ? f_inspecting.value as GameObject : Selection.activeGameObject;
f_inspecting.SetValueWithoutNotify(currentSelection); f_inspecting.SetValueWithoutNotify(currentSelection);
RefreshUI(); RequestRefresh();
} }
private void RefreshUI() private void RefreshUI()
{ {
_refreshPending = false;
var avatar = RuntimeUtil.FindAvatarInParents(currentSelection?.transform); var avatar = RuntimeUtil.FindAvatarInParents(currentSelection?.transform);
if (avatar == null) if (avatar == null)
@ -222,7 +222,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
return; return;
} }
_btn_clear.SetEnabled(!PropertyOverrides.Value.IsEmpty); _btn_clear.SetEnabled(!PropertyOverrides.Value.IsEmpty || !MenuItemOverrides.Value.IsEmpty);
e_debugInfo.style.display = DisplayStyle.Flex; e_debugInfo.style.display = DisplayStyle.Flex;
@ -243,7 +243,7 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
{ {
if (self.is_enabled) if (self.is_enabled)
{ {
self.RefreshUI(); self.RequestRefresh();
} }
} }
@ -254,6 +254,9 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
BindOverrideToMenuItem("this-menu-override", mami); BindOverrideToMenuItem("this-menu-override", mami);
} }
private string _menuItemOverrideProperty;
private ModularAvatarMenuItem _menuItemOverrideTarget;
private void BindOverrideToMenuItem(string overrideElemName, ModularAvatarMenuItem mami) private void BindOverrideToMenuItem(string overrideElemName, ModularAvatarMenuItem mami)
{ {
var elem = e_debugInfo.Q<VisualElement>(overrideElemName); var elem = e_debugInfo.Q<VisualElement>(overrideElemName);
@ -279,9 +282,20 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
else else
soc.SetWithoutNotify(null); soc.SetWithoutNotify(null);
soc.OnStateOverrideChanged += value => { UpdateMenuItemOverride(prop, mami, value); }; // Avoid multiple registration of the same delegate here by reusing the same delegate instead of binding
// these properties in a closure
_menuItemOverrideProperty = prop;
_menuItemOverrideTarget = mami;
soc.OnStateOverrideChanged += MenuItemOverrideChanged;
} }
private void MenuItemOverrideChanged(bool? obj)
{
UpdateMenuItemOverride(_menuItemOverrideProperty, _menuItemOverrideTarget, obj);
}
private string _propertyOverrideProperty;
private float _propertyOverrideTargetValue;
private void BindOverrideToParameter(string overrideElemName, string property, float targetValue) private void BindOverrideToParameter(string overrideElemName, string property, float targetValue)
{ {
var elem = e_debugInfo.Q<VisualElement>(overrideElemName); var elem = e_debugInfo.Q<VisualElement>(overrideElemName);
@ -303,10 +317,14 @@ namespace nadena.dev.modular_avatar.core.editor.Simulator
soc.SetWithoutNotify(null); soc.SetWithoutNotify(null);
} }
soc.OnStateOverrideChanged += value => _propertyOverrideProperty = property;
_propertyOverrideTargetValue = targetValue;
soc.OnStateOverrideChanged += OnParameterOverrideChanged;
}
private void OnParameterOverrideChanged(bool? state)
{ {
UpdatePropertyOverride(property, value, targetValue); UpdatePropertyOverride(_propertyOverrideProperty, state, _propertyOverrideTargetValue);
};
} }
private void SetAffectedBy(GameObject gameObject, Dictionary<TargetProp, AnimatedProperty> shapes) private void SetAffectedBy(GameObject gameObject, Dictionary<TargetProp, AnimatedProperty> shapes)

View File

@ -9,6 +9,7 @@ namespace nadena.dev.modular_avatar.core
[Serializable] [Serializable]
public class AvatarObjectReference public class AvatarObjectReference
{ {
private static long HIERARCHY_CHANGED_SEQ = long.MinValue;
private long ReferencesLockedAtFrame = long.MinValue; private long ReferencesLockedAtFrame = long.MinValue;
public static string AVATAR_ROOT = "$$$AVATAR_ROOT$$$"; public static string AVATAR_ROOT = "$$$AVATAR_ROOT$$$";
@ -16,10 +17,19 @@ namespace nadena.dev.modular_avatar.core
[SerializeField] internal GameObject targetObject; [SerializeField] internal GameObject targetObject;
private long _cacheSeq = long.MinValue;
private bool _cacheValid; private bool _cacheValid;
private string _cachedPath; private string _cachedPath;
private GameObject _cachedReference; private GameObject _cachedReference;
#if UNITY_EDITOR
[InitializeOnLoadMethod]
private static void Init()
{
EditorApplication.hierarchyChanged += () => HIERARCHY_CHANGED_SEQ += 1;
}
#endif
public AvatarObjectReference Clone() public AvatarObjectReference Clone()
{ {
return new AvatarObjectReference return new AvatarObjectReference
@ -54,10 +64,12 @@ namespace nadena.dev.modular_avatar.core
public GameObject Get(Component container) public GameObject Get(Component container)
{ {
bool cacheValid = _cacheValid || ReferencesLockedAtFrame == Time.frameCount; bool cacheValid = _cacheValid || ReferencesLockedAtFrame == Time.frameCount;
cacheValid &= HIERARCHY_CHANGED_SEQ == _cacheSeq;
if (cacheValid && _cachedPath == referencePath && _cachedReference != null) return _cachedReference; if (cacheValid && _cachedPath == referencePath && _cachedReference != null) return _cachedReference;
_cacheValid = true; _cacheValid = true;
_cacheSeq = HIERARCHY_CHANGED_SEQ;
_cachedPath = referencePath; _cachedPath = referencePath;
if (string.IsNullOrEmpty(referencePath)) if (string.IsNullOrEmpty(referencePath))
@ -66,9 +78,6 @@ namespace nadena.dev.modular_avatar.core
return _cachedReference; return _cachedReference;
} }
RuntimeUtil.OnHierarchyChanged -= InvalidateCache;
RuntimeUtil.OnHierarchyChanged += InvalidateCache;
var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(container.transform); var avatarTransform = RuntimeUtil.FindAvatarTransformInParents(container.transform);
if (avatarTransform == null) return (_cachedReference = null); if (avatarTransform == null) return (_cachedReference = null);
@ -130,12 +139,6 @@ namespace nadena.dev.modular_avatar.core
return avatarRoot.transform.Find(referencePath)?.gameObject == targetObject; return avatarRoot.transform.Find(referencePath)?.gameObject == targetObject;
} }
private void InvalidateCache()
{
RuntimeUtil.OnHierarchyChanged -= InvalidateCache;
_cacheValid = false;
}
protected bool Equals(AvatarObjectReference other) protected bool Equals(AvatarObjectReference other)
{ {
return GetDirectTarget() == other.GetDirectTarget() && referencePath == other.referencePath; return GetDirectTarget() == other.GetDirectTarget() && referencePath == other.referencePath;