diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs index 9c9285ffea..a38271eb6b 100644 --- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs +++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9Importer.cs @@ -21,11 +21,9 @@ namespace UnityEditor.SpeedTree.Importer { - // [2024-08-07] version: 2 - // Fixed mesh's UV2 & UV3 data usage strategy to 'always allocate' from 'conditionally allocate' - // to fix unwanted application of leaf-facing effect to geometries without leaf-facing data. - - [ScriptedImporter(version: 2, ext: "st9", AllowCaching = true)] + // [2024-09-27] version: 3 + // Fixed code that would lead to m_LODCount vs m_PerLODSettings.arraySize mismatching in GUI + [ScriptedImporter(version: 3, ext: "st9", AllowCaching = true)] public class SpeedTree9Importer : ScriptedImporter { const int SPEEDTREE_9_WIND_VERSION = 1; @@ -233,6 +231,16 @@ private void CacheTreeImporterValues(string assetPath) { // Variables used a lot are cached, since accessing any Reader array has a non-negligeable cost. m_LODCount = (uint)m_Tree.Lod.Length; + if(m_LODCount > LODGroupGUI.kLODColors.Length) + { + Debug.LogWarningFormat("Number of LOD meshes in asset ({0}) is larger than the maximum number supported by Unity GUI ({1})." + + "\nImporting only the first {1} LOD meshes." + , m_LODCount, LODGroupGUI.kLODColors.Length); + + // LODGroup GUI won't draw if we're above this limit, so we prevent future assertions here. + m_LODCount = (uint)LODGroupGUI.kLODColors.Length; + } + m_HasFacingData = TreeHasFacingData(); m_HasBranch2Data = m_Tree.Wind.DoBranch2; m_LastLodIsBillboard = m_Tree.BillboardInfo.LastLodIsBillboard; @@ -532,6 +540,7 @@ private void CalculateBillboardAndPerLODSettings() } else if (m_PerLODSettings.Count < m_LODCount) { + m_PerLODSettings.Clear(); for (int i = 0; i < m_LODCount; ++i) { bool isBillboardLOD = m_LastLodIsBillboard && i == m_LODCount - 1; diff --git a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs index 49eb05fb0e..29ff3dbadf 100644 --- a/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs +++ b/Editor/Mono/AssetPipeline/SpeedTree/SpeedTree9ImporterModelEditor.cs @@ -493,7 +493,12 @@ private void DrawLODGroupFoldouts(List lods) private string GetLODSubmeshAndTriCountLabel(int numLODs, int lodGroupIndex, SpeedTree9Importer im, LODGroup lodGroup) { LOD[] lods = lodGroup.GetLODs(); - Debug.Assert(lods.Length == numLODs); + + if(lods.Length != numLODs) + { + Debug.LogWarningFormat("Number of LODs mismatch between serialized object & LODGroup: {0}\nPlease re-import the asset and kindly report a bug if this warning keeps coming back.", im.assetPath); + numLODs = lods.Length; + } int[][] primitiveCounts = new int[numLODs][]; int[] submeshCounts = new int[numLODs]; @@ -547,6 +552,11 @@ private string GetLODSubmeshAndTriCountLabel(int numLODs, int lodGroupIndex, Spe return $"{totalTriCount} {LODGroupGUI.GUIStyles.m_TriangleCountLabel.text} {triangleChangeLabel} {submeshCountLabel}"; } + private Color GetLODGroupColor(int lodIndex) + { + return LODGroupGUI.kLODColors[lodIndex % LODGroupGUI.kLODColors.Length]; + } + private void DrawLODGroupFoldout(Camera camera, int lodGroupIndex, ref SavedBool foldoutState, List lodInfoList) { GameObject[] ObjectArrayToGameObjectArray(UnityEngine.Object[] objects) @@ -598,7 +608,7 @@ GameObject[] ObjectArrayToGameObjectArray(UnityEngine.Object[] objects) , foldoutState.value , LODFoldoutHeaderLabel , m_LODColorTextures[lodGroupIndex] - , LODGroupGUI.kLODColors[lodGroupIndex] * 0.6f // 0.5f magic number is copied from LODGroupsGUI.cs + , GetLODGroupColor(lodGroupIndex) * 0.6f // 0.5f magic number is copied from LODGroupsGUI.cs , LODFoldoutHeaderGroupAdditionalText ); @@ -727,13 +737,13 @@ void InitAndSetFoldoutLabelTextures() for (int i = 0; i < m_LODColorTextures.Length; i++) { m_LODColorTextures[i] = new Texture2D(1, 1); - m_LODColorTextures[i].SetPixel(0, 0, LODGroupGUI.kLODColors[i]); + m_LODColorTextures[i].SetPixel(0, 0, GetLODGroupColor(i)); } } void ResetFoldoutLists() { - int lodArraySize = m_PerLODSettings.arraySize; + int lodArraySize = Mathf.Min(m_PerLODSettings.arraySize, LODGroupGUI.kLODColors.Length); m_LODGroupFoldoutHeaderValues = new SavedBool[lodArraySize]; for (int i = 0; i < lodArraySize; i++) { diff --git a/Editor/Mono/AssetPipeline/SpeedTreeImporter.bindings.cs b/Editor/Mono/AssetPipeline/SpeedTreeImporter.bindings.cs index 28a373ac9d..dd19442061 100644 --- a/Editor/Mono/AssetPipeline/SpeedTreeImporter.bindings.cs +++ b/Editor/Mono/AssetPipeline/SpeedTreeImporter.bindings.cs @@ -305,9 +305,14 @@ static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAsse bool st8 = asset.EndsWith(".st", StringComparison.OrdinalIgnoreCase); if(st8) { + SpeedTreeImporter importer = AssetImporter.GetAtPath(asset) as SpeedTreeImporter; + if (importer == null) + continue; + // Check the external materials in case the user has extracted - Dictionary externalAssets = (AssetImporter.GetAtPath(asset) as SpeedTreeImporter).GetExternalObjectMap(); - FixExtraTexture_sRGB(externalAssets.Values); + Dictionary externalAssets = importer.GetExternalObjectMap(); + if(externalAssets != null) + FixExtraTexture_sRGB(externalAssets.Values); // Check the object subassets -- updates the materials if they're embedded in the SpeedTree asset UnityEngine.Object[] subAssets = AssetDatabase.LoadAllAssetsAtPath(asset); diff --git a/Editor/Mono/AssetPreviewUpdater.cs b/Editor/Mono/AssetPreviewUpdater.cs index f3844fd70a..c58d13d430 100644 --- a/Editor/Mono/AssetPreviewUpdater.cs +++ b/Editor/Mono/AssetPreviewUpdater.cs @@ -36,18 +36,9 @@ public static Texture2D CreatePreview(Object obj, Object[] subAssets, string ass var editor = Editor.CreateEditor(obj); if (editor == null) return null; - - //Check that Render Pipeline is ready - //Beware: AssetImportWorkers have their own Render Pipeline instance. Render Pipeline will be separately created for each one of them. - var pipelineWasNotInitialized = !RenderPipelineManager.pipelineSwitchCompleted; - - //We always keep this call to initialize Render Pipeline when Render Pipeline was not ready + var previewTexture = editor.RenderStaticPreview(assetPath, subAssets, width, height); - //If after render our Render Pipeline is initialized we re-render to have a valid result - if (pipelineWasNotInitialized && RenderPipelineManager.pipelineSwitchCompleted) - previewTexture = editor.RenderStaticPreview(assetPath, subAssets, width, height); - // For debugging we write the preview to a file (keep) //{ // var bytes = tex.EncodeToPNG(); diff --git a/Editor/Mono/AssetStore/AssetStoreWindow.cs b/Editor/Mono/AssetStore/AssetStoreWindow.cs index b497413d8a..3004202686 100644 --- a/Editor/Mono/AssetStore/AssetStoreWindow.cs +++ b/Editor/Mono/AssetStore/AssetStoreWindow.cs @@ -42,7 +42,7 @@ public static void OpenAssetStoreInBrowser() [MenuItem("Window/My Assets", false, 1498)] public static void OpenMyAssetsInPackageManager() { - PackageManagerWindow.SelectPackageAndPageStatic(pageId: PackageManager.UI.Internal.MyAssetsPage.k_Id); + PackageManagerWindow.OpenAndSelectPage(PackageManager.UI.Internal.MyAssetsPage.k_Id); } public void OnEnable() @@ -100,7 +100,7 @@ private void OnVisitWebsiteButtonClicked() private void OnLaunchPackageManagerButtonClicked() { - PackageManagerWindow.OpenPackageManager(null); + PackageManagerWindow.OpenAndSelectPackage(null); } private void SetMinMaxSizes() diff --git a/Editor/Mono/Audio/AudioContainerWindow.cs b/Editor/Mono/Audio/AudioContainerWindow.cs index 6837788ec3..be0861dcae 100644 --- a/Editor/Mono/Audio/AudioContainerWindow.cs +++ b/Editor/Mono/Audio/AudioContainerWindow.cs @@ -348,8 +348,6 @@ void OnTargetChanged(object sender, EventArgs e) m_CachedElements.Clear(); else m_CachedElements = State.AudioContainer.elements.ToList(); - - m_AddedElements.Clear(); } void OnSerializedObjectChanged(SerializedObject obj) @@ -836,7 +834,17 @@ void OnElementPropertyChanged(SerializedProperty property) void OnListItemsAdded(IEnumerable indices) { var indicesArray = indices as int[] ?? indices.ToArray(); + const string undoName = $"Add {nameof(AudioRandomContainer)} element"; + var groupUndoName = undoName; + + if (indicesArray.Length > 1) + { + groupUndoName = $"{undoName}s"; + } + + Undo.SetCurrentGroupName(groupUndoName); var elements = State.AudioContainer.elements.ToList(); + m_AddedElements.Clear(); foreach (var index in indicesArray) { @@ -852,22 +860,10 @@ void OnListItemsAdded(IEnumerable indices) State.AudioContainer.elements = elements.ToArray(); - // Object creation undo recording needs to be done in a separate pass from the object property changes above foreach (var element in m_AddedElements) - Undo.RegisterCreatedObjectUndo(element, "Create AudioContainerElement"); - - m_AddedElements.Clear(); - - var undoName = $"Add {nameof(AudioRandomContainer)} element"; - - if (indicesArray.Length > 1) { - undoName = $"{undoName}s"; + Undo.RegisterCreatedObjectUndo(element, undoName); } - - Undo.SetCurrentGroupName(undoName); - - m_AddedElements.Clear(); } void OnListItemsRemoved(IEnumerable indices) @@ -881,13 +877,10 @@ void OnListItemsRemoved(IEnumerable indices) { if (m_CachedElements[index] != null) { - AssetDatabase.RemoveObjectFromAsset(m_CachedElements[index]); Undo.DestroyObjectImmediate(m_CachedElements[index]); } } - State.AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); - var undoName = $"Remove {nameof(AudioRandomContainer)} element"; if (indicesArray.Length > 1) @@ -896,6 +889,7 @@ void OnListItemsRemoved(IEnumerable indices) } Undo.SetCurrentGroupName(undoName); + State.AudioContainer.NotifyObservers(AudioRandomContainer.ChangeEventType.List); } void OnItemListIndexChanged(int oldIndex, int newIndex) @@ -906,14 +900,18 @@ void OnItemListIndexChanged(int oldIndex, int newIndex) void OnAudioClipDrag(List audioClips) { - var undoName = $"Add {nameof(AudioRandomContainer)} element"; + const string undoName = $"Add {nameof(AudioRandomContainer)} element"; + var groupUndoName = undoName; if (audioClips.Count > 1) - undoName = $"{undoName}s"; - - Undo.RegisterCompleteObjectUndo(State.AudioContainer, undoName); + { + groupUndoName = $"{undoName}s"; + } + Undo.RegisterCompleteObjectUndo(State.AudioContainer, groupUndoName); + Undo.SetCurrentGroupName(groupUndoName); var elements = State.AudioContainer.elements.ToList(); + m_AddedElements.Clear(); foreach (var audioClip in audioClips) { @@ -930,12 +928,10 @@ void OnAudioClipDrag(List audioClips) State.AudioContainer.elements = elements.ToArray(); - // Object creation undo recording needs to be done in a separate pass from the object property changes above foreach (var element in m_AddedElements) - Undo.RegisterCreatedObjectUndo(element, "Create AudioContainerElement"); - - m_AddedElements.Clear(); - Undo.SetCurrentGroupName(undoName); + { + Undo.RegisterCreatedObjectUndo(element, undoName); + } } void OnAudioClipListChanged(SerializedProperty property) @@ -948,6 +944,12 @@ void OnAudioClipListChanged(SerializedProperty property) foreach (var elm in elements) { + // If the element is null, OnBindListItem will handle it, log an error and grey out the list entry. + if (elm == null) + { + continue; + } + AssetDatabase.TryGetGUIDAndLocalFileIdentifier(elm, out var guid, out var localId); // An empty asset GUID means the subasset has lost the reference diff --git a/Editor/Mono/BuildPipeline/BuildPlatform.cs b/Editor/Mono/BuildPipeline/BuildPlatform.cs index daac55e53f..9280386840 100644 --- a/Editor/Mono/BuildPipeline/BuildPlatform.cs +++ b/Editor/Mono/BuildPipeline/BuildPlatform.cs @@ -24,12 +24,26 @@ internal class BuildPlatform : ICloneable // TODO: Some packages are still using targetGroup, so we keep it here as a getter for compatibility public BuildTargetGroup targetGroup => namedBuildTarget.ToBuildTargetGroup(); + string m_LocTitle; + string m_IconId; + ScalableGUIContent m_Title; ScalableGUIContent m_SmallTitle; - public GUIContent title => m_Title; + Texture2D m_CompoundSmallIcon; + Texture2D m_CompoundSmallIconForQualitySettings; + string m_CompoundTooltip; + ScalableGUIContent m_CompoundTitle; + + IEnumerable m_DerivedPlatforms; + + public GUIContent title => m_CompoundTitle ?? m_Title; public Texture2D smallIcon => ((GUIContent)m_SmallTitle).image as Texture2D; + public Texture2D compoundSmallIcon => GetCompoundSmallIcon(); + public Texture2D compoundSmallIconForQualitySettings => GetCompoundSmallIconForQualitySettings(); + public string compoundTooltip => m_CompoundTooltip ?? tooltip; + public BuildPlatform(string locTitle, string iconId, NamedBuildTarget namedBuildTarget, BuildTarget defaultTarget, bool hideInUi, bool installed) : this(locTitle, "", iconId, namedBuildTarget, defaultTarget, hideInUi, installed) { @@ -40,11 +54,9 @@ public BuildPlatform(string locTitle, string tooltip, string iconId, NamedBuildT this.namedBuildTarget = namedBuildTarget; name = namedBuildTarget.TargetName; - // Workaround for some platforms which have | in their name which is also used as separator for tooltips - if (locTitle.Contains("|")) - m_Title = new ScalableGUIContent(locTitle.Replace("|", " "), null, iconId); - else - m_Title = new ScalableGUIContent(locTitle, null, iconId); + m_IconId = iconId; + m_LocTitle = locTitle; + m_Title = CreateTitle(locTitle, iconId); m_SmallTitle = new ScalableGUIContent(null, null, iconId + ".Small"); this.tooltip = tooltip; @@ -57,6 +69,138 @@ public object Clone() { return MemberwiseClone(); } + + Texture2D GetCompoundSmallIcon() + { + GenerateCompoundData(); + return m_CompoundSmallIcon ?? smallIcon; + } + + Texture2D GetCompoundSmallIconForQualitySettings() + { + GenerateCompoundData(); + return m_CompoundSmallIconForQualitySettings ?? smallIcon; + } + + void GenerateCompoundData() + { + if (m_DerivedPlatforms != null) + { + GenerateCompoundTooltip(m_DerivedPlatforms); + GenerateCompoundTitle(m_DerivedPlatforms); + GenerateCompoundIconTexture(m_DerivedPlatforms); + m_DerivedPlatforms = null; + } + } + + internal void SetDerivedPlatforms(IEnumerable derivedPlatforms) + { + m_DerivedPlatforms = derivedPlatforms; + } + + static ScalableGUIContent CreateTitle(string locTitle, string iconId) + { + // Workaround for some platforms which have | in their name which is also used as separator for tooltips + const string TooltipSeparator = "|"; + if (locTitle.Contains(TooltipSeparator)) + return new ScalableGUIContent(locTitle.Replace(TooltipSeparator, " "), null, iconId); + else + return new ScalableGUIContent(locTitle, null, iconId); + } + + void GenerateCompoundIconTexture(IEnumerable derivedPlatforms) + { + static Texture2D DuplicateAsReadableTexture(Texture2D sourceTexture) + { + var renderTexture = RenderTexture.GetTemporary(sourceTexture.width, sourceTexture.height, + 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear); + Graphics.Blit(sourceTexture, renderTexture); + var previouslyActiveRenderTexture = RenderTexture.active; + RenderTexture.active = renderTexture; + var readableTexture = new Texture2D(sourceTexture.width, sourceTexture.height); + readableTexture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + readableTexture.Apply(); + RenderTexture.active = previouslyActiveRenderTexture; + RenderTexture.ReleaseTemporary(renderTexture); + return readableTexture; + } + + static void ClearTexture(Texture2D texture) + { + Color TransparentBlack = new(0, 0, 0, 0); + for (int y = 0; y < texture.height; y++) + { + for (int x = 0; x < texture.width; x++) + { + texture.SetPixel(x, y, TransparentBlack); + } + } + } + + var readableSmallIcon = DuplicateAsReadableTexture(smallIcon); + var qualityIconWidth = readableSmallIcon.width; + var qualityIconHeight = readableSmallIcon.height; + var compoundIconWidth = readableSmallIcon.width; + var compoundIconHeight = readableSmallIcon.height; + var horizontalGapSize = (int)Math.Round(4 * readableSmallIcon.pixelsPerPoint); + var verticalGapSize = (int)Math.Round(2 * readableSmallIcon.pixelsPerPoint); + foreach (var derivedPlatform in derivedPlatforms) + { + var derivedSmallIcon = derivedPlatform.smallIcon; + compoundIconWidth += horizontalGapSize; + qualityIconHeight += verticalGapSize; + compoundIconHeight = Math.Max(compoundIconHeight, derivedSmallIcon.height); + compoundIconWidth += derivedSmallIcon.width; + qualityIconHeight += derivedSmallIcon.height; + } + var qualityTexture = new Texture2D(qualityIconWidth, qualityIconHeight, readableSmallIcon.format, false); + var texture = new Texture2D(compoundIconWidth, compoundIconHeight, readableSmallIcon.format, false); + ClearTexture(qualityTexture); + ClearTexture(texture); + var compoundIconXPosition = 0; + var qualityIconYPosition = qualityIconHeight - readableSmallIcon.height; + texture.CopyPixels(readableSmallIcon, 0, 0, 0, 0, readableSmallIcon.width, readableSmallIcon.height, 0, compoundIconXPosition, 0); + qualityTexture.CopyPixels(readableSmallIcon, 0, 0, 0, 0, readableSmallIcon.width, readableSmallIcon.height, 0, 0, qualityIconYPosition); + compoundIconXPosition += readableSmallIcon.width; + qualityIconYPosition -= verticalGapSize; + foreach (var derivedPlatform in derivedPlatforms) + { + var readableDerivedSmallIcon = DuplicateAsReadableTexture(derivedPlatform.smallIcon); + compoundIconXPosition += horizontalGapSize; + qualityIconYPosition -= readableDerivedSmallIcon.height; + texture.CopyPixels(readableDerivedSmallIcon, 0, 0, 0, 0, readableDerivedSmallIcon.width, readableDerivedSmallIcon.height, 0, compoundIconXPosition, 0); + qualityTexture.CopyPixels(readableDerivedSmallIcon, 0, 0, 0, 0, readableDerivedSmallIcon.width, readableDerivedSmallIcon.height, 0, 0, qualityIconYPosition); + compoundIconXPosition += readableDerivedSmallIcon.width; + qualityIconYPosition -= verticalGapSize; + } + texture.pixelsPerPoint = readableSmallIcon.pixelsPerPoint; + texture.Apply(); + qualityTexture.pixelsPerPoint = readableSmallIcon.pixelsPerPoint; + qualityTexture.Apply(); + m_CompoundSmallIcon = texture; + m_CompoundSmallIconForQualitySettings = qualityTexture; + } + + void GenerateCompoundTooltip(IEnumerable derivedPlatforms) + { + var ttip = m_LocTitle; + foreach (var derivedPlatform in derivedPlatforms) + { + ttip += $", {derivedPlatform.m_LocTitle}"; + } + ttip += " settings"; + m_CompoundTooltip = ttip; + } + + void GenerateCompoundTitle(IEnumerable derivedPlatforms) + { + var title = m_LocTitle; + foreach (var derivedPlatform in derivedPlatforms) + { + title += $", {derivedPlatform.m_LocTitle}"; + } + m_CompoundTitle = CreateTitle(title, m_IconId); + } } internal class BuildPlatformWithSubtarget : BuildPlatform @@ -107,13 +251,30 @@ internal BuildPlatforms() if (!target.HasFlag(TargetAttributes.IsStandalonePlatform)) { NamedBuildTarget namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(target.buildTargetPlatformVal)); - buildPlatformsList.Add(new BuildPlatform( + var buildPlatform = new BuildPlatform( BuildPipeline.GetBuildTargetGroupDisplayName(namedBuildTarget.ToBuildTargetGroup()), target.iconName, namedBuildTarget, target.buildTargetPlatformVal, hideInUi: target.HasFlag(TargetAttributes.HideInUI), - installed: BuildPipeline.GetPlaybackEngineDirectory(target.buildTargetPlatformVal, BuildOptions.None, false) != string.Empty)); + installed: BuildPipeline.GetPlaybackEngineDirectory(target.buildTargetPlatformVal, BuildOptions.None, false) != string.Empty); + buildPlatformsList.Add(buildPlatform); + var derivedBuildTargets = BuildTargetDiscovery.GetDerivedBuildTargetInfoList(target.buildTargetPlatformVal); + if (derivedBuildTargets.Length > 0) + { + var derivedBuildPlatforms = new List(); + foreach (var derivedTarget in derivedBuildTargets) + { + derivedBuildPlatforms.Add(new BuildPlatform( + derivedTarget.niceName, + derivedTarget.iconName, + namedBuildTarget, + target.buildTargetPlatformVal, + hideInUi: target.HasFlag(TargetAttributes.HideInUI), + installed: BuildPipeline.GetPlaybackEngineDirectory(target.buildTargetPlatformVal, BuildOptions.None, false) != string.Empty)); + } + buildPlatform.SetDerivedPlatforms(derivedBuildPlatforms); + } } } diff --git a/Editor/Mono/BuildPipeline/NamedBuildTarget.cs b/Editor/Mono/BuildPipeline/NamedBuildTarget.cs index f6f5eca0f8..221f83e9c9 100644 --- a/Editor/Mono/BuildPipeline/NamedBuildTarget.cs +++ b/Editor/Mono/BuildPipeline/NamedBuildTarget.cs @@ -34,7 +34,8 @@ namespace UnityEditor.Build "PS5", "EmbeddedLinux", "QNX", - "ReservedCFE" + "ReservedCFE", + "Kepler", }; public static readonly NamedBuildTarget Unknown = new NamedBuildTarget(""); @@ -124,6 +125,8 @@ public static NamedBuildTarget FromBuildTargetGroup(BuildTargetGroup buildTarget return new NamedBuildTarget("PS5"); case BuildTargetGroup.ReservedCFE: return new NamedBuildTarget("ReservedCFE"); + case BuildTargetGroup.Kepler: + return new NamedBuildTarget("Kepler"); } throw new ArgumentException($"There is no a valid NamedBuildTarget for BuildTargetGroup '{buildTargetGroup}'"); diff --git a/Editor/Mono/BuildPlayerWindow.cs b/Editor/Mono/BuildPlayerWindow.cs index 87cb4b8770..dabde7a7f5 100644 --- a/Editor/Mono/BuildPlayerWindow.cs +++ b/Editor/Mono/BuildPlayerWindow.cs @@ -640,7 +640,7 @@ static public string GetPlaybackEngineDownloadURL(string moduleName) } } - return string.Format("http://{0}.unity3d.com/{1}/{2}/{3}/UnitySetup-{4}-Support-for-Editor-{5}{6}", prefix, suffix, revision, folder, moduleName, shortVersion, extension); + return string.Format("https://{0}.unity3d.com/{1}/{2}/{3}/UnitySetup-{4}-Support-for-Editor-{5}{6}", prefix, suffix, revision, folder, moduleName, shortVersion, extension); } internal static string GetUnityHubModuleDownloadURL(string moduleName) @@ -706,7 +706,7 @@ void ShowBuildTargetSettings() var titleIconSize = 16; Rect r = GUILayoutUtility.GetRect(50, titleIconSize); if (Event.current.type == EventType.Repaint) - GUI.DrawTexture(new Rect(r.x, r.y, titleIconSize, titleIconSize), platform.smallIcon); + GUI.DrawTexture(new Rect(r.x, r.y, titleIconSize, titleIconSize), platform.compoundSmallIcon); r.x += titleIconSize + 5; GUI.Label(r, platform.title.text, styles.title); diff --git a/Editor/Mono/BuildProfile/BuildProfile.cs b/Editor/Mono/BuildProfile/BuildProfile.cs index 2b5dbf9d1d..baac9a08a6 100644 --- a/Editor/Mono/BuildProfile/BuildProfile.cs +++ b/Editor/Mono/BuildProfile/BuildProfile.cs @@ -67,12 +67,21 @@ internal string moduleName /// [SerializeField] string m_PlatformId; [VisibleToOtherModules] - internal string platformId + internal GUID platformGuid { - get => m_PlatformId; - set => m_PlatformId = value; + get => new GUID(m_PlatformId); + set => m_PlatformId = value.ToString(); } + /// + /// Platform ID of the build profile as string. + /// This is needed because the server team decided to use this + /// internal getter in their package. + /// PLEASE DON'T USE, USE platformGuid INSTEAD! + /// + [VisibleToOtherModules] + internal string platformId => m_PlatformId; + /// /// Platform module specific build settings; e.g. AndroidBuildSettings. /// @@ -144,11 +153,20 @@ internal PlayerSettings playerSettings [VisibleToOtherModules] internal Action OnPlayerSettingsUpdatedFromYAML; + [VisibleToOtherModules] + internal Action OnGraphicsSettingsSubAssetRemoved; + + /// + /// Cross-pipeline graphics settings overrides in build profile + /// + [VisibleToOtherModules] + internal BuildProfileGraphicsSettings graphicsSettings; + // TODO: Return server IBuildTargets for server build profiles. (https://jira.unity3d.com/browse/PLAT-6612) /// /// Get the IBuildTarget of the build profile. /// - internal IBuildTarget GetIBuildTarget() => ModuleManager.GetIBuildTarget(buildTarget); + internal IBuildTarget GetIBuildTarget() => ModuleManager.GetIBuildTarget(platformGuid); /// /// Returns true if the given is the active profile or a classic @@ -164,7 +182,7 @@ internal bool IsActiveBuildProfileOrPlatform() || !BuildProfileContext.IsClassicPlatformProfile(this)) return false; - return platformId == EditorUserBuildSettings.activePlatformGuid.ToString(); + return platformGuid == EditorUserBuildSettings.activePlatformGuid; } [VisibleToOtherModules] @@ -172,7 +190,7 @@ internal bool CanBuildLocally() { // Note: A platform build profile may have a non-null value even if its module is not installed. // This scenario is true for server platform profiles, which are the same type as the standalone one. - return platformBuildProfile != null && BuildProfileModuleUtil.IsModuleInstalled(platformId); + return platformBuildProfile != null && BuildProfileModuleUtil.IsModuleInstalled(platformGuid); } internal string GetLastRunnableBuildPathKey() @@ -188,11 +206,25 @@ internal string GetLastRunnableBuildPathKey() return BuildProfileModuleUtil.GetLastRunnableBuildKeyFromAssetPath(assetPath, key); } + /// + /// Duplicate the build profile. Note this does not create a new asset. + /// + [VisibleToOtherModules] + internal BuildProfile Duplicate() + { + var duplicatedProfile = Instantiate(this); + + if (graphicsSettings != null) + duplicatedProfile.graphicsSettings = Instantiate(graphicsSettings); + + return duplicatedProfile; + } + void OnEnable() { ValidateDataConsistency(); - moduleName = BuildProfileModuleUtil.GetModuleName(platformId); + moduleName = BuildProfileModuleUtil.GetModuleName(platformGuid); // Check if the platform support module has been installed, // and try to set an uninitialized platform settings. @@ -202,6 +234,8 @@ void OnEnable() onBuildProfileEnable?.Invoke(this); LoadPlayerSettings(); + TryLoadGraphicsSettings(); + if (!EditorUserBuildSettings.isBuildProfileAvailable || BuildProfileContext.activeProfile != this) return; @@ -217,6 +251,21 @@ void OnEnable() BuildProfileModuleUtil.RequestScriptCompilation(this); } + void TryLoadGraphicsSettings() + { + if (graphicsSettings != null) + return; + + var path = AssetDatabase.GetAssetPath(this); + var objects = AssetDatabase.LoadAllAssetsAtPath(path); + + var data = Array.Find(objects, obj => obj is BuildProfileGraphicsSettings) as BuildProfileGraphicsSettings; + if (data == null) + return; + + graphicsSettings = data; + } + void OnDisable() { if (BuildProfileContext.activeProfile == this) @@ -248,6 +297,7 @@ static void ContextMenuReset(MenuCommand menuCommand) targetBuildProfile.scriptingDefines = Array.Empty(); BuildProfileModuleUtil.RemovePlayerSettings(targetBuildProfile); + targetBuildProfile.RemoveGraphicsSettings(); AssetDatabase.SaveAssetIfDirty(targetBuildProfile); } @@ -256,15 +306,15 @@ void ValidateDataConsistency() { // TODO: Remove migration code (https://jira.unity3d.com/browse/PLAT-8909) // Set platform ID for build profiles created before it is introduced. - if (string.IsNullOrEmpty(platformId)) + if (platformGuid.Empty()) { - platformId = BuildProfileContext.IsSharedProfile(buildTarget) ? - new GUID(string.Empty).ToString() : BuildProfileModuleUtil.GetPlatformId(buildTarget, subtarget); + platformGuid = BuildProfileContext.IsSharedProfile(buildTarget) ? + new GUID(string.Empty) : BuildProfileModuleUtil.GetPlatformId(buildTarget, subtarget); EditorUtility.SetDirty(this); } else { - var (curBuildTarget, curSubtarget) = BuildProfileModuleUtil.GetBuildTargetAndSubtarget(platformId); + var (curBuildTarget, curSubtarget) = BuildProfileModuleUtil.GetBuildTargetAndSubtarget(platformGuid); if (buildTarget != curBuildTarget || subtarget != curSubtarget) { buildTarget = curBuildTarget; diff --git a/Editor/Mono/BuildProfile/BuildProfileContext.cs b/Editor/Mono/BuildProfile/BuildProfileContext.cs index da319d1e64..1c78527f6f 100644 --- a/Editor/Mono/BuildProfile/BuildProfileContext.cs +++ b/Editor/Mono/BuildProfile/BuildProfileContext.cs @@ -7,6 +7,7 @@ using System.IO; using JetBrains.Annotations; using UnityEditor.Modules; +using UnityEditor.Rendering; using UnityEditorInternal; using UnityEngine; using UnityEngine.Bindings; @@ -38,7 +39,7 @@ internal sealed class BuildProfileContext : ScriptableObject /// /// Cached mapping of platform ID to classic platform build profile. /// - Dictionary m_PlatformIdToClassicPlatformProfile = new(); + Dictionary m_PlatformIdToClassicPlatformProfile = new(); /// /// Cached editor scripting defines for the active profile. @@ -84,6 +85,7 @@ internal static BuildProfile activeProfile activeProfileChanged?.Invoke(prev, null); OnActiveProfileChangedForSettingExtension(prev, null); + EditorGraphicsSettings.activeProfileHasGraphicsSettings = false; BuildProfileModuleUtil.RequestScriptCompilation(null); return; } @@ -96,7 +98,7 @@ internal static BuildProfile activeProfile return; if (s_Instance != null && s_Instance.m_PlatformIdToClassicPlatformProfile.TryGetValue( - value.platformId, out var entry) && entry == value) + value.platformGuid, out var entry) && entry == value) { Debug.LogWarning("[BuildProfile] Classic Platforms cannot be set as the active build profile."); return; @@ -107,6 +109,7 @@ internal static BuildProfile activeProfile activeProfileChanged?.Invoke(prev, value); OnActiveProfileChangedForSettingExtension(prev, value); value.UpdateGlobalManagerPlayerSettings(); + EditorGraphicsSettings.activeProfileHasGraphicsSettings = ActiveProfileHasGraphicsSettings(); BuildProfileModuleUtil.RequestScriptCompilation(value); } } @@ -252,7 +255,7 @@ internal BuildProfile GetForClassicPlatform(BuildTarget target, StandaloneBuildS [VisibleToOtherModules("UnityEditor.BuildProfileModule")] internal static bool IsClassicPlatformProfile(BuildProfile profile) { - if (instance.m_PlatformIdToClassicPlatformProfile.TryGetValue(profile.platformId, out var classicProfile)) + if (instance.m_PlatformIdToClassicPlatformProfile.TryGetValue(profile.platformGuid, out var classicProfile)) { return classicProfile == profile; } @@ -266,9 +269,9 @@ internal static bool IsClassicPlatformProfile(BuildProfile profile) /// /// [VisibleToOtherModules("UnityEditor.BuildProfileModule")] - internal List GetMissingKnownPlatformModules() + internal List GetMissingKnownPlatformModules() { - var missingPlatforms = new List(); + var missingPlatforms = new List(); var keys = BuildProfileModuleUtil.FindAllViewablePlatforms(); for (var index = 0; index < keys.Count; index++) { @@ -282,7 +285,7 @@ internal List GetMissingKnownPlatformModules() continue; // Some build targets are only compatible with specific OS - if (!BuildTargetDiscovery.BuildPlatformIsAvailableOnHostPlatform(new GUID(key), SystemInfo.operatingSystemFamily)) + if (!BuildTargetDiscovery.BuildPlatformIsAvailableOnHostPlatform(key, SystemInfo.operatingSystemFamily)) continue; missingPlatforms.Add(key); @@ -300,6 +303,17 @@ static internal bool ProjectHasActiveProfileWithPlayerSettings() return activeBuildProfile?.playerSettings != null; } + /// + /// Check if the active build profile has graphics settings + /// + internal static bool ActiveProfileHasGraphicsSettings() + { + if (activeProfile == null) + return false; + + return activeProfile.graphicsSettings != null; + } + /// /// Sync the active build profile to EditorUserBuildSettings to ensure they are in a consistent state. /// @@ -318,8 +332,7 @@ void SyncActiveProfileToFallback() EditorUserBuildSettings.CopyFromBuildProfile(buildProfile); - string module = BuildTargetDiscovery.GetModuleNameForBuildTarget(buildProfile.buildTarget); - var extension = ModuleManager.GetBuildProfileExtension(module); + var extension = ModuleManager.GetBuildProfileExtension(buildProfile.platformGuid); if (extension != null) { extension.CopyPlatformSettingsFromBuildProfile(buildProfile.platformBuildProfile); @@ -352,40 +365,51 @@ void OnEnable() EditorApplication.quitting -= SyncActiveProfileToFallback; EditorApplication.quitting += SyncActiveProfileToFallback; - if (classicPlatformProfiles != null && classicPlatformProfiles.Count > 0) + if (classicPlatformProfiles == null) + classicPlatformProfiles = new List(); + + if (classicPlatformProfiles.Count > 0) { // classicPlatformProfiles survived the domain reload - just readd them to the classic profile map foreach (var profileObj in classicPlatformProfiles) - m_PlatformIdToClassicPlatformProfile.Add(profileObj.platformId, profileObj); + m_PlatformIdToClassicPlatformProfile.Add(profileObj.platformGuid, profileObj); } - else + + // Load platform build profiles from ProjectSettings folder. + if (!Directory.Exists(k_BuildProfilePath)) + return; + + var viewablePlatformKeys = BuildProfileModuleUtil.FindAllViewablePlatforms(); + for (var index = 0; index < viewablePlatformKeys.Count; index++) { - // first load - populate classic profiles from disk - classicPlatformProfiles = new List(); + var key = viewablePlatformKeys[index]; - // Load platform build profiles from ProjectSettings folder. - if (!Directory.Exists(k_BuildProfilePath)) - return; + if (m_PlatformIdToClassicPlatformProfile.ContainsKey(key)) + continue; - var viewablePlatformKeys = BuildProfileModuleUtil.FindAllViewablePlatforms(); - for (var index = 0; index < viewablePlatformKeys.Count; index++) - { - var key = viewablePlatformKeys[index]; - string path = GetFilePathForBuildProfile(key); + if (!BuildProfileModuleUtil.IsModuleInstalled(key)) + continue; + + if (BuildProfileModuleUtil.IsPlatformVisibleInPlatformBrowserOnly(key)) + continue; - if (!File.Exists(path) || !BuildProfileModuleUtil.IsModuleInstalled(key)) - continue; + string path = GetFilePathForBuildProfile(key); - var profile = InternalEditorUtility.LoadSerializedFileAndForget(path); - if (profile == null || profile.Length == 0 || profile[0] is not BuildProfile profileObj) - { - Debug.LogWarning($"Failed to load build profile from {path}."); - continue; - } + if (!File.Exists(path)) + { + GetOrCreateClassicPlatformBuildProfile(key); + continue; + } - m_PlatformIdToClassicPlatformProfile.Add(profileObj.platformId, profileObj); - classicPlatformProfiles.Add(profileObj); + var profile = InternalEditorUtility.LoadSerializedFileAndForget(path); + if (profile == null || profile.Length == 0 || profile[0] is not BuildProfile profileObj) + { + Debug.LogWarning($"Failed to load build profile from {path}."); + continue; } + + m_PlatformIdToClassicPlatformProfile.Add(profileObj.platformGuid, profileObj); + classicPlatformProfiles.Add(profileObj); } if (sharedProfile == null) @@ -403,6 +427,8 @@ void OnEnable() sharedProfile = sharedProfileObj; } + EditorGraphicsSettings.activeProfileHasGraphicsSettings = ActiveProfileHasGraphicsSettings(); + var buildProfile = activeProfile; if (buildProfile == null) @@ -418,7 +444,7 @@ void OnEnable() } string module = BuildTargetDiscovery.GetModuleNameForBuildTarget(buildProfile.buildTarget); - var extension = ModuleManager.GetBuildProfileExtension(module); + var extension = ModuleManager.GetBuildProfileExtension(buildProfile.platformGuid); if (extension != null) { extension.CopyPlatformSettingsToBuildProfile(buildProfile.platformBuildProfile); @@ -437,10 +463,13 @@ void CheckInstalledBuildPlatforms() var key = viewablePlatformKeys[index]; var moduleName = BuildProfileModuleUtil.GetModuleName(key); + if (BuildProfileModuleUtil.IsPlatformVisibleInPlatformBrowserOnly(key)) + continue; + if (!BuildProfileModuleUtil.IsModuleInstalled(key)) continue; - if (ModuleManager.GetBuildProfileExtension(moduleName) == null) + if (ModuleManager.GetBuildProfileExtension(key) == null) { // Require platform support and implemented build profile // extension for the target platform. @@ -454,7 +483,7 @@ void CheckInstalledBuildPlatforms() GetOrCreateSharedBuildProfile(); } - BuildProfile GetOrCreateClassicPlatformBuildProfile(string platformId) + BuildProfile GetOrCreateClassicPlatformBuildProfile(GUID platformId) { if (m_PlatformIdToClassicPlatformProfile.TryGetValue(platformId, out var profile) && profile != null) { @@ -468,7 +497,7 @@ BuildProfile GetOrCreateClassicPlatformBuildProfile(string platformId) // Platform profiles are not managed by the AssetDatabase. // We will manually handle serialization and deserialization of these objects. - var buildProfile = BuildProfile.CreateInstance(new GUID(platformId)); + var buildProfile = BuildProfile.CreateInstance(platformId); buildProfile.hideFlags = HideFlags.DontSave; m_PlatformIdToClassicPlatformProfile.Add(platformId, buildProfile); @@ -487,7 +516,7 @@ BuildProfile GetOrCreateClassicPlatformBuildProfile(string platformId) // Created profile can also be populated by settings on the managed side string module = BuildTargetDiscovery.GetModuleNameForBuildTarget(buildProfile.buildTarget); - var extension = ModuleManager.GetBuildProfileExtension(module); + var extension = ModuleManager.GetBuildProfileExtension(buildProfile.platformGuid); if (extension != null) { extension.CopyPlatformSettingsToBuildProfile(buildProfile.platformBuildProfile); @@ -510,7 +539,7 @@ BuildProfile GetOrCreateSharedBuildProfile() var buildProfile = ScriptableObject.CreateInstance(); buildProfile.buildTarget = BuildTarget.NoTarget; buildProfile.subtarget = StandaloneBuildSubtarget.Default; - buildProfile.platformId = new GUID(string.Empty).ToString(); + buildProfile.platformGuid = new GUID(string.Empty); buildProfile.platformBuildProfile = new SharedPlatformSettings(); buildProfile.hideFlags = HideFlags.DontSave; @@ -524,7 +553,7 @@ BuildProfile GetOrCreateSharedBuildProfile() } // TODO: Remove migration code (https://jira.unity3d.com/browse/PLAT-8909) - BuildProfile MigrateLegacyIndexedClassicPlatformProfile(string platformId) + BuildProfile MigrateLegacyIndexedClassicPlatformProfile(GUID platformId) { var (buildTarget, subtarget) = BuildProfileModuleUtil.GetBuildTargetAndSubtarget(platformId); var legacyKey = GetLegacyKey(buildTarget, subtarget); @@ -540,8 +569,8 @@ BuildProfile MigrateLegacyIndexedClassicPlatformProfile(string platformId) return null; } - profile.platformId = platformId; - m_PlatformIdToClassicPlatformProfile.Add(profile.platformId, profile); + profile.platformGuid = platformId; + m_PlatformIdToClassicPlatformProfile.Add(profile.platformGuid, profile); classicPlatformProfiles.Add(profile); SaveBuildProfileInProject(profile); @@ -567,7 +596,7 @@ static void SaveBuildProfileInProject(BuildProfile profile) Directory.CreateDirectory(k_BuildProfilePath); } - string path = IsSharedProfile(profile.buildTarget) ? k_SharedProfilePath : GetFilePathForBuildProfile(profile.platformId); + string path = IsSharedProfile(profile.buildTarget) ? k_SharedProfilePath : GetFilePathForBuildProfile(profile.platformGuid); InternalEditorUtility.SaveToSerializedFileAndForget(new []{ profile }, path, allowTextSerialization: true); } @@ -648,10 +677,66 @@ static string GetActiveOrClassicProfileRawPlatformSetting(string settingName, Bu return string.Empty; } + [RequiredByNativeCode, UsedImplicitly] + static bool SetActiveCommonGraphicsSetting(string settingName, int value) + { + if (!ActiveProfileHasGraphicsSettings()) + return false; + + activeProfile.graphicsSettings.SetGraphicsSetting(settingName, value); + return true; + } + + [RequiredByNativeCode, UsedImplicitly] + static int GetActiveCommonGraphicsSetting(string settingName) + { + if (!ActiveProfileHasGraphicsSettings()) + return BuildProfileGraphicsSettings.k_InvalidGraphicsSetting; + + return activeProfile.graphicsSettings.GetGraphicsSetting(settingName); + } + + [RequiredByNativeCode, UsedImplicitly] + static bool SetActiveAlwaysIncludedShaders(Shader[] shaders) + { + if (!ActiveProfileHasGraphicsSettings()) + return false; + + activeProfile.graphicsSettings.alwaysIncludedShaders = shaders; + return true; + } + + [RequiredByNativeCode, UsedImplicitly] + static Shader[] GetActiveAlwaysIncludedShaders() + { + if (!ActiveProfileHasGraphicsSettings()) + return null; + + return activeProfile.graphicsSettings.alwaysIncludedShaders; + } + + [RequiredByNativeCode, UsedImplicitly] + static ShaderVariantCollection[] GetActiveShaderVariantCollections() + { + if (!ActiveProfileHasGraphicsSettings()) + return null; + + return activeProfile?.graphicsSettings.preloadedShaders; + } + + [RequiredByNativeCode, UsedImplicitly] + static bool SetActiveShaderVariantCollections(ShaderVariantCollection[] collection) + { + if (!ActiveProfileHasGraphicsSettings()) + return false; + + activeProfile.graphicsSettings.preloadedShaders = collection; + return true; + } + [RequiredByNativeCode] static string GetActiveBuildProfilePath() { - var activeProfile = BuildProfileContext.activeProfile; if (activeProfile) return AssetDatabase.GetAssetPath(activeProfile); @@ -661,7 +746,6 @@ static string GetActiveBuildProfilePath() [RequiredByNativeCode] static bool HasActiveProfileWithPlayerSettings(out int instanceID) { - var activeProfile = BuildProfileContext.activeProfile; if (activeProfile?.playerSettings != null) { instanceID = activeProfile.GetInstanceID(); @@ -683,17 +767,15 @@ static bool ShouldReturnActiveProfile(BuildTarget buildTarget, StandaloneBuildSu if (!string.IsNullOrEmpty(sharedSetting)) return IsSharedSettingEnabledInActiveProfile(sharedSetting); - var activeProfile = BuildProfileContext.activeProfile; if (activeProfile == null || buildTarget == BuildTarget.NoTarget) return false; var platformId = BuildProfileModuleUtil.GetPlatformId(buildTarget, subtarget); - return platformId == activeProfile.platformId; + return platformId == activeProfile.platformGuid; } static bool IsSharedSettingEnabledInActiveProfile(string settingName) { - var activeProfile = BuildProfileContext.activeProfile; if (activeProfile == null) return false; @@ -704,7 +786,7 @@ static bool IsSharedSettingEnabledInActiveProfile(string settingName) return platformSettingsBase.IsSharedSettingEnabled(settingName); } - static string GetFilePathForBuildProfile(string platformId) => + static string GetFilePathForBuildProfile(GUID platformId) => $"{k_BuildProfilePath}/PlatformProfile.{platformId}.asset"; static string GetLegacyFilePathForBuildProfile((string moduleName, StandaloneBuildSubtarget subtarget) key) => diff --git a/Editor/Mono/BuildProfile/BuildProfileCreate.cs b/Editor/Mono/BuildProfile/BuildProfileCreate.cs index 1a6340a27e..abdb4e306a 100644 --- a/Editor/Mono/BuildProfile/BuildProfileCreate.cs +++ b/Editor/Mono/BuildProfile/BuildProfileCreate.cs @@ -34,20 +34,19 @@ internal static BuildProfile CreateInstance(BuildTarget buildTarget, StandaloneB var buildProfile = CreateInstance(); buildProfile.buildTarget = buildTarget; buildProfile.subtarget = subtarget; - buildProfile.platformId = BuildProfileModuleUtil.GetPlatformId(buildTarget, subtarget); + buildProfile.platformGuid = BuildProfileModuleUtil.GetPlatformId(buildTarget, subtarget); buildProfile.OnEnable(); return buildProfile; } - internal static BuildProfile CreateInstance(GUID platformGuid) + internal static BuildProfile CreateInstance(GUID platformId) { - var platformId = platformGuid.ToString(); var (buildTarget, subtarget) = BuildProfileModuleUtil.GetBuildTargetAndSubtarget(platformId); var moduleName = BuildProfileModuleUtil.GetModuleName(buildTarget); var buildProfile = CreateInstance(); buildProfile.buildTarget = buildTarget; buildProfile.subtarget = subtarget; - buildProfile.platformId = platformId; + buildProfile.platformGuid = platformId; buildProfile.OnEnable(); return buildProfile; } @@ -57,14 +56,14 @@ internal static BuildProfile CreateInstance(GUID platformGuid) /// event after an asset is created by AssetDatabase.CreateAsset. /// [VisibleToOtherModules("UnityEditor.BuildProfileModule")] - internal static void CreateInstance(string platformId, string assetPath) + internal static void CreateInstance(GUID platformId, string assetPath) { var (buildTarget, subtarget) = BuildProfileModuleUtil.GetBuildTargetAndSubtarget(platformId); var moduleName = BuildProfileModuleUtil.GetModuleName(buildTarget); var buildProfile = CreateInstance(); buildProfile.buildTarget = buildTarget; buildProfile.subtarget = subtarget; - buildProfile.platformId = platformId; + buildProfile.platformGuid = platformId; AssetDatabase.CreateAsset( buildProfile, AssetDatabase.GenerateUniqueAssetPath(assetPath)); @@ -81,12 +80,27 @@ void TryCreatePlatformSettings() return; } - IBuildProfileExtension buildProfileExtension = ModuleManager.GetBuildProfileExtension(moduleName); + IBuildProfileExtension buildProfileExtension = ModuleManager.GetBuildProfileExtension(platformGuid); if (buildProfileExtension != null && ModuleManager.IsPlatformSupportLoaded(moduleName)) { platformBuildProfile = buildProfileExtension.CreateBuildProfilePlatformSettings(); EditorUtility.SetDirty(this); } } + + /// + /// Remove the Graphics Settings overrides from the build profile. + /// + internal void RemoveGraphicsSettings() + { + if (graphicsSettings == null) + return; + + AssetDatabase.RemoveObjectFromAsset(graphicsSettings); + graphicsSettings = null; + EditorUtility.SetDirty(this); + + OnGraphicsSettingsSubAssetRemoved?.Invoke(); + } } } diff --git a/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs new file mode 100644 index 0000000000..ab86999ddb --- /dev/null +++ b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettings.cs @@ -0,0 +1,267 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using UnityEngine; +using UnityEngine.Bindings; +using UnityEngine.Rendering; +using UnityEditor.Rendering; + +namespace UnityEditor.Build.Profile +{ + [VisibleToOtherModules("UnityEditor.BuildProfileModule")] + sealed class BuildProfileGraphicsSettings : ScriptableObject + { + internal const int k_InvalidGraphicsSetting = -2; + + [SerializeField] StrippingModes m_LightmapStripping = StrippingModes.Automatic; + [SerializeField] bool m_LightmapKeepPlain = true; + [SerializeField] bool m_LightmapKeepDirCombined = true; + [SerializeField] bool m_LightmapKeepDynamicPlain = true; + [SerializeField] bool m_LightmapKeepDynamicDirCombined = true; + [SerializeField] bool m_LightmapKeepShadowMask = true; + [SerializeField] bool m_LightmapKeepSubtractive = true; + [SerializeField] StrippingModes m_FogStripping = StrippingModes.Automatic; + [SerializeField] bool m_FogKeepLinear = true; + [SerializeField] bool m_FogKeepExp = true; + [SerializeField] bool m_FogKeepExp2 = true; + [SerializeField] InstancingStrippingMode m_InstancingStripping = InstancingStrippingMode.StripUnused; + [SerializeField] BatchRendererGroupStrippingMode m_BrgStripping = BatchRendererGroupStrippingMode.KeepIfEntitiesGraphics; + [SerializeField] bool m_LogWhenShaderIsCompiled = false; + [SerializeField] bool m_CameraRelativeLightCulling = false; + [SerializeField] bool m_CameraRelativeShadowCulling = false; + [SerializeField] VideoShadersIncludeMode m_VideoShadersIncludeMode = VideoShadersIncludeMode.Always; + [SerializeField] Shader[] m_AlwaysIncludedShaders = Array.Empty(); + [SerializeField] LightProbeOutsideHullStrategy m_LightProbeOutsideHullStrategy = LightProbeOutsideHullStrategy.kLightProbeSearchTetrahedralHull; + [SerializeField] ShaderVariantCollection[] m_PreloadedShaders = Array.Empty(); + [SerializeField] int m_PreloadShadersBatchTimeLimit = -1; + + internal StrippingModes lightmapStripping + { + get => m_LightmapStripping; + set => m_LightmapStripping = value; + } + + internal bool lightmapKeepPlain + { + get => m_LightmapKeepPlain; + set => m_LightmapKeepPlain = value; + } + + internal bool lightmapKeepDirCombined + { + get => m_LightmapKeepDirCombined; + set => m_LightmapKeepDirCombined = value; + } + + internal bool lightmapKeepDynamicPlain + { + get => m_LightmapKeepDynamicPlain; + set => m_LightmapKeepDynamicPlain = value; + } + + internal bool lightmapKeepDynamicDirCombined + { + get => m_LightmapKeepDynamicDirCombined; + set => m_LightmapKeepDynamicDirCombined = value; + } + + internal bool lightmapKeepShadowMask + { + get => m_LightmapKeepShadowMask; + set => m_LightmapKeepShadowMask = value; + } + + internal bool lightmapKeepSubtractive + { + get => m_LightmapKeepSubtractive; + set => m_LightmapKeepSubtractive = value; + } + + internal StrippingModes fogStripping + { + get => m_FogStripping; + set => m_FogStripping = value; + } + + internal bool fogKeepLinear + { + get => m_FogKeepLinear; + set => m_FogKeepLinear = value; + } + + internal bool fogKeepExp + { + get => m_FogKeepExp; + set => m_FogKeepExp = value; + } + + internal bool fogKeepExp2 + { + get => m_FogKeepExp2; + set => m_FogKeepExp2 = value; + } + + internal InstancingStrippingMode instancingStripping + { + get => m_InstancingStripping; + set => m_InstancingStripping = value; + } + + internal BatchRendererGroupStrippingMode brgStripping + { + get => m_BrgStripping; + set => m_BrgStripping = value; + } + + internal bool logWhenShaderIsCompiled + { + get => m_LogWhenShaderIsCompiled; + set => m_LogWhenShaderIsCompiled = value; + } + + internal bool cameraRelativeLightCulling + { + get => m_CameraRelativeLightCulling; + set => m_CameraRelativeLightCulling = value; + } + + internal bool cameraRelativeShadowCulling + { + get => m_CameraRelativeShadowCulling; + set => m_CameraRelativeShadowCulling = value; + } + + internal VideoShadersIncludeMode videoShadersIncludeMode + { + get => m_VideoShadersIncludeMode; + set => m_VideoShadersIncludeMode = value; + } + + internal Shader[] alwaysIncludedShaders + { + get => m_AlwaysIncludedShaders; + set => m_AlwaysIncludedShaders = value; + } + + internal LightProbeOutsideHullStrategy lightProbeOutsideHullStrategy + { + get => m_LightProbeOutsideHullStrategy; + set => m_LightProbeOutsideHullStrategy = value; + } + + internal ShaderVariantCollection[] preloadedShaders + { + get => m_PreloadedShaders; + set => m_PreloadedShaders = value; + } + + internal int preloadShadersBatchTimeLimit + { + get => m_PreloadShadersBatchTimeLimit; + set => m_PreloadShadersBatchTimeLimit = value; + } + + public void Instantiate() + { + name = "Graphics Settings"; + hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector; + } + + public void SetGraphicsSetting(string settingName, int value) + { + switch (settingName) + { + case "LightmapStripping": + lightmapStripping = (StrippingModes)value; + break; + case "LightmapKeepPlain": + lightmapKeepPlain = IntegerToBoolean(value); + break; + case "LightmapKeepDirCombined": + lightmapKeepDirCombined = IntegerToBoolean(value); + break; + case "LightmapKeepDynamicPlain": + lightmapKeepDynamicPlain = IntegerToBoolean(value); + break; + case "LightmapKeepDynamicDirCombined": + lightmapKeepDynamicDirCombined = IntegerToBoolean(value); + break; + case "LightmapKeepShadowMask": + lightmapKeepShadowMask = IntegerToBoolean(value); + break; + case "LightmapKeepSubtractive": + lightmapKeepSubtractive = IntegerToBoolean(value); + break; + case "FogStripping": + fogStripping = (StrippingModes)value; + break; + case "FogKeepLinear": + fogKeepLinear = IntegerToBoolean(value); + break; + case "FogKeepExp": + fogKeepExp = IntegerToBoolean(value); + break; + case "FogKeepExp2": + fogKeepExp2 = IntegerToBoolean(value); + break; + case "InstancingStripping": + instancingStripping = (InstancingStrippingMode)value; + break; + case "BrgStripping": + brgStripping = (BatchRendererGroupStrippingMode)value; + break; + case "LogWhenShaderIsCompiled": + logWhenShaderIsCompiled = IntegerToBoolean(value); + break; + case "CameraRelativeLightCulling": + cameraRelativeLightCulling = IntegerToBoolean(value); + break; + case "CameraRelativeShadowCulling": + cameraRelativeShadowCulling = IntegerToBoolean(value); + break; + case "VideoShadersIncludeMode": + videoShadersIncludeMode = (VideoShadersIncludeMode)value; + break; + case "LightProbeOutsideHullStrategy": + lightProbeOutsideHullStrategy = (LightProbeOutsideHullStrategy)value; + break; + case "PreloadShadersBatchTimeLimit": + preloadShadersBatchTimeLimit = value; + break; + } + + static bool IntegerToBoolean(int value) => value != 0; + } + + public int GetGraphicsSetting(string settingName) + { + return settingName switch + { + "LightmapStripping" => (int)lightmapStripping, + "LightmapKeepPlain" => BooleanToInteger(lightmapKeepPlain), + "LightmapKeepDirCombined" => BooleanToInteger(lightmapKeepDirCombined), + "LightmapKeepDynamicPlain" => BooleanToInteger(lightmapKeepDynamicPlain), + "LightmapKeepDynamicDirCombined" => BooleanToInteger(lightmapKeepDynamicDirCombined), + "LightmapKeepShadowMask" => BooleanToInteger(lightmapKeepShadowMask), + "LightmapKeepSubtractive" => BooleanToInteger(lightmapKeepSubtractive), + "FogStripping" => (int)fogStripping, + "FogKeepLinear" => BooleanToInteger(fogKeepLinear), + "FogKeepExp" => BooleanToInteger(fogKeepExp), + "FogKeepExp2" => BooleanToInteger(fogKeepExp2), + "InstancingStripping" => (int)instancingStripping, + "BrgStripping" => (int)brgStripping, + "LogWhenShaderIsCompiled" => BooleanToInteger(logWhenShaderIsCompiled), + "CameraRelativeLightCulling" => BooleanToInteger(cameraRelativeLightCulling), + "CameraRelativeShadowCulling" => BooleanToInteger(cameraRelativeShadowCulling), + "VideoShadersIncludeMode" => (int)videoShadersIncludeMode, + "LightProbeOutsideHullStrategy" => (int)lightProbeOutsideHullStrategy, + "PreloadShadersBatchTimeLimit" => preloadShadersBatchTimeLimit, + _ => k_InvalidGraphicsSetting, + }; + + static int BooleanToInteger(bool value) => value ? 1 : 0; + } + } +} diff --git a/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs new file mode 100644 index 0000000000..00df52650a --- /dev/null +++ b/Editor/Mono/BuildProfile/BuildProfileGraphicsSettingsEditor.cs @@ -0,0 +1,156 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using UnityEditor.UIElements; +using UnityEngine.Bindings; +using UnityEngine.UIElements; +using UnityEngine.Rendering; + +namespace UnityEditor.Build.Profile +{ + [VisibleToOtherModules("UnityEditor.BuildProfileModule")] + [CustomEditor(typeof(BuildProfileGraphicsSettings))] + class BuildProfileGraphicsSettingsEditor : Editor + { + const string k_Uxml = "BuildProfile/UXML/BuildProfileCommonGraphicsSettings.uxml"; + const string k_StyleSheet = "BuildProfile/StyleSheets/BuildProfile.uss"; + const string k_LastDefaultPropertyPath = "m_EditorClassIdentifier"; + + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + var visualTree = EditorGUIUtility.LoadRequired(k_Uxml) as VisualTreeAsset; + var windowUss = EditorGUIUtility.LoadRequired(k_StyleSheet) as StyleSheet; + visualTree.CloneTree(root); + root.styleSheets.Add(windowUss); + + root.Bind(serializedObject); + root.Query() + .ForEach(d => d.InitializeWithoutWindow(serializedObject)); + + BindEnumFieldWithFadeGroup(root, "Lightmap", CalculateLightmapStrippingFromCurrentScene); + BindEnumFieldWithFadeGroup(root, "Fog", CalculateFogStrippingFromCurrentScene); + + // Align fields as in the inspector + var type = typeof(BaseField<>); + root.Query() + .Where(e => + IsSubclassOfGeneric(type, e.GetType())) + .ForEach(e => + e.EnableInClassList(BaseField.alignedFieldUssClassName, true)); + + return root; + } + + void BindEnumFieldWithFadeGroup(VisualElement content, string id, Action buttonCallback) + { + var enumMode = content.MandatoryQ($"{id}Modes"); + var enumModeGroup = content.MandatoryQ($"{id}ModesGroup"); + var enumModeProperty = serializedObject.FindProperty($"m_{id}Stripping"); + + static bool IsModesGroupVisible(StrippingModes mode) => mode == StrippingModes.Custom; + UIElementsEditorUtility.SetVisibility(enumModeGroup, IsModesGroupVisible((StrippingModes)enumModeProperty.intValue)); + var lightmapModesUpdate = UIElementsEditorUtility.BindSerializedProperty(enumMode, enumModeProperty, + mode => UIElementsEditorUtility.SetVisibility(enumModeGroup, IsModesGroupVisible(mode))); + lightmapModesUpdate?.Invoke(); + + content.MandatoryQ