diff --git a/.buginfo b/.buginfo new file mode 100644 index 0000000..72065a7 --- /dev/null +++ b/.buginfo @@ -0,0 +1,4 @@ +system: jira +server: jira.unity3d.com +project: XRMB +issuetype: Bug diff --git a/CHANGELOG.md b/CHANGELOG.md index b8e61ba..3b59677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [4.3.1] - 2022-10-14 + +### Fixed +- Clean up AndroidManifest.xml every XR build to prevent conflicts with other configurations. +- Remove a spurious error when no XR providers were selected + +### Changes +- Removed dependency on `com.unity.subsystemregistration` package for supported editors (2022.2+) + +## [4.2.2] - 2022-04-08 + +### Fixed +* Related Bug: FB [1378643](https://fogbugz.unity3d.com/f/cases/1378643/). + +Fixed an issue where packages that contained `XRGeneralSettingsPerBuildTarget` assets could potentially fail to reimport. + +* Fixed an issue where `m_RegisteredLoaders` list would not get populated on `Awake` at runtime in a built player. + +### Changes +* Updated documentation with new sections `Manual Lifecycle Handling`, `Initializing a Specific Loader`, `Changing the Loader List`, and add examples for the new documentation sections. + ## [4.2.1] - 2021-12-09 ### Fixed * Resolve FB [1378643](https://fogbugz.unity3d.com/f/cases/1378643/) by enforcing creation of `XRGeneralSettingsPerBuildTarget` using a new API `XRGeneralSettingsPerBuildTarget.GetOrCreate()` diff --git a/Documentation~/EndUser.md b/Documentation~/EndUser.md index 16a4793..2369b94 100644 --- a/Documentation~/EndUser.md +++ b/Documentation~/EndUser.md @@ -69,11 +69,13 @@ public class ManualXRControl } ``` -## Automatic XR loading for specific XR loaders +## Managing XR Loader Lifecycles Manually The previous section showed how to manage the entire XR system lifecycle. If you require more granular control, you can manage an individual loader's lifecycle instead. -Use the script above, with some minor tweaks, to manage specific loaders at runtime, as shown in the code example below. You can use the following methods in your script: +### API + +You can use the following methods in your script to control the lifecycle of XR manually: |**Method**|**Description**| |---|---| @@ -84,6 +86,14 @@ Use the script above, with some minor tweaks, to manage specific loaders at runt The following code example demonstrates how to manage individual loaders at runtime. +### Disclaimer + +The following circumvents XR Management Lifecycle control. The developer is indicating that they intend to manage the lifecycle of the loaders initialized in this manner manually. APIs that expect to use XR Plug-In Management to acquire subsystems from a loader will not function properly when manually handling loader lifecycles. + +If you need a specific loader initialized but want that loader to still be managed by XR Plug-In Management, look into the '[Modifying the Loader List](./EndUser.md#example-modifying-the-loader-list)' section on how to do that. + +### Example + ```csharp using System; using System.Collections; @@ -139,6 +149,109 @@ public class RuntimeXRLoaderManager : MonoBehaviour } ``` +## Using XR Plug-In Management to Initialize a Specific Loader + +Sometimes, you may want to include multiple loaders in a build and have them fall through in a specific order. By default, XR Plug-In Management will attempt to initialize the loader in alphabetical order based on the loaders' name. If this isn't adequate you can modify the loader list in Edit Mode, Play Mode, and in a built player with some caveats. + +1) In Edit Mode, you may modify the loaders list without restriction. + +2) You may reorder or remove loaders from the loader list at runtime. A new loader that wasn't known at startup can't be added to the loader list at runtime. Any attempt to add an unknown loader to the list at runtime will fail. + + This means that you are able to do the following during runtime: + + - Remove loaders from the list of loaders. + - Re-add loaders that were previously removed. + - Reorder the list of loaders. + +3) Any operation on the XR Plug-in Manager UI will reset the ordering to the original alphabetical ordering. + +### Example: Modifying the Active Loader List at Runtime + +If you wish to reorder the set of loaders so XR Plug-In Management attempts to initialize a specific loader first you could do the following at runtime: + +```csharp + var generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTarget.Standalone); + var settingsManager = generalSettings.Manager; + + // Get a readonly reference to the current set of loaders. + var readonlyCurrentLoaders = settingsManager.activeLoaders; + + // Modifying the loader list order + var reorderedLoadersList = new List(); + foreach (var loader in readonlyCurrentLoaders) + { +#if UNITY_ANDROID + if (loader is XRFooLoader) + { + // Insert 'Foo' Loaders at head of list + reorderedLoaderList.Insert(loader, 0); + } + else if (loader is XRBarLoader) + { + // Insert 'Bar' Loaders at back of list + reorderedLoaderList.Insert(loader, reorderedLoaderList.Count); + } +#else // !UNITY_ANDROID + if (loader is XRBarLoader) + { + // Insert 'Bar' Loaders at head of list + reorderedLoaderList.Insert(loader, 0); + } + else if (loader is XRFooLoader) + { + // Insert 'Foo' Loaders at back of list + reorderedLoaderList.Insert(loader, reorderedLoaderList.Count); + } +#endif // end UNITY_ANDROID + } + + // Would only fail if the new list contains a loader that was + // not in the original list. + if (!settingsManager.TrySetLoaders(reorderedLoadersList)) + Debug.Log("Failed to set the reordered loader list! Refer to the documentation for additional information!"); + +``` + +### Example: General Modification the loader list + +You may also modify the loader list in other more general ways. The following shows how to use `TryAdd`, `TryRemove`, and `TrySet` in a variety of ways. + +```csharp + var generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTarget.Standalone); + var settingsManager = generalSettings.Manager; + + // Get example loaders as XRLoaders + var fooLoader = new FooLoader() as XRLoader; + var barLoader = new BarLoader() as XRLoader; + + // Adding new loaders + // Append the new FooLoader + if (!settingsManager.TryAddLoader(fooLoader)) + Debug.Log("Adding new Foo Loader failed! Refer to the documentation for additional information!"); + + // Insert the new BarLoader at the start of the list + if (!settingsManager.TryAddLoader(barLoader, 0)) + Debug.Log("Adding new Bar Loader failed! Refer to the documentation for additional information!"); + + // Removing loaders + if (!settingsManager.TryRemoveLoader(fooLoader)) + Debug.Log("Failed to remove the fooLoader! Refer to the documentation for additional information!"); + + // Modifying the loader list order + var readonlyCurrentLoaders = settingsManager.activeLoaders; + + // Copy the returned read only list + var currentLoaders = new List(readonlyCurrentLoaders); + + // Reverse the list + currentLoaders.Reverse(); + + if (!settingsManager.TrySetLoaders(currentLoaders)) + Debug.Log("Failed to set the reordered loader list! Refer to the documentation for additional information!"); +``` + +You would most likely place this script in a custom pre-process build script, but that isn't required. Regardless of the script's location, you should do this as a setup step before you start a build as XR Plug-in Manager will serialize this list as part of the build execution. + ## Customizing build and runtime settings Any package that needs build or runtime settings should provide a settings data type for use. This data type appears in the **Project Settings** window, underneath a top level **XR** node. @@ -202,56 +315,6 @@ Removing a plug-in from the set of assigned plug-ins for a build target: } ``` -### Example: Modifying the loader list - -By default, the XR Plug-in Management UI displays loaders in strict alphabetical order, based on their names. In most scenarios, you don't need to change this order. However, if you need the loaders in a different order, you can modify the loaders list from script like so: - -```csharp - var generalSettings = XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(BuildTarget.Standalone); - var settingsManager = generalSettings.Manager; - - // Get example loaders as XRLoaders - var fooLoader = new FooLoader() as XRLoader; - var barLoader = new BarLoader() as XRLoader; - - // Adding new loaders - // Append the new FooLoader - if (!settingsManager.TryAddLoader(fooLoader)) - Debug.Log("Adding new Foo Loader failed! Refer to the documentation for additional information!"); - - // Insert the new BarLoader at the start of the list - if (!settingsManager.TryAddLoader(barLoader, 0)) - Debug.Log("Adding new Bar Loader failed! Refer to the documentation for additional information!"); - - // Removing loaders - if (!settingsManager.TryRemoveLoader(fooLoader)) - Debug.Log("Failed to remove the fooLoader! Refer to the documentation for additional information!"); - - // Modifying the loader list order - var readonlyCurrentLoaders = settingsManager.activeLoaders; - - // Copy the returned read only list - var currentLoaders = new List(readonlyCurrentLoaders); - - // Reverse the list - currentLoaders.Reverse(); - - if (!settingsManager.TrySetLoaders(currentLoaders)) - Debug.Log("Failed to set the reordered loader list! Refer to the documentation for additional information!"); -``` - -You would most likely place this script in a custom build script, but that isn't required. Regardless of the script's location location, you should do this as a setup step before you start a build. This is because the first thing that XR Plug-in Manager does at build time is to serialize the loader list to the build target. - -**Note:** A new loader that wasn't known at startup can't be added to the loader list at runtime, which causes the modification operation to fail. You can still modify the list during runtime, whether the app runs in Play mode or as a standalone build. - -This means that you are able to do the following during runtime: - -- Remove loaders from the list of loaders. -- Re-add loaders that were previously removed. -- Reorder the list of loaders. - -**Note**: Any operation on the XR Plug-in Manager UI will reset the ordering to the original alphabetical ordering. - ## Installing the XR Plug-in Management package Please see related Unity documentation for [Configuring XR](https://docs.unity3d.com/Manual/configuring-project-for-xr.html ). diff --git a/Editor/Legacy/XRLegacyStandaloneSubsystem.cs b/Editor/Legacy/XRLegacyStandaloneSubsystem.cs new file mode 100644 index 0000000..9014014 --- /dev/null +++ b/Editor/Legacy/XRLegacyStandaloneSubsystem.cs @@ -0,0 +1,23 @@ +#if !UNITY_2020_2_OR_NEWER + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; + +using UnityEditor; +using UnityEditor.PackageManager; + +namespace UnityEditor.XR.Management.Legacy +{ + [InitializeOnLoad] + class XRLegacyStandaloneSubsystem + { + static XRLegacyStandaloneSubsystem() + { + PackageManager.Client.Add("com.unity.subsystemregistration"); + } + } +} + +#endif // !UNITY_2020_2_OR_NEWER diff --git a/Editor/Legacy/XRLegacyStandaloneSubsystem.cs.meta b/Editor/Legacy/XRLegacyStandaloneSubsystem.cs.meta new file mode 100644 index 0000000..18668d4 --- /dev/null +++ b/Editor/Legacy/XRLegacyStandaloneSubsystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4a6fc50e59fbc14ab1f849d70ba8ba9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Metadata/XRPackageMetadataStore.cs b/Editor/Metadata/XRPackageMetadataStore.cs index 9c05c70..9e08732 100644 --- a/Editor/Metadata/XRPackageMetadataStore.cs +++ b/Editor/Metadata/XRPackageMetadataStore.cs @@ -573,7 +573,11 @@ static void UpdateInstallablePackages() var kpi = new KnownPackageInfo(); kpi.packageId = package.name; +#if UNITY_2022_2_OR_NEWER + kpi.verifiedVersion = package.versions.recommended; +#else kpi.verifiedVersion = package.versions.verified; +#endif if (string.IsNullOrEmpty(kpi.verifiedVersion)) kpi.verifiedVersion = package.versions.latestCompatible; knownPackageInfos.Add(kpi); diff --git a/Editor/XRGeneralBuildProcessor.cs b/Editor/XRGeneralBuildProcessor.cs index a5567c9..2109e2c 100644 --- a/Editor/XRGeneralBuildProcessor.cs +++ b/Editor/XRGeneralBuildProcessor.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using UnityEditor.Android; using UnityEditor.Build; using UnityEditor.Build.Reporting; @@ -18,22 +20,22 @@ namespace UnityEditor.XR.Management /// internal class BootConfig { - static readonly string kXrBootSettingsKey = "xr-boot-settings"; + public static readonly string kXrBootSettingsKey = "xr-boot-settings"; Dictionary bootConfigSettings; - BuildReport buildReport; + BuildTarget m_target; string bootConfigPath; - internal BootConfig(BuildReport report) + internal BootConfig(BuildTarget target) { - buildReport = report; + m_target = target; } internal void ReadBootConfig() { bootConfigSettings = new Dictionary(); - string buildTargetName = BuildPipeline.GetBuildTargetName(buildReport.summary.platform); + string buildTargetName = BuildPipeline.GetBuildTargetName(m_target); string xrBootSettings = UnityEditor.EditorUserBuildSettings.GetPlatformSettings(buildTargetName, kXrBootSettingsKey); if (!String.IsNullOrEmpty(xrBootSettings)) { @@ -64,6 +66,11 @@ internal void SetValueForKey(string key, string value, bool replace = false) } } + internal bool DeleteKey(string key) + { + return bootConfigSettings.Remove(key); + } + internal void WriteBootConfig() { // boot settings string format @@ -80,14 +87,14 @@ internal void WriteBootConfig() firstEntry = false; } - string buildTargetName = BuildPipeline.GetBuildTargetName(buildReport.summary.platform); + string buildTargetName = BuildPipeline.GetBuildTargetName(m_target); EditorUserBuildSettings.SetPlatformSettings(buildTargetName, kXrBootSettingsKey, sb.ToString()); } } - class XRGeneralBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport + class XRGeneralBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport, IPostGenerateGradleAndroidProject { - static readonly string kPreInitLibraryKey = "xrsdk-pre-init-library"; + public static readonly string kPreInitLibraryKey = "xrsdk-pre-init-library"; class PreInitInfo { @@ -103,24 +110,58 @@ public PreInitInfo(IXRLoaderPreInit loader, BuildTarget buildTarget, BuildTarget public BuildTargetGroup buildTargetGroup; } + internal static readonly int s_CallbackOrder = 0; public int callbackOrder { - get { return 0; } + get { return s_CallbackOrder; } } + // This has to be static as the OnPostprocessBuild and OnPostGenerateGradleAndroidProject + // callbacks are made on different instances. + internal static string s_ManifestPath; + void CleanOldSettings() { BuildHelpers.CleanOldSettings(); } + internal static bool TryGetSettingsPerBuildTarget(out XRGeneralSettingsPerBuildTarget buildTargetSettings) + { + // Fix for [1378643](https://fogbugz.unity3d.com/f/cases/1378643/) + // Ensure that if a settings asset exists in the project, it gets processed. + if (!EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings)) + { + if (XRGeneralSettingsPerBuildTarget.TryFindSettingsAsset(out buildTargetSettings)) + { + // Asset found but not set. Set the configuration object. If it's empty it will get culled. + EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, buildTargetSettings, true); + } + else + { + // If no asset is found the processor should not run + return false; + } + } + + return true; + } + public void OnPreprocessBuild(BuildReport report) + { + OnPreprocessBuildImpl(report.summary.guid, report.summary.platformGroup, report.summary.platform); + } + + internal void OnPreprocessBuildImpl(in GUID buildGuid, in BuildTargetGroup targetGroup, in BuildTarget target) { // Always remember to cleanup preloaded assets after build to make sure we don't // dirty later builds with assets that may not be needed or are out of date. CleanOldSettings(); - XRGeneralSettingsPerBuildTarget buildTargetSettings = XRGeneralSettingsPerBuildTarget.GetOrCreate(); - XRGeneralSettings settings = buildTargetSettings.SettingsForBuildTarget(report.summary.platformGroup); + // Fix for [1378643](https://fogbugz.unity3d.com/f/cases/1378643/) + if (!TryGetSettingsPerBuildTarget(out var buildTargetSettings)) + return; + + XRGeneralSettings settings = buildTargetSettings.SettingsForBuildTarget(targetGroup); if (settings == null) return; @@ -129,38 +170,40 @@ public void OnPreprocessBuild(BuildReport report) if (loaderManager != null) { var loaders = loaderManager.activeLoaders; - // If there are no loaders present in the current manager instance, then the settings will not be included in the current build. - if (loaders.Count > 0) - { - var summary = report.summary; - XRManagementAnalytics.SendBuildEvent(summary.guid, summary.platform, summary.platformGroup, loaders); + XRManagementAnalytics.SendBuildEvent(buildGuid, target, targetGroup, loaders); - // chances are that our devices won't fall back to graphics device types later in the list so it's better to assume the device will be created with the first gfx api in the list. - // furthermore, we have no way to influence falling back to other graphics API types unless we automatically change settings underneath the user which is no good! - GraphicsDeviceType[] deviceTypes = PlayerSettings.GetGraphicsAPIs(report.summary.platform); - if (deviceTypes.Length > 0) - { - VerifyGraphicsAPICompatibility(loaderManager, deviceTypes[0]); - } - else - { - Debug.LogWarning("No Graphics APIs have been configured in Player Settings."); - } + // chances are that our devices won't fall back to graphics device types later in the list so it's better to assume the device will be created with the first gfx api in the list. + // furthermore, we have no way to influence falling back to other graphics API types unless we automatically change settings underneath the user which is no good! + GraphicsDeviceType[] deviceTypes = PlayerSettings.GetGraphicsAPIs(target); + if (deviceTypes.Length > 0) + { + VerifyGraphicsAPICompatibility(loaderManager, deviceTypes[0]); + } + else + { + Debug.LogWarning("No Graphics APIs have been configured in Player Settings."); + } - PreInitInfo preInitInfo = null; - preInitInfo = new PreInitInfo(loaders[0] as IXRLoaderPreInit, report.summary.platform, report.summary.platformGroup); + PreInitInfo preInitInfo = null; + if (loaders.Count >= 1) + { + preInitInfo = new PreInitInfo(loaders[0] as IXRLoaderPreInit, target, targetGroup); + } - var loader = preInitInfo?.loader ?? null; - if (loader != null) - { - BootConfig bootConfig = new BootConfig(report); - bootConfig.ReadBootConfig(); - string preInitLibraryName = loader.GetPreInitLibraryName(preInitInfo.buildTarget, preInitInfo.buildTargetGroup); - bootConfig.SetValueForKey(kPreInitLibraryKey, preInitLibraryName); - bootConfig.WriteBootConfig(); - } + var loader = preInitInfo?.loader ?? null; + BootConfig bootConfig = new BootConfig(target); + bootConfig.ReadBootConfig(); + if (loader != null) + { + string preInitLibraryName = loader.GetPreInitLibraryName(preInitInfo.buildTarget, preInitInfo.buildTargetGroup); + bootConfig.SetValueForKey(kPreInitLibraryKey, preInitLibraryName); + } + else + { + bootConfig.DeleteKey(kPreInitLibraryKey); } + bootConfig.WriteBootConfig(); } UnityEngine.Object[] preloadedAssets = PlayerSettings.GetPreloadedAssets(); @@ -181,41 +224,41 @@ public void OnPreprocessBuild(BuildReport report) public static void VerifyGraphicsAPICompatibility(XRManagerSettings loaderManager, GraphicsDeviceType selectedDeviceType) { - HashSet allLoaderGraphicsDeviceTypes = new HashSet(); - foreach (var loader in loaderManager.activeLoaders) + HashSet allLoaderGraphicsDeviceTypes = new HashSet(); + foreach (var loader in loaderManager.activeLoaders) + { + List supporteDeviceTypes = loader.GetSupportedGraphicsDeviceTypes(true); + // To help with backward compatibility, if we find that any of the compatibility lists are empty we assume that at least one of the loaders does not implement the GetSupportedGraphicsDeviceTypes method + // Therefore we revert to the previous behavior of building the app regardless of gfx api settings. + if (supporteDeviceTypes.Count == 0) { - List supporteDeviceTypes = loader.GetSupportedGraphicsDeviceTypes(true); - // To help with backward compatibility, if we find that any of the compatibility lists are empty we assume that at least one of the loaders does not implement the GetSupportedGraphicsDeviceTypes method - // Therefore we revert to the previous behavior of building the app regardless of gfx api settings. - if (supporteDeviceTypes.Count == 0) - { - allLoaderGraphicsDeviceTypes.Clear(); - break; - } - foreach (var supportedGraphicsDeviceType in supporteDeviceTypes) - { - allLoaderGraphicsDeviceTypes.Add(supportedGraphicsDeviceType); - } + allLoaderGraphicsDeviceTypes.Clear(); + break; } + foreach (var supportedGraphicsDeviceType in supporteDeviceTypes) + { + allLoaderGraphicsDeviceTypes.Add(supportedGraphicsDeviceType); + } + } - if (allLoaderGraphicsDeviceTypes.Count > 0 && !allLoaderGraphicsDeviceTypes.Contains(selectedDeviceType)) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendFormat( - "The selected grpahics API, {0}, is not supported by any of the current loaders. Please change the preferred Graphics API setting in Player Settings.\n", - selectedDeviceType); + if (allLoaderGraphicsDeviceTypes.Count > 0 && !allLoaderGraphicsDeviceTypes.Contains(selectedDeviceType)) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendFormat( + "The selected graphics API, {0}, is not supported by any of the current loaders. Please change the preferred Graphics API setting in Player Settings.\n", + selectedDeviceType); - foreach (var loader in loaderManager.activeLoaders) + foreach (var loader in loaderManager.activeLoaders) + { + stringBuilder.AppendLine(loader.name + " supports:"); + foreach (var supportedGraphicsDeviceType in loader.GetSupportedGraphicsDeviceTypes(true)) { - stringBuilder.AppendLine(loader.name + " supports:"); - foreach (var supportedGraphicsDeviceType in loader.GetSupportedGraphicsDeviceTypes(true)) - { - stringBuilder.AppendLine("\t -" + supportedGraphicsDeviceType); - } + stringBuilder.AppendLine("\t -" + supportedGraphicsDeviceType); } - throw new BuildFailedException(stringBuilder.ToString()); } + throw new BuildFailedException(stringBuilder.ToString()); + } } public void OnPostprocessBuild(BuildReport report) @@ -223,8 +266,35 @@ public void OnPostprocessBuild(BuildReport report) // Always remember to cleanup preloaded assets after build to make sure we don't // dirty later builds with assets that may not be needed or are out of date. CleanOldSettings(); + + // This is done to clean up XR-provider-specific manifest entries from Android builds when + // the incremental build system fails to rebuild the manifest, as there is currently + // (2023-09-27) no way to mark the manifest as "dirty." + CleanupAndroidManifest(); + } + + public void CleanupAndroidManifest() + { + if (!string.IsNullOrEmpty(s_ManifestPath)) + { + try + { + File.Delete(s_ManifestPath); + } + catch (Exception e) + { + // This only fails if the file can't be deleted; it is quiet if the file does not exist. + Debug.LogWarning("Failed to clean up AndroidManifest file located at " + s_ManifestPath + " : " + e.ToString()); + } + } + s_ManifestPath = ""; } + public void OnPostGenerateGradleAndroidProject(string path) + { + const string k_ManifestPath = "/src/main/AndroidManifest.xml"; + s_ManifestPath = path + k_ManifestPath; + } } } diff --git a/Editor/XRGeneralSettingsPerBuildTarget.cs b/Editor/XRGeneralSettingsPerBuildTarget.cs index 6b9818a..0114f07 100644 --- a/Editor/XRGeneralSettingsPerBuildTarget.cs +++ b/Editor/XRGeneralSettingsPerBuildTarget.cs @@ -32,6 +32,40 @@ static XRGeneralSettingsPerBuildTarget() EditorApplication.playModeStateChanged += PlayModeStateChanged; } + [MethodImpl(MethodImplOptions.Synchronized)] + internal static bool TryFindSettingsAsset(out XRGeneralSettingsPerBuildTarget generalSettings) + { + EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out generalSettings); + if (generalSettings == null) + { + var assets = AssetDatabase.FindAssets("t:XRGeneralSettingsPerBuildTarget"); + if (assets.Length > 0) + { + string path = AssetDatabase.GUIDToAssetPath(assets[0]); + generalSettings = AssetDatabase.LoadAssetAtPath(path, typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget; + } + } + return generalSettings != null; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + static XRGeneralSettingsPerBuildTarget CreateAssetSynchronized() + { + var generalSettings = CreateInstance(typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget; + string assetPath = EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultGeneralSettingsPath); + if (!string.IsNullOrEmpty(assetPath)) + { + assetPath = Path.Combine(assetPath, "XRGeneralSettingsPerBuildTarget.asset"); + AssetDatabase.CreateAsset(generalSettings, assetPath); + AssetDatabase.SaveAssets(); + } + EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, generalSettings, true); + return generalSettings; + } + + internal static XRGeneralSettingsPerBuildTarget GetOrCreate() + => TryFindSettingsAsset(out var generalSettings) ? generalSettings : CreateAssetSynchronized(); + // Simple class to give us updates when the asset database changes. class AssetCallbacks : AssetPostprocessor { @@ -76,9 +110,7 @@ static void PlayModeStateChanged(PlayModeStateChange state) XRGeneralSettingsPerBuildTarget buildTargetSettings = null; EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings); if (buildTargetSettings == null) - { - buildTargetSettings = GetOrCreate(); - } + return; XRGeneralSettings instance = buildTargetSettings.SettingsForBuildTarget(BuildTargetGroup.Standalone); if (instance == null || !instance.InitManagerOnStart) @@ -89,6 +121,7 @@ static void PlayModeStateChanged(PlayModeStateChange state) internal static bool ContainsLoaderForAnyBuildTarget(string loaderTypeName) { + XRGeneralSettingsPerBuildTarget buildTargetSettings = null; EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings); if (buildTargetSettings == null) @@ -102,37 +135,6 @@ internal static bool ContainsLoaderForAnyBuildTarget(string loaderTypeName) return false; } - - [MethodImpl(MethodImplOptions.Synchronized)] - internal static XRGeneralSettingsPerBuildTarget GetOrCreate() - { - EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out var generalSettings); - if (generalSettings == null) - { - string searchText = "t:XRGeneralSettings"; - string[] assets = AssetDatabase.FindAssets(searchText); - if (assets.Length > 0) - { - string path = AssetDatabase.GUIDToAssetPath(assets[0]); - generalSettings = AssetDatabase.LoadAssetAtPath(path, typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget; - } - } - - if (generalSettings == null) - { - generalSettings = CreateInstance(typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget; - string assetPath = EditorUtilities.GetAssetPathForComponents(EditorUtilities.s_DefaultGeneralSettingsPath); - if (!string.IsNullOrEmpty(assetPath)) - { - assetPath = Path.Combine(assetPath, "XRGeneralSettings.asset"); - AssetDatabase.CreateAsset(generalSettings, assetPath); - } - } - - EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, generalSettings, true); - - return generalSettings; - } #endif /// diff --git a/Editor/XRSettingsManager.cs b/Editor/XRSettingsManager.cs index db2fb52..e023b1a 100644 --- a/Editor/XRSettingsManager.cs +++ b/Editor/XRSettingsManager.cs @@ -74,15 +74,10 @@ internal bool ResetUi private Dictionary CachedSettingsEditor = new Dictionary(); + private BuildTargetGroup m_LastBuildTargetGroup = BuildTargetGroup.Unknown; - static XRGeneralSettingsPerBuildTarget currentSettings - { - get - { - return XRGeneralSettingsPerBuildTarget.GetOrCreate(); - } - } + static XRGeneralSettingsPerBuildTarget currentSettings => XRGeneralSettingsPerBuildTarget.GetOrCreate(); [UnityEngine.Internal.ExcludeFromDocs] XRSettingsManager(string path, SettingsScope scopes = SettingsScope.Project) : base(path, scopes) diff --git a/Runtime/XRManagerSettings.cs b/Runtime/XRManagerSettings.cs index a3ea784..052e1fa 100644 --- a/Runtime/XRManagerSettings.cs +++ b/Runtime/XRManagerSettings.cs @@ -364,6 +364,18 @@ public bool TrySetLoaders(List reorderedLoaders) return true; } + void Awake() + { + // Populate the loader set from the current list of loaders + foreach (var loader in currentLoaders) + { + if (!m_RegisteredLoaders.Contains(loader)) + { + m_RegisteredLoaders.Add(loader); + } + } + } + private bool CheckGraphicsAPICompatibility(XRLoader loader) { GraphicsDeviceType deviceType = SystemInfo.graphicsDeviceType; diff --git a/Tests/Editor/BuildTests.cs b/Tests/Editor/BuildTests.cs index 454f2ac..e2af711 100644 --- a/Tests/Editor/BuildTests.cs +++ b/Tests/Editor/BuildTests.cs @@ -1,10 +1,16 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using NUnit.Framework; +using UnityEditor.Android; using UnityEditor.Build; +using UnityEditor.SceneManagement; +using UnityEditor.XR.Management; using UnityEngine; using UnityEngine.Rendering; +using UnityEngine.SceneManagement; +using UnityEngine.TestTools; using UnityEngine.XR.Management; using UnityEngine.XR.Management.Tests; using Object = UnityEngine.Object; @@ -13,18 +19,69 @@ namespace UnityEditor.XR.Management.Tests.BuildTests { + static class BuildTestHelpers + { + public const string k_TemporaryTestPath = "Assets/Hidden_XRManagement_Test_Assets"; + public const string k_TempAssetSearchTerm = "TestGeneralAsset"; + public const string k_AssetName = "TestGeneralAsset.asset"; + + public static void SetupTemporaryTestAssets(BuildTargetGroup buildTargetGroup, out XRGeneralSettingsPerBuildTarget previousGeneralSettingsInstance) + { + EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out previousGeneralSettingsInstance); + EditorBuildSettings.RemoveConfigObject(XRGeneralSettings.k_SettingsKey); + + var emptyBuildTargetSettings = ScriptableObject.CreateInstance(); + var generalSettings = ScriptableObject.CreateInstance(); + generalSettings.AssignedSettings = ScriptableObject.CreateInstance(); + emptyBuildTargetSettings.SetSettingsForBuildTarget(buildTargetGroup, generalSettings); + emptyBuildTargetSettings.SettingsForBuildTarget(buildTargetGroup).AssignedSettings.TrySetLoaders(new List()); + + Directory.CreateDirectory(k_TemporaryTestPath); + AssetDatabase.CreateAsset(emptyBuildTargetSettings, Path.Combine(k_TemporaryTestPath, k_AssetName)); + + EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, emptyBuildTargetSettings, true); + } + + public static void CleanupTemporaryAssets() + { + AssetDatabase.DeleteAsset(Path.Combine(k_TemporaryTestPath, k_AssetName)); + if (Directory.Exists(Path.Combine("./", k_TemporaryTestPath))) + { + Directory.Delete(Path.Combine("./", k_TemporaryTestPath)); + File.Delete($"./{k_TemporaryTestPath}.meta"); + AssetDatabase.Refresh(); + } + } + + // Must match XRGeneralSettingsPerBuildTarget.TryFindSettingsAsset + public static bool TryFindTemporarySettingsAsset(out XRGeneralSettingsPerBuildTarget buildTargetSettings) + { + EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out buildTargetSettings); + if (buildTargetSettings == null) + { + var assets = AssetDatabase.FindAssets(k_TempAssetSearchTerm); + if (assets.Length > 0) + { + string path = AssetDatabase.GUIDToAssetPath(assets[0]); + buildTargetSettings = AssetDatabase.LoadAssetAtPath(path, typeof(XRGeneralSettingsPerBuildTarget)) as XRGeneralSettingsPerBuildTarget; + } + } + return buildTargetSettings != null; + } + } + #if UNITY_EDITOR_WIN - [TestFixture(GraphicsDeviceType.Direct3D11, false, new [] { GraphicsDeviceType.Direct3D11})] - [TestFixture(GraphicsDeviceType.Direct3D11, false, new [] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Direct3D11})] - [TestFixture(GraphicsDeviceType.Direct3D11, true, new [] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Direct3D11, false, new [] { GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Direct3D11, false, new [] { GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null})] + [TestFixture(GraphicsDeviceType.Direct3D11, false, GraphicsDeviceType.Direct3D11, null)] + [TestFixture(GraphicsDeviceType.Direct3D11, false, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Direct3D11)] + [TestFixture(GraphicsDeviceType.Direct3D11, true, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Direct3D11, false, GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Direct3D11, false, GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null)] #elif UNITY_EDITOR_OSX - [TestFixture(GraphicsDeviceType.Metal, false, new [] { GraphicsDeviceType.Metal})] - [TestFixture(GraphicsDeviceType.Metal, false, new [] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Metal})] - [TestFixture(GraphicsDeviceType.Metal, true, new [] { GraphicsDeviceType.OpenGLES3, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Metal, false, new [] { GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Metal, false, new [] { GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null})] + [TestFixture(GraphicsDeviceType.Metal, false, GraphicsDeviceType.Metal, null)] + [TestFixture(GraphicsDeviceType.Metal, false, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Metal)] + [TestFixture(GraphicsDeviceType.Metal, true, GraphicsDeviceType.OpenGLES3, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Metal, false, GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Metal, false, GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null)] #endif class GraphicsAPICompatibilityTests { @@ -35,11 +92,17 @@ class GraphicsAPICompatibilityTests private GraphicsDeviceType[] m_LoadersSupporteDeviceTypes; bool m_BuildFails; - public GraphicsAPICompatibilityTests(GraphicsDeviceType playerSettingsDeviceType, bool fails, GraphicsDeviceType[] loaders) + public GraphicsAPICompatibilityTests(GraphicsDeviceType playerSettingsDeviceType, bool fails, GraphicsDeviceType loader0, GraphicsDeviceType? loader1) { m_BuildFails = fails; m_PlayerSettingsDeviceType = playerSettingsDeviceType; - m_LoadersSupporteDeviceTypes = loaders; + if (loader1.HasValue) + { + m_LoadersSupporteDeviceTypes = new[] {loader0, loader1.Value}; + } + else { + m_LoadersSupporteDeviceTypes = new [] {loader0}; + } } [SetUp] @@ -66,7 +129,7 @@ public void SetupPlayerSettings() } [TearDown] - public void TeadDown() + public void TearDown() { Object.DestroyImmediate(m_Manager); m_Manager = null; @@ -95,13 +158,10 @@ public void CheckGraphicsAPICompatibilityOnBuild() [TestFixture(BuildTargetGroup.iOS)] #if (!UNITY_2021_2_OR_NEWER) [TestFixture(BuildTargetGroup.Lumin)] -#endif +#endif [TestFixture(BuildTargetGroup.PS4)] class XRGeneralSettingsBuildTests { - const string k_TemporaryTestPath = "Assets/Hidden_XRManagement_Test_Assets"; - const string k_AssetName = "TestGeneralAsset.asset"; - BuildTargetGroup m_BuildTargetGroup; XRGeneralSettingsPerBuildTarget m_OldBuildTargetSettings; @@ -116,19 +176,7 @@ public XRGeneralSettingsBuildTests(BuildTargetGroup group) [SetUp] public void SetupPlayerSettings() { - EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out m_OldBuildTargetSettings); - EditorBuildSettings.RemoveConfigObject(XRGeneralSettings.k_SettingsKey); - - var emptyBuildTargetSettings = ScriptableObject.CreateInstance(); - var generalSettings = ScriptableObject.CreateInstance(); - generalSettings.AssignedSettings = ScriptableObject.CreateInstance(); - emptyBuildTargetSettings.SetSettingsForBuildTarget(m_BuildTargetGroup, generalSettings); - emptyBuildTargetSettings.SettingsForBuildTarget(m_BuildTargetGroup).AssignedSettings.TrySetLoaders(new List()); - - Directory.CreateDirectory(k_TemporaryTestPath); - AssetDatabase.CreateAsset(emptyBuildTargetSettings, Path.Combine(k_TemporaryTestPath, k_AssetName)); - - EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, emptyBuildTargetSettings, true); + BuildTestHelpers.SetupTemporaryTestAssets(m_BuildTargetGroup, out m_OldBuildTargetSettings); } [TearDown] @@ -137,14 +185,7 @@ public void TearDown() if (m_OldBuildTargetSettings != null) EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, m_OldBuildTargetSettings, true); - // AssetDatabase.DeleteAsset(k_TemporaryTestPath); - AssetDatabase.DeleteAsset(Path.Combine(k_TemporaryTestPath, k_AssetName)); - if (Directory.Exists(Path.Combine("./", k_TemporaryTestPath))) - { - Directory.Delete(Path.Combine("./", k_TemporaryTestPath)); - File.Delete($"./{k_TemporaryTestPath}.meta"); - AssetDatabase.Refresh(); - } + BuildTestHelpers.CleanupTemporaryAssets(); } [Test] @@ -175,5 +216,318 @@ public void CheckEmptyXRGeneralAssetWillNotGetIncludedInAssets() Assert.False(PlayerSettings.GetPreloadedAssets().Contains(settings)); } } + + [TestFixture] + class XRGeneralSettingsPerBuildTargetExclusionTests + { + static readonly string k_DefaultAssetPathRoot = Path.GetFullPath(Path.Combine(".", "Assets", EditorUtilities.s_DefaultGeneralSettingsPath[0])); + + XRGeneralSettingsPerBuildTarget m_PreviousSettings; + + [SetUp] + public void SetupPlayerSettings() + { + if (EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out _)) + { + EditorBuildSettings.RemoveConfigObject(XRGeneralSettings.k_SettingsKey); + } + + AssetDatabase.DeleteAsset("Assets/XR"); + } + + [TearDown] + public void TearDown() + { + } + + [Test] + public void CheckIfSettingsAreNotAddedIfNoSettingsExistInTheProject() + { + // Ensure Setup Functioned properly. + Assert.False( + EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out var buildSettings), + "Build Settings contains a settings config object when it should not."); + + Assert.False( + XRGeneralBuildProcessor.TryGetSettingsPerBuildTarget(out buildSettings), + "Build Settings Configuration Object exists when it shouldn't in the current project"); + } + } + + /// + /// A set of tests that are explicitly designed to ensure if + /// an asset exists + /// in the current project that it would get processed. + /// + /// + /// Functionally the opposite of the + /// except that these tests will always run since it's possible to create just test assets. + /// + [TestFixture(BuildTargetGroup.Standalone)] + [TestFixture(BuildTargetGroup.Android)] + [TestFixture(BuildTargetGroup.iOS)] +#if (!UNITY_2021_2_OR_NEWER) + [TestFixture(BuildTargetGroup.Lumin)] +#endif + [TestFixture(BuildTargetGroup.PS4)] + class XRGeneralSettingsPerBuildTargetInclusionTests + { + const string k_DefaultAssetPath = "Assets/XR"; + const string k_DefaultAssetName = "XRGeneralSettings.asset"; + + BuildTargetGroup m_BuildTargetGroup; + XRGeneralSettingsPerBuildTarget m_OldBuildTargetSettings; + + public XRGeneralSettingsPerBuildTargetInclusionTests(BuildTargetGroup group) + { + m_BuildTargetGroup = group; + } + + [SetUp] + public void SetupPlayerSettings() + { + if (!EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out m_OldBuildTargetSettings)) + { + // If not then we get or create the asset and delete it in the teardown + XRGeneralSettingsPerBuildTarget.GetOrCreate(); + AssetDatabase.Refresh(); + } + else + { + EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, m_OldBuildTargetSettings, true); + } + } + + [TearDown] + public void TearDown() + { + if (m_OldBuildTargetSettings == null) + { + EditorBuildSettings.RemoveConfigObject(XRGeneralSettings.k_SettingsKey); + + // Asset did not exists prior to test running so delete. + AssetDatabase.DeleteAsset(Path.Combine(k_DefaultAssetPath, k_DefaultAssetName)); + if (Directory.Exists(Path.Combine("./", k_DefaultAssetPath))) + { + Directory.Delete(Path.Combine("./", k_DefaultAssetPath), true); + File.Delete($"./{k_DefaultAssetPath}.meta"); + AssetDatabase.Refresh(); + } + } + } + + [Test] + public void EnsureSettingsAreIncludedWhenAssetExists() + { + // Ensure Setup Functioned properly. + Assert.True(XRGeneralBuildProcessor.TryGetSettingsPerBuildTarget(out var originalSettings), + "XRGeneralSettingsPerBuildTarget should be included at the beginning of the test."); + + // Remove the settings to check if the code functions properly + EditorBuildSettings.RemoveConfigObject(XRGeneralSettings.k_SettingsKey); + + Assert.True(XRGeneralBuildProcessor.TryGetSettingsPerBuildTarget(out var generalSettings), + "The asset should exist even if it's been removed from the editor configuration."); + Assert.True(EditorBuildSettings.TryGetConfigObject(XRGeneralSettings.k_SettingsKey, out generalSettings), + "The configuration object was not set by the build processor!"); + Assert.NotNull(generalSettings, "Build Settings Configuration Object does not exist when it in the current project."); + } + } + + + [TestFixture(BuildTargetGroup.Standalone, BuildTarget.StandaloneOSX)] + [TestFixture(BuildTargetGroup.Standalone, BuildTarget.StandaloneWindows64)] + [TestFixture(BuildTargetGroup.Android, BuildTarget.Android)] + [TestFixture(BuildTargetGroup.iOS, BuildTarget.iOS)] +#if (!UNITY_2021_2_OR_NEWER) + [TestFixture(BuildTargetGroup.Lumin, BuildTarget.Lumin)] +#endif + [TestFixture(BuildTargetGroup.PS4, BuildTarget.PS4)] + class XRLoaderSelection + { + private readonly BuildTargetGroup m_BuildTargetGroup; + private readonly BuildTarget m_BuildTarget; + + private DummyLoader m_Loader; + private string m_ExpectedConfigEntry; + + private XRGeneralSettingsPerBuildTarget m_OldBuildTargetSettings; + + public XRLoaderSelection(BuildTargetGroup group, BuildTarget buildTarget) + { + m_BuildTargetGroup = group; + m_BuildTarget = buildTarget; + + m_Loader = ScriptableObject.CreateInstance(typeof(DummyLoader)) as DummyLoader; + string dummyLoaderLibName = m_Loader.GetPreInitLibraryName(m_BuildTarget, m_BuildTargetGroup); + m_ExpectedConfigEntry = XRGeneralBuildProcessor.kPreInitLibraryKey + ":" + dummyLoaderLibName; + } + + void CleanupOldSettings() => BuildHelpers.CleanOldSettings(); + + [SetUp] + public void SetupPlayerSettings() + { + BuildTestHelpers.SetupTemporaryTestAssets(m_BuildTargetGroup, out m_OldBuildTargetSettings); + } + + [TearDown] + public void TearDown() + { + if (m_OldBuildTargetSettings != null) + EditorBuildSettings.AddConfigObject(XRGeneralSettings.k_SettingsKey, m_OldBuildTargetSettings, true); + + BuildTestHelpers.CleanupTemporaryAssets(); + } + + [Test] + public void EnsureLoaderIsIncludedInConfigWhenSelected() + { + XRGeneralBuildProcessor processor = new XRGeneralBuildProcessor(); + + // Set up with DummyLoader + XRManagerSettings settings = GetXRManagerSettings(); + Assert.True(settings.TryAddLoader(m_Loader)); + processor.OnPreprocessBuildImpl(new GUID(""), m_BuildTargetGroup, m_BuildTarget); + + // Determine that the DummyLoader was committed to Editor settings + string xrBootSettings = GetXRBootSettings(); + Assert.False(String.IsNullOrEmpty(xrBootSettings)); + string dummyLoaderLibName = m_Loader.GetPreInitLibraryName(m_BuildTarget, m_BuildTargetGroup); + Assert.True(xrBootSettings.Contains(m_ExpectedConfigEntry)); + } + + [Test] + public void EnsureXRPreInitKeyIsRemovedWhenNoLoaderSelected() + { + XRGeneralBuildProcessor processor = new XRGeneralBuildProcessor(); + + // Set up with DummyLoader + XRManagerSettings settings = GetXRManagerSettings(); + Assert.True(settings.TryAddLoader(m_Loader)); + processor.OnPreprocessBuildImpl(new GUID(""), m_BuildTargetGroup, m_BuildTarget); + + // Determine that the DummyLoader was committed to Editor settings + string xrBootSettings = GetXRBootSettings(); + Assert.False(String.IsNullOrEmpty(xrBootSettings)); + Assert.True(xrBootSettings.Contains(m_ExpectedConfigEntry)); + + // Now try to remove it and make sure it's removed from the config + Assert.True(settings.TryRemoveLoader(m_Loader)); + processor.OnPreprocessBuildImpl(new GUID(""), m_BuildTargetGroup, m_BuildTarget); + + // Determine that the DummyLoader was removed from the Editor settings + xrBootSettings = GetXRBootSettings(); + Assert.False(xrBootSettings.Contains(m_ExpectedConfigEntry)); + } + + private string GetXRBootSettings() + { + string buildTargetName = BuildPipeline.GetBuildTargetName(m_BuildTarget); + return UnityEditor + .EditorUserBuildSettings + .GetPlatformSettings(buildTargetName, BootConfig.kXrBootSettingsKey); + } + + private XRManagerSettings GetXRManagerSettings() + { + EditorBuildSettings + .TryGetConfigObject( + XRGeneralSettings.k_SettingsKey, + out XRGeneralSettingsPerBuildTarget buildTargetSettings); + return buildTargetSettings + .SettingsForBuildTarget(m_BuildTargetGroup) + .AssignedSettings; + } + } + + [TestFixture(BuildTargetGroup.Android, BuildTarget.Android)] + class TestAndroidManifestReset + { + public class PostGradleCallback : IPostGenerateGradleAndroidProject + { + public int callbackOrder + { + get { return XRGeneralBuildProcessor.s_CallbackOrder + 1; } + } + + public void OnPostGenerateGradleAndroidProject(string path) + { + s_onPostGenerateGradleAndroidProjectEvent?.Invoke(this, path); + } + } + + private readonly BuildTargetGroup m_BuildTargetGroup; + private readonly BuildTarget m_BuildTarget; + + private XRGeneralSettingsPerBuildTarget m_OldBuildTargetSettings; + + private static event EventHandler s_onPostGenerateGradleAndroidProjectEvent; + + public TestAndroidManifestReset(BuildTargetGroup group, BuildTarget target) + { + m_BuildTargetGroup = group; + m_BuildTarget = target; + } + + [Test] + public void DoesCleanUpAfterBuild() + { + const string k_ScenePath = "TestScene.unity"; + const string k_BuildFolder = "./build"; + const string k_BuildFile = k_BuildFolder + "/build.apk"; + + if (!BuildPipeline.IsBuildTargetSupported(m_BuildTargetGroup, m_BuildTarget)) + { + Debug.LogWarning( + string.Format( + "Test platform lacks {0}/{1} build target support", + m_BuildTargetGroup, + m_BuildTarget + ) + ); + return; + } + + Scene currentScene = SceneManager.GetActiveScene(); + currentScene.name = "TestScene"; + EditorSceneManager.SaveScene(currentScene, k_ScenePath); + + EventHandler gradleCallback = (object sender, string s) => + { + Assert.True(File.Exists(XRGeneralBuildProcessor.s_ManifestPath)); + }; + s_onPostGenerateGradleAndroidProjectEvent += gradleCallback; + + // if the project is blank and no package name was set, this prevents the test from + // failing + var packageID = PlayerSettings.GetApplicationIdentifier(m_BuildTargetGroup); + bool shouldChange = packageID?.StartsWith("com.DefaultCompany") ?? false; + if (shouldChange) { + PlayerSettings.SetApplicationIdentifier(m_BuildTargetGroup, "com.unity.test"); + } + + Directory.CreateDirectory(k_BuildFolder); + BuildPipeline.BuildPlayer( + new BuildPlayerOptions{ + locationPathName = k_BuildFile, + options = BuildOptions.None, + scenes = new string[]{k_ScenePath}, + target = m_BuildTarget, + targetGroup = m_BuildTargetGroup + } + ); + + Assert.False(File.Exists(XRGeneralBuildProcessor.s_ManifestPath)); + + File.Delete(k_ScenePath); + Directory.Delete(k_BuildFolder, true); + if (shouldChange) { + PlayerSettings.SetApplicationIdentifier(m_BuildTargetGroup, packageID); + } + + s_onPostGenerateGradleAndroidProjectEvent -= gradleCallback; + } + } } #endif //UNITY_EDITOR_WIN || UNITY_EDITOR_OSX diff --git a/Tests/Editor/EditorTests.cs b/Tests/Editor/EditorTests.cs index 545a013..59728fa 100644 --- a/Tests/Editor/EditorTests.cs +++ b/Tests/Editor/EditorTests.cs @@ -91,9 +91,11 @@ public void CanCreateNewManagerSettingsForMissingBuildTargetSettings() [TestFixture(4)] class EditorActiveLoadersManipulationTests : XRSettingsManagerTestBase { - public EditorActiveLoadersManipulationTests(int loaderCount) - : base(loaderCount) + public override int loaderCount { get; } + + public EditorActiveLoadersManipulationTests(int loaderCountIn) { + loaderCount = loaderCountIn; } [SetUp] diff --git a/Tests/Editor/StandaloneSubsystemTests.cs b/Tests/Editor/StandaloneSubsystemTests.cs index f454077..81d4188 100644 --- a/Tests/Editor/StandaloneSubsystemTests.cs +++ b/Tests/Editor/StandaloneSubsystemTests.cs @@ -6,7 +6,9 @@ using UnityEngine.TestTools; using UnityEngine.XR.Management.Tests.Standalone; +#if USE_LEGACY_SUBSYS_REGISTRATION && !UNITY_2020_2_OR_NEWER using UnityEngine.XR.Management.Tests.Standalone.Providing; +#endif namespace UnityEditor.XR.Management.Tests { @@ -15,8 +17,17 @@ class EditorTests [OneTimeSetUp] public void OneTimeSetUp() { +#if UNITY_2020_2_OR_NEWER + StandaloneSubsystemParams parms = new StandaloneSubsystemParams{ + id = "Standalone Subsystem", + subsystemTypeOverride = typeof(StandaloneSubsystemImpl), + providerType = typeof(StandaloneSubsystemImpl.ProviderImpl) + }; + StandaloneSubsystemDescriptor.Create(parms); +#elif USE_LEGACY_SUBSYS_REGISTRATION StandaloneSubsystemParams parms = new StandaloneSubsystemParams("Standalone Subsystem", typeof(StandaloneSubsystem)); StandaloneSubsystemDescriptor.Create(parms); +#endif } StandaloneLoader loader; diff --git a/Tests/Editor/Unity.XR.Management.EditorTests.asmdef b/Tests/Editor/Unity.XR.Management.EditorTests.asmdef index 339217a..1ebe340 100644 --- a/Tests/Editor/Unity.XR.Management.EditorTests.asmdef +++ b/Tests/Editor/Unity.XR.Management.EditorTests.asmdef @@ -25,6 +25,11 @@ "defineConstraints": [ "UNITY_INCLUDE_TESTS" ], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.subsystemregistration", + "define": "USE_LEGACY_SUBSYS_REGISTRATION" + } + ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Tests/Runtime/DummyLoader.cs b/Tests/Runtime/DummyLoader.cs index bab6c8d..b720d6c 100644 --- a/Tests/Runtime/DummyLoader.cs +++ b/Tests/Runtime/DummyLoader.cs @@ -2,10 +2,13 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; +using UnityEditor; +using UnityEngine.XR.Management; + [assembly: InternalsVisibleTo("Unity.XR.Management.EditorTests")] namespace UnityEngine.XR.Management.Tests { - internal class DummyLoader : XRLoader + internal class DummyLoader : XRLoader, IXRLoaderPreInit { public bool shouldFail = false; public int id; @@ -57,5 +60,10 @@ public override int GetHashCode() return hashCode; } } + + public string GetPreInitLibraryName(BuildTarget target, BuildTargetGroup targetGroup) + { + return "DummyLoader"; + } } } diff --git a/Tests/Runtime/RuntimeLoaderTests.cs b/Tests/Runtime/RuntimeLoaderTests.cs index dae7959..d8b9d69 100644 --- a/Tests/Runtime/RuntimeLoaderTests.cs +++ b/Tests/Runtime/RuntimeLoaderTests.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections; -using System.Collections.Generic; - using NUnit.Framework; using Unity.XR.TestTooling; using UnityEngine.XR.Management.Tests.Standalone; +#if USE_LEGACY_SUBSYS_REGISTRATION && !UNITY_2020_2_OR_NEWER using UnityEngine.XR.Management.Tests.Standalone.Providing; +#endif namespace UnityEngine.XR.Management.Tests { @@ -17,8 +15,17 @@ class RuntimeLoaderTests : LoaderTestSetup m_Loaders; public List loaders => m_Loaders; - public int loaderCount { get; } - - public XRSettingsManagerTestBase(int numberOfLoaders) - { - loaderCount = numberOfLoaders; - } + public abstract int loaderCount { get; } protected void SetupBase() { @@ -68,10 +63,11 @@ protected void TeardownBase() class ManualLifetimeTests : XRSettingsManagerTestBase { int m_LoaderIndexToWin; + public override int loaderCount { get; } - public ManualLifetimeTests(int loaderCount, int loaderIndexToWin) - : base(loaderCount) + public ManualLifetimeTests(int loaderCountIn, int loaderIndexToWin) { + loaderCount = loaderCountIn; m_LoaderIndexToWin = loaderIndexToWin; } @@ -121,30 +117,40 @@ public IEnumerator CheckActivatedLoader() #if UNITY_EDITOR_WIN || UNITY_EDITOR_OSX #if UNITY_EDITOR_WIN - [TestFixture(GraphicsDeviceType.Direct3D11, 0, new [] { GraphicsDeviceType.Direct3D11})] - [TestFixture(GraphicsDeviceType.Direct3D11, 1, new [] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Direct3D11})] - [TestFixture(GraphicsDeviceType.Direct3D11, -1, new [] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Direct3D11, 0, new [] { GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Direct3D11, 1, new [] { GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null})] + [TestFixture(GraphicsDeviceType.Direct3D11, 0, GraphicsDeviceType.Direct3D11, null)] + [TestFixture(GraphicsDeviceType.Direct3D11, 1, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Direct3D11)] + [TestFixture(GraphicsDeviceType.Direct3D11, -1, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Direct3D11, 0, GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Direct3D11, 1, GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null)] #elif UNITY_EDITOR_OSX - [TestFixture(GraphicsDeviceType.Metal, 0, new [] { GraphicsDeviceType.Metal})] - [TestFixture(GraphicsDeviceType.Metal, 1, new [] { GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Metal})] - [TestFixture(GraphicsDeviceType.Metal, -1, new [] { GraphicsDeviceType.OpenGLES3, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Metal, 0, new [] { GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan})] - [TestFixture(GraphicsDeviceType.Metal, 1, new [] { GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null})] + [TestFixture(GraphicsDeviceType.Metal, 0, GraphicsDeviceType.Metal, null)] + [TestFixture(GraphicsDeviceType.Metal, 1, GraphicsDeviceType.Direct3D12, GraphicsDeviceType.Metal)] + [TestFixture(GraphicsDeviceType.Metal, -1, GraphicsDeviceType.OpenGLES3, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Metal, 0, GraphicsDeviceType.Null, GraphicsDeviceType.Vulkan)] + [TestFixture(GraphicsDeviceType.Metal, 1, GraphicsDeviceType.Vulkan, GraphicsDeviceType.Null)] #endif class GraphicsAPICompatibilityTests : XRSettingsManagerTestBase { + public override int loaderCount { get; } private GraphicsDeviceType m_PlayerSettingsDeviceType; private GraphicsDeviceType[] m_LoadersSupporteDeviceTypes; int m_LoaderIndexToWin; - public GraphicsAPICompatibilityTests(GraphicsDeviceType playerSettingsDeviceType, int indexToWin, GraphicsDeviceType[] loaders) - : base(loaders.Length) + public GraphicsAPICompatibilityTests(GraphicsDeviceType playerSettingsDeviceType, int indexToWin, GraphicsDeviceType loader0, GraphicsDeviceType? loader1) { m_LoaderIndexToWin = indexToWin; m_PlayerSettingsDeviceType = playerSettingsDeviceType; - m_LoadersSupporteDeviceTypes = loaders; + + if (loader1.HasValue) + { + m_LoadersSupporteDeviceTypes = new[] {loader0, loader1.Value}; + } + else + { + m_LoadersSupporteDeviceTypes = new[] {loader0}; + } + + loaderCount = m_LoadersSupporteDeviceTypes.Length; } [SetUp] @@ -238,9 +244,10 @@ public IEnumerator CheckGraphicsAPICompatibility() [TestFixture(4)] class RuntimeActiveLoadersManipulationTests : XRSettingsManagerTestBase { - public RuntimeActiveLoadersManipulationTests(int loaderCount) - : base(loaderCount) + public override int loaderCount { get; } + public RuntimeActiveLoadersManipulationTests(int loaderCountIn) { + loaderCount = loaderCountIn; } [SetUp] @@ -285,14 +292,14 @@ public void CheckIfLegalAddSucceeds() if (loaderCount > 0) Assert.IsNotEmpty(manager.registeredLoaders); - Assert.AreEqual(manager.registeredLoaders.Count, loaderCount > 0 ? loaderCount : 0); + Assert.AreEqual(manager.registeredLoaders.Count, loaderCount); for (var i = 0; i < originalLoaders.Count; ++i) { Assert.True(manager.TryAddLoader(originalLoaders[originalLoaders.Count - 1 - i])); } - Assert.AreEqual(manager.registeredLoaders.Count, loaderCount >= 0 ? loaderCount : 0); + Assert.AreEqual(manager.registeredLoaders.Count, loaderCount); } [Test] diff --git a/Tests/Runtime/Unity.XR.Management.Tests.asmdef b/Tests/Runtime/Unity.XR.Management.Tests.asmdef index 17dfa91..4225034 100644 --- a/Tests/Runtime/Unity.XR.Management.Tests.asmdef +++ b/Tests/Runtime/Unity.XR.Management.Tests.asmdef @@ -12,5 +12,11 @@ "UNITY_INCLUDE_TESTS" ], "includePlatforms": [], - "excludePlatforms": [] + "excludePlatforms": [], + "versionDefines": [ + { + "name": "com.unity.subsystemregistration", + "define": "USE_LEGACY_SUBSYS_REGISTRATION" + } + ] } diff --git a/Tests/StandaloneSubsystem/StandaloneLoader.cs b/Tests/StandaloneSubsystem/StandaloneLoader.cs index 662a444..14264c9 100644 --- a/Tests/StandaloneSubsystem/StandaloneLoader.cs +++ b/Tests/StandaloneSubsystem/StandaloneLoader.cs @@ -1,4 +1,7 @@ using System.Collections.Generic; +#if UNITY_2020_2_OR_NEWER +using UnityEngine.SubsystemsImplementation.Extensions; +#endif namespace UnityEngine.XR.Management.Tests.Standalone { @@ -6,13 +9,7 @@ public class StandaloneLoader : XRLoaderHelper { static List s_StandaloneSubsystemDescriptors = new List(); - public StandaloneSubsystem standaloneSubsystem - { - get - { - return GetLoadedSubsystem(); - } - } + public StandaloneSubsystem standaloneSubsystem => GetLoadedSubsystem(); public bool started { get; protected set; } public bool stopped { get; protected set; } @@ -43,9 +40,21 @@ public override bool Initialize() if (standaloneSubsystem == null) return false; +#if UNITY_2020_2_OR_NEWER + var provider = standaloneSubsystem.GetProvider(); + + if (provider == null) + return false; + + provider.startCalled += OnStartCalled; + provider.stopCalled += OnStopCalled; + provider.destroyCalled += OnDestroyCalled; +#elif USE_LEGACY_SUBSYS_REGISTRATION standaloneSubsystem.startCalled += OnStartCalled; standaloneSubsystem.stopCalled += OnStopCalled; standaloneSubsystem.destroyCalled += OnDestroyCalled; +#endif + return true; } @@ -68,9 +77,20 @@ public override bool Deinitialize() DestroySubsystem(); if (standaloneSubsystem != null) { +#if UNITY_2020_2_OR_NEWER + var provider = standaloneSubsystem.GetProvider(); + + if (provider != null) + { + provider.startCalled -= OnStartCalled; + provider.stopCalled -= OnStopCalled; + provider.destroyCalled -= OnDestroyCalled; + } +#elif USE_LEGACY_SUBSYS_REGISTRATION standaloneSubsystem.startCalled -= OnStartCalled; standaloneSubsystem.stopCalled -= OnStopCalled; standaloneSubsystem.destroyCalled -= OnDestroyCalled; +#endif } return base.Deinitialize(); } diff --git a/Tests/StandaloneSubsystem/StandaloneSubsystem.cs b/Tests/StandaloneSubsystem/StandaloneSubsystem.cs index 19804f0..df4f288 100644 --- a/Tests/StandaloneSubsystem/StandaloneSubsystem.cs +++ b/Tests/StandaloneSubsystem/StandaloneSubsystem.cs @@ -1,23 +1,56 @@ using System; +#if UNITY_2020_2_OR_NEWER +using UnityEngine.SubsystemsImplementation; +#endif namespace UnityEngine.XR.Management.Tests.Standalone { +#if UNITY_2020_2_OR_NEWER + public class StandaloneSubsystem : SubsystemWithProvider + { + public class Provider : SubsystemProvider + { + public event Action startCalled; + public event Action stopCalled; + public event Action destroyCalled; + + public override void Start() + { + if (startCalled != null) + startCalled.Invoke(); + } + + public override void Stop() + { + if (stopCalled != null) + stopCalled.Invoke(); + } + + public override void Destroy() + { + if (destroyCalled != null) + destroyCalled.Invoke(); + } + } + } + + public class StandaloneSubsystemImpl : StandaloneSubsystem + { + public class ProviderImpl : Provider{ } + } +#else public class StandaloneSubsystem : Subsystem { private bool isRunning = false; public override bool running { - get - { - return isRunning; - } + get { return isRunning; } } public event Action startCalled; public event Action stopCalled; public event Action destroyCalled; - public override void Start() { isRunning = true; @@ -39,4 +72,5 @@ protected override void OnDestroy() destroyCalled.Invoke(); } } +#endif } diff --git a/Tests/StandaloneSubsystem/StandaloneSubsystemDescriptor.cs b/Tests/StandaloneSubsystem/StandaloneSubsystemDescriptor.cs index 8764c55..32201bb 100644 --- a/Tests/StandaloneSubsystem/StandaloneSubsystemDescriptor.cs +++ b/Tests/StandaloneSubsystem/StandaloneSubsystemDescriptor.cs @@ -1,6 +1,35 @@ +using System; +#if UNITY_2020_2_OR_NEWER +using UnityEngine.SubsystemsImplementation; +#endif + namespace UnityEngine.XR.Management.Tests.Standalone { +#if UNITY_2020_2_OR_NEWER + public class StandaloneSubsystemParams + { + public string id { get; set;} + public Type subsystemTypeOverride { get; set; } + public Type providerType { get; set; } + } + + public class StandaloneSubsystemDescriptor : SubsystemDescriptorWithProvider + { + public static void Create(StandaloneSubsystemParams descriptorParams) + { + var descriptor = new StandaloneSubsystemDescriptor(descriptorParams); + SubsystemDescriptorStore.RegisterDescriptor(descriptor); + } + + StandaloneSubsystemDescriptor(StandaloneSubsystemParams descriptorParams) + { + id = descriptorParams.id; + subsystemTypeOverride = descriptorParams.subsystemTypeOverride; + providerType = descriptorParams.providerType; + } + } +#else namespace Providing { public class StandaloneSubsystemParams @@ -20,7 +49,9 @@ public class StandaloneSubsystemDescriptor : SubsystemDescriptor