You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
19 KiB
373 lines
19 KiB
using UnityEditorInternal;
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.HighDefinition;
|
|
using UnityEngine.Serialization;
|
|
using UnityEditor.Rendering.HighDefinition.ShaderGraph;
|
|
|
|
namespace UnityEditor.Rendering.HighDefinition
|
|
{
|
|
//As ScriptableSingleton is not usable due to internal FilePathAttribute,
|
|
//copying mechanism here
|
|
sealed class HDProjectSettings : HDProjectSettingsReadOnlyBase, IVersionable<HDProjectSettings.Version>
|
|
{
|
|
[SerializeField]
|
|
bool m_WizardPopupAtStart = true;
|
|
|
|
// Note: this is for the *material version*, which uses MaterialPostProcessor k_Migrations[] as migration
|
|
// functions table. See note on Version enum above.
|
|
[SerializeField]
|
|
int m_LastMaterialVersion = k_NeverProcessedMaterialVersion;
|
|
|
|
[SerializeField]
|
|
ShaderGraphVersion m_HDShaderGraphLastSeenVersion = ShaderGraphVersion.NeverMigrated;
|
|
|
|
// This tracks latest versions of every plugin subtargets ever seen in the project.
|
|
// The dictionary key is the SubTarget GUID.
|
|
// If the project has ever seen an external-to-HDRP ("plugin") material, it saves the version
|
|
// of the material here. That way, we can use the same logic to forward upgrade calls
|
|
// than for the internally known HDRP materials: if a plugin material version is higher than
|
|
// what is seen here, we can trigger a re-import (see RegisterUpgraderReimport in MaterialPostProcessor).
|
|
[SerializeField]
|
|
PluginMaterialVersions m_PluginMaterialVersions = new PluginMaterialVersions();
|
|
|
|
// Same as above but for the *subtarget* versioning specific to the subtarget (ie not HDRP ShaderGraphVersion,
|
|
// see m_HDShaderGraphLastSeenVersion above for that).
|
|
[SerializeField]
|
|
PluginMaterialVersions m_PluginSubTargetVersions = new PluginMaterialVersions();
|
|
|
|
// The following filters m_PluginMaterialVersions on load/save based on plugin subtargets
|
|
// with the code base actually still present in the project.
|
|
// Unused for now as we can't filter materials per active shader type so we reimport all materials
|
|
// at the same time.
|
|
[NonSerialized]
|
|
Dictionary<GUID, (int, int)> m_InProjectPluginLastSeenMaterialAndSubTargetVersions = new Dictionary<GUID, (int, int)>();
|
|
|
|
/// <summary>
|
|
/// This is to quickly decide if some materials depending on plugin subtargets
|
|
/// (that have actually their plugin code present in the project) might need upgrade.
|
|
/// See pluginSubTargetMaterialVersionSum in HDShaderUtils: summed versions in here
|
|
/// should never be allowed to be higher than that returned by the currently present
|
|
/// (in project) plugin SubTarget codebase - ie that returned by
|
|
/// <by cref="IPluginSubTargetMaterialUtils"/>.
|
|
/// <See also cref="MaterialReimporter.RegisterUpgraderReimport"/>
|
|
/// </summary>
|
|
[NonSerialized]
|
|
long m_InProjectPluginLastSeenMaterialVersionsSum = (int)PluginMaterial.GenericVersions.NeverMigrated;
|
|
|
|
[NonSerialized]
|
|
long m_InProjectPluginLastSeenSubTargetVersionsSum = (int)PluginMaterial.GenericVersions.NeverMigrated;
|
|
|
|
internal const int k_NeverProcessedMaterialVersion = -1;
|
|
|
|
public static new string projectSettingsFolderPath
|
|
{
|
|
get => instance.m_ProjectSettingFolderPath;
|
|
set
|
|
{
|
|
instance.m_ProjectSettingFolderPath = value;
|
|
Save();
|
|
}
|
|
}
|
|
|
|
public static bool wizardIsStartPopup
|
|
{
|
|
get => instance.m_WizardPopupAtStart;
|
|
set
|
|
{
|
|
instance.m_WizardPopupAtStart = value;
|
|
Save();
|
|
}
|
|
}
|
|
|
|
public static int materialVersionForUpgrade
|
|
{
|
|
get => instance.m_LastMaterialVersion;
|
|
set
|
|
{
|
|
instance.m_LastMaterialVersion = value;
|
|
Save();
|
|
}
|
|
}
|
|
|
|
public static ShaderGraphVersion hdShaderGraphLastSeenVersion
|
|
{
|
|
get => instance.m_HDShaderGraphLastSeenVersion;
|
|
set
|
|
{
|
|
instance.m_HDShaderGraphLastSeenVersion = value;
|
|
Save();
|
|
}
|
|
}
|
|
|
|
public static long pluginSubTargetLastSeenMaterialVersionsSum
|
|
{
|
|
get => instance.m_InProjectPluginLastSeenMaterialVersionsSum;
|
|
}
|
|
|
|
public static long pluginSubTargetLastSeenSubTargetVersionsSum
|
|
{
|
|
get => instance.m_InProjectPluginLastSeenSubTargetVersionsSum;
|
|
}
|
|
|
|
//singleton pattern
|
|
static HDProjectSettings instance
|
|
{
|
|
get
|
|
{
|
|
//In case we are early loading it through HDProjectSettingsReadOnlyBase, migration can have not been done.
|
|
//To not create an infinite callstack loop through "instance", destroy it to force reloading it.
|
|
//(migration is done at loading time)
|
|
if (s_Instance != null && (!(s_Instance is HDProjectSettings inst) || inst.m_Version != MigrationDescription.LastVersion<Version>()))
|
|
{
|
|
if (!(s_Instance is HDProjectSettings))
|
|
Debug.Log($"Not a HDProjectSettings: {s_Instance?.GetType()?.ToString() ?? "null" }");
|
|
else
|
|
Debug.Log($"Version: {(s_Instance as HDProjectSettings).m_Version}");
|
|
DestroyImmediate(s_Instance);
|
|
s_Instance = null;
|
|
}
|
|
|
|
return s_Instance as HDProjectSettings ?? CreateOrLoad();
|
|
}
|
|
}
|
|
|
|
//// We force the instance to be loaded/created and ready with valid values on assembly reload.
|
|
[InitializeOnLoadMethod]
|
|
static void InitializeFillPresentPluginMaterialVersionsOnLoad()
|
|
{
|
|
// Make sure the cached last seen plugin versions (capped to codebase versions) and their sum is valid
|
|
// on assembly reload.
|
|
instance.FillPresentPluginMaterialVersions();
|
|
}
|
|
|
|
void FillPresentPluginMaterialVersions()
|
|
{
|
|
//m_InProjectPluginLastSeenMaterialVersions.Clear();
|
|
m_InProjectPluginLastSeenMaterialAndSubTargetVersions.Clear();
|
|
m_InProjectPluginLastSeenMaterialVersionsSum = 0;
|
|
m_InProjectPluginLastSeenSubTargetVersionsSum = 0;
|
|
|
|
int pluginSubTargetsSeenInHDProjectSettings = 0;
|
|
|
|
foreach (var entry in m_PluginMaterialVersions)
|
|
{
|
|
if (!m_PluginSubTargetVersions.TryGetValue(entry.Key, out int subTargetVer))
|
|
{
|
|
subTargetVer = (int)PluginMaterial.GenericVersions.NeverMigrated;
|
|
}
|
|
else
|
|
{
|
|
pluginSubTargetsSeenInHDProjectSettings++;
|
|
}
|
|
|
|
if (HDShaderUtils.GetMaterialPluginSubTarget(entry.Key, out IPluginSubTargetMaterialUtils subTarget))
|
|
{
|
|
try
|
|
{
|
|
// We clamp from above the last seen version saved in HDRP project settings by the latest
|
|
// known version of the code base as a precaution so we don't constantly try to upgrade
|
|
// potentially newer materials while the code base was seemingly downgraded (an unsupported
|
|
// scenario anyway).
|
|
// Also, the premise that all versions bookkept here are *each* lower or equal to the corresponding
|
|
// code base version is necessary for the plugin version check to be simplified to just checking
|
|
// the sum of all possible plugin versions present.
|
|
int matVer = Math.Min(subTarget.latestMaterialVersion, entry.Value);
|
|
subTargetVer = Math.Min(subTarget.latestSubTargetVersion, subTargetVer);
|
|
m_InProjectPluginLastSeenMaterialAndSubTargetVersions.Add(entry.Key, (matVer, subTargetVer));
|
|
m_InProjectPluginLastSeenMaterialVersionsSum += matVer;
|
|
m_InProjectPluginLastSeenSubTargetVersionsSum += subTargetVer;
|
|
}
|
|
catch
|
|
{
|
|
Debug.LogError("Malformed HDProjectSettings.asset: duplicate plugin SubTarget GUID");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_InProjectPluginLastSeenMaterialAndSubTargetVersions.Count == 0)
|
|
{
|
|
// This means that either we don't have any plugin SubTarget in the project or HDProjectSettings indicates
|
|
// we never saw any one of them yet. Since IPluginSubTargetMaterialUtils.latestMaterialVersion can never
|
|
// be == 0 (never migrated version), we make sure the importer will pick up the materials to be patched at least
|
|
// for the initial migration: we set the "seen material versions" sum to a value that the code base latestversions
|
|
// sum will always exceed:
|
|
m_InProjectPluginLastSeenMaterialVersionsSum = (int)PluginMaterial.GenericVersions.NeverMigrated;
|
|
}
|
|
if (pluginSubTargetsSeenInHDProjectSettings == 0)
|
|
{
|
|
m_InProjectPluginLastSeenSubTargetVersionsSum = (int)PluginMaterial.GenericVersions.NeverMigrated;
|
|
}
|
|
}
|
|
|
|
public static void UpdateLastSeenMaterialVersionsOfPluginSubTargets()
|
|
{
|
|
var allPluginSubTargets = HDShaderUtils.GetHDPluginSubTargets();
|
|
|
|
foreach (var entry in allPluginSubTargets)
|
|
{
|
|
var subTarget = entry.Value;
|
|
if (instance.m_PluginMaterialVersions.TryGetValue(entry.Key, out int lastSeenVersion))
|
|
{
|
|
if (subTarget.latestMaterialVersion > lastSeenVersion)
|
|
{
|
|
instance.m_PluginMaterialVersions[entry.Key] = subTarget.latestMaterialVersion;
|
|
}
|
|
// else SubTarget plugin downgraded or same version, nothing to do
|
|
}
|
|
else
|
|
{
|
|
// It's the first time this HD project has seen this plugin SubTarget, save the
|
|
// last seen material version for this SubTarget GUID:
|
|
instance.m_PluginMaterialVersions.Add(entry.Key, subTarget.latestMaterialVersion);
|
|
}
|
|
}
|
|
|
|
instance.FillPresentPluginMaterialVersions();
|
|
Save();
|
|
}
|
|
|
|
public static void UpdateLastSeenSubTargetVersionsOfPluginSubTargets()
|
|
{
|
|
var allPluginSubTargets = HDShaderUtils.GetHDPluginSubTargets();
|
|
|
|
foreach (var entry in allPluginSubTargets)
|
|
{
|
|
var subTarget = entry.Value;
|
|
if (instance.m_PluginSubTargetVersions.TryGetValue(entry.Key, out int lastSeenVersion))
|
|
{
|
|
if (subTarget.latestSubTargetVersion > lastSeenVersion)
|
|
{
|
|
instance.m_PluginSubTargetVersions[entry.Key] = subTarget.latestSubTargetVersion;
|
|
}
|
|
// else SubTarget plugin downgraded or same version, nothing to do
|
|
}
|
|
else
|
|
{
|
|
// It's the first time this HD project (the material post processor)
|
|
// has seen this plugin SubTarget in a HDRP ShaderGraph import scan triggered,
|
|
// save the last seen *subtarget* version for this SubTarget GUID:
|
|
instance.m_PluginSubTargetVersions.Add(entry.Key, subTarget.latestSubTargetVersion);
|
|
}
|
|
}
|
|
|
|
instance.FillPresentPluginMaterialVersions();
|
|
Save();
|
|
}
|
|
|
|
//Note: if created from HDProjectSettingsReadOnlyBase, it can be loaded fully and such as a HDProjectSettings
|
|
//Thus it is not loaded again and we need to ensure the migration is done when accessing data too (see instance).
|
|
//Never use "instance" here as it can create infinite call loop. Use s_Instance instead.
|
|
static HDProjectSettings CreateOrLoad()
|
|
{
|
|
//try load: if it exists, this will trigger the call to the private ctor
|
|
InternalEditorUtility.LoadSerializedFileAndForget(filePath);
|
|
|
|
HDProjectSettings inst = s_Instance as HDProjectSettings;
|
|
|
|
//else create
|
|
if (inst == null)
|
|
{
|
|
HDProjectSettings created = CreateInstance<HDProjectSettings>();
|
|
created.hideFlags = HideFlags.HideAndDontSave;
|
|
inst = s_Instance as HDProjectSettings;
|
|
}
|
|
|
|
System.Diagnostics.Debug.Assert(s_Instance != null);
|
|
inst.FillPresentPluginMaterialVersions();
|
|
|
|
if (k_Migration.Migrate(inst))
|
|
Save();
|
|
|
|
return inst;
|
|
}
|
|
|
|
static void Save()
|
|
{
|
|
if (s_Instance == null)
|
|
{
|
|
Debug.Log("Cannot save ScriptableSingleton: no instance!");
|
|
return;
|
|
}
|
|
|
|
string folderPath = Path.GetDirectoryName(filePath);
|
|
if (!Directory.Exists(folderPath))
|
|
Directory.CreateDirectory(folderPath);
|
|
|
|
InternalEditorUtility.SaveToSerializedFileAndForget(new[] { s_Instance }, filePath, allowTextSerialization: true);
|
|
}
|
|
|
|
#region Migration
|
|
// Note that k_Migrations in MaterialPostProcessor was started with a valid migration step at k_Migrations[0],
|
|
// that's why we use k_NeverProcessedMaterialVersion = -1. We also dont use IVersionable (see MigrationDescription.cs) for it.
|
|
// But when using the later generic versionable framework, it is better to use 0 as a place holder for a never migrated version
|
|
// (and thus a step never to be executed for that enum entry) because underlying enum values are ordered as unsigned and
|
|
// MigrationDescription.LastVersion<Version>() will not work properly - ie it can return -1 if present instead of other positive values.
|
|
// (as the enum symbol with -1 will be listed as the last enum values in UnityEngine.Rendering.HighDefinition.TypeInfo.GetEnumLastValue<T>())
|
|
internal enum Version
|
|
{
|
|
None,
|
|
First,
|
|
SplittedWithHDUserSettings
|
|
}
|
|
#pragma warning disable 414 // never used
|
|
[SerializeField, FormerlySerializedAs("version")]
|
|
Version m_Version = MigrationDescription.LastVersion<Version>();
|
|
#pragma warning restore 414
|
|
|
|
// Migration Case 1:
|
|
// - No early migration occured, we need to access one field here and this pass by a static accessor such as HDProjectSettings.materialVersionForUpgrade
|
|
// - This will request the instance which is not loaded/created yet. So the LoadOrCreate will be called that should trigger the migration.
|
|
//
|
|
// Migration Case 2:
|
|
// - When opening unity Editor, the package.json have changed and request a newer version of the HDRP package.
|
|
// While evreything is reimported by the ADB, the render loop will start to be called (when modal say "Load Scene" in fact).
|
|
// - This can trigger a migration from former system without GlobalSettings to the new one. In this case we will try to create a
|
|
// HDRenderPipelineGlobalSettings out of the HDRenderPipelineAsset in the GraphicSettings. For this asset to be created, we must
|
|
// access the m_ProjectSettingFolderPath. This is done from the RuntimeAssembly and we have no certitude that the editor assembly
|
|
// is loaded. This is the reason we have a base class HDProjectSettingsReadonlyBase that lives in Runtime assembly.
|
|
// - Though HDProjectSettingsReadonlyBase don't see every part of the object and can just try to load it. When this is the case,
|
|
// the migration is NOT triggered.
|
|
// - So next time we will need access to HDProjectSettings, we need to say "Hey migrate if you need".
|
|
// - For this reason, in the instance, we need to check if we need to migrate or not. This is done by checking the version and remove
|
|
// the static instance in this case to force its reloading in the HDProjectSettings way (which means with Migration)
|
|
//
|
|
// When migration is triggered:
|
|
// - When we call the Migrate(), the System will try first to check the version to see if migration is needed. So if IVersionable.version
|
|
// use instance, we will suppress the static instance to reload it if the version is not last (see Case 2). In the end, as it is loaded
|
|
// again, it will request a Migration. This is a recursive call that will lead to stack overflow. So we must not use instance in IVersionable.version
|
|
// - Then if version is not the last, we will execute missing migration steps. Once again if inside a step we use instance we will cause
|
|
// a recursive call like in above and will produce a stack overflow. So no HDProjectSettings.materialVersionForUpgrade but we can safely
|
|
// use (s_Instance as HDProjectSettings).m_LastMaterialVersion or the given data: data.m_LastMaterialVersion.
|
|
|
|
// NEVER USE "instance" HERE as it can create infinite call loop. Use s_Instance instead. (see comment above)
|
|
Version IVersionable<Version>.version { get => (s_Instance as HDProjectSettings).m_Version; set => (s_Instance as HDProjectSettings).m_Version = value; }
|
|
|
|
// NEVER USE "instance" HERE as it can create infinite call loop. Use s_Instance or data instead. (see comment above)
|
|
static readonly MigrationDescription<Version, HDProjectSettings> k_Migration = MigrationDescription.New(
|
|
MigrationStep.New(Version.SplittedWithHDUserSettings, (HDProjectSettings data) =>
|
|
{
|
|
#pragma warning disable 618 // Type or member is obsolete
|
|
HDUserSettings.wizardPopupAlreadyShownOnce = data.m_ObsoleteWizardPopupAlreadyShownOnce;
|
|
HDUserSettings.wizardActiveTab = data.m_ObsoleteWizardActiveTab;
|
|
HDUserSettings.wizardNeedRestartAfterChangingToDX12 = data.m_ObsoleteWizardNeedRestartAfterChangingToDX12;
|
|
HDUserSettings.wizardNeedToRunFixAllAgainAfterDomainReload = data.m_ObsoleteWizardNeedToRunFixAllAgainAfterDomainReload;
|
|
#pragma warning restore 618 // Type or member is obsolete
|
|
})
|
|
);
|
|
|
|
#pragma warning disable 649 // Field never assigned
|
|
[SerializeField, Obsolete("Moved from HDProjectSettings to HDUserSettings"), FormerlySerializedAs("m_WizardPopupAlreadyShownOnce")]
|
|
bool m_ObsoleteWizardPopupAlreadyShownOnce;
|
|
[SerializeField, Obsolete("Moved from HDProjectSettings to HDUserSettings"), FormerlySerializedAs("m_WizardActiveTab")]
|
|
int m_ObsoleteWizardActiveTab;
|
|
[SerializeField, Obsolete("Moved from HDProjectSettings to HDUserSettings"), FormerlySerializedAs("m_WizardNeedRestartAfterChangingToDX12")]
|
|
bool m_ObsoleteWizardNeedRestartAfterChangingToDX12;
|
|
[SerializeField, Obsolete("Moved from HDProjectSettings to HDUserSettings"), FormerlySerializedAs("m_WizardNeedToRunFixAllAgainAfterDomainReload")]
|
|
bool m_ObsoleteWizardNeedToRunFixAllAgainAfterDomainReload;
|
|
#pragma warning restore 649 // Field never assigned
|
|
#endregion
|
|
}
|
|
}
|