Browse Source

Merge branch 'master' into webp

pull/1552/head
James Jackson-South 6 years ago
committed by GitHub
parent
commit
bd037248c9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      .runsettings
  2. 1
      Directory.Build.props
  3. 10
      Directory.Build.targets
  4. 1
      ImageSharp.sln
  5. 2
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  6. 80
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
  7. 32
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt
  8. 211
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  9. 25
      src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs
  10. 15
      tests/Directory.Build.targets
  11. 14
      tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs
  12. 81
      tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs
  13. 2
      tests/ImageSharp.Benchmarks/Config.cs
  14. 36
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  15. 22
      tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
  16. 11
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  17. 48
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  18. 34
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs
  19. 4
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  20. 7
      tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
  21. 33
      tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
  22. 7
      tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
  23. 6
      tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs
  24. 1
      tests/ImageSharp.Tests/ImageSharp.Tests.csproj
  25. 59
      tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs
  26. 266
      tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs
  27. 33
      tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
  28. 54
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Features.cs
  29. 5
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
  30. 296
      tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs
  31. 4
      tests/coverlet.runsettings

7
.runsettings

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<RunConfiguration>
<!--Used in conjunction with ActiveIssueAttribute to skip tests with known issues-->
<TestCaseFilter>category!=failing</TestCaseFilter>
</RunConfiguration>
</RunSettings>

1
Directory.Build.props

@ -15,6 +15,7 @@
<BaseArtifactsPath>$(MSBuildThisFileDirectory)artifacts/</BaseArtifactsPath>
<BaseArtifactsPathSuffix>$(SixLaborsProjectCategory)/$(MSBuildProjectName)</BaseArtifactsPathSuffix>
<RepositoryUrl Condition="'$(RepositoryUrl)' == ''">https://github.com/SixLabors/ImageSharp/</RepositoryUrl>
<RunSettingsFilePath>$(MSBuildThisFileDirectory)/.runsettings</RunSettingsFilePath>
</PropertyGroup>
<!-- Default settings that explicitly differ from the Sdk.props defaults -->

10
Directory.Build.targets

@ -18,22 +18,18 @@
<!-- Package versions for package references across all projects -->
<ItemGroup>
<!--Global Dependencies-->
<PackageReference Update="Microsoft.Net.Compilers.Toolset" PrivateAssets="All" Version="3.3.1" />
<PackageReference Update="Microsoft.Net.Compilers.Toolset" PrivateAssets="All" Version="3.7.0" />
<PackageReference Update="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" Version="1.0.0" />
<PackageReference Update="StyleCop.Analyzers" PrivateAssets="All" Version="1.1.118" />
<!--Src Dependencies-->
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Update="MinVer" PrivateAssets="All" Version="2.3.0" />
<PackageReference Update="MinVer" PrivateAssets="All" Version="2.3.1" />
<PackageReference Update="System.Buffers" Version="4.5.1" />
<PackageReference Update="System.IO.Compression" Version="4.3.0" />
<PackageReference Update="System.IO.UnmanagedMemoryStream" Version="4.3.0" />
<PackageReference Update="System.Numerics.Vectors" Version="4.5.0" />
<!--
Do no update System.Memory as it currently breaks the CI build
with FileNotFoundException for SixLabors.ImageSharp.Tests.dll.config
-->
<PackageReference Update="System.Memory" Version="4.5.3" />
<PackageReference Update="System.Memory" Version="4.5.4" />
<PackageReference Update="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
<PackageReference Update="System.Threading.Tasks.Parallel" Version="4.3.0" />
<PackageReference Update="System.ValueTuple" Version="4.5.0" />

1
ImageSharp.sln

@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.gitattributes = .gitattributes
.gitignore = .gitignore
.gitmodules = .gitmodules
.runsettings = .runsettings
ci-build.ps1 = ci-build.ps1
ci-pack.ps1 = ci-pack.ps1
ci-test.ps1 = ci-test.ps1

2
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -171,7 +171,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var fileHeader = new BmpFileHeader(
type: BmpConstants.TypeMarkers.Bitmap,
fileSize: BmpFileHeader.Size + infoHeaderSize + infoHeader.ImageSize,
fileSize: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize + infoHeader.ImageSize,
reserved: 0,
offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize);

80
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs

@ -10,86 +10,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
/// <summary>
/// Fallback method to transpose a block into the destination block on non AVX supported CPUs.
/// </summary>
/// <param name="d">The destination block</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeIntoFallback(ref Block8x8F d)
{
d.V0L.X = V0L.X;
d.V1L.X = V0L.Y;
d.V2L.X = V0L.Z;
d.V3L.X = V0L.W;
d.V4L.X = V0R.X;
d.V5L.X = V0R.Y;
d.V6L.X = V0R.Z;
d.V7L.X = V0R.W;
d.V0L.Y = V1L.X;
d.V1L.Y = V1L.Y;
d.V2L.Y = V1L.Z;
d.V3L.Y = V1L.W;
d.V4L.Y = V1R.X;
d.V5L.Y = V1R.Y;
d.V6L.Y = V1R.Z;
d.V7L.Y = V1R.W;
d.V0L.Z = V2L.X;
d.V1L.Z = V2L.Y;
d.V2L.Z = V2L.Z;
d.V3L.Z = V2L.W;
d.V4L.Z = V2R.X;
d.V5L.Z = V2R.Y;
d.V6L.Z = V2R.Z;
d.V7L.Z = V2R.W;
d.V0L.W = V3L.X;
d.V1L.W = V3L.Y;
d.V2L.W = V3L.Z;
d.V3L.W = V3L.W;
d.V4L.W = V3R.X;
d.V5L.W = V3R.Y;
d.V6L.W = V3R.Z;
d.V7L.W = V3R.W;
d.V0R.X = V4L.X;
d.V1R.X = V4L.Y;
d.V2R.X = V4L.Z;
d.V3R.X = V4L.W;
d.V4R.X = V4R.X;
d.V5R.X = V4R.Y;
d.V6R.X = V4R.Z;
d.V7R.X = V4R.W;
d.V0R.Y = V5L.X;
d.V1R.Y = V5L.Y;
d.V2R.Y = V5L.Z;
d.V3R.Y = V5L.W;
d.V4R.Y = V5R.X;
d.V5R.Y = V5R.Y;
d.V6R.Y = V5R.Z;
d.V7R.Y = V5R.W;
d.V0R.Z = V6L.X;
d.V1R.Z = V6L.Y;
d.V2R.Z = V6L.Z;
d.V3R.Z = V6L.W;
d.V4R.Z = V6R.X;
d.V5R.Z = V6R.Y;
d.V6R.Z = V6R.Z;
d.V7R.Z = V6R.W;
d.V0R.W = V7L.X;
d.V1R.W = V7L.Y;
d.V2R.W = V7L.Z;
d.V3R.W = V7L.W;
d.V4R.W = V7R.X;
d.V5R.W = V7R.Y;
d.V6R.W = V7R.Z;
d.V7R.W = V7R.W;
}
/// <summary>
/// Level shift by +maximum/2, clip to [0, maximum]
/// </summary>

32
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt

