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

speedup converters loading on application startup #78

Merged
merged 5 commits into from
Dec 2, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public struct ConverterConfig : IEquatable<ConverterConfig>

public string converterName;

public string converterType;

public List<KeyedConfig> settings;

public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@

namespace Newtonsoft.Json.UnityConverters.Configuration
{

#pragma warning disable CA2235 // Mark all non-serializable fields
[Serializable]
public sealed class UnityConvertersConfig : ScriptableObject
{
internal const string PATH = "Assets/Resources/Newtonsoft.Json-for-Unity.Converters.asset";
internal const string PATH_FOR_RESOURCES_LOAD = "Newtonsoft.Json-for-Unity.Converters";

public bool useBakedConverters = true;

public bool useUnityContractResolver = true;

public bool useAllOutsideConverters = true;
Expand All @@ -28,6 +31,7 @@ public sealed class UnityConvertersConfig : ScriptableObject
new ConverterConfig { converterName = typeof(StringEnumConverter).FullName, enabled = true },
new ConverterConfig { converterName = typeof(VersionConverter).FullName, enabled = true },
};

}
#pragma warning restore CA2235 // Mark all non-serializable fields
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ public class UnityConvertersConfigEditor : Editor
private SerializedProperty _unityConverters;
private SerializedProperty _useAllJsonNetConverters;
private SerializedProperty _jsonNetConverters;
private SerializedProperty _useBakingConverters;

private AnimBool _outsideConvertersShow;
private AnimBool _unityConvertersShow;
private AnimBool _jsonNetConvertersShow;

private UnityConvertersConfig _config;
private GUIStyle _headerStyle;
private GUIStyle _boldHeaderStyle;

Expand All @@ -50,13 +52,15 @@ private void OnEnable()
return;
}

_config = serializedObject.targetObject as UnityConvertersConfig;
_useUnityContractResolver = serializedObject.FindProperty(nameof(UnityConvertersConfig.useUnityContractResolver));
_useAllOutsideConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.useAllOutsideConverters));
_outsideConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.outsideConverters));
_useAllUnityConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.useAllUnityConverters));
_unityConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.unityConverters));
_useAllJsonNetConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.useAllJsonNetConverters));
_jsonNetConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.jsonNetConverters));
_useBakingConverters = serializedObject.FindProperty(nameof(UnityConvertersConfig.useBakedConverters));

_outsideConvertersShow = new AnimBool(_outsideConverters.isExpanded);
_unityConvertersShow = new AnimBool(_unityConverters.isExpanded);
Expand All @@ -72,6 +76,7 @@ private void OnEnable()
AddAndSetupConverters(_outsideConverters, outsideConverterTypes, _useAllOutsideConverters.boolValue);
AddAndSetupConverters(_unityConverters, unityConverterTypes, _useAllUnityConverters.boolValue);
AddAndSetupConverters(_jsonNetConverters, jsonNetConverterTypes, _useAllJsonNetConverters.boolValue);

serializedObject.ApplyModifiedProperties();
}

Expand All @@ -90,6 +95,18 @@ public override void OnInspectorGUI()
" 'UnityEngine.ScriptableObject' via 'ScriptableObject.Create()' instead of the default" +
" 'new ScriptableObject()'.");

ToggleLeft(_useBakingConverters, "If true - use baked converters at runtime");

EditorGUILayout.Separator();
EditorGUILayout.Space();

if (GUILayout.Button("Bake Json Converters"))
{
UnityConverterInitializer.BakeConverters(_config);
EditorUtility.SetDirty(_config);
AssetDatabase.SaveAssetIfDirty(_config);
}

EditorGUILayout.Space();

