From 214e65259e70f450aaa1f9a296b341716b1b2865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 11:25:44 +0300 Subject: [PATCH 01/31] Wip --- .gitignore | 18 + OverdrawMonitor/Assets/Demo.unity | Bin 0 -> 17596 bytes OverdrawMonitor/Assets/Demo.unity.meta | 7 + .../Editor/OverdrawToolWindow.cs | 77 +-- .../Editor/Resources/DebugOverdrawInt.shader | 95 ++-- .../OverdrawParallelReduction.compute | 36 +- .../Assets/OverdrawMonitor/OverdrawMonitor.cs | 504 ++++++++---------- OverdrawMonitor/OverdrawMonitor.sln | 52 +- OverdrawMonitor/Packages/manifest.json | 13 + .../ProjectSettings/EditorSettings.asset | Bin 4168 -> 4224 bytes .../ProjectSettings/PresetManager.asset | Bin 0 -> 4104 bytes .../ProjectSettings/ProjectVersion.txt | 3 +- .../ProjectSettings/VFXManager.asset | Bin 0 -> 4148 bytes .../ProjectSettings/XRSettings.asset | 10 + 14 files changed, 417 insertions(+), 398 deletions(-) create mode 100644 .gitignore create mode 100644 OverdrawMonitor/Assets/Demo.unity create mode 100644 OverdrawMonitor/Assets/Demo.unity.meta create mode 100644 OverdrawMonitor/Packages/manifest.json create mode 100644 OverdrawMonitor/ProjectSettings/PresetManager.asset create mode 100644 OverdrawMonitor/ProjectSettings/VFXManager.asset create mode 100644 OverdrawMonitor/ProjectSettings/XRSettings.asset diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d39fd63 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +**/.DS_Store/ +**/.vs/ +**/.idea/ +**/obj/ +**/bin/ +**/Jetbrains* + +**/Library/ +**/Temp/ +**/Build/ +**/Logs/ + +*.sln +*.user +*.csproj +*.userprefs +OverdrawMonitor/Assets/Plugins.meta +OverdrawMonitor/Assets/Plugins/Editor.meta diff --git a/OverdrawMonitor/Assets/Demo.unity b/OverdrawMonitor/Assets/Demo.unity new file mode 100644 index 0000000000000000000000000000000000000000..1786f6df6b0e77fc6e933f8333da2de588ddf562 GIT binary patch literal 17596 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 literal 0 HcmV?d00001 diff --git a/OverdrawMonitor/Assets/Demo.unity.meta b/OverdrawMonitor/Assets/Demo.unity.meta new file mode 100644 index 0000000..1ff7197 --- /dev/null +++ b/OverdrawMonitor/Assets/Demo.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1a4b9c6f59ac145fa839aba2060986b2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index e50b31e..0e83b65 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -1,41 +1,54 @@ using UnityEditor; using UnityEngine; -/// A simple tool to track the exact amount of overdraw in the game window. This tool -/// only shows the result, to check out how this is implemented. +// A simple tool to track the exact amount of overdraw in the game window. +// This tool only shows the result, see OverdrawMonitor to check out how this is implemented. public class OverdrawToolWindow : EditorWindow { - [MenuItem("Tools/Overdraw Tool")] - public static void ShowWindow() - { - var window = GetWindow(); - window.Focus(); - } + [MenuItem("Tools/Overdraw Tool")] + static void Show() + { + var window = GetWindow(); + window.Focus(); + } - public void OnGUI() - { - using (new GUILayout.HorizontalScope()) - { - if (GUILayout.Button("Start")) - { - OverdrawMonitor.Instance.StartMeasurement(); - OverdrawMonitor.Instance.ResetSampling(); - OverdrawMonitor.Instance.ResetExtreemes(); - } + [MenuItem("Tools/Overdraw Tool", true)] + static bool Validate() + { + return Application.isPlaying; + } - if (GUILayout.Button("End")) - { - OverdrawMonitor.Instance.Stop(); - } - } + void OnGUI() + { + if (Validate()) + { + using (new GUILayout.HorizontalScope()) + { + if (GUILayout.Button("Start")) + { + OverdrawMonitor.Instance.StartMeasurement(); + OverdrawMonitor.Instance.ResetSampling(); + OverdrawMonitor.Instance.ResetExtremes(); + } - using (new GUILayout.HorizontalScope()) - { - GUILayout.Label("Max\n" + OverdrawMonitor.Instance.MaxOverdraw.ToString("0.000")); - GUILayout.FlexibleSpace(); - GUILayout.Label("Average\n" + OverdrawMonitor.Instance.AccumulatedAverageOverdraw.ToString("0.000")); - } + if (GUILayout.Button("End")) + { + OverdrawMonitor.Instance.Stop(); + } + } - Repaint(); - } -} \ No newline at end of file + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label("Max\n" + OverdrawMonitor.Instance.MaxOverdraw.ToString("0.000")); + GUILayout.FlexibleSpace(); + GUILayout.Label("Average\n" + OverdrawMonitor.Instance.AccumulatedAverageOverdraw.ToString("0.000")); + } + } + else + { + GUILayout.Label("Available only in Play mode"); + } + + Repaint(); + } +} diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader index 706a77b..4e6b657 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader @@ -1,52 +1,51 @@ -// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' - -Shader "Debug/OverdrawInt" +Shader "Debug/OverdrawInt" { - Properties - { - [Header(Hardware settings)] - [Enum(UnityEngine.Rendering.CullMode)] HARDWARE_CullMode ("Cull faces", Float) = 2 - [Enum(On, 1, Off, 0)] HARDWARE_ZWrite ("Depth write", Float) = 1 - } - SubShader - { - Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" "Queue" = "Geometry+50"} - LOD 100 - - Pass - { - Cull [HARDWARE_CullMode] - ZWrite [HARDWARE_ZWrite] - Blend One One - - CGPROGRAM - #pragma vertex vert - #pragma fragment frag - #pragma exclude_renderers d3d11_9x + Properties + { + [Header(Hardware settings)] + [Enum(UnityEngine.Rendering.CullMode)] HARDWARE_CullMode ("Cull faces", Float) = 2 + [Enum(On, 1, Off, 0)] HARDWARE_ZWrite ("Depth write", Float) = 1 + } + SubShader + { + Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" "Queue" = "Geometry+50"} + LOD 100 + + Pass + { + Cull [HARDWARE_CullMode] + ZWrite [HARDWARE_ZWrite] + Blend One One + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #pragma exclude_renderers d3d11_9x + + struct appdata + { + float4 vertex : POSITION; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + }; + + v2f vert (appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + return o; + } - struct appdata - { - float4 vertex : POSITION; - }; + uniform float OverdrawFragmentWeight; - struct v2f - { - float4 vertex : SV_POSITION; - }; - - v2f vert (appdata v) - { - v2f o; - o.vertex = UnityObjectToClipPos(v.vertex); - return o; - } - - float4 frag (v2f i) : SV_Target - { - // 1 / 512 = 0.001953125; 1 / 1024 = 0.0009765625 - return 0.0009765625; - } - ENDCG - } - } + float4 frag (v2f i) : SV_Target + { + return OverdrawFragmentWeight; + } + ENDCG + } + } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute index c91dc0c..8e7b409 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute @@ -9,26 +9,26 @@ groupshared int accumulator[GROUPSIZE]; Texture2D Overdraw; RWStructuredBuffer Output; +int BufferSizeX; [numthreads(SIZEX,SIZEY,1)] void CSMain (uint3 gid : SV_GroupID, uint3 inp : SV_DispatchThreadID, uint gtidx : SV_GroupIndex) { - accumulator[gtidx] = (int)(Overdraw[inp.xy].x * 1024); - - // Wait for all - GroupMemoryBarrierWithGroupSync(); - - [unroll] - for (uint ix = GROUPSIZE >> 1; ix > 0; ix = ix >> 1) - { - if (gtidx < ix) - { - accumulator[gtidx] = (accumulator[gtidx] + accumulator[gtidx + ix]); - } - GroupMemoryBarrierWithGroupSync(); - } - - if (gtidx != 0) return; - - Output[gid.y * 128 + gid.x] = accumulator[0]; + accumulator[gtidx] = (int)(Overdraw[inp.xy].x * GROUPSIZE); + + // Wait for all + GroupMemoryBarrierWithGroupSync(); + + [unroll] + for (uint ix = GROUPSIZE >> 1; ix > 0; ix = ix >> 1) + { + if (gtidx < ix) + accumulator[gtidx] = (accumulator[gtidx] + accumulator[gtidx + ix]); + GroupMemoryBarrierWithGroupSync(); + } + + if (gtidx != 0) + return; + + Output[gid.y * BufferSizeX + gid.x] = accumulator[0]; } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs index 288eb8f..106e685 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs @@ -1,278 +1,236 @@ using System; using UnityEngine; -/// This is a singleton component that is responsible for measuring overdraw information -/// on the main camera. You shouldn't add this compoenent manually, but use the Instance getter to -/// access it. -/// -/// The measurements process is done in two passes. First a new camera is created that will render -/// the scene into a texture with high precission, the texture is called overdrawTexture. This texture -/// contains the information how many times a pixel has been overdrawn. After this step a compute shader -/// is used to add up all the pixels in the overdrawTexture and stores the information into this component. -/// -/// We say this tool measures exactly the amount of overdraw, but it does so only in certain cases. In other -/// cases the error margin is very small. This is because of the nature of the compute shaders. Compute -/// shaders should operate in batches in order for them to be efficient. In our case the compute shader -/// batch that sums up the results of the first render pass has a size of 32x32. This means that if the -/// pixel size of the camera is not divisible by 32, then the edge pixels that don't fit in range won't -/// be processed. But since we usually have huge render targets (in comparison to 32x32 pixel blocks) and -/// the error comes from the part of the image that is not important, this is acceptable. -/// -[ExecuteInEditMode] +// This is a singleton component that is responsible for measuring overdraw information +// on the main camera. You shouldn't add this component manually, but use the Instance getter to +// access it. +// +// The measurements process is done in two passes. First a new camera is created that will render +// the scene into a texture with high precision, the texture is called overdrawTexture. This texture +// contains the information how many times a pixel has been overdrawn. After this step a compute shader +// is used to add up all the pixels in the overdrawTexture and stores the information into this component. +// +// We say this tool measures exactly the amount of overdraw, but it does so only in certain cases. In other +// cases the error margin is very small. This is because of the nature of the compute shaders. Compute +// shaders should operate in batches in order for them to be efficient. In our case the compute shader +// batch that sums up the results of the first render pass has a size of 32x32. This means that if the +// pixel size of the camera is not divisible by 32, then the edge pixels that don't fit in range won't +// be processed. But since we usually have huge render targets (in comparison to 32x32 pixel blocks) and +// the error comes from the part of the image that is not important, this is acceptable. + public class OverdrawMonitor : MonoBehaviour { - private static OverdrawMonitor instance; - public static OverdrawMonitor Instance - { - get - { - if (instance == null) - { - instance = GameObject.FindObjectOfType(); - if (instance == null) - { - var go = new GameObject("OverdrawMonitor"); - instance = go.AddComponent(); - } - } - - return instance; - } - } - - private new Camera camera; - private RenderTexture overdrawTexture; - - private ComputeShader computeShader; - - private const int dataSize = 128 * 128; - private int[] inputData = new int[dataSize]; - private int[] resultData = new int[dataSize]; - private ComputeBuffer resultBuffer; - private Shader replacementShader; - - // ========= Results ======== - // Last measurement - /// The number of shaded fragments in the last frame. - public long TotalShadedFragments { get; private set; } - /// The overdraw ration in the last frame. - public float OverdrawRatio { get; private set; } - - // Sampled measurement - /// Number of shaded fragments in the measured time span. - public long IntervalShadedFragments { get; private set; } - /// The average number of shaded fragments in the measured time span. - public float IntervalAverageShadedFragments { get; private set; } - /// The average overdraw in the measured time span. - public float IntervalAverageOverdraw { get; private set; } - public float AccumulatedAverageOverdraw { get { return accumulatedIntervalOverdraw / intervalFrames; } } - - // Extreems - /// The maximum overdraw measured. - public float MaxOverdraw { get; private set; } - - private long accumulatedIntervalFragments; - private float accumulatedIntervalOverdraw; - private long intervalFrames; - - private float intervalTime = 0; - public float SampleTime = 1; - - /// An empty method that can be used to initialize the singleton. - public void Touch() { } - - #region Measurement magic - - public void Awake() - { -#if UNITY_EDITOR - // Since this emulation always turns on by default if on mobile platform. With the emulation - // turned on the tool won't work. - UnityEditor.EditorApplication.ExecuteMenuItem("Edit/Graphics Emulation/No Emulation"); - SubscribeToPlayStateChanged(); -#endif - - if (Application.isPlaying) DontDestroyOnLoad(gameObject); - gameObject.hideFlags = HideFlags.DontSave | HideFlags.HideInInspector; - - // Prepare the camera that is going to render the scene with the initial overdraw data. - replacementShader = Shader.Find("Debug/OverdrawInt"); - - camera = GetComponent(); - if (camera == null) camera = gameObject.AddComponent(); - camera.CopyFrom(Camera.main); - camera.SetReplacementShader(replacementShader, null); - - RecreateTexture(Camera.main); - RecreateComputeBuffer(); - - computeShader = Resources.Load("OverdrawParallelReduction"); - - for (int i = 0; i < inputData.Length; i++) inputData[i] = 0; - } - -#if UNITY_EDITOR - public void SubscribeToPlayStateChanged() - { - UnityEditor.EditorApplication.playmodeStateChanged -= OnPlayStateChanged; - UnityEditor.EditorApplication.playmodeStateChanged += OnPlayStateChanged; - } - - private static void OnPlayStateChanged() - { - if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode && UnityEditor.EditorApplication.isPlaying) - { - if (instance != null) instance.OnDisable(); - } - } -#endif - - private bool disabled = true; - - public void OnEnable() - { - disabled = false; - } - - public void OnDisable() - { - disabled = true; - OnDestroy(); - } - - public void LateUpdate() - { - if (disabled) return; - - Camera main = Camera.main; - camera.CopyFrom(main); - camera.clearFlags = CameraClearFlags.SolidColor; - camera.backgroundColor = Color.black; - camera.targetTexture = overdrawTexture; - camera.SetReplacementShader(replacementShader, null); - - transform.position = main.transform.position; - transform.rotation = main.transform.rotation; - - RecreateTexture(main); - - intervalTime += Time.deltaTime; - if (intervalTime > SampleTime) - { - IntervalShadedFragments = accumulatedIntervalFragments; - IntervalAverageShadedFragments = (float)accumulatedIntervalFragments / intervalFrames; - IntervalAverageOverdraw = (float)accumulatedIntervalOverdraw / intervalFrames; - - intervalTime -= SampleTime; - - accumulatedIntervalFragments = 0; - accumulatedIntervalOverdraw = 0; - intervalFrames = 0; - } - } - - /// Checks if the overdraw texture should be updated. This needs to happen if the main camera - /// configuration changes. - private void RecreateTexture(Camera main) - { - if (overdrawTexture == null) - { - overdrawTexture = new RenderTexture(camera.pixelWidth, camera.pixelHeight, 24, RenderTextureFormat.RFloat); - overdrawTexture.enableRandomWrite = true; - camera.targetTexture = overdrawTexture; - } - - if (main.pixelWidth != overdrawTexture.width || main.pixelHeight != overdrawTexture.height) - { - overdrawTexture.Release(); - overdrawTexture.width = main.pixelWidth; - overdrawTexture.height = main.pixelHeight; - } - } - - private void RecreateComputeBuffer() - { - if (resultBuffer != null) return; - resultBuffer = new ComputeBuffer(resultData.Length, 4); - } - - public void OnDestroy() - { - if (camera != null) - { - camera.targetTexture = null; - } - if (resultBuffer != null) resultBuffer.Release(); - } - - public void OnPostRender() - { - if (disabled) return; - - int kernel = computeShader.FindKernel("CSMain"); - - RecreateComputeBuffer(); - - // Setting up the data - resultBuffer.SetData(inputData); - computeShader.SetTexture(kernel, "Overdraw", overdrawTexture); - computeShader.SetBuffer(kernel, "Output", resultBuffer); - - int xGroups = (overdrawTexture.width / 32); - int yGroups = (overdrawTexture.height / 32); - - // Summing up the fragments - computeShader.Dispatch(kernel, xGroups, yGroups, 1); - resultBuffer.GetData(resultData); - - // Getting the results - TotalShadedFragments = 0; - for (int i = 0; i < resultData.Length; i++) - { - TotalShadedFragments += resultData[i]; - } - - OverdrawRatio = (float)TotalShadedFragments / (xGroups * 32 * yGroups * 32); - - accumulatedIntervalFragments += TotalShadedFragments; - accumulatedIntervalOverdraw += OverdrawRatio; - intervalFrames++; - - if (OverdrawRatio > MaxOverdraw) MaxOverdraw = OverdrawRatio; - } - - #endregion - #region Measurement control methods - - public void StartMeasurement() - { - enabled = true; - camera.enabled = true; - } - - public void Stop() - { - enabled = false; - camera.enabled = false; - } - - public void SetSampleTime(float time) - { - SampleTime = time; - } - - public void ResetSampling() - { - accumulatedIntervalOverdraw = 0; - accumulatedIntervalFragments = 0; - intervalTime = 0; - intervalFrames = 0; - } - - public void ResetExtreemes() - { - MaxOverdraw = 0; - } - - #endregion -} \ No newline at end of file + const int GroupDimension = 32; + const int DataDimension = 128; + const int DataSize = DataDimension * DataDimension; + + public static OverdrawMonitor Instance + { + get + { + if (instance == null) + { + instance = FindObjectOfType(); + if (instance == null) + { + var go = new GameObject("OverdrawMonitor"); + DontDestroyOnLoad(go); + go.hideFlags = HideFlags.DontSave; + go.AddComponent(); + } + } + return instance; + } + } + + // Last measurement + // The number of shaded fragments in the last frame + public long TotalShadedFragments { get; private set; } + + // The overdraw ration in the last frame + public float OverdrawRatio { get; private set; } + + // Sampled measurement + // Number of shaded fragments in the measured time span + public long IntervalShadedFragments { get; private set; } + + // The average number of shaded fragments in the measured time span + public float IntervalAverageShadedFragments { get; private set; } + + // The average overdraw in the measured time span + public float IntervalAverageOverdraw { get; private set; } + public float AccumulatedAverageOverdraw => accumulatedIntervalOverdraw / intervalFrames; + + // Extremes + // The maximum overdraw measured + public float MaxOverdraw { get; private set; } + + static OverdrawMonitor instance; + Camera _camera; + RenderTexture overdrawTexture; + ComputeShader computeShader; + int[] inputData = new int[DataSize]; + int[] resultData = new int[DataSize]; + ComputeBuffer resultBuffer; + Shader replacementShader; + long accumulatedIntervalFragments; + float accumulatedIntervalOverdraw; + long intervalFrames; + float intervalTime = 0; + public float SampleTime = 1; + + void Awake() + { + // Compute shader + computeShader = Resources.Load("OverdrawParallelReduction"); + + // Replacement shader + replacementShader = Shader.Find("Debug/OverdrawInt"); + Shader.SetGlobalFloat("OverdrawFragmentWeight", 1f / (GroupDimension * GroupDimension)); + + // Camera + _camera = gameObject.AddComponent(); + + for (int i = 0; i < inputData.Length; i++) + inputData[i] = 0; + + instance = this; + } + + void LateUpdate() + { + Camera main = Camera.main; + + _camera.CopyFrom(main); + _camera.clearFlags = CameraClearFlags.SolidColor; + _camera.backgroundColor = Color.black; + _camera.SetReplacementShader(replacementShader, null); + + RecreateTexture(main); + _camera.targetTexture = overdrawTexture; + + transform.position = main.transform.position; + transform.rotation = main.transform.rotation; + + intervalTime += Time.deltaTime; + if (intervalTime > SampleTime) + { + IntervalShadedFragments = accumulatedIntervalFragments; + IntervalAverageShadedFragments = (float)accumulatedIntervalFragments / intervalFrames; + IntervalAverageOverdraw = accumulatedIntervalOverdraw / intervalFrames; + + intervalTime -= SampleTime; + + accumulatedIntervalFragments = 0; + accumulatedIntervalOverdraw = 0; + intervalFrames = 0; + } + } + + // Checks if the overdraw texture should be updated. This needs to happen if the main camera configuration changes. + void RecreateTexture(Camera main) + { + if (overdrawTexture == null || main.pixelWidth != overdrawTexture.width || main.pixelHeight != overdrawTexture.height) + { + ReleaseTexture(); + overdrawTexture = new RenderTexture(main.pixelWidth, main.pixelHeight, 24, RenderTextureFormat.RFloat); + } + } + + void ReleaseTexture() + { + if (_camera != null) + _camera.targetTexture = null; + + if (overdrawTexture == null) + return; + + overdrawTexture.Release(); + overdrawTexture = null; + } + + void RecreateComputeBuffer() + { + if (resultBuffer == null) + resultBuffer = new ComputeBuffer(resultData.Length, 4); + } + + void OnDestroy() + { + ReleaseTexture(); + + resultBuffer?.Release(); + + instance = null; + } + + void OnPostRender() + { + if (overdrawTexture == null) + return; + + int kernel = computeShader.FindKernel("CSMain"); + + RecreateComputeBuffer(); + + // Setting up the data + resultBuffer.SetData(inputData); + computeShader.SetInt("BufferSizeX", DataDimension); + computeShader.SetTexture(kernel, "Overdraw", overdrawTexture); + computeShader.SetBuffer(kernel, "Output", resultBuffer); + + int xGroups = overdrawTexture.width / GroupDimension; + int yGroups = overdrawTexture.height / GroupDimension; + + // Summing up the fragments + computeShader.Dispatch(kernel, xGroups, yGroups, 1); + resultBuffer.GetData(resultData); + + // Getting the results + TotalShadedFragments = 0; + for (int i = 0; i < resultData.Length; i++) + TotalShadedFragments += resultData[i]; + + OverdrawRatio = (float)TotalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); + + accumulatedIntervalFragments += TotalShadedFragments; + accumulatedIntervalOverdraw += OverdrawRatio; + intervalFrames++; + + if (OverdrawRatio > MaxOverdraw) MaxOverdraw = OverdrawRatio; + } + + // + // Measurement control methods + // + + public void StartMeasurement() + { + enabled = true; + GetComponent().enabled = true; + } + + public void Stop() + { + enabled = false; + GetComponent().enabled = false; + } + + public void SetSampleTime(float time) + { + SampleTime = time; + } + + public void ResetSampling() + { + accumulatedIntervalOverdraw = 0; + accumulatedIntervalFragments = 0; + intervalTime = 0; + intervalFrames = 0; + } + + public void ResetExtremes() + { + MaxOverdraw = 0; + } +} diff --git a/OverdrawMonitor/OverdrawMonitor.sln b/OverdrawMonitor/OverdrawMonitor.sln index 021c85b..417e796 100644 --- a/OverdrawMonitor/OverdrawMonitor.sln +++ b/OverdrawMonitor/OverdrawMonitor.sln @@ -1,26 +1,26 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2017 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OverdrawMonitor", "OverdrawMonitor.csproj", "{A52161F3-317E-5525-A6BD-A25BBAEAE865}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OverdrawMonitor.Editor", "OverdrawMonitor.Editor.csproj", "{2E1B6611-394C-ECEC-9DC7-06E12D394BC9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A52161F3-317E-5525-A6BD-A25BBAEAE865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A52161F3-317E-5525-A6BD-A25BBAEAE865}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A52161F3-317E-5525-A6BD-A25BBAEAE865}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A52161F3-317E-5525-A6BD-A25BBAEAE865}.Release|Any CPU.Build.0 = Release|Any CPU - {2E1B6611-394C-ECEC-9DC7-06E12D394BC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E1B6611-394C-ECEC-9DC7-06E12D394BC9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E1B6611-394C-ECEC-9DC7-06E12D394BC9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E1B6611-394C-ECEC-9DC7-06E12D394BC9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp", "Assembly-CSharp.csproj", "{EC5ED580-E73A-34AE-BD42-C20D925FCF24}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assembly-CSharp-Editor", "Assembly-CSharp-Editor.csproj", "{47896C88-30AF-3D6A-D336-EE6A9882AF0B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EC5ED580-E73A-34AE-BD42-C20D925FCF24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC5ED580-E73A-34AE-BD42-C20D925FCF24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC5ED580-E73A-34AE-BD42-C20D925FCF24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC5ED580-E73A-34AE-BD42-C20D925FCF24}.Release|Any CPU.Build.0 = Release|Any CPU + {47896C88-30AF-3D6A-D336-EE6A9882AF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47896C88-30AF-3D6A-D336-EE6A9882AF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47896C88-30AF-3D6A-D336-EE6A9882AF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47896C88-30AF-3D6A-D336-EE6A9882AF0B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/OverdrawMonitor/Packages/manifest.json b/OverdrawMonitor/Packages/manifest.json new file mode 100644 index 0000000..1958171 --- /dev/null +++ b/OverdrawMonitor/Packages/manifest.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "com.unity.package-manager-ui": "2.1.2", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + } +} diff --git a/OverdrawMonitor/ProjectSettings/EditorSettings.asset b/OverdrawMonitor/ProjectSettings/EditorSettings.asset index 094eabc0d137bd4f3a0e8f581b807ce80a6243cf..70cbe1e6661f47b073e4705250d3597f835c6cc5 100644 GIT binary patch literal 4224 zcmeI0OK;Oa5P;V$6ezFqD6jG;{D9Jc5JEvHeWWE)TctIWThZ3uBrf)DwBEES2jmM^ z_z55mNSu%=@eerg2M~u!Nc;e<90@btHLep<{(wi?hv%D_-JP9Cb_f~zLC8o%2sr~I z420a8EZmzYOx&pz$nQ?4V_BekgP_xS_hNSV>*Vs2;-~uBJbC@?251)s2swlp02*w_2SII>48%DZ zB;SFJK@5zatdUqCUk1Jb4c9#w`y>?ot=Ru2sL*!^Q3*RO37>&SXfcYU1$v|oLlN1nRFJX>EEdbqCdS7!Z-=o?|DrF;ELpx$r& zG33SAC63g2!un?fq!Hj^Ipz2{T` zT@)?@-GawinY$QaPRJ#;WjoSqDhxNvYP996Y1W{do{+IawW{QO+hLqdg&|W9z^p34 zZheK-TRxR#zUfK91IBT|InQU6S+t?5p(nBN<4V@fZc0zFDs`SQy$tMikE%*t++bB~ zF$uxS%6Y`vswLTs2%3_Gp^(WYGkA`MDmBTVB_RXq_kq)_%daZM26NOr99zc8t8m9} zxy;3kbIdj7tH)|Z2(?557B;EF$c*rPy54hX|5SS%g4OY8Wlq<9=Hlk#nK`+wB%=W& zrNS|yxy*hhRiADflc{ig&w?|jW04q$MIb^)TzOj`Tt2`1Ml|m8KFO}@dHb>Rk0oT#Tr*d$1Tz@ Oa9Pa+LmvzNS@;9o30(L9 literal 4168 zcmeH~yN=U96o!x2-0u*eK}h=q0fc}eO|%zSil8hOD^a1rK8D$KJkfZv+=AhXBzVT~#UFc7V5Zf{y5S4PIsuv|uAfJyRm7fDDj|qfS z69H`rzbI?euSlj6dn>^mnIXWT8;rq2`X=m#oPo}fR!G~zcO{^E9QAbsb^Wcj_Jm{_ zW3S=sEk6k^IkZm!$~fei%)w6s$^b|I?`4iY&j6O--Tu!C?(6>?cpcsT&;R9( z{wG;O|AOQ`efZa>kEhtDFAuoojs6!|L;s58KK%je$2imV|0;ML-Ttq&d^6F`*od9e z`Ky9vFE*EsZLYE%wuLtNy2+h2S+tnXjdijgvDGTmZM7#PTJ~dk*0cC+t7T;f5*U! lM;H2K#;X%{YW1Pcxaf9^`yTG0Zs|tLSvE>@H;R*^nm>lq#qaY5S>j-;`g^U3jTv=1QkI-iXe&~M6nO2o+g@znp3b4uJad|)<#m z))uz@6wmi=b8s7ff;%v?v+vE!?!7&c7%%D+gkjb1oZ}I32jNlM@tZH{4j<=p`w)(|3!)41%&lc4-3eD zfgeQ9{scjvL7S3<$^oI1PZECD9HWw?e1n_949=&=jr=r#lXicKq|cyDp(BfQ8P54> z!ajpGC8=#Y%9rl+2vcCroDG;@h&*JVCY}wljH~EyT zfxiIG`}JXswWBs|=)*gx0*kaM+&He!0AN%9jO<_{^&!r8F!`?VeeQts{2{F7`6=W4 z@JA}JNL%#%(ff=rl1g8?6t6sZA){EHcL5JLzN*)KU zR%@CnIbNl+X4R?H_ML-MIn>AM`3>`A;iMH!CpZ!zNqD71Ll2L?U5*AWWFgYgeV#-gO@Q-l>qs=T0Um#ipZF+!S zpcklDsBO!hpy}Kh)Jee?=(}*fpL6a#&KyRf&;rr?CJ`-S$40cV9*aj~(XB#^eq7fT z0$gPDbDLauIU9bwZzq?&zjF1=>za#MdH}eEYn#Zyi0fS(Z}k>|j^_|LFM^yf81C=P za0Ba8*jIsRU<3%30P_Lwb&xzC0$_#JFV>^*kK;N4_`l)!BG!(73TO5o?JEAE-+v7B zF#quZ-GBa5@a)ge5UhUxNr=PzBLlji1H9h_c+Quz3cNnNMnnt@gLQrevCB<<2_(k( zt-yn`nF0RhAEFcJ*8uag1G=CmXt&^behvs0=gWEEmsXO>=ZBTNX~|35XF#OQl04kUK{M`-J}|Yg0#9#de!PpWMlFN1jFDzgDRDPNN@K1XKbl V0hNGCKqc^>6S!-&67O$EJHNT(bKd{} literal 0 HcmV?d00001 diff --git a/OverdrawMonitor/ProjectSettings/XRSettings.asset b/OverdrawMonitor/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/OverdrawMonitor/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file From 0b664675d36bf71db53aa55ed1f7ea28ab934ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 11:38:32 +0300 Subject: [PATCH 02/31] Fix --- .../Editor/OverdrawToolWindow.cs | 8 +---- .../Assets/OverdrawMonitor/OverdrawMonitor.cs | 33 +++++-------------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 0e83b65..13b4c47 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -24,17 +24,11 @@ void OnGUI() { using (new GUILayout.HorizontalScope()) { - if (GUILayout.Button("Start")) + if (GUILayout.Button("Reset stats")) { - OverdrawMonitor.Instance.StartMeasurement(); OverdrawMonitor.Instance.ResetSampling(); OverdrawMonitor.Instance.ResetExtremes(); } - - if (GUILayout.Button("End")) - { - OverdrawMonitor.Instance.Stop(); - } } using (new GUILayout.HorizontalScope()) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs index 106e685..cecd2b2 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs @@ -125,6 +125,8 @@ void LateUpdate() accumulatedIntervalOverdraw = 0; intervalFrames = 0; } + + _camera.enabled = true; } // Checks if the overdraw texture should be updated. This needs to happen if the main camera configuration changes. @@ -166,7 +168,7 @@ void OnDestroy() void OnPostRender() { - if (overdrawTexture == null) + if (_camera.targetTexture == null) return; int kernel = computeShader.FindKernel("CSMain"); @@ -188,8 +190,8 @@ void OnPostRender() // Getting the results TotalShadedFragments = 0; - for (int i = 0; i < resultData.Length; i++) - TotalShadedFragments += resultData[i]; + foreach (int res in resultData) + TotalShadedFragments += res; OverdrawRatio = (float)TotalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); @@ -197,28 +199,11 @@ void OnPostRender() accumulatedIntervalOverdraw += OverdrawRatio; intervalFrames++; - if (OverdrawRatio > MaxOverdraw) MaxOverdraw = OverdrawRatio; - } - - // - // Measurement control methods - // + if (OverdrawRatio > MaxOverdraw) + MaxOverdraw = OverdrawRatio; - public void StartMeasurement() - { - enabled = true; - GetComponent().enabled = true; - } - - public void Stop() - { - enabled = false; - GetComponent().enabled = false; - } - - public void SetSampleTime(float time) - { - SampleTime = time; + _camera.enabled = false; + _camera.targetTexture = null; } public void ResetSampling() From a9a169c64088b5445fe110241032266585747c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 12:26:43 +0300 Subject: [PATCH 03/31] Multi cam --- .../OverdrawMonitor/CameraOverdrawMonitor.cs | 200 ++++++++++++++++ .../CameraOverdrawMonitor.cs.meta | 12 + .../Editor/OverdrawToolWindow.cs | 18 +- .../Assets/OverdrawMonitor/OverdrawMonitor.cs | 222 +++--------------- .../OverdrawMonitor/OverdrawMonitor.cs.meta | 5 +- 5 files changed, 257 insertions(+), 200 deletions(-) create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs new file mode 100644 index 0000000..0cf9a3b --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -0,0 +1,200 @@ +using UnityEngine; + +// This is a singleton component that is responsible for measuring overdraw information +// on the main camera. You shouldn't add this component manually, but use the Instance getter to +// access it. +// +// The measurements process is done in two passes. First a new camera is created that will render +// the scene into a texture with high precision, the texture is called overdrawTexture. This texture +// contains the information how many times a pixel has been overdrawn. After this step a compute shader +// is used to add up all the pixels in the overdrawTexture and stores the information into this component. +// +// We say this tool measures exactly the amount of overdraw, but it does so only in certain cases. In other +// cases the error margin is very small. This is because of the nature of the compute shaders. Compute +// shaders should operate in batches in order for them to be efficient. In our case the compute shader +// batch that sums up the results of the first render pass has a size of 32x32. This means that if the +// pixel size of the camera is not divisible by 32, then the edge pixels that don't fit in range won't +// be processed. But since we usually have huge render targets (in comparison to 32x32 pixel blocks) and +// the error comes from the part of the image that is not important, this is acceptable. + +[DisallowMultipleComponent] +public class CameraOverdrawMonitor : MonoBehaviour +{ + const int GroupDimension = 32; + const int DataDimension = 128; + const int DataSize = DataDimension * DataDimension; + + // Last measurement + // The number of shaded fragments in the last frame + public long TotalShadedFragments { get; private set; } + + // The overdraw ration in the last frame + public float OverdrawRatio { get; private set; } + + // Sampled measurement + // Number of shaded fragments in the measured time span + public long IntervalShadedFragments { get; private set; } + + // The average number of shaded fragments in the measured time span + public float IntervalAverageShadedFragments { get; private set; } + + // The average overdraw in the measured time span + public float IntervalAverageOverdraw { get; private set; } + public float AccumulatedAverageOverdraw => accumulatedIntervalOverdraw / intervalFrames; + + // Extremes + // The maximum overdraw measured + public float MaxOverdraw { get; private set; } + + public Camera sourceCamera => _sourceCamera; + + Camera _sourceCamera; + Camera _computeCamera; + RenderTexture overdrawTexture; + ComputeShader computeShader; + int[] inputData = new int[DataSize]; + int[] resultData = new int[DataSize]; + ComputeBuffer resultBuffer; + Shader replacementShader; + long accumulatedIntervalFragments; + float accumulatedIntervalOverdraw; + long intervalFrames; + float intervalTime = 0; + public float SampleTime = 1; + + void Awake() + { + // Compute shader + computeShader = Resources.Load("OverdrawParallelReduction"); + + // Replacement shader + replacementShader = Shader.Find("Debug/OverdrawInt"); + Shader.SetGlobalFloat("OverdrawFragmentWeight", 1f / (GroupDimension * GroupDimension)); + + // Camera + _computeCamera = gameObject.AddComponent(); + + for (int i = 0; i < inputData.Length; i++) + inputData[i] = 0; + } + + void OnDestroy() + { + ReleaseTexture(); + resultBuffer?.Release(); + } + + public void SetSourceCamera(Camera sourceCamera) + { + _sourceCamera = sourceCamera; + } + + void RecreateTexture(Camera main) + { + if (overdrawTexture == null || main.pixelWidth != overdrawTexture.width || main.pixelHeight != overdrawTexture.height) + { + ReleaseTexture(); + overdrawTexture = new RenderTexture(main.pixelWidth, main.pixelHeight, 24, RenderTextureFormat.RFloat); + } + } + + void ReleaseTexture() + { + if (_computeCamera != null) + _computeCamera.targetTexture = null; + + if (overdrawTexture == null) + return; + + overdrawTexture.Release(); + overdrawTexture = null; + } + + void RecreateComputeBuffer() + { + if (resultBuffer == null) + resultBuffer = new ComputeBuffer(resultData.Length, 4); + } + + void LateUpdate() + { + if (_sourceCamera == null) + return; + + _computeCamera.CopyFrom(_sourceCamera); + _computeCamera.clearFlags = CameraClearFlags.SolidColor; + _computeCamera.backgroundColor = Color.black; + _computeCamera.SetReplacementShader(replacementShader, null); + + RecreateTexture(_sourceCamera); + _computeCamera.targetTexture = overdrawTexture; + + Transform sourceCameraNode = _sourceCamera.transform; + transform.SetPositionAndRotation(sourceCameraNode.position, sourceCameraNode.rotation); + + intervalTime += Time.deltaTime; + if (intervalTime > SampleTime) + { + IntervalShadedFragments = accumulatedIntervalFragments; + IntervalAverageShadedFragments = (float)accumulatedIntervalFragments / intervalFrames; + IntervalAverageOverdraw = accumulatedIntervalOverdraw / intervalFrames; + + intervalTime -= SampleTime; + + accumulatedIntervalFragments = 0; + accumulatedIntervalOverdraw = 0; + intervalFrames = 0; + } + + _computeCamera.enabled = true; + } + + void OnPostRender() + { + if (_computeCamera.targetTexture == null) + return; + + int kernel = computeShader.FindKernel("CSMain"); + + RecreateComputeBuffer(); + + // Setting up the data + resultBuffer.SetData(inputData); + computeShader.SetInt("BufferSizeX", DataDimension); + computeShader.SetTexture(kernel, "Overdraw", overdrawTexture); + computeShader.SetBuffer(kernel, "Output", resultBuffer); + + int xGroups = overdrawTexture.width / GroupDimension; + int yGroups = overdrawTexture.height / GroupDimension; + + // Summing up the fragments + computeShader.Dispatch(kernel, xGroups, yGroups, 1); + resultBuffer.GetData(resultData); + + // Getting the results + TotalShadedFragments = 0; + foreach (int res in resultData) + TotalShadedFragments += res; + + OverdrawRatio = (float)TotalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); + + accumulatedIntervalFragments += TotalShadedFragments; + accumulatedIntervalOverdraw += OverdrawRatio; + intervalFrames++; + + if (OverdrawRatio > MaxOverdraw) + MaxOverdraw = OverdrawRatio; + + _computeCamera.enabled = false; + _computeCamera.targetTexture = null; + } + + public void ResetStats() + { + accumulatedIntervalOverdraw = 0; + accumulatedIntervalFragments = 0; + intervalTime = 0; + intervalFrames = 0; + MaxOverdraw = 0; + } +} diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs.meta b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs.meta new file mode 100644 index 0000000..a225de4 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 35d88df0531cbd44f98a9d605e8a55b3 +timeCreated: 1498554150 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 13b4c47..be4639a 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -1,8 +1,6 @@ using UnityEditor; using UnityEngine; -// A simple tool to track the exact amount of overdraw in the game window. -// This tool only shows the result, see OverdrawMonitor to check out how this is implemented. public class OverdrawToolWindow : EditorWindow { [MenuItem("Tools/Overdraw Tool")] @@ -25,17 +23,17 @@ void OnGUI() using (new GUILayout.HorizontalScope()) { if (GUILayout.Button("Reset stats")) - { - OverdrawMonitor.Instance.ResetSampling(); - OverdrawMonitor.Instance.ResetExtremes(); - } + OverdrawMonitor.instance.ResetStats(); } - using (new GUILayout.HorizontalScope()) + for (int i = 0; i < OverdrawMonitor.instance.camerasCount; i++) { - GUILayout.Label("Max\n" + OverdrawMonitor.Instance.MaxOverdraw.ToString("0.000")); - GUILayout.FlexibleSpace(); - GUILayout.Label("Average\n" + OverdrawMonitor.Instance.AccumulatedAverageOverdraw.ToString("0.000")); + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label("Max\n" + OverdrawMonitor.instance.GetMaxOverdraw(i).ToString("0.000")); + GUILayout.FlexibleSpace(); + GUILayout.Label("Average\n" + OverdrawMonitor.instance.GetAccumulatedAverageOverdraw(i).ToString("0.000")); + } } } else diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs index cecd2b2..cfa9350 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs @@ -1,221 +1,69 @@ -using System; +using System.Collections.Generic; using UnityEngine; -// This is a singleton component that is responsible for measuring overdraw information -// on the main camera. You shouldn't add this component manually, but use the Instance getter to -// access it. -// -// The measurements process is done in two passes. First a new camera is created that will render -// the scene into a texture with high precision, the texture is called overdrawTexture. This texture -// contains the information how many times a pixel has been overdrawn. After this step a compute shader -// is used to add up all the pixels in the overdrawTexture and stores the information into this component. -// -// We say this tool measures exactly the amount of overdraw, but it does so only in certain cases. In other -// cases the error margin is very small. This is because of the nature of the compute shaders. Compute -// shaders should operate in batches in order for them to be efficient. In our case the compute shader -// batch that sums up the results of the first render pass has a size of 32x32. This means that if the -// pixel size of the camera is not divisible by 32, then the edge pixels that don't fit in range won't -// be processed. But since we usually have huge render targets (in comparison to 32x32 pixel blocks) and -// the error comes from the part of the image that is not important, this is acceptable. - +[DisallowMultipleComponent] public class OverdrawMonitor : MonoBehaviour { - const int GroupDimension = 32; - const int DataDimension = 128; - const int DataSize = DataDimension * DataDimension; - - public static OverdrawMonitor Instance + public static OverdrawMonitor instance { get { - if (instance == null) + if (_instance == null) { - instance = FindObjectOfType(); - if (instance == null) - { - var go = new GameObject("OverdrawMonitor"); - DontDestroyOnLoad(go); - go.hideFlags = HideFlags.DontSave; - go.AddComponent(); - } + var go = new GameObject("OverdrawMonitor"); + DontDestroyOnLoad(go); + go.hideFlags = HideFlags.DontSave; + _instance = go.AddComponent(); } - return instance; + return _instance; } } + public int camerasCount => _cameraOverdrawMonitors.Count; - // Last measurement - // The number of shaded fragments in the last frame - public long TotalShadedFragments { get; private set; } - - // The overdraw ration in the last frame - public float OverdrawRatio { get; private set; } - - // Sampled measurement - // Number of shaded fragments in the measured time span - public long IntervalShadedFragments { get; private set; } - - // The average number of shaded fragments in the measured time span - public float IntervalAverageShadedFragments { get; private set; } - - // The average overdraw in the measured time span - public float IntervalAverageOverdraw { get; private set; } - public float AccumulatedAverageOverdraw => accumulatedIntervalOverdraw / intervalFrames; - - // Extremes - // The maximum overdraw measured - public float MaxOverdraw { get; private set; } - - static OverdrawMonitor instance; - Camera _camera; - RenderTexture overdrawTexture; - ComputeShader computeShader; - int[] inputData = new int[DataSize]; - int[] resultData = new int[DataSize]; - ComputeBuffer resultBuffer; - Shader replacementShader; - long accumulatedIntervalFragments; - float accumulatedIntervalOverdraw; - long intervalFrames; - float intervalTime = 0; - public float SampleTime = 1; + static OverdrawMonitor _instance; + List _cameraOverdrawMonitors; void Awake() { - // Compute shader - computeShader = Resources.Load("OverdrawParallelReduction"); - - // Replacement shader - replacementShader = Shader.Find("Debug/OverdrawInt"); - Shader.SetGlobalFloat("OverdrawFragmentWeight", 1f / (GroupDimension * GroupDimension)); - - // Camera - _camera = gameObject.AddComponent(); - - for (int i = 0; i < inputData.Length; i++) - inputData[i] = 0; - - instance = this; - } - - void LateUpdate() - { - Camera main = Camera.main; - - _camera.CopyFrom(main); - _camera.clearFlags = CameraClearFlags.SolidColor; - _camera.backgroundColor = Color.black; - _camera.SetReplacementShader(replacementShader, null); - - RecreateTexture(main); - _camera.targetTexture = overdrawTexture; - - transform.position = main.transform.position; - transform.rotation = main.transform.rotation; - - intervalTime += Time.deltaTime; - if (intervalTime > SampleTime) - { - IntervalShadedFragments = accumulatedIntervalFragments; - IntervalAverageShadedFragments = (float)accumulatedIntervalFragments / intervalFrames; - IntervalAverageOverdraw = accumulatedIntervalOverdraw / intervalFrames; - - intervalTime -= SampleTime; - - accumulatedIntervalFragments = 0; - accumulatedIntervalOverdraw = 0; - intervalFrames = 0; - } - - _camera.enabled = true; + _cameraOverdrawMonitors = new List(); } - // Checks if the overdraw texture should be updated. This needs to happen if the main camera configuration changes. - void RecreateTexture(Camera main) + void Update() { - if (overdrawTexture == null || main.pixelWidth != overdrawTexture.width || main.pixelHeight != overdrawTexture.height) + foreach (Camera activeCamera in Camera.allCameras) { - ReleaseTexture(); - overdrawTexture = new RenderTexture(main.pixelWidth, main.pixelHeight, 24, RenderTextureFormat.RFloat); + if (!_cameraOverdrawMonitors.Exists(m => m.sourceCamera == activeCamera)) + { + var go = new GameObject("CameraOverdrawMonitor"); + go.transform.SetParent(activeCamera.transform, false); + var cameraOverdrawMonitor = go.AddComponent(); + cameraOverdrawMonitor.SetSourceCamera(activeCamera); + _cameraOverdrawMonitors.Add(cameraOverdrawMonitor); + } } } - void ReleaseTexture() - { - if (_camera != null) - _camera.targetTexture = null; - - if (overdrawTexture == null) - return; - - overdrawTexture.Release(); - overdrawTexture = null; - } - - void RecreateComputeBuffer() + public void ResetStats() { - if (resultBuffer == null) - resultBuffer = new ComputeBuffer(resultData.Length, 4); + foreach (CameraOverdrawMonitor cameraOverdrawMonitor in _cameraOverdrawMonitors) + cameraOverdrawMonitor.ResetStats(); } - void OnDestroy() + public float GetMaxOverdraw(int index) { - ReleaseTexture(); - - resultBuffer?.Release(); - - instance = null; - } - - void OnPostRender() - { - if (_camera.targetTexture == null) - return; - - int kernel = computeShader.FindKernel("CSMain"); - - RecreateComputeBuffer(); - - // Setting up the data - resultBuffer.SetData(inputData); - computeShader.SetInt("BufferSizeX", DataDimension); - computeShader.SetTexture(kernel, "Overdraw", overdrawTexture); - computeShader.SetBuffer(kernel, "Output", resultBuffer); - - int xGroups = overdrawTexture.width / GroupDimension; - int yGroups = overdrawTexture.height / GroupDimension; - - // Summing up the fragments - computeShader.Dispatch(kernel, xGroups, yGroups, 1); - resultBuffer.GetData(resultData); - - // Getting the results - TotalShadedFragments = 0; - foreach (int res in resultData) - TotalShadedFragments += res; - - OverdrawRatio = (float)TotalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); - - accumulatedIntervalFragments += TotalShadedFragments; - accumulatedIntervalOverdraw += OverdrawRatio; - intervalFrames++; - - if (OverdrawRatio > MaxOverdraw) - MaxOverdraw = OverdrawRatio; - - _camera.enabled = false; - _camera.targetTexture = null; + CameraOverdrawMonitor cameraOverdrawMonitor = GetActiveMonitor(index); + return cameraOverdrawMonitor != null ? cameraOverdrawMonitor.MaxOverdraw : 0f; } - public void ResetSampling() + public float GetAccumulatedAverageOverdraw(int index) { - accumulatedIntervalOverdraw = 0; - accumulatedIntervalFragments = 0; - intervalTime = 0; - intervalFrames = 0; + CameraOverdrawMonitor cameraOverdrawMonitor = GetActiveMonitor(index); + return cameraOverdrawMonitor != null ? cameraOverdrawMonitor.AccumulatedAverageOverdraw : 0f; } - public void ResetExtremes() + CameraOverdrawMonitor GetActiveMonitor(int index) { - MaxOverdraw = 0; + CameraOverdrawMonitor cameraOverdrawMonitor = _cameraOverdrawMonitors[index]; + return cameraOverdrawMonitor != null && cameraOverdrawMonitor.isActiveAndEnabled ? cameraOverdrawMonitor : null; } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta index a225de4..9e0ec9f 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta +++ b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 -guid: 35d88df0531cbd44f98a9d605e8a55b3 -timeCreated: 1498554150 -licenseType: Pro +guid: efea442517ace415381fbedf7e9bbd29 MonoImporter: + externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 From 7eeebc2f41a8881748f43d0b673c5f668157970b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 12:38:50 +0300 Subject: [PATCH 04/31] Style fixes --- .../OverdrawMonitor/CameraOverdrawMonitor.cs | 124 +++++++++--------- 1 file changed, 59 insertions(+), 65 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 0cf9a3b..8234043 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -1,10 +1,4 @@ -using UnityEngine; - -// This is a singleton component that is responsible for measuring overdraw information -// on the main camera. You shouldn't add this component manually, but use the Instance getter to -// access it. -// -// The measurements process is done in two passes. First a new camera is created that will render +// The measurements process is done in two passes. First a new camera is created that will render // the scene into a texture with high precision, the texture is called overdrawTexture. This texture // contains the information how many times a pixel has been overdrawn. After this step a compute shader // is used to add up all the pixels in the overdrawTexture and stores the information into this component. @@ -17,21 +11,24 @@ // be processed. But since we usually have huge render targets (in comparison to 32x32 pixel blocks) and // the error comes from the part of the image that is not important, this is acceptable. +using UnityEngine; + [DisallowMultipleComponent] public class CameraOverdrawMonitor : MonoBehaviour { const int GroupDimension = 32; const int DataDimension = 128; const int DataSize = DataDimension * DataDimension; + const float SampleTime = 1f; + + public Camera sourceCamera => _sourceCamera; - // Last measurement // The number of shaded fragments in the last frame public long TotalShadedFragments { get; private set; } // The overdraw ration in the last frame public float OverdrawRatio { get; private set; } - // Sampled measurement // Number of shaded fragments in the measured time span public long IntervalShadedFragments { get; private set; } @@ -40,48 +37,45 @@ public class CameraOverdrawMonitor : MonoBehaviour // The average overdraw in the measured time span public float IntervalAverageOverdraw { get; private set; } - public float AccumulatedAverageOverdraw => accumulatedIntervalOverdraw / intervalFrames; + public float AccumulatedAverageOverdraw => _accumulatedIntervalOverdraw / _intervalFrames; - // Extremes // The maximum overdraw measured public float MaxOverdraw { get; private set; } - public Camera sourceCamera => _sourceCamera; - Camera _sourceCamera; Camera _computeCamera; - RenderTexture overdrawTexture; - ComputeShader computeShader; - int[] inputData = new int[DataSize]; - int[] resultData = new int[DataSize]; - ComputeBuffer resultBuffer; - Shader replacementShader; - long accumulatedIntervalFragments; - float accumulatedIntervalOverdraw; - long intervalFrames; - float intervalTime = 0; - public float SampleTime = 1; + RenderTexture _overdrawTexture; + ComputeShader _computeShader; + ComputeBuffer _resultBuffer; + Shader _replacementShader; + int[] _inputData; + int[] _resultData; + long _accumulatedIntervalFragments; + float _accumulatedIntervalOverdraw; + float _intervalTime; + long _intervalFrames; void Awake() { // Compute shader - computeShader = Resources.Load("OverdrawParallelReduction"); + _computeShader = Resources.Load("OverdrawParallelReduction"); // Replacement shader - replacementShader = Shader.Find("Debug/OverdrawInt"); + _replacementShader = Shader.Find("Debug/OverdrawInt"); Shader.SetGlobalFloat("OverdrawFragmentWeight", 1f / (GroupDimension * GroupDimension)); - // Camera _computeCamera = gameObject.AddComponent(); - for (int i = 0; i < inputData.Length; i++) - inputData[i] = 0; + _inputData = new int[DataSize]; + _resultData = new int[DataSize]; + for (int i = 0; i < _inputData.Length; i++) + _inputData[i] = 0; } void OnDestroy() { ReleaseTexture(); - resultBuffer?.Release(); + _resultBuffer?.Release(); } public void SetSourceCamera(Camera sourceCamera) @@ -91,10 +85,10 @@ public void SetSourceCamera(Camera sourceCamera) void RecreateTexture(Camera main) { - if (overdrawTexture == null || main.pixelWidth != overdrawTexture.width || main.pixelHeight != overdrawTexture.height) + if (_overdrawTexture == null || main.pixelWidth != _overdrawTexture.width || main.pixelHeight != _overdrawTexture.height) { ReleaseTexture(); - overdrawTexture = new RenderTexture(main.pixelWidth, main.pixelHeight, 24, RenderTextureFormat.RFloat); + _overdrawTexture = new RenderTexture(main.pixelWidth, main.pixelHeight, 24, RenderTextureFormat.RFloat); } } @@ -103,17 +97,17 @@ void ReleaseTexture() if (_computeCamera != null) _computeCamera.targetTexture = null; - if (overdrawTexture == null) + if (_overdrawTexture == null) return; - overdrawTexture.Release(); - overdrawTexture = null; + _overdrawTexture.Release(); + _overdrawTexture = null; } void RecreateComputeBuffer() { - if (resultBuffer == null) - resultBuffer = new ComputeBuffer(resultData.Length, 4); + if (_resultBuffer == null) + _resultBuffer = new ComputeBuffer(_resultData.Length, 4); } void LateUpdate() @@ -124,26 +118,26 @@ void LateUpdate() _computeCamera.CopyFrom(_sourceCamera); _computeCamera.clearFlags = CameraClearFlags.SolidColor; _computeCamera.backgroundColor = Color.black; - _computeCamera.SetReplacementShader(replacementShader, null); + _computeCamera.SetReplacementShader(_replacementShader, null); RecreateTexture(_sourceCamera); - _computeCamera.targetTexture = overdrawTexture; + _computeCamera.targetTexture = _overdrawTexture; Transform sourceCameraNode = _sourceCamera.transform; transform.SetPositionAndRotation(sourceCameraNode.position, sourceCameraNode.rotation); - intervalTime += Time.deltaTime; - if (intervalTime > SampleTime) + _intervalTime += Time.deltaTime; + if (_intervalTime > SampleTime) { - IntervalShadedFragments = accumulatedIntervalFragments; - IntervalAverageShadedFragments = (float)accumulatedIntervalFragments / intervalFrames; - IntervalAverageOverdraw = accumulatedIntervalOverdraw / intervalFrames; + IntervalShadedFragments = _accumulatedIntervalFragments; + IntervalAverageShadedFragments = (float)_accumulatedIntervalFragments / _intervalFrames; + IntervalAverageOverdraw = _accumulatedIntervalOverdraw / _intervalFrames; - intervalTime -= SampleTime; + _intervalTime -= SampleTime; - accumulatedIntervalFragments = 0; - accumulatedIntervalOverdraw = 0; - intervalFrames = 0; + _accumulatedIntervalFragments = 0; + _accumulatedIntervalOverdraw = 0; + _intervalFrames = 0; } _computeCamera.enabled = true; @@ -154,33 +148,33 @@ void OnPostRender() if (_computeCamera.targetTexture == null) return; - int kernel = computeShader.FindKernel("CSMain"); + int kernel = _computeShader.FindKernel("CSMain"); RecreateComputeBuffer(); // Setting up the data - resultBuffer.SetData(inputData); - computeShader.SetInt("BufferSizeX", DataDimension); - computeShader.SetTexture(kernel, "Overdraw", overdrawTexture); - computeShader.SetBuffer(kernel, "Output", resultBuffer); + _resultBuffer.SetData(_inputData); + _computeShader.SetInt("BufferSizeX", DataDimension); + _computeShader.SetTexture(kernel, "Overdraw", _overdrawTexture); + _computeShader.SetBuffer(kernel, "Output", _resultBuffer); - int xGroups = overdrawTexture.width / GroupDimension; - int yGroups = overdrawTexture.height / GroupDimension; + int xGroups = _overdrawTexture.width / GroupDimension; + int yGroups = _overdrawTexture.height / GroupDimension; // Summing up the fragments - computeShader.Dispatch(kernel, xGroups, yGroups, 1); - resultBuffer.GetData(resultData); + _computeShader.Dispatch(kernel, xGroups, yGroups, 1); + _resultBuffer.GetData(_resultData); // Getting the results TotalShadedFragments = 0; - foreach (int res in resultData) + foreach (int res in _resultData) TotalShadedFragments += res; OverdrawRatio = (float)TotalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); - accumulatedIntervalFragments += TotalShadedFragments; - accumulatedIntervalOverdraw += OverdrawRatio; - intervalFrames++; + _accumulatedIntervalFragments += TotalShadedFragments; + _accumulatedIntervalOverdraw += OverdrawRatio; + _intervalFrames++; if (OverdrawRatio > MaxOverdraw) MaxOverdraw = OverdrawRatio; @@ -191,10 +185,10 @@ void OnPostRender() public void ResetStats() { - accumulatedIntervalOverdraw = 0; - accumulatedIntervalFragments = 0; - intervalTime = 0; - intervalFrames = 0; + _accumulatedIntervalOverdraw = 0; + _accumulatedIntervalFragments = 0; + _intervalTime = 0; + _intervalFrames = 0; MaxOverdraw = 0; } } From e50166cd8566c0e64ffe032f6bd55b30c9176b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 12:54:13 +0300 Subject: [PATCH 05/31] Improvements --- .../Editor/OverdrawToolWindow.cs | 8 ++++++-- .../Assets/OverdrawMonitor/OverdrawMonitor.cs | 19 ++++--------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index be4639a..07e7966 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -28,11 +28,15 @@ void OnGUI() for (int i = 0; i < OverdrawMonitor.instance.camerasCount; i++) { + CameraOverdrawMonitor cameraOverdrawMonitor = OverdrawMonitor.instance.GetCameraOverdrawMonitor(i); + if (cameraOverdrawMonitor == null) + continue; + using (new GUILayout.HorizontalScope()) { - GUILayout.Label("Max\n" + OverdrawMonitor.instance.GetMaxOverdraw(i).ToString("0.000")); + GUILayout.Label("Max\n" + cameraOverdrawMonitor.MaxOverdraw.ToString("0.000")); GUILayout.FlexibleSpace(); - GUILayout.Label("Average\n" + OverdrawMonitor.instance.GetAccumulatedAverageOverdraw(i).ToString("0.000")); + GUILayout.Label("Average\n" + (cameraOverdrawMonitor.isActiveAndEnabled ? cameraOverdrawMonitor.AccumulatedAverageOverdraw : 0f).ToString("0.000")); } } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs index cfa9350..02cb0ec 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs @@ -41,6 +41,8 @@ void Update() _cameraOverdrawMonitors.Add(cameraOverdrawMonitor); } } + + _cameraOverdrawMonitors.RemoveAll(m => m == null); } public void ResetStats() @@ -49,21 +51,8 @@ public void ResetStats() cameraOverdrawMonitor.ResetStats(); } - public float GetMaxOverdraw(int index) - { - CameraOverdrawMonitor cameraOverdrawMonitor = GetActiveMonitor(index); - return cameraOverdrawMonitor != null ? cameraOverdrawMonitor.MaxOverdraw : 0f; - } - - public float GetAccumulatedAverageOverdraw(int index) - { - CameraOverdrawMonitor cameraOverdrawMonitor = GetActiveMonitor(index); - return cameraOverdrawMonitor != null ? cameraOverdrawMonitor.AccumulatedAverageOverdraw : 0f; - } - - CameraOverdrawMonitor GetActiveMonitor(int index) + public CameraOverdrawMonitor GetCameraOverdrawMonitor(int index) { - CameraOverdrawMonitor cameraOverdrawMonitor = _cameraOverdrawMonitors[index]; - return cameraOverdrawMonitor != null && cameraOverdrawMonitor.isActiveAndEnabled ? cameraOverdrawMonitor : null; + return _cameraOverdrawMonitors[index]; } } From b5a4f76811b075d31b7c50f7d935709c28ee1ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 13:39:17 +0300 Subject: [PATCH 06/31] Fix --- .../OverdrawMonitor/CameraOverdrawMonitor.cs | 1 + .../Editor/OverdrawToolWindow.cs | 45 ++++++++++++-- .../Assets/OverdrawMonitor/OverdrawMonitor.cs | 58 ------------------- .../OverdrawMonitor/OverdrawMonitor.cs.meta | 11 ---- 4 files changed, 41 insertions(+), 74 deletions(-) delete mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs delete mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 8234043..5c032a1 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -65,6 +65,7 @@ void Awake() Shader.SetGlobalFloat("OverdrawFragmentWeight", 1f / (GroupDimension * GroupDimension)); _computeCamera = gameObject.AddComponent(); + _computeCamera.enabled = false; _inputData = new int[DataSize]; _resultData = new int[DataSize]; diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 07e7966..f669d02 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -1,12 +1,17 @@ -using UnityEditor; +using System.Collections.Generic; +using UnityEditor; using UnityEngine; public class OverdrawToolWindow : EditorWindow { + List _cameraOverdrawMonitors; + [MenuItem("Tools/Overdraw Tool")] static void Show() { var window = GetWindow(); + window._cameraOverdrawMonitors = new List(); + window.Focus(); } @@ -16,6 +21,31 @@ static bool Validate() return Application.isPlaying; } + void Update() + { + if (!Validate()) + return; + + _cameraOverdrawMonitors.RemoveAll(m => m == null); + + foreach (Camera activeCamera in Camera.allCameras) + { + if (!_cameraOverdrawMonitors.Exists(m => m.sourceCamera == activeCamera)) + { + var cameraOverdrawMonitor = activeCamera.GetComponentInChildren(true); + if (cameraOverdrawMonitor == null) + { + var go = new GameObject("CameraOverdrawMonitor"); + go.hideFlags = HideFlags.DontSave; + go.transform.SetParent(activeCamera.transform, false); + cameraOverdrawMonitor = go.AddComponent(); + } + cameraOverdrawMonitor.SetSourceCamera(activeCamera); + _cameraOverdrawMonitors.Add(cameraOverdrawMonitor); + } + } + } + void OnGUI() { if (Validate()) @@ -23,12 +53,15 @@ void OnGUI() using (new GUILayout.HorizontalScope()) { if (GUILayout.Button("Reset stats")) - OverdrawMonitor.instance.ResetStats(); + { + foreach (CameraOverdrawMonitor cameraOverdrawMonitor in _cameraOverdrawMonitors) + cameraOverdrawMonitor.ResetStats(); + } } - for (int i = 0; i < OverdrawMonitor.instance.camerasCount; i++) + for (int i = 0; i < _cameraOverdrawMonitors.Count; i++) { - CameraOverdrawMonitor cameraOverdrawMonitor = OverdrawMonitor.instance.GetCameraOverdrawMonitor(i); + CameraOverdrawMonitor cameraOverdrawMonitor = _cameraOverdrawMonitors[i]; if (cameraOverdrawMonitor == null) continue; @@ -36,7 +69,9 @@ void OnGUI() { GUILayout.Label("Max\n" + cameraOverdrawMonitor.MaxOverdraw.ToString("0.000")); GUILayout.FlexibleSpace(); - GUILayout.Label("Average\n" + (cameraOverdrawMonitor.isActiveAndEnabled ? cameraOverdrawMonitor.AccumulatedAverageOverdraw : 0f).ToString("0.000")); + float accumulatedAverageOverdraw = cameraOverdrawMonitor.isActiveAndEnabled ? + cameraOverdrawMonitor.AccumulatedAverageOverdraw : 0f; + GUILayout.Label("Average\n" + accumulatedAverageOverdraw.ToString("0.000")); } } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs deleted file mode 100644 index 02cb0ec..0000000 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; - -[DisallowMultipleComponent] -public class OverdrawMonitor : MonoBehaviour -{ - public static OverdrawMonitor instance - { - get - { - if (_instance == null) - { - var go = new GameObject("OverdrawMonitor"); - DontDestroyOnLoad(go); - go.hideFlags = HideFlags.DontSave; - _instance = go.AddComponent(); - } - return _instance; - } - } - public int camerasCount => _cameraOverdrawMonitors.Count; - - static OverdrawMonitor _instance; - List _cameraOverdrawMonitors; - - void Awake() - { - _cameraOverdrawMonitors = new List(); - } - - void Update() - { - foreach (Camera activeCamera in Camera.allCameras) - { - if (!_cameraOverdrawMonitors.Exists(m => m.sourceCamera == activeCamera)) - { - var go = new GameObject("CameraOverdrawMonitor"); - go.transform.SetParent(activeCamera.transform, false); - var cameraOverdrawMonitor = go.AddComponent(); - cameraOverdrawMonitor.SetSourceCamera(activeCamera); - _cameraOverdrawMonitors.Add(cameraOverdrawMonitor); - } - } - - _cameraOverdrawMonitors.RemoveAll(m => m == null); - } - - public void ResetStats() - { - foreach (CameraOverdrawMonitor cameraOverdrawMonitor in _cameraOverdrawMonitors) - cameraOverdrawMonitor.ResetStats(); - } - - public CameraOverdrawMonitor GetCameraOverdrawMonitor(int index) - { - return _cameraOverdrawMonitors[index]; - } -} diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta b/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta deleted file mode 100644 index 9e0ec9f..0000000 --- a/OverdrawMonitor/Assets/OverdrawMonitor/OverdrawMonitor.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: efea442517ace415381fbedf7e9bbd29 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 5de30eb64d4133e153e2db7423a0f8d317102d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 26 Jun 2019 14:13:52 +0300 Subject: [PATCH 07/31] Files rearranged --- .../{Editor/Resources => }/DebugOverdrawInt.shader | 0 .../{Editor/Resources => }/DebugOverdrawInt.shader.meta | 0 .../Assets/OverdrawMonitor/{Editor => }/Resources.meta | 5 ++--- .../{Editor => }/Resources/OverdrawParallelReduction.compute | 0 .../Resources/OverdrawParallelReduction.compute.meta | 0 5 files changed, 2 insertions(+), 3 deletions(-) rename OverdrawMonitor/Assets/OverdrawMonitor/{Editor/Resources => }/DebugOverdrawInt.shader (100%) rename OverdrawMonitor/Assets/OverdrawMonitor/{Editor/Resources => }/DebugOverdrawInt.shader.meta (100%) rename OverdrawMonitor/Assets/OverdrawMonitor/{Editor => }/Resources.meta (58%) rename OverdrawMonitor/Assets/OverdrawMonitor/{Editor => }/Resources/OverdrawParallelReduction.compute (100%) rename OverdrawMonitor/Assets/OverdrawMonitor/{Editor => }/Resources/OverdrawParallelReduction.compute.meta (100%) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader b/OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader rename to OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader.meta b/OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader.meta similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/DebugOverdrawInt.shader.meta rename to OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta similarity index 58% rename from OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources.meta rename to OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta index 01cff07..cf033ad 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources.meta +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta @@ -1,9 +1,8 @@ fileFormatVersion: 2 -guid: 633512af7657fe7469c43935e7fee4fc +guid: 90c7d44c95a9d451788e15da842abf3c folderAsset: yes -timeCreated: 1498554083 -licenseType: Pro DefaultImporter: + externalObjects: {} userData: assetBundleName: assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute b/OverdrawMonitor/Assets/OverdrawMonitor/Resources/OverdrawParallelReduction.compute similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute rename to OverdrawMonitor/Assets/OverdrawMonitor/Resources/OverdrawParallelReduction.compute diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Resources/OverdrawParallelReduction.compute.meta similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/Editor/Resources/OverdrawParallelReduction.compute.meta rename to OverdrawMonitor/Assets/OverdrawMonitor/Resources/OverdrawParallelReduction.compute.meta From fa9df3a2676dac8d9654b8ac85157d23018ff537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Fri, 6 Sep 2019 11:08:11 +0300 Subject: [PATCH 08/31] Refactor --- DrawCallState/DrawCallState.shader | 52 ------ OverdrawMonitor/Assets/Demo.unity | Bin 17596 -> 35829 bytes .../OverdrawMonitor/CameraOverdrawMonitor.cs | 20 +-- .../Editor/OverdrawToolWindow.cs | 159 ++++++++++++++---- OverdrawMonitor/Packages/manifest.json | 10 +- .../ProjectSettings/ProjectSettings.asset | Bin 51918 -> 66172 bytes .../ProjectSettings/ProjectVersion.txt | 4 +- 7 files changed, 146 insertions(+), 99 deletions(-) delete mode 100644 DrawCallState/DrawCallState.shader diff --git a/DrawCallState/DrawCallState.shader b/DrawCallState/DrawCallState.shader deleted file mode 100644 index 2a2b8e8..0000000 --- a/DrawCallState/DrawCallState.shader +++ /dev/null @@ -1,52 +0,0 @@ -Shader "DrawCallState/DrawCallState" -{ - Properties - { - [Header(Hardware settings)] - [Enum(UnityEngine.Rendering.CullMode)] HARDWARE_CullMode ("Cull faces", Float) = 2 - [Enum(UnityEngine.Rendering.BlendMode)] HARDWARE_BlendSrc ("Blend Source", Float) = 1 - [Enum(UnityEngine.Rendering.BlendMode)] HARDWARE_BlendDst ("Blend Destination", Float) = 0 - - [Enum(On, 1, Off, 0)] HARDWARE_ZWrite ("Depth write", Float) = 1 - [Enum(UnityEngine.Rendering.CompareFunction)] HARDWARE_ZTest("Depth test", Float) = 4 - - [Header(Hardware stencil)] - HARDWARE_StencilRef ("Stencil REF", Range(0, 255)) = 0 - HARDWARE_ReadMask ("Stencil Read Mask", Range(0, 255)) = 255 - HARDWARE_WriteMask ("Stencil Write Mask", Range(0, 255)) = 255 - - [Enum(UnityEngine.Rendering.CompareFunction)] HARDWARE_StencilComp ("Stencil comparison", Float) = 0 - [Enum(UnityEngine.Rendering.StencilOp)] HARDWARE_StencilPass ("Stencil Pass", Float) = 0 - [Enum(UnityEngine.Rendering.StencilOp)] HARDWARE_StencilFail ("Stencil Fail", Float) = 0 - [Enum(UnityEngine.Rendering.StencilOp)] HARDWARE_StencilZFail ("Stencil Z Fail", Float) = 0 - } - SubShader - { - Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" "Queue" = "Geometry+50"} - LOD 100 - - Pass - { - Cull [HARDWARE_CullMode] - ZWrite [HARDWARE_ZWrite] - ZTest [HARDWARE_ZTest] - Blend [HARDWARE_BlendSrc] [HARDWARE_BlendDst] - - Stencil - { - Ref [HARDWARE_StencilRef] - Comp [HARDWARE_StencilComp] - - Pass [HARDWARE_StencilPass] - Fail [HARDWARE_StencilFail] - ZFail [HARDWARE_StencilZFail] - } - - CGPROGRAM - - // Write your shader code here - - ENDCG - } - } -} diff --git a/OverdrawMonitor/Assets/Demo.unity b/OverdrawMonitor/Assets/Demo.unity index 1786f6df6b0e77fc6e933f8333da2de588ddf562..b419a840f44303abd192c2d317c8b1d189223800 100644 GIT binary patch delta 9171 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 OAlLgn^R!(piTy8^ini+j delta 983 zcmZuvUr1A76#u@tt>*=b+z)NYh&YURf%&T==$~{X^yQjw26u#(fZ3?uvH8cg!hrBI;`cC?q zo~G=}xRAd0owy?!_1+xY3SL|Oek1uu(%eQ)={ca1vhF>jGm4t6XpB>#-^zQa%Adt2 zDd5k_n-M4i^P&8ojz1!686=uv1)?{dKt55#Z`DoUZJWNJj*$`qR&%C?v0|v0t_X_h zY1)&)i)pq_qBW0!S_7%{)}Yr_AWBQa0Vy#%j>(sC!h?<6t~2Kg#HS)yG&+J&MeL9? zOb&w=x0H_9C3@{KaMzzJiW^hO6tB;d5t59MBO=wEEo@9Ut`d#+gBL>`B{^5(gEZ8b z#fNAL_5np2Gw4OU-Z+dH%+A~-+oj6K>Zx4RaSO8}qA9VIakb)!-^5@12MqNLyaCur zx}GcP=0r3#2d(@i4FxkyJl55`TEjD9llU8o1g)`uY7MYxqyqZCFZ4}Kt3;Yn7q>In zS*5nAmn)x}CpnL&z7|)?3epi7n;VX;(`t*k(GKXYicVeGP7%x#U`y~i=yMhGp>8+M zdT5T5HNde=V4d({lesG9!(kKWT~M2fRijWWmwN74=s?I%(;+=Q42^M4j&KgwD3`)< soMt;LwAE!&?swQUG~PE3w!Gg--Tii&>C$Q@_EWGojh6bW$udy=3o%B~b^rhX diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 5c032a1..09cf9de 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -21,8 +21,6 @@ public class CameraOverdrawMonitor : MonoBehaviour const int DataSize = DataDimension * DataDimension; const float SampleTime = 1f; - public Camera sourceCamera => _sourceCamera; - // The number of shaded fragments in the last frame public long TotalShadedFragments { get; private set; } @@ -84,6 +82,15 @@ public void SetSourceCamera(Camera sourceCamera) _sourceCamera = sourceCamera; } + public void ResetStats() + { + _accumulatedIntervalOverdraw = 0; + _accumulatedIntervalFragments = 0; + _intervalTime = 0; + _intervalFrames = 0; + MaxOverdraw = 0; + } + void RecreateTexture(Camera main) { if (_overdrawTexture == null || main.pixelWidth != _overdrawTexture.width || main.pixelHeight != _overdrawTexture.height) @@ -183,13 +190,4 @@ void OnPostRender() _computeCamera.enabled = false; _computeCamera.targetTexture = null; } - - public void ResetStats() - { - _accumulatedIntervalOverdraw = 0; - _accumulatedIntervalFragments = 0; - _intervalTime = 0; - _intervalFrames = 0; - MaxOverdraw = 0; - } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index f669d02..2ce70d8 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -2,16 +2,24 @@ using UnityEditor; using UnityEngine; +class MonitorInfo +{ + public Camera sourceCamera; + // public Canvas canvas; + public CameraOverdrawMonitor monitor; +} + public class OverdrawToolWindow : EditorWindow { - List _cameraOverdrawMonitors; + static bool isEnabled => _monitorsRoot != null; + static Transform _monitorsRoot; + List _monitors; [MenuItem("Tools/Overdraw Tool")] - static void Show() + static void ShowWindow() { var window = GetWindow(); - window._cameraOverdrawMonitors = new List(); - + window.Show(); window.Focus(); } @@ -21,29 +29,80 @@ static bool Validate() return Application.isPlaying; } + void Init() + { + var window = GetWindow(); + window.Show(); + window._monitors = new List(); + + var monitorsRootGo = new GameObject("OverdrawMonitorsRoot"); + monitorsRootGo.hideFlags = HideFlags.DontSave; + _monitorsRoot = monitorsRootGo.transform; + } + + void TryShutdown() + { + if (_monitors != null) + { + foreach (MonitorInfo monitorInfo in _monitors.ToArray()) + RemoveMonitor(monitorInfo); + _monitors = null; + } + + if (_monitorsRoot != null) + { + DestroyImmediate(_monitorsRoot.gameObject); + _monitorsRoot = null; + } + } + + void AddMonitorForCamera(Camera camera) + { + var go = new GameObject("CameraOverdrawMonitor"); + go.hideFlags = HideFlags.DontSave; + go.transform.SetParent(_monitorsRoot, false); + var monitor = go.AddComponent(); + monitor.SetSourceCamera(camera); + _monitors.Add(new MonitorInfo + { + sourceCamera = camera, + monitor = monitor, + }); + } + + void RemoveMonitor(MonitorInfo monitorInfo) + { + // Todo: add canvas support + + DestroyImmediate(monitorInfo.monitor.gameObject); + _monitors.Remove(monitorInfo); + } + + void CheckMonitor(MonitorInfo monitorInfo) + { + if ((monitorInfo.sourceCamera == null || !monitorInfo.sourceCamera.isActiveAndEnabled)/* && + monitorInfo.canvas == null || !monitorInfo.canvas.isActiveAndEnabled*/) + { + RemoveMonitor(monitorInfo); + } + } + void Update() { - if (!Validate()) + // Check shutdown if needed + if (!isEnabled || !Validate()) + TryShutdown(); + if (_monitors == null) return; - _cameraOverdrawMonitors.RemoveAll(m => m == null); + // Check existing monitors + foreach (MonitorInfo monitorInfo in _monitors.ToArray()) + CheckMonitor(monitorInfo); + // Refresh monitors foreach (Camera activeCamera in Camera.allCameras) - { - if (!_cameraOverdrawMonitors.Exists(m => m.sourceCamera == activeCamera)) - { - var cameraOverdrawMonitor = activeCamera.GetComponentInChildren(true); - if (cameraOverdrawMonitor == null) - { - var go = new GameObject("CameraOverdrawMonitor"); - go.hideFlags = HideFlags.DontSave; - go.transform.SetParent(activeCamera.transform, false); - cameraOverdrawMonitor = go.AddComponent(); - } - cameraOverdrawMonitor.SetSourceCamera(activeCamera); - _cameraOverdrawMonitors.Add(cameraOverdrawMonitor); - } - } + if (!_monitors.Exists(m => m.sourceCamera == activeCamera)) + AddMonitorForCamera(activeCamera); } void OnGUI() @@ -52,28 +111,56 @@ void OnGUI() { using (new GUILayout.HorizontalScope()) { - if (GUILayout.Button("Reset stats")) + if (GUILayout.Button(isEnabled ? "Stop" : "Start", GUILayout.MaxWidth(100), GUILayout.MaxHeight(25))) { - foreach (CameraOverdrawMonitor cameraOverdrawMonitor in _cameraOverdrawMonitors) - cameraOverdrawMonitor.ResetStats(); + if (!isEnabled && Validate()) + Init(); + else + TryShutdown(); + } + + if (!isEnabled) + return; + + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Reset Max", GUILayout.Width(100), GUILayout.Height(20))) + { + foreach (MonitorInfo monitorInfo in _monitors) + if (monitorInfo.monitor != null) + monitorInfo.monitor.ResetStats(); } } - for (int i = 0; i < _cameraOverdrawMonitors.Count; i++) - { - CameraOverdrawMonitor cameraOverdrawMonitor = _cameraOverdrawMonitors[i]; - if (cameraOverdrawMonitor == null) - continue; + GUILayout.Space(5); + float totalAverage = 0f; + float totalMax = 0f; + foreach (MonitorInfo monitorInfo in _monitors) + { using (new GUILayout.HorizontalScope()) { - GUILayout.Label("Max\n" + cameraOverdrawMonitor.MaxOverdraw.ToString("0.000")); + CameraOverdrawMonitor monitor = monitorInfo.monitor; + + GUILayout.Label(monitorInfo.sourceCamera.name); GUILayout.FlexibleSpace(); - float accumulatedAverageOverdraw = cameraOverdrawMonitor.isActiveAndEnabled ? - cameraOverdrawMonitor.AccumulatedAverageOverdraw : 0f; - GUILayout.Label("Average\n" + accumulatedAverageOverdraw.ToString("0.000")); + + float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.AccumulatedAverageOverdraw : 0f; + GUILayout.Label(FormatResult(accumulatedAverageOverdraw, monitor.MaxOverdraw)); + + totalMax += monitor.MaxOverdraw; + totalAverage += accumulatedAverageOverdraw; } } + + GUILayout.Space(5); + + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label("TOTAL"); + GUILayout.FlexibleSpace(); + GUILayout.Label(FormatResult(totalAverage, totalMax)); + } } else { @@ -82,4 +169,10 @@ void OnGUI() Repaint(); } + + string FormatResult(float average, float max) + { + return $"{average:N3}\tMax: {max:N3}"; + } } + diff --git a/OverdrawMonitor/Packages/manifest.json b/OverdrawMonitor/Packages/manifest.json index 1958171..03901ba 100644 --- a/OverdrawMonitor/Packages/manifest.json +++ b/OverdrawMonitor/Packages/manifest.json @@ -1,6 +1,14 @@ { "dependencies": { - "com.unity.package-manager-ui": "2.1.2", + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.ext.nunit": "1.0.0", + "com.unity.ide.rider": "1.0.8", + "com.unity.ide.vscode": "1.0.7", + "com.unity.package-manager-ui": "2.2.0", + "com.unity.test-framework": "1.0.13", + "com.unity.ugui": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", diff --git a/OverdrawMonitor/ProjectSettings/ProjectSettings.asset b/OverdrawMonitor/ProjectSettings/ProjectSettings.asset index 781c93bda5f880bf7a23f079d8585f1a78f8c8b3..8c9a49db9810d40a353552b818a26cf2b25b11e0 100644 GIT binary patch literal 66172 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 brEx#ej`tk5_c%8dppSsX{?>yt-|~L||CBu- literal 51918 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 X|MxckNY;&AcHC`84t3Fa1|$9t Date: Fri, 6 Sep 2019 11:24:30 +0300 Subject: [PATCH 09/31] Removed useless --- .../OverdrawMonitor/Editor/OverdrawToolWindow.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 2ce70d8..109c123 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -5,14 +5,13 @@ class MonitorInfo { public Camera sourceCamera; - // public Canvas canvas; public CameraOverdrawMonitor monitor; } public class OverdrawToolWindow : EditorWindow { - static bool isEnabled => _monitorsRoot != null; - static Transform _monitorsRoot; + bool isEnabled => _monitorsRoot != null; + Transform _monitorsRoot; List _monitors; [MenuItem("Tools/Overdraw Tool")] @@ -80,11 +79,8 @@ void RemoveMonitor(MonitorInfo monitorInfo) void CheckMonitor(MonitorInfo monitorInfo) { - if ((monitorInfo.sourceCamera == null || !monitorInfo.sourceCamera.isActiveAndEnabled)/* && - monitorInfo.canvas == null || !monitorInfo.canvas.isActiveAndEnabled*/) - { + if (monitorInfo.sourceCamera == null || !monitorInfo.sourceCamera.isActiveAndEnabled) RemoveMonitor(monitorInfo); - } } void Update() From af0d660b7aa6362153cbc7ebb3e19f1500426b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Fri, 6 Sep 2019 13:38:09 +0300 Subject: [PATCH 10/31] Todo --- .../Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 109c123..b52660d 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -8,6 +8,10 @@ class MonitorInfo public CameraOverdrawMonitor monitor; } +// Todo: add ability to use overdraw tool on source camera +// I.e. source camera and compute camera can be the same. It need for canvases. +// Можно сделать галку возле камеры на выводе результатов, она позволит менять способ мониторинга: через отдельную камеру или заменить шейдер на текущей + public class OverdrawToolWindow : EditorWindow { bool isEnabled => _monitorsRoot != null; From 102c72249f957f924520a06bf0d39dc84167be50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Fri, 6 Sep 2019 15:05:06 +0300 Subject: [PATCH 11/31] Replacing mode added --- OverdrawMonitor/Assets/Demo.unity | Bin 35829 -> 35149 bytes .../OverdrawMonitor/CameraOverdrawMonitor.cs | 31 +++++++--- .../Editor/OverdrawToolWindow.cs | 55 ++++++++++++++---- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/OverdrawMonitor/Assets/Demo.unity b/OverdrawMonitor/Assets/Demo.unity index b419a840f44303abd192c2d317c8b1d189223800..a607763cc82f656610632373d996ac5e84685433 100644 GIT binary patch delta 418 zcmYL_y-UMT6va=f(Zu-i+GHryp)?sp(BW|2`Q3Lf@4n$p>aCknKs^VvO8`8cSFV(2sOQ3HCps+W zvW6eEYbQo2V0n^yRKqPA?q`5I`ZV<9$WYjc2a z02V8oQaP9hR+()o{r<5>Lyzo|9#6;!TV4P<%p3t%*bCV{Gq1xFmgja2<|%5ah$;;| zvNNFhKhPJLPXj+Jas~>*RA$`#`Pw~-n>%F5V+O*q?)WRdg!Rdi<-Uat_`S(;CZbw! f?C9T9TUXB=J-V>83U~El%hHYT!I+3Dt!w)aISorm delta 538 zcmYL`KP*F06vn?-eY{fiACa&K9V8^0XetpBD~-Q{GDwR?yOgw5X-Hc`6A44P5e7D6 z{mWa77L%|^j4YH`yGi(-+@>%2a^Ckl_ne%2?&G1Ud~C7-sV!iy48XNEw}dd{2Y1msr18}d8V0koKlQ5KOd0uiqNc2fA-`Qu|L+)IJ&VCUGqm1 z$#5(j7tMcUo@G+Nx&1Lur&)TLy>24IlBk-C!XgzS!9 zrN-5b1qKAHK&78wvR9$Ro!MQMc5_ONrzT|Px(V>oTPXX}`w|06WO+toB8Oz_yk&u& zDL5qqceO%qEWjmMglwC>mg@2$%77AC8p!@P?3OTM2fmqP4sOW;j%xA8{b9@~ej%$2 zQ<5y`tgh!%GL4T_x!jjz{VmnYsn8vXRD%enHKC=g;xwZfTf?O|%dCp2q*)ZRny_Yz U;w3Yn8gHqH&dqt(); - _computeCamera.enabled = false; - _inputData = new int[DataSize]; _resultData = new int[DataSize]; for (int i = 0; i < _inputData.Length; i++) @@ -73,13 +72,18 @@ void Awake() void OnDestroy() { + if (_computeCamera != null) + _computeCamera.ResetReplacementShader(); ReleaseTexture(); _resultBuffer?.Release(); } - public void SetSourceCamera(Camera sourceCamera) + public void SetCameras(Camera sourceCamera, Camera computeCamera) { _sourceCamera = sourceCamera; + _computeCamera = computeCamera; + + CheckComputeCameraState(false); } public void ResetStats() @@ -120,12 +124,16 @@ void RecreateComputeBuffer() void LateUpdate() { - if (_sourceCamera == null) + if (_sourceCamera == null || _computeCamera == null) return; _computeCamera.CopyFrom(_sourceCamera); + + _sourceCameraClearFlags = _sourceCamera.clearFlags; + _sourceCameraClearColor = _sourceCamera.backgroundColor; _computeCamera.clearFlags = CameraClearFlags.SolidColor; - _computeCamera.backgroundColor = Color.black; + _computeCamera.backgroundColor = Color.clear; + _computeCamera.SetReplacementShader(_replacementShader, null); RecreateTexture(_sourceCamera); @@ -148,7 +156,7 @@ void LateUpdate() _intervalFrames = 0; } - _computeCamera.enabled = true; + CheckComputeCameraState(true); } void OnPostRender() @@ -187,7 +195,14 @@ void OnPostRender() if (OverdrawRatio > MaxOverdraw) MaxOverdraw = OverdrawRatio; - _computeCamera.enabled = false; + CheckComputeCameraState(false); _computeCamera.targetTexture = null; + _computeCamera.clearFlags = _sourceCameraClearFlags; + _computeCamera.backgroundColor = _sourceCameraClearColor; + } + + void CheckComputeCameraState(bool needActive) + { + _computeCamera.enabled = needActive || _sourceCamera == _computeCamera; } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index b52660d..13936a0 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -17,6 +17,7 @@ public class OverdrawToolWindow : EditorWindow bool isEnabled => _monitorsRoot != null; Transform _monitorsRoot; List _monitors; + bool _replacing; [MenuItem("Tools/Overdraw Tool")] static void ShowWindow() @@ -34,9 +35,7 @@ static bool Validate() void Init() { - var window = GetWindow(); - window.Show(); - window._monitors = new List(); + _monitors = new List(); var monitorsRootGo = new GameObject("OverdrawMonitorsRoot"); monitorsRootGo.hideFlags = HideFlags.DontSave; @@ -57,15 +56,30 @@ void TryShutdown() DestroyImmediate(_monitorsRoot.gameObject); _monitorsRoot = null; } + + _replacing = false; } void AddMonitorForCamera(Camera camera) { - var go = new GameObject("CameraOverdrawMonitor"); - go.hideFlags = HideFlags.DontSave; - go.transform.SetParent(_monitorsRoot, false); + GameObject go; + Camera computeCamera; + if (_replacing) + { + go = camera.gameObject; + computeCamera = camera; + } + else + { + go = new GameObject("CameraOverdrawMonitor"); + go.hideFlags = HideFlags.DontSave; + go.transform.SetParent(_monitorsRoot, false); + computeCamera = go.AddComponent(); + } + var monitor = go.AddComponent(); - monitor.SetSourceCamera(camera); + monitor.SetCameras(camera, computeCamera); + _monitors.Add(new MonitorInfo { sourceCamera = camera, @@ -75,9 +89,14 @@ void AddMonitorForCamera(Camera camera) void RemoveMonitor(MonitorInfo monitorInfo) { - // Todo: add canvas support - - DestroyImmediate(monitorInfo.monitor.gameObject); + CameraOverdrawMonitor monitor = monitorInfo.monitor; + if (monitor != null) + { + if (IsReplacingMonitor(monitorInfo)) + DestroyImmediate(monitor); + else + DestroyImmediate(monitor.gameObject); + } _monitors.Remove(monitorInfo); } @@ -87,6 +106,11 @@ void CheckMonitor(MonitorInfo monitorInfo) RemoveMonitor(monitorInfo); } + bool IsReplacingMonitor(MonitorInfo monitorInfo) + { + return monitorInfo.monitor.gameObject == monitorInfo.sourceCamera.gameObject; + } + void Update() { // Check shutdown if needed @@ -134,6 +158,17 @@ void OnGUI() GUILayout.Space(5); + bool newReplacing = GUILayout.Toggle(_replacing, "Replacing shader for cameras (for Canvas cameras)"); + if (newReplacing != _replacing) + { + TryShutdown(); + Init(); + _replacing = newReplacing; + return; + } + + GUILayout.Space(5); + float totalAverage = 0f; float totalMax = 0f; foreach (MonitorInfo monitorInfo in _monitors) From c75fafda69868db90aa9a3d1fc67a279dd62cd8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Fri, 6 Sep 2019 15:29:49 +0300 Subject: [PATCH 12/31] Todo --- .../Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 13936a0..a917b7d 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -8,9 +8,7 @@ class MonitorInfo public CameraOverdrawMonitor monitor; } -// Todo: add ability to use overdraw tool on source camera -// I.e. source camera and compute camera can be the same. It need for canvases. -// Можно сделать галку возле камеры на выводе результатов, она позволит менять способ мониторинга: через отдельную камеру или заменить шейдер на текущей +// Todo: remove replacement mode - or do it for each cameras as it can break render texture mode public class OverdrawToolWindow : EditorWindow { From 02d48b676d4f9b40babf15d678541fd7be39132e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Fri, 6 Sep 2019 15:45:29 +0300 Subject: [PATCH 13/31] Fix --- .../OverdrawMonitor/CameraOverdrawMonitor.cs | 17 ++++++++++------- .../Editor/OverdrawToolWindow.cs | 2 -- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 56b2803..584b6ae 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -52,8 +52,9 @@ public class CameraOverdrawMonitor : MonoBehaviour float _accumulatedIntervalOverdraw; float _intervalTime; long _intervalFrames; - CameraClearFlags _sourceCameraClearFlags; - Color _sourceCameraClearColor; + CameraClearFlags _computeCameraOriginalClearFlags; + Color _computeCameraOriginalClearColor; + RenderTexture _computeCameraOriginalTargetTexture; void Awake() { @@ -129,8 +130,10 @@ void LateUpdate() _computeCamera.CopyFrom(_sourceCamera); - _sourceCameraClearFlags = _sourceCamera.clearFlags; - _sourceCameraClearColor = _sourceCamera.backgroundColor; + _computeCameraOriginalClearFlags = _computeCamera.clearFlags; + _computeCameraOriginalClearColor = _computeCamera.backgroundColor; + _computeCameraOriginalTargetTexture = _computeCamera.targetTexture; + _computeCamera.clearFlags = CameraClearFlags.SolidColor; _computeCamera.backgroundColor = Color.clear; @@ -196,9 +199,9 @@ void OnPostRender() MaxOverdraw = OverdrawRatio; CheckComputeCameraState(false); - _computeCamera.targetTexture = null; - _computeCamera.clearFlags = _sourceCameraClearFlags; - _computeCamera.backgroundColor = _sourceCameraClearColor; + _computeCamera.targetTexture = _computeCameraOriginalTargetTexture; + _computeCamera.clearFlags = _computeCameraOriginalClearFlags; + _computeCamera.backgroundColor = _computeCameraOriginalClearColor; } void CheckComputeCameraState(bool needActive) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index a917b7d..2d2922b 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -8,8 +8,6 @@ class MonitorInfo public CameraOverdrawMonitor monitor; } -// Todo: remove replacement mode - or do it for each cameras as it can break render texture mode - public class OverdrawToolWindow : EditorWindow { bool isEnabled => _monitorsRoot != null; From 75d0abc7b6f464c9383680ff96c82fd98a3026d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Fri, 6 Sep 2019 19:17:33 +0300 Subject: [PATCH 14/31] Wip --- .../OverdrawMonitor/CameraOverdrawMonitor.cs | 30 ++++++++++++------- .../Editor/OverdrawToolWindow.cs | 24 ++++++++------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 584b6ae..534fb8d 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -55,6 +55,7 @@ public class CameraOverdrawMonitor : MonoBehaviour CameraClearFlags _computeCameraOriginalClearFlags; Color _computeCameraOriginalClearColor; RenderTexture _computeCameraOriginalTargetTexture; + bool _computeCameraOriginalEnabled; void Awake() { @@ -73,9 +74,10 @@ void Awake() void OnDestroy() { - if (_computeCamera != null) - _computeCamera.ResetReplacementShader(); ReleaseTexture(); + if (_computeCamera != null) + ResetOriginalComputeCameraState(); + _resultBuffer?.Release(); } @@ -108,13 +110,13 @@ void RecreateTexture(Camera main) void ReleaseTexture() { if (_computeCamera != null) - _computeCamera.targetTexture = null; + _computeCamera.targetTexture = _computeCameraOriginalTargetTexture; - if (_overdrawTexture == null) - return; - - _overdrawTexture.Release(); - _overdrawTexture = null; + if (_overdrawTexture != null) + { + _overdrawTexture.Release(); + _overdrawTexture = null; + } } void RecreateComputeBuffer() @@ -133,6 +135,7 @@ void LateUpdate() _computeCameraOriginalClearFlags = _computeCamera.clearFlags; _computeCameraOriginalClearColor = _computeCamera.backgroundColor; _computeCameraOriginalTargetTexture = _computeCamera.targetTexture; + _computeCameraOriginalEnabled = _computeCamera.enabled; _computeCamera.clearFlags = CameraClearFlags.SolidColor; _computeCamera.backgroundColor = Color.clear; @@ -164,7 +167,7 @@ void LateUpdate() void OnPostRender() { - if (_computeCamera.targetTexture == null) + if (_computeCamera.targetTexture == _computeCameraOriginalTargetTexture) return; int kernel = _computeShader.FindKernel("CSMain"); @@ -198,10 +201,17 @@ void OnPostRender() if (OverdrawRatio > MaxOverdraw) MaxOverdraw = OverdrawRatio; - CheckComputeCameraState(false); + ResetOriginalComputeCameraState(); + } + + void ResetOriginalComputeCameraState() + { + _computeCamera.ResetReplacementShader(); _computeCamera.targetTexture = _computeCameraOriginalTargetTexture; _computeCamera.clearFlags = _computeCameraOriginalClearFlags; _computeCamera.backgroundColor = _computeCameraOriginalClearColor; + + CheckComputeCameraState(_computeCameraOriginalEnabled); } void CheckComputeCameraState(bool needActive) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 2d2922b..7534133 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -104,7 +104,7 @@ void CheckMonitor(MonitorInfo monitorInfo) bool IsReplacingMonitor(MonitorInfo monitorInfo) { - return monitorInfo.monitor.gameObject == monitorInfo.sourceCamera.gameObject; + return monitorInfo.sourceCamera != null && monitorInfo.sourceCamera.gameObject == monitorInfo.monitor.gameObject; } void Update() @@ -167,20 +167,24 @@ void OnGUI() float totalAverage = 0f; float totalMax = 0f; - foreach (MonitorInfo monitorInfo in _monitors) + + if (_monitors != null) { - using (new GUILayout.HorizontalScope()) + foreach (MonitorInfo monitorInfo in _monitors) { - CameraOverdrawMonitor monitor = monitorInfo.monitor; + using (new GUILayout.HorizontalScope()) + { + CameraOverdrawMonitor monitor = monitorInfo.monitor; - GUILayout.Label(monitorInfo.sourceCamera.name); - GUILayout.FlexibleSpace(); + GUILayout.Label(monitorInfo.sourceCamera.name); + GUILayout.FlexibleSpace(); - float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.AccumulatedAverageOverdraw : 0f; - GUILayout.Label(FormatResult(accumulatedAverageOverdraw, monitor.MaxOverdraw)); + float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.AccumulatedAverageOverdraw : 0f; + GUILayout.Label(FormatResult(accumulatedAverageOverdraw, monitor.MaxOverdraw)); - totalMax += monitor.MaxOverdraw; - totalAverage += accumulatedAverageOverdraw; + totalMax += monitor.MaxOverdraw; + totalAverage += accumulatedAverageOverdraw; + } } } From 9476da5c78d8f7d324bdb1e3fb0e6df29efb33f1 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 6 Sep 2019 19:21:54 +0300 Subject: [PATCH 15/31] Create README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1271ec6 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Unite2017 + +-- Todo: make replacing mode full functioned, considering any post-effects in original cameras. +The goal is to leave only one mode: replacing. From 0ae02279776452c5a4c7b5275736639f4fb630cf Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 6 Sep 2019 19:22:15 +0300 Subject: [PATCH 16/31] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1271ec6..f9baaca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Unite2017 +# Todos --- Todo: make replacing mode full functioned, considering any post-effects in original cameras. +* Make replacing mode full functioned, considering any post-effects in original cameras. The goal is to leave only one mode: replacing. From 44ba6e4f38c99c83ed3a3a0071245744229ea75b Mon Sep 17 00:00:00 2001 From: Kirill Belonogov Date: Fri, 6 Sep 2019 23:36:02 +0300 Subject: [PATCH 17/31] Refactor --- OverdrawMonitor/Assets/Demo.unity | Bin 35149 -> 35149 bytes .../OverdrawMonitor/CameraOverdrawMonitor.cs | 35 +++++------------- OverdrawMonitor/Packages/manifest.json | 4 +- .../ProjectSettings/ProjectSettings.asset | Bin 66172 -> 66272 bytes .../ProjectSettings/ProjectVersion.txt | 4 +- 5 files changed, 14 insertions(+), 29 deletions(-) diff --git a/OverdrawMonitor/Assets/Demo.unity b/OverdrawMonitor/Assets/Demo.unity index a607763cc82f656610632373d996ac5e84685433..e978aa913f91573888075ca3e9605f053e064b42 100644 GIT binary patch delta 14 VcmX>*iRtVlrU}xFCL3i8I{_=D1u6gl delta 14 VcmX>*iRtVlrU}xF1{-AzI{_<^1ttIh diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 534fb8d..b9791c2 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -22,10 +22,10 @@ public class CameraOverdrawMonitor : MonoBehaviour const float SampleTime = 1f; // The number of shaded fragments in the last frame - public long TotalShadedFragments { get; private set; } + public long totalShadedFragments { get; private set; } // The overdraw ration in the last frame - public float OverdrawRatio { get; private set; } + public float overdrawRatio { get; private set; } // Number of shaded fragments in the measured time span public long IntervalShadedFragments { get; private set; } @@ -75,9 +75,6 @@ void Awake() void OnDestroy() { ReleaseTexture(); - if (_computeCamera != null) - ResetOriginalComputeCameraState(); - _resultBuffer?.Release(); } @@ -140,8 +137,6 @@ void LateUpdate() _computeCamera.clearFlags = CameraClearFlags.SolidColor; _computeCamera.backgroundColor = Color.clear; - _computeCamera.SetReplacementShader(_replacementShader, null); - RecreateTexture(_sourceCamera); _computeCamera.targetTexture = _overdrawTexture; @@ -163,12 +158,8 @@ void LateUpdate() } CheckComputeCameraState(true); - } - void OnPostRender() - { - if (_computeCamera.targetTexture == _computeCameraOriginalTargetTexture) - return; + _computeCamera.RenderWithShader(_replacementShader, null); int kernel = _computeShader.FindKernel("CSMain"); @@ -188,25 +179,19 @@ void OnPostRender() _resultBuffer.GetData(_resultData); // Getting the results - TotalShadedFragments = 0; + totalShadedFragments = 0; foreach (int res in _resultData) - TotalShadedFragments += res; + totalShadedFragments += res; - OverdrawRatio = (float)TotalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); + overdrawRatio = (float)totalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); - _accumulatedIntervalFragments += TotalShadedFragments; - _accumulatedIntervalOverdraw += OverdrawRatio; + _accumulatedIntervalFragments += totalShadedFragments; + _accumulatedIntervalOverdraw += overdrawRatio; _intervalFrames++; - if (OverdrawRatio > MaxOverdraw) - MaxOverdraw = OverdrawRatio; - - ResetOriginalComputeCameraState(); - } + if (overdrawRatio > MaxOverdraw) + MaxOverdraw = overdrawRatio; - void ResetOriginalComputeCameraState() - { - _computeCamera.ResetReplacementShader(); _computeCamera.targetTexture = _computeCameraOriginalTargetTexture; _computeCamera.clearFlags = _computeCameraOriginalClearFlags; _computeCamera.backgroundColor = _computeCameraOriginalClearColor; diff --git a/OverdrawMonitor/Packages/manifest.json b/OverdrawMonitor/Packages/manifest.json index 03901ba..dbc7c7f 100644 --- a/OverdrawMonitor/Packages/manifest.json +++ b/OverdrawMonitor/Packages/manifest.json @@ -3,8 +3,8 @@ "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", "com.unity.ext.nunit": "1.0.0", - "com.unity.ide.rider": "1.0.8", - "com.unity.ide.vscode": "1.0.7", + "com.unity.ide.rider": "1.1.0", + "com.unity.ide.vscode": "1.1.0", "com.unity.package-manager-ui": "2.2.0", "com.unity.test-framework": "1.0.13", "com.unity.ugui": "1.0.0", diff --git a/OverdrawMonitor/ProjectSettings/ProjectSettings.asset b/OverdrawMonitor/ProjectSettings/ProjectSettings.asset index 8c9a49db9810d40a353552b818a26cf2b25b11e0..d1692ddd017c21d08909f6b28807dad1916907b2 100644 GIT binary patch literal 66272 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 literal 66172 zcmeI5cbsHJ)%Wl0E;%R3uw+nJHjokM**q}2JM7E`R9Jdu?#wjX)7^CU?CyX-1BxIZ zNrEC_5kW)=f@E<-f&>Kw36en&MG!>E;XS{qQ+?~))3-10^Zxlh_1W&}uJ5h$t2&kI z-kA-8_nr<~Mtw5~g7t&oy|?LSFme2Zy~j-)H@Mw%emH(L^VJ;SpHBZ4`i}gq zj`$4TY;fuSKwn{h4Mb<~W&`+4^6y{iE9!4eMAz_U1No$73j(TdzFvMr75#)bU zvH|<3Ls`G0gM*15K`+Fwqqx;a`-6Sco@(@kLtKA7if^V*ZMmub28y=?ca92bpHMt} z_a%Dy0qq0jTa_}8@@+^jYRdIXz{gS9*CUR)-$?P6;926EG2hsv9Bbb3hV;wH{+HSQ zCWeeP?|5^3XXfY!K`_?5;|=vUpVE&b4&QHP$XN4^H)cV@m*0`Fq};u{F(aSqm)04FS~=+@MZ(07vmdEX+bbz8Q$aL8_jbffWJz% zF}|UG_5!Wp&4!j>OmGg>-+Oco#+SVvsPh%X__7bh%kgCeF}~P&s8+WE`^!o3W3rF_ z@l}uv-fTemF}{r%ZT0tapw8v^wm-#7eL21zpt!Za72D6SUH1d^25&aF z_W2NTw9f+(U9$}D@%D*zhDm^#opK{*45F2J=I~Gtot^{x2m?MGOuD%HYigSN`ib z{UQfq`~l{NC-7&OXA}4v%)1l#=gfN&_y)9ahyQyM_^!-D@Jw`(EC0d7QU1ONnNP0# zIZof7z?U%}NZ=<>O6 zj`67oRP(u-kNNL5;%L9$jF9={_yf$3L2Sk# z^9GLn_0!CcjZmFq{C$n}k3($6AoJ$@kIfJ2r-s-J-fVDuZRW=#I%65$9p9e$35d+# z%?5CkZ(rgl--!;y_$=n%O5lex|8@eeFh41QpUV6@4LJOB32~JFWbh2$Y(W0u-y1mn zDGqoX<$Ij@cN6%V%uh|=E3RVwb6Nu5lKJTg`~c?POW=nyKLb1yUF7QTIN~V(nLru5 z*#Le4v7gY3hVbAlMAz_U1J;M|->*6S*$%|`Z<(Ky!0%!H{RI9X^K%pUBh1eO&qNm? z{}}(CB#!a#e4v`o<@k32p=RUX55S}3^jVI7KUCcAUtVSX3z4>lHyh&X!yh3!V;SDv z`p=!uTofVm34ECL2Us7jPB9lNK1QtHFCi}X2V=zk2Qa@pfp;_i33w*D2~eaceg72EBY3mH`TrW?DF0Q6&fv`kaJ2v1h@)Nn%z-+$_8%s`7R6nS z_z}F>5V!wx2Rsh@PqF=9B=G+*zb1jNu$s-^wcwfPBB#GOag^`62$@fg@5B6h#YY7T zh#gKZ`0tnC8NAux(mVg%;DEtSL~&}%fs+>Y20yxD;C z(0`6N%72FgG5#;+zfRyEGrto&6I}#-*k5gRYyUTj+x6Mj%tsRVWaht(Oq)-x|1M;H zS3>$C^WQb#INv#jIL7C@!E1Q4f$}fTckV%S#xlIS`gh~wy%92>96y4rqWURj zl0NwF6Zpl`H|+O8u%lMuOR&YC~-MIu4v{L=l?%}!v=?zU`28N;4zAq-0H7NZ20|2--G#U34Av5zct|Se}OpK z-|OJD=%RT2|DBL5zwCei0FRQ>XSqInLvcI)4wF98{S#?3c(cKk?-b7en-0YI#mwJo zz}u-rx3m3!CGf|Xzn#GU&Yb#+2yXoUkoms_MVV~*R$J4S@7)BxHS_;~XQGQ-`M*kh z3`PGpLgtg>hcJJy0Z08FK^*n_K6ov9efgj}*7<_e9R$$6i`| zbpAP;IP89c$V?6ICV(UVH*)?zMQqI=^A^wlXB68k|DS_L$?3DqKcM8c{{G1MGtU%= z^p?NKe3YNqXj%Rt^U(=>O*xYy?@fh_Hm!t4^%&5|FxP*FYT|cxV2wk`)j1wUsI)*_SaI}+CPo$ zubpCl9hF|%UsrK!|Ho_}_u2gr_s{w&y|ll9;@19MZ2wCs_BT}NrTvW*xAtFV`x~d& z-$bRC_BU1B+HYCgwog1~;D@+>HdpDT{Vf!?_P1jDTc+3_tI|vRTPbes@5A=DPO-m@ zN-ynitGKoQHMYN9iv8_XdTD~xNwJUn=rX;u zzl-A5{*7!O&yn~c-hOsf>81Vgid*}Sv;7Gv_9v?J(*7jHt^Ie|{%$GucUS49{XG=7 z_Saj-_OCrt?C+)0OZ$5(Ztd^D_V-D#zpqL!?SEBqdw#ed*~j{2KiIEDHB9v@_GkNp zXM8T#rw4$00rNlRN9>O=KV}q{=ZCGtW&4!phm#ez`9GNRKLz${Q3m4qp9-Gwxy=7G za4*F3KV5N|{~5%a<$tE)Hvj#c|2EjKMHz_a|3L7J&t?7(0{22Z|Luy){LdoZEdO{8 zOmdt5qdEU`V80e+AfA6bC)OZuc)yQPBkMdO; z*vI*MmidrLA5{Rxhvk&MkNFan-tuFZFKxiFzdnmN=GSH5HQ&A*UxtY{+g~3A9wn#G za({ia;k2Usd^&C+97Wem#i*e`w7g)d6z`pPQ z<4yXyd6U28p%GaOl9Am4?oWOXDKev=g%fC%P-G|&jF9JN1x^W zf$u9m#?1e(u>QG7T#F3E&&SULAMv@=Ki?Op%b@=bS#;&UK-phOtgnBd_{w5^@k3>Q zWpV%ILY00MasK}!#a9*j7ZI2KT}|j;3?3DqKCdS3zg(iY)pz$_E=A&6WS}LG`pc0% z<8!HhnX)hSf2_D|-*0jKUas_I|N05|h_5f(_Z8q?K>J4jb@h9tvOh-n_ou{V`Rw!E z?*7JA;KKe2!oNRL+&(|_36*aQrMucsOy4oS;Cz0q^{w5XgJ)(>1e`|{VZOXpXzg=-V|366jXdib#-wUXJlkq6SHl;X}?{^7& z8|HT>@UJkxCxK65ey`%Te$S=+p?%*6UZWR>c>Df6@n-GoesGtVe#YDP1BzSyD_Q?R zB(6mU;`$FEea7dqeg8q(m+kA1ire;c2kFEA4@2Jzu6;ep{E-G6>(94|WBvK4;&T1@ zC!=4hE1>@U->-X2aqFKkU$XV{IP6Es$-i>^`7_dI>gJ9A{r>-iu`l_-WD(;(Oh2i( z)!&HqpTb2oKfTm{8tF4Wm-X`(Wnb3MGm6{se`nH%^s{*Lg6qGtnLn4nL*~yX@Cx%6 z68K5XUrgXXWd2eDzlHhB;F+iZ$iMmhE#l~ZuYlKlF8%vgm0tSyRi!^Vc$sXYerbpd zUQ^uezb_qS+yCDZxcmLH*Bfxt{z(Jv|L=;+-~ajtak;;jzyI}y;_e! z7l(NNeHVPhQE18f{SUYoz?YG2w|@JtvTye%r?CEeip%}U`^07cmHUGaz=gitpM0pe z-JhJ#`X3>2Eiw?_pL`6S@wwFh#22T_M$!J{5-$Hn^!?KqZ=v64NB#bsu0j2Nrnudo ztV;1{AD`1tS-&fb@tFi;d?mB~xPj~=eG73vKp*4p?aW6d@Vl9hR@}Cq2Z^Koi~+Aj z3i0-{0{Do}W&2rC*&iLeMz&r1SxMQK?Pq1hW&2r$c(eAiDtMGV`YhYeYKqJDgWp4z z<(K+vfR981@#nAB1or~?M_&2YQubFe9+UEv}%l7#tWnZ?>4HLL)pBpvc*dMLS{@ED378S51 zkp9_(xNINNKbwL_$?5YdV*j|A;EpeK~(_0X`B5#K%{97E}cAiDcXLkFm>pbxF5BnU#AW&A__htWmxFl!*jDi|W`8`L@`!ZXLEj6wztm1OvVi&a;F;(GxBken z|8@Yc`CR&MNAMA!%kgC=a4*1rCwl(-va&Dz_Z7vZ|L}JPn)z=RaG@{9mvM^Q@#R7; z|E?+eT#)t#Qew`4v8JQE4PKF)u~5y$*D6TIegIX<*0F5BOM;9kJ^;6HzV zkVv{k96OFz8Bp5 zHIMysD0nTpAU-}G20r3***+I3`=f*7$hMpR4_Ef(_?T5(`lp+CGyn8}N7^YV|AP{^G=04-FgK_ zIenJrcl5iqF>du=XZ@p6^p8%_|E97p^^Z~9>bKAVJIa4-ivDpa`ZZ-=>L0JT)!&Tu zPcZsX1;qQ`iALY&(*NHw_9ge*|F;#l?Qdt&A4Bza67;ENP#vVOj&xUHWD zSpSR^{WDYa&rCHLFMd5T;8cUk}Z6#WZQ z^nal2%kuwFajU=LX14w>Ows>QivC5)zSO^1ajU;E>tB+he`$*Ta%ErYU#7U#-`VP?6u0^z z>tCCqe_e|H^~%1~|E1zqe;MoFkfMKMivF*ZeW`zw;#U7u*1tJL|CSW}Ta|sOf1Bb~ z|6`VQ7 z6}S3-Vg373^nahCf4{OX^&e2&>c7SM52olpl%oF!Wnb$5QE{t3YI8gPJe;EcNQ(ZW z%D&Y9lj2r?W7dBxMgQ>>{XZ-FQvV6Xt^Rn{e=8e<4Nx#T5OQlzplHvf@^Mi1lAd(f@0T{;SHq)PGHJtA85n|1CxT z^%VWTEBjLaABtQ3AG7`&Df<6R(SK9fm-=rhZuM_t{ePwCzn!B0jQWKiI;KzaOXQf0CmAsj@HiKU3W5 zZ^Zhar|8phN7M6>7Vv2MMW3bqD8;S*c-9|H+}pmY{U83$aZ4r=h@bDSpzKTi6&1Jo zGg*Hn;?49|M*1fDt0?81Vk6u0)j%l6k#vA=;zFYSLxaclnqw!dMD z{f$(5X@6tIt^F(5{w68*={a(0AIbH*nc~*|jckAO6#HAK^fLcjDsJuH#rDUh*vH=q zZuVf)*p*xy#Am-*jLaclp1w!eLf{T)<#X@5t>t^I$p{hdxAymB`+KF>-&>`Z_V-cT+CPx(@0()(t17*;zn|jP{-JDt{}lTN zsPxi)MsaJu#P;!b{QVH0zb32n(*6|1t^E_({?ruv(^PtCf4bt<{&{SFMv8rU79!Sv zr2RI4^ru+{dUEz{X5wHtQ7mRReEWEj^ft-!)(7J#s0x6y|h19aclo& zw%?gzAO8Chp8>e`$zrCzFfZ#C~ozS7;E)&Df&mG=r30GrG8#U+5CSK`XasMn=wBo zflp$7Yy$6Kep~~N@-HHe^4Gwdl>d0*&B}iQxX8aO|A~s*@}0o>`xf*?ddn|m{_O;Q z5A%}}_>0WHlfd6)esTg|X=__QrzG%=nSa-Cl)(n~{K3x5Pfg$lFh4DU&t-mk0uPyg zFM%&(entX6mHC+o{9@*3CGhK+pPj((W`0frf0Ft46Zjj<&uzfbem^IU_In<9jh#tH19yR{vt+ z&Gaup`X>69f;Z7$uDI1dWE-o08S!TNKSuf{`j>+@(f^6!R)6U>R{sj(&GfHC`X>56 z1#hB%mEu&xkkEzZ&VA=>HtNiT*DXxB541WA(2g-c0{mq;H~s9e5M{>lL^9 zk8Wf2e@VQV{tZaqME^$cCi=fp+&;hfuWf9)n~3`v!1#;jvsT#F@|zR*X3TF%;CnK^ zHGv<-{I&!>%=~unObwYd!Oain5g$V_cQiNTe_!+#H%^o~N)_OD}zV}APsc+KbX`N2OD zZ^`&v&TkKcN0>g#`Rx(Kt^W5||54)2^#6qPP4pjA_ND&gire+yW$d3n6ZeDj&kdZv zC%|jb74iB1N!ZW$T>9rJ@CYmaJgvCR-w5aLFU0-e^7jzu?-}q~bVWRW&%%Dj=Q4lK zfk#;7?|H>-{XNI}FA#60|02>i(SJ$Vm-Y9u;?_TJuzy}5?g!_e_c(ul1+PU{#QpOs z>}Px~{qq`lgq45(rnt@DiaXf;_d0PuAb%LYHfR3#1im}-eM_85rUy9rIaSH3dO}v@@J4oL||KG~KY#;9`ZuOV5{(p!! z)Bi8hH_?Aj*_Zn7D{l2~Vf_z?H`D(R>6_?(r0h%mj}^E253&9y#GC1Viu6tNKU4Ol z{^yEY{a08&7}H$8g?KalQQ&04h4}n3T5+rYKI@N3(O)4&e??_qmVYJ1t^Vpe+Wxn4 zivB7o`l~AYQhznYt^TI0zj})P8Y%i~D*IA@Eyb<=_N>2livBt&`s*tDQhzRzJu3o2BS)o}#~nvM=?wRNT&Q zN0UCJ$KuTkZhk(G^S2dvExI5+KW$CC+4!&xc$8etPunVP^LIVxZ#&|CaP#v+%<=zN z`@!*7neULmKVrUP0$*z<>z|zx__oZyoWS>H{*?qii}}t8d=c|q5_pyQxCR{Si|-J} z`eIk`TI8X4{f#Hyto|l|N6A(FO;p_Ozt16kq?<(C52zpPua-04ErDOleD?%?2lG7= z_#@2sOyI9D-z$NC$b9bvzRH(v`SwZR+cMubf$zuss|mb|`F;sJ#~lApdyt7d?Am{g zIM4wN(qnylA@fWEzm<7w1CI9fAaS&>$>23#U+zz*5O3DLrh-SwRr{KzxE(*AVg2dE zgC_bjkUkR$#P1KzRQBcg*`~PFe~0xCBpx);KM3iY=(j8TQh%1>w*Rb5e;^Tln@!vg zu77RFd`<%2m3c=3pUV8;1ipaz+yp+zyfcBHz`QGgU&wr30>72{eDF-<0oVSYAP%&k z0jD#C+K0r^eiwp|_~~W)Jw$Q2|N0uZ7tp@YKaQYQ0sCJEuSFNo-}exIU+EjfW&4nS zfA&!DC^>zWe}DEc#clgseP>&~MZ}wx?{K9r%a>L5W&7+_yd^ki6#Z>J(qEmVd!X+H znt#Oeue~OHt$qdgIP%|mROpo{HdNf&--qq@rP%LR>81Sv#clsuME2qL9PDT4#R2UL z^?MX?)bA1CHAj&~wx7kYpYgf$Zyr3t^jZ3MP;t9HI-T?h1qHl$!L|R3m=_!HcFOS$ z#GzYC;14iAGJ!wCybPX+GT{94CUN+u0$%gE^iP#|Gye>MN6D3cmMCuPr)3wbzm#~; zM1L95XCi_4{4lKS%lbJ=ajU-}>mN-#Xrlj3q;H~sjIuBFk5$~Z-|a{remjo1A5cH- zl%su_*T6H;6|VivBMx+Y0?#u)A;JE!Z2!arem3)OHL(8<{iGWT~>E++g`L0SY|Nh6R#9K1HzWnkP%Ef6fH=0^0wUG=97J>nxRC{{5V@6_@2dhq!Ftvi#o% z7y9z=!m z{qqyz&HQr(c$9tRpDPuY{`o2Nz2N-w8T;od@LF_1+&@24T>9r~WncQ|=PJGQ&o300 z{<(&DGyhx*9%Wzo=Q_ouf3AnV7o30AA7{tsUxL@73*!E{L2>Dy8u!_?c+YhW&8L&ap_+bfa0?MJP3U+z&|*D`8oUl zp#**h^FK7;Xg_~q|NarY774`r-^0XZ`J{gz0gsZa_VcLXw*9`s`hOxGG|_(y=`)c) ze0}}6vM<~3pB1;z1MrXOO;${h6;*nhf0w_N61dCX$_d=% zZxzMu`tAbC9~B{3m3X}Vu>ZV+IQrLW;3M?n5byu1gV!8|P1(NJ0QW+?|F5ai%k|G% zip&1JHt}ZtdmV70FZ=hpijOh-Z#TZIm!iME;&Oc10Ne}M|N7?>UsCB;GV5!MfAIf? zip#&>zY%d+e);$NHwG8_^6&R=qPW#}<=-?#f3p<*&6RzrzlGv_{=w~UOQkQz*RhJr z_O}(d7vk-2Yn5KMzikwk?QdJ+(*Lslw*wdYvi)tZxSZd1NYURhMSmw{U+RBZaXY`g z!T$e>(wF|`E8uy_V4R|!1}wU=#K{C5`xPuZ9CzrW&Ee=_SI0R39BekMh~RoR#NlNGo6ovc44MSp6F{xoG@>Q7hP z>i4q#j1>KuDf(^7zSKWZajQSX`Uj=xx2NdOQud|(Y{jkqNvuC7MZY6O|6pZb>d#f& z>YvN{ohkZVDf;u2eW^cRajUzSKWV zajX9n>n}>tKRiW0tL#htZpE$s`>fxSqTidMA1eD&zfW@Iu2IAxQ5zx>0T#nz1!6QtcW&ZPu+xdGg>kp>r7r;j%f%yDg1or|s z%IEIym6Uz?e9Do;W%=auDP?eBUp}8wQQVfVo6A>K`f`0V1U?c8#LKq?+zavYEmihq z`IZrHR=#0yVPBT-D8=piyUOJ|TItL3eN%DSevSe60?Oy_e~wk@<@)NroJ9VC-EV(3k!_UGXu&{Zw|hzxELR3`L)%^j8x9zth=@uVD0F;`%u!MgRLL`sXV9QvW=~ z?fCRM=_CK=L*EN{eh$z7z03K#0K67mK<5j>KR*D^_}u>gg3dobRQ4^$_zL|C6}SHR zg!O-vqJI(bX7zJ1c$7W*Ed6td;?_T_(SM+g{9OutFF5~fNgVUva`2Jpg1CP!1JC$e z`sc^WzVy%Kirc?0G>-IFC$m3M_T}?~R}iPBUe5r|Fa75SuT^R`5(@0QtL?f*t`yFS^P?4y5=!2SrmIK=Dk zx8OBLQ5IQ$cY%8W++UykPNkRg=iQ3S`STv)vi->Q$-Ur0U(TQRDK4Mi_&xN!fc~*3 z`KFcqb3b@3x&ZA5?cdF>4=6rb%pVUzKjWvj?ZeF<4}nLRK93Re>mL-i`Ui3T{+Ocw zaEksT%D&WpRB@|+DC_?T`Xf;RsQyLyA4}1HT-lfUe^$IDD3SK@tp5b`YrekC->J-> zR9xooDcH~W>1F*qt@LI7{-XFO^Zd_wq>p-i2KvIjy?=B$^Jg3In~2@V`F{?4gkBub z{xCj2P8{RQ^Wa`^?ek?$|AI;{%l{(rX61hgT-cZ8e_3(6|9_kHUx9utG7ukM{;Krl z`0}cC60msJPX?o%KIT(f>F_ z{}W|j>VK-ZZC?+NKK%L_^u6HP*OSaYPv9%;!S%ADmzX}f^xH6RiSdqnb~r3|hSh4W z&|e8kA8G0 zEO!-$dIrMEf`PCwr@PWKR1SOH1=9-I?tIueRH?wzowwwzK)oe9aECkhTxj(E<4VUD4!eFQp&M8a{DOvB_uu>c<_k@)o+tU+L zPKtve*Y4VDGNggQ(4Y-PY^ZgRF~aeuiuROp>j%#($3&>wUMSzI+xTsp6is}B2@ z>-hF!e=%lr&XTZ9RWhfP?V;o`*jmU9T3^VFz)oj%I3LEYpIXi?H9nP>NBOQB{93U% z7)uPJ&3I&`ohqTylP!gnN71CI z)m6X9B%}NxlVR6zi8@5CSV2=X>8N0Bh2C6GwpuLDDYO@}y<|9`szWDExvDo;p>{uv zo|Yd%XPlbLl2c%RUL~ADUAZ{eS}H|VJvki6F3C}!petJQ`Qp-t zbJeM~w3CPW`e;}ymBUI{UJ_0()A-SC;#%{ifowod!9X@OEL8_+ETk$c^yKn36^%vg zSQ@peZH4JW`FzEUXb#wFn=@2JnL4w5VQV?eI!I3KT$(MFhF!#DYL}=|*J9c#v#ISa z38NYb2D3eLLmI`iokOJ(^|YWoRA?(iRT5ASt_ zumZt~8G;6=m@{c)o;*~olCLX6-Gej|6tel@YObdO-`lB(`e)EhmZ_EJa}}zVSz(1H zl7Qxj9?pAPfu;_cE|6$?K39VMJp9!&Kr>1}in*guGYfKs-r`bRH7nag&9jr5TR5M( zIL&ORZ}OOnokESUzgQj)`b$Hd)WZwt8{FwD2Nt_4!Lsh+vW~WeUEwm6X^QowiM8`t z>k^tS%U;AmG`Z1Wh;;S}8-W(*h7k`=&s7Uyr4lTmGE>$?#hIoYwV=+WxoXdVpF88= zzIqcgU!hBN6!r{Eo$Tq%470udq#9*!#t>DPcO@0JGdozy!xJ>2X7gSLm{Z6Pn@i^u z{3Zxv6w~RoN{(8nqh=KgMXKz6vfmyq3G>cgqd4l3sL*K@>Vxh&YNQof8?+4$hP^qA zAp;aKP|WuR72}|Thr%IhMT1x(%nAolxdIfbcAB0gu(o|^(E($X!U8=cC(9xFeu<{MzKgk6%AbF-tHp3(db8GEsa663JOBAj5OKK zp*zpdg-dCzI^0X+=pZ_M;tJEIoKFK%u^p>lGx*R%MBO#)HPKxAgE?Ao4Gr>u8up^| zxZ>03u`C(FZS7&95>SII=JCN~-A)eFmN2PXkMsuZL!=Yr&3hjvnXVi~^m74cv`@*> zN;>Gzo1e3?%O>ZG-JQ9k{3xtkP1Ky8q5M!KDB2$t5(<0G1jUnCFBY#nKhPYE)#d;d z%8VG+EtB>bZxIX{f^M`;OeQFh7+%hVDf@nim|MJv43?`b|dN)Y_m=xcQRYE;kS$i{l#q)`P^p%4rD+8k{%DXQom|HNHTh1&mZ+e1NaV;K zimXndX#j}_X>x;!z|6&M1zIT&YYUkA@b2>dA;_F=6y5-8@v}LrWqWn+; zAt#vu8yihr(b&{b8@Zl_Fv>kSzlWSO)W)_d!p@LmrYxitIjFVU{WHx^u2zC-EnahdD}uR`b=7e(m3evJ(zS+B_z|`_N1n! z&F#K>uHY8Q3Anz#ZN&F#o}6$}@@#K{Znl?8k;s!xa>}F&&=>Y-USi;;$tN@|@g9U5 zC&yqBcFh+~Y!EkbnQgg)RGVmOW-6gk46Rwl@s;fuEn-*3;`qw0VySZ=%=fhoX8S|W zZiDznDQJ6{m?~nFlDcGjcBs(9=NV>$5sPzG8^5;hx;Th79DFF!-<^$LL*q@pgVx3| zEi;3~;_TjXVXoV3fnzY&LGv+oMGfMO?;3%41{(= z>xn^}4$SF{oPn9uM9ng5eeAYo(hP#Bpt#g58MFCmy*Vm|TR)ghL2E~wx1%D9c3(>) zcR6cj|2YM-ZZuOc?UiYvU!>`$i`Hm#!baE6qQ!JJ-mUb=RSKNhc5L6 zF8cz;Ok?>X=FEkr)7xbZc5OI%>*0(d=Ug?da}Uayrd+><287|MbOhN$``g~u-d0GS+ta*S*j;b<6ZA`I=M6_ue5ij37{Hw&}j~h0Bya2i{WiXXHQ7W zW3dWpE%yx2#-YdTxE=DxFsz_Lw?6WLyZE4xcI~t{H;smEQyi_kvUJ)&XQv!D8Ox^D z(qfuWr<8_#U3W#-e5?k@U8dB-F1oFPiQIj&j_t}8=qN607g;uHGS(Ai7l(d?6D&H6 z&GrmOD=C|TsO>*4^Ef55$eO7*Ikws@j3YlK>eO~6Y1dqwoLhC+O2=AeQD}NTLlN0k zbk}<8ReM0jqJxW!&+M87Ge9L!5#kyug}n^#3v$%~BW@#!*<(i9oVjCB+IaO+yl0dS zQ>uNmQEIFs=c z!WAN(Y7&@!xXvew=-b6MLk z;o>gtnzq%^5K9>>QZo$Rfnw= z%opxbx;cZPj&9l%tFy!EyaH_-=!l|s+Oi(n)MF3~&4%7?XtMeGZ6!Kqp3{BAlzD}o z><~Gzetm@#Oexczc5YarBTs5CbXYJLmb3FJG;U4}>ByCKPB5P5Etmq-M$5$#)|(3P z;Z9g>&vn!I!iPPb#h%4s)oycfo@2VEz0{&sI^8y>><#IZVu6M-+N025Mtixcvue&` zF_YT^_WA+3wTyADV_w}SHj+l6`mT1WJBsR@L;HQ~faoxqrW<=@SFV~j<9cP$cpWuC zM@`gGlXTQ>I%;O)J@0DbP$f2 zF7i?=+9}1Nomec|>BXWGB%SACS3B>;qMZk0(awvpXy?gTblscr_=IxSy&Athp`>;1 znpvpc^z1jMVZS*S+HcOD{pK8Izd1kIZ_YjToAZkO=A2=_IUm?>^`i4vnVWh+nacds zi&fv}MGeM_awfefS4uC+nf9VwdA%rCXfMi@+>3I>_oAE+yeQ`gFRJbhKdwRk>wfXC zYfyr^hwQR_X4kBC?zgm%!yyqJ=F>)`!jbJ^e>UG%zLq%C!(L%C&GW%C!$K%C&GW%C&GW%C&GW%C&GW%C&GW%C&GWs@}r= zIG2CyDsX@~I8>;g5=ULSF>RjKxuJ1d=aI(LIj1poerime%NkSXy$*Am2Sqk#s@x*5 zZduBYHPDST*Zys^xh-OE2}HfiB3cB}sdPvi-8ltVq@xNx_h!Rr%(z=burDXOxYw{L z%ZuGg(l$|h7a40JJl-Sj$klt_Y;51~=!+Xm?0xa+6?3=5Ywv9px`$y-i)PX~_9)6{ zHqZGJ#-?~(->&COw(Bcno3*ZqMO^!gT9Aq*L@pS72KsQ`21w z&Jq~_R;i;a=Jl3jVopi%DwQjzS-IM*OzZ2*(E<^VN3pXF=Z$RqWortWddX~JJ{K`%xRbgPLE@l`%iSJ zhFgqunTzI<+MTx-KbdA1Y;lq}ZA{9=Bdiyw)bs!vNtcTSIvJ#!*0eKOIIG=dLBw~q z&2aJ2A)PtYbkRIxCB{{zQ-i026jH=8#$4ycxoJkG?_G?0f@L~EM!Lx}X4TbjFA+D2 zLrn_^RQ zrZi|>T}65pke2JNrD49NRMT)f%syfw&3Tifsrd=GIvzQ(QRJjXk-H(%JB`PLQJMdShl%FB&gyoKYkVrx-BJPS+a|D1tWW{;kb=KW7{K zcpj}_@W_!Jp~+s>ta9iK(yq%WhT2unK*wVHyqx>qPTj6jjmP4)Wq+8XMW<_wCh=5y z_L7cP>(((r*}={=OshJpxP%@sun$_*FP=12Md4n*zK{+v=r%pg3-jkfs@Fc=f)Sr? zJBIdf7GMc|!z5szzM4*VaPd49<;nPyIyENS&M-@7zj0`@Ic=Dpt)n+u@|s7c_~^&V zwiPHBbbFP?uOUpJgY-nsw8E00Qfl;!t9?`?y6@BMs`_1{?)*^M7ROe5G?bAF9(2Jl zkLPNr(+meo!|oaFcAhuA!hEkE_&DF1SZvcV8dX{=GwJ+?a&My12Qc$vjbbC{k-h%T z;lXZtaL2|>8?2ZH7ZzH}bb4p5Vh_;xLL)Vv6f&{ytj~Tk&#E0oGdZ1pN4cZAm_`o} z(7j^sIb@UCyCKfgiHUB{6{x`WD41o;7N_1_%~cDsmBISaTVOLxWuoJMdS1X4hpNe( z@ron(`n9g(jbdielW#+VI^Nt>*3r24t7Exa)mxieyPHz9(|U@J&3o-lClk%Lp^P!CdQBw0 zQA-c)Bk{PO5XDFR-rf~92^tR!2^UaDiyLw6U)*1_Suqdh*wg>%bg-SrQK%u)yILdv z(HD#%SQ1u(S*T<$hJJ0p46kOvh&z$ygE2x=dbQY7%o|G!?L&_ghu@r_!w8O;SL&sP zg>z$$=%k|wx;DCk`l8t^)svW|aKt;OM4vIu&#AbznQ8t`m`2bno}z37sj<=QmFuGk zun~6ML71YYe@}VX9-B*Gi{wB5$lPzp9OaHhTP}VU(Gma1}7RcFd5+oR~mgZ>jm6dawEKL5{r1zWgEPi@7*mV zPT@4Y3DOChy)gYYi)#k;97WF^nN2xv^wZYK%ph!Tp{aM?-!9{4Xzep!JZd`;SBGtM zyiaRs`ztN;=kk4d_e(rW%CQLZL?sS<_$f+$ND{k(sKeHuokVoh)o^cmsJD|I=cHe| zskmjEd1et8+UFL{Ck)5*Ahumk@&FCr)04wyBVwXVLcWQP5!I$QpXg~oir|E}#I*wR zXe6}_`=A*$Y5T||H(s0@S}U$K4=&U^x(K6& z`_7Lg*T2*5iyjoKe_hzuJ(#b5qZPW@_tN^Lg4RUc4|o0>%x~7c)M_xHKtB2{1M}Rb zZ56Ns=i=9waG@zmvD{Bx+dd3PDNQ}O`U%awGQrEr0{^;|K(vu5A;_s&jRP5uI!|83Fk zA8+;A;Ior&yLSF{s)>uW!W+e>LAJmnOJt-A{K7em+4j{AcXQ@7`X1qwkTwasB^>MjrV6 z~~&G$~EZy=v;vZ#AEgu>+Dgr!X923db`oZdq2U>^S-Bj zM7u?Qw$J*KZFCPjMn*qi3vCL2e}v|QLNEPd933E-_?0M*=lJ?{9i6+G&xA}sdJ$ox z&0j02(PxZ@uHURnpT{p7KYsk~^qMk(-YC!?=KJ69|CDAL?-d{byTxO`AGl zk4ckunccB);qDVA?J;rD`0=~g4idd14g4~m9D^Cm{|x3rjXEw~%Us0&|{ziTsL+5C>aB?tDD`{y3Av@4f8sxq-!KG;sb)*8kej0^v- zp`JJTQ2)$E*Q1}U9Ubj$Q(C*)=FHxQ*JOs`+W7UHO#Se$bLqb5dd?a9qSwPd#>!Jp zUU~oPuN%MrbI*Q*)`3Cr?Qf0ozGI#DKMxqwO~2xv<;G@cqkV?-n=)V2I+}p*pD&uu brEx#ej`tk5_c%8dppSsX{?>yt-|~L||CBu- diff --git a/OverdrawMonitor/ProjectSettings/ProjectVersion.txt b/OverdrawMonitor/ProjectSettings/ProjectVersion.txt index 7e64146..dbb1604 100644 --- a/OverdrawMonitor/ProjectSettings/ProjectVersion.txt +++ b/OverdrawMonitor/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2019.2.0f1 -m_EditorVersionWithRevision: 2019.2.0f1 (20c1667945cf) +m_EditorVersion: 2019.2.4f1 +m_EditorVersionWithRevision: 2019.2.4f1 (c63b2af89a85) From 8615bf366c142867aef7d9ea1c954ab4b4f00b02 Mon Sep 17 00:00:00 2001 From: Kirill Belonogov Date: Fri, 6 Sep 2019 23:48:32 +0300 Subject: [PATCH 18/31] Wip --- .../OverdrawMonitor/CameraOverdrawMonitor.cs | 47 +++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index b9791c2..27d74ad 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -40,7 +40,6 @@ public class CameraOverdrawMonitor : MonoBehaviour // The maximum overdraw measured public float MaxOverdraw { get; private set; } - Camera _sourceCamera; Camera _computeCamera; RenderTexture _overdrawTexture; ComputeShader _computeShader; @@ -52,10 +51,6 @@ public class CameraOverdrawMonitor : MonoBehaviour float _accumulatedIntervalOverdraw; float _intervalTime; long _intervalFrames; - CameraClearFlags _computeCameraOriginalClearFlags; - Color _computeCameraOriginalClearColor; - RenderTexture _computeCameraOriginalTargetTexture; - bool _computeCameraOriginalEnabled; void Awake() { @@ -78,12 +73,9 @@ void OnDestroy() _resultBuffer?.Release(); } - public void SetCameras(Camera sourceCamera, Camera computeCamera) + public void SetComputeCamera(Camera computeCamera) { - _sourceCamera = sourceCamera; _computeCamera = computeCamera; - - CheckComputeCameraState(false); } public void ResetStats() @@ -104,11 +96,9 @@ void RecreateTexture(Camera main) } } + // Todo: refactor ReleaseTexture void ReleaseTexture() { - if (_computeCamera != null) - _computeCamera.targetTexture = _computeCameraOriginalTargetTexture; - if (_overdrawTexture != null) { _overdrawTexture.Release(); @@ -124,25 +114,20 @@ void RecreateComputeBuffer() void LateUpdate() { - if (_sourceCamera == null || _computeCamera == null) + if (_computeCamera == null) return; - _computeCamera.CopyFrom(_sourceCamera); - - _computeCameraOriginalClearFlags = _computeCamera.clearFlags; - _computeCameraOriginalClearColor = _computeCamera.backgroundColor; - _computeCameraOriginalTargetTexture = _computeCamera.targetTexture; - _computeCameraOriginalEnabled = _computeCamera.enabled; + CameraClearFlags originalClearFlags = _computeCamera.clearFlags; + Color originalClearColor = _computeCamera.backgroundColor; + RenderTexture originalTargetTexture = _computeCamera.targetTexture; + bool originalIsCameraEnabled = _computeCamera.enabled; _computeCamera.clearFlags = CameraClearFlags.SolidColor; _computeCamera.backgroundColor = Color.clear; - RecreateTexture(_sourceCamera); + RecreateTexture(_computeCamera); _computeCamera.targetTexture = _overdrawTexture; - Transform sourceCameraNode = _sourceCamera.transform; - transform.SetPositionAndRotation(sourceCameraNode.position, sourceCameraNode.rotation); - _intervalTime += Time.deltaTime; if (_intervalTime > SampleTime) { @@ -157,7 +142,7 @@ void LateUpdate() _intervalFrames = 0; } - CheckComputeCameraState(true); + _computeCamera.enabled = false; _computeCamera.RenderWithShader(_replacementShader, null); @@ -192,15 +177,9 @@ void LateUpdate() if (overdrawRatio > MaxOverdraw) MaxOverdraw = overdrawRatio; - _computeCamera.targetTexture = _computeCameraOriginalTargetTexture; - _computeCamera.clearFlags = _computeCameraOriginalClearFlags; - _computeCamera.backgroundColor = _computeCameraOriginalClearColor; - - CheckComputeCameraState(_computeCameraOriginalEnabled); - } - - void CheckComputeCameraState(bool needActive) - { - _computeCamera.enabled = needActive || _sourceCamera == _computeCamera; + _computeCamera.targetTexture = originalTargetTexture; + _computeCamera.clearFlags = originalClearFlags; + _computeCamera.backgroundColor = originalClearColor; + _computeCamera.enabled = originalIsCameraEnabled; } } From 9dc749ea5aac7f7cd489fef453afaf40ad3b5c1c Mon Sep 17 00:00:00 2001 From: Kirill Belonogov Date: Fri, 6 Sep 2019 23:52:45 +0300 Subject: [PATCH 19/31] Refactor --- .../Editor/OverdrawToolWindow.cs | 141 +++++------------- 1 file changed, 37 insertions(+), 104 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs index 7534133..090a899 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs @@ -1,19 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using UnityEditor; using UnityEngine; -class MonitorInfo -{ - public Camera sourceCamera; - public CameraOverdrawMonitor monitor; -} - public class OverdrawToolWindow : EditorWindow { - bool isEnabled => _monitorsRoot != null; - Transform _monitorsRoot; - List _monitors; - bool _replacing; + bool isEnabled => _monitors != null && Application.isPlaying; + List _monitors; [MenuItem("Tools/Overdraw Tool")] static void ShowWindow() @@ -23,117 +16,70 @@ static void ShowWindow() window.Focus(); } - [MenuItem("Tools/Overdraw Tool", true)] - static bool Validate() - { - return Application.isPlaying; - } - + // Todo: fix auto start!!! void Init() { - _monitors = new List(); - - var monitorsRootGo = new GameObject("OverdrawMonitorsRoot"); - monitorsRootGo.hideFlags = HideFlags.DontSave; - _monitorsRoot = monitorsRootGo.transform; + _monitors = new List(); } void TryShutdown() { - if (_monitors != null) - { - foreach (MonitorInfo monitorInfo in _monitors.ToArray()) - RemoveMonitor(monitorInfo); - _monitors = null; - } - - if (_monitorsRoot != null) - { - DestroyImmediate(_monitorsRoot.gameObject); - _monitorsRoot = null; - } + if (_monitors == null) + return; - _replacing = false; + foreach (CameraOverdrawMonitor monitor in _monitors.ToArray()) + RemoveMonitor(monitor); + _monitors = null; } void AddMonitorForCamera(Camera camera) { - GameObject go; - Camera computeCamera; - if (_replacing) - { - go = camera.gameObject; - computeCamera = camera; - } - else - { - go = new GameObject("CameraOverdrawMonitor"); - go.hideFlags = HideFlags.DontSave; - go.transform.SetParent(_monitorsRoot, false); - computeCamera = go.AddComponent(); - } - - var monitor = go.AddComponent(); - monitor.SetCameras(camera, computeCamera); + // Todo: create separate object in root object for monitor components - no need to use source camera objects - _monitors.Add(new MonitorInfo - { - sourceCamera = camera, - monitor = monitor, - }); + var monitor = camera.gameObject.AddComponent(); + monitor.SetComputeCamera(camera); + _monitors.Add(monitor); } - void RemoveMonitor(MonitorInfo monitorInfo) + void RemoveMonitor(CameraOverdrawMonitor monitor) { - CameraOverdrawMonitor monitor = monitorInfo.monitor; if (monitor != null) - { - if (IsReplacingMonitor(monitorInfo)) - DestroyImmediate(monitor); - else - DestroyImmediate(monitor.gameObject); - } - _monitors.Remove(monitorInfo); - } - - void CheckMonitor(MonitorInfo monitorInfo) - { - if (monitorInfo.sourceCamera == null || !monitorInfo.sourceCamera.isActiveAndEnabled) - RemoveMonitor(monitorInfo); - } - - bool IsReplacingMonitor(MonitorInfo monitorInfo) - { - return monitorInfo.sourceCamera != null && monitorInfo.sourceCamera.gameObject == monitorInfo.monitor.gameObject; + DestroyImmediate(monitor); + _monitors.Remove(monitor); } void Update() { // Check shutdown if needed - if (!isEnabled || !Validate()) + if (!isEnabled) + { TryShutdown(); - if (_monitors == null) return; + } + + Camera[] activeCameras = Camera.allCameras; - // Check existing monitors - foreach (MonitorInfo monitorInfo in _monitors.ToArray()) - CheckMonitor(monitorInfo); + // Remove expired monitors + _monitors.RemoveAll(m => m == null); + foreach (CameraOverdrawMonitor monitor in _monitors.ToArray()) + if (!Array.Exists(activeCameras, c => monitor.GetComponent() == c)) + RemoveMonitor(monitor); // Refresh monitors - foreach (Camera activeCamera in Camera.allCameras) - if (!_monitors.Exists(m => m.sourceCamera == activeCamera)) + foreach (Camera activeCamera in activeCameras) + if (!_monitors.Exists(m => m.GetComponent() == activeCamera)) AddMonitorForCamera(activeCamera); } void OnGUI() { - if (Validate()) + if (Application.isPlaying) { using (new GUILayout.HorizontalScope()) { if (GUILayout.Button(isEnabled ? "Stop" : "Start", GUILayout.MaxWidth(100), GUILayout.MaxHeight(25))) { - if (!isEnabled && Validate()) + if (!isEnabled) Init(); else TryShutdown(); @@ -146,37 +92,24 @@ void OnGUI() if (GUILayout.Button("Reset Max", GUILayout.Width(100), GUILayout.Height(20))) { - foreach (MonitorInfo monitorInfo in _monitors) - if (monitorInfo.monitor != null) - monitorInfo.monitor.ResetStats(); + foreach (CameraOverdrawMonitor monitor in _monitors) + if (monitor != null) + monitor.ResetStats(); } } GUILayout.Space(5); - bool newReplacing = GUILayout.Toggle(_replacing, "Replacing shader for cameras (for Canvas cameras)"); - if (newReplacing != _replacing) - { - TryShutdown(); - Init(); - _replacing = newReplacing; - return; - } - - GUILayout.Space(5); - float totalAverage = 0f; float totalMax = 0f; if (_monitors != null) { - foreach (MonitorInfo monitorInfo in _monitors) + foreach (CameraOverdrawMonitor monitor in _monitors) { using (new GUILayout.HorizontalScope()) { - CameraOverdrawMonitor monitor = monitorInfo.monitor; - - GUILayout.Label(monitorInfo.sourceCamera.name); + GUILayout.Label(monitor.gameObject.name); GUILayout.FlexibleSpace(); float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.AccumulatedAverageOverdraw : 0f; From 3c07749e3b316cfa51728e3216da77a13f3fc0fb Mon Sep 17 00:00:00 2001 From: Kirill Belonogov Date: Sat, 7 Sep 2019 09:00:47 +0300 Subject: [PATCH 20/31] Refactor --- .gitignore | 2 - .../OverdrawMonitor/CameraOverdrawMonitor.cs | 90 +++++------ .../Editor/OverdrawMonitorWindow.cs | 135 ++++++++++++++++ ....cs.meta => OverdrawMonitorWindow.cs.meta} | 0 .../Editor/OverdrawToolWindow.cs | 146 ------------------ 5 files changed, 175 insertions(+), 198 deletions(-) create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs rename OverdrawMonitor/Assets/OverdrawMonitor/Editor/{OverdrawToolWindow.cs.meta => OverdrawMonitorWindow.cs.meta} (100%) delete mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs diff --git a/.gitignore b/.gitignore index d39fd63..30bdd7b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,3 @@ *.user *.csproj *.userprefs -OverdrawMonitor/Assets/Plugins.meta -OverdrawMonitor/Assets/Plugins/Editor.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 27d74ad..5e5a601 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -13,7 +13,6 @@ using UnityEngine; -[DisallowMultipleComponent] public class CameraOverdrawMonitor : MonoBehaviour { const int GroupDimension = 32; @@ -21,26 +20,28 @@ public class CameraOverdrawMonitor : MonoBehaviour const int DataSize = DataDimension * DataDimension; const float SampleTime = 1f; + public Camera targetCamera => _targetCamera; + // The number of shaded fragments in the last frame public long totalShadedFragments { get; private set; } - // The overdraw ration in the last frame + // The overdraw ratio in the last frame public float overdrawRatio { get; private set; } // Number of shaded fragments in the measured time span - public long IntervalShadedFragments { get; private set; } + public long intervalShadedFragments { get; private set; } // The average number of shaded fragments in the measured time span - public float IntervalAverageShadedFragments { get; private set; } + public float intervalAverageShadedFragments { get; private set; } // The average overdraw in the measured time span - public float IntervalAverageOverdraw { get; private set; } - public float AccumulatedAverageOverdraw => _accumulatedIntervalOverdraw / _intervalFrames; + public float intervalAverageOverdraw { get; private set; } + public float accumulatedAverageOverdraw => _accumulatedIntervalOverdraw / _intervalFrames; // The maximum overdraw measured - public float MaxOverdraw { get; private set; } + public float maxOverdraw { get; private set; } - Camera _computeCamera; + Camera _targetCamera; RenderTexture _overdrawTexture; ComputeShader _computeShader; ComputeBuffer _resultBuffer; @@ -62,9 +63,11 @@ void Awake() Shader.SetGlobalFloat("OverdrawFragmentWeight", 1f / (GroupDimension * GroupDimension)); _inputData = new int[DataSize]; - _resultData = new int[DataSize]; for (int i = 0; i < _inputData.Length; i++) _inputData[i] = 0; + + _resultData = new int[DataSize]; + _resultBuffer = new ComputeBuffer(_resultData.Length, 4); } void OnDestroy() @@ -73,9 +76,9 @@ void OnDestroy() _resultBuffer?.Release(); } - public void SetComputeCamera(Camera computeCamera) + public void SetTargetCamera(Camera targetCamera) { - _computeCamera = computeCamera; + _targetCamera = targetCamera; } public void ResetStats() @@ -84,19 +87,9 @@ public void ResetStats() _accumulatedIntervalFragments = 0; _intervalTime = 0; _intervalFrames = 0; - MaxOverdraw = 0; + maxOverdraw = 0; } - void RecreateTexture(Camera main) - { - if (_overdrawTexture == null || main.pixelWidth != _overdrawTexture.width || main.pixelHeight != _overdrawTexture.height) - { - ReleaseTexture(); - _overdrawTexture = new RenderTexture(main.pixelWidth, main.pixelHeight, 24, RenderTextureFormat.RFloat); - } - } - - // Todo: refactor ReleaseTexture void ReleaseTexture() { if (_overdrawTexture != null) @@ -106,34 +99,33 @@ void ReleaseTexture() } } - void RecreateComputeBuffer() - { - if (_resultBuffer == null) - _resultBuffer = new ComputeBuffer(_resultData.Length, 4); - } - void LateUpdate() { - if (_computeCamera == null) + if (_targetCamera == null) return; - CameraClearFlags originalClearFlags = _computeCamera.clearFlags; - Color originalClearColor = _computeCamera.backgroundColor; - RenderTexture originalTargetTexture = _computeCamera.targetTexture; - bool originalIsCameraEnabled = _computeCamera.enabled; + CameraClearFlags originalClearFlags = _targetCamera.clearFlags; + Color originalClearColor = _targetCamera.backgroundColor; + RenderTexture originalTargetTexture = _targetCamera.targetTexture; + bool originalIsCameraEnabled = _targetCamera.enabled; - _computeCamera.clearFlags = CameraClearFlags.SolidColor; - _computeCamera.backgroundColor = Color.clear; + _targetCamera.clearFlags = CameraClearFlags.SolidColor; + _targetCamera.backgroundColor = Color.clear; - RecreateTexture(_computeCamera); - _computeCamera.targetTexture = _overdrawTexture; + // Recreate texture if needed + if (_overdrawTexture == null || _targetCamera.pixelWidth != _overdrawTexture.width || _targetCamera.pixelHeight != _overdrawTexture.height) + { + ReleaseTexture(); + _overdrawTexture = new RenderTexture(_targetCamera.pixelWidth, _targetCamera.pixelHeight, 24, RenderTextureFormat.RFloat); + } + _targetCamera.targetTexture = _overdrawTexture; _intervalTime += Time.deltaTime; if (_intervalTime > SampleTime) { - IntervalShadedFragments = _accumulatedIntervalFragments; - IntervalAverageShadedFragments = (float)_accumulatedIntervalFragments / _intervalFrames; - IntervalAverageOverdraw = _accumulatedIntervalOverdraw / _intervalFrames; + intervalShadedFragments = _accumulatedIntervalFragments; + intervalAverageShadedFragments = (float)_accumulatedIntervalFragments / _intervalFrames; + intervalAverageOverdraw = _accumulatedIntervalOverdraw / _intervalFrames; _intervalTime -= SampleTime; @@ -142,14 +134,12 @@ void LateUpdate() _intervalFrames = 0; } - _computeCamera.enabled = false; + _targetCamera.enabled = false; - _computeCamera.RenderWithShader(_replacementShader, null); + _targetCamera.RenderWithShader(_replacementShader, null); int kernel = _computeShader.FindKernel("CSMain"); - RecreateComputeBuffer(); - // Setting up the data _resultBuffer.SetData(_inputData); _computeShader.SetInt("BufferSizeX", DataDimension); @@ -174,12 +164,12 @@ void LateUpdate() _accumulatedIntervalOverdraw += overdrawRatio; _intervalFrames++; - if (overdrawRatio > MaxOverdraw) - MaxOverdraw = overdrawRatio; + if (overdrawRatio > maxOverdraw) + maxOverdraw = overdrawRatio; - _computeCamera.targetTexture = originalTargetTexture; - _computeCamera.clearFlags = originalClearFlags; - _computeCamera.backgroundColor = originalClearColor; - _computeCamera.enabled = originalIsCameraEnabled; + _targetCamera.targetTexture = originalTargetTexture; + _targetCamera.clearFlags = originalClearFlags; + _targetCamera.backgroundColor = originalClearColor; + _targetCamera.enabled = originalIsCameraEnabled; } } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs new file mode 100644 index 0000000..8c700f0 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs @@ -0,0 +1,135 @@ +using System; +using UnityEditor; +using UnityEngine; + +public class OverdrawMonitorWindow : EditorWindow +{ + bool isEnabled => _monitorsGo != null && Application.isPlaying; + GameObject _monitorsGo; + + [MenuItem("Tools/Overdraw Monitor")] + static void ShowWindow() + { + GetWindow().Show(); + } + + void Init() + { + if (_monitorsGo != null) + throw new Exception("Attempt to start overdraw monitor twice"); + + _monitorsGo = new GameObject("OverdrawMonitor"); + _monitorsGo.hideFlags = HideFlags.DontSave; + } + + void TryShutdown() + { + if (_monitorsGo != null) + DestroyImmediate(_monitorsGo); + } + + void Update() + { + // Check shutdown if needed + if (!isEnabled) + { + TryShutdown(); + return; + } + + Camera[] activeCameras = Camera.allCameras; + + // Remove monitors for non-active cameras + var monitors = GetAllMonitors(); + foreach (var monitor in monitors) + if (!Array.Exists(activeCameras, c => monitor.targetCamera == c)) + DestroyImmediate(monitor); + + // Add new monitors + monitors = GetAllMonitors(); + foreach (Camera activeCamera in activeCameras) + { + if (!Array.Exists(monitors,m => m.targetCamera == activeCamera)) + { + var monitor = _monitorsGo.AddComponent(); + monitor.SetTargetCamera(activeCamera); + } + } + } + + CameraOverdrawMonitor[] GetAllMonitors() + { + return _monitorsGo.GetComponentsInChildren(true); + } + + void OnGUI() + { + if (Application.isPlaying) + { + int startButtonHeight = 25; + if (GUILayout.Button(isEnabled ? "Stop" : "Start", GUILayout.MaxWidth(100), GUILayout.MaxHeight(startButtonHeight))) + { + if (!isEnabled) + Init(); + else + TryShutdown(); + } + + if (!isEnabled) + return; + + CameraOverdrawMonitor[] monitors = GetAllMonitors(); + + GUILayout.Space(-startButtonHeight); + using (new GUILayout.HorizontalScope()) + { + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Reset Max", GUILayout.Width(100), GUILayout.Height(20))) + { + foreach (CameraOverdrawMonitor monitor in monitors) + monitor.ResetStats(); + } + } + + GUILayout.Space(5); + + float totalAverage = 0f; + float totalMax = 0f; + foreach (CameraOverdrawMonitor monitor in monitors) + { + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label(monitor.targetCamera.name); + GUILayout.FlexibleSpace(); + + float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.accumulatedAverageOverdraw : 0f; + GUILayout.Label(FormatResult(accumulatedAverageOverdraw, monitor.maxOverdraw)); + + totalMax += monitor.maxOverdraw; + totalAverage += accumulatedAverageOverdraw; + } + } + + GUILayout.Space(5); + + using (new GUILayout.HorizontalScope()) + { + GUILayout.Label("TOTAL"); + GUILayout.FlexibleSpace(); + GUILayout.Label(FormatResult(totalAverage, totalMax)); + } + } + else + { + GUILayout.Label("Available only in Play mode"); + } + + Repaint(); + } + + string FormatResult(float average, float max) + { + return $"{average:N3}\tMax: {max:N3}"; + } +} diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs.meta similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs.meta rename to OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs deleted file mode 100644 index 090a899..0000000 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawToolWindow.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEditor; -using UnityEngine; - -public class OverdrawToolWindow : EditorWindow -{ - bool isEnabled => _monitors != null && Application.isPlaying; - List _monitors; - - [MenuItem("Tools/Overdraw Tool")] - static void ShowWindow() - { - var window = GetWindow(); - window.Show(); - window.Focus(); - } - - // Todo: fix auto start!!! - void Init() - { - _monitors = new List(); - } - - void TryShutdown() - { - if (_monitors == null) - return; - - foreach (CameraOverdrawMonitor monitor in _monitors.ToArray()) - RemoveMonitor(monitor); - _monitors = null; - } - - void AddMonitorForCamera(Camera camera) - { - // Todo: create separate object in root object for monitor components - no need to use source camera objects - - var monitor = camera.gameObject.AddComponent(); - monitor.SetComputeCamera(camera); - _monitors.Add(monitor); - } - - void RemoveMonitor(CameraOverdrawMonitor monitor) - { - if (monitor != null) - DestroyImmediate(monitor); - _monitors.Remove(monitor); - } - - void Update() - { - // Check shutdown if needed - if (!isEnabled) - { - TryShutdown(); - return; - } - - Camera[] activeCameras = Camera.allCameras; - - // Remove expired monitors - _monitors.RemoveAll(m => m == null); - foreach (CameraOverdrawMonitor monitor in _monitors.ToArray()) - if (!Array.Exists(activeCameras, c => monitor.GetComponent() == c)) - RemoveMonitor(monitor); - - // Refresh monitors - foreach (Camera activeCamera in activeCameras) - if (!_monitors.Exists(m => m.GetComponent() == activeCamera)) - AddMonitorForCamera(activeCamera); - } - - void OnGUI() - { - if (Application.isPlaying) - { - using (new GUILayout.HorizontalScope()) - { - if (GUILayout.Button(isEnabled ? "Stop" : "Start", GUILayout.MaxWidth(100), GUILayout.MaxHeight(25))) - { - if (!isEnabled) - Init(); - else - TryShutdown(); - } - - if (!isEnabled) - return; - - GUILayout.FlexibleSpace(); - - if (GUILayout.Button("Reset Max", GUILayout.Width(100), GUILayout.Height(20))) - { - foreach (CameraOverdrawMonitor monitor in _monitors) - if (monitor != null) - monitor.ResetStats(); - } - } - - GUILayout.Space(5); - - float totalAverage = 0f; - float totalMax = 0f; - - if (_monitors != null) - { - foreach (CameraOverdrawMonitor monitor in _monitors) - { - using (new GUILayout.HorizontalScope()) - { - GUILayout.Label(monitor.gameObject.name); - GUILayout.FlexibleSpace(); - - float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.AccumulatedAverageOverdraw : 0f; - GUILayout.Label(FormatResult(accumulatedAverageOverdraw, monitor.MaxOverdraw)); - - totalMax += monitor.MaxOverdraw; - totalAverage += accumulatedAverageOverdraw; - } - } - } - - GUILayout.Space(5); - - using (new GUILayout.HorizontalScope()) - { - GUILayout.Label("TOTAL"); - GUILayout.FlexibleSpace(); - GUILayout.Label(FormatResult(totalAverage, totalMax)); - } - } - else - { - GUILayout.Label("Available only in Play mode"); - } - - Repaint(); - } - - string FormatResult(float average, float max) - { - return $"{average:N3}\tMax: {max:N3}"; - } -} - From 8462bbc69a0db567bb22655542ef4a39de2f442c Mon Sep 17 00:00:00 2001 From: Kirill Belonogov Date: Sat, 7 Sep 2019 09:05:00 +0300 Subject: [PATCH 21/31] Hide Flag for monitor gameObject --- .../Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs index 8c700f0..437a976 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs @@ -19,7 +19,7 @@ void Init() throw new Exception("Attempt to start overdraw monitor twice"); _monitorsGo = new GameObject("OverdrawMonitor"); - _monitorsGo.hideFlags = HideFlags.DontSave; + _monitorsGo.hideFlags = HideFlags.HideAndDontSave; } void TryShutdown() From 3e3abb0a5e43ed20b79362e9868742f27af23981 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 7 Sep 2019 09:23:18 +0300 Subject: [PATCH 22/31] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index f9baaca..8b13789 100644 --- a/README.md +++ b/README.md @@ -1,4 +1 @@ -# Todos -* Make replacing mode full functioned, considering any post-effects in original cameras. -The goal is to leave only one mode: replacing. From 31f495767a1261a9e9be5a15ed8e6e8d486d808a Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 7 Sep 2019 09:23:25 +0300 Subject: [PATCH 23/31] Delete README.md --- README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 8b13789..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ - From 0bb09770537521e9b09676696e5b50b6eabc0149 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 9 Sep 2019 09:22:34 +0300 Subject: [PATCH 24/31] Create README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..3a5c893 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Todo: +What if camera renders to texture with lower resolution. Check results From 1c685b8651f8dbed37fd9ba3e65c5d9d65778b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Mon, 9 Sep 2019 09:59:29 +0300 Subject: [PATCH 25/31] Fix for cameras with render textures --- OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index 5e5a601..ab82d94 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -158,7 +158,7 @@ void LateUpdate() foreach (int res in _resultData) totalShadedFragments += res; - overdrawRatio = (float)totalShadedFragments / (xGroups * GroupDimension * yGroups * GroupDimension); + overdrawRatio = (float)totalShadedFragments / (Screen.width * Screen.height); _accumulatedIntervalFragments += totalShadedFragments; _accumulatedIntervalOverdraw += overdrawRatio; From 0057882853a21d5c487fefd3d5cde08218a72672 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 9 Sep 2019 10:00:40 +0300 Subject: [PATCH 26/31] Delete README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 3a5c893..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Todo: -What if camera renders to texture with lower resolution. Check results From e29c28175cadaa95497e525f925da82414202ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 11 Sep 2019 10:07:05 +0300 Subject: [PATCH 27/31] Refactor --- OverdrawMonitor/.vs/OverdrawMonitor/v15/.suo | Bin 107520 -> 0 bytes .../OverdrawMonitor/CameraOverdrawMonitor.cs | 85 ++++-------------- .../Editor/OverdrawMonitorWindow.cs | 81 +++++++++++++---- 3 files changed, 82 insertions(+), 84 deletions(-) delete mode 100644 OverdrawMonitor/.vs/OverdrawMonitor/v15/.suo diff --git a/OverdrawMonitor/.vs/OverdrawMonitor/v15/.suo b/OverdrawMonitor/.vs/OverdrawMonitor/v15/.suo deleted file mode 100644 index 6f019d98d0e1c1f0cd60b6329c50c9db0682e3ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107520 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 diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs index ab82d94..56d3f71 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs @@ -18,28 +18,11 @@ public class CameraOverdrawMonitor : MonoBehaviour const int GroupDimension = 32; const int DataDimension = 128; const int DataSize = DataDimension * DataDimension; - const float SampleTime = 1f; public Camera targetCamera => _targetCamera; - // The number of shaded fragments in the last frame - public long totalShadedFragments { get; private set; } - - // The overdraw ratio in the last frame - public float overdrawRatio { get; private set; } - - // Number of shaded fragments in the measured time span - public long intervalShadedFragments { get; private set; } - - // The average number of shaded fragments in the measured time span - public float intervalAverageShadedFragments { get; private set; } - - // The average overdraw in the measured time span - public float intervalAverageOverdraw { get; private set; } - public float accumulatedAverageOverdraw => _accumulatedIntervalOverdraw / _intervalFrames; - - // The maximum overdraw measured - public float maxOverdraw { get; private set; } + public long fragmentsCount => isActiveAndEnabled ? _fragmentsCount : 0L; + public float fillRate => isActiveAndEnabled ? _fillRate : 0f; Camera _targetCamera; RenderTexture _overdrawTexture; @@ -48,10 +31,8 @@ public class CameraOverdrawMonitor : MonoBehaviour Shader _replacementShader; int[] _inputData; int[] _resultData; - long _accumulatedIntervalFragments; - float _accumulatedIntervalOverdraw; - float _intervalTime; - long _intervalFrames; + long _fragmentsCount; + float _fillRate; void Awake() { @@ -81,15 +62,6 @@ public void SetTargetCamera(Camera targetCamera) _targetCamera = targetCamera; } - public void ResetStats() - { - _accumulatedIntervalOverdraw = 0; - _accumulatedIntervalFragments = 0; - _intervalTime = 0; - _intervalFrames = 0; - maxOverdraw = 0; - } - void ReleaseTexture() { if (_overdrawTexture != null) @@ -104,69 +76,48 @@ void LateUpdate() if (_targetCamera == null) return; + // Save original params CameraClearFlags originalClearFlags = _targetCamera.clearFlags; Color originalClearColor = _targetCamera.backgroundColor; RenderTexture originalTargetTexture = _targetCamera.targetTexture; bool originalIsCameraEnabled = _targetCamera.enabled; - _targetCamera.clearFlags = CameraClearFlags.SolidColor; - _targetCamera.backgroundColor = Color.clear; - // Recreate texture if needed if (_overdrawTexture == null || _targetCamera.pixelWidth != _overdrawTexture.width || _targetCamera.pixelHeight != _overdrawTexture.height) { ReleaseTexture(); _overdrawTexture = new RenderTexture(_targetCamera.pixelWidth, _targetCamera.pixelHeight, 24, RenderTextureFormat.RFloat); } - _targetCamera.targetTexture = _overdrawTexture; - - _intervalTime += Time.deltaTime; - if (_intervalTime > SampleTime) - { - intervalShadedFragments = _accumulatedIntervalFragments; - intervalAverageShadedFragments = (float)_accumulatedIntervalFragments / _intervalFrames; - intervalAverageOverdraw = _accumulatedIntervalOverdraw / _intervalFrames; - - _intervalTime -= SampleTime; - - _accumulatedIntervalFragments = 0; - _accumulatedIntervalOverdraw = 0; - _intervalFrames = 0; - } + // Set replacement params + _targetCamera.clearFlags = CameraClearFlags.SolidColor; + _targetCamera.backgroundColor = Color.clear; + _targetCamera.targetTexture = _overdrawTexture; _targetCamera.enabled = false; + // Render _targetCamera.RenderWithShader(_replacementShader, null); - int kernel = _computeShader.FindKernel("CSMain"); - - // Setting up the data + // Compute _resultBuffer.SetData(_inputData); + int kernel = _computeShader.FindKernel("CSMain"); _computeShader.SetInt("BufferSizeX", DataDimension); _computeShader.SetTexture(kernel, "Overdraw", _overdrawTexture); _computeShader.SetBuffer(kernel, "Output", _resultBuffer); + // Summing up the fragments int xGroups = _overdrawTexture.width / GroupDimension; int yGroups = _overdrawTexture.height / GroupDimension; - - // Summing up the fragments _computeShader.Dispatch(kernel, xGroups, yGroups, 1); _resultBuffer.GetData(_resultData); - // Getting the results - totalShadedFragments = 0; + // Results + _fragmentsCount = 0; foreach (int res in _resultData) - totalShadedFragments += res; - - overdrawRatio = (float)totalShadedFragments / (Screen.width * Screen.height); - - _accumulatedIntervalFragments += totalShadedFragments; - _accumulatedIntervalOverdraw += overdrawRatio; - _intervalFrames++; - - if (overdrawRatio > maxOverdraw) - maxOverdraw = overdrawRatio; + _fragmentsCount += res; + _fillRate = fragmentsCount / ((float)_overdrawTexture.width * _overdrawTexture.height); + // Restore original params _targetCamera.targetTexture = originalTargetTexture; _targetCamera.clearFlags = originalClearFlags; _targetCamera.backgroundColor = originalClearColor; diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs index 437a976..cdcd13f 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using UnityEditor; using UnityEngine; @@ -6,6 +8,7 @@ public class OverdrawMonitorWindow : EditorWindow { bool isEnabled => _monitorsGo != null && Application.isPlaying; GameObject _monitorsGo; + Dictionary _globalMaxInfos; [MenuItem("Tools/Overdraw Monitor")] static void ShowWindow() @@ -20,12 +23,16 @@ void Init() _monitorsGo = new GameObject("OverdrawMonitor"); _monitorsGo.hideFlags = HideFlags.HideAndDontSave; + _globalMaxInfos = new Dictionary(); } void TryShutdown() { - if (_monitorsGo != null) - DestroyImmediate(_monitorsGo); + if (_monitorsGo == null) + return; + + DestroyImmediate(_monitorsGo); + _globalMaxInfos = null; } void Update() @@ -85,39 +92,59 @@ void OnGUI() { GUILayout.FlexibleSpace(); - if (GUILayout.Button("Reset Max", GUILayout.Width(100), GUILayout.Height(20))) - { - foreach (CameraOverdrawMonitor monitor in monitors) - monitor.ResetStats(); - } + if (GUILayout.Button("Reset Stats", GUILayout.Width(100), GUILayout.Height(20))) + ResetStats(); } GUILayout.Space(5); - float totalAverage = 0f; - float totalMax = 0f; + Vector2Int gameViewResolution = GetGameViewResolution(); + GUILayout.Label($"Screen {gameViewResolution.x}x{gameViewResolution.y}"); + + GUILayout.Space(5); + + foreach (CameraOverdrawMonitor monitor in _globalMaxInfos.Keys.ToArray()) + if (!Array.Exists(monitors, m => monitor)) + _globalMaxInfos.Remove(monitor); + + long gameViewArea = gameViewResolution.x * gameViewResolution.y; + float totalGlobalFillRate = 0f; foreach (CameraOverdrawMonitor monitor in monitors) { using (new GUILayout.HorizontalScope()) { - GUILayout.Label(monitor.targetCamera.name); + Camera cam = monitor.targetCamera; + GUILayout.Label($"{cam.name} {cam.pixelWidth}x{cam.pixelHeight}"); + GUILayout.FlexibleSpace(); - float accumulatedAverageOverdraw = monitor.isActiveAndEnabled ? monitor.accumulatedAverageOverdraw : 0f; - GUILayout.Label(FormatResult(accumulatedAverageOverdraw, monitor.maxOverdraw)); + float localFillRate = monitor.fillRate; + float globalFillRate = monitor.fragmentsCount / (float)gameViewArea; + totalGlobalFillRate += globalFillRate; - totalMax += monitor.maxOverdraw; - totalAverage += accumulatedAverageOverdraw; + if (!_globalMaxInfos.TryGetValue(monitor, out CameraOverdrawStats globalMaxInfo)) + { + globalMaxInfo = new CameraOverdrawStats(); + _globalMaxInfos.Add(monitor, globalMaxInfo); + } + globalMaxInfo.maxLocalFillRate = Math.Max(localFillRate, globalMaxInfo.maxLocalFillRate); + globalMaxInfo.maxGlobalFillRate = Math.Max(globalFillRate, globalMaxInfo.maxGlobalFillRate); + + GUILayout.Label(FormatResult("Local: {0} / {1} \t Global: {2} / {3}", + localFillRate, globalMaxInfo.maxLocalFillRate, globalFillRate, globalMaxInfo.maxGlobalFillRate)); } } GUILayout.Space(5); + float maxTotalGlobalFillRate = 0f; + foreach (CameraOverdrawStats stat in _globalMaxInfos.Values) + maxTotalGlobalFillRate += stat.maxGlobalFillRate; using (new GUILayout.HorizontalScope()) { GUILayout.Label("TOTAL"); GUILayout.FlexibleSpace(); - GUILayout.Label(FormatResult(totalAverage, totalMax)); + GUILayout.Label(FormatResult("Global: {0} / {1}", totalGlobalFillRate, maxTotalGlobalFillRate)); } } else @@ -128,8 +155,28 @@ void OnGUI() Repaint(); } - string FormatResult(float average, float max) + void ResetStats() + { + _globalMaxInfos.Clear(); + } + + string FormatResult(string format, params float[] args) + { + var stringArgs = new List(); + foreach (float arg in args) + stringArgs.Add($"{arg:N3}"); + return string.Format(format, stringArgs.ToArray()); + } + + static Vector2Int GetGameViewResolution() { - return $"{average:N3}\tMax: {max:N3}"; + var resString = UnityStats.screenRes.Split('x'); + return new Vector2Int(int.Parse(resString[0]), int.Parse(resString[1])); } } + +class CameraOverdrawStats +{ + public float maxLocalFillRate; + public float maxGlobalFillRate; +} From 9d98f084bf3784e875e9b99362e98ceb7e296353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 11 Sep 2019 10:10:15 +0300 Subject: [PATCH 28/31] Directories --- OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta | 8 ++++++++ .../{ => Sources}/CameraOverdrawMonitor.cs | 0 .../{ => Sources}/CameraOverdrawMonitor.cs.meta | 0 .../OverdrawMonitor/{ => Sources}/DebugOverdrawInt.shader | 0 .../{ => Sources}/DebugOverdrawInt.shader.meta | 0 5 files changed, 8 insertions(+) create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta rename OverdrawMonitor/Assets/OverdrawMonitor/{ => Sources}/CameraOverdrawMonitor.cs (100%) rename OverdrawMonitor/Assets/OverdrawMonitor/{ => Sources}/CameraOverdrawMonitor.cs.meta (100%) rename OverdrawMonitor/Assets/OverdrawMonitor/{ => Sources}/DebugOverdrawInt.shader (100%) rename OverdrawMonitor/Assets/OverdrawMonitor/{ => Sources}/DebugOverdrawInt.shader.meta (100%) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta new file mode 100644 index 0000000..0d90291 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 42f490e540ce247059a4da7f1af3877a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs rename to OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs.meta similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/CameraOverdrawMonitor.cs.meta rename to OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/DebugOverdrawInt.shader similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader rename to OverdrawMonitor/Assets/OverdrawMonitor/Sources/DebugOverdrawInt.shader diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/DebugOverdrawInt.shader.meta similarity index 100% rename from OverdrawMonitor/Assets/OverdrawMonitor/DebugOverdrawInt.shader.meta rename to OverdrawMonitor/Assets/OverdrawMonitor/Sources/DebugOverdrawInt.shader.meta From 130e425b3599a5cb37fe640abe31412f00803cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 11 Sep 2019 11:14:15 +0300 Subject: [PATCH 29/31] Rename --- .../Editor/OverdrawMonitorWindow.cs | 44 +++++++++---------- .../Sources/CameraOverdrawMonitor.cs | 6 +-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs index cdcd13f..3d2d38b 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitorWindow.cs @@ -8,7 +8,7 @@ public class OverdrawMonitorWindow : EditorWindow { bool isEnabled => _monitorsGo != null && Application.isPlaying; GameObject _monitorsGo; - Dictionary _globalMaxInfos; + Dictionary _stats; [MenuItem("Tools/Overdraw Monitor")] static void ShowWindow() @@ -23,7 +23,7 @@ void Init() _monitorsGo = new GameObject("OverdrawMonitor"); _monitorsGo.hideFlags = HideFlags.HideAndDontSave; - _globalMaxInfos = new Dictionary(); + _stats = new Dictionary(); } void TryShutdown() @@ -32,7 +32,7 @@ void TryShutdown() return; DestroyImmediate(_monitorsGo); - _globalMaxInfos = null; + _stats = null; } void Update() @@ -103,12 +103,12 @@ void OnGUI() GUILayout.Space(5); - foreach (CameraOverdrawMonitor monitor in _globalMaxInfos.Keys.ToArray()) + foreach (CameraOverdrawMonitor monitor in _stats.Keys.ToArray()) if (!Array.Exists(monitors, m => monitor)) - _globalMaxInfos.Remove(monitor); + _stats.Remove(monitor); long gameViewArea = gameViewResolution.x * gameViewResolution.y; - float totalGlobalFillRate = 0f; + float totalGlobalOverdrawRatio = 0f; foreach (CameraOverdrawMonitor monitor in monitors) { using (new GUILayout.HorizontalScope()) @@ -118,33 +118,33 @@ void OnGUI() GUILayout.FlexibleSpace(); - float localFillRate = monitor.fillRate; - float globalFillRate = monitor.fragmentsCount / (float)gameViewArea; - totalGlobalFillRate += globalFillRate; + float localOverdrawRatio = monitor.overdrawRatio; + float globalOverdrawRatio = monitor.fragmentsCount / (float)gameViewArea; + totalGlobalOverdrawRatio += globalOverdrawRatio; - if (!_globalMaxInfos.TryGetValue(monitor, out CameraOverdrawStats globalMaxInfo)) + if (!_stats.TryGetValue(monitor, out CameraOverdrawStats monitorStats)) { - globalMaxInfo = new CameraOverdrawStats(); - _globalMaxInfos.Add(monitor, globalMaxInfo); + monitorStats = new CameraOverdrawStats(); + _stats.Add(monitor, monitorStats); } - globalMaxInfo.maxLocalFillRate = Math.Max(localFillRate, globalMaxInfo.maxLocalFillRate); - globalMaxInfo.maxGlobalFillRate = Math.Max(globalFillRate, globalMaxInfo.maxGlobalFillRate); + monitorStats.maxLocalOverdrawRatio = Math.Max(localOverdrawRatio, monitorStats.maxLocalOverdrawRatio); + monitorStats.maxGlobalOverdrawRatio = Math.Max(globalOverdrawRatio, monitorStats.maxGlobalOverdrawRatio); GUILayout.Label(FormatResult("Local: {0} / {1} \t Global: {2} / {3}", - localFillRate, globalMaxInfo.maxLocalFillRate, globalFillRate, globalMaxInfo.maxGlobalFillRate)); + localOverdrawRatio, monitorStats.maxLocalOverdrawRatio, globalOverdrawRatio, monitorStats.maxGlobalOverdrawRatio)); } } GUILayout.Space(5); - float maxTotalGlobalFillRate = 0f; - foreach (CameraOverdrawStats stat in _globalMaxInfos.Values) - maxTotalGlobalFillRate += stat.maxGlobalFillRate; + float maxTotalGlobalOverdrawRatio = 0f; + foreach (CameraOverdrawStats stat in _stats.Values) + maxTotalGlobalOverdrawRatio += stat.maxGlobalOverdrawRatio; using (new GUILayout.HorizontalScope()) { GUILayout.Label("TOTAL"); GUILayout.FlexibleSpace(); - GUILayout.Label(FormatResult("Global: {0} / {1}", totalGlobalFillRate, maxTotalGlobalFillRate)); + GUILayout.Label(FormatResult("Global: {0} / {1}", totalGlobalOverdrawRatio, maxTotalGlobalOverdrawRatio)); } } else @@ -157,7 +157,7 @@ void OnGUI() void ResetStats() { - _globalMaxInfos.Clear(); + _stats.Clear(); } string FormatResult(string format, params float[] args) @@ -177,6 +177,6 @@ static Vector2Int GetGameViewResolution() class CameraOverdrawStats { - public float maxLocalFillRate; - public float maxGlobalFillRate; + public float maxLocalOverdrawRatio; + public float maxGlobalOverdrawRatio; } diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs index 56d3f71..4084d80 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/CameraOverdrawMonitor.cs @@ -22,7 +22,7 @@ public class CameraOverdrawMonitor : MonoBehaviour public Camera targetCamera => _targetCamera; public long fragmentsCount => isActiveAndEnabled ? _fragmentsCount : 0L; - public float fillRate => isActiveAndEnabled ? _fillRate : 0f; + public float overdrawRatio => isActiveAndEnabled ? _overdrawRatio : 0f; Camera _targetCamera; RenderTexture _overdrawTexture; @@ -32,7 +32,7 @@ public class CameraOverdrawMonitor : MonoBehaviour int[] _inputData; int[] _resultData; long _fragmentsCount; - float _fillRate; + float _overdrawRatio; void Awake() { @@ -115,7 +115,7 @@ void LateUpdate() _fragmentsCount = 0; foreach (int res in _resultData) _fragmentsCount += res; - _fillRate = fragmentsCount / ((float)_overdrawTexture.width * _overdrawTexture.height); + _overdrawRatio = fragmentsCount / ((float)_overdrawTexture.width * _overdrawTexture.height); // Restore original params _targetCamera.targetTexture = originalTargetTexture; From 5a1c3f036eba5a8195222bcb3e572f49cb99bc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 20 Nov 2019 09:05:56 +0300 Subject: [PATCH 30/31] Package manager --- .../Editor/OverdrawMonitor.Editor.asmdef | 16 ++++++++++++++++ .../Editor/OverdrawMonitor.Editor.asmdef.meta | 7 +++++++ .../Assets/OverdrawMonitor/Resources.meta | 5 +++-- .../Assets/OverdrawMonitor/Sources.meta | 2 +- .../Sources/OverdrawMonitor.asmdef | 3 +++ .../Sources/OverdrawMonitor.asmdef.meta | 7 +++++++ .../Assets/OverdrawMonitor/package.json | 6 ++++++ .../Assets/OverdrawMonitor/package.json.meta | 7 +++++++ 8 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef.meta create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef.meta create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/package.json create mode 100644 OverdrawMonitor/Assets/OverdrawMonitor/package.json.meta diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef new file mode 100644 index 0000000..43e4d52 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef @@ -0,0 +1,16 @@ +{ + "name": "OverdrawMonitor.Editor", + "references": [ + "GUID:7594097e076d14122ac321968bbd88c0" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [] +} \ No newline at end of file diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef.meta new file mode 100644 index 0000000..b6334a3 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Editor/OverdrawMonitor.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e3bb7e2192ed44d3fa17492fe70682fa +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta index cf033ad..01cff07 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Resources.meta @@ -1,8 +1,9 @@ fileFormatVersion: 2 -guid: 90c7d44c95a9d451788e15da842abf3c +guid: 633512af7657fe7469c43935e7fee4fc folderAsset: yes +timeCreated: 1498554083 +licenseType: Pro DefaultImporter: - externalObjects: {} userData: assetBundleName: assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta index 0d90291..751206a 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Sources.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 42f490e540ce247059a4da7f1af3877a +guid: c065eaf008ca949a499e1ff3ca3d5e3b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef new file mode 100644 index 0000000..753dd14 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef @@ -0,0 +1,3 @@ +{ + "name": "OverdrawMonitor" +} diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef.meta b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef.meta new file mode 100644 index 0000000..187e384 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/Sources/OverdrawMonitor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7594097e076d14122ac321968bbd88c0 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/package.json b/OverdrawMonitor/Assets/OverdrawMonitor/package.json new file mode 100644 index 0000000..1563a9c --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/package.json @@ -0,0 +1,6 @@ +{ + "name": "ken48.overdraw_monitor", + "displayName": "Overdraw Monitor", + "version": "1.0.0", + "unity": "2019.1" +} \ No newline at end of file diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/package.json.meta b/OverdrawMonitor/Assets/OverdrawMonitor/package.json.meta new file mode 100644 index 0000000..fc6b7a1 --- /dev/null +++ b/OverdrawMonitor/Assets/OverdrawMonitor/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8e2e1b17e45fe492cb35960bdbf4abd4 +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From b2ee9f2fa2522fa3733f1ddd8bfb813c4553705e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Kirill=20Belonogov?= Date: Wed, 20 Nov 2019 09:25:54 +0300 Subject: [PATCH 31/31] Package name fixed --- OverdrawMonitor/Assets/OverdrawMonitor/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OverdrawMonitor/Assets/OverdrawMonitor/package.json b/OverdrawMonitor/Assets/OverdrawMonitor/package.json index 1563a9c..8a7ab57 100644 --- a/OverdrawMonitor/Assets/OverdrawMonitor/package.json +++ b/OverdrawMonitor/Assets/OverdrawMonitor/package.json @@ -1,5 +1,5 @@ { - "name": "ken48.overdraw_monitor", + "name": "ken48.overdrawmonitor", "displayName": "Overdraw Monitor", "version": "1.0.0", "unity": "2019.1"