@ -23,38 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal partial struct Block8x8F
{
/// <summary>
/// Fallback method to transpose a block into the destination block on non AVX supported CPUs.
/// </summary>
/// <param name="d">The destination block</param>
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeIntoFallback(ref Block8x8F d)
{
<#
PushIndent(" ");
for (int i = 0; i < 8; i++)
{
char destCoord = coordz[i % 4];
char destSide = (i / 4) % 2 == 0 ? 'L' : 'R';
for (int j = 0; j < 8; j++)
{
if(i > 0 && j == 0){
WriteLine("");
}
char srcCoord = coordz[j % 4];
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R';
var expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n";
Write(expression);
}
}
PopIndent();
#>
}
/// <summary>
/// Level shift by +maximum/2, clip to [0, maximum]
/// </summary>

211
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -611,87 +611,146 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
this.TransposeIntoAvx(ref d);
// https://stackoverflow.com/questions/25622745/transpose-an-8x8-float-using-avx-avx2/25627536#25627536
Vector256<float> r0 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V0L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V4L),
1);
Vector256<float> r1 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V1L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V5L),
1);
Vector256<float> r2 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V2L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V6L),
1);
Vector256<float> r3 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V3L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V7L),
1);
Vector256<float> r4 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V0R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V4R),
1);
Vector256<float> r5 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V1R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V5R),
1);
Vector256<float> r6 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V2R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V6R),
1);
Vector256<float> r7 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V3R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V7R),
1);
Vector256<float> t0 = Avx.UnpackLow(r0, r1);
Vector256<float> t2 = Avx.UnpackLow(r2, r3);
Vector256<float> v = Avx.Shuffle(t0, t2, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V0L) = Avx.Blend(t0, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V1L) = Avx.Blend(t2, v, 0x33);
Vector256<float> t4 = Avx.UnpackLow(r4, r5);
Vector256<float> t6 = Avx.UnpackLow(r6, r7);
v = Avx.Shuffle(t4, t6, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V4L) = Avx.Blend(t4, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V5L) = Avx.Blend(t6, v, 0x33);
Vector256<float> t1 = Avx.UnpackHigh(r0, r1);
Vector256<float> t3 = Avx.UnpackHigh(r2, r3);
v = Avx.Shuffle(t1, t3, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V2L) = Avx.Blend(t1, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V3L) = Avx.Blend(t3, v, 0x33);
Vector256<float> t5 = Avx.UnpackHigh(r4, r5);
Vector256<float> t7 = Avx.UnpackHigh(r6, r7);
v = Avx.Shuffle(t5, t7, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V6L) = Avx.Blend(t5, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V7L) = Avx.Blend(t7, v, 0x33);
}
else
#endif
{
this.TransposeIntoFallback(ref d);
d.V0L.X = this.V0L.X;
d.V1L.X = this.V0L.Y;
d.V2L.X = this.V0L.Z;
d.V3L.X = this.V0L.W;
d.V4L.X = this.V0R.X;
d.V5L.X = this.V0R.Y;
d.V6L.X = this.V0R.Z;
d.V7L.X = this.V0R.W;
d.V0L.Y = this.V1L.X;
d.V1L.Y = this.V1L.Y;
d.V2L.Y = this.V1L.Z;
d.V3L.Y = this.V1L.W;
d.V4L.Y = this.V1R.X;
d.V5L.Y = this.V1R.Y;
d.V6L.Y = this.V1R.Z;
d.V7L.Y = this.V1R.W;
d.V0L.Z = this.V2L.X;
d.V1L.Z = this.V2L.Y;
d.V2L.Z = this.V2L.Z;
d.V3L.Z = this.V2L.W;
d.V4L.Z = this.V2R.X;
d.V5L.Z = this.V2R.Y;
d.V6L.Z = this.V2R.Z;
d.V7L.Z = this.V2R.W;
d.V0L.W = this.V3L.X;
d.V1L.W = this.V3L.Y;
d.V2L.W = this.V3L.Z;
d.V3L.W = this.V3L.W;
d.V4L.W = this.V3R.X;
d.V5L.W = this.V3R.Y;
d.V6L.W = this.V3R.Z;
d.V7L.W = this.V3R.W;
d.V0R.X = this.V4L.X;
d.V1R.X = this.V4L.Y;
d.V2R.X = this.V4L.Z;
d.V3R.X = this.V4L.W;
d.V4R.X = this.V4R.X;
d.V5R.X = this.V4R.Y;
d.V6R.X = this.V4R.Z;
d.V7R.X = this.V4R.W;
d.V0R.Y = this.V5L.X;
d.V1R.Y = this.V5L.Y;
d.V2R.Y = this.V5L.Z;
d.V3R.Y = this.V5L.W;
d.V4R.Y = this.V5R.X;
d.V5R.Y = this.V5R.Y;
d.V6R.Y = this.V5R.Z;
d.V7R.Y = this.V5R.W;
d.V0R.Z = this.V6L.X;
d.V1R.Z = this.V6L.Y;
d.V2R.Z = this.V6L.Z;
d.V3R.Z = this.V6L.W;
d.V4R.Z = this.V6R.X;
d.V5R.Z = this.V6R.Y;
d.V6R.Z = this.V6R.Z;
d.V7R.Z = this.V6R.W;
d.V0R.W = this.V7L.X;
d.V1R.W = this.V7L.Y;
d.V2R.W = this.V7L.Z;
d.V3R.W = this.V7L.W;
d.V4R.W = this.V7R.X;
d.V5R.W = this.V7R.Y;
d.V6R.W = this.V7R.Z;
d.V7R.W = this.V7R.W;
}
}
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary>
/// AVX-only variant for executing <see cref="TransposeInto(ref Block8x8F)"/>.
/// <see href="https://stackoverflow.com/questions/25622745/transpose-an-8x8-float-using-avx-avx2/25627536#25627536"/>
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
public void TransposeIntoAvx(ref Block8x8F d)
{
Vector256<float> r0 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V0L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V4L),
1);
Vector256<float> r1 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V1L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V5L),
1);
Vector256<float> r2 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V2L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V6L),
1);
Vector256<float> r3 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V3L).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V7L),
1);
Vector256<float> r4 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V0R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V4R),
1);
Vector256<float> r5 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V1R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V5R),
1);
Vector256<float> r6 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V2R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V6R),
1);
Vector256<float> r7 = Avx.InsertVector128(
Unsafe.As<Vector4, Vector128<float>>(ref this.V3R).ToVector256(),
Unsafe.As<Vector4, Vector128<float>>(ref this.V7R),
1);
Vector256<float> t0 = Avx.UnpackLow(r0, r1);
Vector256<float> t2 = Avx.UnpackLow(r2, r3);
Vector256<float> v = Avx.Shuffle(t0, t2, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V0L) = Avx.Blend(t0, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V1L) = Avx.Blend(t2, v, 0x33);
Vector256<float> t4 = Avx.UnpackLow(r4, r5);
Vector256<float> t6 = Avx.UnpackLow(r6, r7);
v = Avx.Shuffle(t4, t6, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V4L) = Avx.Blend(t4, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V5L) = Avx.Blend(t6, v, 0x33);
Vector256<float> t1 = Avx.UnpackHigh(r0, r1);
Vector256<float> t3 = Avx.UnpackHigh(r2, r3);
v = Avx.Shuffle(t1, t3, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V2L) = Avx.Blend(t1, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V3L) = Avx.Blend(t3, v, 0x33);
Vector256<float> t5 = Avx.UnpackHigh(r4, r5);
Vector256<float> t7 = Avx.UnpackHigh(r6, r7);
v = Avx.Shuffle(t5, t7, 0x4E);
Unsafe.As<Vector4, Vector256<float>>(ref d.V6L) = Avx.Blend(t5, v, 0xCC);
Unsafe.As<Vector4, Vector256<float>>(ref d.V7L) = Avx.Blend(t7, v, 0x33);
}
#endif
}
}

