Skip to content

Commit

Permalink
C# (Unity/Burst): update with C++ side optimizations (SoA, algebraic …
Browse files Browse the repository at this point in the history
…simplifications, list of emissive objects)

- PC 155 -> 165 Mray/s
- Mac 45.1 -> 47.9 Mray/s
  • Loading branch information
aras-p committed Apr 16, 2018
1 parent 5895cb3 commit 577d375
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 57 deletions.
106 changes: 82 additions & 24 deletions Unity/Assets/Maths.cs
@@ -1,3 +1,4 @@
using Unity.Collections;
using Unity.Mathematics; using Unity.Mathematics;
using static Unity.Mathematics.math; using static Unity.Mathematics.math;


Expand Down Expand Up @@ -99,39 +100,96 @@ public struct Sphere
{ {
public float3 center; public float3 center;
public float radius; public float radius;
public float invRadius;


public Sphere(float3 center_, float radius_) { center = center_; radius = radius_; invRadius = 1.0f / radius_; } public Sphere(float3 center_, float radius_) { center = center_; radius = radius_; }
public void UpdateDerivedData() { invRadius = 1.0f / radius; } }


public bool HitSphere(Ray r, float tMin, float tMax, ref Hit outHit) struct SpheresSoA
{
[ReadOnly] public NativeArray<float> centerX;
[ReadOnly] public NativeArray<float> centerY;
[ReadOnly] public NativeArray<float> centerZ;
[ReadOnly] public NativeArray<float> sqRadius;
[ReadOnly] public NativeArray<float> invRadius;
[ReadOnly] public NativeArray<int> emissives;
public int emissiveCount;

public SpheresSoA(int len)
{ {
float3 oc = r.orig - center; centerX = new NativeArray<float>(len, Allocator.Persistent);
float b = dot(oc, r.dir); centerY = new NativeArray<float>(len, Allocator.Persistent);
float c = dot(oc, oc) - radius * radius; centerZ = new NativeArray<float>(len, Allocator.Persistent);
float discr = b * b - c; sqRadius = new NativeArray<float>(len, Allocator.Persistent);
if (discr > 0) invRadius = new NativeArray<float>(len, Allocator.Persistent);
{ emissives = new NativeArray<int>(len, Allocator.Persistent);
float discrSq = sqrt(discr); emissiveCount = 0;
}


float t = (-b - discrSq); public void Dispose()
if (t < tMax && t > tMin) {
centerX.Dispose();
centerY.Dispose();
centerZ.Dispose();
sqRadius.Dispose();
invRadius.Dispose();
emissives.Dispose();
}

public void Update(Sphere[] src, Material[] mat)
{
emissiveCount = 0;
for (var i = 0; i < src.Length; ++i)
{
Sphere s = src[i];
centerX[i] = s.center.x;
centerY[i] = s.center.y;
centerZ[i] = s.center.z;
sqRadius[i] = s.radius * s.radius;
invRadius[i] = 1.0f / s.radius;
if (mat[i].HasEmission)
{ {
outHit.pos = r.PointAt(t); emissives[emissiveCount++] = i;
outHit.normal = (outHit.pos - center) * invRadius;
outHit.t = t;
return true;
} }
t = (-b + discrSq); }
if (t < tMax && t > tMin) }

public int HitSpheres(ref Ray r, float tMin, float tMax, ref Hit outHit)
{
float hitT = tMax;
int id = -1;
for (int i = 0; i < centerX.Length; ++i)
{
float coX = centerX[i] - r.orig.x;
float coY = centerY[i] - r.orig.y;
float coZ = centerZ[i] - r.orig.z;
float nb = coX * r.dir.x + coY * r.dir.y + coZ * r.dir.z;
float c = coX * coX + coY * coY + coZ * coZ - sqRadius[i];
float discr = nb * nb - c;
if (discr > 0)
{ {
outHit.pos = r.PointAt(t); float discrSq = sqrt(discr);
outHit.normal = (outHit.pos - center) * invRadius;
outHit.t = t; // Try earlier t
return true; float t = nb - discrSq;
if (t <= tMin) // before min, try later t!
t = nb + discrSq;

if (t > tMin && t < hitT)
{
id = i;
hitT = t;
}
} }
} }
return false; if (id != -1)
{
outHit.pos = r.PointAt(hitT);
outHit.normal = (outHit.pos - new float3(centerX[id], centerY[id], centerZ[id])) * invRadius[id];
outHit.t = hitT;
return id;
}
else
return -1;
} }
} }


Expand Down
65 changes: 32 additions & 33 deletions Unity/Assets/Test.cs
Expand Up @@ -52,30 +52,30 @@ class Test
new Material(Material.Type.Lambert, new float3(0.8f, 0.6f, 0.2f), new float3(30,25,15), 0, 0), new Material(Material.Type.Lambert, new float3(0.8f, 0.6f, 0.2f), new float3(30,25,15), 0, 0),
}; };


SpheresSoA s_SpheresSoA;

