206 lines
6.8 KiB
C#
206 lines
6.8 KiB
C#
using System.Collections.Generic;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using UnityEngine;
|
|
using UnityEngine.LightTransport;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace UnityEngine.PathTracing.Core
|
|
{
|
|
internal class UnityComputeDeviceContext : IDeviceContext
|
|
{
|
|
private readonly Dictionary<BufferID, GraphicsBuffer> _buffers = new();
|
|
private readonly HashSet<EventID> _inProgressRequests = new();
|
|
private readonly HashSet<EventID> _failedRequests = new();
|
|
private readonly HashSet<EventID> _successfulRequests = new();
|
|
private uint _nextFreeBufferId;
|
|
private uint _nextFreeEventId;
|
|
private CommandBuffer _cmdBuffer;
|
|
|
|
private List<BufferID> _temporaryBuffers = new();
|
|
|
|
private void CreateCommandBuffer()
|
|
{
|
|
_cmdBuffer?.Dispose();
|
|
_cmdBuffer = new CommandBuffer();
|
|
_cmdBuffer.name = "UnityComputeDeviceContextCommandBuffer";
|
|
}
|
|
|
|
public BufferID CreateBuffer(ulong count, ulong stride)
|
|
{
|
|
Debug.Assert(count != 0, "Buffer element count cannot be zero.");
|
|
Debug.Assert(stride != 0, "Stride cannot be zero.");
|
|
Debug.Assert(stride % 4 == 0, "Stride must be a multiple of 4.");
|
|
Debug.Assert(stride <= 2048, "Stride must be 2048 or less.");
|
|
GraphicsBuffer buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)count, (int)stride);
|
|
Debug.Assert(buffer.IsValid(), "Buffer was not successfully created.");
|
|
var zeros = new NativeArray<byte>((int)(count * stride), Allocator.Temp, NativeArrayOptions.ClearMemory);
|
|
buffer.SetData(zeros);
|
|
zeros.Dispose();
|
|
|
|
var idInteger = _nextFreeBufferId++;
|
|
var id = new BufferID(idInteger);
|
|
_buffers[id] = buffer;
|
|
return id;
|
|
}
|
|
|
|
public void DestroyBuffer(BufferID id)
|
|
{
|
|
Debug.Assert(_buffers.ContainsKey(id), "Invalid buffer ID given.");
|
|
|
|
_buffers[id].Release();
|
|
_buffers.Remove(id);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ReleaseTemporaryBuffers();
|
|
_cmdBuffer?.Dispose();
|
|
}
|
|
|
|
public bool Flush()
|
|
{
|
|
Debug.Assert(_cmdBuffer != null);
|
|
Graphics.ExecuteCommandBuffer(_cmdBuffer);
|
|
|
|
// TODO(pema.malling): Don't block here https://jira.unity3d.com/browse/LIGHT-1699
|
|
// Ideally we shouldn't need this, but if we don't do it, read-backs will never finish unless explicitly waited on.
|
|
AsyncGPUReadback.WaitAllRequests();
|
|
|
|
ReleaseTemporaryBuffers();
|
|
|
|
CreateCommandBuffer();
|
|
return true;
|
|
}
|
|
|
|
public bool Initialize()
|
|
{
|
|
CreateCommandBuffer();
|
|
return true;
|
|
}
|
|
|
|
public bool IsCompleted(EventID id)
|
|
{
|
|
return _successfulRequests.Contains(id) || _failedRequests.Contains(id);
|
|
}
|
|
|
|
public bool Wait(EventID id)
|
|
{
|
|
AsyncGPUReadback.WaitAllRequests();
|
|
|
|
if (_failedRequests.Contains(id))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void ReadBuffer<T>(BufferSlice<T> src, NativeArray<T> result) where T : struct
|
|
{
|
|
Debug.Assert(_buffers.ContainsKey(src.Id), "Invalid buffer ID given.");
|
|
|
|
int stride = UnsafeUtility.SizeOf<T>();
|
|
int offset = (int)src.Offset * stride;
|
|
int size = result.Length * stride;
|
|
_cmdBuffer.RequestAsyncReadbackIntoNativeArray(ref result, _buffers[src.Id], size, offset, delegate { });
|
|
}
|
|
|
|
public void ReadBuffer<T>(BufferSlice<T> src, NativeArray<T> result, EventID id) where T : struct
|
|
{
|
|
Debug.Assert(_buffers.ContainsKey(src.Id), "Invalid buffer ID given.");
|
|
|
|
int stride = UnsafeUtility.SizeOf<T>();
|
|
int offset = (int)src.Offset * stride;
|
|
int size = result.Length * stride;
|
|
_cmdBuffer.RequestAsyncReadbackIntoNativeArray(ref result, _buffers[src.Id], size, offset, request =>
|
|
{
|
|
Debug.Assert(request.done);
|
|
// The user may have destroyed the event before the readback was completed, so we check if its still there.
|
|
if (_inProgressRequests.Remove(id))
|
|
{
|
|
if (request.hasError)
|
|
{
|
|
_failedRequests.Add(id);
|
|
}
|
|
else
|
|
{
|
|
_successfulRequests.Add(id);
|
|
}
|
|
}
|
|
});
|
|
_inProgressRequests.Add(id);
|
|
}
|
|
|
|
public void WriteBuffer<T>(BufferSlice<T> dst, NativeArray<T> src)
|
|
where T : struct
|
|
{
|
|
Debug.Assert(_buffers.ContainsKey(dst.Id), "Invalid buffer ID given.");
|
|
|
|
_cmdBuffer.SetBufferData(_buffers[dst.Id], src, 0, (int)dst.Offset, src.Length);
|
|
}
|
|
|
|
public void WriteBuffer<T>(BufferSlice<T> dst, NativeArray<T> src, EventID id)
|
|
where T : struct
|
|
{
|
|
Debug.Assert(_buffers.ContainsKey(dst.Id), "Invalid buffer ID given.");
|
|
|
|
_cmdBuffer.SetBufferData(_buffers[dst.Id], src, 0, (int)dst.Offset, src.Length);
|
|
|
|
_successfulRequests.Add(id);
|
|
}
|
|
|
|
public EventID CreateEvent()
|
|
{
|
|
var eventIdInteger = _nextFreeEventId++;
|
|
var eventId = new EventID(eventIdInteger);
|
|
return eventId;
|
|
}
|
|
|
|
public void DestroyEvent(EventID id)
|
|
{
|
|
if (_inProgressRequests.Contains(id))
|
|
{
|
|
_inProgressRequests.Remove(id);
|
|
}
|
|
if (_failedRequests.Contains(id))
|
|
{
|
|
_failedRequests.Remove(id);
|
|
}
|
|
if (_successfulRequests.Contains(id))
|
|
{
|
|
_successfulRequests.Remove(id);
|
|
}
|
|
}
|
|
|
|
public GraphicsBuffer GetComputeBuffer(BufferID id)
|
|
{
|
|
Debug.Assert(_buffers.ContainsKey(id), "Invalid buffer ID given.");
|
|
return _buffers[id];
|
|
}
|
|
|
|
public CommandBuffer GetCommandBuffer()
|
|
{
|
|
return _cmdBuffer;
|
|
}
|
|
|
|
// Temporary buffers are valid until the next call to Flush().
|
|
public BufferID GetTemporaryBuffer(ulong count, ulong stride)
|
|
{
|
|
BufferID bufferID = CreateBuffer(count, stride);
|
|
_temporaryBuffers.Add(bufferID);
|
|
return bufferID;
|
|
}
|
|
|
|
private void ReleaseTemporaryBuffers()
|
|
{
|
|
foreach (var bufferId in _temporaryBuffers)
|
|
{
|
|
if (_buffers.ContainsKey(bufferId))
|
|
{
|
|
DestroyBuffer(bufferId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|