25
src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs

@ -106,15 +106,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
/// <inheritdoc/>
#if NETSTANDARD2_0
// https://github.com/SixLabors/ImageSharp/issues/1204
[MethodImpl(MethodImplOptions.NoOptimization)]
#else
[MethodImpl(InliningOptions.ShortMethod)]
#endif
public void Invoke(int y)
{
ref int histogramBase = ref MemoryMarshal.GetReference(this.histogramBuffer.GetSpan());
ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
int levels = this.luminanceLevels;
for (int x = 0; x < this.bounds.Width; x++)
{
int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.luminanceLevels);
// TODO: We should bulk convert here.
var vector = Unsafe.Add(ref pixelBase, x).ToVector4();
int luminance = ImageMaths.GetBT709Luminance(ref vector, levels);
Unsafe.Add(ref histogramBase, luminance)++;
}
}
@ -147,18 +155,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization
}
/// <inheritdoc/>
#if NETSTANDARD2_0
// https://github.com/SixLabors/ImageSharp/issues/1204
[MethodImpl(MethodImplOptions.NoOptimization)]
#else
[MethodImpl(InliningOptions.ShortMethod)]
#endif
public void Invoke(int y)
{
ref int cdfBase = ref MemoryMarshal.GetReference(this.cdfBuffer.GetSpan());
ref TPixel pixelBase = ref MemoryMarshal.GetReference(this.source.GetPixelRowSpan(y));
int levels = this.luminanceLevels;
float noOfPixelsMinusCdfMin = this.numberOfPixelsMinusCdfMin;
for (int x = 0; x < this.bounds.Width; x++)
{
// TODO: We should bulk convert here.
ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x);
int luminance = GetLuminance(pixel, this.luminanceLevels);
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / this.numberOfPixelsMinusCdfMin;
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W));
var vector = pixel.ToVector4();
int luminance = ImageMaths.GetBT709Luminance(ref vector, levels);
float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / noOfPixelsMinusCdfMin;
pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, vector.W));
}
}
}

15
tests/Directory.Build.targets

@ -26,18 +26,19 @@
<ItemGroup>
<!--Test Dependencies-->
<PackageReference Update="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Update="BenchmarkDotNet.Diagnostics.Windows" Version="0.12.1" Condition="'$(OS)' == 'Windows_NT'" />
<PackageReference Update="BenchmarkDotNet.Diagnostics.Windows" Version="0.12.1" Condition="'$(OS)' == 'Windows_NT'" />
<PackageReference Update="Colourful" Version="2.0.5" />
<PackageReference Update="coverlet.collector" Version="1.3.1-preview.27.gdd2237a3be" PrivateAssets="All"/>
<PackageReference Update="Magick.NET-Q16-AnyCPU" Version="7.15.5" />
<PackageReference Update="Microsoft.DotNet.RemoteExecutor" Version="5.0.0-beta.20069.1" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Update="Moq" Version="4.14.5" />
<PackageReference Update="Magick.NET-Q16-AnyCPU" Version="7.22.0" />
<PackageReference Update="Microsoft.DotNet.RemoteExecutor" Version="6.0.0-beta.20513.1" />
<PackageReference Update="Microsoft.DotNet.XUnitExtensions" Version="6.0.0-beta.20513.1" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Update="Moq" Version="4.14.6" />
<PackageReference Update="Pfim" Version="0.9.1" />
<PackageReference Update="SharpZipLib" Version="1.2.0" />
<PackageReference Update="SharpZipLib" Version="1.3.0" />
<PackageReference Update="System.Drawing.Common" Version="4.7.0" />
<PackageReference Update="xunit" Version="2.4.1" />
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3" />
</ItemGroup>
</Project>

14
tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Transpose.cs

@ -6,25 +6,17 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components;
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations
{
[Config(typeof(Config.HwIntrinsics_SSE_AVX))]
public class Block8x8F_Transpose
{
private static readonly Block8x8F Source = Create8x8FloatData();
[Benchmark(Baseline=true)]
public void TransposeIntoVector4()
{
var dest = default(Block8x8F);
Source.TransposeIntoFallback(ref dest);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Benchmark]
public void TransposeIntoAvx()
public void TransposeInto()
{
var dest = default(Block8x8F);
Source.TransposeIntoAvx(ref dest);
Source.TransposeInto(ref dest);
}
#endif
private static Block8x8F Create8x8FloatData()
{

81
tests/ImageSharp.Benchmarks/Config.HwIntrinsics.cs

@ -0,0 +1,81 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
namespace SixLabors.ImageSharp.Benchmarks
{
public partial class Config
{
private const string On = "1";
private const string Off = "0";
// See https://github.com/SixLabors/ImageSharp/pull/1229#discussion_r440477861
// * EnableHWIntrinsic
// * EnableSSE
// * EnableSSE2
// * EnableAES
// * EnablePCLMULQDQ
// * EnableSSE3
// * EnableSSSE3
// * EnableSSE41
// * EnableSSE42
// * EnablePOPCNT
// * EnableAVX
// * EnableFMA
// * EnableAVX2
// * EnableBMI1
// * EnableBMI2
// * EnableLZCNT
//
// `FeatureSIMD` ends up impacting all SIMD support(including `System.Numerics`) but not things
// like `LZCNT`, `BMI1`, or `BMI2`
// `EnableSSE3_4` is a legacy switch that exists for compat and is basically the same as `EnableSSE3`
private const string EnableAES = "COMPlus_EnableAES";
private const string EnableAVX = "COMPlus_EnableAVX";
private const string EnableAVX2 = "COMPlus_EnableAVX2";
private const string EnableBMI1 = "COMPlus_EnableBMI1";
private const string EnableBMI2 = "COMPlus_EnableBMI2";
private const string EnableFMA = "COMPlus_EnableFMA";
private const string EnableHWIntrinsic = "COMPlus_EnableHWIntrinsic";
private const string EnableLZCNT = "COMPlus_EnableLZCNT";
private const string EnablePCLMULQDQ = "COMPlus_EnablePCLMULQDQ";
private const string EnablePOPCNT = "COMPlus_EnablePOPCNT";
private const string EnableSSE = "COMPlus_EnableSSE";
private const string EnableSSE2 = "COMPlus_EnableSSE2";
private const string EnableSSE3 = "COMPlus_EnableSSE3";
private const string EnableSSE3_4 = "COMPlus_EnableSSE3_4";
private const string EnableSSE41 = "COMPlus_EnableSSE41";
private const string EnableSSE42 = "COMPlus_EnableSSE42";
private const string EnableSSSE3 = "COMPlus_EnableSSSE3";
private const string FeatureSIMD = "COMPlus_FeatureSIMD";
public class HwIntrinsics_SSE_AVX : Config
{
public HwIntrinsics_SSE_AVX()
{
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx.IsSupported)
{
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)
.WithId("AVX").AsBaseline());
}
if (Sse.IsSupported)
{
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)
.WithEnvironmentVariables(new EnvironmentVariable(EnableAVX, Off))
.WithId("SSE"));
}
#endif
this.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)
.WithEnvironmentVariables(new EnvironmentVariable(EnableHWIntrinsic, Off))
.WithId("No HwIntrinsics"));
}
}
}
}

2
tests/ImageSharp.Benchmarks/Config.cs

