forked from BilalY/Rasagar
218 lines
8.6 KiB
C#
218 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Mathematics;
|
|
using UnityEngine.Assertions;
|
|
|
|
namespace UnityEngine.Rendering.UnifiedRayTracing
|
|
{
|
|
internal class AccelStructInstances : IDisposable
|
|
{
|
|
internal AccelStructInstances(GeometryPool geometryPool)
|
|
{
|
|
m_GeometryPool = geometryPool;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach (InstanceEntry instanceEntry in m_Instances.Values)
|
|
{
|
|
GeometryPoolHandle geomHandle = instanceEntry.geometryPoolHandle;
|
|
m_GeometryPool.Unregister(geomHandle);
|
|
}
|
|
m_GeometryPool.SendGpuCommands();
|
|
|
|
m_InstanceBuffer?.Dispose();
|
|
m_GeometryPool.Dispose();
|
|
}
|
|
|
|
public PersistentGpuArray<RTInstance> instanceBuffer { get => m_InstanceBuffer; }
|
|
public IReadOnlyCollection<InstanceEntry> instances { get => m_Instances.Values; }
|
|
public GeometryPool geometryPool { get => m_GeometryPool; }
|
|
|
|
public int AddInstance(MeshInstanceDesc meshInstance, uint materialID)
|
|
{
|
|
|
|
if (meshInstance.mesh == null)
|
|
throw new System.ArgumentException("targetRenderer.mesh is null");
|
|
|
|
GeometryPoolHandle geometryHandle;
|
|
if (!m_GeometryPool.Register(meshInstance.mesh, out geometryHandle))
|
|
throw new System.InvalidOperationException("Failed to allocate geometry data for instance");
|
|
m_GeometryPool.SendGpuCommands();
|
|
|
|
var slotAllocation = m_InstanceBuffer.Add(
|
|
new RTInstance
|
|
{
|
|
localToWorld = meshInstance.localToWorldMatrix,
|
|
localToWorldNormals = NormalMatrix(meshInstance.localToWorldMatrix),
|
|
previousLocalToWorld = meshInstance.localToWorldMatrix,
|
|
userMaterialID = materialID,
|
|
instanceMask = meshInstance.mask,
|
|
userInstanceID = meshInstance.instanceID,
|
|
geometryIndex = (uint)(m_GeometryPool.GetEntryGeomAllocation(geometryHandle).meshChunkTableAlloc.block.offset + meshInstance.subMeshIndex)
|
|
});
|
|
|
|
|
|
var allocInfo = m_GeometryPool.GetEntryGeomAllocation(geometryHandle).meshChunks[meshInstance.subMeshIndex];
|
|
|
|
var instanceEntry = new InstanceEntry
|
|
{
|
|
geometryPoolHandle = geometryHandle,
|
|
indexInInstanceBuffer = slotAllocation,
|
|
instanceMask = meshInstance.mask,
|
|
vertexOffset = (uint)(allocInfo.vertexAlloc.block.offset) * ((uint)GeometryPool.GetVertexByteSize() / 4),
|
|
indexOffset = (uint)allocInfo.indexAlloc.block.offset,
|
|
};
|
|
m_Instances.Add(slotAllocation.block.offset, instanceEntry);
|
|
|
|
return slotAllocation.block.offset;
|
|
}
|
|
|
|
public GeometryPool.MeshChunk GetEntryGeomAllocation(GeometryPoolHandle handle, int submeshIndex)
|
|
{
|
|
return m_GeometryPool.GetEntryGeomAllocation(handle).meshChunks[submeshIndex];
|
|
}
|
|
|
|
public GraphicsBuffer indexBuffer { get { return m_GeometryPool.globalIndexBuffer; } }
|
|
public GraphicsBuffer vertexBuffer { get { return m_GeometryPool.globalVertexBuffer; } }
|
|
|
|
public void RemoveInstance(int instanceHandle)
|
|
{
|
|
bool success = m_Instances.TryGetValue(instanceHandle, out InstanceEntry removedEntry);
|
|
Assert.IsTrue(success);
|
|
|
|
m_Instances.Remove(instanceHandle);
|
|
m_InstanceBuffer.Remove(removedEntry.indexInInstanceBuffer);
|
|
|
|
var geomHandle = removedEntry.geometryPoolHandle;
|
|
m_GeometryPool.Unregister(geomHandle);
|
|
m_GeometryPool.SendGpuCommands();
|
|
}
|
|
|
|
public void ClearInstances()
|
|
{
|
|
foreach (InstanceEntry instanceEntry in m_Instances.Values)
|
|
{
|
|
GeometryPoolHandle geomHandle = instanceEntry.geometryPoolHandle;
|
|
m_GeometryPool.Unregister(geomHandle);
|
|
}
|
|
m_GeometryPool.SendGpuCommands();
|
|
|
|
m_Instances.Clear();
|
|
m_InstanceBuffer.Clear();
|
|
}
|
|
|
|
public void UpdateInstanceTransform(int instanceHandle, Matrix4x4 localToWorldMatrix)
|
|
{
|
|
bool success = m_Instances.TryGetValue(instanceHandle, out InstanceEntry instanceEntry);
|
|
Assert.IsTrue(success);
|
|
|
|
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
|
|
instanceInfo.localToWorld = localToWorldMatrix;
|
|
instanceInfo.localToWorldNormals = NormalMatrix(localToWorldMatrix);
|
|
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
|
|
|
|
m_TransformTouchedLastTimestamp = m_FrameTimestamp;
|
|
}
|
|
|
|
public void UpdateInstanceMaterialID(int instanceHandle, uint materialID)
|
|
{
|
|
InstanceEntry instanceEntry;
|
|
bool success = m_Instances.TryGetValue(instanceHandle, out instanceEntry);
|
|
Assert.IsTrue(success);
|
|
|
|
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
|
|
instanceInfo.userMaterialID = materialID;
|
|
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
|
|
}
|
|
|
|
public void UpdateInstanceID(int instanceHandle, uint instanceID)
|
|
{
|
|
InstanceEntry instanceEntry;
|
|
bool success = m_Instances.TryGetValue(instanceHandle, out instanceEntry);
|
|
Assert.IsTrue(success);
|
|
|
|
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
|
|
instanceInfo.userInstanceID = instanceID;
|
|
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
|
|
}
|
|
|
|
public void UpdateInstanceMask(int instanceHandle, uint mask)
|
|
{
|
|
bool success = m_Instances.TryGetValue(instanceHandle, out InstanceEntry instanceEntry);
|
|
Assert.IsTrue(success);
|
|
|
|
instanceEntry.instanceMask = mask;
|
|
|
|
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
|
|
instanceInfo.instanceMask = mask;
|
|
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
|
|
}
|
|
|
|
public void NextFrame()
|
|
{
|
|
if ((m_FrameTimestamp - m_TransformTouchedLastTimestamp) <= 1)
|
|
{
|
|
m_InstanceBuffer.ModifyForEach(
|
|
instance =>
|
|
{
|
|
instance.previousLocalToWorld = instance.localToWorld;
|
|
return instance;
|
|
});
|
|
}
|
|
|
|
m_FrameTimestamp++;
|
|
}
|
|
|
|
public bool instanceListValid => m_InstanceBuffer != null;
|
|
|
|
public void Bind(CommandBuffer cmd, IRayTracingShader shader)
|
|
{
|
|
var gpuBuffer = m_InstanceBuffer.GetGpuBuffer(cmd);
|
|
shader.SetBufferParam(cmd, Shader.PropertyToID("g_AccelStructInstanceList"), gpuBuffer);
|
|
shader.SetBufferParam(cmd, Shader.PropertyToID("g_globalIndexBuffer"), m_GeometryPool.globalIndexBuffer);
|
|
shader.SetBufferParam(cmd, Shader.PropertyToID("g_globalVertexBuffer"), m_GeometryPool.globalVertexBuffer);
|
|
shader.SetIntParam(cmd, Shader.PropertyToID("g_globalVertexBufferStride"), m_GeometryPool.globalVertexBufferStrideBytes/4);
|
|
shader.SetBufferParam(cmd, Shader.PropertyToID("g_MeshList"), m_GeometryPool.globalMeshChunkTableEntryBuffer);
|
|
}
|
|
|
|
public int GetInstanceCount()
|
|
{
|
|
return m_Instances.Count;
|
|
}
|
|
|
|
static private float4x4 NormalMatrix(float4x4 m)
|
|
{
|
|
float3x3 t = new float3x3(m);
|
|
return new float4x4(math.inverse(math.transpose(t)), new float3(0.0));
|
|
}
|
|
|
|
GeometryPool m_GeometryPool;
|
|
PersistentGpuArray<RTInstance> m_InstanceBuffer = new PersistentGpuArray<RTInstance>(100);
|
|
|
|
public struct RTInstance
|
|
{
|
|
public float4x4 localToWorld;
|
|
public float4x4 previousLocalToWorld;
|
|
public float4x4 localToWorldNormals;
|
|
public uint userInstanceID;
|
|
public uint instanceMask;
|
|
public uint userMaterialID;
|
|
public uint geometryIndex;
|
|
};
|
|
|
|
public class InstanceEntry
|
|
{
|
|
public GeometryPoolHandle geometryPoolHandle;
|
|
public BlockAllocator.Allocation indexInInstanceBuffer;
|
|
public uint instanceMask;
|
|
public uint vertexOffset;
|
|
public uint indexOffset;
|
|
}
|
|
|
|
Dictionary<int, InstanceEntry> m_Instances = new Dictionary<int, InstanceEntry>();
|
|
uint m_FrameTimestamp = 0;
|
|
uint m_TransformTouchedLastTimestamp = 0;
|
|
}
|
|
}
|