using System; using System.Runtime.InteropServices; using Unity.Mathematics; namespace UnityEngine.Rendering.RadeonRays { internal struct TopLevelAccelStruct : IDisposable { public const GraphicsBuffer.Target topLevelBvhTarget = GraphicsBuffer.Target.Structured; public const GraphicsBuffer.Target instanceInfoTarget = GraphicsBuffer.Target.Structured; public GraphicsBuffer topLevelBvh; public GraphicsBuffer bottomLevelBvhs; public GraphicsBuffer instanceInfos; public uint instanceCount; public void Dispose() { topLevelBvh?.Dispose(); instanceInfos?.Dispose(); } } internal class HlbvhTopLevelBuilder { readonly ComputeShader shaderBuildHlbvh; readonly int kernelInit; readonly int kernelCalculateAabb; readonly int kernelCalculateMortonCodes; readonly int kernelBuildTreeBottomUp; readonly RadixSort radixSort; const uint kTrianglesPerThread = 8u; const uint kGroupSize = 256u; const uint kTrianglesPerGroup = kTrianglesPerThread * kGroupSize; public HlbvhTopLevelBuilder(RadeonRaysShaders shaders) { shaderBuildHlbvh = shaders.buildHlbvh; kernelInit = shaderBuildHlbvh.FindKernel("Init"); kernelCalculateAabb = shaderBuildHlbvh.FindKernel("CalculateAabb"); kernelCalculateMortonCodes = shaderBuildHlbvh.FindKernel("CalculateMortonCodes"); kernelBuildTreeBottomUp = shaderBuildHlbvh.FindKernel("BuildTreeBottomUp"); radixSort = new RadixSort(shaders); } public ulong GetScratchDataSizeInDwords(uint instanceCount) { var scratchLayout = ScratchBufferLayout.Create(instanceCount); return scratchLayout.TotalSize; } public static uint GetBvhNodeCount(uint leafCount) { return leafCount - 1; } public void AllocateResultBuffers(uint instanceCount, ref TopLevelAccelStruct accelStruct) { var bvhNodeCount = GetBvhNodeCount(instanceCount); accelStruct.Dispose(); accelStruct.instanceInfos = new GraphicsBuffer(TopLevelAccelStruct.instanceInfoTarget, (int)instanceCount, Marshal.SizeOf()); accelStruct.topLevelBvh = new GraphicsBuffer(TopLevelAccelStruct.topLevelBvhTarget, (int)bvhNodeCount + 1, Marshal.SizeOf()); // plus one for header } public void CreateEmpty(ref TopLevelAccelStruct accelStruct) { accelStruct.Dispose(); accelStruct.topLevelBvh = new GraphicsBuffer(TopLevelAccelStruct.topLevelBvhTarget, 2, Marshal.SizeOf()); accelStruct.instanceInfos = accelStruct.topLevelBvh; accelStruct.bottomLevelBvhs = accelStruct.topLevelBvh; accelStruct.instanceCount = 0; var top = new BvhNode[2]; top[0].child0 = 0; top[0].child1 = 0; top[0].parent = 0; top[1].child0 = 0; top[1].child1 = 0; top[1].parent = 0xFFFFFFFF; top[1].update = 0; top[1].aabb0_min = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); top[1].aabb0_max = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); top[1].aabb1_min = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); top[1].aabb1_max = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity); accelStruct.topLevelBvh.SetData(top); } public void Execute(CommandBuffer cmd, GraphicsBuffer scratch, ref TopLevelAccelStruct accelStruct) { Common.EnableKeyword(cmd, shaderBuildHlbvh, "TOP_LEVEL", true); Common.EnableKeyword(cmd, shaderBuildHlbvh, "UINT16_INDICES", false); uint instanceCount = accelStruct.instanceCount; var scratchLayout = ScratchBufferLayout.Create(instanceCount); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_constants_vertex_stride, 0); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_constants_triangle_count, (int)instanceCount); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_bvh_offset, 0); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_internal_node_range_offset, (int)scratchLayout.InternalNodeRange); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_aabb_offset, (int)scratchLayout.Aabb); BindKernelArguments(cmd, kernelInit, scratch, scratchLayout, accelStruct, false); cmd.DispatchCompute(shaderBuildHlbvh, kernelInit, 1, 1, 1); BindKernelArguments(cmd, kernelCalculateAabb, scratch, scratchLayout, accelStruct, false); cmd.DispatchCompute(shaderBuildHlbvh, kernelCalculateAabb, (int)Common.CeilDivide(instanceCount, kTrianglesPerGroup), 1, 1); BindKernelArguments(cmd, kernelCalculateMortonCodes, scratch, scratchLayout, accelStruct, false); cmd.DispatchCompute(shaderBuildHlbvh, kernelCalculateMortonCodes, (int)Common.CeilDivide(instanceCount, kTrianglesPerGroup), 1, 1); radixSort.Execute(cmd, scratch, scratchLayout.MortonCodes, scratchLayout.SortedMortonCodes, scratchLayout.PrimitiveRefs, scratchLayout.SortedPrimitiveRefs, scratchLayout.SortMemory, instanceCount); BindKernelArguments(cmd, kernelBuildTreeBottomUp, scratch, scratchLayout, accelStruct, true); cmd.DispatchCompute(shaderBuildHlbvh, kernelBuildTreeBottomUp, (int)Common.CeilDivide(instanceCount, kTrianglesPerGroup), 1, 1); } struct ScratchBufferLayout { public uint Aabb; public uint MortonCodes; public uint PrimitiveRefs; public uint SortedMortonCodes; public uint SortedPrimitiveRefs; public uint SortMemory; public uint InternalNodeRange; public uint TotalSize; public static ScratchBufferLayout Create(uint instanceCount) { var result = new ScratchBufferLayout(); result.Aabb = result.Reserve(6); result.MortonCodes = result.Reserve(instanceCount); result.PrimitiveRefs = result.Reserve(instanceCount); result.SortedMortonCodes = result.Reserve(instanceCount); result.SortedPrimitiveRefs = result.Reserve(instanceCount); result.SortMemory = result.Reserve((uint)RadixSort.GetScratchDataSizeInDwords(instanceCount)); // overlaps with MortonCodes and PrimitiveRefs result.InternalNodeRange = result.MortonCodes; return result; } uint Reserve(uint size) { var temp = TotalSize; TotalSize += size; return temp; } } private void BindKernelArguments( CommandBuffer cmd, int kernel, GraphicsBuffer scratch, ScratchBufferLayout scratchLayout, TopLevelAccelStruct accelStruct, bool setSortedCodes) { cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_scratch_buffer, scratch); cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_bvh, accelStruct.topLevelBvh); cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_bottom_bvhs, accelStruct.bottomLevelBvhs); cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_instance_infos, accelStruct.instanceInfos); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_aabb_offset, (int)scratchLayout.Aabb); if (setSortedCodes) { cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_morton_codes_offset, (int)scratchLayout.SortedMortonCodes); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_primitive_refs_offset, (int)scratchLayout.SortedPrimitiveRefs); } else { cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_morton_codes_offset, (int)scratchLayout.MortonCodes); cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_primitive_refs_offset, (int)scratchLayout.PrimitiveRefs); } } } }