diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3487467..32be250 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,12 +27,12 @@ jobs: - uses: actions/setup-dotnet@v4 with: dotnet-version: 9.x - - name: Install dependencies - run: dotnet restore - - name: Build - run: dotnet build --configuration Release --no-restore + - uses: gerlero/apt-install@v1 + if: startsWith(matrix.os, 'ubuntu') == true + with: + packages: libhwy-dev liblcms2-dev libjpeg-turbo8-dev libpng-dev libjpeg-dev zlib1g-dev libgif-dev - name: Test - run: dotnet test --no-restore --configuration Release + run: dotnet test --no-restore --configuration Release --filter CI!=false --logger "console;verbosity=detailed" - name: Upload Test Results if: failure() uses: actions/upload-artifact@v4 diff --git a/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj b/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj index d3050a7..88e296d 100644 --- a/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj +++ b/Compressonator.NET.Tests/Compressonator.NET.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/Compressonator.NET.Tests/EnumTests.cs b/Compressonator.NET.Tests/EnumTests.cs index d4218e5..ddea104 100644 --- a/Compressonator.NET.Tests/EnumTests.cs +++ b/Compressonator.NET.Tests/EnumTests.cs @@ -7,7 +7,8 @@ public class EnumTests // If there's an enum mismatch, it can lead to unreliable undefined behavior. // We test both classes, because they use different compilation chains, and could get out of sync. [TestEnum(typeof(CMP_FORMAT))] - [DataTestMethod] + [TestMethod] + [TestProperty("CI", "true")] public void TestCMP_FORMAT(CMP_FORMAT format) { Assert.IsTrue(SDK_NativeMethods.CMP_IsValidFormat(format), $"CMP_Format must be valid for: {format}"); diff --git a/Compressonator.NET.Tests/GCAsset.cs b/Compressonator.NET.Tests/GCAsset.cs new file mode 100644 index 0000000..bcb555c --- /dev/null +++ b/Compressonator.NET.Tests/GCAsset.cs @@ -0,0 +1,30 @@ +namespace Compressonator.NET.Tests; + +// Please don't use this outside of tests. +// Largely from: https://stackoverflow.com/a/9827349 + other answers on the same question. +// Also: https://stackoverflow.com/questions/3829928/under-what-circumstances-we-need-to-call-gc-collect-twice from why we GC.Collect Twice. +internal static class GCAssert +{ + public static void AssertGarbageCollectable(Func create) + where T : class + { + WeakReference reference = CreateAndDropReference(create); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.IsFalse(reference.IsAlive, $"{typeof(T).Name} should have been garbage collected"); + } + + private static WeakReference CreateAndDropReference(Func create) + where T : class + { + var obj = create(); + + if (obj is IDisposable disposable) + disposable.Dispose(); + + return new WeakReference(obj); + } +} diff --git a/Compressonator.NET.Tests/Marshaling/MarshalTests.TestStructs_t=Compressonator.NET.CMP_Texture.verified.txt b/Compressonator.NET.Tests/Marshaling/MarshalTests.TestStructs_t=Compressonator.NET.CMP_Texture.verified.txt new file mode 100644 index 0000000..c59653a --- /dev/null +++ b/Compressonator.NET.Tests/Marshaling/MarshalTests.TestStructs_t=Compressonator.NET.CMP_Texture.verified.txt @@ -0,0 +1,78 @@ +{ + FieldName: CMP_Texture, + Size: 48, + Nested: [ + { + FieldName: size, + Size: 4, + Type: UInt32 + }, + { + FieldName: width, + Size: 4, + Offset: 4, + Type: UInt32 + }, + { + FieldName: height, + Size: 4, + Offset: 8, + Type: UInt32 + }, + { + FieldName: pitch, + Size: 4, + Offset: 12, + Type: UInt32 + }, + { + FieldName: format, + Size: 4, + Offset: 16, + Type: CMP_FORMAT + }, + { + FieldName: transcodeFormat, + Size: 4, + Offset: 20, + Type: CMP_FORMAT + }, + { + FieldName: blockHeight, + Size: 1, + Offset: 24, + Type: Byte + }, + { + FieldName: blockWidth, + Size: 1, + Offset: 25, + Type: Byte + }, + { + FieldName: blockDepth, + Size: 1, + Offset: 26, + Type: Byte + }, + { + FieldName: dataSize, + Size: 4, + Offset: 28, + Type: UInt32 + }, + { + FieldName: data, + Size: 8, + Offset: 32, + Type: IntPtr + }, + { + FieldName: mipSet, + Size: 8, + Offset: 40, + Type: IntPtr + } + ], + Type: struct +} \ No newline at end of file diff --git a/Compressonator.NET.Tests/Marshaling/MarshalTests.cs b/Compressonator.NET.Tests/Marshaling/MarshalTests.cs index 6f6e6c0..e83ad6e 100644 --- a/Compressonator.NET.Tests/Marshaling/MarshalTests.cs +++ b/Compressonator.NET.Tests/Marshaling/MarshalTests.cs @@ -33,11 +33,11 @@ public void TestCMP_FORMAT() [DataRow(12, typeof(KernelPerformanceStats))] [DataRow(388, typeof(KernelDeviceInfo))] [DataRow(48, typeof(AMD_CMD))] + [DataRow(48, typeof(CMP_Texture))] [DataTestMethod] public async Task TestStructs(int correctSize, Type t) { Assert.AreEqual(correctSize, Marshal.SizeOf(t), $"{nameof(t)} must marshal as {correctSize} bytes"); - await VerifyMemoryLayout(t); } } diff --git a/Compressonator.NET.Tests/NativeTests.cs b/Compressonator.NET.Tests/NativeTests.cs index 9f90a61..0536f65 100644 --- a/Compressonator.NET.Tests/NativeTests.cs +++ b/Compressonator.NET.Tests/NativeTests.cs @@ -1,15 +1,20 @@ namespace Compressonator.NET.Tests; +[TestCategory("Smoke")] [TestClass] public sealed class NativeTests { [TestMethod] + [Priority(1)] + [TestProperty("CI", "true")] public void TestSDKNative() { Assert.IsTrue(SDK_NativeMethods.IsSupported, "SDK Native methods should be supported"); } [TestMethod] + [Priority(1)] + [TestProperty("CI", "true")] public void TestFrameworkNative() { Assert.IsTrue(FrameworkNativeMethods.IsSupported, "Framework Native Methods should be supported."); @@ -18,9 +23,63 @@ public void TestFrameworkNative() /// /// Test that this method just doesn't throw any exceptions. /// + [TestProperty("CI", "true")] [TestMethod] public void TestLibraryInitialization() { FrameworkNativeMethods.CMP_InitFramework(); } + + [TestProperty("CI", "true")] + [TestMethod] + // This was causing some CI Crashes, let's write a test + public void TestFreeingMipMaps() + { + var mip = new CMP_MipSet(); + mip.Dispose(); + + // We don't have any assertions, it just must not throw. + } + + [TestProperty("CI", "true")] + [TestMethod] + // This was causing some CI Crashes, let's write a test + public void TestFreeingMipMapsMultipleTimes() + { + var mip = new CMP_MipSet(); + FrameworkNativeMethods.CMP_FreeMipSet(mip); + FrameworkNativeMethods.CMP_FreeMipSet(mip); + + // We don't have any assertions, it just must not throw. + } + + [TestProperty("CI", "true")] + [TestMethod] + // This was causing some CI Crashes, let's write a test + public void TestMipSetDisposeTwice() + { + var mip = new CMP_MipSet(); + mip.Dispose(); + mip.Dispose(); + + // We don't have any assertions, it just must not throw. + } + + // The structure of GCAssert.AssertGarbageCollectable requires a factory due to its use of WeakReferences + // This structure, wraps the factory in a nice way, that's easier to type :) + public static IEnumerable GCTests() + { + static object[] w(Func factory) where T : class => new object[] { typeof(T), factory }; + + yield return w(() => new CMP_MipSet()); + yield return w(() => new CMP_Texture()); + } + + [TestMethod] + [TestProperty("CI", "true")] + [DynamicData(nameof(GCTests))] + public void TestGC(Type t, Func typeCreator) + { + GCAssert.AssertGarbageCollectable(typeCreator); + } } diff --git a/Compressonator.NET.Tests/Snapshot/CompressionTests.cs b/Compressonator.NET.Tests/Snapshot/CompressionTests.cs index 18bc4b5..146c66d 100644 --- a/Compressonator.NET.Tests/Snapshot/CompressionTests.cs +++ b/Compressonator.NET.Tests/Snapshot/CompressionTests.cs @@ -4,7 +4,7 @@ namespace Compressonator.NET.Tests.Snapshot; [TestClass] public class CompressionTests : SnapshotTestingBase { - public const CMP_FORMAT DEFAULT_SOURCE_FORMAT = CMP_FORMAT.RGBA_8888; + public const CMP_FORMAT DEFAULT_SOURCE_FORMAT = SnapshotUtilities.DEFAULT_SOURCE_FORMAT; public const float DEFAULT_BC67COMPRESSION_QUALITY = 0.05f; public const float RESONITE_BC67COMPRESSION_QUALITY = 0.6f; @@ -68,7 +68,7 @@ public class CompressionTests : SnapshotTestingBase [DataRow(CMP_FORMAT.BC7, DEFAULT_SOURCE_FORMAT, "Resources/flowers.png", RESONITE_BC67COMPRESSION_QUALITY)] [DataRow(CMP_FORMAT.BC7, DEFAULT_SOURCE_FORMAT, "Resources/helicopter.png", RESONITE_BC67COMPRESSION_QUALITY)] - + [TestProperty("CI", "true")] [DataTestMethod] public async Task TestCompression( CMP_FORMAT targetFormat, @@ -82,17 +82,14 @@ public async Task TestCompression( // see: https://github.com/Yellow-Dog-Man/Compressonator.NET/issues/20 settings.UniqueForOSPlatform(); - if (IsSlow(targetFormat)) - { - if (quality > DEFAULT_BC67COMPRESSION_QUALITY) - Assert.Inconclusive($"BC6H & BC7 Tests at higher qualities than {DEFAULT_BC67COMPRESSION_QUALITY} due to how long they take."); - } + TestUtilities.GuardCITests(targetFormat, quality); //// see: https://github.com/Yellow-Dog-Man/Compressonator.NET/issues/21 //if (targetFormat == CMP_FORMAT.BC2 && inputFileRelativePath == "Resources/rainbow.png") // settings.UniqueForOSPlatform(); - var (res, mipSetIn) = SnapshotUtilities.Load(inputFileRelativePath, targetFormat, sourceFormat); + var (res, mipSetIn) = SnapshotUtilities.Load(inputFileRelativePath, sourceFormat, mipLevels: 3); + Assert.IsTrue(SDK_NativeMethods.CMP_IsValidFormat(targetFormat), "Target format must be supported by native library"); CMP_MipSet mipSetOut = new(); CMP_ERROR cmpStatus = CMP_ERROR.CMP_OK; @@ -119,28 +116,24 @@ public async Task TestCompression( [DataRow(CMP_FORMAT.BC5, DEFAULT_SOURCE_FORMAT, "Resources/rainbow.png")] [DataRow(CMP_FORMAT.BC6H, DEFAULT_SOURCE_FORMAT, "Resources/rainbow.png")] [DataRow(CMP_FORMAT.BC7, DEFAULT_SOURCE_FORMAT, "Resources/rainbow.png")] - [DataTestMethod] + [TestMethod] + [TestProperty("CI", "true")] public async Task TestProcessTexture(CMP_FORMAT targetFormat, CMP_FORMAT sourceFormat, string inputFileRelativePath, float quality = 0.9f, uint maxThreads = 0) { + TestUtilities.GuardCITests(targetFormat, quality); VerifySettings settings = new VerifySettings(); // see: https://github.com/Yellow-Dog-Man/Compressonator.NET/issues/21 if (targetFormat == CMP_FORMAT.BC2 && inputFileRelativePath == "Resources/rainbow.png") settings.UniqueForOSPlatform(); - if (IsSlow(targetFormat)) - { - settings.UniqueForOSPlatform(); - // See: https://github.com/Yellow-Dog-Man/Compressonator.NET/issues/17 - // See; https://github.com/Yellow-Dog-Man/compressonator/issues/10 - Assert.Inconclusive("BC6H & BC67 Tests are disabled for this test."); - } - + - var (res, mipSetIn) = SnapshotUtilities.Load(inputFileRelativePath, targetFormat, sourceFormat); + var (res, mipSetIn) = SnapshotUtilities.Load(inputFileRelativePath, sourceFormat); + Assert.IsTrue(SDK_NativeMethods.CMP_IsValidFormat(targetFormat), "Target format must be supported by native library"); CMP_MipSet mipSetOut = new(); CMP_ERROR cmpStatus = CMP_ERROR.CMP_OK; diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests.cs b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests.cs new file mode 100644 index 0000000..a2de47d --- /dev/null +++ b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests.cs @@ -0,0 +1,60 @@ +using Compressonator.NET.Tests.Snapshot; + +namespace Compressonator.NET.Tests; + +/// +/// These tests output PNGs, rather than DDS files. This means you can more easily evaluate qualities in your browser etc. +/// +[TestClass] +public class QualityEvaluationTests: SnapshotTestingBase +{ + static List paths = new List() + { + "Resources/rainbow.png", + "Resources/carrots.png", + "Resources/colorpatch.png", + "Resources/squares.png", + + //"Resources/wings.png", + //"Resources/tulipbody.png" + }; + + [DataRow(CMP_FORMAT.BC1, 1f)] + [DataRow(CMP_FORMAT.BC2, 1f)] + [DataRow(CMP_FORMAT.BC3, 1f)] + [DataRow(CMP_FORMAT.BC4, 1f)] + [DataRow(CMP_FORMAT.BC5, 1f)] + + [DataRow(CMP_FORMAT.BC6H, 0.05f)] + [DataRow(CMP_FORMAT.BC6H, 0.10f)] + [DataRow(CMP_FORMAT.BC6H, 0.25f)] + + [DataRow(CMP_FORMAT.BC7, 0.05f)] + [DataRow(CMP_FORMAT.BC7, 0.10f)] + [DataRow(CMP_FORMAT.BC7, 0.2f)] + [DataRow(CMP_FORMAT.BC7, 0.25f)] + [TestMethod] + [TestProperty("CI", "false")] + public async Task VerifyTexture(CMP_FORMAT targetFormat, float quality, bool disabled = false) + { + TestUtilities.GuardCITests(targetFormat, quality); + + if (disabled) + Assert.Inconclusive($"This test is disabled"); + + foreach (string path in paths) + { + var settings = new VerifySettings(); + settings.UseDirectory("QualityEvaluationTests/"+ Path.GetFileName(path)); + settings.UseFileName($"{targetFormat}-{quality}"); + + /// TMP: REMOVE IT! + settings.AutoVerify(); + var distortedPath = CurrentFile.Relative(SnapshotUtilities.GetFileNameForTest("png")); + + CMP_Texture distortedTexture = SnapshotUtilities.RoundTripWithCompression(path, targetFormat, quality); + + await SnapshotUtilities.SaveVerifyDelete(distortedTexture, "png", settings); + } + } +} diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC1-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC1-1.verified.png new file mode 100644 index 0000000..605bcff Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC1-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC2-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC2-1.verified.png new file mode 100644 index 0000000..78b9e62 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC2-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC3-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC3-1.verified.png new file mode 100644 index 0000000..78b9e62 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC3-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC4-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC4-1.verified.png new file mode 100644 index 0000000..a378205 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC4-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC5-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC5-1.verified.png new file mode 100644 index 0000000..50c730e Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC5-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.05.verified.png new file mode 100644 index 0000000..be9e567 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.1.verified.png new file mode 100644 index 0000000..be9e567 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.25.verified.png new file mode 100644 index 0000000..be9e567 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC6H-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.05.verified.png new file mode 100644 index 0000000..6104577 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.1.verified.png new file mode 100644 index 0000000..3dca00f Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.2.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.2.verified.png new file mode 100644 index 0000000..6eeac43 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.2.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.25.verified.png new file mode 100644 index 0000000..19e8cf3 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/carrots.png/BC7-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC1-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC1-1.verified.png new file mode 100644 index 0000000..db6751a Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC1-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC2-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC2-1.verified.png new file mode 100644 index 0000000..f7b1770 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC2-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC3-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC3-1.verified.png new file mode 100644 index 0000000..f7b1770 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC3-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC4-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC4-1.verified.png new file mode 100644 index 0000000..32b23fb Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC4-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC5-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC5-1.verified.png new file mode 100644 index 0000000..c50e14f Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC5-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.05.verified.png new file mode 100644 index 0000000..f488222 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.1.verified.png new file mode 100644 index 0000000..f488222 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.25.verified.png new file mode 100644 index 0000000..f488222 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC6H-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.05.verified.png new file mode 100644 index 0000000..1ed46f4 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.1.verified.png new file mode 100644 index 0000000..e4d14ea Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.2.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.2.verified.png new file mode 100644 index 0000000..ed5f38f Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.2.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.25.verified.png new file mode 100644 index 0000000..ae6a0fd Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/colorpatch.png/BC7-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC1-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC1-1.verified.png new file mode 100644 index 0000000..8bdd00a Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC1-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC2-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC2-1.verified.png new file mode 100644 index 0000000..6fd7995 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC2-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC3-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC3-1.verified.png new file mode 100644 index 0000000..6fd7995 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC3-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC4-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC4-1.verified.png new file mode 100644 index 0000000..56196b7 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC4-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC5-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC5-1.verified.png new file mode 100644 index 0000000..75eced4 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC5-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.05.verified.png new file mode 100644 index 0000000..e116f34 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.1.verified.png new file mode 100644 index 0000000..e116f34 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.25.verified.png new file mode 100644 index 0000000..e116f34 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC6H-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.05.verified.png new file mode 100644 index 0000000..0c920b5 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.1.verified.png new file mode 100644 index 0000000..0c920b5 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.2.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.2.verified.png new file mode 100644 index 0000000..0c920b5 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.2.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.25.verified.png new file mode 100644 index 0000000..0f7e138 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/rainbow.png/BC7-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC1-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC1-1.verified.png new file mode 100644 index 0000000..4cc843e Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC1-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC2-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC2-1.verified.png new file mode 100644 index 0000000..c369f98 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC2-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC3-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC3-1.verified.png new file mode 100644 index 0000000..c369f98 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC3-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC4-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC4-1.verified.png new file mode 100644 index 0000000..75ba62c Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC4-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC5-1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC5-1.verified.png new file mode 100644 index 0000000..68d49c3 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC5-1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.05.verified.png new file mode 100644 index 0000000..471cc2e Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.1.verified.png new file mode 100644 index 0000000..471cc2e Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.25.verified.png new file mode 100644 index 0000000..471cc2e Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC6H-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.05.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.05.verified.png new file mode 100644 index 0000000..6f2ea0c Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.05.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.1.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.1.verified.png new file mode 100644 index 0000000..97ef422 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.1.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.2.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.2.verified.png new file mode 100644 index 0000000..3634c69 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.2.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.25.verified.png b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.25.verified.png new file mode 100644 index 0000000..03a8cc3 Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/QualityEvaluationTests/squares.png/BC7-0.25.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/Resources/README.md b/Compressonator.NET.Tests/Snapshot/Resources/README.md index 25c08e0..745844f 100644 --- a/Compressonator.NET.Tests/Snapshot/Resources/README.md +++ b/Compressonator.NET.Tests/Snapshot/Resources/README.md @@ -7,10 +7,11 @@ Renamed to keep snapshot file lengths smaller. - rainbow.png - noise.png - square.png +- colorpatch.png # Test Images - paperclips.png -The images in this archive are part of the TESTIMAGES project: http://testimages.tecnick.com +The images in this archive are part of the TESTIMAGES project: https://testimages.org/ Copyright (C) 2011-2014 Nicola Asuni - Tecnick.com LTD # Misc diff --git a/Compressonator.NET.Tests/Snapshot/Resources/colorpatch.png b/Compressonator.NET.Tests/Snapshot/Resources/colorpatch.png new file mode 100644 index 0000000..4dccb1b Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/Resources/colorpatch.png differ diff --git a/Compressonator.NET.Tests/Snapshot/RoundTrip.CompressDecompressValidate.verified.png b/Compressonator.NET.Tests/Snapshot/RoundTrip.CompressDecompressValidate.verified.png new file mode 100644 index 0000000..605bcff Binary files /dev/null and b/Compressonator.NET.Tests/Snapshot/RoundTrip.CompressDecompressValidate.verified.png differ diff --git a/Compressonator.NET.Tests/Snapshot/RoundTrip.cs b/Compressonator.NET.Tests/Snapshot/RoundTrip.cs index 2daf83e..f64a505 100644 --- a/Compressonator.NET.Tests/Snapshot/RoundTrip.cs +++ b/Compressonator.NET.Tests/Snapshot/RoundTrip.cs @@ -1,16 +1,34 @@ namespace Compressonator.NET.Tests.Snapshot; +[TestCategory("Smoke")] [TestClass] public class RoundTrip : SnapshotTestingBase { + [TestMethod] + [TestProperty("CI", "true")] public async Task BasicRoundTrip() { + // Verify that IN == OUT string sourceFile = "Resources/rainbow.png"; - var (res,set) = SnapshotUtilities.Load(sourceFile, CompressionTests.DEFAULT_SOURCE_FORMAT, CompressionTests.DEFAULT_SOURCE_FORMAT); - - Assert.AreEqual(CMP_FORMAT.RGBA_8888, set.format); + var (res,set) = SnapshotUtilities.Load(sourceFile); await SnapshotUtilities.SaveVerifyDelete(set, "png"); } + + [TestMethod] + [TestProperty("CI", "true")] + public void CompressDecompressValidate() + { + // This test is very similar to our quality evaluation tests, but its designed to be faster and easier + // Suitable for "Smoke Tests". Easier to Debug etc. + string sourceFile = "Resources/carrots.png"; + var tmpPath = CurrentFile.Relative(SnapshotUtilities.GetFileNameForTest("png")); + + CMP_Texture distortedTexture = SnapshotUtilities.RoundTripWithCompression(sourceFile, CMP_FORMAT.BC1, 0.05f); + + SnapshotUtilities.Save(tmpPath, distortedTexture); + + File.Delete(tmpPath); + } } diff --git a/Compressonator.NET.Tests/Snapshot/ScoringTests.cs b/Compressonator.NET.Tests/Snapshot/ScoringTests.cs new file mode 100644 index 0000000..f8ca895 --- /dev/null +++ b/Compressonator.NET.Tests/Snapshot/ScoringTests.cs @@ -0,0 +1,61 @@ +using Compressonator.NET.Tests.Snapshot; +using ssimulacra2.NET; + +namespace Compressonator.NET.Tests; + +/// +/// Scoring tests are designed to evaluate the compression algorithm's quality in a machine readable way and for speed. +/// They are not designed for visual inspection. See our snapshot tests, which are designed for visual inspection. +/// +[TestClass] +public class ScoringTests +{ + public static bool DISABLE_SLOW_ONES = TestUtilities.IsCI; + + // All times recorded on Prime's Main Development Machine (Intel i7-9700K CPU) + // All times vary a little by up to 5 s + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.05f, 89.43)] // 20 s + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.1f, 89.54)] // 16-20 s + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.2f, 89.58)] // 21 s + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.25f, 94.47)] //1.8 Minutes + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.3f, 94.47)] // 1.9 Minutes + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.35f, 94.51)] // 2.3 Minutes + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.4f, 94.51)] // 2.4 Minutes + [DataRow(CMP_FORMAT.BC7, "Resources/squares.png", 0.6f, 94.62)] // 8+ Minutes + + //[DataRow(CMP_FORMAT.BC7, "Resources/wings.png", 0.05f, 89)] //1.8 Minutes + //[DataRow(CMP_FORMAT.BC7, "Resources/wings.png", 0.1f, 89)] //1.8 Minutes + //[DataRow(CMP_FORMAT.BC7, "Resources/wings.png", 0.25f, 94)] //1.8 Minutes + + [DataRow(CMP_FORMAT.BC7, "Resources/colorpatch.png", 0.05f, 89)] + [DataRow(CMP_FORMAT.BC7, "Resources/colorpatch.png", 0.1f, 89)] + [DataRow(CMP_FORMAT.BC7, "Resources/colorpatch.png", 0.25f, 94)] + [TestMethod] + [TestProperty("CI", "true")] + public void TestExpectedSsimulacraScore( + CMP_FORMAT targetFormat, + string inputFileRelativePath, + float quality = 0.9f, + double threshold = 90) + { + TestUtilities.GuardCITests(targetFormat, quality); + + var originalPath = CurrentFile.Relative(inputFileRelativePath); + var distortedPath = CurrentFile.Relative(SnapshotUtilities.GetFileNameForTest("png")); + + CMP_Texture distortedTexture = SnapshotUtilities.RoundTripWithCompression(inputFileRelativePath, targetFormat, quality); + + SnapshotUtilities.Save(distortedPath, distortedTexture); + + var score = Ssimulacra2.ComputeFromFiles(originalPath, distortedPath); + + File.Delete(distortedPath); //Delete before assertions to keep "tidy" + + string displayScore = string.Format("{0:F2}", score); + + Console.WriteLine($"{displayScore} >= {threshold}"); + Console.WriteLine($"Unrounded Score: {score}"); + + Assert.IsTrue(score >= threshold, $"Expected end result to be at or above threshold. {displayScore} >= {threshold} (Unrounded Score: {score})"); + } +} diff --git a/Compressonator.NET.Tests/Snapshot/SnapshotTestingBase.cs b/Compressonator.NET.Tests/Snapshot/SnapshotTestingBase.cs index 6bc2cfa..3d0cd7d 100644 --- a/Compressonator.NET.Tests/Snapshot/SnapshotTestingBase.cs +++ b/Compressonator.NET.Tests/Snapshot/SnapshotTestingBase.cs @@ -5,12 +5,14 @@ public abstract partial class SnapshotTestingBase: VerifyBase { public bool Initialized { get; } = false; [TestMethod] + [TestProperty("CI", "false")] public Task Run() => VerifyChecks.Run(); public SnapshotTestingBase() { - FrameworkNativeMethods.CMP_InitFramework(); + if (!Initialized) + FrameworkNativeMethods.CMP_InitFramework(); Initialized = true; } } diff --git a/Compressonator.NET.Tests/Snapshot/SnapshotUtilities.cs b/Compressonator.NET.Tests/Snapshot/SnapshotUtilities.cs index 80dd3ea..1c19c8f 100644 --- a/Compressonator.NET.Tests/Snapshot/SnapshotUtilities.cs +++ b/Compressonator.NET.Tests/Snapshot/SnapshotUtilities.cs @@ -6,18 +6,20 @@ namespace Compressonator.NET.Tests.Snapshot; public static class SnapshotUtilities { + public const CMP_FORMAT DEFAULT_SOURCE_FORMAT = CMP_FORMAT.RGBA_8888; /// /// Standardized way to create the start of a compressonator chain for testing. /// /// /// - public static (CMP_ERROR, CMP_MipSet) Load(string relativePath, CMP_FORMAT targetFormat, CMP_FORMAT sourceFormat) + public static (CMP_ERROR, CMP_MipSet) Load(string relativePath, CMP_FORMAT sourceFormat = DEFAULT_SOURCE_FORMAT, int mipLevels = 3) { - Assert.IsTrue(SDK_NativeMethods.CMP_IsValidFormat(targetFormat), "Target format must be supported by native library"); Assert.IsTrue(SDK_NativeMethods.CMP_IsValidFormat(sourceFormat), "Source format must be supported by native library"); var path = CurrentFile.Relative(relativePath); CMP_MipSet mipSetIn = new(); + mipSetIn.maxMipLevels = mipLevels; + mipSetIn.mipLevels = mipLevels; Assert.IsTrue(File.Exists(path), "Input file must exist!"); @@ -30,21 +32,42 @@ public static (CMP_ERROR, CMP_MipSet) Load(string relativePath, CMP_FORMAT targe return (cmpStatus, mipSetIn); } - public static string GetFileNameForTest(string extension = "dds") + public static string GetFileNameForTest(string extension = "dds", string extraInfo = "") { - return Path.Join("Out", Path.ChangeExtension(Path.GetRandomFileName(), extension)); + var randomName = Path.GetRandomFileName(); + var path = Path.GetFileNameWithoutExtension(randomName); + var finalPath = $"{path}{extraInfo}.{extension}"; + return Path.Join("Out", finalPath); } - public static async Task SaveVerifyDelete(CMP_MipSet set, string extension = "dds", VerifySettings? settings = null) + public static async Task SaveVerifyDelete(CMP_Texture texture, string extension = "png", VerifySettings? settings = null) + { + var path = CurrentFile.Relative(GetFileNameForTest(extension)); + var cmpStatus = FrameworkNativeMethods.CMP_SaveTexture(path, texture); + Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus, "Save operation must succeed"); + + Assert.IsTrue(File.Exists(path), $"Saved file must exist:{path}"); + + await VerifyDelete(settings, path); + } + + public static async Task SaveVerifyDelete(CMP_MipSet set, string extension = "dds", VerifySettings? settings = null, bool dispose = true) { var path = CurrentFile.Relative(GetFileNameForTest(extension)); var cmpStatus = FrameworkNativeMethods.CMP_SaveTexture(path, set); - Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus, "Save operation must succeed"); + Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus, "Save operation must succeed"); Assert.IsTrue(File.Exists(path), $"Saved file must exist:{path}"); - VerifyResult? res; + if (dispose) + set.Dispose(); + + await VerifyDelete(settings, path); + } + private static async Task VerifyDelete(VerifySettings? settings, String path) + { + VerifyResult res; if (settings != null) res = await VerifyFile(path, settings); else @@ -55,6 +78,77 @@ public static async Task SaveVerifyDelete(CMP_MipSet set, string extension = "dd Assert.IsNotNull(res, "Verify result must be present"); Assert.IsNull(res.Target, "Verify result must not be error"); + return res; + } + + public static void Save(string path, CMP_Texture texture) + { + CMP_ERROR cmpStatus = FrameworkNativeMethods.CMP_SaveTexture(path, texture); + Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus, "Save operation must succeed"); + Assert.IsTrue(File.Exists(path), $"Saved file must exist: {path}"); + } + + /// + /// Takes an input file, compresses it with at . + /// Then immediately decompresses it back to the source format. + /// + /// + /// + /// + /// + public static CMP_Texture RoundTripWithCompression(string inputFileRelativePath, CMP_FORMAT targetFormat, float quality) + { + CMP_ERROR cmpStatus = CMP_ERROR.CMP_OK; + + // Load original + (CMP_ERROR res, CMP_MipSet mipSetIn) = SnapshotUtilities.Load(inputFileRelativePath); + + // Compress to target format and quality + using CMP_MipSet compressedSet = new(); + + var options = new CMP_CompressOptions() + { + destFormat = targetFormat, + sourceFormat = SnapshotUtilities.DEFAULT_SOURCE_FORMAT, + quality = quality, + encodeWidth = CMP_ComputeType.CMP_CPU, + miplevels = 0 + }; + cmpStatus = SDK_NativeMethods.CMP_ConvertMipTexture(mipSetIn, compressedSet, options); + Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus, "Compression process must succeed"); + + + // Extract mip level 0 + CMP_Texture mipZeroTexture = new(); + + cmpStatus = SDK_NativeMethods.CMP_MipSetToTexture(compressedSet, 0, mipZeroTexture); + Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus); + + // De-compress mip 0 + options = new CMP_CompressOptions() + { + destFormat = SnapshotUtilities.DEFAULT_SOURCE_FORMAT, + sourceFormat = targetFormat, + }; + + CMP_Texture decompressed = new(); + decompressed.CopyDimensionsFrom(mipZeroTexture); + decompressed.format = SnapshotUtilities.DEFAULT_SOURCE_FORMAT; + decompressed.transcodeFormat = SnapshotUtilities.DEFAULT_SOURCE_FORMAT; + decompressed.AllocateDataPointer(); + + options = new CMP_CompressOptions() + { + destFormat = SnapshotUtilities.DEFAULT_SOURCE_FORMAT, + }; + + cmpStatus = SDK_NativeMethods.CMP_ConvertTexture(mipZeroTexture, decompressed, options); + Assert.AreEqual(CMP_ERROR.CMP_OK, cmpStatus, "Decompression must succeeed"); + + mipSetIn.Dispose(); + + // Return final Decompressed Texture + return decompressed; } public static int UpdateMips(CMP_MipSet mipSetIn) diff --git a/Compressonator.NET.Tests/TestUtilities.cs b/Compressonator.NET.Tests/TestUtilities.cs new file mode 100644 index 0000000..4947e45 --- /dev/null +++ b/Compressonator.NET.Tests/TestUtilities.cs @@ -0,0 +1,34 @@ +namespace Compressonator.NET.Tests; + +public static class TestUtilities +{ + public const float SLOW_QUALITY_THRESHOLD = 0.15f; + public static bool IsCI => Environment.GetEnvironmentVariable("CI") == "true"; + + public static void GuardCITests(CMP_FORMAT targetFormat, float quality) + { + if (!IsCI) + return; + + if (targetFormat.IsSlowToCompute(quality)) + Assert.Inconclusive($"BC6H & BC7 Tests at higher qualities than {SLOW_QUALITY_THRESHOLD} are disabled in CI due to speed"); + } +} + +public static class CMPFormatExtensions +{ + public static bool IsSlowToCompute(this CMP_FORMAT format, float quality = 1f) + { + switch (format) + { + case CMP_FORMAT.BC7: + case CMP_FORMAT.BC6H: + case CMP_FORMAT.BC6H_SF: + if (quality > TestUtilities.SLOW_QUALITY_THRESHOLD) + return true; + return false; + default: + return false; + } + } +} diff --git a/Compressonator.NET/FrameworkNativeMethods.cs b/Compressonator.NET/FrameworkNativeMethods.cs index 3c0dd47..70960e5 100644 --- a/Compressonator.NET/FrameworkNativeMethods.cs +++ b/Compressonator.NET/FrameworkNativeMethods.cs @@ -22,6 +22,15 @@ public static unsafe class FrameworkNativeMethods [DllImport(LIBRARY_NAME)] public static extern CMP_ERROR CMP_SaveTexture(string destinationFile, [In][Out] CMP_MipSet mipSet); + [DllImport(LIBRARY_NAME)] + private static extern CMP_ERROR CMP_SaveTextureEx(string destinationFile, [In][Out] CMP_Texture texture); + + // Aliasesed to avoid, exposing API consumers to multiple functions here. + public static CMP_ERROR CMP_SaveTexture(string destinationFile, CMP_Texture texture) + { + return CMP_SaveTextureEx(destinationFile, texture); + } + [DllImport(LIBRARY_NAME)] public static extern CMP_ERROR CMP_ProcessTexture([In][Out] CMP_MipSet srcMipSet, [In][Out] CMP_MipSet dstMipSet, [MarshalAs(UnmanagedType.Struct)] KernelOptions kernelOptions, IntPtr feedbackProc); diff --git a/Compressonator.NET/Native/linux-x64/libCMP_Compressonator.so b/Compressonator.NET/Native/linux-x64/libCMP_Compressonator.so index 66f45ef..8af097d 100644 Binary files a/Compressonator.NET/Native/linux-x64/libCMP_Compressonator.so and b/Compressonator.NET/Native/linux-x64/libCMP_Compressonator.so differ diff --git a/Compressonator.NET/Native/linux-x64/libCMP_Framework.so b/Compressonator.NET/Native/linux-x64/libCMP_Framework.so index 639b6bf..a86789c 100644 Binary files a/Compressonator.NET/Native/linux-x64/libCMP_Framework.so and b/Compressonator.NET/Native/linux-x64/libCMP_Framework.so differ diff --git a/Compressonator.NET/Native/win-x64/CMP_Compressonator.dll b/Compressonator.NET/Native/win-x64/CMP_Compressonator.dll index 8b35f15..5b476d9 100644 Binary files a/Compressonator.NET/Native/win-x64/CMP_Compressonator.dll and b/Compressonator.NET/Native/win-x64/CMP_Compressonator.dll differ diff --git a/Compressonator.NET/Native/win-x64/CMP_Framework.dll b/Compressonator.NET/Native/win-x64/CMP_Framework.dll index e15f45c..8c3d408 100644 Binary files a/Compressonator.NET/Native/win-x64/CMP_Framework.dll and b/Compressonator.NET/Native/win-x64/CMP_Framework.dll differ diff --git a/Compressonator.NET/Native/win-x64/CMP_Framework_MD_DLL.pdb b/Compressonator.NET/Native/win-x64/CMP_Framework_MD_DLL.pdb index e3a6e50..6103312 100644 Binary files a/Compressonator.NET/Native/win-x64/CMP_Framework_MD_DLL.pdb and b/Compressonator.NET/Native/win-x64/CMP_Framework_MD_DLL.pdb differ diff --git a/Compressonator.NET/Native/win-x64/Compressonator_MD_DLL.pdb b/Compressonator.NET/Native/win-x64/Compressonator_MD_DLL.pdb index bcd61d1..2d3e975 100644 Binary files a/Compressonator.NET/Native/win-x64/Compressonator_MD_DLL.pdb and b/Compressonator.NET/Native/win-x64/Compressonator_MD_DLL.pdb differ diff --git a/Compressonator.NET/SDK_NativeMethods.cs b/Compressonator.NET/SDK_NativeMethods.cs index bc102a5..b07f369 100644 --- a/Compressonator.NET/SDK_NativeMethods.cs +++ b/Compressonator.NET/SDK_NativeMethods.cs @@ -16,8 +16,8 @@ private static extern CMP_ERROR CMP_ConvertTexture([In][Out] CMP_Texture sourceT public static CMP_ERROR CMP_ConvertTexture(CMP_Texture sourceTexture, CMP_Texture destTexture, CMP_CompressOptions options) { - var ptr = new MarshaledStruct(); - return CMP_ConvertTexture(sourceTexture, destTexture, ptr.Write(options), IntPtr.Zero); + var compressOptionsPtr = new MarshaledStruct().Write(options); + return CMP_ConvertTexture(sourceTexture, destTexture, compressOptionsPtr, IntPtr.Zero); } [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl)] @@ -33,6 +33,9 @@ public static CMP_ERROR CMP_ConvertMipTexture(CMP_MipSet mipSetIn, CMP_MipSet mi [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl)] public static extern bool CMP_IsValidFormat([In]CMP_FORMAT format); + [DllImport(LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl)] + public static extern CMP_ERROR CMP_MipSetToTexture([In][Out] CMP_MipSet mipSetIn, int mipLevel,[In][Out] CMP_Texture destTexture); + public static bool IsSupported => _isSupported.Value; static readonly Lazy _isSupported = new Lazy(() => diff --git a/Compressonator.NET/Structs/CMP_GPUDecode.cs b/Compressonator.NET/Structs/CMP_GPUDecode.cs index d0b2f59..a835144 100644 --- a/Compressonator.NET/Structs/CMP_GPUDecode.cs +++ b/Compressonator.NET/Structs/CMP_GPUDecode.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Compressonator.NET +namespace Compressonator.NET { public enum CMP_GPUDecode: uint { diff --git a/Compressonator.NET/Structs/CMP_MipSet.cs b/Compressonator.NET/Structs/CMP_MipSet.cs index d03973f..846a6af 100644 --- a/Compressonator.NET/Structs/CMP_MipSet.cs +++ b/Compressonator.NET/Structs/CMP_MipSet.cs @@ -82,16 +82,30 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - if (!disposed) - { + if (disposed) + return; + + if (data != IntPtr.Zero) // Already freed? FrameworkNativeMethods.CMP_FreeMipSet(this); - disposed = true; - } + + disposed = true; } ~CMP_MipSet() { Dispose(false); } + + public void CopyDetailsFrom(CMP_MipSet sourceSet) + { + this.format = sourceSet.format; + this.channelFormat = sourceSet.channelFormat; + + this.width = sourceSet.width; + this.height = sourceSet.height; + this.blockHeight = sourceSet.blockHeight; + this.blockWidth = sourceSet.blockWidth; + } } + } diff --git a/Compressonator.NET/Structs/CMP_Texture.cs b/Compressonator.NET/Structs/CMP_Texture.cs index 4ea64bd..ea30202 100644 --- a/Compressonator.NET/Structs/CMP_Texture.cs +++ b/Compressonator.NET/Structs/CMP_Texture.cs @@ -5,7 +5,7 @@ namespace Compressonator.NET { [StructLayout(LayoutKind.Sequential)] - public class CMP_Texture + public class CMP_Texture: IDisposable { [MarshalAs(UnmanagedType.U4)] public uint size = (uint)Marshal.SizeOf(); @@ -38,5 +38,40 @@ public void CalculateDataSize() { dataSize = SDK_NativeMethods.CMP_CalculateBufferSize(this); } + + public void AllocateDataPointer() + { + if (dataSize == 0) + CalculateDataSize(); + + data = Marshal.AllocHGlobal((int)dataSize); + } + + public void CopyDimensionsFrom(CMP_Texture sourceTexture) + { + this.width = sourceTexture.width; + this.height = sourceTexture.height; + this.blockHeight = sourceTexture.blockHeight; + this.blockWidth = sourceTexture.blockWidth; + this.blockDepth = sourceTexture.blockDepth; + this.pitch = sourceTexture.pitch; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (data != IntPtr.Zero) + Marshal.FreeHGlobal(data); + } + + ~CMP_Texture() + { + Dispose(false); + } } }