Skip to content

Commit

Permalink
Merge pull request #878 from Sergio0694/dev/cross-plat-conversions
Browse files Browse the repository at this point in the history
Optimize pixel conversion methods with cross-plat intrinsics
  • Loading branch information
Sergio0694 authored Nov 25, 2024
2 parents 9aa98b3 + 142b6cc commit 132cfab
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 59 deletions.
31 changes: 10 additions & 21 deletions src/ComputeSharp/Imaging/Bgra32.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

namespace ComputeSharp;

Expand Down Expand Up @@ -86,25 +84,16 @@ public uint PackedValue
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Float4 ToPixel()
{
if (Sse2.IsSupported)
{
int pack = Unsafe.As<Bgra32, int>(ref Unsafe.AsRef(in this));
Vector128<byte> vZero = Vector128<byte>.Zero;
Vector128<byte> vByte = Vector128.CreateScalarUnsafe(pack).AsByte();
Vector128<ushort> vUShort = Sse2.UnpackLow(vByte, vZero).AsUInt16();
Vector128<int> vInt = Sse2.UnpackLow(vUShort, vZero.AsUInt16()).AsInt32();
Vector128<int> vShuffle = Sse2.Shuffle(vInt, 0b11_00_01_10);
Vector128<float> vFloat = Sse2.ConvertToVector128Single(vShuffle);
Vector128<float> vMax = Vector128.Create((float)byte.MaxValue);
Vector128<float> vNorm = Sse.Divide(vFloat, vMax);

return vNorm.AsVector4();
}

Vector4 linear = new(this.R, this.G, this.B, this.A);
Vector4 normalized = linear / byte.MaxValue;

return normalized;
int pack = Unsafe.As<Bgra32, int>(ref Unsafe.AsRef(in this));
Vector128<byte> vByte = Vector128.CreateScalarUnsafe(pack).AsByte();
Vector128<ushort> vUShort = Vector128.WidenLower(vByte);
Vector128<int> vInt = Vector128.WidenLower(vUShort).AsInt32();
Vector128<int> vShuffle = Vector128.Shuffle(vInt, Vector128.Create(2, 1, 0, 3));
Vector128<float> vFloat = Vector128.ConvertToSingle(vShuffle);
Vector128<float> vMax = Vector128.Create((float)byte.MaxValue);
Vector128<float> vNorm = Vector128.Divide(vFloat, vMax);

return vNorm.AsVector4();
}

/// <summary>
Expand Down
29 changes: 9 additions & 20 deletions src/ComputeSharp/Imaging/Rgba32.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

namespace ComputeSharp;

Expand Down Expand Up @@ -85,24 +83,15 @@ public uint PackedValue
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Float4 ToPixel()
{
if (Sse2.IsSupported)
{
int pack = Unsafe.As<Rgba32, int>(ref Unsafe.AsRef(in this));
Vector128<byte> vZero = Vector128<byte>.Zero;
Vector128<byte> vByte = Vector128.CreateScalarUnsafe(pack).AsByte();
Vector128<ushort> vUShort = Sse2.UnpackLow(vByte, vZero).AsUInt16();
Vector128<int> vInt = Sse2.UnpackLow(vUShort, vZero.AsUInt16()).AsInt32();
Vector128<float> vFloat = Sse2.ConvertToVector128Single(vInt);
Vector128<float> vMax = Vector128.Create((float)byte.MaxValue);
Vector128<float> vNorm = Sse.Divide(vFloat, vMax);

return vNorm.AsVector4();
}

Vector4 linear = new(this.R, this.G, this.B, this.A);
Vector4 normalized = linear / byte.MaxValue;

return normalized;
int pack = Unsafe.As<Rgba32, int>(ref Unsafe.AsRef(in this));
Vector128<byte> vByte = Vector128.CreateScalarUnsafe(pack).AsByte();
Vector128<ushort> vUShort = Vector128.WidenLower(vByte);
Vector128<int> vInt = Vector128.WidenLower(vUShort).AsInt32();
Vector128<float> vFloat = Vector128.ConvertToSingle(vInt);
Vector128<float> vMax = Vector128.Create((float)byte.MaxValue);
Vector128<float> vNorm = Vector128.Divide(vFloat, vMax);

return vNorm.AsVector4();
}

