diff --git a/Runtime/OpticalFlow/OpticalFlow.cs b/Runtime/OpticalFlow/OpticalFlow.cs index b09884c..c3fa045 100644 --- a/Runtime/OpticalFlow/OpticalFlow.cs +++ b/Runtime/OpticalFlow/OpticalFlow.cs @@ -14,6 +14,7 @@ namespace FidelityFX.OpticalFlow public struct ContextDescription { public Vector2Int resolution; + public OpticalFlowShaders shaders; } public struct DispatchDescription diff --git a/Runtime/OpticalFlow/OpticalFlowContext.cs b/Runtime/OpticalFlow/OpticalFlowContext.cs index 17229a1..a3a950e 100644 --- a/Runtime/OpticalFlow/OpticalFlowContext.cs +++ b/Runtime/OpticalFlow/OpticalFlowContext.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Rendering; @@ -6,12 +7,191 @@ namespace FidelityFX.OpticalFlow { public class OpticalFlowContext { - private bool _firstExecution = true; - private uint _resourceFrameIndex; + private const int MaxQueuedFrames = 16; + + private OpticalFlow.ContextDescription _contextDescription; + + private OpticalFlowPass _prepareLumaPass; + private OpticalFlowPass _generateInputPyramidPass; + private OpticalFlowPass _generateScdHistogramPass; + private OpticalFlowPass _computeScdDivergencePass; + private OpticalFlowPass _computeOpticalFlowPass; + private OpticalFlowPass _filterOpticalFlowPass; + private OpticalFlowPass _scaleOpticalFlowPass; + + private readonly OpticalFlowResources _resources = new OpticalFlowResources(); + + private ComputeBuffer _opticalFlowConstantsBuffer; + private readonly OpticalFlow.OpticalFlowConstants[] _opticalFlowConstantsArray = { new OpticalFlow.OpticalFlowConstants() }; + private ref OpticalFlow.OpticalFlowConstants Constants => ref _opticalFlowConstantsArray[0]; + + private ComputeBuffer _spdConstantsBuffer; + private readonly OpticalFlow.SpdConstants[] _spdConstantsArray = { new OpticalFlow.SpdConstants() }; + private ref OpticalFlow.SpdConstants SpdConsts => ref _spdConstantsArray[0]; + + private bool _firstExecution; + private int _resourceFrameIndex; + + public void Create(OpticalFlow.ContextDescription contextDescription) + { + _contextDescription = contextDescription; + + _opticalFlowConstantsBuffer = CreateConstantBuffer(); + _spdConstantsBuffer = CreateConstantBuffer(); + + _firstExecution = true; + _resourceFrameIndex = 0; + + Constants.inputLumaResolution = _contextDescription.resolution; + + _resources.Create(_contextDescription); + CreatePasses(); + } + + private void CreatePasses() + { + _prepareLumaPass = new OpticalFlowPrepareLumaPass(_contextDescription, _resources, _opticalFlowConstantsBuffer); + _generateInputPyramidPass = new OpticalFlowGenerateInputPyramidPass(_contextDescription, _resources, _opticalFlowConstantsBuffer, _spdConstantsBuffer); + _generateScdHistogramPass = new OpticalFlowGenerateSCDHistogramPass(_contextDescription, _resources, _opticalFlowConstantsBuffer); + _computeScdDivergencePass = new OpticalFlowComputeSCDDivergencePass(_contextDescription, _resources, _opticalFlowConstantsBuffer); + _computeOpticalFlowPass = new OpticalFlowComputePass(_contextDescription, _resources, _opticalFlowConstantsBuffer); + _filterOpticalFlowPass = new OpticalFlowFilterPass(_contextDescription, _resources, _opticalFlowConstantsBuffer); + _scaleOpticalFlowPass = new OpticalFlowScalePass(_contextDescription, _resources, _opticalFlowConstantsBuffer); + } + + public void Destroy() + { + DestroyPass(ref _scaleOpticalFlowPass); + DestroyPass(ref _filterOpticalFlowPass); + DestroyPass(ref _computeOpticalFlowPass); + DestroyPass(ref _computeScdDivergencePass); + DestroyPass(ref _generateScdHistogramPass); + DestroyPass(ref _generateInputPyramidPass); + DestroyPass(ref _prepareLumaPass); + + _resources.Destroy(); + + DestroyConstantBuffer(ref _spdConstantsBuffer); + DestroyConstantBuffer(ref _opticalFlowConstantsBuffer); + } public void Dispatch(CommandBuffer commandBuffer, in OpticalFlow.DispatchDescription dispatchDescription) { - throw new NotImplementedException(); + const int advancedAlgorithmIterations = 7; + const int opticalFlowBlockSize = 8; + + Constants.backbufferTransferFunction = (uint)dispatchDescription.backbufferTransferFunction; + Constants.minMaxLuminance = dispatchDescription.minMaxLuminance; + + int frameIndex = _resourceFrameIndex % 2; + bool resetAccumulation = dispatchDescription.reset || _firstExecution; + _firstExecution = false; + + if (resetAccumulation) + Constants.frameIndex = 0; + else + Constants.frameIndex++; + + if (resetAccumulation) + { + commandBuffer.SetRenderTarget(_resources.OpticalFlowSCDTemp); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(dispatchDescription.opticalFlowSCD.RenderTarget); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowSCDHistogram); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowSCDPreviousHistogram); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + for (int i = 0; i < 2; ++i) + { + commandBuffer.SetRenderTarget(_resources.OpticalFlowInput[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowInputLevel1[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowInputLevel2[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowInputLevel3[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowInputLevel4[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowInputLevel5[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + commandBuffer.SetRenderTarget(_resources.OpticalFlowInputLevel6[i]); + commandBuffer.ClearRenderTarget(false, true, Color.clear); + } + } + + SetupSpdConstants(out var dispatchThreadGroupCount); + + commandBuffer.SetBufferData(_opticalFlowConstantsBuffer, _opticalFlowConstantsArray); + commandBuffer.SetBufferData(_spdConstantsBuffer, _spdConstantsArray); + + { + const int threadGroupSizeX = 16; + const int threadGroupSizeY = 16; + const int threadPixelsX = 2; + const int threadPixelsY = 2; + int dispatchX = ((_contextDescription.resolution.x + (threadPixelsX - 1)) / threadPixelsX + (threadGroupSizeX - 1)) / threadGroupSizeX; + int dispatchY = ((_contextDescription.resolution.y + (threadPixelsY - 1)) / threadPixelsY + (threadGroupSizeY - 1)) / threadGroupSizeY; + _prepareLumaPass.ScheduleDispatch(commandBuffer, dispatchDescription, frameIndex, dispatchX, dispatchY); + } + + _resourceFrameIndex = (_resourceFrameIndex + 1) % MaxQueuedFrames; + } + + private void SetupSpdConstants(out Vector2Int dispatchThreadGroupCount) + { + const int resolutionMultiplier = 1; + + RectInt rectInfo = new RectInt(0, 0, _contextDescription.resolution.x * resolutionMultiplier, _contextDescription.resolution.y * resolutionMultiplier); + SpdSetup(rectInfo, out dispatchThreadGroupCount, out var workGroupOffset, out var numWorkGroupsAndMips, 4); + + ref OpticalFlow.SpdConstants spdConstants = ref SpdConsts; + spdConstants.numWorkGroups = (uint)numWorkGroupsAndMips.x; + spdConstants.mips = (uint)numWorkGroupsAndMips.y; + spdConstants.workGroupOffsetX = (uint)workGroupOffset.x; + spdConstants.workGroupOffsetY = (uint)workGroupOffset.y; + spdConstants.numWorkGroupsOpticalFlowInputPyramid = (uint)numWorkGroupsAndMips.x; + } + + private static void SpdSetup(RectInt rectInfo, out Vector2Int dispatchThreadGroupCount, out Vector2Int workGroupOffset, out Vector2Int numWorkGroupsAndMips, int mips = -1) + { + workGroupOffset = new Vector2Int(rectInfo.x / 64, rectInfo.y / 64); + + int endIndexX = (rectInfo.x + rectInfo.width - 1) / 64; + int endIndexY = (rectInfo.y + rectInfo.height - 1) / 64; + + dispatchThreadGroupCount = new Vector2Int(endIndexX + 1 - workGroupOffset.x, endIndexY + 1 - workGroupOffset.y); + + numWorkGroupsAndMips = new Vector2Int(dispatchThreadGroupCount.x * dispatchThreadGroupCount.y, mips); + if (mips < 0) + { + float resolution = Math.Max(rectInfo.width, rectInfo.height); + numWorkGroupsAndMips.y = Math.Min(Mathf.FloorToInt(Mathf.Log(resolution, 2.0f)), 12); + } + } + + private static ComputeBuffer CreateConstantBuffer() where TConstants: struct + { + return new ComputeBuffer(1, Marshal.SizeOf(), ComputeBufferType.Constant); + } + + private static void DestroyConstantBuffer(ref ComputeBuffer bufferRef) + { + if (bufferRef == null) + return; + + bufferRef.Release(); + bufferRef = null; + } + + private static void DestroyPass(ref OpticalFlowPass pass) + { + if (pass == null) + return; + + pass.Dispose(); + pass = null; } } } diff --git a/Runtime/OpticalFlow/OpticalFlowPass.cs b/Runtime/OpticalFlow/OpticalFlowPass.cs new file mode 100644 index 0000000..e4202fb --- /dev/null +++ b/Runtime/OpticalFlow/OpticalFlowPass.cs @@ -0,0 +1,154 @@ +using System; +using System.Runtime.InteropServices; +using UnityEngine; +using UnityEngine.Profiling; +using UnityEngine.Rendering; + +namespace FidelityFX.OpticalFlow +{ + internal abstract class OpticalFlowPass: IDisposable + { + protected readonly OpticalFlow.ContextDescription ContextDescription; + protected readonly OpticalFlowResources Resources; + protected readonly ComputeBuffer Constants; + + protected ComputeShader ComputeShader; + protected int KernelIndex; + + private CustomSampler _sampler; + + protected OpticalFlowPass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + { + ContextDescription = contextDescription; + Resources = resources; + Constants = constants; + } + + public virtual void Dispose() + { + } + + public void ScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + commandBuffer.BeginSample(_sampler); + DoScheduleDispatch(commandBuffer, dispatchParams, frameIndex, dispatchX, dispatchY); + commandBuffer.EndSample(_sampler); + } + + protected abstract void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY); + + protected void InitComputeShader(string passName, ComputeShader shader) + { + if (shader == null) + { + throw new MissingReferenceException($"Shader for Optical Flow pass '{passName}' could not be loaded! Please ensure it is included in the project correctly."); + } + + ComputeShader = shader; + KernelIndex = ComputeShader.FindKernel("CS"); + _sampler = CustomSampler.Create(passName); + } + } + + internal class OpticalFlowPrepareLumaPass : OpticalFlowPass + { + public OpticalFlowPrepareLumaPass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + InitComputeShader("Prepare Luma", contextDescription.shaders.prepareLuma); + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + ref var color = ref dispatchParams.color; + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, OpticalFlowShaderIDs.SrvInputColor, color.RenderTarget, color.MipLevel, color.SubElement); + + commandBuffer.SetComputeTextureParam(ComputeShader, KernelIndex, OpticalFlowShaderIDs.UavOpticalFlowInput, Resources.OpticalFlowInput[frameIndex]); + + commandBuffer.SetComputeConstantBufferParam(ComputeShader, OpticalFlowShaderIDs.CbOpticalFlow, Constants, 0, Marshal.SizeOf()); + + commandBuffer.DispatchCompute(ComputeShader, KernelIndex, dispatchX, dispatchY, 1); + } + } + + internal class OpticalFlowGenerateInputPyramidPass : OpticalFlowPass + { + private ComputeBuffer _spdConstants; + + public OpticalFlowGenerateInputPyramidPass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants, ComputeBuffer spdConstants) + : base(contextDescription, resources, constants) + { + _spdConstants = spdConstants; + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + throw new NotImplementedException(); + } + } + + internal class OpticalFlowGenerateSCDHistogramPass : OpticalFlowPass + { + public OpticalFlowGenerateSCDHistogramPass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + throw new NotImplementedException(); + } + } + + internal class OpticalFlowComputeSCDDivergencePass : OpticalFlowPass + { + public OpticalFlowComputeSCDDivergencePass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + throw new NotImplementedException(); + } + } + + internal class OpticalFlowComputePass : OpticalFlowPass + { + public OpticalFlowComputePass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + throw new NotImplementedException(); + } + } + + internal class OpticalFlowFilterPass : OpticalFlowPass + { + public OpticalFlowFilterPass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + throw new NotImplementedException(); + } + } + + internal class OpticalFlowScalePass : OpticalFlowPass + { + public OpticalFlowScalePass(OpticalFlow.ContextDescription contextDescription, OpticalFlowResources resources, ComputeBuffer constants) + : base(contextDescription, resources, constants) + { + } + + protected override void DoScheduleDispatch(CommandBuffer commandBuffer, OpticalFlow.DispatchDescription dispatchParams, int frameIndex, int dispatchX, int dispatchY) + { + throw new NotImplementedException(); + } + } +} diff --git a/Runtime/OpticalFlow/OpticalFlowPass.cs.meta b/Runtime/OpticalFlow/OpticalFlowPass.cs.meta new file mode 100644 index 0000000..0b66433 --- /dev/null +++ b/Runtime/OpticalFlow/OpticalFlowPass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a509b94b3bffef549b9d3316b44df221 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpticalFlow/OpticalFlowResources.cs b/Runtime/OpticalFlow/OpticalFlowResources.cs new file mode 100644 index 0000000..a36a198 --- /dev/null +++ b/Runtime/OpticalFlow/OpticalFlowResources.cs @@ -0,0 +1,189 @@ +using UnityEngine; +using UnityEngine.Experimental.Rendering; + +namespace FidelityFX.OpticalFlow +{ + internal class OpticalFlowResources + { + private const int OpticalFlowMaxPyramidLevels = 7; + private const int HistogramBins = 256; + private const int HistogramsPerDim = 3; + private const int HistogramShifts = 3; + + public readonly RenderTexture[] OpticalFlowInput = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowInputLevel1 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowInputLevel2 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowInputLevel3 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowInputLevel4 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowInputLevel5 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowInputLevel6 = new RenderTexture[2]; + + public readonly RenderTexture[] OpticalFlow = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowLevel1 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowLevel2 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowLevel3 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowLevel4 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowLevel5 = new RenderTexture[2]; + public readonly RenderTexture[] OpticalFlowLevel6 = new RenderTexture[2]; + + public RenderTexture OpticalFlowSCDHistogram; + public RenderTexture OpticalFlowSCDPreviousHistogram; + public RenderTexture OpticalFlowSCDTemp; + + public void Create(OpticalFlow.ContextDescription contextDescription) + { + Vector2Int opticalFlowInputTextureSize = contextDescription.resolution; + + const int minBlockSize = 8; + Vector2Int opticalFlowTextureSize = GetOpticalFlowTextureSize(contextDescription.resolution, minBlockSize); + Vector2Int opticalFlowLevel1TextureSize = NextLevelSize(opticalFlowTextureSize); + Vector2Int opticalFlowLevel2TextureSize = NextLevelSize(opticalFlowLevel1TextureSize); + Vector2Int opticalFlowLevel3TextureSize = NextLevelSize(opticalFlowLevel2TextureSize); + Vector2Int opticalFlowLevel4TextureSize = NextLevelSize(opticalFlowLevel3TextureSize); + Vector2Int opticalFlowLevel5TextureSize = NextLevelSize(opticalFlowLevel4TextureSize); + Vector2Int opticalFlowLevel6TextureSize = NextLevelSize(opticalFlowLevel5TextureSize); + + Vector2Int opticalFlowHistogramTextureSize = GetOpticalFlowHistogramSize(0); + Vector2Int globalMotionSearchMaxDispatchSize = GetGlobalMotionSearchDispatchSize(0); + int globalMotionSearchTextureWidth = 4 + (globalMotionSearchMaxDispatchSize.x * globalMotionSearchMaxDispatchSize.y); + + CreateDoubleBufferedResource(OpticalFlowInput, "OPTICALFLOW_OpticalFlowInput", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + opticalFlowInputTextureSize = new Vector2Int(opticalFlowInputTextureSize.x >> 1, opticalFlowInputTextureSize.y >> 1); + CreateDoubleBufferedResource(OpticalFlowInputLevel1, "OPTICALFLOW_OpticalFlowInputLevel1-", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + opticalFlowInputTextureSize = new Vector2Int(opticalFlowInputTextureSize.x >> 1, opticalFlowInputTextureSize.y >> 1); + CreateDoubleBufferedResource(OpticalFlowInputLevel2, "OPTICALFLOW_OpticalFlowInputLevel2-", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + opticalFlowInputTextureSize = new Vector2Int(opticalFlowInputTextureSize.x >> 1, opticalFlowInputTextureSize.y >> 1); + CreateDoubleBufferedResource(OpticalFlowInputLevel3, "OPTICALFLOW_OpticalFlowInputLevel3-", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + opticalFlowInputTextureSize = new Vector2Int(opticalFlowInputTextureSize.x >> 1, opticalFlowInputTextureSize.y >> 1); + CreateDoubleBufferedResource(OpticalFlowInputLevel4, "OPTICALFLOW_OpticalFlowInputLevel4-", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + opticalFlowInputTextureSize = new Vector2Int(opticalFlowInputTextureSize.x >> 1, opticalFlowInputTextureSize.y >> 1); + CreateDoubleBufferedResource(OpticalFlowInputLevel5, "OPTICALFLOW_OpticalFlowInputLevel5-", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + opticalFlowInputTextureSize = new Vector2Int(opticalFlowInputTextureSize.x >> 1, opticalFlowInputTextureSize.y >> 1); + CreateDoubleBufferedResource(OpticalFlowInputLevel6, "OPTICALFLOW_OpticalFlowInputLevel6-", opticalFlowInputTextureSize, GraphicsFormat.R8_UInt); + + CreateDoubleBufferedResource(OpticalFlow, "OPTICALFLOW_OpticalFlow", opticalFlowTextureSize, GraphicsFormat.R16G16_SInt); + CreateDoubleBufferedResource(OpticalFlowLevel1, "OPTICALFLOW_OpticalFlowLevel1-", opticalFlowLevel1TextureSize, GraphicsFormat.R16G16_SInt); + CreateDoubleBufferedResource(OpticalFlowLevel2, "OPTICALFLOW_OpticalFlowLevel2-", opticalFlowLevel2TextureSize, GraphicsFormat.R16G16_SInt); + CreateDoubleBufferedResource(OpticalFlowLevel3, "OPTICALFLOW_OpticalFlowLevel3-", opticalFlowLevel3TextureSize, GraphicsFormat.R16G16_SInt); + CreateDoubleBufferedResource(OpticalFlowLevel4, "OPTICALFLOW_OpticalFlowLevel4-", opticalFlowLevel4TextureSize, GraphicsFormat.R16G16_SInt); + CreateDoubleBufferedResource(OpticalFlowLevel5, "OPTICALFLOW_OpticalFlowLevel5-", opticalFlowLevel5TextureSize, GraphicsFormat.R16G16_SInt); + CreateDoubleBufferedResource(OpticalFlowLevel6, "OPTICALFLOW_OpticalFlowLevel6-", opticalFlowLevel6TextureSize, GraphicsFormat.R16G16_SInt); + + OpticalFlowSCDHistogram = CreateResource("OPTICALFLOW_OpticalFlowSCDHistogram", new Vector2Int(GetSCDHistogramTextureWidth(), 1), GraphicsFormat.R32_UInt); + OpticalFlowSCDPreviousHistogram = CreateResource("OPTICALFLOW_OpticalFlowSCDPreviousHistogram", new Vector2Int(GetSCDHistogramTextureWidth(), 1), GraphicsFormat.R32_SFloat); + OpticalFlowSCDTemp = CreateResource("OPTICALFLOW_OpticalFlowSCDTemp", new Vector2Int(3, 1), GraphicsFormat.R32_UInt); + } + + public void Destroy() + { + DestroyResource(ref OpticalFlowSCDTemp); + DestroyResource(ref OpticalFlowSCDPreviousHistogram); + DestroyResource(ref OpticalFlowSCDHistogram); + + DestroyResource(OpticalFlowLevel6); + DestroyResource(OpticalFlowLevel5); + DestroyResource(OpticalFlowLevel4); + DestroyResource(OpticalFlowLevel3); + DestroyResource(OpticalFlowLevel2); + DestroyResource(OpticalFlowLevel1); + DestroyResource(OpticalFlow); + + DestroyResource(OpticalFlowInputLevel6); + DestroyResource(OpticalFlowInputLevel5); + DestroyResource(OpticalFlowInputLevel4); + DestroyResource(OpticalFlowInputLevel3); + DestroyResource(OpticalFlowInputLevel2); + DestroyResource(OpticalFlowInputLevel1); + DestroyResource(OpticalFlowInput); + } + + private static Vector2Int NextLevelSize(Vector2Int size) + { + return new Vector2Int(AlignUp(size.x, 2) / 2, AlignUp(size.y, 2) / 2); + } + + private static int AlignUp(int x, int y) + { + return (x + (y - 1)) & ~(y - 1); + } + + private static Vector2Int GetOpticalFlowTextureSize(Vector2Int displaySize, int opticalFlowBlockSize) + { + int width = (displaySize.x + opticalFlowBlockSize - 1) / opticalFlowBlockSize; + int height = (displaySize.y + opticalFlowBlockSize - 1) / opticalFlowBlockSize; + return new Vector2Int(width, height); + } + + private static Vector2Int GetOpticalFlowHistogramSize(int level) + { + const int searchRadius = 8; + int maxVelocity = searchRadius * (1 << (OpticalFlowMaxPyramidLevels - 1 - level)); + int binsPerDimension = 2 * maxVelocity + 1; + return new Vector2Int(binsPerDimension, binsPerDimension); + } + + private static Vector2Int GetGlobalMotionSearchDispatchSize(int level) + { + const int threadGroupSizeX = 16; + const int threadGroupSizeY = 16; + Vector2Int opticalFlowHistogramSize = GetOpticalFlowHistogramSize(level); + int additionalElementsDueToShiftsX = opticalFlowHistogramSize.x / threadGroupSizeX; + int additionalElementsDueToShiftsY = opticalFlowHistogramSize.y / threadGroupSizeY; + int dispatchX = (opticalFlowHistogramSize.x + additionalElementsDueToShiftsX + threadGroupSizeX - 1) / threadGroupSizeX; + int dispatchY = (opticalFlowHistogramSize.y + additionalElementsDueToShiftsY + threadGroupSizeY - 1) / threadGroupSizeY; + return new Vector2Int(dispatchX, dispatchY); + } + + private static int GetSCDHistogramTextureWidth() + { + return HistogramBins * (HistogramsPerDim * HistogramsPerDim); + } + + private static RenderTexture CreateResource(string name, Vector2Int size, GraphicsFormat format) + { + var rt = new RenderTexture(size.x, size.y, 0, format) { name = name, enableRandomWrite = true }; + rt.Create(); + return rt; + } + + private static void CreateDoubleBufferedResource(RenderTexture[] resource, string name, Vector2Int size, GraphicsFormat format) + { + for (int i = 0; i < 2; ++i) + { + resource[i] = new RenderTexture(size.x, size.y, 0, format) { name = name + (i + 1), enableRandomWrite = true }; + resource[i].Create(); + } + } + + private static void DestroyResource(ref Texture2D resource) + { + if (resource == null) + return; + +#if UNITY_EDITOR + if (Application.isPlaying && !UnityEditor.EditorApplication.isPaused) + UnityEngine.Object.Destroy(resource); + else + UnityEngine.Object.DestroyImmediate(resource); +#else + UnityEngine.Object.Destroy(resource); +#endif + resource = null; + } + + private static void DestroyResource(ref RenderTexture resource) + { + if (resource == null) + return; + + resource.Release(); + resource = null; + } + + private static void DestroyResource(RenderTexture[] resource) + { + for (int i = 0; i < resource.Length; ++i) + DestroyResource(ref resource[i]); + } + } +} diff --git a/Runtime/OpticalFlow/OpticalFlowResources.cs.meta b/Runtime/OpticalFlow/OpticalFlowResources.cs.meta new file mode 100644 index 0000000..604d1e9 --- /dev/null +++ b/Runtime/OpticalFlow/OpticalFlowResources.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56e728c675f67404a966d919d71f9041 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpticalFlow/OpticalFlowShaderIDs.cs b/Runtime/OpticalFlow/OpticalFlowShaderIDs.cs new file mode 100644 index 0000000..4a46cd6 --- /dev/null +++ b/Runtime/OpticalFlow/OpticalFlowShaderIDs.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace FidelityFX.OpticalFlow +{ + public static class OpticalFlowShaderIDs + { + // Shader resource views, i.e. read-only bindings + public static readonly int SrvInputColor = Shader.PropertyToID("r_input_color"); + public static readonly int SrvOpticalFlowInput = Shader.PropertyToID("r_optical_flow_input"); + public static readonly int SrvOpticalFlowPreviousInput = Shader.PropertyToID("r_optical_flow_previous_input"); + public static readonly int SrvOpticalFlow = Shader.PropertyToID("r_optical_flow"); + public static readonly int SrvOpticalFlowPrevious = Shader.PropertyToID("r_optical_flow_previous"); + + // Unordered access views, i.e. random read/write bindings + public static readonly int UavOpticalFlowInput = Shader.PropertyToID("rw_optical_flow_input"); + public static readonly int UavOpticalFlowInputLevel1 = Shader.PropertyToID("rw_optical_flow_input_level_1"); + public static readonly int UavOpticalFlowInputLevel2 = Shader.PropertyToID("rw_optical_flow_input_level_2"); + public static readonly int UavOpticalFlowInputLevel3 = Shader.PropertyToID("rw_optical_flow_input_level_3"); + public static readonly int UavOpticalFlowInputLevel4 = Shader.PropertyToID("rw_optical_flow_input_level_4"); + public static readonly int UavOpticalFlowInputLevel5 = Shader.PropertyToID("rw_optical_flow_input_level_5"); + public static readonly int UavOpticalFlowInputLevel6 = Shader.PropertyToID("rw_optical_flow_input_level_6"); + public static readonly int UavOpticalFlow = Shader.PropertyToID("rw_optical_flow"); + public static readonly int UavOpticalFlowNextLevel = Shader.PropertyToID("rw_optical_flow_next_level"); + public static readonly int UavOpticalFlowScdHistogram = Shader.PropertyToID("rw_optical_flow_scd_histogram"); // scene change detection histogram + public static readonly int UavOpticalFlowScdPreviousHistogram = Shader.PropertyToID("rw_optical_flow_previous_histogram"); + public static readonly int UavOpticalFlowScdTemp = Shader.PropertyToID("rw_optical_flow_scd_temp"); + public static readonly int UavOpticalFlowScdOutput = Shader.PropertyToID("rw_optical_flow_scd_output"); + + // Constant buffer bindings + public static readonly int CbOpticalFlow = Shader.PropertyToID("cbOF"); + public static readonly int CbSpd = Shader.PropertyToID("cbOF_SPD"); + } +} diff --git a/Runtime/OpticalFlow/OpticalFlowShaderIDs.cs.meta b/Runtime/OpticalFlow/OpticalFlowShaderIDs.cs.meta new file mode 100644 index 0000000..3b9e163 --- /dev/null +++ b/Runtime/OpticalFlow/OpticalFlowShaderIDs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 12d00661151ef1b468cbe643922fc0cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: