From: Andy Ayers Date: Wed, 20 Jan 2016 22:10:27 +0000 (-0800) Subject: Add the RayTracer benchmark X-Git-Tag: accepted/tizen/base/20180629.140029~5791^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1b59223f6bddf57c79dc83c21d9580cd764a36fa;p=platform%2Fupstream%2Fcoreclr.git Add the RayTracer benchmark --- diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs new file mode 100644 index 0000000..f7482be --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Camera.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +internal class Camera +{ + public Vector Pos; + public Vector Forward; + public Vector Up; + public Vector Right; + + public Camera(Vector pos, Vector forward, Vector up, Vector right) { Pos = pos; Forward = forward; Up = up; Right = right; } + + public static Camera Create(Vector pos, Vector lookAt) + { + Vector forward = Vector.Norm(Vector.Minus(lookAt, pos)); + Vector down = new Vector(0, -1, 0); + Vector right = Vector.Times(1.5F, Vector.Norm(Vector.Cross(forward, down))); + Vector up = Vector.Times(1.5F, Vector.Norm(Vector.Cross(forward, right))); + + return new Camera(pos, forward, up, right); + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs new file mode 100644 index 0000000..278e3d7 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Color.cs @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Numerics; + +public struct Color +{ + private Vector3 _simdVector; + public float R { get { return _simdVector.X; } } + public float G { get { return _simdVector.Y; } } + public float B { get { return _simdVector.Z; } } + + public Color(double r, double g, double b) + { + _simdVector = new Vector3((float)r, (float)g, (float)b); + } + public Color(string str) + { + string[] nums = str.Split(','); + if (nums.Length != 3) throw new ArgumentException(); + _simdVector = new Vector3(float.Parse(nums[0]), float.Parse(nums[1]), float.Parse(nums[2])); + } + + + public static Color Times(double n, Color v) + { + Color result; + result._simdVector = (float)n * v._simdVector; + return result; + } + public static Color Times(Color v1, Color v2) + { + Color result; + result._simdVector = v1._simdVector * v2._simdVector; + return result; + } + + public static Color Plus(Color v1, Color v2) + { + Color result; + result._simdVector = v1._simdVector + v2._simdVector; + return result; + } + public static Color Minus(Color v1, Color v2) + { + Color result; + result._simdVector = v1._simdVector - v2._simdVector; + return result; + } + + public static Color Background { get { Color result; result._simdVector = Vector3.Zero; return result; } } + public static Color DefaultColor { get { Color result; result._simdVector = Vector3.Zero; return result; } } + + public static float Legalize(float d) + { + return d > 1 ? 1 : d; + } + + public static byte ToByte(float c) + { + return (byte)(255 * Legalize(c)); + } + + public static Int32 ToInt32(float c) + { + Int32 r = (Int32)(255 * c); + return (r > 255 ? 255 : r); + } + + public Int32 ToInt32() + { + return (ToInt32(B) | ToInt32(G) << 8 | ToInt32(R) << 16 | 255 << 24); + } + + public float Brightness() + { + float r = (float)R / 255.0f; + float g = (float)G / 255.0f; + float b = (float)B / 255.0f; + + float max, min; + + max = r; min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + return (max + min) / 2; + } + + public void ChangeHue(float hue) + { + float H, S, L, Br; + + Br = Brightness(); + H = hue; + S = 0.9F; + L = ((Br - 0.5F) * 0.5F) + 0.5F; + + if (L == 0) + { + _simdVector = Vector3.Zero; + } + else + { + if (S == 0) + { + _simdVector = new Vector3(L); + } + else + { + float temp2 = ((L <= 0.5F) ? L * (1.0F + S) : L + S - (L * S)); + float temp1 = 2.0F * L - temp2; + + float[] t3 = new float[] { H + 1.0F / 3.0F, H, H - 1.0F / 3.0F }; + float[] clr = new float[] { 0, 0, 0 }; + + for (int i = 0; i < 3; i++) + { + if (t3[i] < 0) t3[i] += 1.0F; + if (t3[i] > 1) t3[i] -= 1.0F; + if (6.0 * t3[i] < 1.0) + clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0F; + else if (2.0 * t3[i] < 1.0) + clr[i] = temp2; + else if (3.0 * t3[i] < 2.0) + clr[i] = (temp1 + (temp2 - temp1) * ((2.0F / 3.0F) - t3[i]) * 6.0F); + else + clr[i] = temp1; + } + + _simdVector = new Vector3(clr[0], clr[1], clr[2]); + } + } + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs new file mode 100644 index 0000000..819dcb4 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ISect.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +internal class ISect +{ + public SceneObject Thing; + public Ray Ray; + public double Dist; + + public ISect(SceneObject thing, Ray ray, double dist) { Thing = thing; Ray = ray; Dist = dist; } + + public static bool IsNull(ISect sect) { return sect == null; } + public readonly static ISect Null = null; +} diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs new file mode 100644 index 0000000..fa884ab --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Light.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +internal class Light +{ + public Vector Pos; + public Color Color; + + public Light(Vector pos, Color color) { Pos = pos; Color = color; } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs new file mode 100644 index 0000000..2b88c82 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ObjectPool.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// Provides a thread-safe object pool. + /// Specifies the type of the elements stored in the pool. + [DebuggerDisplay("Count={Count}")] + [DebuggerTypeProxy(typeof(IProducerConsumerCollection_DebugView<>))] + public sealed class ObjectPool : ProducerConsumerCollectionBase + { + private readonly Func _generator; + + /// Initializes an instance of the ObjectPool class. + /// The function used to create items when no items exist in the pool. + public ObjectPool(Func generator) : this(generator, new ConcurrentQueue()) { } + + /// Initializes an instance of the ObjectPool class. + /// The function used to create items when no items exist in the pool. + /// The collection used to store the elements of the pool. + public ObjectPool(Func generator, IProducerConsumerCollection collection) + : base(collection) + { + if (generator == null) throw new ArgumentNullException("generator"); + _generator = generator; + } + + /// Adds the provided item into the pool. + /// The item to be added. + public void PutObject(T item) { base.TryAdd(item); } + + /// Gets an item from the pool. + /// The removed or created item. + /// If the pool is empty, a new item will be created and returned. + public T GetObject() + { + T value; + return base.TryTake(out value) ? value : _generator(); + } + + /// Clears the object pool, returning all of the data that was in the pool. + /// An array containing all of the elements in the pool. + public T[] ToArrayAndClear() + { + var items = new List(); + T value; + while (base.TryTake(out value)) items.Add(value); + return items.ToArray(); + } + + protected override bool TryAdd(T item) + { + PutObject(item); + return true; + } + + protected override bool TryTake(out T item) + { + item = GetObject(); + return true; + } + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs new file mode 100644 index 0000000..6a16fcb --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Plane.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +internal class Plane : SceneObject +{ + public Vector Norm; + public double Offset; + + public Plane(Vector norm, double offset, Surface surface) : base(surface) { Norm = norm; Offset = offset; } + + public override ISect Intersect(Ray ray) + { + double denom = Vector.Dot(Norm, ray.Dir); + if (denom > 0) return ISect.Null; + return new ISect(this, ray, (Vector.Dot(Norm, ray.Start) + Offset) / (-denom)); + } + + public override Vector Normal(Vector pos) + { + return Norm; + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs new file mode 100644 index 0000000..2abf0f3 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/ProducerConsumerCollectionBase.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// Debug view for the IProducerConsumerCollection. + /// Specifies the type of the data being aggregated. + internal sealed class IProducerConsumerCollection_DebugView + { + private IProducerConsumerCollection _collection; + + public IProducerConsumerCollection_DebugView(IProducerConsumerCollection collection) + { + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Values { get { return _collection.ToArray(); } } + } + + /// + /// Provides a base implementation for producer-consumer collections that wrap other + /// producer-consumer collections. + /// + /// Specifies the type of elements in the collection. + public abstract class ProducerConsumerCollectionBase : IProducerConsumerCollection + { + private readonly IProducerConsumerCollection _contained; + + /// Initializes the ProducerConsumerCollectionBase instance. + /// The collection to be wrapped by this instance. + protected ProducerConsumerCollectionBase(IProducerConsumerCollection contained) + { + if (contained == null) throw new ArgumentNullException("contained"); + _contained = contained; + } + + /// Gets the contained collection. + protected IProducerConsumerCollection ContainedCollection { get { return _contained; } } + + /// Attempts to add the specified value to the end of the deque. + /// The item to add. + /// true if the item could be added; otherwise, false. + protected virtual bool TryAdd(T item) { return _contained.TryAdd(item); } + + /// Attempts to remove and return an item from the collection. + /// + /// When this method returns, if the operation was successful, item contains the item removed. If + /// no item was available to be removed, the value is unspecified. + /// + /// + /// true if an element was removed and returned from the collection; otherwise, false. + /// + protected virtual bool TryTake(out T item) { return _contained.TryTake(out item); } + + /// Attempts to add the specified value to the end of the deque. + /// The item to add. + /// true if the item could be added; otherwise, false. + bool IProducerConsumerCollection.TryAdd(T item) { return TryAdd(item); } + + /// Attempts to remove and return an item from the collection. + /// + /// When this method returns, if the operation was successful, item contains the item removed. If + /// no item was available to be removed, the value is unspecified. + /// + /// + /// true if an element was removed and returned from the collection; otherwise, false. + /// + bool IProducerConsumerCollection.TryTake(out T item) { return TryTake(out item); } + + /// Gets the number of elements contained in the collection. + public int Count { get { return _contained.Count; } } + + /// Creates an array containing the contents of the collection. + /// The array. + public T[] ToArray() { return _contained.ToArray(); } + + /// Copies the contents of the collection to an array. + /// The array to which the data should be copied. + /// The starting index at which data should be copied. + public void CopyTo(T[] array, int index) { _contained.CopyTo(array, index); } + + /// Copies the contents of the collection to an array. + /// The array to which the data should be copied. + /// The starting index at which data should be copied. + void ICollection.CopyTo(Array array, int index) { _contained.CopyTo(array, index); } + + /// Gets an enumerator for the collection. + /// An enumerator. + public IEnumerator GetEnumerator() { return _contained.GetEnumerator(); } + + /// Gets an enumerator for the collection. + /// An enumerator. + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// Gets whether the collection is synchronized. + bool ICollection.IsSynchronized { get { return _contained.IsSynchronized; } } + + /// Gets the synchronization root object for the collection. + object ICollection.SyncRoot { get { return _contained.SyncRoot; } } + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs new file mode 100644 index 0000000..9472002 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Ray.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +internal struct Ray +{ + public Vector Start; + public Vector Dir; + + public Ray(Vector start, Vector dir) { Start = start; Dir = dir; } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj new file mode 100644 index 0000000..3399cef --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracer.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + Properties + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages + ..\..\ + 7a9bfb7d + + + + + + pdbonly + true + + + + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)benchmark\project.json + $(JitPackagesConfigFileDirectory)benchmark\project.lock.json + + + + + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs new file mode 100644 index 0000000..03f694d --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/RayTracerBench.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// Based on the Raytracer example from +// Samples for Parallel Programming with the .NET Framework +// https://code.msdn.microsoft.com/windowsdesktop/Samples-for-Parallel-b4b76364 + +using Microsoft.Xunit.Performance; +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Concurrent; + +[assembly: OptimizeForBenchmarks] +[assembly: MeasureInstructionsRetired] + +public class RayTracerBench +{ +#if DEBUG + + private const int RunningTime = 200; + private const int Width = 100; + private const int Height = 100; + private const int Iterations = 1; + private const int MaxIterations = 1000; + +#else + + private const int RunningTime = 1000; + private const int Width = 250; + private const int Height = 250; + private const int Iterations = 7; + private const int MaxIterations = 1000; + +#endif + + public RayTracerBench() + { + _width = Width; + _height = Height; + _parallel = false; + _showThreads = false; + _freeBuffers = new ObjectPool(() => new int[_width * _height]); + } + + private double _framesPerSecond; + private bool _parallel; + private bool _showThreads; + private int _width, _height; + private int _degreeOfParallelism = Environment.ProcessorCount; + private int _frames; + private CancellationTokenSource _cancellation; + private ObjectPool _freeBuffers; + + private void RenderTest() + { + _cancellation = new CancellationTokenSource(RunningTime); + RenderLoop(MaxIterations); + } + + private void RenderBench() + { + _cancellation = new CancellationTokenSource(); + RenderLoop(Iterations); + } + + private void RenderLoop(int iterations) + { + // Create a ray tracer, and create a reference to "sphere2" that we are going to bounce + var rayTracer = new RayTracer(_width, _height); + var scene = rayTracer.DefaultScene; + var sphere2 = (Sphere)scene.Things[0]; // The first item is assumed to be our sphere + var baseY = sphere2.Radius; + sphere2.Center.Y = sphere2.Radius; + + // Timing determines how fast the ball bounces as well as diagnostics frames/second info + var renderingTime = new Stopwatch(); + var totalTime = Stopwatch.StartNew(); + + // Keep rendering until the iteration count is hit + for (_frames = 0; _frames < iterations; _frames++) + { + // Or the rendering task has been canceled + if (_cancellation.IsCancellationRequested) + { + break; + } + + // Get the next buffer + var rgb = _freeBuffers.GetObject(); + + // Determine the new position of the sphere based on the current time elapsed + double dy2 = 0.8 * Math.Abs(Math.Sin(totalTime.ElapsedMilliseconds * Math.PI / 3000)); + sphere2.Center.Y = (float)(baseY + dy2); + + // Render the scene + renderingTime.Reset(); + renderingTime.Start(); + ParallelOptions options = new ParallelOptions + { + MaxDegreeOfParallelism = _degreeOfParallelism, + CancellationToken = _cancellation.Token + }; + if (!_parallel) rayTracer.RenderSequential(scene, rgb); + else if (_showThreads) rayTracer.RenderParallelShowingThreads(scene, rgb, options); + else rayTracer.RenderParallel(scene, rgb, options); + renderingTime.Stop(); + + _framesPerSecond = (1000.0 / renderingTime.ElapsedMilliseconds); + _freeBuffers.PutObject(rgb); + } + } + + [Benchmark] + public static void Bench() + { + var m = new RayTracerBench(); + foreach (var iteration in Benchmark.Iterations) + { + using (iteration.StartMeasurement()) + { + m.RenderBench(); + } + } + } + + public bool Run() + { + RenderTest(); + Console.WriteLine("{0} frames, {1} frames/sec", + _frames, + _framesPerSecond.ToString("F2")); + return true; + } + + public static int Main(string[] args) + { + var r = new RayTracerBench(); + bool result = r.Run(); + return (result ? 100 : -1); + } +} diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs new file mode 100644 index 0000000..d9ab5ef --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Raytracer.cs @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Numerics; + +internal sealed class RayTracer +{ + private int _screenWidth; + private int _screenHeight; + private const int MaxDepth = 5; + + public RayTracer(int screenWidth, int screenHeight) + { + _screenWidth = screenWidth; + _screenHeight = screenHeight; + } + + internal void RenderSequential(Scene scene, Int32[] rgb) + { + for (int y = 0; y < _screenHeight; y++) + { + int stride = y * _screenWidth; + Camera camera = scene.Camera; + for (int x = 0; x < _screenWidth; x++) + { + Color color = TraceRay(new Ray(camera.Pos, GetPoint(x, y, camera)), scene, 0); + rgb[x + stride] = color.ToInt32(); + } + } + } + + internal void RenderParallel(Scene scene, Int32[] rgb, ParallelOptions options) + { + Parallel.For(0, _screenHeight, options, y => + { + int stride = y * _screenWidth; + Camera camera = scene.Camera; + for (int x = 0; x < _screenWidth; x++) + { + Color color = TraceRay(new Ray(camera.Pos, GetPoint(x, y, camera)), scene, 0); + rgb[x + stride] = color.ToInt32(); + } + }); + } + + internal void RenderParallelShowingThreads(Scene scene, Int32[] rgb, ParallelOptions options) + { + int id = 0; + Parallel.For(0, _screenHeight, options, () => GetHueShift(Interlocked.Increment(ref id)), (y, state, hue) => + { + int stride = y * _screenWidth; + Camera camera = scene.Camera; + for (int x = 0; x < _screenWidth; x++) + { + Color color = TraceRay(new Ray(camera.Pos, GetPoint(x, y, camera)), scene, 0); + color.ChangeHue(hue); + rgb[x + stride] = color.ToInt32(); + } + return hue; + }, + hue => Interlocked.Decrement(ref id)); + } + + private Dictionary _numToHueShiftLookup = new Dictionary(); + private Random _rand = new Random(); + + private float GetHueShift(int id) + { + float shift; + lock (_numToHueShiftLookup) + { + if (!_numToHueShiftLookup.TryGetValue(id, out shift)) + { + shift = (float)_rand.NextDouble(); + _numToHueShiftLookup.Add(id, shift); + } + } + return shift; + } + + internal readonly Scene DefaultScene = CreateDefaultScene(); + + private static Scene CreateDefaultScene() + { + SceneObject[] things = { + new Sphere( new Vector(-0.5,1,1.5), 0.5, Surfaces.MatteShiny), + new Sphere( new Vector(0,1,-0.25), 1, Surfaces.Shiny), + new Plane( new Vector(0,1,0), 0, Surfaces.CheckerBoard) + }; + Light[] lights = { + new Light(new Vector(-2,2.5,0),new Color(.5,.45,.41)), + new Light(new Vector(2,4.5,2), new Color(.99,.95,.8)) + }; + Camera camera = Camera.Create(new Vector(2.75, 2, 3.75), new Vector(-0.6, .5, 0)); + + return new Scene(things, lights, camera); + } + + + private ISect MinIntersection(Ray ray, Scene scene) + { + ISect min = ISect.Null; + foreach (SceneObject obj in scene.Things) + { + ISect isect = obj.Intersect(ray); + if (!ISect.IsNull(isect)) + { + if (ISect.IsNull(min) || min.Dist > isect.Dist) + { + min = isect; + } + } + } + return min; + } + + private double TestRay(Ray ray, Scene scene) + { + ISect isect = MinIntersection(ray, scene); + if (ISect.IsNull(isect)) + return 0; + return isect.Dist; + } + + private Color TraceRay(Ray ray, Scene scene, int depth) + { + ISect isect = MinIntersection(ray, scene); + if (ISect.IsNull(isect)) + return Color.Background; + return Shade(isect, scene, depth); + } + + private Color GetNaturalColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene) + { + Color ret = new Color(0, 0, 0); + foreach (Light light in scene.Lights) + { + Vector ldis = Vector.Minus(light.Pos, pos); + Vector livec = Vector.Norm(ldis); + double neatIsect = TestRay(new Ray(pos, livec), scene); + bool isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0)); + if (!isInShadow) + { + float illum = Vector.Dot(livec, norm); + Color lcolor = illum > 0 ? Color.Times(illum, light.Color) : new Color(0, 0, 0); + float specular = Vector.Dot(livec, Vector.Norm(rd)); + Color scolor = specular > 0 ? Color.Times(Math.Pow(specular, thing.Surface.Roughness), light.Color) : new Color(0, 0, 0); + ret = Color.Plus(ret, Color.Plus(Color.Times(thing.Surface.Diffuse(pos), lcolor), + Color.Times(thing.Surface.Specular(pos), scolor))); + } + } + return ret; + } + + private Color GetReflectionColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene, int depth) + { + return Color.Times(thing.Surface.Reflect(pos), TraceRay(new Ray(pos, rd), scene, depth + 1)); + } + + private Color Shade(ISect isect, Scene scene, int depth) + { + Vector d = isect.Ray.Dir; + Vector pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start); + Vector normal = isect.Thing.Normal(pos); + Vector reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal)); + Color ret = Color.DefaultColor; + ret = Color.Plus(ret, GetNaturalColor(isect.Thing, pos, normal, reflectDir, scene)); + if (depth >= MaxDepth) + { + return Color.Plus(ret, new Color(.5, .5, .5)); + } + return Color.Plus(ret, GetReflectionColor(isect.Thing, Vector.Plus(pos, Vector.Times(.001, reflectDir)), normal, reflectDir, scene, depth)); + } + + private double RecenterX(double x) + { + return (x - (_screenWidth / 2.0)) / (2.0 * _screenWidth); + } + private double RecenterY(double y) + { + return -(y - (_screenHeight / 2.0)) / (2.0 * _screenHeight); + } + + private Vector GetPoint(double x, double y, Camera camera) + { + return Vector.Norm(Vector.Plus(camera.Forward, Vector.Plus(Vector.Times(RecenterX(x), camera.Right), + Vector.Times(RecenterY(y), camera.Up)))); + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs new file mode 100644 index 0000000..77a65d1 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Scene.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; + +internal class Scene +{ + public SceneObject[] Things; + public Light[] Lights; + public Camera Camera; + + public Scene(SceneObject[] things, Light[] lights, Camera camera) { Things = things; Lights = lights; Camera = camera; } + + public IEnumerable Intersect(Ray r) + { + foreach (SceneObject obj in Things) + { + yield return obj.Intersect(r); + } + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs new file mode 100644 index 0000000..e7ab24f --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/SceneObject.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +internal abstract class SceneObject +{ + public Surface Surface; + public abstract ISect Intersect(Ray ray); + public abstract Vector Normal(Vector pos); + + public SceneObject(Surface surface) { Surface = surface; } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs new file mode 100644 index 0000000..93b17d6 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Sphere.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + +internal class Sphere : SceneObject +{ + public Vector Center; + public float Radius; + + public Sphere(Vector center, double radius, Surface surface) : base(surface) { Center = center; Radius = (float)radius; } + + public override ISect Intersect(Ray ray) + { + Vector eo = Vector.Minus(Center, ray.Start); + float v = Vector.Dot(eo, ray.Dir); + float dist; + if (v < 0) + { + dist = 0; + } + else + { + double disc = Math.Pow(Radius, 2) - (Vector.Dot(eo, eo) - Math.Pow(v, 2)); + dist = disc < 0 ? 0 : v - (float)Math.Sqrt(disc); + } + if (dist == 0) return ISect.Null; + return new ISect(this, ray, dist); + } + + public override Vector Normal(Vector pos) + { + return Vector.Norm(Vector.Minus(pos, Center)); + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs new file mode 100644 index 0000000..fac64e5 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surface.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + +internal class Surface +{ + public Func Diffuse; + public Func Specular; + public Func Reflect; + public double Roughness; + + public Surface(Func Diffuse, + Func Specular, + Func Reflect, + double Roughness) + { + this.Diffuse = Diffuse; + this.Specular = Specular; + this.Reflect = Reflect; + this.Roughness = Roughness; + } +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs new file mode 100644 index 0000000..14ecd24 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Surfaces.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + +internal static class Surfaces +{ + // Only works with X-Z plane. + public static readonly Surface CheckerBoard = + new Surface( + delegate (Vector pos) + { + return ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0) + ? new Color(1, 1, 1) + : new Color(0.02, 0.0, 0.14); + }, + delegate (Vector pos) { return new Color(1, 1, 1); }, + delegate (Vector pos) + { + return ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0) + ? .1 + : .5; + }, + 150); + + + + public static readonly Surface Shiny = + new Surface( + delegate (Vector pos) { return new Color(1, 1, 1); }, + delegate (Vector pos) { return new Color(.5, .5, .5); }, + delegate (Vector pos) { return .7; }, + 250); + + public static readonly Surface MatteShiny = + new Surface( + delegate (Vector pos) { return new Color(1, 1, 1); }, + delegate (Vector pos) { return new Color(.25, .25, .25); }, + delegate (Vector pos) { return .7; }, + 250); +} + diff --git a/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs new file mode 100644 index 0000000..49ee439 --- /dev/null +++ b/tests/src/JIT/Performance/CodeQuality/SIMD/RayTracer/Vector.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +internal struct Vector +{ + private Vector3 _simdVector; + public float X { get { return _simdVector.X; } } + public float Y { get { return _simdVector.Y; } set { _simdVector = new Vector3(_simdVector.X, value, _simdVector.Z); } } + public float Z { get { return _simdVector.Z; } } + + public Vector(double x, double y, double z) + { + _simdVector = new Vector3((float)x, (float)y, (float)z); + } + public Vector(string str) + { + string[] nums = str.Split(','); + if (nums.Length != 3) throw new ArgumentException(); + _simdVector = new Vector3(float.Parse(nums[0]), float.Parse(nums[1]), float.Parse(nums[2])); + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static Vector Times(double n, Vector v) + { + Vector result; + result._simdVector = (float)n * v._simdVector; + return result; + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static Vector Minus(Vector v1, Vector v2) + { + Vector result; + result._simdVector = v1._simdVector - v2._simdVector; + return result; + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static Vector Plus(Vector v1, Vector v2) + { + Vector result; + result._simdVector = v1._simdVector + v2._simdVector; + return result; + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static float Dot(Vector v1, Vector v2) + { + return Vector3.Dot(v1._simdVector, v2._simdVector); + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static float Mag(Vector v) { return (float)Math.Sqrt(Dot(v, v)); } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static Vector Norm(Vector v) + { + float mag = Mag(v); + float div = mag == 0 ? float.PositiveInfinity : 1 / mag; + return Times(div, v); + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static Vector Cross(Vector v1, Vector v2) + { + return new Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)), + ((v1.Z * v2.X) - (v1.X * v2.Z)), + ((v1.X * v2.Y) - (v1.Y * v2.X))); + } + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static bool Equals(Vector v1, Vector v2) + { + return v1._simdVector.Equals(v2._simdVector); + } + + public static Vector Null { get { Vector result; result._simdVector = Vector3.Zero; return result; } } +} +