FoldedConverters(_useAllOutsideConverters, _outsideConverters, _outsideConvertersShow,
Expand Down Expand Up @@ -127,6 +144,7 @@ private void AddMissingConverters(SerializedProperty arrayProperty, IEnumerable<
var elementTypes = elements
.Select(e => TypeCache.FindType(e.FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue))
.ToArray();

Type[] missingConverters = converterTypes
.Where(type => !elementTypes.Contains(type))
.ToArray();
Expand All @@ -135,13 +153,15 @@ private void AddMissingConverters(SerializedProperty arrayProperty, IEnumerable<
{
int nextIndex = arrayProperty.arraySize;
arrayProperty.InsertArrayElementAtIndex(nextIndex);

SerializedProperty elemProp = arrayProperty.GetArrayElementAtIndex(nextIndex);

SerializedProperty enabledProp = elemProp.FindPropertyRelative(nameof(ConverterConfig.enabled));
SerializedProperty converterNameProp = elemProp.FindPropertyRelative(nameof(ConverterConfig.converterName));
SerializedProperty converterTypeProp = elemProp.FindPropertyRelative(nameof(ConverterConfig.converterType));

enabledProp.boolValue = newAreEnabledByDefault;
converterNameProp.stringValue = converterType.FullName;
converterTypeProp.stringValue = converterType.AssemblyQualifiedName;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace Newtonsoft.Json.UnityConverters
public static class UnityConverterInitializer
{
private static bool _shouldAddConvertsToDefaultSettings = true;

private static JsonSerializerSettings _defaultSettings;
/// <summary>
/// The default <see cref="JsonSerializerSettings"/> given by <c>Newtonsoft.Json-for-Unity.Converters</c>
/// </summary>
Expand Down Expand Up @@ -97,6 +97,48 @@ private static void UpdateDefaultSettings()
}
}

public static void BakeConverters(UnityConvertersConfig configuration)
{
_defaultSettings = null;

var outsideConverterTypes = FindCustomConverters().ToArray();
var unityConverterTypes = FindUnityConverters().ToArray();
var jsonNetConverterTypes = FindJsonNetConverters().ToArray();

UpdateConverter(configuration.unityConverters, unityConverterTypes);
UpdateConverter(configuration.outsideConverters, outsideConverterTypes);
UpdateConverter(configuration.jsonNetConverters, jsonNetConverterTypes);
}

public static void UpdateConverter(List<ConverterConfig> configs, Type[] types)
{
var configurations = new List<ConverterConfig>();

for (var i = 0; i < configs.Count; i++)
{
var config = configs[i];
foreach (var type in types)
{
var fullName = type.FullName;
if (fullName == null || !fullName
.Equals(config.converterName, StringComparison.OrdinalIgnoreCase)) continue;

var typeName = type.AssemblyQualifiedName;
if(string.IsNullOrEmpty(typeName)) break;

config.converterType = typeName;
var targetType = Type.GetType(typeName, false, true);
if(targetType == null) continue;

configurations.Add(config);
break;
}
}

configs.Clear();
configs.AddRange(configurations);
}

/// <summary>
/// Refreshes the settings that are found in the Resources folder
/// (specified in <see cref="UnityConvertersConfig.PATH_FOR_RESOURCES_LOAD"/>);
Expand All @@ -113,50 +155,57 @@ internal static JsonSerializerSettings GetExistingDefaultUnitySettings()

private static JsonSerializerSettings CreateJsonSettingsWithFreslyLoadedConfig()
{
//return new JsonSerializerSettings();
if (_defaultSettings != null) return _defaultSettings;

var config = Resources.Load<UnityConvertersConfig>(UnityConvertersConfig.PATH_FOR_RESOURCES_LOAD);

if (!config)
{
config = ScriptableObject.CreateInstance<UnityConvertersConfig>();
}

var settings = new JsonSerializerSettings {
_defaultSettings = new JsonSerializerSettings {
Converters = CreateConverters(config),
};

if (config.useUnityContractResolver)
{
settings.ContractResolver = new UnityTypeContractResolver();
_defaultSettings.ContractResolver = new UnityTypeContractResolver();
}

return settings;
return _defaultSettings;
}

/// <summary>
/// Create the converter instances.
/// </summary>
/// <returns>The converters.</returns>
private static List<JsonConverter> CreateConverters(UnityConvertersConfig config)
public static List<JsonConverter> CreateConverters(UnityConvertersConfig config)
{
var customs = FindFilteredCustomConverters(config)
.Concat(FindFilteredUnityConverters(config))
.Concat(FindFilteredJsonNetConverters(config))
.Select(type => CreateConverter(type))
.Where(o => o != null);

return new List<JsonConverter>(customs);
var converterTypes = new List<Type>();
var result = new List<JsonConverter>();

converterTypes.AddRange(FindFilteredCustomConverters(config));
converterTypes.AddRange(FindFilteredUnityConverters(config));
converterTypes.AddRange(FindFilteredJsonNetConverters(config));

foreach (var type in converterTypes)
{
var converter = CreateConverter(type);
if(converter == null) continue;
result.Add(converter);
}

return result;
}

private static IEnumerable<Type> FindFilteredCustomConverters(UnityConvertersConfig config)
{
return ApplyConfigFilter(FindCustomConverters(), config.useAllOutsideConverters, config.outsideConverters);
}

/// <summary>
/// Find all the valid converter types outside of Newtonsoft.Json namespaces.
/// </summary>
/// <returns>The types.</returns>
internal static IEnumerable<Type> FindCustomConverters()
public static IEnumerable<Type> FindCustomConverters()
{
var typesFromOtherDomains = AppDomain.CurrentDomain.GetAssemblies()
.Select(dll => dll.GetLoadableTypes()
Expand All @@ -166,17 +215,13 @@ internal static IEnumerable<Type> FindCustomConverters()

return FilterToJsonConvertersAndOrder(typesFromOtherDomains);
}

private static IEnumerable<Type> FindFilteredUnityConverters(UnityConvertersConfig config)
{
return ApplyConfigFilter(FindUnityConverters(), config.useAllUnityConverters, config.unityConverters);
}


/// <summary>
/// Find all the valid converter types inside this assembly, <c>Newtonsoft.Json.UnityConverters</c>
/// </summary>
/// <returns>The types.</returns>
internal static IEnumerable<Type> FindUnityConverters()
public static IEnumerable<Type> FindUnityConverters()
{
var typesFromPackageDomains = AppDomain.CurrentDomain.GetAssemblies()
.Select(dll => dll.GetLoadableTypes()
Expand All @@ -187,21 +232,44 @@ internal static IEnumerable<Type> FindUnityConverters()
return FilterToJsonConvertersAndOrder(typesFromPackageDomains);
}

private static IEnumerable<Type> FindFilteredUnityConverters(UnityConvertersConfig config)
{
var unityTypes = config.useBakedConverters
? GetUnityConvertersTypes(config)
: FindUnityConverters();

return ApplyConfigFilter(unityTypes, config.useAllUnityConverters, config.unityConverters);
}

private static IEnumerable<Type> FindFilteredCustomConverters(UnityConvertersConfig config)
{
var customTypes = config.useBakedConverters
? GetCustomConvertersTypes(config)
: FindCustomConverters();

return ApplyConfigFilter(customTypes, config.useAllOutsideConverters, config.outsideConverters);
}

private static IEnumerable<Type> FindFilteredJsonNetConverters(UnityConvertersConfig config)
{
return ApplyConfigFilter(FindJsonNetConverters(), config.useAllJsonNetConverters, config.jsonNetConverters);
var converterTypes = config.useBakedConverters
? GetJsonNetConvertersTypes(config)
: FindJsonNetConverters();

return ApplyConfigFilter(converterTypes, config.useAllJsonNetConverters, config.jsonNetConverters);
}

/// <summary>
/// Finds all the valid converter types inside the <c>Newtonsoft.Json</c> assembly.
/// </summary>
/// <returns>The types.</returns>
internal static IEnumerable<Type> FindJsonNetConverters()
public static IEnumerable<Type> FindJsonNetConverters()
{
return FilterToJsonConvertersAndOrder(typeof(JsonConverter).Assembly.GetTypes());
var types = typeof(JsonConverter).Assembly.GetTypes();
return FilterToJsonConvertersAndOrder(types);
}

private static IEnumerable<Type> FilterToJsonConvertersAndOrder(IEnumerable<Type> types)
public static IEnumerable<Type> FilterToJsonConvertersAndOrder(IEnumerable<Type> types)
{
return types
.Where(type
Expand Down Expand Up @@ -250,10 +318,56 @@ private static JsonConverter CreateConverter(Type jsonConverterType)
}
catch (Exception exception)
{
Debug.LogErrorFormat("Cannot create JsonConverter '{0}':\n{1}", jsonConverterType.FullName, exception);
Debug.LogErrorFormat("Cannot create JsonConverter '{0}':\n{1}", jsonConverterType?.FullName, exception);
}

return null;
}


public static IEnumerable<Type> GetCustomConvertersTypes(UnityConvertersConfig config)
{
var customConvertersTypes = config.outsideConverters;
var types = ConvertTypes(customConvertersTypes, config.useAllOutsideConverters);
foreach (var type in types)
yield return type;
}

public static IEnumerable<Type> GetUnityConvertersTypes(UnityConvertersConfig config)
{
var unityConvertersTypes = config.unityConverters;
var types = ConvertTypes(unityConvertersTypes, config.useAllUnityConverters);
foreach (var type in types)
yield return type;
}

public static IEnumerable<Type> GetJsonNetConvertersTypes(UnityConvertersConfig config)
{
var unityConvertersTypes = config.jsonNetConverters;
var types = ConvertTypes(unityConvertersTypes, config.useAllJsonNetConverters);
foreach (var type in types)
yield return type;
}

private static IEnumerable<Type> ConvertTypes(IEnumerable<ConverterConfig> items,bool useAll)
{
foreach (var item in items)
{
if(!useAll && !item.enabled) continue;

var typeValue = string.IsNullOrEmpty(item.converterType)
? string.Empty
: item.converterType;

var type = Type.GetType(typeValue, false, true);
#if UNITY_EDITOR
if (type == null)
{
Debug.LogErrorFormat("JsonConverter Type is NULL for {0} : {1}", item.converterName, item.converterType);
}
#endif
yield return type;
}
}
}
}