UnityGame/Library/PackageCache/com.unity.rendering.light-transport/Tests/Editor/UnifiedRayTracing/AccelStructAdapterTests.cs

264 lines
10 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
using NUnit.Framework;
using System;
using UnityEditor;
using System.Runtime.InteropServices;
using Unity.Mathematics;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.Rendering.UnifiedRayTracing.Tests
{
[TestFixture("Compute")]
[TestFixture("Hardware")]
internal class AccelStructAdapterTests
{
readonly RayTracingBackend m_Backend;
RayTracingContext m_Context;
AccelStructAdapter m_AccelStruct;
IRayTracingShader m_Shader;
public AccelStructAdapterTests(string backendAsString)
{
m_Backend = Enum.Parse<RayTracingBackend>(backendAsString);
}
[SetUp]
public void SetUp()
{
if (!SystemInfo.supportsRayTracing && m_Backend == RayTracingBackend.Hardware)
{
Assert.Ignore("Cannot run test on this Graphics API. Hardware RayTracing is not supported");
}
if (!SystemInfo.supportsComputeShaders && m_Backend == RayTracingBackend.Compute)
{
Assert.Ignore("Cannot run test on this Graphics API. Compute shaders are not supported");
}
if (SystemInfo.graphicsDeviceName.Contains("llvmpipe"))
{
Assert.Ignore("Cannot run test on this device (Renderer: llvmpipe (LLVM 10.0.0, 128 bits)). Tests are disabled because they fail on some platforms (that do not support 11 SSBOs). Once we do not run Ubuntu 18.04 try removing this");
}
CreateRayTracingResources();
}
[TearDown]
public void TearDown()
{
DisposeRayTracingResources();
}
void RayTraceAndCheckUVs(Mesh mesh, float2 expected, int uvChannel, float tolerance = 0.01f)
{
const int instanceCount = 4;
CreateMatchingRaysAndInstanceDescs(instanceCount, mesh, out RayWithFlags[] rays, out MeshInstanceDesc[] instanceDescs);
for (int i = 0; i < instanceCount; ++i)
{
m_AccelStruct.AddInstance(i, instanceDescs[i].mesh, instanceDescs[i].localToWorldMatrix, new uint[]{ 0xFFFFFFFF }, new uint[]{ 0xFFFFFFFF }, 1);
}
HitGeomAttributes[] hitAttributes = null;
var hits = TraceRays(rays, out hitAttributes);
for (int i = 0; i < rays.Length; ++i)
{
Assert.IsTrue(hits[i].Valid(), "Expected ray to hit the mesh.");
float2 uv = uvChannel == 1 ? hitAttributes[i].uv1 : hitAttributes[i].uv0;
Assert.AreEqual(expected.x, uv.x, tolerance, $"Expected x (from uv{uvChannel}) to be fetched correctly in the ray tracing shader.");
Assert.AreEqual(expected.y, uv.y, tolerance, $"Expected y (from uv{uvChannel}) to be fetched correctly in the ray tracing shader.");
}
}
[Test]
[TestCase(0)]
[TestCase(1)]
public void GeometryPool_MeshWithTwoWideUVs_UVsAreFetchedCorrectly(int uvChannel)
{
Mesh mesh = MeshUtil.CreateSingleTriangleMesh(new float2(1.5f, 1.5f), new float3(-0.5f, -0.5f, 0.0f));
float x = 100.2f;
float y = -9.3f;
var uvs = new Vector2[] { new Vector2(x, y), new Vector2(x, y), new Vector2(x, y) };
// Here we use the Vector2 version for setting the UVs on the mesh
mesh.SetUVs(uvChannel, uvs);
RayTraceAndCheckUVs(mesh, new float2(x, y), uvChannel);
}
[Test]
[TestCase(0)]
[TestCase(1)]
public void GeometryPool_MeshWithThreeWideUVs_UVsAreFetchedCorrectly(int uvChannel)
{
Mesh mesh = MeshUtil.CreateSingleTriangleMesh(new float2(1.5f, 1.5f), new float3(-0.5f, -0.5f, 0.0f));
float x = 100.2f;
float y = -9.3f;
float z = 32.4f;
var uvs = new Vector3[] { new Vector3(x, y, z), new Vector3(x, y, z), new Vector3(x, y, z) };
// Here we use the Vector3 version for setting the UVs on the mesh
mesh.SetUVs(uvChannel, uvs);
RayTraceAndCheckUVs(mesh, new float2(x, y), uvChannel);
}
[Test]
[TestCase(0)]
[TestCase(1)]
public void GeometryPool_MeshWithFourWideUVs_UVsAreFetchedCorrectly(int uvChannel)
{
Mesh mesh = MeshUtil.CreateSingleTriangleMesh(new float2(1.5f, 1.5f), new float3(-0.5f, -0.5f, 0.0f));
float x = 100.2f;
float y = -9.3f;
float z = 32.4f;
float w = -12.5f;
var uvs = new Vector4[] { new Vector4(x, y, z, w), new Vector4(x, y, z, w), new Vector4(x, y, z, w) };
// Here we use the Vector4 version for setting the UVs on the mesh
mesh.SetUVs(uvChannel, uvs);
RayTraceAndCheckUVs(mesh, new float2(x, y), uvChannel);
}
[Test]
[TestCase(0)]
[TestCase(1)]
public void GeometryPool_MeshWithLargeUVValues_UVsAreFetchedCorrectly(int uvChannel)
{
Mesh mesh = MeshUtil.CreateSingleTriangleMesh(new float2(1.5f, 1.5f), new float3(-0.5f, -0.5f, 0.0f));
float x = 100000.2f;
float y = -900000.3f;
float z = 32000.4f;
float w = -1200000.5f;
var uvs = new Vector4[] { new Vector4(x, y, z, w), new Vector4(x, y, z, w), new Vector4(x, y, z, w) };
// Here we use the Vector4 version for setting the UVs on the mesh
mesh.SetUVs(uvChannel, uvs);
RayTraceAndCheckUVs(mesh, new float2(x, y), uvChannel, 0.2f);
}
[Test]
[TestCase(0)]
[TestCase(1)]
public void GeometryPool_MeshWithDifferentVertexUVs_UVsAreInterpolatedCorrectly(int uvChannel)
{
Mesh mesh = MeshUtil.CreateSingleTriangleMesh(new float2(1.5f, 1.5f), new float3(-0.5f, -0.5f, 0.0f));
float x = 1.0f;
float y = 5.0f;
float z = 7.0f;
var uvs = new Vector4[] { new Vector4(x, x, x, x), new Vector4(y, y, y, y), new Vector4(z, z, z, z) };
// Here we use the Vector4 version for setting the UVs on the mesh
mesh.SetUVs(uvChannel, uvs);
RayTraceAndCheckUVs(mesh, new float2(4.333f, 4.333f), uvChannel, 0.001f);
}
void CreateMatchingRaysAndInstanceDescs(uint instanceCount, Mesh mesh, out RayWithFlags[] rays, out MeshInstanceDesc[] instanceDescs)
{
instanceDescs = new MeshInstanceDesc[instanceCount];
rays = new RayWithFlags[instanceCount];
var ray = new RayWithFlags(new float3(0.0f, 0.0f, 1.0f), new float3(0.0f, 0.0f, -1.0f));
float3 step = new float3(2.0f, 0.0f, 0.0f);
for (int i = 0; i < instanceCount; ++i)
{
instanceDescs[i] = new MeshInstanceDesc(mesh);
instanceDescs[i].localToWorldMatrix = float4x4.Translate(step * i);
rays[i] = ray;
rays[i].origin += step * i;
}
}
Hit[] TraceRays(RayWithFlags[] rays, out HitGeomAttributes[] hitAttributes)
{
var bufferTarget = GraphicsBuffer.Target.Structured;
var rayCount = rays.Length;
using var raysBuffer = new GraphicsBuffer(bufferTarget, rayCount, Marshal.SizeOf<RayWithFlags>());
raysBuffer.SetData(rays);
using var hitsBuffer = new GraphicsBuffer(bufferTarget, rayCount, Marshal.SizeOf<Hit>());
using var attributesBuffer = new GraphicsBuffer(bufferTarget, rayCount, Marshal.SizeOf<HitGeomAttributes>());
var scratchBuffer = RayTracingHelper.CreateScratchBufferForBuildAndDispatch(m_AccelStruct.GetAccelerationStructure(), m_Shader, (uint)rayCount, 1, 1);
var cmd = new CommandBuffer();
m_AccelStruct.Build(cmd, ref scratchBuffer);
m_AccelStruct.Bind(cmd, "_AccelStruct", m_Shader);
m_Shader.SetBufferParam(cmd, Shader.PropertyToID("_Rays"), raysBuffer);
m_Shader.SetBufferParam(cmd, Shader.PropertyToID("_Hits"), hitsBuffer);
m_Shader.SetBufferParam(cmd, Shader.PropertyToID("_HitAttributes"), attributesBuffer);
m_Shader.Dispatch(cmd, scratchBuffer, (uint)rayCount, 1, 1);
Graphics.ExecuteCommandBuffer(cmd);
var hits = new Hit[rayCount];
hitsBuffer.GetData(hits);
hitAttributes = new HitGeomAttributes[rayCount];
attributesBuffer.GetData(hitAttributes);
scratchBuffer?.Dispose();
return hits;
}
void CreateRayTracingResources()
{
var resources = new RayTracingResources();
resources.Load();
m_Context = new RayTracingContext(m_Backend, resources);
m_AccelStruct = new AccelStructAdapter(m_Context.CreateAccelerationStructure(new AccelerationStructureOptions()), resources);
m_Shader = m_Context.LoadRayTracingShader("Packages/com.unity.rendering.light-transport/Tests/Editor/UnifiedRayTracing/TraceRaysAndFetchAttributes.urtshader");
}
void DisposeRayTracingResources()
{
m_AccelStruct?.Dispose();
m_Context?.Dispose();
}
[StructLayout(LayoutKind.Sequential)]
public struct RayWithFlags
{
public float3 origin;
public float minT;
public float3 direction;
public float maxT;
public uint culling;
public uint instanceMask;
uint padding;
uint padding2;
public RayWithFlags(float3 origin, float3 direction)
{
this.origin = origin;
this.direction = direction;
minT = 0.0f;
maxT = float.MaxValue;
instanceMask = 0xFFFFFFFF;
culling = 0;
padding = 0;
padding2 = 0;
}
}
[System.Flags]
enum RayCulling { None = 0, CullFrontFace = 0x10, CullBackFace = 0x20 }
[StructLayout(LayoutKind.Sequential)]
public struct Hit
{
public uint instanceID;
public uint primitiveIndex;
public float2 uvBarycentrics;
public float hitDistance;
public uint isFrontFace;
public bool Valid() { return instanceID != 0xFFFFFFFF; }
}
[StructLayout(LayoutKind.Sequential)]
public struct HitGeomAttributes
{
public float3 position;
public float3 normal;
public float3 faceNormal;
public float2 uv0;
public float2 uv1;
}
}
}