public Test()
{
s_SpheresSoA = new SpheresSoA(s_SpheresData.Length);
}

public void Dispose()
{
s_SpheresSoA.Dispose();
}

const float kMinT = 0.001f; const float kMinT = 0.001f;
const float kMaxT = 1.0e7f; const float kMaxT = 1.0e7f;
const int kMaxDepth = 10; const int kMaxDepth = 10;




static bool HitWorld(Ray r, float tMin, float tMax, ref Hit outHit, ref int outID, NativeArray<Sphere> spheres) static bool HitWorld(Ray r, float tMin, float tMax, ref Hit outHit, ref int outID, ref SpheresSoA spheres)
{ {
Hit tmpHit = default(Hit); outID = spheres.HitSpheres(ref r, tMin, tMax, ref outHit);
bool anything = false; return outID != -1;
float closest = tMax;
for (int i = 0; i < spheres.Length; ++i)
{
if (spheres[i].HitSphere(r, tMin, closest, ref tmpHit))
{
anything = true;
closest = tmpHit.t;
outHit = tmpHit;
outID = i;
}
}
return anything;
} }


static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount, NativeArray<Sphere> spheres, NativeArray<Material> materials, ref uint randState) static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount, ref SpheresSoA spheres, NativeArray<Material> materials, ref uint randState)
{ {
outLightE = new float3(0, 0, 0); outLightE = new float3(0, 0, 0);
if (mat.type == Material.Type.Lambert) if (mat.type == Material.Type.Lambert)
Expand All @@ -87,21 +87,22 @@ static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out


// sample lights // sample lights
#if DO_LIGHT_SAMPLING #if DO_LIGHT_SAMPLING
for (int i = 0; i < spheres.Length; ++i) for (int j = 0; j < spheres.emissiveCount; ++j)
{ {
if (!materials[i].HasEmission) int i = spheres.emissives[j];
continue; // skip non-emissive
//@TODO if (&mat == &smat) //@TODO if (&mat == &smat)
// continue; // skip self // continue; // skip self
var s = spheres[i]; //var s = spheres[i];
float3 scenter = new float3(spheres.centerX[i], spheres.centerY[i], spheres.centerZ[i]);
float sqRradius = spheres.sqRadius[i];


// create a random direction towards sphere // create a random direction towards sphere
// coord system for sampling: sw, su, sv // coord system for sampling: sw, su, sv
float3 sw = normalize(s.center - rec.pos); float3 sw = normalize(scenter - rec.pos);
float3 su = normalize(cross(abs(sw.x) > 0.01f ? new float3(0, 1, 0) : new float3(1, 0, 0), sw)); float3 su = normalize(cross(abs(sw.x) > 0.01f ? new float3(0, 1, 0) : new float3(1, 0, 0), sw));
float3 sv = cross(sw, su); float3 sv = cross(sw, su);
// sample sphere by solid angle // sample sphere by solid angle
float cosAMax = sqrt(max(0.0f, 1.0f - s.radius * s.radius / lengthSquared(rec.pos - s.center))); float cosAMax = sqrt(max(0.0f, 1.0f - sqRradius / lengthSquared(rec.pos - scenter)));
float eps1 = RandomFloat01(ref randState), eps2 = RandomFloat01(ref randState); float eps1 = RandomFloat01(ref randState), eps2 = RandomFloat01(ref randState);
float cosA = 1.0f - eps1 + eps1 * cosAMax; float cosA = 1.0f - eps1 + eps1 * cosAMax;
float sinA = sqrt(1.0f - cosA * cosA); float sinA = sqrt(1.0f - cosA * cosA);
Expand All @@ -113,7 +114,7 @@ static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out
Hit lightHit = default(Hit); Hit lightHit = default(Hit);
int hitID = 0; int hitID = 0;
++inoutRayCount; ++inoutRayCount;
if (HitWorld(new Ray(rec.pos, l), kMinT, kMaxT, ref lightHit, ref hitID, spheres) && hitID == i) if (HitWorld(new Ray(rec.pos, l), kMinT, kMaxT, ref lightHit, ref hitID, ref spheres) && hitID == i)
{ {
float omega = 2 * PI * (1 - cosAMax); float omega = 2 * PI * (1 - cosAMax);


Expand Down Expand Up @@ -177,25 +178,25 @@ static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out
return true; return true;
} }


static float3 Trace(Ray r, int depth, ref int inoutRayCount, NativeArray<Sphere> spheres, NativeArray<Material> materials, ref uint randState, bool doMaterialE = true) static float3 Trace(Ray r, int depth, ref int inoutRayCount, ref SpheresSoA spheres, NativeArray<Material> materials, ref uint randState, bool doMaterialE = true)
{ {
Hit rec = default(Hit); Hit rec = default(Hit);
int id = 0; int id = 0;
++inoutRayCount; ++inoutRayCount;
if (HitWorld(r, kMinT, kMaxT, ref rec, ref id, spheres)) if (HitWorld(r, kMinT, kMaxT, ref rec, ref id, ref spheres))
{ {
Ray scattered; Ray scattered;
float3 attenuation; float3 attenuation;
float3 lightE; float3 lightE;
var mat = materials[id]; var mat = materials[id];
var matE = mat.emissive; var matE = mat.emissive;
if (depth < kMaxDepth && Scatter(mat, r, rec, out attenuation, out scattered, out lightE, ref inoutRayCount, spheres, materials, ref randState)) if (depth < kMaxDepth && Scatter(mat, r, rec, out attenuation, out scattered, out lightE, ref inoutRayCount, ref spheres, materials, ref randState))
{ {
#if DO_LIGHT_SAMPLING #if DO_LIGHT_SAMPLING
if (!doMaterialE) matE = new float3(0, 0, 0); if (!doMaterialE) matE = new float3(0, 0, 0);
doMaterialE = (mat.type != Material.Type.Lambert); doMaterialE = (mat.type != Material.Type.Lambert);
#endif #endif
return matE + lightE + attenuation * Trace(scattered, depth + 1, ref inoutRayCount, spheres, materials, ref randState, doMaterialE); return matE + lightE + attenuation * Trace(scattered, depth + 1, ref inoutRayCount, ref spheres, materials, ref randState, doMaterialE);
} }
else else
{ {
Expand All @@ -219,7 +220,7 @@ struct TraceRowJob : IJobParallelFor
public Camera cam; public Camera cam;


[NativeDisableParallelForRestriction] public NativeArray<int> rayCounter; [NativeDisableParallelForRestriction] public NativeArray<int> rayCounter;
[NativeDisableParallelForRestriction] public NativeArray<Sphere> spheres; [NativeDisableParallelForRestriction] public SpheresSoA spheres;
[NativeDisableParallelForRestriction] public NativeArray<Material> materials; [NativeDisableParallelForRestriction] public NativeArray<Material> materials;


public void Execute(int y) public void Execute(int y)
Expand All @@ -241,7 +242,7 @@ public void Execute(int y)
float u = (x + RandomFloat01(ref state)) * invWidth; float u = (x + RandomFloat01(ref state)) * invWidth;
float v = (y + RandomFloat01(ref state)) * invHeight; float v = (y + RandomFloat01(ref state)) * invHeight;
Ray r = cam.GetRay(u, v, ref state); Ray r = cam.GetRay(u, v, ref state);
col += Trace(r, 0, ref rayCount, spheres, materials, ref state); col += Trace(r, 0, ref rayCount, ref spheres, materials, ref state);
} }
col *= 1.0f / (float)DO_SAMPLES_PER_PIXEL; col *= 1.0f / (float)DO_SAMPLES_PER_PIXEL;


Expand All @@ -267,8 +268,7 @@ public void DrawTest(float time, int frameCount, int screenWidth, int screenHeig
float distToFocus = 3; float distToFocus = 3;
float aperture = 0.1f; float aperture = 0.1f;


for (int i = 0; i < s_SpheresData.Length; ++i) s_SpheresSoA.Update(s_SpheresData, s_SphereMatsData);
s_SpheresData[i].UpdateDerivedData();


Camera cam = new Camera(lookfrom, lookat, new float3(0, 1, 0), 60, (float)screenWidth / (float)screenHeight, aperture, distToFocus); Camera cam = new Camera(lookfrom, lookat, new float3(0, 1, 0), 60, (float)screenWidth / (float)screenHeight, aperture, distToFocus);


Expand All @@ -280,13 +280,12 @@ public void DrawTest(float time, int frameCount, int screenWidth, int screenHeig
job.backbuffer = backbuffer; job.backbuffer = backbuffer;
job.cam = cam; job.cam = cam;
job.rayCounter = new NativeArray<int>(1, Allocator.Temp); job.rayCounter = new NativeArray<int>(1, Allocator.Temp);
job.spheres = new NativeArray<Sphere>(s_SpheresData, Allocator.Temp); job.spheres = s_SpheresSoA;
job.materials = new NativeArray<Material>(s_SphereMatsData, Allocator.Temp); job.materials = new NativeArray<Material>(s_SphereMatsData, Allocator.Temp);
var fence = job.Schedule(screenHeight, 4); var fence = job.Schedule(screenHeight, 4);
fence.Complete(); fence.Complete();
rayCount = job.rayCounter[0]; rayCount = job.rayCounter[0];
job.rayCounter.Dispose(); job.rayCounter.Dispose();
job.spheres.Dispose();
job.materials.Dispose(); job.materials.Dispose();
#else #else
for (int y = 0; y < screenHeight; ++y) for (int y = 0; y < screenHeight; ++y)
Expand Down
1 change: 1 addition & 0 deletions Unity/Assets/TestScript.cs
Expand Up @@ -33,6 +33,7 @@ void Start ()
private void OnDestroy() private void OnDestroy()
{ {
m_Backbuffer.Dispose(); m_Backbuffer.Dispose();
m_Test.Dispose();
} }


void UpdateLoop() void UpdateLoop()
Expand Down

0 comments on commit 577d375

Please sign in to comment.