diff --git a/src/benchmarks/micro/MicroBenchmarks.csproj b/src/benchmarks/micro/MicroBenchmarks.csproj index 0787630ff8c..e49272f95da 100644 --- a/src/benchmarks/micro/MicroBenchmarks.csproj +++ b/src/benchmarks/micro/MicroBenchmarks.csproj @@ -8,6 +8,8 @@ pdbonly true true + + true @@ -68,5 +70,9 @@ - - \ No newline at end of file + + + + + + diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Camera.cs b/src/benchmarks/micro/coreclr/PacketTracer/Camera.cs new file mode 100644 index 00000000000..e7bd54e9d40 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Camera.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics; + +namespace HardwareIntrinsics.RayTracer +{ + internal class Camera + { + public Camera(VectorPacket256 pos, VectorPacket256 forward, VectorPacket256 up, VectorPacket256 right) + { + Pos = pos; + Forward = forward; + Up = up; + Right = right; + } + + public VectorPacket256 Pos; + public VectorPacket256 Forward; + public VectorPacket256 Up; + public VectorPacket256 Right; + + public static Camera Create(VectorPacket256 pos, VectorPacket256 lookAt) + { + VectorPacket256 forward = (lookAt - pos).Normalize(); + VectorPacket256 down = + new VectorPacket256(Vector256.Zero, Vector256.Create(-1.0f), Vector256.Zero); + Vector256 OnePointFive = Vector256.Create(1.5f); + VectorPacket256 right = OnePointFive * VectorPacket256.CrossProduct(forward, down).Normalize(); + VectorPacket256 up = OnePointFive * VectorPacket256.CrossProduct(forward, right).Normalize(); + + return new Camera(pos, forward, up, right); + } + } +} diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Color.cs b/src/benchmarks/micro/coreclr/PacketTracer/Color.cs new file mode 100644 index 00000000000..4cc936e2faf --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Color.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace HardwareIntrinsics.RayTracer +{ + internal struct Color + { + public float R { get; } + public float G { get; } + public float B { get; } + + public static readonly Color Background = new Color(0, 0, 0); + + public Color(float _r, float _g, float _b) + { + R = _r; + G = _g; + B = _b; + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/ColorPacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/ColorPacket.cs new file mode 100644 index 00000000000..e6953e58def --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/ColorPacket.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +using ColorPacket256 = HardwareIntrinsics.RayTracer.VectorPacket256; + +namespace HardwareIntrinsics.RayTracer +{ + internal static class ColorPacket256Helper + { + private static readonly Vector256 One = Vector256.Create(1.0f); + private static readonly Vector256 Max = Vector256.Create(255.0f); + + public static Int32RGBPacket256 ConvertToIntRGB(this VectorPacket256 colors) + { + var rsMask = Compare(colors.Xs, One, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var gsMask = Compare(colors.Ys, One, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var bsMask = Compare(colors.Zs, One, FloatComparisonMode.GreaterThanOrderedNonSignaling); + + var rs = BlendVariable(colors.Xs, One, rsMask); + var gs = BlendVariable(colors.Ys, One, gsMask); + var bs = BlendVariable(colors.Zs, One, bsMask); + + var rsInt = ConvertToVector256Int32(Multiply(rs, Max)); + var gsInt = ConvertToVector256Int32(Multiply(gs, Max)); + var bsInt = ConvertToVector256Int32(Multiply(bs, Max)); + + return new Int32RGBPacket256(rsInt, gsInt, bsInt); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ColorPacket256 Times(ColorPacket256 left, ColorPacket256 right) + { + return new VectorPacket256(Multiply(left.Xs, right.Xs), Multiply(left.Ys, right.Ys), + Multiply(left.Zs, right.Zs)); + } + + public static readonly ColorPacket256 BackgroundColor = new ColorPacket256(Vector256.Zero); + public static readonly ColorPacket256 DefaultColor = new ColorPacket256(Vector256.Zero); + } + + internal struct Int32RGBPacket256 + { + public Vector256 Rs; + public Vector256 Gs; + public Vector256 Bs; + + public Int32RGBPacket256(Vector256 rs, Vector256 gs, Vector256 bs) + { + Rs = rs; + Gs = gs; + Bs = bs; + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Intersections.cs b/src/benchmarks/micro/coreclr/PacketTracer/Intersections.cs new file mode 100644 index 00000000000..6640e3d1631 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Intersections.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +using static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Avx2; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +namespace HardwareIntrinsics.RayTracer +{ + internal struct Intersections + { + public Vector256 Distances; + public Vector256 ThingIndices; + + public static readonly Vector256 NullDistance = Vector256.Create(float.MaxValue); + public static readonly Vector256 NullIndex = Vector256.Create(-1); + + public Intersections(Vector256 dis, Vector256 things) + { + Distances = dis; + ThingIndices = things; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool AllNullIntersections() + { + return AllNullIntersections(Distances); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AllNullIntersections(Vector256 dis) + { + var cmp = Compare(dis, NullDistance, FloatComparisonMode.EqualOrderedNonSignaling); + var zero = Vector256.Zero; + // efficiently generate an all-one mask vector by lower latency AVX2 ComapreEqual + var mask = Avx2.CompareEqual(zero, zero); + return TestC(cmp, mask.AsSingle()); + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/LightPacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/LightPacket.cs new file mode 100644 index 00000000000..f27de9ddb1e --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/LightPacket.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using ColorPacket256 = HardwareIntrinsics.RayTracer.VectorPacket256; + +namespace HardwareIntrinsics.RayTracer +{ + internal class LightPacket256 + { + public VectorPacket256 Positions; + public ColorPacket256 Colors; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public LightPacket256(Vector pos, Color col) + { + Positions = new VectorPacket256(pos.X, pos.Y, pos.Z); + Colors = new ColorPacket256(col.R, col.G, col.B); + } + } +} diff --git a/src/benchmarks/micro/coreclr/PacketTracer/ObjectPacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/ObjectPacket.cs new file mode 100644 index 00000000000..d4c80fd7acf --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/ObjectPacket.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics; + +namespace HardwareIntrinsics.RayTracer +{ + internal abstract class ObjectPacket256 + { + public Surface Surface { get; } + public abstract Vector256 Intersect(RayPacket256 rayPacket256); + public abstract VectorPacket256 Normals(VectorPacket256 pos); + + public ObjectPacket256(Surface surface) + { + Surface = surface; + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/PacketTracer.cs b/src/benchmarks/micro/coreclr/PacketTracer/PacketTracer.cs new file mode 100644 index 00000000000..f1ae3fc03ba --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/PacketTracer.cs @@ -0,0 +1,265 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Avx2; +using static System.Runtime.Intrinsics.X86.Sse2; +using System.Runtime.Intrinsics; + +using ColorPacket256 = HardwareIntrinsics.RayTracer.VectorPacket256; + +namespace HardwareIntrinsics.RayTracer +{ + internal class Packet256Tracer + { + public int Width { get; } + public int Height { get; } + private static readonly int MaxDepth = 5; + + private static readonly Vector256 SevenToZero = Vector256.Create(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f); + + public Packet256Tracer(int width, int height) + { + if ((width % VectorPacket256.Packet256Size) != 0) + { + width += VectorPacket256.Packet256Size - (width % VectorPacket256.Packet256Size); + } + + Width = width; + Height = height; + } + + internal unsafe void RenderVectorized(Scene scene, int* rgb) + { + Camera camera = scene.Camera; + // Iterate y then x in order to preserve cache locality. + for (int y = 0; y < Height; y++) + { + int stride = y * Width; + for (int x = 0; x < Width; x += VectorPacket256.Packet256Size) + { + float fx = x; + Vector256 Xs = Add(Vector256.Create(fx), SevenToZero); + VectorPacket256 dirs = GetPoints(Xs, Vector256.Create((float) (y)), camera); + var rayPacket256 = new RayPacket256(camera.Pos, dirs); + var SoAcolors = TraceRay(rayPacket256, scene, depth: 0); + + var AoS = SoAcolors.Transpose(); + var intAoS = AoS.ConvertToIntRGB(); + + int* output = &rgb[(x + stride) * 3]; // Each pixel has 3 fields (RGB) + { + Store(output, intAoS.Rs); + Store(output + 8, intAoS.Gs); + Store(output + 16, intAoS.Bs); + } + + } + } + + } + + private ColorPacket256 TraceRay(RayPacket256 rayPacket256, Scene scene, int depth) + { + var isect = MinIntersections(rayPacket256, scene); + if (isect.AllNullIntersections()) + { + return ColorPacket256Helper.BackgroundColor; + } + + var color = Shade(isect, rayPacket256, scene, depth); + var isNull = Compare(isect.Distances, Intersections.NullDistance, + FloatComparisonMode.EqualOrderedNonSignaling); + var backgroundColor = ColorPacket256Helper.BackgroundColor.Xs; + return new ColorPacket256(BlendVariable(color.Xs, backgroundColor, isNull), + BlendVariable(color.Ys, backgroundColor, isNull), + BlendVariable(color.Zs, backgroundColor, isNull)); + } + + private Vector256 TestRay(RayPacket256 rayPacket256, Scene scene) + { + var isect = MinIntersections(rayPacket256, scene); + if (isect.AllNullIntersections()) + { + return Vector256.Zero; + } + + var isNull = Compare(isect.Distances, Intersections.NullDistance, + FloatComparisonMode.EqualOrderedNonSignaling); + return BlendVariable(isect.Distances, Vector256.Zero, isNull); + } + + private Intersections MinIntersections(RayPacket256 rayPacket256, Scene scene) + { + Intersections mins = new Intersections(Intersections.NullDistance, Intersections.NullIndex); + for (int i = 0; i < scene.Things.Length; i++) + { + Vector256 distance = scene.Things[i].Intersect(rayPacket256); + + if (!Intersections.AllNullIntersections(distance)) + { + var notNullMask = Compare(distance, Intersections.NullDistance, + FloatComparisonMode.NotEqualOrderedNonSignaling); + var nullMinMask = Compare(mins.Distances, Intersections.NullDistance, + FloatComparisonMode.EqualOrderedNonSignaling); + + var lessMinMask = Compare(mins.Distances, distance, + FloatComparisonMode.GreaterThanOrderedNonSignaling); + var minMask = And(notNullMask, Or(nullMinMask, lessMinMask)); + var minDis = BlendVariable(mins.Distances, distance, minMask); + var minIndices = BlendVariable(mins.ThingIndices.AsSingle(), + Vector256.Create(i).AsSingle(), + minMask).AsInt32(); + mins.Distances = minDis; + mins.ThingIndices = minIndices; + } + } + + return mins; + } + + private ColorPacket256 Shade(Intersections isect, RayPacket256 rays, Scene scene, int depth) + { + + var ds = rays.Dirs; + var pos = isect.Distances * ds + rays.Starts; + var normals = scene.Normals(isect.ThingIndices, pos); + var reflectDirs = + ds - (Multiply(VectorPacket256.DotProduct(normals, ds), Vector256.Create(2.0f)) * normals); + var colors = GetNaturalColor(isect.ThingIndices, pos, normals, reflectDirs, scene); + + if (depth >= MaxDepth) + { + return colors + new ColorPacket256(.5f, .5f, .5f); + } + + return colors + GetReflectionColor(isect.ThingIndices, pos + (Vector256.Create(0.001f) * reflectDirs), + normals, reflectDirs, scene, depth); + } + + private ColorPacket256 GetNaturalColor(Vector256 things, VectorPacket256 pos, VectorPacket256 norms, + VectorPacket256 rds, Scene scene) + { + var colors = ColorPacket256Helper.DefaultColor; + for (int i = 0; i < scene.Lights.Length; i++) + { + var lights = scene.Lights[i]; + var zero = Vector256.Zero; + var colorPacket = lights.Colors; + var ldis = lights.Positions - pos; + var livec = ldis.Normalize(); + var neatIsectDis = TestRay(new RayPacket256(pos, livec), scene); + + // is in shadow? + var mask1 = Compare(neatIsectDis, ldis.Lengths, FloatComparisonMode.LessThanOrEqualOrderedNonSignaling); + var mask2 = Compare(neatIsectDis, zero, FloatComparisonMode.NotEqualOrderedNonSignaling); + var isInShadow = And(mask1, mask2); + + Vector256 illum = VectorPacket256.DotProduct(livec, norms); + Vector256 illumGraterThanZero = + Compare(illum, zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + var tmpColor1 = illum * colorPacket; + var defaultRGB = zero; + Vector256 lcolorR = BlendVariable(defaultRGB, tmpColor1.Xs, illumGraterThanZero); + Vector256 lcolorG = BlendVariable(defaultRGB, tmpColor1.Ys, illumGraterThanZero); + Vector256 lcolorB = BlendVariable(defaultRGB, tmpColor1.Zs, illumGraterThanZero); + ColorPacket256 lcolor = new ColorPacket256(lcolorR, lcolorG, lcolorB); + + Vector256 specular = VectorPacket256.DotProduct(livec, rds.Normalize()); + Vector256 specularGraterThanZero = + Compare(specular, zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + + var difColor = new ColorPacket256(1, 1, 1); + var splColor = new ColorPacket256(1, 1, 1); + var roughness = Vector256.Create(1.0f); + + for (int j = 0; j < scene.Things.Length; j++) + { + Vector256 thingMask = CompareEqual(things, Vector256.Create(j)).AsSingle(); + Vector256 rgh = Vector256.Create(scene.Things[j].Surface.Roughness); + var dif = scene.Things[j].Surface.Diffuse(pos); + var spl = scene.Things[j].Surface.Specular; + + roughness = BlendVariable(roughness, rgh, thingMask); + + difColor.Xs = BlendVariable(difColor.Xs, dif.Xs, thingMask); + difColor.Ys = BlendVariable(difColor.Ys, dif.Ys, thingMask); + difColor.Zs = BlendVariable(difColor.Zs, dif.Zs, thingMask); + + splColor.Xs = BlendVariable(splColor.Xs, spl.Xs, thingMask); + splColor.Ys = BlendVariable(splColor.Ys, spl.Ys, thingMask); + splColor.Zs = BlendVariable(splColor.Zs, spl.Zs, thingMask); + } + + var tmpColor2 = VectorMath.Pow(specular, roughness) * colorPacket; + Vector256 scolorR = BlendVariable(defaultRGB, tmpColor2.Xs, specularGraterThanZero); + Vector256 scolorG = BlendVariable(defaultRGB, tmpColor2.Ys, specularGraterThanZero); + Vector256 scolorB = BlendVariable(defaultRGB, tmpColor2.Zs, specularGraterThanZero); + ColorPacket256 scolor = new ColorPacket256(scolorR, scolorG, scolorB); + + var oldColor = colors; + + colors = colors + ColorPacket256Helper.Times(difColor, lcolor) + + ColorPacket256Helper.Times(splColor, scolor); + + colors = new ColorPacket256(BlendVariable(colors.Xs, oldColor.Xs, isInShadow), + BlendVariable(colors.Ys, oldColor.Ys, isInShadow), + BlendVariable(colors.Zs, oldColor.Zs, isInShadow)); + + } + + return colors; + } + + private ColorPacket256 GetReflectionColor(Vector256 things, VectorPacket256 pos, VectorPacket256 norms, + VectorPacket256 rds, Scene scene, int depth) + { + return scene.Reflect(things, pos) * TraceRay(new RayPacket256(pos, rds), scene, depth + 1); + } + + private readonly static Vector256 ConstTwo = Vector256.Create(2.0f); + + private VectorPacket256 GetPoints(Vector256 x, Vector256 y, Camera camera) + { + Vector256 widthVector = Vector256.Create((float) (Width)); + Vector256 heightVector = Vector256.Create((float) (Height)); + + var widthRate1 = Divide(widthVector, ConstTwo); + var widthRate2 = Multiply(widthVector, ConstTwo); + + var heightRate1 = Divide(heightVector, ConstTwo); + var heightRate2 = Multiply(heightVector, ConstTwo); + + var recenteredX = Divide(Subtract(x, widthRate1), widthRate2); + var recenteredY = Subtract(Vector256.Zero, Divide(Subtract(y, heightRate1), heightRate2)); + + var result = camera.Forward + (recenteredX * camera.Right) + (recenteredY * camera.Up); + + return result.Normalize(); + } + + internal readonly Scene DefaultScene = CreateDefaultScene(); + + private static Scene CreateDefaultScene() + { + ObjectPacket256[] things = + { + new SpherePacket256(new VectorPacket256(-0.5f, 1f, 1.5f), Vector256.Create(0.5f), Surfaces.MatteShiny), + new SpherePacket256(new VectorPacket256(0f, 1f, -0.25f), Vector256.Create(1f), Surfaces.Shiny), + new PlanePacket256((new VectorPacket256(0, 1, 0)), Vector256.Create(0f), Surfaces.CheckerBoard) + }; + + LightPacket256[] lights = + { + new LightPacket256(new Vector(-2f, 2.5f, 0f), new Color(.5f, .45f, .41f)), + new LightPacket256(new Vector(2, 4.5f, 2), new Color(.99f, .95f, .8f)) + }; + + Camera camera = Camera.Create(new VectorPacket256(2.75f, 2f, 3.75f), new VectorPacket256(-0.6f, .5f, 0f)); + + return new Scene(things, lights, camera); + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/PlanePacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/PlanePacket.cs new file mode 100644 index 00000000000..271fcce80f0 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/PlanePacket.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +namespace HardwareIntrinsics.RayTracer +{ + internal sealed class PlanePacket256 : ObjectPacket256 + { + public VectorPacket256 Norms; + public Vector256 Offsets; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public PlanePacket256(VectorPacket256 norms, Vector256 offsets, Surface surface) : base(surface) + { + Norms = norms; + Offsets = offsets; + } + + public override VectorPacket256 Normals(VectorPacket256 pos) + { + return Norms; + } + + public override Vector256 Intersect(RayPacket256 rayPacket256) + { + var denom = VectorPacket256.DotProduct(Norms, rayPacket256.Dirs); + var dist = Divide(Add(VectorPacket256.DotProduct(Norms, rayPacket256.Starts), Offsets), + Subtract(Vector256.Zero, denom)); + var gtMask = Compare(denom, Vector256.Zero, FloatComparisonMode.GreaterThanOrderedNonSignaling); + return BlendVariable(dist, Intersections.NullDistance, gtMask); + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/RayPacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/RayPacket.cs new file mode 100644 index 00000000000..acfa7626030 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/RayPacket.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; + +namespace HardwareIntrinsics.RayTracer +{ + internal class RayPacket256 + { + public VectorPacket256 Starts; + public VectorPacket256 Dirs; + + public RayPacket256(VectorPacket256 starts, VectorPacket256 dirs) + { + Starts = starts; + Dirs = dirs; + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Render.cs b/src/benchmarks/micro/coreclr/PacketTracer/Render.cs new file mode 100644 index 00000000000..7a6d31e7c51 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Render.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Concurrent; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using BenchmarkDotNet.Attributes; +using MicroBenchmarks; + +namespace HardwareIntrinsics.RayTracer +{ + [BenchmarkCategory(Categories.CoreCLR)] + public class SoA + { + private const int RunningTime = 1000; + private const int Width = 248; + private const int Height = 248; + + private int[] rgbBuffer = new int[Width * 3 * Height]; // Each pixel has 3 fields (RGB) + + [GlobalSetup] + public unsafe void Setup() => Render(); // run it once during the Setup to avoid https://github.com/dotnet/BenchmarkDotNet/issues/837s + + [Benchmark] + public unsafe void Render() + { + if (!Avx2.IsSupported) + return; + + // Create a ray tracer, and create a reference to "sphere2" that we are going to bounce + var packetTracer = new Packet256Tracer(Width, Height); + var scene = packetTracer.DefaultScene; + var sphere2 = (SpherePacket256)scene.Things[0]; // The first item is assumed to be our sphere + var baseY = sphere2.Radiuses; + sphere2.Centers.Ys = sphere2.Radiuses; + + float dy2 = 0.8f * MathF.Abs(MathF.Sin((float) (1 * Math.PI / 3000))); + sphere2.Centers.Ys = Avx.Add(baseY, Vector256.Create(dy2)); + + fixed (int* ptr = rgbBuffer) + { + packetTracer.RenderVectorized(scene, ptr); + } + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Scene.cs b/src/benchmarks/micro/coreclr/PacketTracer/Scene.cs new file mode 100644 index 00000000000..4aae44b86fb --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Scene.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +using System.Collections.Generic; +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Avx2; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +namespace HardwareIntrinsics.RayTracer +{ + internal class Scene + { + public ObjectPacket256[] Things; + public LightPacket256[] Lights; + public Camera Camera; + + public Scene(ObjectPacket256[] things, LightPacket256[] lights, Camera camera) + { + Things = things; + Lights = lights; + Camera = camera; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Normals(Vector256 things, VectorPacket256 pos) + { + VectorPacket256 norms = new VectorPacket256(1, 1, 1); + + for (int i = 0; i < Things.Length; i++) + { + Vector256 mask = CompareEqual(things, Vector256.Create(i)).AsSingle(); + var n = Things[i].Normals(pos); + norms.Xs = BlendVariable(norms.Xs, n.Xs, mask); + norms.Ys = BlendVariable(norms.Ys, n.Ys, mask); + norms.Zs = BlendVariable(norms.Zs, n.Zs, mask); + } + + return norms; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector256 Reflect(Vector256 things, VectorPacket256 pos) + { + Vector256 rfl = Vector256.Create(1.0f); + for (int i = 0; i < Things.Length; i++) + { + Vector256 mask = CompareEqual(things, Vector256.Create(i)).AsSingle(); + rfl = BlendVariable(rfl, Things[i].Surface.Reflect(pos), mask); + } + + return rfl; + } + + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/SpherePacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/SpherePacket.cs new file mode 100644 index 00000000000..0d43fca3629 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/SpherePacket.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +namespace HardwareIntrinsics.RayTracer +{ + internal sealed class SpherePacket256 : ObjectPacket256 + { + public VectorPacket256 Centers; + public Vector256 Radiuses; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SpherePacket256(VectorPacket256 centers, Vector256 radiuses, Surface surface) : base(surface) + { + Centers = centers; + Radiuses = radiuses; + } + + public override VectorPacket256 Normals(VectorPacket256 pos) + { + return (pos - Centers).Normalize(); + } + + public override Vector256 Intersect(RayPacket256 rayPacket256) + { + var eo = Centers - rayPacket256.Starts; + var v = VectorPacket256.DotProduct(eo, rayPacket256.Dirs); + var zero = Vector256.Zero; + var vLessZeroMask = Compare(v, zero, FloatComparisonMode.LessThanOrderedNonSignaling); + var discs = Subtract(Multiply(Radiuses, Radiuses), + Subtract(VectorPacket256.DotProduct(eo, eo), Multiply(v, v))); + var discLessZeroMask = Compare(discs, zero, FloatComparisonMode.LessThanOrderedNonSignaling); + var dists = BlendVariable(Subtract(v, Sqrt(discs)), zero, Or(vLessZeroMask, discLessZeroMask)); + var isZero = Compare(dists, zero, FloatComparisonMode.EqualOrderedNonSignaling); + return BlendVariable(dists, Intersections.NullDistance, isZero); + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Surface.cs b/src/benchmarks/micro/coreclr/PacketTracer/Surface.cs new file mode 100644 index 00000000000..1fdc2215946 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Surface.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics; +using System; +using ColorPacket256 = HardwareIntrinsics.RayTracer.VectorPacket256; + +namespace HardwareIntrinsics.RayTracer +{ + internal class Surface + { + public Func Diffuse; + public VectorPacket256 Specular; + public Func> Reflect; + public float Roughness; + + public Surface(Func Diffuse, + VectorPacket256 Specular, + Func> Reflect, + float Roughness) + { + this.Diffuse = Diffuse; + this.Specular = Specular; + this.Reflect = Reflect; + this.Roughness = Roughness; + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Surfaces.cs b/src/benchmarks/micro/coreclr/PacketTracer/Surfaces.cs new file mode 100644 index 00000000000..ad241abb534 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Surfaces.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// + +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using static System.Runtime.Intrinsics.X86.Avx; +using ColorPacket256 = HardwareIntrinsics.RayTracer.VectorPacket256; + +namespace HardwareIntrinsics.RayTracer +{ + internal static class Surfaces + { + private static readonly ColorPacket256 White = new ColorPacket256(Vector256.Create(1.0f)); + + private static readonly ColorPacket256 Black = new ColorPacket256(0.02f, 0.0f, 0.14f); + + // Only works with X-Z plane. + public static readonly Surface CheckerBoard = + new Surface( + delegate(VectorPacket256 pos) + { + var floored = ConvertToVector256Int32(Add(Floor(pos.Zs), Floor(pos.Xs))); + var modMask = Vector256.Create(1); + var evenMaskint = Avx2.And(floored, modMask); + var evenMask = Avx2.CompareEqual(evenMaskint, modMask); + + var resultX = BlendVariable(Black.Xs, White.Xs, evenMask.AsSingle()); + var resultY = BlendVariable(Black.Ys, White.Ys, evenMask.AsSingle()); + var resultZ = BlendVariable(Black.Zs, White.Zs, evenMask.AsSingle()); + + return new ColorPacket256(resultX, resultY, resultZ); + }, + new VectorPacket256(1f, 1f, 1f), + delegate(VectorPacket256 pos) + { + var floored = ConvertToVector256Int32(Add(Floor(pos.Zs), Floor(pos.Xs))); + var modMask = Vector256.Create(1); + var evenMaskUint = Avx2.And(floored, modMask); + var evenMask = Avx2.CompareEqual(evenMaskUint, modMask); + + return BlendVariable(Vector256.Create(0.5f), Vector256.Create(0.1f), evenMask.AsSingle()); + }, + 150f); + + public static readonly Surface Shiny = + new Surface( + delegate(VectorPacket256 pos) { return new VectorPacket256(1f, 1f, 1f); }, + new VectorPacket256(.5f, .5f, .5f), + delegate(VectorPacket256 pos) { return Vector256.Create(0.7f); }, + 250f); + + public static readonly Surface MatteShiny = + new Surface( + delegate(VectorPacket256 pos) { return new VectorPacket256(1f, 1f, 1f); }, + new VectorPacket256(.25f, .25f, .25f), + delegate(VectorPacket256 pos) { return Vector256.Create(0.7f); }, + 250f); + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/Vector.cs b/src/benchmarks/micro/coreclr/PacketTracer/Vector.cs new file mode 100644 index 00000000000..46e69a3c5ba --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/Vector.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace HardwareIntrinsics.RayTracer +{ + internal struct Vector + { + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + + public Vector(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/VectorMath.cs b/src/benchmarks/micro/coreclr/PacketTracer/VectorMath.cs new file mode 100644 index 00000000000..78af804bd96 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/VectorMath.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static System.Runtime.Intrinsics.X86.Avx; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +namespace HardwareIntrinsics.RayTracer +{ + public static class VectorMath + { + static readonly Vector256 MaxValue = Vector256.Create(88.0f); + static readonly Vector256 MinValue = Vector256.Create(-88.0f); + static readonly Vector256 Log2 = Vector256.Create(1.44269502f); + static readonly Vector256 C1 = Vector256.Create(0.693359375f); + static readonly Vector256 C2 = Vector256.Create(-0.0002121944417f); + static readonly Vector256 P0 = Vector256.Create(0.0001987569121f); + static readonly Vector256 P1 = Vector256.Create(0.001398199936f); + static readonly Vector256 P2 = Vector256.Create(0.008333452046f); + static readonly Vector256 P3 = Vector256.Create(0.04166579619f); + static readonly Vector256 P4 = Vector256.Create(0.1666666567f); + static readonly Vector256 LogP0 = Vector256.Create(0.07037683576f); + static readonly Vector256 LogP1 = Vector256.Create(-0.1151461005f); + static readonly Vector256 LogP2 = Vector256.Create(0.1167699844f); + static readonly Vector256 LogP3 = Vector256.Create(-0.1242014095f); + static readonly Vector256 LogP4 = Vector256.Create(0.1424932331f); + static readonly Vector256 LogP5 = Vector256.Create(-0.1666805744f); + static readonly Vector256 LogP6 = Vector256.Create(0.2000071406f); + static readonly Vector256 LogP7 = Vector256.Create(-0.2499999404f); + static readonly Vector256 LogP8 = Vector256.Create(0.3333333135f); + static readonly Vector256 LogQ1 = Vector256.Create(-0.0002121944417f); + static readonly Vector256 LogQ2 = Vector256.Create(0.693359375f); + static readonly Vector256 Point5 = Vector256.Create(0.5f); + static readonly Vector256 Sqrthf = Vector256.Create(0.7071067691f); + static readonly Vector256 One = Vector256.Create(1.0f); + static readonly Vector256 Ox7 = Vector256.Create(127); + static readonly Vector256 MinNormPos = Vector256.Create(8388608); + static readonly Vector256 MantMask = Vector256.Create(-2139095041); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Pow(Vector256 left, Vector256 right) + { + return Exp(Multiply(right, Log(left))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Exp(Vector256 value) + { + value = Min(value, MaxValue); + value = Max(value, MinValue); + Vector256 fx = Multiply(value, Log2); + fx = Floor(Add(fx, Point5)); + + Vector256 tmp = Multiply(fx, C1); + Vector256 z = Multiply(fx, C2); + Vector256 x = Subtract(value, tmp); + x = Subtract(x, z); + z = Multiply(x, x); + Vector256 y = P0; + y = Add(Multiply(y, x), P1); + y = Add(Multiply(y, x), P2); + y = Add(Multiply(y, x), P3); + y = Add(Multiply(y, x), P4); + y = Add(Multiply(y, x), Point5); + y = Add(Add(Multiply(y, z), x), One); + + Vector256 pow2n = ConvertToVector256Int32(fx); + pow2n = Avx2.Add(pow2n, Ox7); + pow2n = Avx2.ShiftLeftLogical(pow2n, 23); + + return Multiply(y, pow2n.AsSingle()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Log(Vector256 value) + { + Vector256 invalidMask = Compare(value, Vector256.Zero, + FloatComparisonMode.LessThanOrEqualOrderedNonSignaling); + Vector256 x = Max(value, MinNormPos.AsSingle()); + Vector256 ei = Avx2.ShiftRightLogical(x.AsInt32(), 23); + x = Or(And(x, MantMask.AsSingle()), Point5); + ei = Avx2.Subtract(ei, Ox7); + Vector256 e = Add(ConvertToVector256Single(ei), One); + Vector256 mask = Compare(x, Sqrthf, FloatComparisonMode.LessThanOrderedNonSignaling); + Vector256 tmp = And(x, mask); + x = Subtract(x, One); + e = Subtract(e, And(One, mask)); + x = Add(x, tmp); + Vector256 z = Multiply(x, x); + Vector256 y = LogP0; + y = Add(Multiply(y, x), LogP1); + y = Add(Multiply(y, x), LogP2); + y = Add(Multiply(y, x), LogP3); + y = Add(Multiply(y, x), LogP4); + y = Add(Multiply(y, x), LogP5); + y = Add(Multiply(y, x), LogP6); + y = Add(Multiply(y, x), LogP7); + y = Add(Multiply(y, x), LogP8); + y = Multiply(Multiply(y, x), z); + y = Add(y, Multiply(e, LogQ1)); + y = Subtract(y, Multiply(z, Point5)); + x = Add(Add(x, y), Multiply(e, LogQ2)); + return Or(x, invalidMask); + } + } +} \ No newline at end of file diff --git a/src/benchmarks/micro/coreclr/PacketTracer/VectorPacket.cs b/src/benchmarks/micro/coreclr/PacketTracer/VectorPacket.cs new file mode 100644 index 00000000000..6ce607bc6d8 --- /dev/null +++ b/src/benchmarks/micro/coreclr/PacketTracer/VectorPacket.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static System.Runtime.Intrinsics.X86.Avx; +using static System.Runtime.Intrinsics.X86.Sse; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; + +namespace HardwareIntrinsics.RayTracer +{ + internal struct VectorPacket256 + { + public Vector256 Xs; + public Vector256 Ys; + public Vector256 Zs; + + public Vector256 Lengths + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Sqrt(DotProduct(this, this)); } + } + + + public readonly static int Packet256Size = 8; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(Vector256 init) + { + Xs = init; + Ys = init; + Zs = init; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(float xs, float ys, float zs) + { + Xs = Vector256.Create(xs); + Ys = Vector256.Create(ys); + Zs = Vector256.Create(zs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256(Vector256 _Xs, Vector256 _ys, Vector256 _Zs) + { + Xs = _Xs; + Ys = _ys; + Zs = _Zs; + } + + // Convert AoS vectors to SoA Packet256 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe VectorPacket256(float* vectors) + { + Vector256 m03 = LoadVector128(&vectors[0]).ToVector256(); // load lower halves + Vector256 m14 = LoadVector128(&vectors[4]).ToVector256(); + Vector256 m25 = LoadVector128(&vectors[8]).ToVector256(); + m03 = InsertVector128(m03, LoadVector128(&vectors[12]), 1); // load higher halves + m14 = InsertVector128(m14, LoadVector128(&vectors[16]), 1); + m25 = InsertVector128(m25, LoadVector128(&vectors[20]), 1); + + var xy = Shuffle(m14, m25, 2 << 6 | 1 << 4 | 3 << 2 | 2); + var yz = Shuffle(m03, m14, 1 << 6 | 0 << 4 | 2 << 2 | 1); + var _Xs = Shuffle(m03, xy, 2 << 6 | 0 << 4 | 3 << 2 | 0); + var _ys = Shuffle(yz, xy, 3 << 6 | 1 << 4 | 2 << 2 | 0); + var _Zs = Shuffle(yz, m25, 3 << 6 | 0 << 4 | 3 << 2 | 1); + + Xs = _Xs; + Ys = _ys; + Zs = _Zs; + } + + // Convert SoA VectorPacket256 to AoS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Transpose() + { + var rxy = Shuffle(Xs, Ys, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var ryz = Shuffle(Ys, Zs, 3 << 6 | 1 << 4 | 3 << 2 | 1); + var rzx = Shuffle(Zs, Xs, 3 << 6 | 1 << 4 | 2 << 2 | 0); + + var r03 = Shuffle(rxy, rzx, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var r14 = Shuffle(ryz, rxy, 3 << 6 | 1 << 4 | 2 << 2 | 0); + var r25 = Shuffle(rzx, ryz, 3 << 6 | 1 << 4 | 3 << 2 | 1); + + var m0 = r03.GetLower(); + var m1 = r14.GetLower(); + var m2 = r25.GetLower(); + var m3 = ExtractVector128(r03, 1); + var m4 = ExtractVector128(r14, 1); + var m5 = ExtractVector128(r25, 1); + + var _Xs = Vector256.Create(m0, m1); + var _ys = Vector256.Create(m2, m3); + var _Zs = Vector256.Create(m4, m5); + + return new VectorPacket256(_Xs, _ys, _Zs); + } + + // Convert SoA VectorPacket256 to an incomplete AoS + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 FastTranspose() + { + var rxy = Shuffle(Xs, Ys, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var ryz = Shuffle(Ys, Zs, 3 << 6 | 1 << 4 | 3 << 2 | 1); + var rzx = Shuffle(Zs, Xs, 3 << 6 | 1 << 4 | 2 << 2 | 0); + + var r03 = Shuffle(rxy, rzx, 2 << 6 | 0 << 4 | 2 << 2 | 0); + var r14 = Shuffle(ryz, rxy, 3 << 6 | 1 << 4 | 2 << 2 | 0); + var r25 = Shuffle(rzx, ryz, 3 << 6 | 1 << 4 | 3 << 2 | 1); + + return new VectorPacket256(r03, r14, r25); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator +(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Add(left.Xs, right.Xs), Add(left.Ys, right.Ys), Add(left.Zs, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator -(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Subtract(left.Xs, right.Xs), Subtract(left.Ys, right.Ys), + Subtract(left.Zs, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator /(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Divide(left.Xs, right.Xs), Divide(left.Ys, right.Ys), Divide(left.Zs, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 DotProduct(VectorPacket256 left, VectorPacket256 right) + { + var x2 = Multiply(left.Xs, right.Xs); + var y2 = Multiply(left.Ys, right.Ys); + var z2 = Multiply(left.Zs, right.Zs); + return Add(Add(x2, y2), z2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 CrossProduct(VectorPacket256 left, VectorPacket256 right) + { + return new VectorPacket256(Subtract(Multiply(left.Ys, right.Zs), Multiply(left.Zs, right.Ys)), + Subtract(Multiply(left.Zs, right.Xs), Multiply(left.Xs, right.Zs)), + Subtract(Multiply(left.Xs, right.Ys), Multiply(left.Ys, right.Xs))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VectorPacket256 operator *(Vector256 left, VectorPacket256 right) + { + return new VectorPacket256(Multiply(left, right.Xs), Multiply(left, right.Ys), Multiply(left, right.Zs)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public VectorPacket256 Normalize() + { + var length = this.Lengths; + return new VectorPacket256(Divide(Xs, length), Divide(Ys, length), Divide(Zs, length)); + } + } +} \ No newline at end of file