Rasagar/Library/PackageCache/com.unity.render-pipelines.high-definition/Runtime/Water/AsyncTextureSynchronizer.cs
2024-08-26 23:07:20 +03:00

192 lines
6.3 KiB
C#

using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine.Experimental.Rendering;
using static Unity.Mathematics.math;
namespace UnityEngine.Rendering.HighDefinition
{
internal enum ReadbackCode
{
UpToDate,
Enqueued,
Busy
}
internal class AsyncTextureSynchronizer<T> where T : struct
{
// The pair of buffers that allows us to keep doing the async readback "permanently"
NativeArray<T>[] m_InternalBuffers = new NativeArray<T>[2];
int2 m_CurrentResolution = new int2(0, 0);
int m_CurrentSlices = 0;
// Tracker of the current "valid" buffer
int m_CurrentBuffer = -1;
// GPU buffer that the synchronizer will read from
RenderTexture m_InternalRT;
// Tracker of hash of the last request
int m_TargetTextureHash = 0;
// Is there any job on going right now?
bool m_CurrentlyOnGoingJob = false;
// What is the format that is currently used
GraphicsFormat m_InternalGraphicsFormat;
// Callback for the end of the async readback
System.Action<AsyncGPUReadbackRequest> m_Callback;
public AsyncTextureSynchronizer(GraphicsFormat format)
{
m_InternalGraphicsFormat = format;
m_Callback = OnReceive;
}
public void OnReceive(AsyncGPUReadbackRequest request)
{
if (!request.hasError)
SwapCurrentBuffer();
m_CurrentlyOnGoingJob = false;
}
public bool TryGetBuffer(out NativeArray<T> buffer)
{
if (m_CurrentBuffer == -1)
{
buffer = default;
return false;
}
buffer = CurrentBuffer();
return true;
}
internal NativeArray<T> CurrentBuffer()
{
return m_InternalBuffers[m_CurrentBuffer];
}
public int2 CurrentResolution()
{
return m_CurrentResolution;
}
public int CurrentSlices()
{
return m_CurrentSlices;
}
void SwapCurrentBuffer()
{
m_CurrentBuffer = (m_CurrentBuffer + 1) % 2;
}
int NextBufferIndex()
{
return (m_CurrentBuffer + 1) % 2;
}
void ValidateNativeBuffer(ref NativeArray<T> buffer, int textureSize)
{
if (!buffer.IsCreated || buffer.Length != textureSize)
{
if (buffer.IsCreated)
buffer.Dispose();
buffer = new NativeArray<T>(textureSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
}
void ValidateResources(int width, int height, int slices, TextureDimension dimension)
{
int textureSize = width * height * slices;
ValidateNativeBuffer(ref m_InternalBuffers[0], textureSize);
ValidateNativeBuffer(ref m_InternalBuffers[1], textureSize);
// Make sure the GPU buffer is the right size
if (m_InternalRT == null || m_InternalRT.width != width || m_InternalRT.height != height || m_InternalRT.volumeDepth != slices)
{
if (m_InternalRT != null)
m_InternalRT.Release();
m_InternalRT = new RenderTexture(width, height, 0, m_InternalGraphicsFormat)
{
volumeDepth = slices,
dimension = dimension,
useMipMap = false,
};
}
m_CurrentResolution = int2(width, height);
m_CurrentSlices = slices;
}
public ReadbackCode EnqueueRequest(CommandBuffer cmd, Texture targetTexture, bool intermediateBlit)
{
// If the texture hash is already up to date, we have nothing to do
int currentHash = CoreUtils.GetTextureHash(targetTexture);
if (currentHash == m_TargetTextureHash)
return ReadbackCode.UpToDate;
// A job is already going on, we need to wait before we do anything
if (m_CurrentlyOnGoingJob)
return ReadbackCode.Busy;
// Ok we are now inside a read back job
m_CurrentlyOnGoingJob = true;
m_TargetTextureHash = currentHash;
int slices = 1;
if (targetTexture.dimension == TextureDimension.Tex2DArray)
slices = ((RenderTexture)targetTexture).volumeDepth;
// No job is going on, so we can free the resources if needed
ValidateResources(targetTexture.width, targetTexture.height, slices, targetTexture.dimension);
// Decompress and pick a single channel
Texture targetRT = targetTexture;
if (intermediateBlit)
{
if (targetTexture.dimension == TextureDimension.Tex2DArray)
cmd.CopyTexture(targetTexture, m_InternalRT);
else
cmd.Blit(targetTexture, m_InternalRT);
targetRT = m_InternalRT;
}
// Grab the next buffer
NativeArray<T> nextBuffer = m_InternalBuffers[NextBufferIndex()];
#if UNITY_EDITOR
// TODO: Remove this when the bug is fixed
AtomicSafetyHandle ash = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(nextBuffer);
AtomicSafetyHandle.CheckReadAndThrow(ash);
AtomicSafetyHandle.CheckDeallocateAndThrow(ash);
#endif
// Enqueue the job
cmd.RequestAsyncReadbackIntoNativeArray(ref nextBuffer, targetRT, 0, m_InternalGraphicsFormat, m_Callback);
// Notify that we enqueued
return ReadbackCode.Enqueued;
}
internal void ReleaseATSResources()
{
// If a job is still ongoing, we need to wait that it is done before we free the resources
if (m_CurrentlyOnGoingJob)
AsyncGPUReadback.WaitAllRequests();
if (m_InternalBuffers[0].IsCreated)
m_InternalBuffers[0].Dispose();
if (m_InternalBuffers[1].IsCreated)
m_InternalBuffers[1].Dispose();
if (m_InternalRT != null)
m_InternalRT.Release();
m_CurrentResolution = 0;
m_TargetTextureHash = 0;
}
}
}