Permalink
Browse files

Use explicit random state instead of thread-local storage:

- C++ PC 130 -> 136, Mac 34.7 -> 37.8 Mray/s
- C# netcore PC 55 -> 67, Mac 15.2 -> 17.5 Mray/s
- C# Unity PC 10.2 -> 11.3, Mac 4.6 Mray/s
- C# Unity+Burst PC 172, Mac 48.1 Mray/s
  • Loading branch information...
aras-p committed Mar 30, 2018
1 parent 9a8dc16 commit e2aee497f3a8c309fb5735336fe451268903717f
Showing with 60 additions and 60 deletions.
  1. +9 −11 Cpp/Source/Maths.cpp
  2. +6 −5 Cpp/Source/Maths.h
  3. +13 −12 Cpp/Source/Test.cpp
  4. +11 −12 Cs/Maths.cs
  5. +14 −13 Cs/Test.cs
  6. +7 −7 Unity/Assets/Test.cs
@@ -2,38 +2,36 @@
#include <stdlib.h>
#include <stdint.h>
static thread_local uint32_t s_RndState = 1;
static uint32_t XorShift32()
static uint32_t XorShift32(uint32_t& state)
{
uint32_t x = s_RndState + 1; // avoid zero seed
uint32_t x = state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 15;
s_RndState = x;
state = x;
return x;
}
float RandomFloat01()
float RandomFloat01(uint32_t& state)
{
return (XorShift32() & 0xFFFFFF) / 16777216.0f;
return (XorShift32(state) & 0xFFFFFF) / 16777216.0f;
}
float3 RandomInUnitDisk()
float3 RandomInUnitDisk(uint32_t& state)
{
float3 p;
do
{
p = 2.0 * float3(RandomFloat01(),RandomFloat01(),0) - float3(1,1,0);
p = 2.0 * float3(RandomFloat01(state),RandomFloat01(state),0) - float3(1,1,0);
} while (dot(p,p) >= 1.0);
return p;
}
float3 RandomInUnitSphere()
float3 RandomInUnitSphere(uint32_t& state)
{
float3 p;
do {
p = 2.0*float3(RandomFloat01(),RandomFloat01(),RandomFloat01()) - float3(1,1,1);
p = 2.0*float3(RandomFloat01(state),RandomFloat01(state),RandomFloat01(state)) - float3(1,1,1);
} while (p.sqLength() >= 1.0);
return p;
}
@@ -2,6 +2,7 @@
#include <math.h>
#include <assert.h>
#include <stdint.h>
#define kPI 3.1415926f
@@ -101,9 +102,9 @@ struct Sphere
bool HitSphere(const Ray& r, const Sphere& s, float tMin, float tMax, Hit& outHit);
float RandomFloat01();
float3 RandomInUnitDisk();
float3 RandomInUnitSphere();
float RandomFloat01(uint32_t& state);
float3 RandomInUnitDisk(uint32_t& state);
float3 RandomInUnitSphere(uint32_t& state);
struct Camera
@@ -124,9 +125,9 @@ struct Camera
vertical = 2*halfHeight*focusDist*v;
}
Ray GetRay(float s, float t) const
Ray GetRay(float s, float t, uint32_t& state) const
{
float3 rd = lensRadius * RandomInUnitDisk();
float3 rd = lensRadius * RandomInUnitDisk(state);
float3 offset = u * rd.x + v * rd.y;
return Ray(origin + offset, normalize(lowerLeftCorner + s*horizontal + t*vertical - origin - offset));
}
@@ -70,13 +70,13 @@ bool HitWorld(const Ray& r, float tMin, float tMax, Hit& outHit, int& outID)
}
static bool Scatter(const Material& mat, const Ray& r_in, const Hit& rec, float3& attenuation, Ray& scattered, float3& outLightE, int& inoutRayCount)
static bool Scatter(const Material& mat, const Ray& r_in, const Hit& rec, float3& attenuation, Ray& scattered, float3& outLightE, int& inoutRayCount, uint32_t& state)
{
outLightE = float3(0,0,0);
if (mat.type == Material::Lambert)
{
// random point inside unit sphere that is tangent to the hit point
float3 target = rec.pos + rec.normal + RandomInUnitSphere();
float3 target = rec.pos + rec.normal + RandomInUnitSphere(state);
scattered = Ray(rec.pos, normalize(target - rec.pos));
attenuation = mat.albedo;
@@ -98,7 +98,7 @@ static bool Scatter(const Material& mat, const Ray& r_in, const Hit& rec, float3
float3 sv = cross(sw, su);
// sample sphere by solid angle
float cosAMax = sqrtf(1.0f - s.radius*s.radius / (rec.pos-s.center).sqLength());
float eps1 = RandomFloat01(), eps2 = RandomFloat01();
float eps1 = RandomFloat01(state), eps2 = RandomFloat01(state);
float cosA = 1.0f - eps1 + eps1 * cosAMax;
float sinA = sqrtf(1.0f - cosA*cosA);
float phi = 2 * kPI * eps2;
@@ -127,7 +127,7 @@ static bool Scatter(const Material& mat, const Ray& r_in, const Hit& rec, float3
AssertUnit(r_in.dir); AssertUnit(rec.normal);
float3 refl = reflect(r_in.dir, rec.normal);
// reflected ray, and random inside of sphere based on roughness
scattered = Ray(rec.pos, normalize(refl + mat.roughness*RandomInUnitSphere()));
scattered = Ray(rec.pos, normalize(refl + mat.roughness*RandomInUnitSphere(state)));
attenuation = mat.albedo;
return dot(scattered.dir, rec.normal) > 0;
}
@@ -162,7 +162,7 @@ static bool Scatter(const Material& mat, const Ray& r_in, const Hit& rec, float3
{
reflProb = 1;
}
if (RandomFloat01() < reflProb)
if (RandomFloat01(state) < reflProb)
scattered = Ray(rec.pos, normalize(refl));
else
scattered = Ray(rec.pos, normalize(refr));
@@ -175,7 +175,7 @@ static bool Scatter(const Material& mat, const Ray& r_in, const Hit& rec, float3
return true;
}
static float3 Trace(const Ray& r, int depth, int& inoutRayCount)
static float3 Trace(const Ray& r, int depth, int& inoutRayCount, uint32_t& state)
{
Hit rec;
int id = 0;
@@ -186,9 +186,9 @@ static float3 Trace(const Ray& r, int depth, int& inoutRayCount)
float3 attenuation;
float3 lightE;
const Material& mat = s_SphereMats[id];
if (depth < kMaxDepth && Scatter(mat, r, rec, attenuation, scattered, lightE, inoutRayCount))
if (depth < kMaxDepth && Scatter(mat, r, rec, attenuation, scattered, lightE, inoutRayCount, state))
{
return mat.emissive + lightE + attenuation * Trace(scattered, depth+1, inoutRayCount);
return mat.emissive + lightE + attenuation * Trace(scattered, depth+1, inoutRayCount, state);
}
else
{
@@ -240,15 +240,16 @@ static void TraceRowJob(uint32_t start, uint32_t end, uint32_t threadnum, void*
int rayCount = 0;
for (uint32_t y = start; y < end; ++y)
{
uint32_t state = (y * 9781 + data.frameCount * 6271) | 1;
for (int x = 0; x < data.screenWidth; ++x)
{
float3 col(0, 0, 0);
for (int s = 0; s < DO_SAMPLES_PER_PIXEL; s++)
{
float u = float(x + RandomFloat01()) * invWidth;
float v = float(y + RandomFloat01()) * invHeight;
Ray r = data.cam->GetRay(u, v);
col += Trace(r, 0, rayCount);
float u = float(x + RandomFloat01(state)) * invWidth;
float v = float(y + RandomFloat01(state)) * invHeight;
Ray r = data.cam->GetRay(u, v, state);
col += Trace(r, 0, rayCount, state);
}
col *= 1.0f / float(DO_SAMPLES_PER_PIXEL);
col = float3(sqrtf(col.x), sqrtf(col.y), sqrtf(col.z));
@@ -57,38 +57,37 @@ public static float Schlick(float cosine, float ri)
return r0 + (1 - r0) * MathF.Pow(1 - cosine, 5);
}
[ThreadStatic] static uint s_RndState;
static uint XorShift32()
static uint XorShift32(ref uint state)
{
uint x = s_RndState+1; // avoid zero seed
uint x = state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 15;
s_RndState = x;
state = x;
return x;
}
public static float RandomFloat01()
public static float RandomFloat01(ref uint state)
{
return (XorShift32() & 0xFFFFFF) / 16777216.0f;
return (XorShift32(ref state) & 0xFFFFFF) / 16777216.0f;
}
public static float3 RandomInUnitDisk()
public static float3 RandomInUnitDisk(ref uint state)
{
float3 p;
do
{
p = 2.0f * new float3(RandomFloat01(), RandomFloat01(), 0) - new float3(1, 1, 0);
p = 2.0f * new float3(RandomFloat01(ref state), RandomFloat01(ref state), 0) - new float3(1, 1, 0);
} while (p.SqLength >= 1.0);
return p;
}
public static float3 RandomInUnitSphere()
public static float3 RandomInUnitSphere(ref uint state)
{
float3 p;
do
{
p = 2.0f * new float3(RandomFloat01(), RandomFloat01(), RandomFloat01()) - new float3(1, 1, 1);
p = 2.0f * new float3(RandomFloat01(ref state), RandomFloat01(ref state), RandomFloat01(ref state)) - new float3(1, 1, 1);
} while (p.SqLength >= 1.0);
return p;
}
@@ -178,9 +177,9 @@ public Camera(float3 lookFrom, float3 lookAt, float3 vup, float vfov, float aspe
vertical = 2*halfHeight * focusDist*v;
}
public Ray GetRay(float s, float t)
public Ray GetRay(float s, float t, ref uint state)
{
float3 rd = lensRadius * MathUtil.RandomInUnitDisk();
float3 rd = lensRadius * MathUtil.RandomInUnitDisk(ref state);
float3 offset = u * rd.x + v * rd.y;
return new Ray(origin + offset, float3.Normalize(lowerLeftCorner + s*horizontal + t*vertical - origin - offset));
}
@@ -1,5 +1,5 @@
#define DO_ANIMATE
//#define DO_ANIMATE
#define DO_LIGHT_SAMPLING
#define DO_THREADED
@@ -78,13 +78,13 @@ bool HitWorld(Ray r, float tMin, float tMax, ref Hit outHit, ref int outID)
return anything;
}
bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount)
bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount, ref uint state)
{
outLightE = new float3(0, 0, 0);
if (mat.type == Material.Type.Lambert)
{
// random point inside unit sphere that is tangent to the hit point
float3 target = rec.pos + rec.normal + MathUtil.RandomInUnitSphere();
float3 target = rec.pos + rec.normal + MathUtil.RandomInUnitSphere(ref state);
scattered = new Ray(rec.pos, float3.Normalize(target - rec.pos));
attenuation = mat.albedo;
@@ -105,7 +105,7 @@ bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray sc
float3 sv = Cross(sw, su);
// sample sphere by solid angle
float cosAMax = MathF.Sqrt(MathF.Max(0.0f, 1.0f - s.radius * s.radius / (rec.pos - s.center).SqLength));
float eps1 = RandomFloat01(), eps2 = RandomFloat01();
float eps1 = RandomFloat01(ref state), eps2 = RandomFloat01(ref state);
float cosA = 1.0f - eps1 + eps1 * cosAMax;
float sinA = MathF.Sqrt(1.0f - cosA * cosA);
float phi = 2 * PI * eps2;
@@ -134,7 +134,7 @@ bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray sc
Debug.Assert(r_in.dir.IsNormalized); Debug.Assert(rec.normal.IsNormalized);
float3 refl = Reflect(r_in.dir, rec.normal);
// reflected ray, and random inside of sphere based on roughness
scattered = new Ray(rec.pos, Normalize(refl + mat.roughness * RandomInUnitSphere()));
scattered = new Ray(rec.pos, Normalize(refl + mat.roughness * RandomInUnitSphere(ref state)));
attenuation = mat.albedo;
return Dot(scattered.dir, rec.normal) > 0;
}
@@ -169,7 +169,7 @@ bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray sc
{
reflProb = 1;
}
if (RandomFloat01() < reflProb)
if (RandomFloat01(ref state) < reflProb)
scattered = new Ray(rec.pos, Normalize(refl));
else
scattered = new Ray(rec.pos, Normalize(refr));
@@ -183,7 +183,7 @@ bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray sc
return true;
}
float3 Trace(Ray r, int depth, ref int inoutRayCount)
float3 Trace(Ray r, int depth, ref int inoutRayCount, ref uint state)
{
Hit rec = default(Hit);
int id = 0;
@@ -194,9 +194,9 @@ float3 Trace(Ray r, int depth, ref int inoutRayCount)
float3 attenuation;
float3 lightE;
var mat = s_SphereMats[id];
if (depth < kMaxDepth && Scatter(mat, r, rec, out attenuation, out scattered, out lightE, ref inoutRayCount))
if (depth < kMaxDepth && Scatter(mat, r, rec, out attenuation, out scattered, out lightE, ref inoutRayCount, ref state))
{
return mat.emissive + lightE + attenuation * Trace(scattered, depth + 1, ref inoutRayCount);
return mat.emissive + lightE + attenuation * Trace(scattered, depth + 1, ref inoutRayCount, ref state);
}
else
{
@@ -224,15 +224,16 @@ int TraceRowJob(int y, int screenWidth, int screenHeight, int frameCount, float[
int rayCount = 0;
//for (uint32_t y = start; y < end; ++y)
{
uint state = (uint)(y * 9781 + frameCount * 6271) | 1;
for (int x = 0; x < screenWidth; ++x)
{
float3 col = new float3(0, 0, 0);
for (int s = 0; s < DO_SAMPLES_PER_PIXEL; s++)
{
float u = (x + RandomFloat01()) * invWidth;
float v = (y + RandomFloat01()) * invHeight;
Ray r = cam.GetRay(u, v);
col += Trace(r, 0, ref rayCount);
float u = (x + RandomFloat01(ref state)) * invWidth;
float v = (y + RandomFloat01(ref state)) * invHeight;
Ray r = cam.GetRay(u, v, ref state);
col += Trace(r, 0, ref rayCount, ref state);
}
col *= 1.0f / (float)DO_SAMPLES_PER_PIXEL;
col = new float3(MathF.Sqrt(col.x), MathF.Sqrt(col.y), MathF.Sqrt(col.z));
@@ -1,4 +1,4 @@
#define DO_ANIMATE
//#define DO_ANIMATE
#define DO_LIGHT_SAMPLING
#define DO_THREADED
@@ -25,7 +25,7 @@ public Material(Type t, float3 a, float3 e, float r, float i)
class Test
{
const int DO_SAMPLES_PER_PIXEL = 1;
const int DO_SAMPLES_PER_PIXEL = 4;
const float DO_ANIMATE_SMOOTHING = 0.5f;
static Sphere[] s_SpheresData = {
@@ -223,20 +223,20 @@ public void Execute(int y)
float invWidth = 1.0f / screenWidth;
float invHeight = 1.0f / screenHeight;
float lerpFac = (float)frameCount / (float)(frameCount + 1);
uint randState = (uint)(frameCount * 37 + y * 13 + 1);
#if DO_ANIMATE
lerpFac *= DO_ANIMATE_SMOOTHING;
#endif
uint state = (uint)(y * 9781 + frameCount * 6271) | 1;
int rayCount = 0;
for (int x = 0; x < screenWidth; ++x)
{
float3 col = new float3(0, 0, 0);
for (int s = 0; s < DO_SAMPLES_PER_PIXEL; s++)
{
float u = (x + RandomFloat01(ref randState)) * invWidth;
float v = (y + RandomFloat01(ref randState)) * invHeight;
Ray r = cam.GetRay(u, v, ref randState);
col += Trace(r, 0, ref rayCount, spheres, materials, ref randState);
float u = (x + RandomFloat01(ref state)) * invWidth;
float v = (y + RandomFloat01(ref state)) * invHeight;
Ray r = cam.GetRay(u, v, ref state);
col += Trace(r, 0, ref rayCount, spheres, materials, ref state);
}
col *= 1.0f / (float)DO_SAMPLES_PER_PIXEL;
col = sqrt(col);

0 comments on commit e2aee49

Please sign in to comment.