@ -12,7 +12,7 @@ using BenchmarkDotNet.Jobs;
namespace SixLabors.ImageSharp.Benchmarks
{
public class Config : ManualConfig
public partial class Config : ManualConfig
{
public Config()
{

36
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -39,22 +39,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
};
[Theory]
[WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32, false)]
[WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32, true)]
public void BmpDecoder_CanDecode_MiscellaneousBitmaps<TPixel>(TestImageProvider<TPixel> provider, bool enforceDiscontiguousBuffers)
[WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32)]
public void BmpDecoder_CanDecode_MiscellaneousBitmaps<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(BmpDecoder);
image.DebugSave(provider);
if (TestEnvironment.IsWindows)
{
image.CompareToOriginal(provider);
}
}
[Theory]
[WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32)]
public void BmpDecoder_CanDecode_MiscellaneousBitmaps_WithLimitedAllocatorBufferCapacity(
TestImageProvider<Rgba32> provider)
{
static void RunTest(string providerDump, string nonContiguousBuffersStr)
{
TestImageProvider<TPixel> provider = BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
TestImageProvider<Rgba32> provider = BasicSerializer.Deserialize<TestImageProvider<Rgba32>>(providerDump);
if (!string.IsNullOrEmpty(nonContiguousBuffersStr))
{
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
}
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<TPixel> image = provider.GetImage(BmpDecoder);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
using Image<Rgba32> image = provider.GetImage(BmpDecoder);
image.DebugSave(provider, nonContiguousBuffersStr);
if (TestEnvironment.IsWindows)
{
@ -66,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
RemoteExecutor.Invoke(
RunTest,
providerDump,
enforceDiscontiguousBuffers ? "Disco" : string.Empty)
"Disco")
.Dispose();
}
@ -348,7 +358,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
using (Image<TPixel> image = provider.GetImage(BmpDecoder))
{
image.DebugSave(provider);
image.CompareToOriginal(provider);
// Do not validate. Reference files will fail validation.
image.CompareToOriginal(provider, new MagickReferenceDecoder(false));
}
}

22
tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs

@ -10,7 +10,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit;
using Xunit.Abstractions;
@ -200,10 +200,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
Quantizer = new WuQuantizer()
};
string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false);
// Use the default decoder to test our encoded image. This verifies the content.
// We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using (var referenceImage = Image.Load<TPixel>(actualOutputFile, referenceDecoder))
{
referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false);
referenceImage.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.01f),
provider,
extension: "bmp",
appendPixelTypeToFileName: false,
decoder: new MagickReferenceDecoder(false));
}
}
}
@ -226,10 +234,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
Quantizer = new OctreeQuantizer()
};
string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, appendPixelTypeToFileName: false);
// Use the default decoder to test our encoded image. This verifies the content.
// We do not verify the reference image though as some are invalid.
IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile);
using (var referenceImage = Image.Load<TPixel>(actualOutputFile, referenceDecoder))
{
referenceImage.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.01f), provider, extension: "bmp", appendPixelTypeToFileName: false);
referenceImage.CompareToReferenceOutput(
ImageComparer.TolerantPercentage(0.01f),
provider,
extension: "bmp",
appendPixelTypeToFileName: false,
decoder: new MagickReferenceDecoder(false));
}
}
}

11
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -198,17 +198,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif
[Theory]
[WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)]
[WithFile(TestImages.Gif.Kumin, PixelTypes.Rgba32)]
public void GifDecoder_CanDecode_WithLimitedAllocatorBufferCapacity<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
public void GifDecoder_CanDecode_WithLimitedAllocatorBufferCapacity(
TestImageProvider<Rgba32> provider)
{
static void RunTest(string providerDump, string nonContiguousBuffersStr)
{
TestImageProvider<TPixel> provider = BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
TestImageProvider<Rgba32> provider
= BasicSerializer.Deserialize<TestImageProvider<Rgba32>>(providerDump);
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<TPixel> image = provider.GetImage(GifDecoder);
image.DebugSave(provider);
using Image<Rgba32> image = provider.GetImage(GifDecoder);
image.DebugSave(provider, nonContiguousBuffersStr);
image.CompareToOriginal(provider);
}

48
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -5,10 +5,9 @@
// #define BENCHMARKING
using System;
using System.Diagnostics;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@ -163,42 +162,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
[Fact]
public void TransposeIntoFallback()
public void TransposeInto()
{
float[] expected = Create8x8FloatData();
ReferenceImplementations.Transpose8x8(expected);
var source = default(Block8x8F);
source.LoadFrom(Create8x8FloatData());
var dest = default(Block8x8F);
source.TransposeIntoFallback(ref dest);
float[] actual = new float[64];
dest.ScaledCopyTo(actual);
Assert.Equal(expected, actual);
}
static void RunTest()
{
float[] expected = Create8x8FloatData();
ReferenceImplementations.Transpose8x8(expected);
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void TransposeIntoAvx()
{
float[] expected = Create8x8FloatData();
ReferenceImplementations.Transpose8x8(expected);
var source = default(Block8x8F);
source.LoadFrom(Create8x8FloatData());
var source = default(Block8x8F);
source.LoadFrom(Create8x8FloatData());
var dest = default(Block8x8F);
source.TransposeInto(ref dest);
var dest = default(Block8x8F);
source.TransposeIntoAvx(ref dest);
float[] actual = new float[64];
dest.ScaledCopyTo(actual);
float[] actual = new float[64];
dest.ScaledCopyTo(actual);
Assert.Equal(expected, actual);
}
Assert.Equal(expected, actual);
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX);
}
#endif
private class BufferHolder
{

34
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Progressive.cs

@ -14,22 +14,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public const string DecodeProgressiveJpegOutputName = "DecodeProgressiveJpeg";
[Theory]
[WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32, false)]
[WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32, true)]
public void DecodeProgressiveJpeg<TPixel>(TestImageProvider<TPixel> provider, bool enforceDiscontiguousBuffers)
[WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32)]
public void DecodeProgressiveJpeg<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
image.DebugSave(provider);
provider.Utility.TestName = DecodeProgressiveJpegOutputName;
image.CompareToReferenceOutput(
GetImageComparer(provider),
provider,
appendPixelTypeToFileName: false);
}
[Theory]
[WithFile(TestImages.Jpeg.Progressive.Progress, PixelTypes.Rgba32)]
public void DecodeProgressiveJpeg_WithLimitedAllocatorBufferCapacity(TestImageProvider<Rgba32> provider)
{
static void RunTest(string providerDump, string nonContiguousBuffersStr)
{
TestImageProvider<TPixel> provider =
BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
TestImageProvider<Rgba32> provider =
BasicSerializer.Deserialize<TestImageProvider<Rgba32>>(providerDump);
if (!string.IsNullOrEmpty(nonContiguousBuffersStr))
{
provider.LimitAllocatorBufferCapacity().InBytesSqrt(200);
}
provider.LimitAllocatorBufferCapacity().InBytesSqrt(200);
using Image<TPixel> image = provider.GetImage(JpegDecoder);
using Image<Rgba32> image = provider.GetImage(JpegDecoder);
image.DebugSave(provider, nonContiguousBuffersStr);
provider.Utility.TestName = DecodeProgressiveJpegOutputName;
@ -44,8 +54,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
RemoteExecutor.Invoke(
RunTest,
providerDump,
enforceDiscontiguousBuffers ? "Disco" : string.Empty)
.Dispose();
"Disco")
.Dispose();
}
}
}

