| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- using UnityEngine;
- using UnityEditor;
- using System.IO;
- using System.Collections.Generic;
- using UnityEngine.Rendering;
- namespace Cinemachine.Editor
- {
- internal class WaveformWindow : EditorWindow
- {
- WaveformGenerator mWaveformGenerator;
- Texture2D mScreenshot;
- string mScreenshotFilename;
- // Controls how frequently (in seconds) the view will update.
- // Performance is really bad, so keep this as large as possible.
- public static float UpdateInterval = 0.5f;
- public static void SetDefaultUpdateInterval() { UpdateInterval = 0.5f; }
- //[MenuItem("Window/Waveform Monitor")]
- public static void OpenWindow()
- {
- WaveformWindow window = EditorWindow.GetWindow<WaveformWindow>(false);
- window.autoRepaintOnSceneChange = true;
- //window.position = new Rect(100, 100, 400, 400);
- window.Show(true);
- }
- private void OnEnable()
- {
- titleContent = new GUIContent("Waveform", CinemachineSettings.CinemachineLogoTexture);
- mWaveformGenerator = new WaveformGenerator();
- mScreenshotFilename = Path.GetFullPath(FileUtil.GetUniqueTempPathInProject() + ".png");
- ScreenCapture.CaptureScreenshot(mScreenshotFilename);
- EditorApplication.update += UpdateScreenshot;
- }
- private void OnDisable()
- {
- EditorApplication.update -= UpdateScreenshot;
- if (!string.IsNullOrEmpty(mScreenshotFilename) && File.Exists(mScreenshotFilename))
- File.Delete(mScreenshotFilename);
- mScreenshotFilename = null;
- mWaveformGenerator.DestroyBuffers();
- if (mScreenshot != null)
- DestroyImmediate(mScreenshot);
- mScreenshot = null;
- }
- private void OnGUI()
- {
- Rect rect = EditorGUILayout.GetControlRect(true);
- EditorGUIUtility.labelWidth /= 2;
- EditorGUI.BeginChangeCheck();
- mWaveformGenerator.m_Exposure = EditorGUI.Slider(
- rect, "Exposure", mWaveformGenerator.m_Exposure, 0.01f, 2);
- if (EditorGUI.EndChangeCheck())
- UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
- EditorGUIUtility.labelWidth *= 2;
- rect.y += rect.height;
- rect.height = position.height - rect.height;
- var tex = mWaveformGenerator.Result;
- if (tex != null)
- GUI.DrawTexture(rect, tex);
- }
- float mLastUpdateTime = 0;
- private void UpdateScreenshot()
- {
- // Don't do this too often
- float now = Time.time;
- if (mScreenshot != null && now - mLastUpdateTime < UpdateInterval)
- return;
- mLastUpdateTime = now;
- if (!string.IsNullOrEmpty(mScreenshotFilename) && File.Exists(mScreenshotFilename))
- {
- byte[] fileData = File.ReadAllBytes(mScreenshotFilename);
- if (mScreenshot == null)
- mScreenshot = new Texture2D(2, 2);
- mScreenshot.LoadImage(fileData); // this will auto-resize the texture dimensions.
- mWaveformGenerator.RenderWaveform(mScreenshot);
- // Capture the next one
- ScreenCapture.CaptureScreenshot(mScreenshotFilename);
- }
- }
- class WaveformGenerator
- {
- public float m_Exposure = 0.2f;
- RenderTexture mOutput;
- ComputeBuffer mData;
- int mThreadGroupSize;
- int mThreadGroupSizeX;
- int mThreadGroupSizeY;
- ComputeShader mWaveformCompute;
- MaterialPropertyBlock mWaveformProperties;
- Material mWaveformMaterial;
- CommandBuffer mCmd;
- static Mesh sFullscreenTriangle;
- static Mesh FullscreenTriangle
- {
- get
- {
- if (sFullscreenTriangle == null)
- {
- sFullscreenTriangle = new Mesh { name = "Fullscreen Triangle" };
- sFullscreenTriangle.SetVertices(new List<Vector3>
- {
- new Vector3(-1f, -1f, 0f),
- new Vector3(-1f, 3f, 0f),
- new Vector3( 3f, -1f, 0f)
- });
- sFullscreenTriangle.SetIndices(
- new [] { 0, 1, 2 }, MeshTopology.Triangles, 0, false);
- sFullscreenTriangle.UploadMeshData(false);
- }
- return sFullscreenTriangle;
- }
- }
- public WaveformGenerator()
- {
- mWaveformCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>(
- ScriptableObjectUtility.CinemachineRealativeInstallPath
- + "/Editor/EditorResources/CMWaveform.compute");
- mWaveformProperties = new MaterialPropertyBlock();
- mWaveformMaterial = new Material(AssetDatabase.LoadAssetAtPath<Shader>(
- ScriptableObjectUtility.CinemachineRealativeInstallPath
- + "/Editor/EditorResources/CMWaveform.shader"))
- {
- name = "CMWaveformMaterial",
- hideFlags = HideFlags.DontSave
- };
- mCmd = new CommandBuffer();
- }
- void CreateBuffers(int width, int height)
- {
- if (mOutput == null || !mOutput.IsCreated()
- || mOutput.width != width || mOutput.height != height)
- {
- DestroyImmediate(mOutput);
- mOutput = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32)
- {
- anisoLevel = 0,
- filterMode = FilterMode.Bilinear,
- wrapMode = TextureWrapMode.Clamp,
- useMipMap = false
- };
- mOutput.Create();
- }
- int count = Mathf.CeilToInt(width / (float)mThreadGroupSizeX) * mThreadGroupSizeX * height;
- if (mData == null)
- mData = new ComputeBuffer(count, sizeof(uint) << 2);
- else if (mData.count < count)
- {
- mData.Release();
- mData = new ComputeBuffer(count, sizeof(uint) << 2);
- }
- }
- public void DestroyBuffers()
- {
- if (mData != null)
- mData.Release();
- mData = null;
- DestroyImmediate(mOutput);
- mOutput = null;
- }
- public RenderTexture Result { get { return mOutput; } }
- public void RenderWaveform(Texture2D source)
- {
- if (mWaveformMaterial == null)
- return;
- int width = source.width;
- int height = source.height;
- int histogramResolution = 256;
- mThreadGroupSize = 256;
- mThreadGroupSizeX = 16;
- mThreadGroupSizeY = 16;
- CreateBuffers(width, histogramResolution);
- mCmd.Clear();
- mCmd.BeginSample("CMWaveform");
- var parameters = new Vector4(
- width, height,
- QualitySettings.activeColorSpace == ColorSpace.Linear ? 1 : 0,
- histogramResolution);
- // Clear the buffer on every frame
- int kernel = mWaveformCompute.FindKernel("KCMWaveformClear");
- mCmd.SetComputeBufferParam(mWaveformCompute, kernel, "_WaveformBuffer", mData);
- mCmd.SetComputeVectorParam(mWaveformCompute, "_Params", parameters);
- mCmd.DispatchCompute(mWaveformCompute, kernel,
- Mathf.CeilToInt(width / (float)mThreadGroupSizeX),
- Mathf.CeilToInt(histogramResolution / (float)mThreadGroupSizeY), 1);
- // Gather all pixels and fill in our waveform
- kernel = mWaveformCompute.FindKernel("KCMWaveformGather");
- mCmd.SetComputeBufferParam(mWaveformCompute, kernel, "_WaveformBuffer", mData);
- mCmd.SetComputeTextureParam(mWaveformCompute, kernel, "_Source", source);
- mCmd.SetComputeVectorParam(mWaveformCompute, "_Params", parameters);
- mCmd.DispatchCompute(mWaveformCompute, kernel, width,
- Mathf.CeilToInt(height / (float)mThreadGroupSize), 1);
- // Generate the waveform texture
- float exposure = Mathf.Max(0f, m_Exposure);
- exposure *= (float)histogramResolution / height;
- mWaveformProperties.SetVector(Shader.PropertyToID("_Params"),
- new Vector4(width, histogramResolution, exposure, 0f));
- mWaveformProperties.SetBuffer(Shader.PropertyToID("_WaveformBuffer"), mData);
- mCmd.SetRenderTarget(mOutput);
- mCmd.DrawMesh(
- FullscreenTriangle, Matrix4x4.identity,
- mWaveformMaterial, 0, 0, mWaveformProperties);
- mCmd.EndSample("CMWaveform");
- Graphics.ExecuteCommandBuffer(mCmd);
- }
- }
- }
- }
|