From 1e01c8b7c2a2737f9b1b8d7faa6fc3cf57e9201c Mon Sep 17 00:00:00 2001 From: Michael Durrant Date: Thu, 15 Jun 2023 22:38:01 -0600 Subject: [PATCH] 4x4 test passes, 5x5 test fails --- .../Graphics/AstcDecoderTests.cs | 129 ++++++++++++------ 1 file changed, 87 insertions(+), 42 deletions(-) diff --git a/src/Ryujinx.Tests/Graphics/AstcDecoderTests.cs b/src/Ryujinx.Tests/Graphics/AstcDecoderTests.cs index c19996bb5..221b3bfb9 100644 --- a/src/Ryujinx.Tests/Graphics/AstcDecoderTests.cs +++ b/src/Ryujinx.Tests/Graphics/AstcDecoderTests.cs @@ -4,6 +4,7 @@ using Microsoft.VisualBasic; using NUnit.Framework; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Texture.Astc; +using Ryujinx.HLE.HOS.Services.SurfaceFlinger; using System; using System.Collections.Generic; using System.Diagnostics; @@ -47,18 +48,71 @@ namespace Ryujinx.Tests.Graphics { _workingDir = TestContext.CurrentContext.TestDirectory; _testDataDir = Path.Join(_workingDir, "Graphics", "TestData"); - } + GraphicsConfig.EnableTextureRecompression = false; + } - [Test] - public void Simple_Test() + // public void + [TestCase(4, 4)] + [TestCase(5, 5)] + public void Paramterized_BlockSizes_Test(int blockWidth, int blockHeight) + { + var (encodedRef, decodedRef) = _getTestDataTupleFromShortname("MoreRocks", blockWidth, blockHeight); + int astcHeaderLength = 16; + + // skip the header. Decode method doesn't work without this and will return false. + var rawastc = encodedRef.Slice(astcHeaderLength, encodedRef.Length - astcHeaderLength); + + int texWidth = 256; + int texHeight = 256; + byte[] outputBuffer = Array.Empty(); + + int depth = 1; + int levels = 1; + int layers = 1; + + bool succeeded = AstcDecoder.TryDecodeToRgba8P(rawastc, blockWidth, blockHeight, texWidth, texHeight, depth, levels, layers, out outputBuffer); + + + // The decode function said it was valid data and that it could parse it. + Assert.AreEqual(true, succeeded); + // Length is the same as the one we made w/ ARM's decoder. That's good. + Assert.AreEqual(decodedRef.Length, outputBuffer.Length); + + var wordsRef = RgbaWord.FromBytes(decodedRef.ToArray()); + var wordsOut = RgbaWord.FromBytes(outputBuffer); + var wordDifferences = wordsRef.Select((x, i) => new { index = i, diff = x.Diff(wordsOut[i]) }).ToArray(); + + // BUT compression is funny. + // Calculate the byte differences. + var byteDifferences = decodedRef.ToArray().Select((x, i) => new { index = i, delta = x - outputBuffer[i] }).ToList(); + + var matchCount = byteDifferences.Count(x => x.delta == 0); + var matchPercent = ((float)matchCount / outputBuffer.Length); + + var wordUnchangedCount = wordDifferences.Count(x => x.diff.IsZero()); + var wordUnchangedPercent = (float)wordUnchangedCount / wordDifferences.Count(); + + Debug.WriteLine($"Pixel-wise comparison: {wordUnchangedPercent * 100:F4} ({wordUnchangedCount}/{wordDifferences.Length})"); + Debug.WriteLine($"Byte-wise comparison: {matchPercent * 100:F4} ({matchCount}/{byteDifferences.Count}) were same."); + + for (var threshold = 1; threshold < 16; threshold++) + { + var tc = byteDifferences.Count(x => Math.Abs(x.delta) >= threshold); + var tcp = ((float)tc / byteDifferences.Count); + Debug.WriteLine($"{tcp * 100:F4}% ({tc}/{byteDifferences.Count}) are different by at least {threshold}."); + } + + Assert.IsTrue(byteDifferences.All(x => Math.Abs(x.delta) < 2)); + } + + public void _Linear_4x4_Static_Test() { /// This test doesn't do anything particularly tricky. - GraphicsConfig.EnableTextureRecompression = false; int blockWidth = 4; int blockHeight = 4; - var (original, encodedRef, decodedRef) = _getTestDataTupleFromShortname("MoreRocks", blockWidth, blockHeight); + var (encodedRef, decodedRef) = _getTestDataTupleFromShortname("MoreRocks", blockWidth, blockHeight); int astcHeaderLength = 16; // skip the header. Decode method doesn't work without this and will return false. @@ -73,20 +127,14 @@ namespace Ryujinx.Tests.Graphics int layers = 1; bool succeeded = AstcDecoder.TryDecodeToRgba8P(rawastc, blockWidth, blockHeight, texWidth, texHeight, depth, levels, layers, out outputBuffer); - var outputPath = Path.Join(_testDataDir, "output-MoreRocks.l-4x4-100.astc.rgba8"); - - // Make sure we're clobbering the test output. - if (File.Exists(outputPath)) - File.Delete(outputPath); - File.WriteAllBytes(outputPath, outputBuffer); // The decode function said it was valid data and that it could parse it. Assert.AreEqual(true, succeeded); // Length is the same as the one we made w/ ARM's decoder. That's good. Assert.AreEqual(decodedRef.Length, outputBuffer.Length); - var wordsRef = toWords(decodedRef.ToArray()); - var wordsOut = toWords(outputBuffer); + var wordsRef = RgbaWord.FromBytes(decodedRef.ToArray()); + var wordsOut = RgbaWord.FromBytes(outputBuffer); var wordDifferences = wordsRef.Select((x, i) => new { index = i, diff = x.Diff(wordsOut[i]) }).ToArray(); // BUT compression is funny. @@ -98,9 +146,9 @@ namespace Ryujinx.Tests.Graphics var wordUnchangedCount = wordDifferences.Count(x => x.diff.IsZero()); var wordUnchangedPercent = (float)wordUnchangedCount / wordDifferences.Count(); - + Debug.WriteLine($"Pixel-wise comparison: {wordUnchangedPercent * 100:F4} ({wordUnchangedCount}/{wordDifferences.Length})"); - Debug.WriteLine($"Byte-wise comparison{matchPercent * 100:F4} ({matchCount}/{byteDifferences.Count}) were same."); + Debug.WriteLine($"Byte-wise comparison: {matchPercent * 100:F4} ({matchCount}/{byteDifferences.Count}) were same."); for (var threshold = 1; threshold< 16; threshold++) { @@ -117,16 +165,13 @@ namespace Ryujinx.Tests.Graphics /// /// /// - private (ReadOnlyMemory, ReadOnlyMemory, ReadOnlyMemory) _getTestDataTupleFromShortname(string shortName, int blockWidth, int blockHeight) + private (ReadOnlyMemory, ReadOnlyMemory) _getTestDataTupleFromShortname(string shortName, int blockWidth, int blockHeight) { - var original = _getFileDataFromPath($"{shortName}.png"); - // TODO: add brains for block sizes/etc - var encodedRef = _getFileDataFromPath($"{shortName}.l-{blockWidth}x{blockHeight}-100.astc"); // var decodedRef = _getFileDataFromPath($"{shortName}.s4x4.astc.png"); var rgba8raw = _getFileDataFromPath($"{shortName}.l-{blockWidth}x{blockHeight}-100.astc.rgba"); - return (original, encodedRef, rgba8raw); + return (encodedRef, rgba8raw); } private ReadOnlyMemory _getFileDataFromPath(string relativeFilePath) @@ -135,28 +180,6 @@ namespace Ryujinx.Tests.Graphics return File.ReadAllBytes(fullPath); } - /// - /// Return an array of RGBA words given an array of bytes. - /// - /// - /// - private RgbaWord[] toWords(byte[] rawBytes) - { - var result = new List(); - // rawbytes has to be factor-of-4-sized. - for (var i = 0; i < rawBytes.Length; i += 4) - { - result.Add(new RgbaWord() - { - r = rawBytes[i], - g = rawBytes[i + 1], - b = rawBytes[i + 2], - a = rawBytes[i + 3] - }) ; - } - return result.ToArray(); - } - private class RgbaWord { public byte r; @@ -190,6 +213,28 @@ namespace Ryujinx.Tests.Graphics a = (byte)Math.Abs(this.a - other.a) }; } + + /// + /// Return an array of RGBA words given an array of bytes. + /// + /// + /// + public static RgbaWord[] FromBytes(byte[] rawBytes) + { + var result = new List(); + // rawbytes has to be factor-of-4-sized. + for (var i = 0; i < rawBytes.Length; i += 4) + { + result.Add(new RgbaWord() + { + r = rawBytes[i], + g = rawBytes[i + 1], + b = rawBytes[i + 2], + a = rawBytes[i + 3] + }); + } + return result.ToArray(); + } } } }