/// <summary>
Expand Down
26 changes: 8 additions & 18 deletions src/ComputeSharp/Imaging/Rgba64.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

namespace ComputeSharp;

Expand Down Expand Up @@ -85,22 +83,14 @@ public ulong PackedValue
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Float4 ToPixel()
{
if (Sse2.IsSupported)
{
long pack = Unsafe.As<Rgba64, long>(ref Unsafe.AsRef(in this));
Vector128<ushort> vUShort = Vector128.CreateScalarUnsafe(pack).AsUInt16();
Vector128<int> vInt = Sse2.UnpackLow(vUShort, Vector128<ushort>.Zero).AsInt32();
Vector128<float> vFloat = Sse2.ConvertToVector128Single(vInt);
Vector128<float> vMax = Vector128.Create((float)ushort.MaxValue);
Vector128<float> vNorm = Sse.Divide(vFloat, vMax);

return vNorm.AsVector4();
}

Vector4 linear = new(this.R, this.G, this.B, this.A);
Vector4 normalized = linear / ushort.MaxValue;

return normalized;
long pack = Unsafe.As<Rgba64, long>(ref Unsafe.AsRef(in this));
Vector128<ushort> vUShort = Vector128.CreateScalarUnsafe(pack).AsUInt16();
Vector128<int> vInt = Vector128.WidenLower(vUShort).AsInt32();
Vector128<float> vFloat = Vector128.ConvertToSingle(vInt);
Vector128<float> vMax = Vector128.Create((float)ushort.MaxValue);
Vector128<float> vNorm = Vector128.Divide(vFloat, vMax);

return vNorm.AsVector4();
}

/// <summary>
Expand Down
88 changes: 88 additions & 0 deletions tests/ComputeSharp.Tests/PixelTypeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ComputeSharp.Tests;

[TestClass]
public partial class PixelTypeTests
{
[TestMethod]
public unsafe void Bgra32_ToPixel()
{
Bgra32 color = new(0x20, 0x4A, 0x7C, 0xE4);

float4 pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel.R, delta: 0.0001f);
Assert.AreEqual(0.2901961f, pixel.G, delta: 0.0001f);
Assert.AreEqual(0.4862745f, pixel.B, delta: 0.0001f);
Assert.AreEqual(0.89411765f, pixel.A, delta: 0.0001f);
}

[TestMethod]
public unsafe void R16_ToPixel()
{
R16 color = new(0x2020);

float pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel, delta: 0.0001f);
}

[TestMethod]
public unsafe void R8_ToPixel()
{
R8 color = new(0x20);

float pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel, delta: 0.0001f);
}

[TestMethod]
public unsafe void Rg16_ToPixel()
{
Rg16 color = new(0x20, 0x4A);

float2 pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel.R, delta: 0.0001f);
Assert.AreEqual(0.2901961f, pixel.G, delta: 0.0001f);
}

[TestMethod]
public unsafe void Rg32_ToPixel()
{
Rg32 color = new(0x2020, 0x4A4A);

float2 pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel.R, delta: 0.0001f);
Assert.AreEqual(0.2901961f, pixel.G, delta: 0.0001f);
}

[TestMethod]
public unsafe void Rgba32_ToPixel()
{
Rgba32 color = new(0x20, 0x4A, 0x7C, 0xE4);

float4 pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel.R, delta: 0.0001f);
Assert.AreEqual(0.2901961f, pixel.G, delta: 0.0001f);
Assert.AreEqual(0.4862745f, pixel.B, delta: 0.0001f);
Assert.AreEqual(0.89411765f, pixel.A, delta: 0.0001f);
}

[TestMethod]
public unsafe void Rgba64_ToPixel()
{
Rgba64 color = new(0x2020, 0x4A4A, 0x7C7C, 0xE4E4);

float4 pixel = color.ToPixel();

Assert.AreEqual(0.1254902f, pixel.R, delta: 0.0001f);
Assert.AreEqual(0.2901961f, pixel.G, delta: 0.0001f);
Assert.AreEqual(0.4862745f, pixel.B, delta: 0.0001f);
Assert.AreEqual(0.89411765f, pixel.A, delta: 0.0001f);
}
}

0 comments on commit 132cfab

Please sign in to comment.