diff --git a/.gitattributes b/.gitattributes index 46705fd9068..3cac295945b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -387,3 +387,15 @@ Editor/Resources/unity[[:space:]]editor[[:space:]]resources filter=lfs diff=lfs /Templates/**/*.[tT][gG][aA] filter=lfs diff=lfs merge=lfs -text /Templates/**/*.[tT][iI][fF] filter=lfs diff=lfs merge=lfs -text /Templates/**/*.[fF][bB][xX] filter=lfs diff=lfs merge=lfs -text + +# Netcode Source Generators +/Packages/com.unity.netcode/**/*.[jJ][pP][gG] filter=lfs diff=lfs merge=lfs -text +/Packages/com.unity.netcode/**/*.[pP][nN][gG] filter=lfs diff=lfs merge=lfs -text +/Packages/com.unity.netcode/Runtime/SourceGenerators/**/*.[pP][dD][bB] filter=lfs diff=lfs merge=lfs -text + +/Packages/com.unity.transport/**/*.[pP][nN][gG] filter=lfs diff=lfs merge=lfs -text + +/Tests/EditModeAndPlayModeTests/NetcodeSamples/**/*.[pP][nN][gG] filter=lfs diff=lfs merge=lfs -text +/Tests/EditModeAndPlayModeTests/NetcodeSamples/**/*.[jJ][pP][gG] filter=lfs diff=lfs merge=lfs -text +/Tests/EditModeAndPlayModeTests/NetcodeSamples/**/*.[tT][iI][fF] filter=lfs diff=lfs merge=lfs -text +/Tests/EditModeAndPlayModeTests/NetcodeSamples/**/*.[fF][bB][xX] filter=lfs diff=lfs merge=lfs -text diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev1.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev1.png deleted file mode 100644 index d512286757b..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev1.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev2.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev2.png deleted file mode 100644 index 961d9584421..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev2.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev3.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev3.png deleted file mode 100644 index d669ef40239..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev3.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev4.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev4.png deleted file mode 100644 index 649c19ff328..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev4.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev5.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev5.png deleted file mode 100644 index 9347fe64951..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev5.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev6.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev6.png deleted file mode 100644 index c79af44c093..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev6.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev7.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev7.png deleted file mode 100644 index 7462d285198..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev7.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev8.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev8.png deleted file mode 100644 index 7c719e979ff..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev8.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev9.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev9.png deleted file mode 100644 index 49e573b0855..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDev9.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary1.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary1.png deleted file mode 100644 index aa240a4c0eb..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary1.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary2.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary2.png deleted file mode 100644 index f20efd26914..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary2.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary3.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary3.png deleted file mode 100644 index 818f28dd47e..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary3.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary4.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary4.png deleted file mode 100644 index f86261e0073..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary4.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary5.png b/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary5.png deleted file mode 100644 index c692c7a0fa6..00000000000 Binary files a/Packages/com.unity.render-pipelines.core/Documentation~/Images/LookDevEnvironmentLibrary5.png and /dev/null differ diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Look-Dev-Environment-Library.md b/Packages/com.unity.render-pipelines.core/Documentation~/Look-Dev-Environment-Library.md deleted file mode 100644 index 8b836adba35..00000000000 --- a/Packages/com.unity.render-pipelines.core/Documentation~/Look-Dev-Environment-Library.md +++ /dev/null @@ -1,52 +0,0 @@ -# Environment Library - -An Environment Library is an Asset that contains a list of environments that you can use in [Look Dev](Look-Dev.html) to simulate different lighting conditions. Each environment uses an HDRI (High Dynamic Range Image) for its skybox and also includes properties that you can use to fine-tune the environment. - - - -![Environment Library asset.](Images/LookDevEnvironmentLibrary1.png) - -## Creating an Environment Library - -To create an Environment Library Asset, either: - -- Select **Assets > Create > Rendering Environment Library (Look Dev)**. -- Open [Look Dev](Look-Dev.html) and click the **New Library** button. - -## Creating and editing an environment - -After you create an Environment Library, you can add environments to it which you can then use in Look Dev. To create environments or edit their properties, use the Look Dev window itself. To create and edit environments, you need to open an Environment Library in Look Dev. To do this, either: - -- Go to the Look Dev window (menu: **Window > Rendering > Look Dev**) and drag your Environment Library from your Project window into the sidebar. -- In your Project window, click on your Environment Library Asset. Then, in the Inspector, click the **Open in LookDev window** button. - -If you already have environments in the Environment Library, you can see a list of them in the sidebar. When you click on any of the HDRI previews for an environment, a box appears at the bottom of the Environment Library list. This contains the selected environment's properties for you to edit. - -To add, remove, or duplicate environments, use the toolbar at the bottom of the Environment Library list, which contains the following buttons. - -| **Button** | **Function** | **Description** | -| ------------------------------------------------------------ | ------------- | ------------------------------------------------------------ | -| ![This button adds a new environment to the bottom of the list.](Images/LookDevEnvironmentLibrary2.png) | **Add** | Click this button to add a new environment to the bottom of the list. | -| ![This button removes the currently selected environment.](Images/LookDevEnvironmentLibrary3.png) | **Remove** | Click this button to remove the environment currently selected. Note that the environment that you have selected is the one with the blue frame. | -| ![This button duplicates the currently selected environment.](Images/LookDevEnvironmentLibrary4.png) | **Duplicate** | Click this button to duplicate the currently selected environment and add it as a new environment to the bottom of the list. | - -## Properties - -![Environment Library properties](Images/LookDevEnvironmentLibrary5.png) - -| **Property** | **Description** | -| ------------------- | ------------------------------------------------------------ | -| **Sky with Sun** | Set the HDRI Texture that Look Dev uses for the sky lighting when using this environment. For information on how to import an HDRI Texture, see [Importing an HDRI Texture](#ImportingAnHDRI). | -| **Sky without Sun** | Set the HDRI Texture that Look Dev uses for compositing the shadows when simulating a sun in the view. If you do not assign this value, Look Dev uses a lower intensity version of the same HDRI Texture in **Sky with Sun**. For information on how to import an HDRI Texture, see [Importing an HDRI Texture](#ImportingAnHDRI). | -| **Rotation** | Set the offset longitude that Look Dev applies for the whole sky and sun position. | -| **Exposure** | Set the exposure that Look Dev uses when it renders the environment. | -| **Sun Position** | Set the position of the sun when compositing the shadows. The Sun button at the end of the line automatically places the sun on the brightest spot of the **Sky with Sun** HDRI Texture. | -| **Shadow Tint** | Use the color picker to set the color of the tint that Look Dev uses to color shadows. | - - - -## Importing an HDRI Texture - -To import an HDRI Texture into the Unity Editor, load an **.hdr** or **.exr** file into your Unity Project like you would any other image. In the Texture Importer Inspector window, set **Texture Type** to **Default**, set **Texture Shape** to **Cube**, and set **Convolution Type** to **None**. - -When you want to test an HDRI Texture Asset or a skybox cube map Material, drag and drop it into the Look Dev view. diff --git a/Packages/com.unity.render-pipelines.core/Documentation~/Look-Dev.md b/Packages/com.unity.render-pipelines.core/Documentation~/Look-Dev.md deleted file mode 100644 index 4b9f4763648..00000000000 --- a/Packages/com.unity.render-pipelines.core/Documentation~/Look-Dev.md +++ /dev/null @@ -1,138 +0,0 @@ -# Look Dev - -Look Dev is an image-based lighting tool that contains a viewer for you to check and compare Assets to ensure they work well in various lighting conditions. Look Dev uses the Scriptable Render Pipeline, so it can display the Asset in the same way as it looks in your Scene. You can load Assets into Look Dev either as Prefabs or from the Hierarchy window. - -Look Dev is only available in Edit mode. The Look Dev window closes when you enter Play mode. - -### Asset validation - -Asset validation confirms whether Assets are authored correctly and behave as expected in different lighting environments. - -You must use an HDRI (high dynamic range image) to validate your Assets in Look Dev. An HDRI contains real-world lighting with incredibly high detail. As such, it offers perfect lighting that is difficult to create by hand. By using such an accurate lighting environment to test an Asset, you can determine whether the Asset itself or your Project's lighting is reducing the visual quality of your Scene. - -You can load two different Assets into Look Dev at the same time and compare them in two viewports. For example, an Art Director can check that a new Asset matches the art direction guidelines of a reference Asset. - -## Using Look Dev - -To open Look Dev in the Unity Editor, select **Window > Rendering > Look Dev**. The first time you use Look Dev, you must either create a new [Environment Library](Look-Dev-Environment-Library.html) or load one. For information on how to create an Environment Library, see the [Environment Library documentation](Look-Dev-Environment-Library.html). - -### Viewports - -By default, there is only one viewport in Look Dev, but you can choose from a selection of split-screen views (see the [Multi-view section](#MultiView)). - -### Controls - -Navigation with the Look Dev Camera works in a similar way to the [Scene view Camera](https://docs.unity3d.com/Manual/SceneViewNavigation.html): - -- **Rotate around pivot:** Left click and drag (this is similar to the Scene view except that you need to press the Alt key for the Scene view Camera). -- **Pan camera:** Middle click and drag. -- **Zoom:** Alt + right click and drag. -- **Forward/backward:** Mouse wheel. -- **First Person mode:** Right click + W, A,S, and D. - -### Loading Assets into Look Dev - -Look Dev lets you view: - -**Prefabs** - To load a Prefab into Look Dev, drag it from the Project window into the Look Dev viewport. - -**GameObjects** - To load a copy of a Hierarchy GameObject, drag the GameObject from the Hierarchy into the Look Dev viewport. - - - -## Viewport modes - -Use the toolbar in the top-left of the window to change which viewing mode Look Dev uses. - -### Single viewport - -![Buttons for switching between viewports 1 and 2.](Images/LookDev1.png) - -By default, Look Dev displays a single viewport which contains the Prefab or GameObject you are working with. If you are in another viewing mode, you can click either the number **1** or number **2** button to go back to single view. Each button corresponds to a viewport in Look Dev. Select button **1** to use viewport 1, and button 2 to use viewport **2**. - - - -### Multi-viewport - -![Buttons for changing the multi-viewport mode.](Images/LookDev2.png) - -Use multiple viewports to compare different environments and settings for the same Asset. You can arrange viewports: - -- Vertically side-by-side. Use this mode to compare two different lighting conditions on the same Asset to check that the Asset behaves correctly. -- Horizontally side-by-side. Use this mode to compare two different lighting conditions for horizontal objects, like an environment Asset, to check that the Asset behaves correctly. -- Split-screen. Use this mode investigate texture problems using a debug Shader mode (for example, use one screen to view Normal or Albedo shading, and the other for environment-lit mode). -- Side-by-side and split-screen: Use this mode to compare two different versions of the same Asset using the same lighting conditions to see which changes improve the Asset’s quality. - -All three of these modes are useful to compare two different versions of the same Asset using the same lighting conditions to see which changes improve the Asset’s quality. - -To load a different Prefab or Hierarchy GameObject into each split-screen view, drag and drop the Asset into the viewport that you want to view it in. - -When using multiple viewports, it only makes sense to compare different Prefabs or GameObjects when you want to look at two versions of the same Asset. Comparing completely different Assets doesn’t give you a good idea of the difference in lighting or visual effect. - -##### Vertical or horizontal side-by-side - -Vertical and horizontal side-by-side viewports show an identical view of your Asset. - -![Vertical side-by-side viewport view.](Images/LookDev3.png) - -##### Split-screen - -In a split-screen view, there is a red/blue manipulation Gizmo that separates the two viewports. For information on how to use this Gizmo, see [Using the manipulation Gizmo](#ManipulationGizmo). - -![Split-screen viewport view.](Images/LookDev4.png) - -#### Multi-viewport Camera - -By default, Look Dev synchronizes the camera movement for both views. To decouple the Cameras from one another, and manipulate them independently, click the **Synchronized Cameras** button in-between the two numbered Camera buttons. - -To align the cameras with each other, or reset them, click on the drop-down arrow next to the viewport **2** icon: - -![Drop-down options of multi-viewport cameras.](Images/LookDev6.png) - - - -### Using the manipulation Gizmo - -The manipulation Gizmo represents the separation plane between the two viewports. It has different behavior in split-screen mode, but you use it in the same way for both side-by-side or split-screen modes. - -#### Moving the separator - -To move the separator, click and drag the straight line of the Gizmo to the location you want. - -#### Changing the orientation and length - -To change the orientation and length of the manipulator Gizmo, click and drag the circle at either end of the manipulator. Changing the length of the Gizmo lets you set the orientation and [blending](#Blending) values more precisely. - -![Click and drag the circle at either end of the gizmo to change its orientation and length.](Images/LookDev8.png) - -#### Changing the split in increments - -To change the split in increments, click and hold the circle on the end of the manipulation Gizmo, then hold Shift as you move the mouse. This snaps the manipulation Gizmo to set angles in increments of 22.5°, which is useful for a perfectly horizontal, vertical or diagonal angle. - - - -#### Blending - -The central white circle on the separator allows you to blend between the two views. Left click on it and drag along the red line to blend the left-hand view with the right-hand view. Drag along the blue line to blend the right-hand view with the left-hand view (as shown in the image below). - -The white circle automatically snaps back into the center when you drag it back. This helps you get back to the default blending value quickly. - -![The white circle on the separator lets you change the blending behavior.](Images/LookDev9.png) - -### HDRI environments in Look Dev - -Lighting in Look Dev uses an HDRI. The Look Dev view allows you to manipulate and easily switch between HDRIs to simulate different environments for the Asset you are working on. - -Look Dev uses the [Environment Library](Look-Dev-Environment-Library.html) Asset to store a list of environments, which are HDRIs with extra properties that you can use to further refine the environment. For information on how to create, edit, and assign Environment Libraries, see the [Environment Library documentation](Look-Dev-Environment-Library.html#Creation). - -## Implementing Look Dev for your custom Scriptable Render Pipeline - -In order to use Look Dev in your custom Scriptable Render Pipeline, you must implement the **UnityEngine.Rendering.LookDev.IDataProvider** interface. - -| **Function** | **Description** | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| **void FirstInitScene(StageRuntimeInterface stage)** | Look Dev calls this function after it initializes the Scene with a Light and Camera. It uses this function to add and configure extra components according to the needs of your Scriptable Render Pipeline. | -| **void UpdateSky(Camera camera, Sky sky, StageRuntimeInterface stage)** | Look Dev uses this function to update the environment when you change something in Look Dev. You can handle the sky in various ways, so add code that corresponds to your Scriptable Render Pipeline. | -| **IEnumerable****** **supportedDebugModes { get; }** | Use this function to specify the list of supported debug modes. You do not need to add **None** because Look Dev handles that automatically. | -| **void UpdateDebugMode(int debugIndex)** | Use this function to update the debug mode based on what the user selects. The **debugIndex** matches the list in **supportedDebugModes**. If the user selects **None**, then the **debugIndex** is **-1**; | -| **void GetShadowMask(ref RenderTexture output, StageRuntimeInterface stage)** | This function computes a shadow map. The given **StageRuntimeInterface** contains access to the Camera and a Light simulating the sun. | diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterAssetItem.cs b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterAssetItem.cs index 80a8a7ab3c0..de8db770eb8 100644 --- a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterAssetItem.cs +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterAssetItem.cs @@ -16,6 +16,15 @@ public string assetPath protected set => m_AssetPath = value; } + [SerializeField] + private string m_GUID; + + public string guid + { + get => m_GUID; + protected set => m_GUID = value; + } + [SerializeField] private string m_GlobalObjectId; @@ -58,6 +67,7 @@ public RenderPipelineConverterAssetItem(string id) m_AssetPath = AssetDatabase.GUIDToAssetPath(gid.assetGUID); m_GlobalObjectId = gid.ToString(); + m_GUID = gid.assetGUID.ToString(); } public RenderPipelineConverterAssetItem(GlobalObjectId gid, string assetPath) @@ -67,6 +77,7 @@ public RenderPipelineConverterAssetItem(GlobalObjectId gid, string assetPath) m_AssetPath = assetPath; m_GlobalObjectId = gid.ToString(); + m_GUID = AssetDatabase.AssetPathToGUID(assetPath); } public UnityEngine.Object LoadObject() diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterMaterialUpgrader.cs b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterMaterialUpgrader.cs index 7029e50ddc8..d4c0681e554 100644 --- a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterMaterialUpgrader.cs +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderPipelineConverterMaterialUpgrader.cs @@ -85,7 +85,7 @@ internal abstract class RenderPipelineConverterMaterialUpgrader : IRenderPipelin internal List assets = new(); - public RenderPipelineConverterMaterialUpgrader() + public void Scan(Action> onScanFinish) { m_UpgradersCache = upgraders; @@ -94,10 +94,7 @@ public RenderPipelineConverterMaterialUpgrader() Debug.Log($"No upgraders specified for this converter ({GetType()}). Skipping Initialization."); return; } - } - public void Scan(Action> onScanFinish) - { var materialsGroupByShader = MaterialFinder.GroupAllMaterialsInProject(); using (HashSetPool.Get(out var destinationShaders)) { diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderSettingsConverter.cs b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderSettingsConverter.cs new file mode 100644 index 00000000000..8b3195ad280 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderSettingsConverter.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEngine.Rendering; + +namespace UnityEditor.Rendering.Converter +{ + [Serializable] + internal class RenderSettingsConverterItem : IRenderPipelineConverterItem + { + public int qualityLevelIndex { get; set; } + + public string name { get; set; } + + public string info { get; set; } + + public bool isEnabled { get; set; } + public string isDisabledMessage { get; set; } + + public Texture2D icon + { + get + { + var iconAttribute = typeof(RenderPipelineAsset).GetCustomAttribute(); + if (iconAttribute == null || string.IsNullOrEmpty(iconAttribute.path)) + return null; + return EditorGUIUtility.IconContent(iconAttribute.path)?.image as Texture2D; + } + } + public void OnClicked() + { + SettingsService.OpenProjectSettings("Project/Quality"); + } + } + + [Serializable] + abstract class RenderSettingsConverter : IRenderPipelineConverter + { + public void Scan(Action> onScanFinish) + { + List renderPipelineConverterItems = new (); + QualitySettings.ForEach((index, name) => + { + var item = new RenderSettingsConverterItem + { + qualityLevelIndex = index, + name = name + }; + + if (QualitySettings.renderPipeline is not RenderPipelineAsset) + { + item.isEnabled = true; + item.info = $"Create a Render Pipeline Asset for Quality Level {index} ({name})"; + } + else + { + item.info = "Quality Level already references a Render Pipeline Asset."; + item.isEnabled = false; + item.isDisabledMessage = item.info; + } + renderPipelineConverterItems.Add(item); + }); + + onScanFinish?.Invoke(renderPipelineConverterItems); + } + public abstract bool isEnabled { get; } + + public abstract string isDisabledMessage { get; } + + public Status Convert(IRenderPipelineConverterItem item, out string message) + { + message = string.Empty; + + if (item is RenderSettingsConverterItem qualityLevelItem) + { + if (CreateRPAssetForQualityLevel(qualityLevelItem.qualityLevelIndex, out message)) + { + message = "Each Quality Level now has a new, unique RP asset, but all share identical settings. Modify each asset to restore your performance/quality tiers."; + return Status.Warning; + } + } + + return Status.Error; + } + + private bool CreateRPAssetForQualityLevel(int qualityIndex, out string message) + { + bool ok = false; + message = string.Empty; + + var currentQualityLevel = QualitySettings.GetQualityLevel(); + + QualitySettings.SetQualityLevel(qualityIndex); + + if (QualitySettings.renderPipeline is RenderPipelineAsset rpAsset) + { + message = $"Quality Level {qualityIndex} already references a Render Pipeline Asset: {rpAsset.name}."; + } + else + { + var asset = CreateAsset($"{QualitySettings.names[qualityIndex]}"); + + if (asset != null) + { + // Map built-in data to the URP asset data + SetPipelineSettings(asset); + + // Set the asset dirty to make sure that the renderer data is saved + EditorUtility.SetDirty(asset); + AssetDatabase.SaveAssetIfDirty(asset); + + QualitySettings.renderPipeline = asset; + ok = true; + } + else + { + message = "Failed to create Universal Render Pipeline Asset."; + } + } + + // Restore back the quality level + QualitySettings.SetQualityLevel(currentQualityLevel); + + return ok; + } + + protected abstract RenderPipelineAsset CreateAsset(string name); + + protected abstract void SetPipelineSettings(RenderPipelineAsset asset); + } +} diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderSettingsConverter.cs.meta b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderSettingsConverter.cs.meta new file mode 100644 index 00000000000..2086555801d --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/RenderSettingsConverter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 81990f396fa7c0a41be94125220f755a \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.cs b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.cs index 2779480893e..d457e54e824 100644 --- a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.cs +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.cs @@ -79,6 +79,12 @@ public static void ShowWindow() wnd.Show(); } + [MenuItem("Window/Rendering/Render Pipeline Converter", true, 50)] + public static bool CanShowWindow() + { + return !EditorApplication.isPlaying; + } + internal static void DontSaveToLayout(EditorWindow wnd) { // Making sure that the window is not saved in layouts. @@ -182,6 +188,7 @@ public void CreateGUI() RenderPipelineConverterVisualElement converterVisualElement = new(element); converterVisualElement.converterSelected += EnableOrDisableConvertButton; + converterVisualElement.converterSelected += EnableOrDisableScanButton; m_ConvertersVisualElements.Add(element, converterVisualElement); } } @@ -219,11 +226,26 @@ public void CreateGUI() }); HideUnhideConverters(); + EnableOrDisableScanButton(); EnableOrDisableConvertButton(); UpdateUiForPlayMode(EditorApplication.isPlaying); } + private bool CanEnableScan() + { + foreach (var kvp in m_ConvertersVisualElements) + { + var ve = kvp.Value; + if (ve.isSelectedAndEnabled && + !ve.state.isInitialized) + { + return true; + } + } + return false; + } + private bool CanEnableConvert() { foreach (var kvp in m_ConvertersVisualElements) @@ -245,6 +267,11 @@ private void EnableOrDisableConvertButton() m_ConvertButton.SetEnabled(CanEnableConvert()); } + private void EnableOrDisableScanButton() + { + m_InitButton.SetEnabled(CanEnableScan()); + } + private void HideUnhideConverters() { if (currentContainer == null) @@ -298,8 +325,8 @@ IEnumerable selectedConverters void InitializeAllActiveConverters(ClickEvent evt) { - if (!SaveCurrentSceneAndContinue()) - return; + if (EditorApplication.isPlaying) return; + if (!SaveCurrentSceneAndContinue()) return; // Gather all the converters that are selected var convertersToInitialize = new List(); @@ -368,6 +395,7 @@ private void RefreshUI() converterVE.Refresh(); } + EnableOrDisableScanButton(); EnableOrDisableConvertButton(); } @@ -399,6 +427,7 @@ struct AnalyticContextInfo void Convert(ClickEvent evt) { + if (EditorApplication.isPlaying) return; if (!ShowIrreversibleChangesDialog()) return; // Ask to save save the current open scene and after the conversion is done reload the same scene. if (!SaveCurrentSceneAndContinue()) return; diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uss b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uss index 25de710cf15..4bddc8cb2ae 100644 --- a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uss +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uss @@ -17,6 +17,7 @@ padding-top: 2px; width: auto; padding-bottom: 20px; + flex-direction: column; } .converter-top-info { @@ -57,6 +58,8 @@ .converter-scroll-view { flex-grow: 1; align-items: stretch; + flex-shrink: 1; + min-height: 0; } .converter-bottom-buttons { @@ -82,12 +85,14 @@ flex-direction: row; justify-content: flex-end; /* right align */ flex-wrap: wrap; /* allow wrapping */ - margin-bottom: 10px; + margin-top: 10px; + flex-shrink: 0; + min-height: 40px; } .converter-button { flex-shrink: 0; min-width: 100px; /* adjust to your layout */ - min-height: 20px; - margin-top: 10px; + align-self: flex-end; } + diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uxml b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uxml index 09391e43e6d..c537e9592bd 100644 --- a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uxml +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConvertersEditor.uxml @@ -1,5 +1,5 @@ - + diff --git a/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/CoreBuildData.cs b/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/CoreBuildData.cs index 3f675d9c7c2..05de0b1a735 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/CoreBuildData.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/CoreBuildData.cs @@ -34,15 +34,22 @@ public class CoreBuildData : IDisposable /// public List renderPipelineAssets { get; private set; } = new(); + /// + /// Whether the build is a development build or not. + /// + public bool developmentBuild { get; private set; } = false; + internal Dictionary computeShaderCache { get; private set; } = new(); internal bool pipelineSupportGPUResidentDrawer { get; private set; } = false; internal bool playerNeedGPUResidentDrawer { get; private set; } = false; - private CoreBuildData(BuildTarget buildTarget) + private CoreBuildData(BuildTarget buildTarget, bool development) { m_Instance = this; + developmentBuild = development; + if (!buildTarget.TryGetRenderPipelineAssets(renderPipelineAssets)) return; @@ -56,7 +63,7 @@ private CoreBuildData(BuildTarget buildTarget) } private static CoreBuildData CreateInstance() - => new(EditorUserBuildSettings.activeBuildTarget); + => new(EditorUserBuildSettings.activeBuildTarget, EditorUserBuildSettings.development); private void CheckGPUResidentDrawerUsage() { diff --git a/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/SettingsStrippers/RenderingDebuggerRuntimeResourcesStripper.cs b/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/SettingsStrippers/RenderingDebuggerRuntimeResourcesStripper.cs new file mode 100644 index 00000000000..15fc576e592 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/SettingsStrippers/RenderingDebuggerRuntimeResourcesStripper.cs @@ -0,0 +1,11 @@ +using UnityEngine.Rendering; + +namespace UnityEditor.Rendering +{ + class RenderingDebuggerRuntimeResourcesStripper : IRenderPipelineGraphicsSettingsStripper + { + public bool active => true; + + public bool CanRemoveSettings(RenderingDebuggerRuntimeResources settings) => !CoreBuildData.instance.developmentBuild; + } +} diff --git a/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/SettingsStrippers/RenderingDebuggerRuntimeResourcesStripper.cs.meta b/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/SettingsStrippers/RenderingDebuggerRuntimeResourcesStripper.cs.meta new file mode 100644 index 00000000000..54f99ef88e4 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/BuildProcessors/SettingsStrippers/RenderingDebuggerRuntimeResourcesStripper.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8f0cdf6a21623f84cad55c1a41d92b16 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugState.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugState.cs index c9dafbe881b..eeda5bddfc4 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugState.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugState.cs @@ -8,6 +8,7 @@ namespace UnityEditor.Rendering /// Serialized state of a Debug Item. /// [Serializable] + [Obsolete("This class is no longer used. #from(6000.5)")] public abstract class DebugState : ScriptableObject { /// @@ -58,6 +59,7 @@ public virtual void OnEnable() /// /// The type of the Debug Item. [Serializable] + [Obsolete("This class is no longer used. #from(6000.5)")] public class DebugState : DebugState { /// @@ -115,6 +117,7 @@ public override int GetHashCode() /// /// Attribute specifying which types should be save as this Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] public sealed class DebugStateAttribute : Attribute { internal readonly Type[] types; @@ -133,12 +136,14 @@ public DebugStateAttribute(params Type[] types) /// /// Boolean Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.BoolField), typeof(DebugUI.Foldout), typeof(DebugUI.HistoryBoolField))] public sealed class DebugStateBool : DebugState { } /// /// Enums Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.EnumField), typeof(DebugUI.HistoryEnumField))] public sealed class DebugStateEnum : DebugState { @@ -173,12 +178,14 @@ public override void OnEnable() /// /// Integer Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.IntField))] public sealed class DebugStateInt : DebugState { } /// /// Object Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.ObjectPopupField), typeof(DebugUI.CameraSelector), typeof(DebugUI.ObjectField))] public sealed class DebugStateObject : DebugState { @@ -235,6 +242,7 @@ public override int GetHashCode() /// /// Flags Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.BitField))] public sealed class DebugStateFlags : DebugState { @@ -266,42 +274,49 @@ public override void SetValue(object value, DebugUI.IValueField field) /// /// Unsigned Integer Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.UIntField))] public sealed class DebugStateUInt : DebugState { } /// /// Rendering layer mask state. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.RenderingLayerField))] public sealed class DebugStateRenderingLayer : DebugState { } /// /// Float Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.FloatField))] public sealed class DebugStateFloat : DebugState { } /// /// Color Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.ColorField))] public sealed class DebugStateColor : DebugState { } /// /// Vector2 Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.Vector2Field))] public sealed class DebugStateVector2 : DebugState { } /// /// Vector3 Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.Vector3Field))] public sealed class DebugStateVector3 : DebugState { } /// /// Vector4 Debug State. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [Serializable, DebugState(typeof(DebugUI.Vector4Field))] public sealed class DebugStateVector4 : DebugState { } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs index 1236c047ef2..3276089b073 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.Builtins.cs @@ -9,6 +9,7 @@ namespace UnityEditor.Rendering /// /// Builtin Drawer for Value Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Value))] public sealed class DebugUIDrawerValue : DebugUIWidgetDrawer { @@ -27,6 +28,7 @@ protected override void DoGUI(Rect rect, GUIContent label, DebugUI.Value field) /// /// Builtin Drawer for ValueTuple Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.ValueTuple))] public sealed class DebugUIDrawerValueTuple : DebugUIWidgetDrawer { @@ -48,13 +50,13 @@ protected override void DoGUI(Rect rect, GUIContent label, DebugUI.ValueTuple fi for (int i = 0; i < field.numElements; i++) { var columnRect = drawRect; - columnRect.x += EditorGUIUtility.labelWidth + i * DebugWindow.Styles.foldoutColumnWidth; - columnRect.width = DebugWindow.Styles.foldoutColumnWidth; + columnRect.x += EditorGUIUtility.labelWidth + i * LegacyStyles.foldoutColumnWidth; + columnRect.width = LegacyStyles.foldoutColumnWidth; var value = field.values[i].GetValue(); var style = EditorStyles.label; if (Convert.ToSingle(value) == 0) - style = DebugWindow.Styles.labelWithZeroValueStyle; + style = LegacyStyles.labelWithZeroValueStyle; EditorGUI.LabelField(columnRect, field.values[i].FormatString(value), style); } @@ -65,6 +67,7 @@ protected override void DoGUI(Rect rect, GUIContent label, DebugUI.ValueTuple fi /// /// Builtin Drawer for ProgressBarValue Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.ProgressBarValue))] public sealed class DebugUIDrawerProgressBarValue : DebugUIWidgetDrawer { @@ -85,6 +88,7 @@ protected override void DoGUI(Rect rect, GUIContent label, DebugUI.ProgressBarVa /// /// Builtin Drawer for Button Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Button))] public sealed class DebugUIDrawerButton : DebugUIWidgetDrawer { @@ -108,6 +112,7 @@ protected override void DoGUI(Rect rect, GUIContent label, DebugUI.Button field) /// /// Builtin Drawer for Boolean Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.BoolField))] public sealed class DebugUIDrawerBoolField : DebugUIFieldDrawer { @@ -128,6 +133,7 @@ protected override bool DoGUI(Rect rect, GUIContent label, DebugUI.BoolField fie /// /// Builtin Drawer for History Boolean Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.HistoryBoolField))] public sealed class DebugUIDrawerHistoryBoolField : DebugUIFieldDrawer { @@ -168,6 +174,7 @@ protected override bool DoGUI(Rect rect, GUIContent label, DebugUI.HistoryBoolFi /// /// Builtin Drawer for Integer Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.IntField))] public sealed class DebugUIDrawerIntField : DebugUIFieldDrawer { @@ -190,6 +197,7 @@ protected override int DoGUI(Rect rect, GUIContent label, DebugUI.IntField field /// /// Builtin Drawer for Unsigned Integer Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.UIntField))] public sealed class DebugUIDrawerUIntField : DebugUIFieldDrawer { @@ -215,6 +223,7 @@ protected override uint DoGUI(Rect rect, GUIContent label, DebugUI.UIntField fie /// /// Builtin Drawer for Float Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.FloatField))] public sealed class DebugUIDrawerFloatField : DebugUIFieldDrawer { @@ -237,6 +246,7 @@ protected override float DoGUI(Rect rect, GUIContent label, DebugUI.FloatField f /// /// Builtin Drawer for Enum Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.EnumField))] public sealed class DebugUIDrawerEnumField : DebugUIFieldDrawer { @@ -274,6 +284,7 @@ protected override int DoGUI(Rect rect, GUIContent label, DebugUI.EnumField fiel /// /// Builtin Drawer for Object Popup Fields Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.ObjectPopupField))] public sealed class DebugUIDrawerObjectPopupField : DebugUIFieldDrawer { @@ -339,6 +350,7 @@ protected override UnityEngine.Object DoGUI(Rect rect, GUIContent label, DebugUI /// /// Builtin Drawer for History Enum Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.HistoryEnumField))] public sealed class DebugUIDrawerHistoryEnumField : DebugUIFieldDrawer { @@ -398,6 +410,7 @@ protected override int DoGUI(Rect rect, GUIContent label, DebugUI.HistoryEnumFie /// /// Builtin Drawer for Bitfield Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.BitField))] public sealed class DebugUIDrawerBitField : DebugUIFieldDrawer { @@ -427,6 +440,7 @@ protected override Enum DoGUI(Rect rect, GUIContent label, DebugUI.BitField fiel /// /// Builtin Drawer for Maskfield Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.RenderingLayerField))] public sealed class DebugUIDrawerRenderingLayerField : DebugUIFieldDrawer { @@ -449,6 +463,7 @@ protected override RenderingLayerMask DoGUI(Rect rect, GUIContent label, DebugUI /// /// Builtin Drawer for Foldout Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Foldout))] public sealed class DebugUIDrawerFoldout : DebugUIDrawer { @@ -456,7 +471,7 @@ public sealed class DebugUIDrawerFoldout : DebugUIDrawer static void DisplayColumns(Rect drawRect, List rowContents) { drawRect.x += EditorGUIUtility.labelWidth; - drawRect.width = DebugWindow.Styles.foldoutColumnWidth; + drawRect.width = LegacyStyles.foldoutColumnWidth; int indent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; //be at left of rects @@ -465,7 +480,7 @@ static void DisplayColumns(Rect drawRect, List rowContents) EditorGUI.LabelField(drawRect, rowContents[i], EditorStyles.miniBoldLabel); // Offset the rect to the next possible column - drawRect.x += DebugWindow.Styles.foldoutColumnWidth; + drawRect.x += LegacyStyles.foldoutColumnWidth; } EditorGUI.indentLevel = indent; } @@ -478,7 +493,7 @@ static void DisplayColumns(Rect drawRect, List rowContents) public override void Begin(DebugUI.Widget widget, DebugState state) { CoreEditorUtils.DrawSplitter(); - + var w = Cast(widget); var s = Cast(state); @@ -540,6 +555,7 @@ public override void End(DebugUI.Widget widget, DebugState state) /// /// Builtin Drawer for Color Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.ColorField))] public sealed class DebugUIDrawerColorField : DebugUIFieldDrawer { @@ -560,6 +576,7 @@ protected override Color DoGUI(Rect rect, GUIContent label, DebugUI.ColorField f /// /// Builtin Drawer for Vector2 Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Vector2Field))] public sealed class DebugUIDrawerVector2Field : DebugUIFieldDrawer { @@ -580,6 +597,7 @@ protected override Vector2 DoGUI(Rect rect, GUIContent label, DebugUI.Vector2Fie /// /// Builtin Drawer for Vector3 Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Vector3Field))] public sealed class DebugUIDrawerVector3Field : DebugUIFieldDrawer { @@ -600,6 +618,7 @@ protected override Vector3 DoGUI(Rect rect, GUIContent label, DebugUI.Vector3Fie /// /// Builtin Drawer for Vector4 Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Vector4Field))] public sealed class DebugUIDrawerVector4Field : DebugUIFieldDrawer { @@ -620,6 +639,7 @@ protected override Vector4 DoGUI(Rect rect, GUIContent label, DebugUI.Vector4Fie /// /// Builtin Drawer for items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.ObjectField))] public sealed class DebugUIDrawerObjectField : DebugUIFieldDrawer { @@ -640,6 +660,7 @@ protected override UnityEngine.Object DoGUI(Rect rect, GUIContent label, DebugUI /// /// Builtin Drawer for Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.ObjectListField))] public sealed class DebugUIDrawerObjectListField : DebugUIDrawer { @@ -654,7 +675,7 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state) var w = Cast(widget); var objects = w.GetValue(); - float height = Math.Max(objects != null ? objects.Length : 0, 1) * DebugWindow.Styles.singleRowHeight; + float height = Math.Max(objects != null ? objects.Length : 0, 1) * LegacyStyles.singleRowHeight; var rect = PrepareControlRect(height); rect = EditorGUI.PrefixLabel(rect, EditorGUIUtility.TrTextContent(widget.displayName)); @@ -679,7 +700,7 @@ internal static void DoObjectList(Rect rect, DebugUI.ObjectListField widget, Uni for (int i = 0; i < objects.Length; i++) { objects[i] = EditorGUI.ObjectField(rect, GUIContent.none, objects[i], widget.type, true); - rect.y += DebugWindow.Styles.singleRowHeight; + rect.y += LegacyStyles.singleRowHeight; } } } @@ -687,6 +708,7 @@ internal static void DoObjectList(Rect rect, DebugUI.ObjectListField widget, Uni /// /// Builtin Drawer for MessageBox Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.MessageBox))] public sealed class DebugUIDrawerMessageBox : DebugUIDrawer { @@ -717,6 +739,7 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state) /// /// Builtin Drawer for Container Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Container))] public sealed class DebugUIDrawerContainer : DebugUIDrawer { @@ -748,6 +771,7 @@ public override void End(DebugUI.Widget widget, DebugState state) /// /// Builtin Drawer for Horizontal Box Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.HBox))] public sealed class DebugUIDrawerHBox : DebugUIDrawer { @@ -774,6 +798,7 @@ public override void End(DebugUI.Widget widget, DebugState state) /// /// Builtin Drawer for Vertical Box Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.VBox))] public sealed class DebugUIDrawerVBox : DebugUIDrawer { @@ -801,6 +826,7 @@ public override void End(DebugUI.Widget widget, DebugState state) /// /// Builtin Drawer for Table Debug Items. /// + [Obsolete("This class is no longer used. #from(6000.5)")] [DebugUIDrawer(typeof(DebugUI.Table))] public sealed class DebugUIDrawerTable : DebugUIDrawer { @@ -855,7 +881,7 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state) bool isAlternate = r % 2 == 0; - EditorGUI.LabelField(rowRect, GUIContent.none, EditorGUIUtility.TrTextContent(row.displayName), isAlternate ? DebugWindow.Styles.centeredLeft : DebugWindow.Styles.centeredLeftAlternate); + EditorGUI.LabelField(rowRect, GUIContent.none, EditorGUIUtility.TrTextContent(row.displayName), isAlternate ? LegacyStyles.centeredLeft : LegacyStyles.centeredLeftAlternate); rowRect.xMin -= 2; rowRect.xMax += 2; @@ -885,7 +911,7 @@ internal float GetRowHeight(DebugUI.Table.Row row, int[] visibleColumns) var child = row.children[visibleColumns[c] - 1] as DebugUI.ObjectListField; if (child == null || child.GetValue() == null) continue; - height = Mathf.Max(height, child.GetValue().Length * DebugWindow.Styles.singleRowHeight); + height = Mathf.Max(height, child.GetValue().Length * LegacyStyles.singleRowHeight); } return height; } @@ -923,7 +949,7 @@ internal void DisplayChild(Rect rect, DebugUI.Widget child, bool isAlternate) if (child.GetType() == typeof(DebugUI.Value)) { var widget = Cast(child); - EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(widget.GetValue().ToString()), isAlternate ? DebugWindow.Styles.centeredLeft : DebugWindow.Styles.centeredLeftAlternate); + EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(widget.GetValue().ToString()), isAlternate ? LegacyStyles.centeredLeft : LegacyStyles.centeredLeftAlternate); } else if (child.GetType() == typeof(DebugUI.ColorField)) { @@ -948,4 +974,28 @@ internal void DisplayChild(Rect rect, DebugUI.Widget child, bool isAlternate) } } } + + class LegacyStyles + { + public static GUIStyle labelWithZeroValueStyle { get; } = new GUIStyle(EditorStyles.label); + + public readonly GUIStyle sectionScrollView = "PreferencesSectionBox"; + public readonly GUIStyle sectionElement = new GUIStyle("PreferencesSection"); + + public static GUIStyle centeredLeft = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft }; + public static GUIStyle centeredLeftAlternate = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft }; + public static float singleRowHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + public static int foldoutColumnWidth = 70; + + public LegacyStyles() + { + sectionScrollView = new GUIStyle(sectionScrollView); + sectionScrollView.overflow.bottom += 1; + + sectionElement.alignment = TextAnchor.MiddleLeft; + + labelWithZeroValueStyle.normal.textColor = Color.gray; + } + } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.cs index 2840a1ef939..014866fe478 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIDrawer.cs @@ -10,6 +10,7 @@ namespace UnityEditor.Rendering /// /// Attribute specifying wich type of Debug Item should this drawer be used with. /// + [Obsolete("This class is no longer used. #from(6000.5)")] public class DebugUIDrawerAttribute : Attribute { internal readonly Type type; @@ -27,6 +28,7 @@ public DebugUIDrawerAttribute(Type type) /// /// Debug Item Drawer /// + [Obsolete("This class is no longer used. #from(6000.5)")] public class DebugUIDrawer { /// @@ -130,6 +132,7 @@ protected Rect PrepareControlRect(float height = -1, bool fullWidth = false) /// The internal value of the field /// The type of the field widget /// The state of the field + [Obsolete("This class is no longer used. #from(6000.5)")] public abstract class DebugUIFieldDrawer : DebugUIDrawer where TField : DebugUI.Field where TState : DebugState @@ -174,14 +177,6 @@ public override bool OnGUI(DebugUI.Widget widget, DebugState state) /// The current value from the UI protected abstract TValue DoGUI(Rect rect, GUIContent label, TField field, TState state); - struct WidgetChangedAction - { - public string query_path; - public TValue previous_value; - public TValue new_value; - } - - static List s_Analytic = new List(); /// /// Implement this to execute processing after UI rendering. /// @@ -194,16 +189,7 @@ public override void End(DebugUI.Widget widget, DebugState state) var w = Cast(widget); var s = Cast(state); - s_Analytic.Clear(); - s_Analytic.Add(new() - { - query_path = widget.queryPath, - previous_value = w.GetValue(), - new_value = value - }); - Apply(w, s, value); - GraphicsToolUsageAnalytic.ActionPerformed("Widget Value Changed", s_Analytic.ToNestedColumn()); } } } @@ -212,6 +198,7 @@ public override void End(DebugUI.Widget widget, DebugState state) /// Common class to help drawing widgets /// /// The widget + [Obsolete("This class is no longer used. #from(6000.5)")] public abstract class DebugUIWidgetDrawer : DebugUIDrawer where TWidget : DebugUI.Widget { diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIHandlerCanvasEditor.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIHandlerCanvasEditor.cs deleted file mode 100644 index 988bbba97b6..00000000000 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIHandlerCanvasEditor.cs +++ /dev/null @@ -1,87 +0,0 @@ -#if ENABLE_UGUI_PACKAGE && (UNITY_EDITOR || DEVELOPMENT_BUILD) -#define ENABLE_RENDERING_DEBUGGER_UI -#endif - -#if ENABLE_RENDERING_DEBUGGER_UI - -using System; -using System.Linq; -using UnityEditor; -using UnityEditor.Rendering; -using UnityEditorInternal; -using UnityEngine.Rendering; - -namespace UnityEngine.Rendering.UI -{ - [CustomEditor(typeof(DebugUIHandlerCanvas))] - sealed class DebugUIHandlerCanvasEditor : Editor - { - SerializedProperty m_PanelPrefab; - SerializedProperty m_Prefabs; - ReorderableList m_PrefabList; - - static string[] s_Types; // Assembly qualified names - static string[] s_DisplayTypes; // Pretty names - - static DebugUIHandlerCanvasEditor() - { - s_Types = CoreUtils.GetAllTypesDerivedFrom() - .Where(t => !t.IsAbstract) - .Select(t => t.AssemblyQualifiedName) - .ToArray(); - - s_DisplayTypes = new string[s_Types.Length]; - for (int i = 0; i < s_Types.Length; i++) - s_DisplayTypes[i] = Type.GetType(s_Types[i]).Name; - } - - void OnEnable() - { - var o = new PropertyFetcher(serializedObject); - m_PanelPrefab = o.Find(x => x.panelPrefab); - m_Prefabs = o.Find(x => x.prefabs); - - m_PrefabList = new ReorderableList(serializedObject, m_Prefabs, true, true, true, true) - { - drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Widget Prefabs"), - drawElementCallback = (rect, index, isActive, isFocused) => - { - var element = m_PrefabList.serializedProperty.GetArrayElementAtIndex(index); - rect.y += 2f; - const float kTypeWidth = 100f; - - // Type selector - var typeProp = element.FindPropertyRelative("type"); - int typeIndex = ArrayUtility.IndexOf(s_Types, typeProp.stringValue); - typeIndex = Mathf.Max(typeIndex, 0); - typeIndex = EditorGUI.Popup(new Rect(rect.x, rect.y, kTypeWidth, EditorGUIUtility.singleLineHeight), typeIndex, s_DisplayTypes); - typeProp.stringValue = s_Types[typeIndex]; - - // Prefab - EditorGUI.PropertyField( - new Rect(rect.x + kTypeWidth + 2f, rect.y, rect.width - kTypeWidth - 2f, EditorGUIUtility.singleLineHeight), - element.FindPropertyRelative("prefab"), GUIContent.none); - }, - onSelectCallback = list => - { - var prefab = list.serializedProperty.GetArrayElementAtIndex(list.index).FindPropertyRelative("prefab").objectReferenceValue as GameObject; - if (prefab) - EditorGUIUtility.PingObject(prefab); - } - }; - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - EditorGUILayout.PropertyField(m_PanelPrefab); - EditorGUILayout.Space(); - m_PrefabList.DoLayoutList(); - - serializedObject.ApplyModifiedProperties(); - } - } -} - -#endif diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIHandlerCanvasEditor.cs.meta b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIHandlerCanvasEditor.cs.meta deleted file mode 100644 index 510e472df28..00000000000 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugUIHandlerCanvasEditor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: f1d41f85c15ac6048a850b51ff36c098 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.Deprecated.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.Deprecated.cs new file mode 100644 index 00000000000..46caf670494 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.Deprecated.cs @@ -0,0 +1,236 @@ +#if ENABLE_UIELEMENTS_MODULE && (UNITY_EDITOR || DEVELOPMENT_BUILD) +#define ENABLE_RENDERING_DEBUGGER_UI +#endif + +#if ENABLE_RENDERING_DEBUGGER_UI + +using System; +using System.Collections.Generic; +using UnityEditor.Callbacks; +using UnityEngine; +using UnityEngine.Assertions; +using UnityEngine.Rendering; + +// This file encapsulates the deprecated widget state handling logic for the DebugWindow. It will be removed in a future release. + +#pragma warning disable CS0618 // Type or member is obsolete + +namespace UnityEditor.Rendering +{ + [Serializable] + sealed class WidgetStateDictionary : SerializedDictionary { } + + sealed partial class DebugWindow + { + [SerializeField] + WidgetStateDictionary m_WidgetStates; + + static bool s_TypeMapDirty; + static Dictionary s_WidgetStateMap; // DebugUI.Widget type -> DebugState type + + [DidReloadScripts] + static void OnEditorReload() + { + s_TypeMapDirty = true; + } + + void HookLegacyWidgetStateHandlingCallbacks() + { + AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload; + AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload; + + AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload; + AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload; + + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + } + + void OnBeforeAssemblyReload() + { + UpdateWidgetStates(updateCurrentStates: true); + } + + void OnAfterAssemblyReload() + { + ApplyStates(); + } + + void OnPlayModeStateChanged(PlayModeStateChange state) + { + if (state == PlayModeStateChange.ExitingEditMode || state == PlayModeStateChange.ExitingPlayMode) + UpdateWidgetStates(updateCurrentStates: true); + if (state == PlayModeStateChange.EnteredPlayMode || state == PlayModeStateChange.EnteredEditMode) + ApplyStates(); + } + + static void RebuildTypeMaps() + { + // Map states to widget (a single state can map to several widget types if the value to + // serialize is the same) + var attrType = typeof(DebugStateAttribute); + var stateTypes = new List(); + foreach (var type in CoreUtils.GetAllTypesDerivedFrom()) + { + if (type.IsDefined(attrType, false) && !type.IsAbstract) + { + stateTypes.Add(type); + } + } + + s_WidgetStateMap = new Dictionary(); + + foreach (var stateType in stateTypes) + { + var attr = (DebugStateAttribute)stateType.GetCustomAttributes(attrType, false)[0]; + + foreach (var t in attr.types) + s_WidgetStateMap.Add(t, stateType); + } + + // Done + s_TypeMapDirty = false; + } + + void DestroyWidgetStates() + { + if (m_WidgetStates == null) + return; + + // Clear all the states from memory + foreach (var state in m_WidgetStates) + { + var s = state.Value; + DestroyImmediate(s); + } + + m_WidgetStates.Clear(); + } + + void ReloadWidgetStates() + { + if (m_WidgetStates == null) + return; + + // Clear states from memory that don't have a corresponding widget + List keysToRemove = new (); + foreach (var state in m_WidgetStates) + { + var widget = DebugManager.instance.GetItem(state.Key); + if (widget == null) + { + var s = state.Value; + DestroyImmediate(s); + keysToRemove.Add(state.Key); + } + } + + // Cleanup null entries because they can break the dictionary serialization + foreach (var key in keysToRemove) + { + m_WidgetStates.Remove(key); + } + + UpdateWidgetStates(); + } + + bool AreWidgetStatesValid() + { + foreach (var state in m_WidgetStates) + { + if (state.Value == null) + { + return false; + } + } + return true; + } + + // We use item states to keep a cached value of each serializable debug items in order to + // handle domain reloads and play mode entering/exiting + // Note: no removal of orphan states + void UpdateWidgetStates(bool updateCurrentStates = false) + { + foreach (var panel in DebugManager.instance.panels) + UpdateWidgetStates(panel, updateCurrentStates); + } + + DebugState GetOrCreateDebugStateForValueField(DebugUI.Widget widget, bool updateCurrentStates) + { + // Skip runtime & readonly only items + if (widget.isInactiveInEditor) + return null; + + if (widget is not DebugUI.IValueField valueField) + return null; + + if (!widget.m_RequiresLegacyStateHandling) + return null; + + string queryPath = widget.queryPath; + if (!m_WidgetStates.TryGetValue(queryPath, out var state) || state == null) + { + var widgetType = widget.GetType(); + if (s_WidgetStateMap.TryGetValue(widgetType, out Type stateType)) + { + Assert.IsNotNull(stateType); + state = (DebugState)CreateInstance(stateType); + state.queryPath = queryPath; + state.SetValue(valueField.GetValue(), valueField); + m_WidgetStates[queryPath] = state; + } + } + + if (state != null && updateCurrentStates) + { + state.SetValue(valueField.GetValue(), valueField); + } + + return state; + } + + void UpdateWidgetStates(DebugUI.IContainer container, bool updateCurrentStates) + { + // Skip runtime only containers, we won't draw them so no need to serialize them either + if (container is DebugUI.Widget actualWidget && actualWidget.isInactiveInEditor) + return; + + // Recursively update widget states + foreach (var widget in container.children) + { + // Skip non-serializable widgets but still traverse them in case one of their + // children needs serialization support + var state = GetOrCreateDebugStateForValueField(widget, updateCurrentStates); + + if (state != null) + continue; + + // Recurse if the widget is a container + if (widget is DebugUI.IContainer containerField) + UpdateWidgetStates(containerField, updateCurrentStates); + } + } + + void ApplyStates() + { + // If we are in playmode, and the runtime UI is shown, avoid that the editor UI + // applies the data of the internal debug states, as they are not kept in sync + if (Application.isPlaying && DebugManager.instance.displayRuntimeUI) + return; + + foreach (var state in m_WidgetStates) + ApplyState(state.Key, state.Value); + } + + void ApplyState(string queryPath, DebugState state) + { + if (state == null || !(DebugManager.instance.GetItem(queryPath) is DebugUI.IValueField widget)) + return; + + widget.SetValue(state.GetValue()); + } + } +} + +#pragma warning restore CS0618 // Type or member is obsolete +#endif diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.Deprecated.cs.meta b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.Deprecated.cs.meta new file mode 100644 index 00000000000..49a8f086268 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.Deprecated.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 54fde5d97fa74a669522408aa1aa5685 +timeCreated: 1762166012 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs index b66521c4fc2..9d5fc8b904e 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.cs @@ -1,376 +1,187 @@ -#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE -#define USE_INPUT_SYSTEM +#if ENABLE_UIELEMENTS_MODULE && (UNITY_EDITOR || DEVELOPMENT_BUILD) +#define ENABLE_RENDERING_DEBUGGER_UI #endif using System; using System.Collections.Generic; -using System.Linq; -using UnityEditor.Callbacks; +using System.Reflection; using UnityEditor.Rendering.Analytics; -using UnityEditorInternal; using UnityEngine; -using UnityEngine.Assertions; using UnityEngine.Rendering; -using static UnityEngine.Rendering.DebugUI; +using UnityEditor.UIElements; +using UnityEngine.UIElements; namespace UnityEditor.Rendering { -#pragma warning disable 414 - - [Serializable] - sealed class WidgetStateDictionary : SerializedDictionary { } - - sealed class DebugWindowSettings : ScriptableObject + struct WidgetChangedAction { - // Keep these settings in a separate scriptable object so we can handle undo/redo on them - // without the rest of the debug window interfering - public int currentStateHash; - - public int selectedPanel - { - get => Mathf.Max(0, DebugManager.instance.PanelIndex(selectedPanelDisplayName)); - set - { - var displayName = DebugManager.instance.PanelDisplayName(value); - if (!string.IsNullOrEmpty(displayName)) - selectedPanelDisplayName = displayName; - } - } - - public string selectedPanelDisplayName; - - void OnEnable() - { - hideFlags = HideFlags.HideAndDontSave; - } + public string query_path; + public T previous_value; + public T new_value; } [CoreRPHelpURL("Rendering-Debugger")] - sealed class DebugWindow : EditorWindowWithHelpButton, IHasCustomMenu + sealed partial class DebugWindow : EditorWindowWithHelpButton +#if ENABLE_RENDERING_DEBUGGER_UI + , IHasCustomMenu +#endif { - static Styles s_Styles; - static GUIStyle s_SplitterLeft; + internal static GUIContent s_TitleContent = EditorGUIUtility.TrTextContent("Rendering Debugger"); - static float splitterPos = 150f; - const float minSideBarWidth = 100; - const float minContentWidth = 100; - bool dragging = false; - - [SerializeField] - WidgetStateDictionary m_WidgetStates; - - [SerializeField] - DebugWindowSettings m_Settings; - - bool m_IsDirty; - - Vector2 m_PanelScroll; - Vector2 m_ContentScroll; - - static bool s_TypeMapDirty; - static Dictionary s_WidgetStateMap; // DebugUI.Widget type -> DebugState type - static Dictionary s_WidgetDrawerMap; // DebugUI.Widget type -> DebugUIDrawer - - public static bool open + [MenuItem("Window/Analysis/Rendering Debugger", priority = 10005)] + static void Init() { - get => DebugManager.instance.displayEditorUI; - private set => DebugManager.instance.displayEditorUI = value; + var window = GetWindow(); + window.titleContent = s_TitleContent; + window.minSize = new Vector2(800f, 300f); } - [DidReloadScripts] - static void OnEditorReload() + [MenuItem("Window/Analysis/Rendering Debugger", validate = true)] + static bool ValidateMenuItem() { - s_TypeMapDirty = true; - - //find if it where open, relink static event end propagate the info - open = (Resources.FindObjectsOfTypeAll()?.Length ?? 0) > 0; + return RenderPipelineManager.currentPipeline != null; } - static void RebuildTypeMaps() + public void CreateGUI() { - // Map states to widget (a single state can map to several widget types if the value to - // serialize is the same) - var attrType = typeof(DebugStateAttribute); - var stateTypes = CoreUtils.GetAllTypesDerivedFrom() - .Where( - t => t.IsDefined(attrType, false) - && !t.IsAbstract - ); - - s_WidgetStateMap = new Dictionary(); - - foreach (var stateType in stateTypes) - { - var attr = (DebugStateAttribute)stateType.GetCustomAttributes(attrType, false)[0]; - - foreach (var t in attr.types) - s_WidgetStateMap.Add(t, stateType); - } +#if ENABLE_RENDERING_DEBUGGER_UI + RecreateGUI(); - // Drawers - attrType = typeof(DebugUIDrawerAttribute); - var types = CoreUtils.GetAllTypesDerivedFrom() - .Where( - t => t.IsDefined(attrType, false) - && !t.IsAbstract - ); + UpdateWidgetStates(); +#else + var helpBox = new HelpBox( + "UIElements Module is disabled. In order to use Rendering Debugger, enable the module in Package Manager > Built-in. ", + HelpBoxMessageType.Info); + helpBox.buttonText = "Open in Package Manager"; + helpBox.onButtonClicked += () => PackageManager.UI.Window.Open("com.unity.modules.uielements"); + rootVisualElement.Add(helpBox); +#endif + } - s_WidgetDrawerMap = new Dictionary(); +#if ENABLE_RENDERING_DEBUGGER_UI + [SerializeField] + string m_SelectedPanelName; - foreach (var t in types) + DebugUI.Panel m_SelectedPanel; + DebugUI.Panel selectedPanel + { + get => m_SelectedPanel; + set { - var attr = (DebugUIDrawerAttribute)t.GetCustomAttributes(attrType, false)[0]; - var inst = (DebugUIDrawer)Activator.CreateInstance(t); - s_WidgetDrawerMap.Add(attr.type, inst); + m_SelectedPanel = value; + m_SelectedPanelName = m_SelectedPanel?.displayName; } - - // Done - s_TypeMapDirty = false; } - [MenuItem("Window/Analysis/Rendering Debugger", priority = 10005)] - static void Init() - { - var window = GetWindow(); - window.titleContent = Styles.windowTitle; - window.minSize = new Vector2(800f, 300f); - } + VisualElement m_LeftPaneElement; + VisualElement m_RightPaneElement; - [MenuItem("Window/Analysis/Rendering Debugger", validate = true)] - static bool ValidateMenuItem() - { - return RenderPipelineManager.currentPipeline != null; - } + const string k_UssCommon = "Packages/com.unity.render-pipelines.core/Runtime/DEbugging/Runtime UI Resources/DebugWindowCommon.uss"; + const string k_Uss = "Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.uss"; + const string k_Uxml = "Packages/com.unity.render-pipelines.core/Editor/Debugging/DebugWindow.uxml"; + + bool m_IsDirty; + + Vector2 m_PanelScroll; + Vector2 m_ContentScroll; void OnEnable() { - open = true; + DebugManager.instance.displayEditorUI = true; DebugManager.instance.refreshEditorRequested = false; hideFlags = HideFlags.HideAndDontSave; autoRepaintOnSceneChange = true; - if (m_Settings == null) - m_Settings = CreateInstance(); - - // States are ScriptableObjects (necessary for Undo/Redo) but are not saved on disk so when the editor is closed then reopened, any existing debug window will have its states set to null - // Since we don't care about persistence in this case, we just re-init everything. if (m_WidgetStates == null || !AreWidgetStatesValid()) m_WidgetStates = new WidgetStateDictionary(); - - if (s_WidgetStateMap == null || s_WidgetDrawerMap == null || s_TypeMapDirty) + if (s_WidgetStateMap == null || s_TypeMapDirty) RebuildTypeMaps(); - Undo.undoRedoPerformed += OnUndoRedoPerformed; DebugManager.instance.onSetDirty += MarkDirty; - // First init - UpdateWidgetStates(); - - EditorApplication.update -= Repaint; - var panels = DebugManager.instance.panels; - var selectedPanelIndex = m_Settings.selectedPanel; - if (selectedPanelIndex >= 0 - && selectedPanelIndex < panels.Count - && panels[selectedPanelIndex].editorForceUpdate) - EditorApplication.update += Repaint; - GraphicsToolLifetimeAnalytic.WindowOpened(); - } - - // Note: this won't get called if the window is opened when the editor itself is closed - void OnDestroy() - { - open = false; - DebugManager.instance.onSetDirty -= MarkDirty; - Undo.ClearUndo(m_Settings); - - DestroyWidgetStates(); - } - private void OnDisable() - { - GraphicsToolLifetimeAnalytic.WindowClosed(); + HookLegacyWidgetStateHandlingCallbacks(); + HookValueChangedAnalytics(); } - public void DestroyWidgetStates() + // We use reflection to hook analytics to the onWidgetValueChangedAnalytic callback. The callback itself is required here because + // GraphicsToolUsageAnalytic is in the editor assembly but all widgets are in runtime. Reflection is used because we want to + // also hook analytics for any custom user widgets derived from public DebugUI.Field. + void HookValueChangedAnalytics() { - if (m_WidgetStates == null) - return; - - // Clear all the states from memory - foreach (var state in m_WidgetStates) + var allFieldTypes = TypeCache.GetTypesDerivedFrom(typeof(DebugUI.Field<>)); + foreach (var fieldType in allFieldTypes) { - var s = state.Value; - Undo.ClearUndo(s); // Don't leave dangling states in the global undo/redo stack - DestroyImmediate(s); - } - - m_WidgetStates.Clear(); - } - - public void ReloadWidgetStates() - { - if (m_WidgetStates == null) - return; - - // Clear states from memory that don't have a corresponding widget - List keysToRemove = new (); - foreach (var state in m_WidgetStates) - { - var widget = DebugManager.instance.GetItem(state.Key); - if (widget == null) + try { - var s = state.Value; - Undo.ClearUndo(s); // Don't leave dangling states in the global undo/redo stack - DestroyImmediate(s); - keysToRemove.Add(state.Key); + var genericArgs = fieldType.BaseType.GetGenericArguments(); + if (fieldType.IsAbstract || genericArgs.Length == 0) + continue; + + var field = fieldType.GetField(nameof(DebugUI.Field.onWidgetValueChangedAnalytic), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy); + var method = GetType().GetMethod(nameof(SendWidgetValueChangedAnalytic), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy); + var genericArg = fieldType.BaseType.GetGenericArguments()[0]; + var genericMethod = method.MakeGenericMethod(genericArg); + var delegateType = typeof(Action<,,>).MakeGenericType(typeof(string), genericArg, genericArg); + var callback = Delegate.CreateDelegate(delegateType, null, genericMethod); + field.SetValue(null, callback); } - } - - // Cleanup null entries because they can break the dictionary serialization - foreach (var key in keysToRemove) - { - m_WidgetStates.Remove(key); - } - - UpdateWidgetStates(); - } - - bool AreWidgetStatesValid() - { - foreach (var state in m_WidgetStates) - { - if (state.Value == null) + catch (Exception ex) { - return false; + Debug.LogWarning($"Failed to hook analytics for {fieldType}: {ex.Message}"); } } - return true; } - void MarkDirty() - { - m_IsDirty = true; - } - - // We use item states to keep a cached value of each serializable debug items in order to - // handle domain reloads, play mode entering/exiting and undo/redo - // Note: no removal of orphan states - void UpdateWidgetStates() - { - foreach (var panel in DebugManager.instance.panels) - UpdateWidgetStates(panel); - } - - internal DebugState GetOrCreateDebugStateForValueField(DebugUI.Widget widget) - { - // Skip runtime & readonly only items - if (widget.isInactiveInEditor) - return null; - - if (widget is not DebugUI.IValueField valueField) - return null; - - string queryPath = widget.queryPath; - if (!m_WidgetStates.TryGetValue(queryPath, out var state) || state == null) - { - var widgetType = widget.GetType(); - if (s_WidgetStateMap.TryGetValue(widgetType, out Type stateType)) - { - Assert.IsNotNull(stateType); - state = (DebugState)CreateInstance(stateType); - state.queryPath = queryPath; - state.SetValue(valueField.GetValue(), valueField); - m_WidgetStates[queryPath] = state; - } - } - - if (valueField is ISyncUIState sync && sync.syncState) - { - state.SetValue(valueField.GetValue(), valueField); - sync.syncState = false; - } - - return state; - } + // Store timestamps to throttle event sending + static readonly Dictionary s_SentAnalyticsTimestamps = new (); - void UpdateWidgetStates(DebugUI.IContainer container) + static void SendWidgetValueChangedAnalytic(string queryPath, T previousValue, T newValue) { - // Skip runtime only containers, we won't draw them so no need to serialize them either - if (container is DebugUI.Widget actualWidget && actualWidget.isInactiveInEditor) + if (queryPath == null) return; - // Recursively update widget states - foreach (var widget in container.children) - { - // Skip non-serializable widgets but still traverse them in case one of their - // children needs serialization support - var state = GetOrCreateDebugStateForValueField(widget); - - if (state != null) - continue; - - // Recurse if the widget is a container - if (widget is DebugUI.IContainer containerField) - UpdateWidgetStates(containerField); - } - } - - public void ApplyStates(bool forceApplyAll = false) - { - // If we are in playmode, and the runtime UI is shown, avoid that the editor UI - // applies the data of the internal debug states, as they are not kept in sync - if (Application.isPlaying && DebugManager.instance.displayRuntimeUI) + const float kMaxSendRateSeconds = 0.5f; + float now = (float)EditorApplication.timeSinceStartup; + if (s_SentAnalyticsTimestamps.TryGetValue(queryPath, out float lastSentAt) && now - lastSentAt < kMaxSendRateSeconds) return; - if (!forceApplyAll && DebugState.m_CurrentDirtyState != null) - { - ApplyState(DebugState.m_CurrentDirtyState.queryPath, DebugState.m_CurrentDirtyState); - } - else - { - foreach (var state in m_WidgetStates) - ApplyState(state.Key, state.Value); - } + s_SentAnalyticsTimestamps[queryPath] = now; - DebugState.m_CurrentDirtyState = null; + var analytic = new List> { new() + { + query_path = queryPath, + previous_value = previousValue, + new_value = newValue + } }; + GraphicsToolUsageAnalytic.ActionPerformed("Widget Value Changed", analytic.ToNestedColumn()); } - void ApplyState(string queryPath, DebugState state) + // Note: this won't get called if the window is opened when the editor itself is closed + void OnDestroy() { - if (state == null || !(DebugManager.instance.GetItem(queryPath) is DebugUI.IValueField widget)) - return; + // Note: In the case where the window is maximized/unmaximized, OnEnable for the new window gets called *before* OnDestroy. + // Therefore you need to be careful with statics/globals. In this case, we only mark displayEditorUI as false if we are + // closing the only/last DebugWindow instance. + if (Resources.FindObjectsOfTypeAll(typeof(DebugWindow)).Length == 0) + DebugManager.instance.displayEditorUI = false; - widget.SetValue(state.GetValue()); + DebugManager.instance.onSetDirty -= MarkDirty; + + DestroyWidgetStates(); } - void OnUndoRedoPerformed() + private void OnDisable() { - int stateHash = ComputeStateHash(); - - // Something has been undone / redone, re-apply states to the debug tree - if (stateHash != m_Settings.currentStateHash) - { - ApplyStates(true); - m_Settings.currentStateHash = stateHash; - } - - Repaint(); + GraphicsToolLifetimeAnalytic.WindowClosed(); } - int ComputeStateHash() + void MarkDirty() { - unchecked - { - int hash = 13; - - foreach (var state in m_WidgetStates) - hash = hash * 23 + state.Value.GetHashCode(); - - return hash; - } + m_IsDirty = true; } void Update() @@ -384,289 +195,130 @@ void Update() DebugManager.instance.refreshEditorRequested = false; } - int? requestedPanelIndex = DebugManager.instance.GetRequestedEditorWindowPanelIndex(); - if (requestedPanelIndex != null) + string requestedPanel = DebugManager.instance.GetRequestedEditorWindowPanel(); + if (requestedPanel != null) { - m_Settings.selectedPanel = requestedPanelIndex.Value; + SetSelectedPanel(requestedPanel); } if (m_IsDirty) { - UpdateWidgetStates(); - ApplyStates(); m_IsDirty = false; - - Repaint(); + RecreateGUI(); } } - void OnGUI() + private void RecreateGUI() { - if (s_Styles == null) - { - s_Styles = new Styles(); - s_SplitterLeft = new GUIStyle(); - } + rootVisualElement.Clear(); var panels = DebugManager.instance.panels; - int itemCount = panels.Count(x => !x.isInactiveInEditor && x.children.Count(w => !w.isInactiveInEditor) > 0); - - if (itemCount == 0) - { - EditorGUILayout.HelpBox("No debug item found.", MessageType.Info); - return; - } - GUILayout.BeginHorizontal(EditorStyles.toolbar); - GUILayout.FlexibleSpace(); - if (GUILayout.Button(Styles.resetButtonContent, EditorStyles.toolbarButton)) + // Adding all panels that are not inactive in editor and have at least one active child + var activePanels = new List(); + foreach (var panel in panels) { - DebugManager.instance.Reset(); - DestroyWidgetStates(); - UpdateWidgetStates(); - InternalEditorUtility.RepaintAllViews(); - } - - GUILayout.EndHorizontal(); - - using (new EditorGUILayout.HorizontalScope()) - { - // Side bar - using (var scrollScope = new EditorGUILayout.ScrollViewScope(m_PanelScroll, s_Styles.sectionScrollView, GUILayout.Width(splitterPos))) + if (!panel.isInactiveInEditor) { - if (m_Settings.selectedPanel >= panels.Count) - m_Settings.selectedPanel = 0; - - // Validate container id - while (panels[m_Settings.selectedPanel].isInactiveInEditor || panels[m_Settings.selectedPanel].children.Count(x => !x.isInactiveInEditor) == 0) + foreach (var child in panel.children) { - m_Settings.selectedPanel++; - - if (m_Settings.selectedPanel >= panels.Count) - m_Settings.selectedPanel = 0; - } - - // Root children are containers - for (int i = 0; i < panels.Count; i++) - { - var panel = panels[i]; - - if (panel.isInactiveInEditor) - continue; - - if (panel.children.Count(x => !x.isInactiveInEditor) == 0) - continue; - - var elementRect = GUILayoutUtility.GetRect(EditorGUIUtility.TrTextContent(panel.displayName), s_Styles.sectionElement, GUILayout.ExpandWidth(true)); - - if (m_Settings.selectedPanel == i && Event.current.type == EventType.Repaint) - s_Styles.selected.Draw(elementRect, false, false, false, false); - - EditorGUI.BeginChangeCheck(); - GUI.Toggle(elementRect, m_Settings.selectedPanel == i, panel.displayName, s_Styles.sectionElement); - if (EditorGUI.EndChangeCheck()) + if (!child.isInactiveInEditor) { - Undo.RegisterCompleteObjectUndo(m_Settings, $"Debug Panel '{panel.displayName}' Selection"); - var previousPanel = m_Settings.selectedPanel >= 0 && m_Settings.selectedPanel < panels.Count - ? panels[m_Settings.selectedPanel] - : null; - if (previousPanel != null && previousPanel.editorForceUpdate && !panel.editorForceUpdate) - EditorApplication.update -= Repaint; - else if ((previousPanel == null || !previousPanel.editorForceUpdate) && panel.editorForceUpdate) - EditorApplication.update += Repaint; - m_Settings.selectedPanel = i; + activePanels.Add(panel); + break; } } - - m_PanelScroll = scrollScope.scrollPosition; } + } - Rect splitterRect = new Rect(splitterPos - 3, 0, 6, Screen.height); - GUI.Box(splitterRect, "", s_SplitterLeft); - - const float topMargin = 2f; - GUILayout.Space(topMargin); - - // Main section - traverse current container - using (var changedScope = new EditorGUI.ChangeCheckScope()) - { - using (new EditorGUILayout.VerticalScope()) - { - var selectedPanel = panels[m_Settings.selectedPanel]; + if (activePanels.Count == 0) + { + rootVisualElement.Add(new HelpBox("No debug items registered. Make sure a Render Pipeline Asset is assigned in Quality Settings.", HelpBoxMessageType.Info)); + return; + } - using (new EditorGUILayout.HorizontalScope()) - { - var style = new GUIStyle(CoreEditorStyles.sectionHeaderStyle) { fontStyle = FontStyle.Bold }; - EditorGUILayout.LabelField(new GUIContent(selectedPanel.displayName), style); + var windowUxml = EditorGUIUtility.LoadRequired(k_Uxml) as VisualTreeAsset; + var commonUss = EditorGUIUtility.LoadRequired(k_UssCommon) as StyleSheet; + var windowUss = EditorGUIUtility.LoadRequired(k_Uss) as StyleSheet; - // Context menu - var rect = GUILayoutUtility.GetLastRect(); - var contextMenuRect = new Rect(rect.xMax, rect.y + 4f, 16f, 16f); + if (commonUss == null || windowUss == null || windowUxml == null) + throw new InvalidOperationException("Unable to find required UXML and USS files"); - CoreEditorUtils.ShowHelpButton(contextMenuRect, selectedPanel.documentationUrl, new GUIContent($"{selectedPanel.displayName} panel.")); - } + rootVisualElement.styleSheets.Add(commonUss); + rootVisualElement.styleSheets.Add(windowUss); + windowUxml.CloneTree(rootVisualElement); - const float leftMargin = 4f; - GUILayout.Space(leftMargin); + m_LeftPaneElement = rootVisualElement.Q(name: "tabs-insertion-element"); + m_RightPaneElement = rootVisualElement.Q(name: "panels-inspector-insertion-element"); - using (var scrollScope = new EditorGUILayout.ScrollViewScope(m_ContentScroll)) - { - const float scrollViewTopMargin = 4f; - GUILayout.Space(scrollViewTopMargin); - - TraverseContainerGUI(selectedPanel); - m_ContentScroll = scrollScope.scrollPosition; - - const float scrollViewBottomMargin = 10f; - GUILayout.Space(scrollViewBottomMargin); - } - } + if (m_LeftPaneElement == null || m_RightPaneElement == null) + throw new InvalidOperationException("Unable to find required insertion Visual Elements"); - if (changedScope.changed) - { - m_Settings.currentStateHash = ComputeStateHash(); - DebugManager.instance.ReDrawOnScreenDebug(); - } - } + m_LeftPaneElement.Clear(); + m_RightPaneElement.Clear(); - // Splitter events - if (Event.current != null) - { - switch (Event.current.rawType) - { - case EventType.MouseDown: - if (splitterRect.Contains(Event.current.mousePosition)) - { - dragging = true; - } - break; - case EventType.MouseDrag: - if (dragging) - { - splitterPos += Event.current.delta.x; - splitterPos = Mathf.Clamp(splitterPos, minSideBarWidth, position.width - minContentWidth); - Repaint(); - } - break; - case EventType.MouseUp: - if (dragging) - { - dragging = false; - } - break; - } - } - EditorGUIUtility.AddCursorRect(splitterRect, MouseCursor.ResizeHorizontal); - } - } + var resetButton = rootVisualElement.Q(name: "btn-reset"); + resetButton.clicked -= ResetClicked; + resetButton.clicked += ResetClicked; - void OnWidgetGUI(DebugUI.Widget widget) - { - if (widget.isInactiveInEditor || widget.isHidden) - return; + var uiPanels = DebugUIExtensions.CreatePanels(activePanels, DebugUI.Context.Editor); - if (widget.queryPath == null) + foreach (var (tab, panel) in uiPanels) { - Debug.LogError($"Widget {widget.GetType()} query path is null"); - return; + panel.style.display = DisplayStyle.None; + tab.RegisterCallback(_ => SetSelectedPanel(tab.text)); + m_LeftPaneElement.Add(tab); + m_RightPaneElement.Add(panel); } - if (!s_WidgetDrawerMap.TryGetValue(widget.GetType(), out DebugUIDrawer drawer)) + string selectedPanelName = m_SelectedPanelName; + if (string.IsNullOrEmpty(selectedPanelName) || m_LeftPaneElement.Q [DebugUIDrawer(typeof(DebugUI.MaskField))] - [Obsolete("DebugUI.MaskField has been deprecated and is not longer supported, please use BitField instead. #from(6000.2)")] + [Obsolete("DebugUI.MaskField has been deprecated and is not longer supported, please use BitField instead. #from(6000.2)", true)] public sealed class DebugUIDrawerMaskField : DebugUIFieldDrawer { /// diff --git a/Packages/com.unity.render-pipelines.core/Editor/HeaderFoldout.cs b/Packages/com.unity.render-pipelines.core/Editor/HeaderFoldout.cs index d140d8d685a..809c11ee3a1 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/HeaderFoldout.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/HeaderFoldout.cs @@ -99,7 +99,8 @@ public bool showEnableCheckbox set => m_Text.tooltip = value; } - Vector2 m_LastLocalMousePos; + bool m_IsHoveringLabel; + Rect m_TooltipRect; /// Constructor public HeaderFoldout() : base() @@ -111,7 +112,33 @@ public HeaderFoldout() : base() RegisterCallback(DelayedInit); var line = hierarchy[0][0]; //pass by herarchy to ignore content redirection - + line.RegisterCallback(e => + { + m_IsHoveringLabel = true; + + const float tipWidth = 200f; + const float tipHeight = 24f; + + m_TooltipRect = new Rect( + e.position.x, + e.position.y, + tipWidth, + tipHeight + ); + }); + line.RegisterCallback(e => + { + m_IsHoveringLabel = false; + }); + line.RegisterCallback(evt => + { + if (!m_IsHoveringLabel) + return; + + evt.rect = m_TooltipRect; + evt.StopPropagation(); + }); + m_IconElement = new Image() { style = @@ -133,21 +160,6 @@ public HeaderFoldout() : base() m_Text = new Label(); m_Text.AddToClassList(k_LabelClass); - m_Text.RegisterCallback(e => - { - m_LastLocalMousePos = e.position; // in root's local coords - }); - m_Text.RegisterCallback(evt => - { - // Offset the tooltip slightly from the cursor - const float tipWidth = 200f; // approximate width; the system will size it - const float tipHeight = 24f; // approximate height - - evt.rect = new Rect(m_LastLocalMousePos.x, m_LastLocalMousePos.y, tipWidth, tipHeight); - - // Optional: ensure only this handler sets the rect - evt.StopPropagation(); - }); line.Add(m_Text); m_HelpButton = new Button(Background.FromTexture2D(CoreEditorStyles.iconHelp), () => Help.BrowseURL(m_DocumentationURL)); diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/DynamicGI/DynamicGISkyOcclusion.urtshader b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/DynamicGI/DynamicGISkyOcclusion.urtshader index c149aa3d06b..d69846750b5 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/DynamicGI/DynamicGISkyOcclusion.urtshader +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/DynamicGI/DynamicGISkyOcclusion.urtshader @@ -11,7 +11,6 @@ UNIFIED_RT_DECLARE_ACCEL_STRUCT(_AccelStruct); - int _SampleCount; int _SampleId; int _MaxBounces; @@ -31,19 +30,21 @@ void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo) int probeId = dispatchInfo.globalThreadIndex; - QrngSobol rngState; + QrngSobol2D rngState; rngState.Init(uint2((uint)probeId, 0), _SampleId); - if (_SampleId==0) + float4 skyOcclusionEstimate = 0; + float3 skyShadingEstimate = 0; + if (_SampleId != 0) { - _SkyOcclusionOut[probeId] = float4(0,0,0,0); + skyOcclusionEstimate = _SkyOcclusionOut[probeId]; if (_BakeSkyShadingDirection > 0) - _SkyShadingOut[probeId] = float3(0,0,0); + skyShadingEstimate = _SkyShadingOut[probeId]; } UnifiedRT::RayTracingAccelStruct accelStruct = UNIFIED_RT_GET_ACCEL_STRUCT(_AccelStruct); - float2 u = float2(rngState.GetFloat(0), rngState.GetFloat(1)); + float2 u = rngState.GetSample(0); float3 rayFirstDirection = MapSquareToSphere(u); float pathWeight = 4.0f * PI; // 1 / SphereSamplePDF @@ -69,7 +70,7 @@ void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo) } else { - u = float2(rngState.GetFloat(2*bounceIndex), rngState.GetFloat(2*bounceIndex+1)); + u = rngState.GetSample(bounceIndex); SampleDiffuseBrdf(u, normalWS, ray.direction); ray.direction = normalize(ray.direction); @@ -107,12 +108,12 @@ void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo) rayFirstDirection.y * norm * kSHBasis1, rayFirstDirection.z * norm * kSHBasis1); - _SkyOcclusionOut[probeId] += tempSH; + skyOcclusionEstimate += tempSH; + if(_BakeSkyShadingDirection > 0) - _SkyShadingOut[probeId] += ray.direction / _SampleCount; + skyShadingEstimate += ray.direction / _SampleCount; - // break the loop; - bounceIndex = _MaxBounces + 2; + break; } } @@ -120,34 +121,37 @@ void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo) if (_SampleId == _SampleCount - 1) { // Window L1 coefficients to make sure no value is negative when sampling SH, layout is DC, x, y, z - float4 SHData = _SkyOcclusionOut[probeId]; // find main direction for light float3 mainDir; - mainDir.x = SHData.y; - mainDir.y = SHData.z; - mainDir.z = SHData.w; + mainDir.x = skyOcclusionEstimate.y; + mainDir.y = skyOcclusionEstimate.z; + mainDir.z = skyOcclusionEstimate.w; mainDir = normalize(mainDir); // find the value in the opposite direction, which is the lowest value in the SH float4 temp2 = float4(kSHBasis0, kSHBasis1 * -mainDir.x, kSHBasis1 * -mainDir.y, kSHBasis1 * -mainDir.z); - float value = dot(temp2, SHData); + float value = dot(temp2, skyOcclusionEstimate); float windowL1 = 1.0f; if (value < 0.0f) { // find the L1 factor for this value to be null instead of negative - windowL1 = -(temp2.x * SHData.x) / dot(temp2.yzw, SHData.yzw); + windowL1 = -(temp2.x * skyOcclusionEstimate.x) / dot(temp2.yzw, skyOcclusionEstimate.yzw); windowL1 = saturate(windowL1); } - _SkyOcclusionOut[probeId].yzw *= windowL1; + skyOcclusionEstimate.yzw *= windowL1; float radianceToIrradianceFactor = 2.0f / 3.0f; // This is a hacky solution for mitigating the radianceToIrradianceFactor based on the previous windowing operation. // The 1.125f exponent comes from experimental testing. It's the value that works the best when trying to match a bake and deringing done with the lightmapper, but it has no theoretical explanation. // In the future, we should replace these custom windowing and deringing operations with the ones used in the lightmapper to implement a more academical solution. - _SkyOcclusionOut[probeId].yzw *= lerp(1.0f, radianceToIrradianceFactor, pow(windowL1, 1.125f)); + skyOcclusionEstimate.yzw *= lerp(1.0f, radianceToIrradianceFactor, pow(windowL1, 1.125f)); } + + _SkyOcclusionOut[probeId] = skyOcclusionEstimate; + if(_BakeSkyShadingDirection > 0) + _SkyShadingOut[probeId] = skyShadingEstimate; } #ifdef UNIFIED_RT_BACKEND_COMPUTE diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs index 44516cd7bce..2dd1f2683fb 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs @@ -5,12 +5,6 @@ using Unity.Collections; using UnityEditor; using UnityEngine.LightTransport; -using UnityEngine.LightTransport.PostProcessing; -using UnityEditor.PathTracing.LightBakerBridge; -using UnityEngine.PathTracing.Core; -using UnityEngine.PathTracing.Integration; -using UnityEngine.PathTracing.PostProcessing; -using UnityEditor.LightBaking; using UnityEngine.Rendering.Sampling; using UnityEngine.Rendering.UnifiedRayTracing; using UnityEngine.SceneManagement; @@ -153,41 +147,50 @@ public override bool Step() // At this point, the baked data exists on disk. Either the regular LightBaker process wrote it, // or our local BakePipeline wrote it, in case of APV-only bake. + for (int requestIdx = 0; requestIdx < s_BakeData.jobs.Length; requestIdx++) { - using NativeArray shBytes = new(System.IO.File.ReadAllBytes(System.IO.Path.Combine(APVLightBakerPostProcessingOutputFolder, "irradiance.shl2")), Allocator.TempJob); - using NativeArray shData = shBytes.GetSubArray(sizeof(ulong), shBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); - irradiance.CopyFrom(shData); - } - { - using NativeArray validityBytes = new(System.IO.File.ReadAllBytes(System.IO.Path.Combine(APVLightBakerOutputFolder, "validity0.float")), Allocator.TempJob); - using NativeArray validityData = validityBytes.GetSubArray(sizeof(ulong), validityBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); - validity.CopyFrom(validityData); - } - if (occlusionResults.IsCreated) - { - // Read LightProbeOcclusion structs from disk - using NativeArray occlusionBytes = new(System.IO.File.ReadAllBytes(System.IO.Path.Combine(APVLightBakerPostProcessingOutputFolder, "occlusion.occ")), Allocator.TempJob); - using NativeArray occlusionData = occlusionBytes.GetSubArray(sizeof(ulong), occlusionBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); + BakeJob job = s_BakeData.jobs[requestIdx]; + string probeOutputSubFolder = $"/probeRequest{requestIdx}"; + string outputFolder = APVLightBakerOutputFolder + probeOutputSubFolder; + string postProcessOutputFolder = APVLightBakerPostProcessingOutputFolder + probeOutputSubFolder; - // Create swizzled occlusion buffer which is indexed by shadowmask channel. This the format expected by shader code. - NativeArray swizzledOcclusion = new NativeArray(occlusionData.Length, Allocator.TempJob); - for (int probeIdx = 0; probeIdx < occlusionData.Length; probeIdx++) { - LightProbeOcclusion occlusion = occlusionData[probeIdx]; - Vector4 swizzled = Vector4.zero; - for (int lightIdx = 0; lightIdx < 4; lightIdx++) + using NativeArray shBytes = new(File.ReadAllBytes(Path.Combine(postProcessOutputFolder, "irradiance.shl2")), Allocator.TempJob); + using NativeArray shData = shBytes.GetSubArray(sizeof(ulong), shBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); + irradiance.GetSubArray(job.startOffset, job.probeCount).CopyFrom(shData); + } + { + using NativeArray validityBytes = new(File.ReadAllBytes(Path.Combine(outputFolder, $"validity{requestIdx}.float")), Allocator.TempJob); + using NativeArray validityData = validityBytes.GetSubArray(sizeof(ulong), validityBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); + validity.GetSubArray(job.startOffset, job.probeCount).CopyFrom(validityData); + } + + if (occlusionResults.IsCreated) + { + // Read LightProbeOcclusion structs from disk + using NativeArray occlusionBytes = new(File.ReadAllBytes(Path.Combine(postProcessOutputFolder, "occlusion.occ")), Allocator.TempJob); + using NativeArray occlusionData = occlusionBytes.GetSubArray(sizeof(ulong), occlusionBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); + + // Create swizzled occlusion buffer which is indexed by shadowmask channel. This the format expected by shader code. + NativeArray swizzledOcclusion = new NativeArray(occlusionData.Length, Allocator.TempJob); + for (int probeIdx = 0; probeIdx < occlusionData.Length; probeIdx++) { - if (occlusionData[probeIdx].GetOcclusionMaskChannel(lightIdx, out sbyte shadowmaskIdx) && shadowmaskIdx >= 0) + LightProbeOcclusion probeOcclusionData = occlusionData[probeIdx]; + Vector4 swizzled = Vector4.zero; + for (int lightIdx = 0; lightIdx < 4; lightIdx++) { - occlusion.GetOcclusion(lightIdx, out float occlusionFactor); - swizzled[shadowmaskIdx] = occlusionFactor; + if (probeOcclusionData.GetOcclusionMaskChannel(lightIdx, out sbyte shadowmaskIdx) && shadowmaskIdx >= 0) + { + probeOcclusionData.GetOcclusion(lightIdx, out float occlusionFactor); + swizzled[shadowmaskIdx] = occlusionFactor; + } } - } - swizzledOcclusion[probeIdx] = swizzled; + swizzledOcclusion[probeIdx] = swizzled; + } + occlusion.GetSubArray(job.startOffset, job.probeCount).CopyFrom(swizzledOcclusion); + swizzledOcclusion.Dispose(); } - occlusion.CopyFrom(swizzledOcclusion); - swizzledOcclusion.Dispose(); } isDone = true; @@ -218,8 +221,8 @@ private struct BakeJob : IDisposable public int directSampleCount; public int indirectSampleCount; + public int environmentSampleCount; public int validitySampleCount; - public int occlusionSampleCount; public int maxBounces; public int skyOcclusionBakingSamples; @@ -243,7 +246,7 @@ public void Create(ProbeVolumeBakingSet bakingSet, LightingSettings lightingSett #else int indirectSampleCount = Math.Max(lightingSettings.indirectSampleCount, lightingSettings.environmentSampleCount); #endif - Create(lightingSettings, ignoreEnvironement, lightingSettings.directSampleCount, indirectSampleCount, + Create(lightingSettings, ignoreEnvironement, lightingSettings.directSampleCount, indirectSampleCount, lightingSettings.environmentSampleCount, (int)lightingSettings.lightProbeSampleCountMultiplier, lightingSettings.maxBounces); } @@ -257,19 +260,19 @@ internal void Create(LightingSettings lightingSettings, bool ignoreEnvironement, skyOcclusionBakingSamples = touchup.skyOcclusionSampleCount; skyOcclusionBakingBounces = touchup.skyOcclusionMaxBounces; - Create(lightingSettings, ignoreEnvironement, touchup.directSampleCount, touchup.indirectSampleCount, touchup.sampleCountMultiplier, touchup.maxBounces); + Create(lightingSettings, ignoreEnvironement, touchup.directSampleCount, touchup.indirectSampleCount, touchup.indirectSampleCount,touchup.sampleCountMultiplier, touchup.maxBounces); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - void Create(LightingSettings lightingSettings, bool ignoreEnvironement, int directSampleCount, int indirectSampleCount, int sampleCountMultiplier, int maxBounces) + void Create(LightingSettings lightingSettings, bool ignoreEnvironement, int directSampleCount, int indirectSampleCount, int environmentSampleCount, int sampleCountMultiplier, int maxBounces) { // We could preallocate wrt touchup aabb volume, or total brick count for the global job progress = new BakeProgressState(); this.directSampleCount = directSampleCount * sampleCountMultiplier; this.indirectSampleCount = indirectSampleCount * sampleCountMultiplier; + this.environmentSampleCount = environmentSampleCount * sampleCountMultiplier; this.validitySampleCount = indirectSampleCount * sampleCountMultiplier; - this.occlusionSampleCount = directSampleCount * sampleCountMultiplier; this.maxBounces = maxBounces; this.indirectScale = lightingSettings.indirectScale; @@ -297,7 +300,7 @@ static void UpdateLightStatus() var sceneLights = new Dictionary>(); // Modify each baked light, take note of which scenes they belong to. - var allLights = Object.FindObjectsByType(FindObjectsSortMode.None); + var allLights = Object.FindObjectsByType(); foreach (var light in allLights) { if (light.lightmapBakeType != LightmapBakeType.Realtime) diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.Placement.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.Placement.cs index cc44edcc970..325d9f899f8 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.Placement.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.Placement.cs @@ -73,7 +73,9 @@ internal static List GetPerSceneDataList() internal static List GetProbeVolumeList() { + #pragma warning disable CS0618 // Type or member is obsolete var fullPvList = GameObject.FindObjectsByType(FindObjectsSortMode.InstanceID); +#pragma warning restore CS0618 // Type or member is obsolete List usedPVList; if (isBakingSceneSubset) diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.RenderingLayers.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.RenderingLayers.cs index 78fe73ff26c..1cef74c3290 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.RenderingLayers.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.RenderingLayers.cs @@ -127,14 +127,14 @@ static AccelStructAdapter BuildAccelerationStructure() Span perSubMeshOpaqueness = stackalloc bool[subMeshCount]; perSubMeshOpaqueness.Fill(true); - accelStruct.AddInstance(renderer.component.GetEntityId().GetRawData(), renderer.component, perSubMeshMask, matIndices, perSubMeshOpaqueness, 1); + accelStruct.AddInstance(EntityId.ToULong(renderer.component.GetEntityId()), renderer.component, perSubMeshMask, matIndices, perSubMeshOpaqueness, 1); } foreach (var terrain in contributors.terrains) { uint mask = GetInstanceMask(terrain.component.shadowCastingMode); uint materialID = terrain.component.renderingLayerMask; // repurpose the material id as we don't need it here - accelStruct.AddInstance(terrain.component.GetEntityId().GetRawData(), terrain.component, new uint[1] { mask }, new uint[1] { materialID }, new bool[1] { true }, 1); + accelStruct.AddInstance(EntityId.ToULong(terrain.component.GetEntityId()), terrain.component, new uint[1] { mask }, new uint[1] { materialID }, new bool[1] { true }, 1); } return accelStruct; diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.SkyOcclusion.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.SkyOcclusion.cs index a1a3439789e..7e58424b088 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.SkyOcclusion.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.SkyOcclusion.cs @@ -223,13 +223,13 @@ static AccelStructAdapter BuildAccelerationStructure() Span perSubMeshOpaqueness = stackalloc bool[subMeshCount]; perSubMeshOpaqueness.Fill(true); - accelStruct.AddInstance(renderer.component.GetEntityId().GetRawData(), renderer.component, perSubMeshMask, matIndices, perSubMeshOpaqueness, 1); + accelStruct.AddInstance(EntityId.ToULong(renderer.component.GetEntityId()), renderer.component, perSubMeshMask, matIndices, perSubMeshOpaqueness, 1); } foreach (var terrain in contributors.terrains) { uint mask = GetInstanceMask(terrain.component.shadowCastingMode); - accelStruct.AddInstance(terrain.component.GetEntityId().GetRawData(), terrain.component, new uint[1] { mask }, new uint[1] { 0 }, new bool[1] { true }, 1); + accelStruct.AddInstance(EntityId.ToULong(terrain.component.GetEntityId()), terrain.component, new uint[1] { mask }, new uint[1] { 0 }, new bool[1] { true }, 1); } return accelStruct; diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs index afeb8c5c959..7e12d43d7df 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs @@ -151,7 +151,7 @@ static AccelStructAdapter BuildAccelerationStructure(int mask) Span perSubMeshOpaqueness = stackalloc bool[subMeshCount]; perSubMeshOpaqueness.Fill(true); - accelStruct.AddInstance(renderer.component.GetEntityId().GetRawData(), renderer.component, maskAndMatDummy, maskAndMatDummy, perSubMeshOpaqueness, 1); + accelStruct.AddInstance(EntityId.ToULong(renderer.component.GetEntityId()), renderer.component, maskAndMatDummy, maskAndMatDummy, perSubMeshOpaqueness, 1); } foreach (var terrain in contributors.terrains) @@ -160,7 +160,7 @@ static AccelStructAdapter BuildAccelerationStructure(int mask) if ((layerMask & mask) == 0) continue; - accelStruct.AddInstance(terrain.component.GetEntityId().GetRawData(), terrain.component, new uint[1] { 0xFFFFFFFF }, new uint[1] { 0xFFFFFFFF }, new bool[1] { true }, 1); + accelStruct.AddInstance(EntityId.ToULong(terrain.component.GetEntityId()), terrain.component, new uint[1] { 0xFFFFFFFF }, new uint[1] { 0xFFFFFFFF }, new bool[1] { true }, 1); } return accelStruct; @@ -482,7 +482,7 @@ static uint[] GetMaterialIndices(Renderer renderer) for (int i = 0; i < matIndices.Length; ++i) { if (i < renderer.sharedMaterials.Length && renderer.sharedMaterials[i] != null) - matIndices[i] = (uint)renderer.sharedMaterials[i].GetEntityId().GetRawData(); + matIndices[i] = (uint)EntityId.ToULong(renderer.sharedMaterials[i].GetEntityId()); else matIndices[i] = 0; } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs index 10d597db8fd..3467867e883 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs @@ -883,7 +883,9 @@ static internal void Clear() if (activeSet != null) activeSet.Clear(); + #pragma warning disable CS0618 // Type or member is obsolete var probeVolumes = GameObject.FindObjectsByType(FindObjectsSortMode.InstanceID); +#pragma warning restore CS0618 // Type or member is obsolete foreach (var probeVolume in probeVolumes) probeVolume.OnLightingDataAssetCleared(); } @@ -1016,7 +1018,9 @@ static void CellCountInDirections(out Vector3Int minCellPositionXYZ, out Vector3 static TouchupVolumeWithBoundsList GetAdjustementVolumes() { // This is slow, but we should have very little amount of touchup volumes. +#pragma warning disable CS0618 // Type or member is obsolete var touchupVolumes = Object.FindObjectsByType(FindObjectsSortMode.InstanceID); +#pragma warning restore CS0618 // Type or member is obsolete var touchupVolumesAndBounds = new TouchupVolumeWithBoundsList(touchupVolumes.Length); foreach (var touchup in touchupVolumes) @@ -1067,9 +1071,9 @@ private static void OnInputExtraction(InputExtraction.BakeInput bakeInput) if (s_BakeData.sortedPositions.Length == 0) return; - var lsa = ProbeVolumeLightingTab.GetLightingSettings(); + var lightingSettings = ProbeVolumeLightingTab.GetLightingSettings(); ProbeBakeRequestOutput outputTypes = ProbeBakeRequestOutput.All; - if (lsa.mixedBakeMode == MixedLightingMode.IndirectOnly) + if (lightingSettings.mixedBakeMode == MixedLightingMode.IndirectOnly) outputTypes &= ~ProbeBakeRequestOutput.Occlusion; var extraPos = s_BakeData.sortedPositions.ToArray(); @@ -1087,19 +1091,36 @@ private static void OnInputExtraction(InputExtraction.BakeInput bakeInput) bakeInput.SetProbePositions(newPositions); bakeInput.SetOcclusionLightIndices(newOcclusionIndices); - bakeInput.AddProbeRequest(new ProbeBakeRequest - { - outputTypes = outputTypes, - positionOffset = (ulong)prevProbeCount, - positionLength = (ulong)extraPos.Length, - bakeOutputFolderPath = APVLightBakerOutputFolder, - postProcessOutputFolderPath = APVLightBakerPostProcessingOutputFolder, - ignoreDirectEnvironment = m_BakingSet != null ? m_BakingSet.bakedSkyOcclusion : false, - ignoreIndirectEnvironment = m_BakingSet != null ? m_BakingSet.bakedSkyOcclusion : false, - pushoff = 0.0001f, - indirectScale = lsa.indirectScale, - dering = true, - }); + + var ignoreEnvironmentLight = m_BakingSet != null && m_BakingSet.skyOcclusion; + + var lightmapParameters = LightmapParameters.GetLightmapParametersForLightingSettings(lightingSettings); + float pushoff = lightmapParameters != null ? lightmapParameters.pushoff : 0.0001f; + + int requestIdx = 0; + foreach (var bakeJob in s_BakeData.jobs) + { + string probeOutputSubFolder = $"/probeRequest{requestIdx}"; + bakeInput.AddProbeRequest(new ProbeBakeRequest + { + outputTypes = outputTypes, + directSampleCount = (uint)bakeJob.directSampleCount, + indirectSampleCount = (uint)bakeJob.indirectSampleCount, + environmentSampleCount = (uint)bakeJob.environmentSampleCount, + maxBounces = (uint)bakeJob.maxBounces, + positionOffset = (ulong)bakeJob.startOffset, + positionLength = (ulong)bakeJob.probeCount, + bakeOutputFolderPath = APVLightBakerOutputFolder + probeOutputSubFolder, + postProcessOutputFolderPath = APVLightBakerPostProcessingOutputFolder + probeOutputSubFolder, + ignoreDirectEnvironment = ignoreEnvironmentLight, + ignoreIndirectEnvironment = ignoreEnvironmentLight, + pushoff = pushoff, + indirectScale = bakeJob.indirectScale, + dering = true, + }); + + requestIdx++; + } s_BakeData.bakeInput = bakeInput; } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs index f54e06a7637..0ed852bcf0e 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeSubdivisionContext.cs @@ -28,7 +28,7 @@ static RealtimeProbeSubdivisionDebug() static void UpdateRealtimeSubdivisionDebug() { var debugDisplay = ProbeReferenceVolume.instance.probeVolumeDebug; - if (!debugDisplay.realtimeSubdivision) + if (debugDisplay == null || !debugDisplay.realtimeSubdivision) return; // Avoid killing the GPU when Unity is in background and runInBackground is disabled @@ -42,7 +42,9 @@ static void UpdateRealtimeSubdivisionDebug() if (Time.realtimeSinceStartupAsDouble - s_LastSubdivisionTime > debugDisplay.subdivisionDelayInSeconds) { + #pragma warning disable CS0618 // Type or member is obsolete var probeVolume = GameObject.FindFirstObjectByType(); +#pragma warning restore CS0618 // Type or member is obsolete if (probeVolume == null || !probeVolume.isActiveAndEnabled) return; diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs index fd87b3966a8..c98fda030da 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs @@ -1,3 +1,7 @@ +#if ENABLE_UIELEMENTS_MODULE && (UNITY_EDITOR || DEVELOPMENT_BUILD) +#define ENABLE_RENDERING_DEBUGGER_UI +#endif + using System; using System.Linq; using System.Reflection; @@ -288,12 +292,12 @@ public override void OnHeaderSettingsGUI() internal static void OpenProbeVolumeDebugPanel(object userData, string[] options, int selected) { - var debugPanel = EditorWindow.GetWindow(); - debugPanel.titleContent = DebugWindow.Styles.windowTitle; - debugPanel.Show(); - var index = DebugManager.instance.FindPanelIndex(ProbeReferenceVolume.k_DebugPanelName); - if (index != -1) - DebugManager.instance.RequestEditorWindowPanelIndex(index); +#if ENABLE_RENDERING_DEBUGGER_UI + var debugWindow = EditorWindow.GetWindow(); + debugWindow.titleContent = DebugWindow.s_TitleContent; + debugWindow.Show(); + DebugManager.instance.RequestEditorWindowPanel(ProbeReferenceVolume.k_DebugPanelName); +#endif } // Need to have this only clear probes when we properly split lightmap and probe baking. @@ -1218,7 +1222,9 @@ internal class ProbeVolumeOverlay : Overlay, ITransientOverlay { if (ProbeReferenceVolume.instance.probeVolumeDebug.realtimeSubdivision) { + #pragma warning disable CS0618 // Type or member is obsolete var probeVolume = GameObject.FindFirstObjectByType(); +#pragma warning restore CS0618 // Type or member is obsolete if (probeVolume != null && probeVolume.isActiveAndEnabled) { var profile = ProbeVolumeBakingSet.GetBakingSetForScene(probeVolume.gameObject.scene); @@ -1236,6 +1242,9 @@ bool IsVisible() var debug = ProbeReferenceVolume.instance.probeVolumeDebug; var bakingSet = ProbeReferenceVolume.instance.currentBakingSet; + if (debug == null) + return false; + bool debugLayers = debug.drawProbes && debug.probeShading == DebugProbeShadingMode.RenderingLayerMasks && bakingSet != null; if (!debug.drawBricks && !debug.drawProbeSamplingDebug && !debugLayers) return false; diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/RenderingLayerMask/TraceRenderingLayerMask.urtshader b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/RenderingLayerMask/TraceRenderingLayerMask.urtshader index 98d6b31bb14..ed6daaaeee5 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/RenderingLayerMask/TraceRenderingLayerMask.urtshader +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/RenderingLayerMask/TraceRenderingLayerMask.urtshader @@ -26,7 +26,7 @@ void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo) ray.tMax = FLT_MAX; ray.tMin = 0.0f; - QrngSobol rngState; + QrngSobol2D rngState; rngState.Init(0, SAMPLE_COUNT); int4 hitCount = 0; @@ -35,7 +35,7 @@ void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo) for (uint i = 0; i < SAMPLE_COUNT; ++i) { - float2 u = float2(rngState.GetFloat(2*i), rngState.GetFloat(2*i+1)); + float2 u = rngState.GetSample(i); ray.direction = MapSquareToSphere(u); uint hitMask = 0; diff --git a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputSerialization.cs b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputSerialization.cs index 0327156571c..5a8301d8a9b 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputSerialization.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputSerialization.cs @@ -262,7 +262,7 @@ public void Transfer(IBakeInputVisitor visitor) visitor.TransferBlittable(ref mixedLightingMode); visitor.TransferBoolean(ref aoEnabled); visitor.TransferBlittable(ref aoDistance); - visitor.TransferBlittable(ref useHardwareRayTracing); + visitor.TransferBoolean(ref useHardwareRayTracing); } } @@ -615,6 +615,8 @@ internal struct ProbeRequest : IBakeInputVisitable public ProbeRequestOutputType outputTypeMask; public UInt64 positionOffset; public UInt64 count; + public SampleCount sampleCount; + public UInt32 maxBounces; public float pushoff; public string outputFolderPath; @@ -628,6 +630,8 @@ public void Transfer(IBakeInputVisitor visitor) visitor.TransferBlittable(ref outputTypeMask); visitor.TransferBlittable(ref positionOffset); visitor.TransferBlittable(ref count); + visitor.TransferBlittable(ref sampleCount); + visitor.TransferBlittable(ref maxBounces); visitor.TransferBlittable(ref pushoff); visitor.TransferString(ref outputFolderPath); visitor.TransferBlittable(ref integrationRadiusOffset); @@ -923,7 +927,7 @@ internal static class BakeInputSerialization { // Should match BakeInputSerialization::kCurrentFileVersion in BakeInputSerialization.h. // If these are out of sync, the implementation in this file probably needs to be updated. - const UInt64 CurrentFileVersion = 202509021; + const UInt64 CurrentFileVersion = 202601191; public static bool Deserialize(string path, out BakeInput bakeInput) { diff --git a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs index f0b242bb8ab..d5ba4eb2309 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs @@ -81,37 +81,6 @@ internal static void AddContributingInstancesToWorld(World world, in FatInstance } } - [InitializeOnLoad] - internal class SetLightmappingUnifiedBaker - { - static SetLightmappingUnifiedBaker() - { - try - { - var lightmappingType = typeof(UnityEditor.Lightmapping); - var unifiedBakerProperty = lightmappingType.GetProperty("UnifiedBaker", - System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); - - if (unifiedBakerProperty != null && unifiedBakerProperty.CanWrite) - { - #if UNIFIED_BAKER - unifiedBakerProperty.SetValue(null, true); - #else - unifiedBakerProperty.SetValue(null, false); - #endif - } - else - { - UnityEngine.Debug.LogWarning("Could not find or access UnifiedBaker property on Lightmapping class"); - } - } - catch (System.Exception ex) - { - UnityEngine.Debug.LogError($"Failed to set UnifiedBaker property via reflection: {ex.Message}"); - } - } - } - internal class LightBakerStrangler { internal enum Result @@ -398,7 +367,7 @@ internal static bool Bake(string bakeInputPath, string lightmapRequestsPath, str ulong lightmapWorkSteps = CalculateWorkStepsForLightmapRequests(in lightmapRequestData, lightmapDescriptors, lightmapBakeSettings); progressState.SetTotalWorkSteps(probeWorkSteps + lightmapWorkSteps); - if (!ExecuteProbeRequests(in bakeInput, in probeRequestData, deviceContext, useLegacyBakingBehavior, world, bakeInput.lightingSettings.maxBounces, progressState, samplingResources)) + if (!ExecuteProbeRequests(in bakeInput, in probeRequestData, deviceContext, useLegacyBakingBehavior, world, progressState, samplingResources)) return false; if (lightmapRequestData.requests.Length <= 0) @@ -419,11 +388,16 @@ internal static bool Bake(string bakeInputPath, string lightmapRequestsPath, str internal static ulong CalculateWorkStepsForProbeRequests(in BakeInput bakeInput, in ProbeRequestData probeRequestData) { - (uint directSampleCount, uint effectiveIndirectSampleCount) = GetProbeSampleCounts(bakeInput.lightingSettings.probeSampleCounts); ulong calculatedWorkSteps = 0; foreach (ProbeRequest probeRequest in probeRequestData.requests) - calculatedWorkSteps += CalculateProbeWorkSteps(probeRequest.count, probeRequest.outputTypeMask, directSampleCount, effectiveIndirectSampleCount, bakeInput.lightingSettings.mixedLightingMode != MixedLightingMode.IndirectOnly, - bakeInput.lightingSettings.maxBounces); + { + (uint directSampleCount, uint effectiveIndirectSampleCount) = GetProbeSampleCounts(probeRequest.sampleCount); + + calculatedWorkSteps += CalculateProbeWorkSteps(probeRequest.count, probeRequest.outputTypeMask, + directSampleCount, effectiveIndirectSampleCount, + bakeInput.lightingSettings.mixedLightingMode != MixedLightingMode.IndirectOnly, + probeRequest.maxBounces); + } return calculatedWorkSteps; } @@ -488,12 +462,10 @@ private static ulong CalculateIntegratedLightmapWorkSteps(uint samplesPerTexel, private static IntegrationSettings GetIntegrationSettings(in BakeInput bakeInput) { - IntegrationSettings retVal = IntegrationSettings.Default; - retVal.Backend = RayTracingBackend.Compute; - // TODO(pema.malling) - // retVal.Backend = - // bakeInput.lightingSettings.useHardwareRayTracing && RayTracingContext.IsBackendSupported(RayTracingBackend.Hardware) ? - // RayTracingBackend.Hardware : RayTracingBackend.Compute; + var retVal = IntegrationSettings.Default; + retVal.Backend = + bakeInput.lightingSettings.useHardwareRayTracing && RayTracingContext.IsBackendSupported(RayTracingBackend.Hardware) ? + RayTracingBackend.Hardware : RayTracingBackend.Compute; return retVal; } @@ -1443,7 +1415,7 @@ private static (uint directSampleCount, uint effectiveIndirectSampleCount) GetPr } internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeRequestData probeRequestData, UnityComputeDeviceContext deviceContext, - bool useLegacyBakingBehavior, UnityComputeWorld world, uint bounceCount, BakeProgressState progressState, + bool useLegacyBakingBehavior, UnityComputeWorld world, BakeProgressState progressState, UnityEngine.Rendering.Sampling.SamplingResources samplingResources) { if (probeRequestData.requests.Length == 0) @@ -1470,8 +1442,6 @@ internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeReques BufferSlice perProbeLightIndicesBufferSlice = perProbeLightIndicesBuffer.Slice(); deviceContext.WriteBuffer(perProbeLightIndicesBufferSlice, inputPerProbeLightIndices); - (uint directSampleCount, uint effectiveIndirectSampleCount) = GetProbeSampleCounts(bakeInput.lightingSettings.probeSampleCounts); - ProbeRequest[] probeRequests = probeRequestData.requests; for (int probeRequestIndex = 0; probeRequestIndex < probeRequests.Length; probeRequestIndex++) { @@ -1481,6 +1451,9 @@ internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeReques int requestLength = (int)request.count; ulong floatBufferSize = sizeof(float) * request.count; float pushoff = request.pushoff; + int bounceCount = (int)request.maxBounces; + (uint directSampleCount, uint effectiveIndirectSampleCount) = GetProbeSampleCounts(request.sampleCount); + probeIntegrator.Prepare(deviceContext, world, positionsBuffer.Slice(), pushoff, (int)bounceCount); List eventsToWaitFor = new(); @@ -1488,9 +1461,8 @@ internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeReques // Integrate indirect radiance using NativeArray outputIndirectRadiance = new(requestLength, Allocator.Persistent); - if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.RadianceIndirect)) + if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.RadianceIndirect) && effectiveIndirectSampleCount > 0) { - Debug.Assert(effectiveIndirectSampleCount > 0); var shIndirectBuffer = deviceContext.CreateBuffer(request.count * 27, sizeof(float)); buffersToDestroy.Add(shIndirectBuffer); var shIndirectBufferSlice = shIndirectBuffer.Slice(); @@ -1504,9 +1476,8 @@ internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeReques // Integrate direct radiance using NativeArray outputDirectRadiance = new(requestLength, Allocator.Persistent); - if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.RadianceDirect)) + if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.RadianceDirect) && directSampleCount > 0) { - Debug.Assert(directSampleCount > 0); var shDirectBuffer = deviceContext.CreateBuffer(request.count * 27, sizeof(float)); buffersToDestroy.Add(shDirectBuffer); var shDirectBufferSlice = shDirectBuffer.Slice(); @@ -1520,9 +1491,8 @@ internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeReques // Integrate validity using NativeArray outputValidity = new(requestLength, Allocator.Persistent); - if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.Validity)) + if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.Validity) && effectiveIndirectSampleCount > 0) { - Debug.Assert(effectiveIndirectSampleCount > 0); var validityBuffer = deviceContext.CreateBuffer(request.count, sizeof(float)); buffersToDestroy.Add(validityBuffer); var validityBufferSlice = validityBuffer.Slice(); @@ -1537,8 +1507,8 @@ internal static bool ExecuteProbeRequests(in BakeInput bakeInput, in ProbeReques // Integrate occlusion values const int maxOcclusionLightsPerProbe = 4; bool usesProbeOcclusion = bakeInput.lightingSettings.mixedLightingMode != MixedLightingMode.IndirectOnly; - using NativeArray outputOcclusion = new(requestLength * maxOcclusionLightsPerProbe, Allocator.Persistent, NativeArrayOptions.ClearMemory); - if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.LightProbeOcclusion) && usesProbeOcclusion) + using NativeArray outputOcclusion = new(requestLength * maxOcclusionLightsPerProbe, Allocator.Persistent); + if (request.outputTypeMask.HasFlag(ProbeRequestOutputType.LightProbeOcclusion) && usesProbeOcclusion && effectiveIndirectSampleCount > 0) { var occlusionBuffer = deviceContext.CreateBuffer(maxOcclusionLightsPerProbe * request.count, sizeof(float)); buffersToDestroy.Add(occlusionBuffer); diff --git a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphEditorLocalDebugSession.cs b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphEditorLocalDebugSession.cs index 79070230a4b..e5a142e2b36 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphEditorLocalDebugSession.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphEditorLocalDebugSession.cs @@ -8,6 +8,8 @@ internal sealed class RenderGraphEditorLocalDebugSession : RenderGraphDebugSessi public RenderGraphEditorLocalDebugSession() : base() { + connectionName = "Editor"; + RegisterAllLocallyKnownGraphsAndExecutions(); var analyticsPayload = new DebugMessageHandler.AnalyticsPayload(); diff --git a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.PlayerConnection.cs b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.PlayerConnection.cs index 800f7294110..1bbe0f5432a 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.PlayerConnection.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.PlayerConnection.cs @@ -8,19 +8,20 @@ public partial class RenderGraphViewer { class PlayerConnection : IDisposable { - IConnectionState m_ConnectionState; - - bool m_EditorQuitting; + public IConnectionState connectionState { get; private set; } readonly UnityEngine.Events.UnityAction m_OnPlayerConnected; readonly UnityEngine.Events.UnityAction m_OnPlayerDisconnected; - public PlayerConnection(IConnectionState connectionState, UnityEngine.Events.UnityAction onPlayerConnected, UnityEngine.Events.UnityAction onPlayerDisconnected) + public PlayerConnection(EditorWindow rgvWindow, UnityEngine.Events.UnityAction onPlayerConnected, UnityEngine.Events.UnityAction onPlayerDisconnected) { - m_ConnectionState = connectionState; + connectionState = PlayerConnectionGUIUtility.GetConnectionState(rgvWindow); m_OnPlayerConnected = onPlayerConnected; m_OnPlayerDisconnected = onPlayerDisconnected; + } + public void Connect() + { EditorConnection.instance.Initialize(); EditorConnection.instance.RegisterConnection(m_OnPlayerConnected); EditorConnection.instance.RegisterDisconnection(m_OnPlayerDisconnected); @@ -28,19 +29,19 @@ public PlayerConnection(IConnectionState connectionState, UnityEngine.Events.Uni public void Dispose() { - if (m_ConnectionState != null) + if (connectionState != null) { EditorConnection.instance.UnregisterConnection(m_OnPlayerConnected); EditorConnection.instance.UnregisterDisconnection(m_OnPlayerDisconnected); - m_ConnectionState.Dispose(); - m_ConnectionState = null; + connectionState.Dispose(); + connectionState = null; } } public void OnConnectionDropdownIMGUI() { - PlayerConnectionGUILayout.ConnectionTargetSelectionDropdown(m_ConnectionState, EditorStyles.toolbarDropDown, 250); + PlayerConnectionGUILayout.ConnectionTargetSelectionDropdown(connectionState, EditorStyles.toolbarDropDown, 250); } } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.cs b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.cs index 184a807165a..cf5a1c3d27d 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/RenderGraph/RenderGraphViewer.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using UnityEditor.Networking.PlayerConnection; using UnityEditor.Rendering.Analytics; -using UnityEditor.Toolbars; using UnityEditor.UIElements; using UnityEngine; +using UnityEngine.Networking.PlayerConnection; using UnityEngine.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Scripting.APIUpdating; @@ -125,9 +124,6 @@ internal static partial class Classes bool m_Paused = false; static EntityId s_EditorWindowEntityId; - DateTime m_LastDataCaptureTime = DateTime.MinValue; - string m_ConnectedDeviceName = "Local Editor"; - bool m_IsDeviceConnected = true; bool HasValidDebugData => m_CurrentDebugData != null && m_CurrentDebugData.valid; @@ -889,7 +885,11 @@ void OnAutoPlayStatusChanged(ChangeEvent evt) autoPlayToggle.text = evt.newValue ? L10n.Tr("Auto Update") : L10n.Tr("Pause"); m_Paused = evt.newValue; - if (!m_Paused && !m_IsDeviceConnected && m_ConnectedDeviceName != k_EditorName) + // When enabling Auto Update, if the current debug data is from a player that is no longer connected, + // switch back to Editor target. + if (!m_Paused && + RenderGraphDebugSession.currentDebugSession is RenderGraphEditorRemoteDebugSession && + m_PlayerConnection.connectionState.connectedToTarget != ConnectionTarget.Player) { ConnectDebugSession(); } @@ -1033,7 +1033,7 @@ void RebuildPassFilterUI() var passFilter = rootVisualElement.Q(Names.kPassFilterField); BuildEnumFlagsToggleDropdown(passFilter, m_PassFilter, kPassFilterEditorPrefsKey, val => m_PassFilter = val, true); - + passFilter.text = L10n.Tr("Pass Filter"); } @@ -2010,17 +2010,25 @@ void UpdateStatusLabel() if (!m_Paused) return; - string connectionStatus = m_IsDeviceConnected ? "Online" : "Offline"; - - bool isEditor = m_ConnectedDeviceName == k_EditorName; - string sourceLabel = isEditor ? "Source: Editor" : $"Source: {m_ConnectedDeviceName} ({connectionStatus})"; - - bool hasCapture = HasValidDebugData && m_LastDataCaptureTime != DateTime.MinValue; - string captureLabel = hasCapture ? $"Captured: {m_LastDataCaptureTime:HH:mm:ss}" : "No data captured"; - - string statusText = $"{sourceLabel} | {captureLabel}"; - - statusLabel.text = statusText; + if (!HasValidDebugData) + { + statusLabel.text = "No data captured"; + } + else + { + string sourceLabel; + if (RenderGraphDebugSession.currentDebugSession is RenderGraphEditorLocalDebugSession) + { + sourceLabel = "Editor"; + } + else + { + bool isConnected = m_PlayerConnection.connectionState.connectedToTarget == ConnectionTarget.Player; + string connectionStatus = isConnected ? "Online" : "Offline"; + sourceLabel = $"{m_CurrentDebugData.captureSourceString} ({connectionStatus})"; + } + statusLabel.text = $"Source: {sourceLabel} | Captured: {m_CurrentDebugData.captureTimestamp}"; + } } void UpdateCurrentDebugData(bool force = false) @@ -2031,10 +2039,6 @@ void UpdateCurrentDebugData(bool force = false) if (selectedExecutionItem != null) { m_CurrentDebugData = RenderGraphDebugSession.GetDebugData(m_SelectedRenderGraph, selectedExecutionItem.id); - - // Update timestamp when we get valid data, or when forcing an update - if (HasValidDebugData || force) - m_LastDataCaptureTime = DateTime.Now; } else { @@ -2047,8 +2051,6 @@ void UpdateCurrentDebugData(bool force = false) currentGraphDropdown.style.display = DisplayStyle.None; if (currentExecutionToolbarMenu != null) currentExecutionToolbarMenu.style.display = DisplayStyle.None; - - m_LastDataCaptureTime = DateTime.MinValue; } UpdateStatusLabel(); @@ -2090,31 +2092,15 @@ void CreateGUI() InitializeUI(); - if (m_PlayerConnection == null) - { - var connectionState = PlayerConnectionGUIUtility.GetConnectionState(this); - m_PlayerConnection = new PlayerConnection(connectionState, OnPlayerConnected, OnPlayerDisconnected); - - // Initialize device connection state right here while we have it - if (!string.IsNullOrEmpty(connectionState.connectionName)) - { - m_ConnectedDeviceName = connectionState.connectionName; - m_IsDeviceConnected = true; - } - else - { - m_ConnectedDeviceName = k_EditorName; - m_IsDeviceConnected = true; - } - - connectionState.Dispose(); // Dispose it immediately after use - } + m_PlayerConnection ??= new PlayerConnection(this, OnPlayerConnected, OnPlayerDisconnected); + m_PlayerConnection.Connect(); var connectionDropdown = rootVisualElement.Q(Names.kConnectionDropdown); connectionDropdown.onGUIHandler = m_PlayerConnection.OnConnectionDropdownIMGUI; if (RenderGraphDebugSession.currentDebugSession == null) ConnectDebugSession(); + UpdateStatusLabel(); } @@ -2137,22 +2123,12 @@ void OnDisable() void OnPlayerConnected(int playerID) { - // Get device name fresh when needed - using (var connectionState = PlayerConnectionGUIUtility.GetConnectionState(this)) - { - m_ConnectedDeviceName = connectionState?.connectionName ?? $"Remote Device {playerID}"; - } - m_IsDeviceConnected = true; - - ConnectDebugSession(); + RenderGraphDebugSession.currentDebugSession.connectionName = m_PlayerConnection.connectionState.connectionName; } void OnPlayerDisconnected(int playerID) { - m_IsDeviceConnected = false; - m_ConnectedDeviceName = k_EditorName; - if (!m_Paused) { var autoPlayToggle = rootVisualElement.Q(Names.kAutoPauseToggle); @@ -2175,23 +2151,11 @@ void OnPlayerDisconnected(int playerID) internal void ConnectDebugSession() where TSession : RenderGraphDebugSession, new() { - if (typeof(TSession) == typeof(RenderGraphEditorLocalDebugSession)) - { - if (m_ConnectedDeviceName == "Unknown" || m_ConnectedDeviceName == k_EditorName) - { - m_ConnectedDeviceName = k_EditorName; - m_IsDeviceConnected = true; - } - } - - //If we are paused, we need to force update the current debug data once to ensure that the UI is up to date when the - //connection changes + // If we are paused, we need to force update the current debug data once to ensure that the UI is up to date when the + // connection changes if (m_Paused) OnRegisteredGraphsChangedInternal(true); - if (RenderGraphDebugSession.currentDebugSession?.GetType() == typeof(TSession)) - return; - DisconnectDebugSession(); RenderGraphDebugSession.Create(); @@ -2210,9 +2174,7 @@ void DisconnectDebugSession() RenderGraphDebugSession.onRegisteredGraphsChanged -= OnRegisteredGraphsChanged; RenderGraphDebugSession.onDebugDataUpdated -= OnDebugDataUpdated; - m_IsDeviceConnected = false; UpdateStatusLabel(); - ClearGraphViewerUI(); } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/SampleDependencyImportSystem/SampleDependencyImporter.cs b/Packages/com.unity.render-pipelines.core/Editor/SampleDependencyImportSystem/SampleDependencyImporter.cs index fcc9fb36d9a..c52489bb120 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/SampleDependencyImportSystem/SampleDependencyImporter.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/SampleDependencyImportSystem/SampleDependencyImporter.cs @@ -80,7 +80,7 @@ VisualElement IPackageManagerExtension.CreateExtensionUI() const string importButtonClassName = "actionButton"; const string injectedButtonClassName = "importWithDependenciesButton"; - void RefreshSampleButtons() + internal void RefreshSampleButtons() { if (injectingElement == null || m_PackageInfo == null || m_SampleList == null || panelRoot == null) return; @@ -111,14 +111,38 @@ void RefreshSampleButtons() var sampleContainer = sampleContainers[i]; var injectedButton = sampleContainer.Q public bool refreshEditorRequested; - int? m_RequestedPanelIndex; + string m_RequestedPanel; #if ENABLE_RENDERING_DEBUGGER_UI - GameObject m_Root; - DebugUIHandlerCanvas m_RootUICanvas; + internal RuntimeDebugWindow m_RuntimeDebugWindow; + RuntimePersistentDebugUI m_RuntimePersistentDebugUI; - GameObject m_PersistentRoot; - DebugUIHandlerPersistentCanvas m_RootUIPersistentCanvas; + internal SchedulerTracker schedulerTracker { get; } = new(); #endif + internal DebugUI.Widget selectedWidget { get; set; } + /// /// Is any debug window or UI currently active. /// @@ -157,8 +173,7 @@ public bool isAnyDebugUIActive DebugManager() { #if DEVELOPMENT_BUILD || UNITY_EDITOR - RegisterInputs(); - RegisterActions(); + RegisterDebugInputs(); #endif } @@ -175,18 +190,20 @@ public void RefreshEditor() /// public void Reset() { + DebugDisplaySerializer.SaveFoldoutStates(); + DebugDisplaySerializer.Clear(); resetData?.Invoke(); - ReDrawOnScreenDebug(); } /// /// Request the runtime debug UI be redrawn on the next update. /// + [Obsolete("This method is obsolete. #from(6000.5)")] public void ReDrawOnScreenDebug() { #if ENABLE_RENDERING_DEBUGGER_UI if (displayRuntimeUI) - m_RootUICanvas?.RequestHierarchyReset(); + m_RuntimeDebugWindow?.RequestRecreateGUI(); #endif } @@ -202,78 +219,45 @@ public void ReDrawOnScreenDebug() /// Data to be registered. public void UnregisterData(IDebugData data) => resetData -= data.GetReset(); - /// - /// Get hashcode state of the Debug Window. - /// - /// The calculated hashcode for the current state of the Debug Window. - public int GetState() - { - int hash = 17; - - foreach (var panel in m_Panels) - hash = hash * 23 + panel.GetHashCode(); - - return hash; - } - -#if ENABLE_RENDERING_DEBUGGER_UI - internal void RegisterRootCanvas(DebugUIHandlerCanvas root) - { - Assert.IsNotNull(root); - m_Root = root.gameObject; - m_RootUICanvas = root; - } - - internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext) + int GetMaxPinnedIndex(DebugUI.Container container) { - m_RootUICanvas.ChangeSelection(widget, fromNext); - } - - internal void SetScrollTarget(DebugUIHandlerWidget widget) - { - if (m_RootUICanvas != null) - m_RootUICanvas.SetScrollTarget(widget); - } - - void EnsurePersistentCanvas() - { - if (m_RootUIPersistentCanvas == null) + int pinnedIndex = -1; + foreach (var child in container.children) { - var uiManager = UnityObject.FindFirstObjectByType(); - - if (uiManager == null) + if (child is DebugUI.ValueTuple valueTuple && valueTuple.pinnedElementIndex > pinnedIndex) { - m_PersistentRoot = UnityObject.Instantiate(Resources.Load("DebugUIPersistentCanvas")).gameObject; - m_PersistentRoot.name = "[Debug Canvas - Persistent]"; - m_PersistentRoot.transform.localPosition = Vector3.zero; + pinnedIndex = valueTuple.pinnedElementIndex; } - else - { - m_PersistentRoot = uiManager.gameObject; - } - - m_RootUIPersistentCanvas = m_PersistentRoot.GetComponent(); } + return pinnedIndex; } - internal void TogglePersistent(DebugUI.Widget widget, int? forceTupleIndex = null) + internal void TogglePersistent() { + if (selectedWidget != null) + TogglePersistent(selectedWidget); + } + + void TogglePersistent(DebugUI.Widget widget, int? forceTupleIndex = null) + { +#if ENABLE_RENDERING_DEBUGGER_UI if (widget == null) return; - EnsurePersistentCanvas(); + displayPersistentRuntimeUI = true; + switch (widget) { case DebugUI.Value value: - m_RootUIPersistentCanvas.Toggle(value); + m_RuntimePersistentDebugUI.Toggle(value); break; case DebugUI.ValueTuple valueTuple: - m_RootUIPersistentCanvas.Toggle(valueTuple, forceTupleIndex); + m_RuntimePersistentDebugUI.Toggle(valueTuple, forceTupleIndex); break; case DebugUI.Container container: // When container is toggled, we make sure that if there are ValueTuples, they all get the same element index. - int pinnedIndex = container.children.Max(w => (w as DebugUI.ValueTuple)?.pinnedElementIndex ?? -1); + int pinnedIndex = GetMaxPinnedIndex(container); foreach (var child in container.children) { if (child is DebugUI.Value || child is DebugUI.ValueTuple) @@ -284,8 +268,11 @@ internal void TogglePersistent(DebugUI.Widget widget, int? forceTupleIndex = nul Debug.Log("Only readonly items can be made persistent."); break; } - } + + if (m_RuntimePersistentDebugUI != null && m_RuntimePersistentDebugUI.IsEmpty()) + displayPersistentRuntimeUI = false; #endif + } void OnPanelDirty(DebugUI.Panel panel) { @@ -321,7 +308,7 @@ public string PanelDiplayName(int panelIndex) { return PanelDisplayName(panelIndex); } - + /// /// Returns the panel display name /// @@ -339,19 +326,35 @@ public string PanelDisplayName(int panelIndex) /// Request DebugWindow to open the specified panel. /// /// Index of the debug window panel to activate. + [Obsolete("Use RequestEditorWindowPanelName instead. #from(6000.5)")] public void RequestEditorWindowPanelIndex(int index) { - // Similar to RefreshEditor(), this function is required to bypass a dependency problem where DebugWindow - // cannot be accessed from the Core.Runtime assembly. Should there be a better way to allow editor-dependent - // features in DebugUI? - m_RequestedPanelIndex = index; + if (m_Panels[index] != null) + RequestEditorWindowPanel(m_Panels[index].displayName); + } + + /// + /// Request DebugWindow to open the specified panel. + /// + /// Name of window panel to activate. + public void RequestEditorWindowPanel(string panelName) + { + int panelIndex = FindPanelIndex(panelName); + if (panelIndex != -1) + { + m_RequestedPanel = panelName; + } + else + { + Debug.LogWarning($"No panel with name {panelName} has been registered."); + } } - internal int? GetRequestedEditorWindowPanelIndex() + internal string GetRequestedEditorWindowPanel() { - int? requestedIndex = m_RequestedPanelIndex; - m_RequestedPanelIndex = null; - return requestedIndex; + string requestedPanel = m_RequestedPanel; + m_RequestedPanel = null; + return requestedPanel; } // TODO: Optimally we should use a query path here instead of a display name diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugUI.Containers.cs b/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugUI.Containers.cs index db12b85c92a..94afe08d2fc 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugUI.Containers.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Debugging/DebugUI.Containers.cs @@ -1,7 +1,15 @@ +#if ENABLE_UIELEMENTS_MODULE && (UNITY_EDITOR || DEVELOPMENT_BUILD) +#define ENABLE_RENDERING_DEBUGGER_UI +#endif + using System; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; + +#endif +#if ENABLE_RENDERING_DEBUGGER_UI +using UnityEngine.UIElements; #endif namespace UnityEngine.Rendering @@ -13,6 +21,71 @@ public partial class DebugUI /// public class Container : Widget, IContainer { +#if ENABLE_RENDERING_DEBUGGER_UI + /// + protected override VisualElement Create() + { + VisualElement container = new UIElements.VisualElement(); + container.AddToClassList("debug-window-container"); + if (!hideDisplayName) + { + var label = new Label(displayName); + label.AddToClassList("debug-window-container-displayname"); + container.Add(label); + } + + var content = new VisualElement(); + content.AddToClassList("debug-window-container-content"); + AddChildren(content, m_Context); + container.Add(content); + + return container; + } + + /// + /// Adds all the children to the given container + /// + /// The container to insert elements + /// The context where the widget is being added + protected void AddChildren(VisualElement container, DebugUI.Context context) + { + for (int i = 0; i < children.Count; ++i) + { + var child = children[i]; + + if (context == Context.Editor && child.isRuntimeOnly) + continue; + if (context.IsAnyRuntimeContext() && child.isEditorOnly) + continue; + + var childUIElement = child.ToVisualElement(context); + if (childUIElement != null) + container.Add(childUIElement); + } + + // As the elements might change visibility, we need to update alternating background colors + if (alternateRowColors) + { + this.ScheduleTracked(container, () => container.schedule.Execute(() => + { + int i = 0; + foreach (var child in container.Children()) + { + if (child.style.display == DisplayStyle.Flex) + { + if (i % 2 != 0) + child.AddToClassList(k_AlternateRowColorClassName); + else + child.RemoveFromClassList(k_AlternateRowColorClassName); + + ++i; + } + } + }).Every(100)); + } + } +#endif + const string k_IDToken = "#"; internal bool hideDisplayName => string.IsNullOrEmpty(displayName) || displayName.StartsWith(k_IDToken); @@ -21,6 +94,13 @@ public class Container : Widget, IContainer /// public ObservableList children { get; private set; } + const string k_AlternateRowColorClassName = "debug-window-row-alternate"; + + /// + /// Set to true to alternate background colors of the rows inside the container. + /// + public bool alternateRowColors { get; set; } + /// /// Panel the container is attached to. /// @@ -29,10 +109,6 @@ public override Panel panel get { return m_Panel; } internal set { - /// Frequenlty used panels do now own widgets - if (value != null && value.flags.HasFlag(DebugUI.Flags.FrequentlyUsed)) - return; - m_Panel = value; // Bubble down @@ -118,30 +194,133 @@ protected virtual void OnItemRemoved(ObservableList sender, ListChangedE if (m_Panel != null) m_Panel.SetDirty(); } - - /// - /// Returns the hash code of the widget. - /// - /// Hash code of the widget. - public override int GetHashCode() - { - int hash = 17; - hash = hash * 23 + queryPath.GetHashCode(); - hash = hash * 23 + isHidden.GetHashCode(); - - int numChildren = children.Count; - for (int i = 0; i < numChildren; i++) - hash = hash * 23 + children[i].GetHashCode(); - - return hash; - } } /// /// Unity-like foldout that can be collapsed. /// - public class Foldout : Container, IValueField + public class Foldout : Container +#pragma warning disable CS0618 // Type or member is obsolete + , IValueField +#pragma warning restore CS0618 // Type or member is obsolete { + +#if ENABLE_RENDERING_DEBUGGER_UI + + /// + protected override VisualElement Create() + { + var container = new UIElements.Foldout { text = displayName }; + if (m_Context == Context.Editor && (contextMenuItems != null || !string.IsNullOrEmpty(documentationUrl))) + { +#if UNITY_EDITOR + // Get Toggle element of the Foldout to be able to attach the help and menu buttons + var toggleHeader = container.Q().ElementAt(0); + if (!string.IsNullOrEmpty(documentationUrl)) + { + var infoButton = new UIElements.Button((Background.FromTexture2D((Texture2D)EditorGUIUtility.TrIconContent("_Help").image )),() => Help.BrowseURL(documentationUrl)); + infoButton.AddToClassList("info-button"); + infoButton.tooltip = $"Open Reference for {displayName}."; + toggleHeader.Add(infoButton); + } + if (contextMenuItems != null) + { + var button = new UIElements.Button(); + button.AddToClassList("more-options-button"); + + var contextualMenu = new GenericDropdownMenu(); + foreach(var item in contextMenuItems) + { + contextualMenu.AddItem(item.displayName, false, item.action ); + } + + button.clicked += () => + { + contextualMenu.DropDown(button.worldBound, button, DropdownMenuSizeMode.Auto); + }; + + toggleHeader.Add(button); + } +#endif + } + + // Add special classes for styles + container.AddToClassList("debug-window-foldout"); + + // Update UI to match object's initial state + container.SetValueWithoutNotify(GetValue()); + + // Sync value to object when changed in UI + container.RegisterCallback>(evt => + { + SetValue(container.value); + }); + // Sync value to object based on navigation events separately, because they don't trigger a ChangeEvent + container.RegisterCallback(evt => + { + if (DebugManager.instance.selectedWidget == this) + { + if (evt.direction == NavigationMoveEvent.Direction.Right) + SetValue(true); + else if (evt.direction == NavigationMoveEvent.Direction.Left) + SetValue(false); + } + }, TrickleDown.TrickleDown); // Foldout internally stops propagation so must use TrickleDown to see it + + // Sync opened state from object to UI + this.ScheduleTracked(container, () => container.schedule.Execute(() => + { + container.SetValueWithoutNotify(GetValue()); + }).Every(100)); + + if (columnLabels is { Length: > 0 }) + { + var columnLabelsValues = new DebugUI.Value[columnLabels.Length]; + + for (int i = 0; i < columnLabels.Length; i++) + { + string value = columnLabels[i]; + object Getter() => value; + columnLabelsValues[i] = new DebugUI.Value() + { + displayName = string.Empty, + getter = Getter + }; + } + + var header = new DebugUI.ValueTuple() + { + displayName = string.Empty, + isHeader = true, + values = columnLabelsValues + }; + + container.Add(header.ToVisualElement(m_Context)); + + container.RegisterCallback(evt => + { + var numColumns = columnLabels.Length; + foreach (var visualElement in container.Children()) + { + var width = (evt.newRect.width - ValueTuple.GetLabelWidth(m_Context)) / numColumns; + int i = 0; + foreach (var child in visualElement.Children()) + { + if (i != 0) + child.style.width = width; + + ++i; + } + } + }); + } + + AddChildren(container, m_Context); + + return container; + } +#endif + /// /// Context menu item. /// @@ -163,10 +342,27 @@ public struct ContextMenuItem /// public bool isReadOnly { get { return false; } } + bool m_Opened; + /// /// Opened state of the foldout. /// - public bool opened { get; set; } + public bool opened + { + get => m_Opened; + set + { + if (opened != value) + { +#if ENABLE_RENDERING_DEBUGGER_UI + if (m_VisualElement != null) + DebugManager.instance.schedulerTracker.SetHierarchyEnabled(m_Context, this, value); + +#endif + m_Opened = value; + } + } + } /// /// Draw the foldout in full width using a header style. @@ -287,6 +483,7 @@ public Foldout(string displayName, ObservableList children, string[] col /// Set the opened state of the foldout. /// /// True to open the foldout, false to close it. + [Obsolete("This method is no longer used. #from(6000.5)")] public void SetValue(object value) => SetValue((bool)value); /// @@ -294,6 +491,7 @@ public Foldout(string displayName, ObservableList children, string[] col /// /// Input value. /// The validated value. + [Obsolete("This method is no longer used. #from(6000.5)")] public object ValidateValue(object value) => value; /// @@ -301,16 +499,6 @@ public Foldout(string displayName, ObservableList children, string[] col /// /// Input value. public void SetValue(bool value) => opened = value; - - void IValueField.SetValue(object value) - { - SetValue((bool)ValidateValue(value)); - } - - object IValueField.ValidateValue(object value) - { - return (bool)value; - } } /// @@ -346,16 +534,120 @@ public VBox() /// public class Table : Container { - static GUIStyle columnHeaderStyle = new GUIStyle() +#if ENABLE_RENDERING_DEBUGGER_UI + /// + protected override VisualElement Create() { - alignment = TextAnchor.MiddleCenter - }; + var container = new UIElements.VisualElement(); + container.AddToClassList("debug-window-table"); + + using (ListPool.Get(out var tmp)) + { + if (children.Count != 0 && children[0] is Row row) + { + foreach (var child in row.children) + tmp.Add(child.displayName); + } + + var columnLabelsValues = new DebugUI.Value[tmp.Count]; + for (int i = 0; i < columnLabelsValues.Length; i++) + { + string value = tmp[i]; + object Getter() => value; + columnLabelsValues[i] = new DebugUI.Value() + { + displayName = string.Empty, + getter = Getter + }; + } + + var header = new DebugUI.ValueTuple() + { + displayName = string.Empty, + isHeader = true, + values = columnLabelsValues + }; + + var headerVisualElement = header.ToVisualElement(m_Context); + headerVisualElement.AddToClassList("debug-window-table-header-row"); + + foreach (var headerElement in headerVisualElement.Children()) + headerElement.AddToClassList("debug-window-table-header-cell"); + + container.Add(headerVisualElement); + + // NOTE: Current implementation constructs the table cells from individual elements, so cell width needs to be + // set manually like this. This is necessary because of the DebugUI.Table/Row structure, which is why we can't use + // a proper table. + container.RegisterCallback(evt => + { + var numColumns = columnLabelsValues.Length; + var availableWidth = evt.newRect.width; + if (displayRowNames) + availableWidth -= ValueTuple.GetLabelWidth(m_Context); + var width = availableWidth / numColumns; + + foreach (var visualElement in container.Children()) + { + int i = 0; + foreach (var child in visualElement.Children()) + { + if (i == 0) + child.style.display = displayRowNames ? DisplayStyle.Flex : DisplayStyle.None; + else + child.style.width = width; + ++i; + } + } + }); + } + + AddChildren(container, m_Context); + + container.enabledSelf = !isReadOnly; + + return container; + } +#endif /// Row Container. - public class Row : Foldout + public class Row : Container { /// Constructor. public Row() { displayName = "Row"; } + + /// Opened state of the row. No longer used. + [Obsolete("This class no longer inherits from Foldout, but from Container. This field is kept for compatibility but it no longer does anything. #from(6000.5)")] + public bool opened { get; set; } + +#if ENABLE_RENDERING_DEBUGGER_UI + /// + protected override VisualElement Create() + { + var rowContainer = new UIElements.VisualElement(); + rowContainer.AddToClassList("debug-window-table-row"); + + var label = new Label(displayName) { style = { width = ValueTuple.GetLabelWidth(m_Context) }, }; + label.AddToClassList("debug-window-table-row-displayname"); + rowContainer.Add(label); + + foreach (var c in children) + { + c.keepLayoutWhenHidden = true; + var rowElement = c.ToVisualElement(m_Context); + rowElement.AddToClassList("debug-window-table-cell"); + + // In Table layout we don't want to show the labels of DebugUI.Values + var valueLabel = rowElement.Q