4
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -129,10 +129,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Theory]
[InlineData(TestImages.Jpeg.Baseline.Jpeg420Small, 0)]
[InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 1)]
[InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 10)]
[InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 15)]
[InlineData(TestImages.Jpeg.Issues.ExifGetString750Transform, 30)]
[InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 1)]
[InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 10)]
[InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 15)]
[InlineData(TestImages.Jpeg.Issues.BadRstProgressive518, 30)]
public async Task Decode_IsCancellable(string fileName, int cancellationDelayMs)
{

7
tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs

@ -404,16 +404,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Theory]
[WithFile(TestImages.Png.Splash, PixelTypes.Rgba32)]
[WithFile(TestImages.Png.Bike, PixelTypes.Rgba32)]
public void PngDecoder_CanDecode_WithLimitedAllocatorBufferCapacity<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
public void PngDecoder_CanDecode_WithLimitedAllocatorBufferCapacity(TestImageProvider<Rgba32> provider)
{
static void RunTest(string providerDump, string nonContiguousBuffersStr)
{
TestImageProvider<TPixel> provider = BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
TestImageProvider<Rgba32> provider = BasicSerializer.Deserialize<TestImageProvider<Rgba32>>(providerDump);
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<TPixel> image = provider.GetImage(PngDecoder);
using Image<Rgba32> image = provider.GetImage(PngDecoder);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
image.CompareToOriginal(provider);
}

33
tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs

@ -2,13 +2,8 @@
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
using System.Diagnostics;
using System.IO;
using System.Linq;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using Microsoft.DotNet.RemoteExecutor;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Metadata;
@ -16,7 +11,6 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Png
@ -536,16 +530,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void EncodeWorksWithoutSsse3Intrinsics<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
public void EncodeWorksWithoutSsse3Intrinsics(TestImageProvider<Rgba32> provider)
{
static void RunTest(string providerDump)
static void RunTest(string serialized)
{
TestImageProvider<TPixel> provider =
BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
#if SUPPORTS_RUNTIME_INTRINSICS
Assert.False(Ssse3.IsSupported);
#endif
TestImageProvider<Rgba32> provider =
FeatureTestRunner.Deserialize<TestImageProvider<Rgba32>>(serialized);
foreach (PngInterlaceMode interlaceMode in InterlaceMode)
{
@ -560,19 +550,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png
}
}
string providerDump = BasicSerializer.Serialize(provider);
var processStartInfo = new ProcessStartInfo();
processStartInfo.Environment[TestEnvironment.Features.EnableSSE3] = TestEnvironment.Features.Off;
RemoteExecutor.Invoke(
FeatureTestRunner.RunWithHwIntrinsicsFeature(
RunTest,
providerDump,
new RemoteInvokeOptions
{
StartInfo = processStartInfo
})
.Dispose();
HwIntrinsics.DisableSSSE3,
provider);
}
private static void TestPngEncoderCore<TPixel>(

7
tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs

@ -747,16 +747,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
[Theory]
[WithFile(Bit24BottomLeft, PixelTypes.Rgba32)]
[WithFile(Bit32BottomLeft, PixelTypes.Rgba32)]
public void TgaDecoder_CanDecode_WithLimitedAllocatorBufferCapacity<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
public void TgaDecoder_CanDecode_WithLimitedAllocatorBufferCapacity(TestImageProvider<Rgba32> provider)
{
static void RunTest(string providerDump, string nonContiguousBuffersStr)
{
TestImageProvider<TPixel> provider = BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
TestImageProvider<Rgba32> provider = BasicSerializer.Deserialize<TestImageProvider<Rgba32>>(providerDump);
provider.LimitAllocatorBufferCapacity().InPixelsSqrt(100);
using Image<TPixel> image = provider.GetImage(TgaDecoder);
using Image<Rgba32> image = provider.GetImage(TgaDecoder);
image.DebugSave(provider, testOutputDetails: nonContiguousBuffersStr);
if (TestEnvironment.IsWindows)

6
tests/ImageSharp.Tests/Formats/Tga/TgaTestUtils.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
Image<TPixel> image,
bool useExactComparer = true,
float compareTolerance = 0.01f)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
string path = TestImageProvider<TPixel>.GetFilePathOrNull(provider);
if (path == null)
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
}
public static Image<TPixel> DecodeWithMagick<TPixel>(Configuration configuration, FileInfo fileInfo)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
using (var magickImage = new MagickImage(fileInfo))
{
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga
Assert.True(result.TryGetSinglePixelSpan(out Span<TPixel> resultPixels));
using (IPixelCollection pixels = magickImage.GetPixelsUnsafe())
using (IUnsafePixelCollection<ushort> pixels = magickImage.GetPixelsUnsafe())
{
byte[] data = pixels.ToByteArray(PixelMapping.RGBA);

1
tests/ImageSharp.Tests/ImageSharp.Tests.csproj

@ -20,6 +20,7 @@
<ItemGroup>
<PackageReference Include="Magick.NET-Q16-AnyCPU" />
<PackageReference Include="Microsoft.DotNet.RemoteExecutor" />
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
<PackageReference Include="Moq" />
<PackageReference Include="SharpZipLib" />
<PackageReference Include="System.Drawing.Common" />

59
tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs

@ -138,21 +138,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
public void BokehBlurFilterProcessor<TPixel>(TestImageProvider<TPixel> provider, BokehBlurInfo value)
where TPixel : unmanaged, IPixel<TPixel>
{
static void RunTest(string providerDump, string infoDump)
{
TestImageProvider<TPixel> provider =
BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
BokehBlurInfo value = BasicSerializer.Deserialize<BokehBlurInfo>(infoDump);
provider.RunValidatingProcessorTest(
x => x.BokehBlur(value.Radius, value.Components, value.Gamma),
testOutputDetails: value.ToString(),
appendPixelTypeToFileName: false);
}
RemoteExecutor
.Invoke(RunTest, BasicSerializer.Serialize(provider), BasicSerializer.Serialize(value))
.Dispose();
provider.RunValidatingProcessorTest(
x => x.BokehBlur(value.Radius, value.Components, value.Gamma),
testOutputDetails: value.ToString(),
appendPixelTypeToFileName: false);
}
[Theory]
@ -164,18 +153,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
public void BokehBlurFilterProcessor_WorksWithAllPixelTypes<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
static void RunTest(string providerDump)
{
TestImageProvider<TPixel> provider =
BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
provider.RunValidatingProcessorTest(
provider.RunValidatingProcessorTest(
x => x.BokehBlur(8, 2, 3),
appendSourceFileOrDescription: false);
}
RemoteExecutor
.Invoke(RunTest, BasicSerializer.Serialize(provider))
.Dispose();
}
[Theory]
@ -183,26 +163,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution
public void BokehBlurFilterProcessor_Bounded<TPixel>(TestImageProvider<TPixel> provider, BokehBlurInfo value)
where TPixel : unmanaged, IPixel<TPixel>
{
static void RunTest(string providerDump, string infoDump)
{
TestImageProvider<TPixel> provider =
BasicSerializer.Deserialize<TestImageProvider<TPixel>>(providerDump);
BokehBlurInfo value = BasicSerializer.Deserialize<BokehBlurInfo>(infoDump);
provider.RunValidatingProcessorTest(
x =>
{
Size size = x.GetCurrentSize();
var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2);
x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds);
},
testOutputDetails: value.ToString(),
appendPixelTypeToFileName: false);
}
RemoteExecutor
.Invoke(RunTest, BasicSerializer.Serialize(provider), BasicSerializer.Serialize(value))
.Dispose();
provider.RunValidatingProcessorTest(
x =>
{
Size size = x.GetCurrentSize();
var bounds = new Rectangle(10, 10, size.Width / 2, size.Height / 2);
x.BokehBlur(value.Radius, value.Components, value.Gamma, bounds);
},
testOutputDetails: value.ToString(),
appendPixelTypeToFileName: false);
}
[Theory]

266
tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs

@ -0,0 +1,266 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.DotNet.RemoteExecutor;
using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.TestUtilities
{
/// <summary>
/// Allows the testing against specific feature sets.
/// </summary>
public static class FeatureTestRunner
{
private static readonly char[] SplitChars = new[] { ',', ' ' };
/// <summary>
/// Allows the deserialization of parameters passed to the feature test.
/// <remark>
/// <para>
/// This is required because <see cref="RemoteExecutor"/> does not allow
/// marshalling of fields so we cannot pass a wrapped <see cref="Action{T}"/>
/// allowing automatic deserialization.
/// </para>
/// </remark>
/// </summary>
/// <typeparam name="T">The type to deserialize to.</typeparam>
/// <param name="value">The string value to deserialize.</param>
/// <returns>The <see cref="T"/> value.</returns>
public static T Deserialize<T>(string value)
where T : IXunitSerializable
=> BasicSerializer.Deserialize<T>(value);
/// <summary>
/// Runs the given test <paramref name="action"/> within an environment
/// where the given <paramref name="intrinsics"/> features.
/// </summary>
/// <param name="action">The test action to run.</param>
/// <param name="intrinsics">The intrinsics features.</param>
public static void RunWithHwIntrinsicsFeature(
Action action,
HwIntrinsics intrinsics)
{
if (!RemoteExecutor.IsSupported)
{
return;
}
foreach (KeyValuePair<HwIntrinsics, string> intrinsic in intrinsics.ToFeatureKeyValueCollection())
{
var processStartInfo = new ProcessStartInfo();
if (intrinsic.Key != HwIntrinsics.AllowAll)
{
processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0";
RemoteExecutor.Invoke(
action,
new RemoteInvokeOptions
{
StartInfo = processStartInfo
})
.Dispose();
}
else
{
// Since we are running using the default architecture there is no
// point creating the overhead of running the action in a separate process.
action();
}
}
}
/// <summary>
/// Runs the given test <paramref name="action"/> within an environment
/// where the given <paramref name="intrinsics"/> features.
/// </summary>
/// <param name="action">
/// The test action to run.
/// The parameter passed will be a string representing the currently testing <see cref="HwIntrinsics"/>.</param>
/// <param name="intrinsics">The intrinsics features.</param>
public static void RunWithHwIntrinsicsFeature(
Action<string> action,
HwIntrinsics intrinsics)
{
if (!RemoteExecutor.IsSupported)
{
return;
}
foreach (KeyValuePair<HwIntrinsics, string> intrinsic in intrinsics.ToFeatureKeyValueCollection())
{
var processStartInfo = new ProcessStartInfo();
if (intrinsic.Key != HwIntrinsics.AllowAll)
{
processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0";
RemoteExecutor.Invoke(
action,
intrinsic.Key.ToString(),
new RemoteInvokeOptions
{
StartInfo = processStartInfo
})
.Dispose();
}
else
{
// Since we are running using the default architecture there is no
// point creating the overhead of running the action in a separate process.
action(intrinsic.Key.ToString());
}
}
}
/// <summary>
/// Runs the given test <paramref name="action"/> within an environment
/// where the given <paramref name="intrinsics"/> features.
/// </summary>
/// <param name="action">The test action to run.</param>
/// <param name="intrinsics">The intrinsics features.</param>
/// <param name="serializable">The value to pass as a parameter to the test action.</param>
public static void RunWithHwIntrinsicsFeature<T>(
Action<string> action,
HwIntrinsics intrinsics,
T serializable)
where T : IXunitSerializable
{
if (!RemoteExecutor.IsSupported)
{
return;
}
foreach (KeyValuePair<HwIntrinsics, string> intrinsic in intrinsics.ToFeatureKeyValueCollection())
{
var processStartInfo = new ProcessStartInfo();
if (intrinsic.Key != HwIntrinsics.AllowAll)
{
processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0";
RemoteExecutor.Invoke(
action,
BasicSerializer.Serialize(serializable),
new RemoteInvokeOptions
{
StartInfo = processStartInfo
})
.Dispose();
}
else
{
// Since we are running using the default architecture there is no
// point creating the overhead of running the action in a separate process.
action(BasicSerializer.Serialize(serializable));
}
}
}
/// <summary>
/// Runs the given test <paramref name="action"/> within an environment
/// where the given <paramref name="intrinsics"/> features.
/// </summary>
/// <param name="action">The test action to run.</param>
/// <param name="intrinsics">The intrinsics features.</param>
/// <param name="serializable">The value to pass as a parameter to the test action.</param>
public static void RunWithHwIntrinsicsFeature<T>(
Action<string, string> action,
HwIntrinsics intrinsics,
T serializable)
where T : IXunitSerializable
{
if (!RemoteExecutor.IsSupported)
{
return;
}
foreach (KeyValuePair<HwIntrinsics, string> intrinsic in intrinsics.ToFeatureKeyValueCollection())
{
var processStartInfo = new ProcessStartInfo();
if (intrinsic.Key != HwIntrinsics.AllowAll)
{
processStartInfo.Environment[$"COMPlus_{intrinsic.Value}"] = "0";
RemoteExecutor.Invoke(
action,
BasicSerializer.Serialize(serializable),
intrinsic.Key.ToString(),
new RemoteInvokeOptions
{
StartInfo = processStartInfo
})
.Dispose();
}
else
{
// Since we are running using the default architecture there is no
// point creating the overhead of running the action in a separate process.
action(BasicSerializer.Serialize(serializable), intrinsic.Key.ToString());
}
}
}
internal static Dictionary<HwIntrinsics, string> ToFeatureKeyValueCollection(this HwIntrinsics intrinsics)
{
// Loop through and translate the given values into COMPlus equivaluents
var features = new Dictionary<HwIntrinsics, string>();
foreach (string intrinsic in intrinsics.ToString("G").Split(SplitChars, StringSplitOptions.RemoveEmptyEntries))
{
var key = (HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic);
switch (intrinsic)
{
case nameof(HwIntrinsics.DisableSIMD):
features.Add(key, "FeatureSIMD");
break;
case nameof(HwIntrinsics.AllowAll):
// Not a COMPlus value. We filter in calling method.
features.Add(key, nameof(HwIntrinsics.AllowAll));
break;
default:
features.Add(key, intrinsic.Replace("Disable", "Enable"));
break;
}
}
return features;
}
}
/// <summary>
/// See <see href="https://github.com/dotnet/runtime/blob/50ac454d8d8a1915188b2a4bb3fff3b81bf6c0cf/src/coreclr/src/jit/jitconfigvalues.h#L224"/>
/// <remarks>
/// <see cref="DisableSIMD"/> ends up impacting all SIMD support(including System.Numerics)
/// but not things like <see cref="DisableBMI1"/>, <see cref="DisableBMI2"/>, and <see cref="DisableLZCNT"/>.
/// </remarks>
/// </summary>
[Flags]
#pragma warning disable RCS1135 // Declare enum member with zero value (when enum has FlagsAttribute).
public enum HwIntrinsics
#pragma warning restore RCS1135 // Declare enum member with zero value (when enum has FlagsAttribute).
{
// Use flags so we can pass multiple values without using params.
// Don't base on 0 or use inverse for All as that doesn't translate to string values.
DisableSIMD = 1 << 0,
DisableHWIntrinsic = 1 << 1,
DisableSSE = 1 << 2,
DisableSSE2 = 1 << 3,
DisableAES = 1 << 4,
DisablePCLMULQDQ = 1 << 5,
DisableSSE3 = 1 << 6,
DisableSSSE3 = 1 << 7,
DisableSSE41 = 1 << 8,
DisableSSE42 = 1 << 9,
DisablePOPCNT = 1 << 10,
DisableAVX = 1 << 11,
DisableFMA = 1 << 12,
DisableAVX2 = 1 << 13,
DisableBMI1 = 1 << 14,
DisableBMI2 = 1 << 15,
DisableLZCNT = 1 << 16,
AllowAll = 1 << 17
}
}

33
tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using ImageMagick;
using ImageMagick.Formats.Bmp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -15,10 +16,22 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
{
public class MagickReferenceDecoder : IImageDecoder
{
private readonly bool validate;
public MagickReferenceDecoder()
: this(true)
{
}
public MagickReferenceDecoder(bool validate)
{
this.validate = validate;
}
public static MagickReferenceDecoder Instance { get; } = new MagickReferenceDecoder();
private static void FromRgba32Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
foreach (Memory<TPixel> m in destinationGroup)
{
@ -33,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
}
private static void FromRgba64Bytes<TPixel>(Configuration configuration, Span<byte> rgbaBytes, IMemoryGroup<TPixel> destinationGroup)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
foreach (Memory<TPixel> m in destinationGroup)
{
@ -48,17 +61,25 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
}
public Task<Image<TPixel>> DecodeAsync<TPixel>(Configuration configuration, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
=> Task.FromResult(this.Decode<TPixel>(configuration, stream));
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
where TPixel : unmanaged, ImageSharp.PixelFormats.IPixel<TPixel>
{
using var magickImage = new MagickImage(stream);
var bmpReadDefines = new BmpReadDefines
{
IgnoreFileSize = !this.validate
};
var settings = new MagickReadSettings();
settings.SetDefines(bmpReadDefines);
using var magickImage = new MagickImage(stream, settings);
var result = new Image<TPixel>(configuration, magickImage.Width, magickImage.Height);
MemoryGroup<TPixel> resultPixels = result.GetRootFramePixelBuffer().FastMemoryGroup;
using (IPixelCollection pixels = magickImage.GetPixelsUnsafe())
using (IUnsafePixelCollection<ushort> pixels = magickImage.GetPixelsUnsafe())
{
if (magickImage.Depth == 8)
{

54
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Features.cs

@ -1,54 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests
{
public static partial class TestEnvironment
{
internal static class Features
{
public const string On = "1";
public const string Off = "0";
// See https://github.com/SixLabors/ImageSharp/pull/1229#discussion_r440477861
// * EnableHWIntrinsic
// * EnableSSE
// * EnableSSE2
// * EnableAES
// * EnablePCLMULQDQ
// * EnableSSE3
// * EnableSSSE3
// * EnableSSE41
// * EnableSSE42
// * EnablePOPCNT
// * EnableAVX
// * EnableFMA
// * EnableAVX2
// * EnableBMI1
// * EnableBMI2
// * EnableLZCNT
//
// `FeatureSIMD` ends up impacting all SIMD support(including `System.Numerics`) but not things
// like `LZCNT`, `BMI1`, or `BMI2`
// `EnableSSE3_4` is a legacy switch that exists for compat and is basically the same as `EnableSSE3`
public const string EnableAES = "COMPlus_EnableAES";
public const string EnableAVX = "COMPlus_EnableAVX";
public const string EnableAVX2 = "COMPlus_EnableAVX2";
public const string EnableBMI1 = "COMPlus_EnableBMI1";
public const string EnableBMI2 = "COMPlus_EnableBMI2";
public const string EnableFMA = "COMPlus_EnableFMA";
public const string EnableHWIntrinsic = "COMPlus_EnableHWIntrinsic";
public const string EnableLZCNT = "COMPlus_EnableLZCNT";
public const string EnablePCLMULQDQ = "COMPlus_EnablePCLMULQDQ";
public const string EnablePOPCNT = "COMPlus_EnablePOPCNT";
public const string EnableSSE = "COMPlus_EnableSSE";
public const string EnableSSE2 = "COMPlus_EnableSSE2";
public const string EnableSSE3 = "COMPlus_EnableSSE3";
public const string EnableSSE3_4 = "COMPlus_EnableSSE3_4";
public const string EnableSSE41 = "COMPlus_EnableSSE41";
public const string EnableSSE42 = "COMPlus_EnableSSE42";
public const string EnableSSSE3 = "COMPlus_EnableSSSE3";
public const string FeatureSIMD = "COMPlus_FeatureSIMD";
}
}
}

5
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs

@ -170,7 +170,10 @@ namespace SixLabors.ImageSharp.Tests
}
string testProjectConfigPath = TestAssemblyFile.FullName + ".config";
File.Copy(testProjectConfigPath, remoteExecutorConfigPath);
if (File.Exists(testProjectConfigPath))
{
File.Copy(testProjectConfigPath, remoteExecutorConfigPath);
}
if (Is64BitProcess)
{

296
tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs

@ -0,0 +1,296 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics.X86;
#endif
using Xunit;
using Xunit.Abstractions;
namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests
{
public class FeatureTestRunnerTests
{
public static TheoryData<HwIntrinsics, string[]> Intrinsics =>
new TheoryData<HwIntrinsics, string[]>
{
{ HwIntrinsics.DisableAES | HwIntrinsics.AllowAll, new string[] { "EnableAES", "AllowAll" } },
{ HwIntrinsics.DisableSIMD | HwIntrinsics.DisableHWIntrinsic, new string[] { "FeatureSIMD", "EnableHWIntrinsic" } },
{ HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableAVX, new string[] { "EnableSSE42", "EnableAVX" } }
};
[Theory]
[MemberData(nameof(Intrinsics))]
public void ToFeatureCollectionReturnsExpectedResult(HwIntrinsics expectedItrinsics, string[] expectedValues)
{
Dictionary<HwIntrinsics, string> features = expectedItrinsics.ToFeatureKeyValueCollection();
HwIntrinsics[] keys = features.Keys.ToArray();
HwIntrinsics actualIntrinsics = keys[0];
for (int i = 1; i < keys.Length; i++)
{
actualIntrinsics |= keys[i];
}
Assert.Equal(expectedItrinsics, actualIntrinsics);
IEnumerable<string> actualValues = features.Select(x => x.Value);
Assert.Equal(expectedValues, actualValues);
}
[Fact]
public void AllowsAllHwIntrinsicFeatures()
{
if (!Vector.IsHardwareAccelerated)
{
return;
}
FeatureTestRunner.RunWithHwIntrinsicsFeature(
() => Assert.True(Vector.IsHardwareAccelerated),
HwIntrinsics.AllowAll);
}
[Fact]
public void CanLimitHwIntrinsicSIMDFeatures()
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(
() => Assert.False(Vector.IsHardwareAccelerated),
HwIntrinsics.DisableSIMD);
}
#if SUPPORTS_RUNTIME_INTRINSICS
[Fact]
public void CanLimitHwIntrinsicBaseFeatures()
{
static void AssertDisabled()
{
Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported);
Assert.False(Aes.IsSupported);
Assert.False(Pclmulqdq.IsSupported);
Assert.False(Sse3.IsSupported);
Assert.False(Ssse3.IsSupported);
Assert.False(Sse41.IsSupported);
Assert.False(Sse42.IsSupported);
Assert.False(Popcnt.IsSupported);
Assert.False(Avx.IsSupported);
Assert.False(Fma.IsSupported);
Assert.False(Avx2.IsSupported);
Assert.False(Bmi1.IsSupported);
Assert.False(Bmi2.IsSupported);
Assert.False(Lzcnt.IsSupported);
}
FeatureTestRunner.RunWithHwIntrinsicsFeature(
AssertDisabled,
HwIntrinsics.DisableHWIntrinsic);
}
#endif
[Fact]
public void CanLimitHwIntrinsicFeaturesWithIntrinsicsParam()
{
static void AssertHwIntrinsicsFeatureDisabled(string intrinsic)
{
Assert.NotNull(intrinsic);
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic))
{
case HwIntrinsics.DisableSIMD:
Assert.False(Vector.IsHardwareAccelerated);
break;
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported);
Assert.False(Aes.IsSupported);
Assert.False(Pclmulqdq.IsSupported);
Assert.False(Sse3.IsSupported);
Assert.False(Ssse3.IsSupported);
Assert.False(Sse41.IsSupported);
Assert.False(Sse42.IsSupported);
Assert.False(Popcnt.IsSupported);
Assert.False(Avx.IsSupported);
Assert.False(Fma.IsSupported);
Assert.False(Avx2.IsSupported);
Assert.False(Bmi1.IsSupported);
Assert.False(Bmi2.IsSupported);
Assert.False(Lzcnt.IsSupported);
break;
case HwIntrinsics.DisableSSE:
Assert.False(Sse.IsSupported);
break;
case HwIntrinsics.DisableSSE2:
Assert.False(Sse2.IsSupported);
break;
case HwIntrinsics.DisableAES:
Assert.False(Aes.IsSupported);
break;
case HwIntrinsics.DisablePCLMULQDQ:
Assert.False(Pclmulqdq.IsSupported);
break;
case HwIntrinsics.DisableSSE3:
Assert.False(Sse3.IsSupported);
break;
case HwIntrinsics.DisableSSSE3:
Assert.False(Ssse3.IsSupported);
break;
case HwIntrinsics.DisableSSE41:
Assert.False(Sse41.IsSupported);
break;
case HwIntrinsics.DisableSSE42:
Assert.False(Sse42.IsSupported);
break;
case HwIntrinsics.DisablePOPCNT:
Assert.False(Popcnt.IsSupported);
break;
case HwIntrinsics.DisableAVX:
Assert.False(Avx.IsSupported);
break;
case HwIntrinsics.DisableFMA:
Assert.False(Fma.IsSupported);
break;
case HwIntrinsics.DisableAVX2:
Assert.False(Avx2.IsSupported);
break;
case HwIntrinsics.DisableBMI1:
Assert.False(Bmi1.IsSupported);
break;
case HwIntrinsics.DisableBMI2:
Assert.False(Bmi2.IsSupported);
break;
case HwIntrinsics.DisableLZCNT:
Assert.False(Lzcnt.IsSupported);
break;
#endif
}
}
foreach (HwIntrinsics intrinsic in (HwIntrinsics[])Enum.GetValues(typeof(HwIntrinsics)))
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic);
}
}
[Fact]
public void CanLimitHwIntrinsicFeaturesWithSerializableParam()
{
static void AssertHwIntrinsicsFeatureDisabled(string serializable)
{
Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.Deserialize<FakeSerializable>(serializable));
#if SUPPORTS_RUNTIME_INTRINSICS
Assert.False(Sse.IsSupported);
#endif
}
FeatureTestRunner.RunWithHwIntrinsicsFeature(
AssertHwIntrinsicsFeatureDisabled,
HwIntrinsics.DisableSSE,
new FakeSerializable());
}
[Fact]
public void CanLimitHwIntrinsicFeaturesWithSerializableAndIntrinsicsParams()
{
static void AssertHwIntrinsicsFeatureDisabled(string serializable, string intrinsic)
{
Assert.NotNull(serializable);
Assert.NotNull(FeatureTestRunner.Deserialize<FakeSerializable>(serializable));
switch ((HwIntrinsics)Enum.Parse(typeof(HwIntrinsics), intrinsic))
{
case HwIntrinsics.DisableSIMD:
Assert.False(Vector.IsHardwareAccelerated, nameof(Vector.IsHardwareAccelerated));
break;
#if SUPPORTS_RUNTIME_INTRINSICS
case HwIntrinsics.DisableHWIntrinsic:
Assert.False(Sse.IsSupported);
Assert.False(Sse2.IsSupported);
Assert.False(Aes.IsSupported);
Assert.False(Pclmulqdq.IsSupported);
Assert.False(Sse3.IsSupported);
Assert.False(Ssse3.IsSupported);
Assert.False(Sse41.IsSupported);
Assert.False(Sse42.IsSupported);
Assert.False(Popcnt.IsSupported);
Assert.False(Avx.IsSupported);
Assert.False(Fma.IsSupported);
Assert.False(Avx2.IsSupported);
Assert.False(Bmi1.IsSupported);
Assert.False(Bmi2.IsSupported);
Assert.False(Lzcnt.IsSupported);
break;
case HwIntrinsics.DisableSSE:
Assert.False(Sse.IsSupported);
break;
case HwIntrinsics.DisableSSE2:
Assert.False(Sse2.IsSupported);
break;
case HwIntrinsics.DisableAES:
Assert.False(Aes.IsSupported);
break;
case HwIntrinsics.DisablePCLMULQDQ:
Assert.False(Pclmulqdq.IsSupported);
break;
case HwIntrinsics.DisableSSE3:
Assert.False(Sse3.IsSupported);
break;
case HwIntrinsics.DisableSSSE3:
Assert.False(Ssse3.IsSupported);
break;
case HwIntrinsics.DisableSSE41:
Assert.False(Sse41.IsSupported);
break;
case HwIntrinsics.DisableSSE42:
Assert.False(Sse42.IsSupported);
break;
case HwIntrinsics.DisablePOPCNT:
Assert.False(Popcnt.IsSupported);
break;
case HwIntrinsics.DisableAVX:
Assert.False(Avx.IsSupported);
break;
case HwIntrinsics.DisableFMA:
Assert.False(Fma.IsSupported);
break;
case HwIntrinsics.DisableAVX2:
Assert.False(Avx2.IsSupported);
break;
case HwIntrinsics.DisableBMI1:
Assert.False(Bmi1.IsSupported);
break;
case HwIntrinsics.DisableBMI2:
Assert.False(Bmi2.IsSupported);
break;
case HwIntrinsics.DisableLZCNT:
Assert.False(Lzcnt.IsSupported);
break;
#endif
}
}
foreach (HwIntrinsics intrinsic in (HwIntrinsics[])Enum.GetValues(typeof(HwIntrinsics)))
{
FeatureTestRunner.RunWithHwIntrinsicsFeature(AssertHwIntrinsicsFeatureDisabled, intrinsic, new FakeSerializable());
}
}
public class FakeSerializable : IXunitSerializable
{
public void Deserialize(IXunitSerializationInfo info)
{
}
public void Serialize(IXunitSerializationInfo info)
{
}
}
}
}

4
tests/coverlet.runsettings

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<RunConfiguration>
<!--Used in conjunction with ActiveIssueAttribute to skip tests with known issues-->
<TestCaseFilter>category!=failing</TestCaseFilter>
</RunConfiguration>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="XPlat code coverage">

Loading…
Cancel
Save