From 1cd2f2bbf51b7422a7f938d5a714fffa441e589f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 12 Nov 2016 13:39:25 +0100 Subject: [PATCH 01/25] Buffer64 --- .../Formats/Jpg/Components/Buffer64.cs | 404 ++++++++++++++++++ .../{MagicDCT.cs => ReferenceDCT.cs} | 61 +-- .../Formats/Jpg/Components/Span.cs | 100 +++++ .../Formats/Jpg/JpegDecoderCore.cs | 3 +- src/ImageSharp46/ImageSharp46.csproj | 4 +- tests/ImageSharp.Tests46/DctSandbox.cs | 97 ----- .../Formats/Jpg/Buffer64Tests.cs | 180 ++++++++ .../Formats/Jpg/DctTests.cs | 67 +++ .../Formats/Jpg/UtilityTestClassBase.cs | 90 ++++ .../ImageSharp.Tests46.csproj | 4 +- 10 files changed, 857 insertions(+), 153 deletions(-) create mode 100644 src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs rename src/ImageSharp46/Formats/Jpg/Components/{MagicDCT.cs => ReferenceDCT.cs} (92%) create mode 100644 src/ImageSharp46/Formats/Jpg/Components/Span.cs delete mode 100644 tests/ImageSharp.Tests46/DctSandbox.cs create mode 100644 tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs create mode 100644 tests/ImageSharp.Tests46/Formats/Jpg/DctTests.cs create mode 100644 tests/ImageSharp.Tests46/Formats/Jpg/UtilityTestClassBase.cs diff --git a/src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs b/src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs new file mode 100644 index 000000000..f3b47e3a4 --- /dev/null +++ b/src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs @@ -0,0 +1,404 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImageSharp.Formats +{ + public struct Buffer64 + { + public Vector4 V00; + public Vector4 V01; + public Vector4 V02; + public Vector4 V03; + + public Vector4 V10; + public Vector4 V11; + public Vector4 V12; + public Vector4 V13; + + public Vector4 V20; + public Vector4 V21; + public Vector4 V22; + public Vector4 V23; + + public Vector4 V30; + public Vector4 V31; + public Vector4 V32; + public Vector4 V33; + + + public const int VectorCount = 16; + public const int ScalarCount = VectorCount * 4; + + public unsafe void LoadFrom(Span source) + { + fixed (Vector4* ptr = &V00) + { + float* fp = (float*)ptr; + for (int i = 0; i < ScalarCount; i++) + { + fp[i] = source[i]; + } + } + } + + public unsafe void CopyTo(Span dest) + { + fixed (Vector4* ptr = &V00) + { + float* fp = (float*)ptr; + for (int i = 0; i < ScalarCount; i++) + { + dest[i] = fp[i]; + } + } + } + + internal unsafe void LoadFrom(Span source) + { + fixed (Vector4* ptr = &V00) + { + float* fp = (float*)ptr; + for (int i = 0; i < ScalarCount; i++) + { + fp[i] = source[i]; + } + } + } + + internal unsafe void CopyTo(Span dest) + { + fixed (Vector4* ptr = &V00) + { + float* fp = (float*)ptr; + for (int i = 0; i < ScalarCount; i++) + { + dest[i] = (int) fp[i]; + } + } + } + + public unsafe void TransposeInplace() + { + fixed (Vector4* ptr = &V00) + { + float* data = (float*) ptr; + + for (int i = 1; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < i; j++) + { + float tmp = data[i8 + j]; + data[i8 + j] = data[j * 8 + i]; + data[j * 8 + i] = tmp; + } + } + } + + } + + public unsafe void TranposeInto(ref Buffer64 destination) + { + fixed (Vector4* sPtr = &V00) + { + float* src = (float*)sPtr; + + fixed (Vector4* dPtr = &destination.V00) + { + float* dest = (float*) dPtr; + + for (int i = 0; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < 8; j++) + { + dest[j * 8 + i] = src[i8 + j]; + } + } + } + } + } + + //public struct Matrix + //{ + // public Matrix4x4 A, B, C, D; + + // public void LoadFrom(ref Buffer64 b) + // { + // fixed (Vector4*) + // } + //} + + public void TransposeIntoSafe(ref Buffer64 destination) + { + Matrix4x4 a; + + + } + + private static readonly Vector4 _c = new Vector4(0.1250f); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MultiplyAllInplace(Vector4 s) + { + V00 *= s; V01 *= s; V02 *= s; V03 *= s; + V10 *= s; V11 *= s; V12 *= s; V13 *= s; + V20 *= s; V21 *= s; V22 *= s; V23 *= s; + V30 *= s; V31 *= s; V32 *= s; V33 *= s; + } + + // ReSharper disable once InconsistentNaming + public void TransformIDCTInto(ref Buffer64 dest, ref Buffer64 temp) + { + TranposeInto(ref temp); + temp.iDCT2D8x4_LeftPart(ref dest); + temp.iDCT2D8x4_RightPart(ref dest); + + dest.TranposeInto(ref temp); + + temp.iDCT2D8x4_LeftPart(ref dest); + temp.iDCT2D8x4_RightPart(ref dest); + + dest.MultiplyAllInplace(new Vector4(0.1250f)); + } + + private static readonly Vector4 _1_175876 = new Vector4(1.175876f); + private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); + private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); + private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); + private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); + private static readonly Vector4 _0_298631 = new Vector4(0.298631f); + private static readonly Vector4 _2_053120 = new Vector4(2.053120f); + private static readonly Vector4 _3_072711 = new Vector4(3.072711f); + private static readonly Vector4 _1_501321 = new Vector4(1.501321f); + private static readonly Vector4 _0_541196 = new Vector4(0.541196f); + private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); + private static readonly Vector4 _0_765367 = new Vector4(0.765367f); + + internal void iDCT2D8x4_LeftPart(ref Buffer64 d) + { + /* + float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; + for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } + */ + /* + 0: 1.414214 + 1: 1.387040 + 2: 1.306563 + 3: + 4: 1.000000 + 5: 0.785695 + 6: + 7: 0.275899 + */ + + Vector4 my1 = V02; + Vector4 my7 = V32; + Vector4 mz0 = my1 + my7; + + Vector4 my3 = V12; + Vector4 mz2 = my3 + my7; + Vector4 my5 = V22; + Vector4 mz1 = my3 + my5; + Vector4 mz3 = my1 + my5; + + Vector4 mz4 = ((mz0 + mz1) * _1_175876); + //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; + //z4 = (z0 + z1) * r[3]; + + mz2 = mz2 * _1_961571 + mz4; + mz3 = mz3 * _0_390181 + mz4; + mz0 = mz0 * _0_899976; + mz1 = mz1 * _2_562915; + + /* + -0.899976 + -2.562915 + -1.961571 + -0.390181 + z0 = z0 * (-r[3] + r[7]); + z1 = z1 * (-r[3] - r[1]); + z2 = z2 * (-r[3] - r[5]) + z4; + z3 = z3 * (-r[3] + r[5]) + z4;*/ + + + Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; + Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; + Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; + Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; + + /* + 0.298631 + 2.053120 + 3.072711 + 1.501321 + b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; + b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; + b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; + b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; + */ + + Vector4 my2 = V10; + Vector4 my6 = V30; + mz4 = (my2 + my6) * _0_541196; + Vector4 my0 = V00; + Vector4 my4 = V20; + mz0 = my0 + my4; + mz1 = my0 - my4; + + mz2 = mz4 + my6 * _1_847759; + mz3 = mz4 + my2 * _0_765367; + + my0 = mz0 + mz3; + my3 = mz0 - mz3; + my1 = mz1 + mz2; + my2 = mz1 - mz2; + /* + 1.847759 + 0.765367 + z4 = (y[2] + y[6]) * r[6]; + z0 = y[0] + y[4]; z1 = y[0] - y[4]; + z2 = z4 - y[6] * (r[2] + r[6]); + z3 = z4 + y[2] * (r[2] - r[6]); + a0 = z0 + z3; a3 = z0 - z3; + a1 = z1 + z2; a2 = z1 - z2; + */ + + d.V00 = my0 + mb0; + d.V32 = my0 - mb0; + d.V02 = my1 + mb1; + d.V30 = my1 - mb1; + d.V10 = my2 + mb2; + d.V22 = my2 - mb2; + d.V12 = my3 + mb3; + d.V20 = my3 - mb3; + /* + x[0] = a0 + b0; x[7] = a0 - b0; + x[1] = a1 + b1; x[6] = a1 - b1; + x[2] = a2 + b2; x[5] = a2 - b2; + x[3] = a3 + b3; x[4] = a3 - b3; + for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } + */ + } + + + internal void iDCT2D8x4_RightPart(ref Buffer64 d) + { + /* + float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; + for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } + */ + /* + 0: 1.414214 + 1: 1.387040 + 2: 1.306563 + 3: + 4: 1.000000 + 5: 0.785695 + 6: + 7: 0.275899 + */ + + Vector4 my1 = V03; + Vector4 my7 = V33; + Vector4 mz0 = my1 + my7; + + Vector4 my3 = V13; + Vector4 mz2 = my3 + my7; + Vector4 my5 = V23; + Vector4 mz1 = my3 + my5; + Vector4 mz3 = my1 + my5; + + Vector4 mz4 = ((mz0 + mz1) * _1_175876); + //z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5]; + //z4 = (z0 + z1) * r[3]; + + mz2 = mz2 * _1_961571 + mz4; + mz3 = mz3 * _0_390181 + mz4; + mz0 = mz0 * _0_899976; + mz1 = mz1 * _2_562915; + + /* + -0.899976 + -2.562915 + -1.961571 + -0.390181 + z0 = z0 * (-r[3] + r[7]); + z1 = z1 * (-r[3] - r[1]); + z2 = z2 * (-r[3] - r[5]) + z4; + z3 = z3 * (-r[3] + r[5]) + z4;*/ + + + Vector4 mb3 = my7 * _0_298631 + mz0 + mz2; + Vector4 mb2 = my5 * _2_053120 + mz1 + mz3; + Vector4 mb1 = my3 * _3_072711 + mz1 + mz2; + Vector4 mb0 = my1 * _1_501321 + mz0 + mz3; + + /* + 0.298631 + 2.053120 + 3.072711 + 1.501321 + b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; + b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; + b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; + b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; + */ + + Vector4 my2 = V11; + Vector4 my6 = V31; + mz4 = (my2 + my6) * _0_541196; + Vector4 my0 = V01; + Vector4 my4 = V21; + mz0 = my0 + my4; + mz1 = my0 - my4; + + mz2 = mz4 + my6 * _1_847759; + mz3 = mz4 + my2 * _0_765367; + + my0 = mz0 + mz3; + my3 = mz0 - mz3; + my1 = mz1 + mz2; + my2 = mz1 - mz2; + /* + 1.847759 + 0.765367 + z4 = (y[2] + y[6]) * r[6]; + z0 = y[0] + y[4]; z1 = y[0] - y[4]; + z2 = z4 - y[6] * (r[2] + r[6]); + z3 = z4 + y[2] * (r[2] - r[6]); + a0 = z0 + z3; a3 = z0 - z3; + a1 = z1 + z2; a2 = z1 - z2; + */ + + d.V01 = my0 + mb0; + d.V33 = my0 - mb0; + d.V03 = my1 + mb1; + d.V31 = my1 - mb1; + d.V11 = my2 + mb2; + d.V23 = my2 - mb2; + d.V13 = my3 + mb3; + d.V21 = my3 - mb3; + /* + x[0] = a0 + b0; x[7] = a0 - b0; + x[1] = a1 + b1; x[6] = a1 - b1; + x[2] = a2 + b2; x[5] = a2 - b2; + x[3] = a3 + b3; x[4] = a3 - b3; + for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } + */ + } + + public static void SuchIDCT(ref Block block) + { + Buffer64 source = new Buffer64(); + source.LoadFrom(block.Data); + + Buffer64 dest = new Buffer64(); + Buffer64 temp = new Buffer64(); + + source.TransformIDCTInto(ref dest, ref temp); + dest.CopyTo(block.Data); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp46/Formats/Jpg/Components/MagicDCT.cs b/src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs similarity index 92% rename from src/ImageSharp46/Formats/Jpg/Components/MagicDCT.cs rename to src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs index 6c71ec92e..a83ec5d4c 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/MagicDCT.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/ReferenceDCT.cs @@ -5,59 +5,13 @@ using System.Runtime.CompilerServices; namespace ImageSharp.Formats { - public struct Span - where T : struct - { - public T[] Data; - public int Offset; - - public Span(int size, int offset = 0) - { - Data = new T[size]; - Offset = offset; - } - - public Span(T[] data, int offset = 0) - { - Data = data; - Offset = offset; - } - - public T this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Data[idx + Offset]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Data[idx + Offset] = value; } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span Slice(int offset) - { - return new Span(Data, Offset + offset); - } - - public static implicit operator Span(T[] data) => new Span(data, 0); - - private static readonly ArrayPool Pool = ArrayPool.Create(128, 10); - - public static Span RentFromPool(int size, int offset = 0) - { - return new Span(Pool.Rent(size), offset); - } - - public void ReturnToPool() - { - Pool.Return(Data, true); - Data = null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddOffset(int offset) - { - Offset += offset; - } - } - - public static class MagicDCT + /// + /// Ported from https://github.com/norishigefukushima/dct_simd + /// In this form, its Slow in C# + /// Used as a reference implementation in test cases! + /// + // ReSharper disable once InconsistentNaming + public static class ReferenceDCT { private static readonly ArrayPool FloatArrayPool = ArrayPool.Create(Block.BlockSize, 50); @@ -425,6 +379,7 @@ namespace ImageSharp.Formats 6: 7: 0.275899 */ + Vector4 my1 = _mm_load_ps(y, 8); Vector4 my7 = _mm_load_ps(y, 56); Vector4 mz0 = my1 + my7; diff --git a/src/ImageSharp46/Formats/Jpg/Components/Span.cs b/src/ImageSharp46/Formats/Jpg/Components/Span.cs new file mode 100644 index 000000000..8ef747125 --- /dev/null +++ b/src/ImageSharp46/Formats/Jpg/Components/Span.cs @@ -0,0 +1,100 @@ +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImageSharp.Formats +{ + public struct Span + { + public T[] Data; + public int Offset; + + public int TotalCount => Data.Length - Offset; + + public Span(int size, int offset = 0) + { + Data = new T[size]; + Offset = offset; + } + + public Span(T[] data, int offset = 0) + { + Data = data; + Offset = offset; + } + + public T this[int idx] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Data[idx + Offset]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { Data[idx + Offset] = value; } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(int offset) + { + return new Span(Data, Offset + offset); + } + + public static implicit operator Span(T[] data) => new Span(data, 0); + + private static readonly ArrayPool Pool = ArrayPool.Create(128, 10); + + public static Span RentFromPool(int size, int offset = 0) + { + return new Span(Pool.Rent(size), offset); + } + + public void ReturnToPool() + { + Pool.Return(Data, true); + Data = null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddOffset(int offset) + { + Offset += offset; + } + } + + public static class SpanExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SaveTo(this Span data, ref Vector4 v) + { + v.X = data[0]; + v.Y = data[1]; + v.Z = data[2]; + v.W = data[3]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SaveTo(this Span data, ref Vector4 v) + { + v.X = data[0]; + v.Y = data[1]; + v.Z = data[2]; + v.W = data[3]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LoadFrom(this Span data, ref Vector4 v) + { + data[0] = v.X; + data[1] = v.Y; + data[2] = v.Z; + data[3] = v.W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LoadFrom(this Span data, ref Vector4 v) + { + data[0] = (int)v.X; + data[1] = (int)v.Y; + data[2] = (int)v.Z; + data[3] = (int)v.W; + } + + + } +} \ No newline at end of file diff --git a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs index 2f0b22829..08368a31a 100644 --- a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs @@ -1813,7 +1813,8 @@ namespace ImageSharp.Formats //IDCT.Transform(ref b); //FloatIDCT.Transform(ref b); - MagicDCT.IDCT(ref b); + //ReferenceDCT.IDCT(ref b); + Buffer64.SuchIDCT(ref b); byte[] dst; int offset; diff --git a/src/ImageSharp46/ImageSharp46.csproj b/src/ImageSharp46/ImageSharp46.csproj index fdb1b7293..64b931c0d 100644 --- a/src/ImageSharp46/ImageSharp46.csproj +++ b/src/ImageSharp46/ImageSharp46.csproj @@ -227,6 +227,7 @@ + @@ -234,7 +235,8 @@ - + + diff --git a/tests/ImageSharp.Tests46/DctSandbox.cs b/tests/ImageSharp.Tests46/DctSandbox.cs deleted file mode 100644 index d3b1bacb2..000000000 --- a/tests/ImageSharp.Tests46/DctSandbox.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System.Numerics; -using System.Text; -using ImageSharp.Formats; -using Xunit; -using Xunit.Abstractions; - -namespace ImageSharp.Tests -{ - public class DctSandbox - { - - private ITestOutputHelper Output { get; } - - public DctSandbox(ITestOutputHelper output) - { - Output = output; - } - - private float[] CreateTestData() - { - float[] result =new float[64]; - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - result[i*8 + j] = i*10 + j; - } - } - return result; - } - - private void Print(float[] data) - { - StringBuilder bld = new StringBuilder(); - for (int i = 0; i < 8; i++) - { - for (int j = 0; j < 8; j++) - { - bld.Append($"{data[i * 8 + j],3} "); - } - bld.AppendLine(); - } - - Output.WriteLine(bld.ToString()); - } - - [Fact] - public void Mennyi() - { - Output.WriteLine(Vector.IsHardwareAccelerated.ToString()); - Output.WriteLine(Vector.Count.ToString()); - } - - [Fact] - public void CheckTestData() - { - var data = CreateTestData(); - - Print(data); - } - - [Fact] - public void Load_Store() - { - var data = CreateTestData(); - - var m = MagicDCT.Load(data, 1, 1); - m = Matrix4x4.Transpose(m); - - MagicDCT.Store(m, data, 4, 4); - - Print(data); - } - - [Fact] - public void Transpose8x8() - { - var data = CreateTestData(); - - Span result = new Span(64); - - MagicDCT.Transpose8x8(data, result); - - Print(result.Data); - } - - [Fact] - public void Transpose8x8_Inplace() - { - var data = CreateTestData(); - - MagicDCT.Transpose8x8(data); - - Print(data); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs new file mode 100644 index 000000000..be57d22d4 --- /dev/null +++ b/tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs @@ -0,0 +1,180 @@ +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using ImageSharp.Formats; +using Xunit; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.Formats.Jpg +{ + public class Buffer64Tests : UtilityTestClassBase + { + public Buffer64Tests(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData(1)] + [InlineData(1000000)] + public void Load_Store_FloatArray(int times) + { + + + float[] data = new float[Buffer64.ScalarCount]; + float[] mirror = new float[Buffer64.ScalarCount]; + + for (int i = 0; i < Buffer64.ScalarCount; i++) + { + data[i] = i; + } + Measure(times, () => + { + Buffer64 v = new Buffer64(); + v.LoadFrom(data); + v.CopyTo(mirror); + }); + + Assert.Equal(data, mirror); + PrintLinearData((Span)mirror); + } + + [Theory] + [InlineData(1)] + [InlineData(1000000)] + public void Load_Store_IntArray(int times) + { + + + int[] data = new int[Buffer64.ScalarCount]; + int[] mirror = new int[Buffer64.ScalarCount]; + + for (int i = 0; i < Buffer64.ScalarCount; i++) + { + data[i] = i; + } + Measure(times, () => + { + Buffer64 v = new Buffer64(); + v.LoadFrom(data); + v.CopyTo(mirror); + }); + + Assert.Equal(data, mirror); + PrintLinearData((Span)mirror); + } + + [Fact] + public void TransposeInplace() + { + float[] expected = Create8x8FloatData(); + ReferenceDCT.Transpose8x8(expected); + + Buffer64 buffer = new Buffer64(); + buffer.LoadFrom(Create8x8FloatData()); + + buffer.TransposeInplace(); + + float[] actual = new float[64]; + buffer.CopyTo(actual); + + Assert.Equal(expected, actual); + + } + + [Fact] + public void TransposeInto() + { + float[] expected = Create8x8FloatData(); + ReferenceDCT.Transpose8x8(expected); + + Buffer64 source = new Buffer64(); + source.LoadFrom(Create8x8FloatData()); + + Buffer64 dest = new Buffer64(); + source.TranposeInto(ref dest); + + float[] actual = new float[64]; + dest.CopyTo(actual); + + Assert.Equal(expected, actual); + + } + + [Fact] + public void iDCT2D8x4_LeftPart() + { + float[] sourceArray = Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + + ReferenceDCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); + + Buffer64 source = new Buffer64(); + source.LoadFrom(sourceArray); + + Buffer64 dest = new Buffer64(); + + source.iDCT2D8x4_LeftPart(ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + Print8x8Data(expectedDestArray); + Output.WriteLine("**************"); + Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray, actualDestArray); + } + + [Fact] + public void iDCT2D8x4_RightPart() + { + Span sourceArray = Create8x8FloatData(); + Span expectedDestArray = new float[64]; + + ReferenceDCT.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); + + Buffer64 source = new Buffer64(); + source.LoadFrom(sourceArray); + + Buffer64 dest = new Buffer64(); + + source.iDCT2D8x4_RightPart(ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + Print8x8Data(expectedDestArray); + Output.WriteLine("**************"); + Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray.Data, actualDestArray); + } + + [Fact] + public void IDCT() + { + float[] sourceArray = Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + float[] tempArray = new float[64]; + + ReferenceDCT.iDCT8x8_llm_sse(sourceArray, expectedDestArray, tempArray); + + Buffer64 source = new Buffer64(); + source.LoadFrom(sourceArray); + + Buffer64 dest = new Buffer64(); + Buffer64 tempBuffer = new Buffer64(); + + source.TransformIDCTInto(ref dest, ref tempBuffer); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + Print8x8Data(expectedDestArray); + Output.WriteLine("**************"); + Print8x8Data(actualDestArray); + Assert.Equal(expectedDestArray, actualDestArray); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/DctTests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/DctTests.cs new file mode 100644 index 000000000..a25988bc5 --- /dev/null +++ b/tests/ImageSharp.Tests46/Formats/Jpg/DctTests.cs @@ -0,0 +1,67 @@ +using System.Numerics; +using ImageSharp.Formats; +using Xunit; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.Formats.Jpg +{ + public class DctTests : UtilityTestClassBase + { + public DctTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void Mennyi() + { + Output.WriteLine(Vector.IsHardwareAccelerated.ToString()); + Output.WriteLine(Vector.Count.ToString()); + } + + [Fact] + public void CheckTestData() + { + var data = Create8x8FloatData(); + + Print8x8Data(data); + } + + [Fact] + public void Load_Store() + { + var data = Create8x8FloatData(); + + var m = ReferenceDCT.Load(data, 1, 1); + m = Matrix4x4.Transpose(m); + + ReferenceDCT.Store(m, data, 4, 4); + + Print8x8Data(data); + } + + [Fact] + public void Transpose8x8() + { + var data = Create8x8FloatData(); + + Span result = new Span(64); + + ReferenceDCT.Transpose8x8(data, result); + + Print8x8Data(result.Data); + } + + [Fact] + public void Transpose8x8_Inplace() + { + var data = Create8x8FloatData(); + + ReferenceDCT.Transpose8x8(data); + + Print8x8Data(data); + } + + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/UtilityTestClassBase.cs b/tests/ImageSharp.Tests46/Formats/Jpg/UtilityTestClassBase.cs new file mode 100644 index 000000000..1e205655d --- /dev/null +++ b/tests/ImageSharp.Tests46/Formats/Jpg/UtilityTestClassBase.cs @@ -0,0 +1,90 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using ImageSharp.Formats; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.Formats.Jpg +{ + public class UtilityTestClassBase + { + public UtilityTestClassBase(ITestOutputHelper output) + { + Output = output; + } + + protected ITestOutputHelper Output { get; } + + // ReSharper disable once InconsistentNaming + public static float[] Create8x8FloatData() + { + float[] result = new float[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = i * 10 + j; + } + } + return result; + } + + // ReSharper disable once InconsistentNaming + public static int[] Create8x8IntData() + { + int[] result = new int[64]; + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + result[i * 8 + j] = i * 10 + j; + } + } + return result; + } + + protected void Print8x8Data(Span data) => Print8x8Data(data.Data); + + protected void Print8x8Data(T[] data) + { + StringBuilder bld = new StringBuilder(); + for (int i = 0; i < 8; i++) + { + for (int j = 0; j < 8; j++) + { + bld.Append($"{data[i * 8 + j],3} "); + } + bld.AppendLine(); + } + + Output.WriteLine(bld.ToString()); + } + + protected void PrintLinearData(Span data, int count = -1) + { + if (count < 0) count = data.TotalCount; + + StringBuilder bld = new StringBuilder(); + for (int i = 0; i < count; i++) + { + bld.Append($"{data[i],3} "); + } + Output.WriteLine(bld.ToString()); + } + + protected void Measure(int times, Action action, [CallerMemberName] string operationName = null) + { + Output.WriteLine($"{operationName} X {times} ..."); + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < times; i++) + { + action(); + } + + sw.Stop(); + Output.WriteLine($"{operationName} finished in {sw.ElapsedMilliseconds} ms"); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj index ab80f3d93..07b7cf407 100644 --- a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj +++ b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj @@ -71,11 +71,13 @@ - + + + From 18bd7f5c4a30dc44885c2d99b0a47ae142735cc5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 12 Nov 2016 20:24:11 +0100 Subject: [PATCH 02/25] Buffer8x8 sandboxing --- .../Components/{Buffer64.cs => Buffer8x8.cs} | 166 ++++---- .../Formats/Jpg/JpegDecoderCore.cs | 2 +- src/ImageSharp46/ImageSharp46.csproj | 2 +- .../Formats/Jpg/Buffer64Tests.cs | 180 --------- .../Formats/Jpg/Buffer8x8Tests.cs | 379 ++++++++++++++++++ .../ImageSharp.Tests46.csproj | 2 +- 6 files changed, 470 insertions(+), 261 deletions(-) rename src/ImageSharp46/Formats/Jpg/Components/{Buffer64.cs => Buffer8x8.cs} (75%) delete mode 100644 tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs create mode 100644 tests/ImageSharp.Tests46/Formats/Jpg/Buffer8x8Tests.cs diff --git a/src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs b/src/ImageSharp46/Formats/Jpg/Components/Buffer8x8.cs similarity index 75% rename from src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs rename to src/ImageSharp46/Formats/Jpg/Components/Buffer8x8.cs index f3b47e3a4..546c712ac 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/Buffer64.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/Buffer8x8.cs @@ -4,27 +4,32 @@ using System.Runtime.CompilerServices; namespace ImageSharp.Formats { - public struct Buffer64 + // ReSharper disable once InconsistentNaming + public struct Buffer8x8 { - public Vector4 V00; - public Vector4 V01; - public Vector4 V02; - public Vector4 V03; + public Vector4 V0L; + public Vector4 V0R; - public Vector4 V10; - public Vector4 V11; - public Vector4 V12; - public Vector4 V13; + public Vector4 V1L; + public Vector4 V1R; - public Vector4 V20; - public Vector4 V21; - public Vector4 V22; - public Vector4 V23; + public Vector4 V2L; + public Vector4 V2R; - public Vector4 V30; - public Vector4 V31; - public Vector4 V32; - public Vector4 V33; + public Vector4 V3L; + public Vector4 V3R; + + public Vector4 V4L; + public Vector4 V4R; + + public Vector4 V5L; + public Vector4 V5R; + + public Vector4 V6L; + public Vector4 V6R; + + public Vector4 V7L; + public Vector4 V7R; public const int VectorCount = 16; @@ -32,7 +37,7 @@ namespace ImageSharp.Formats public unsafe void LoadFrom(Span source) { - fixed (Vector4* ptr = &V00) + fixed (Vector4* ptr = &V0L) { float* fp = (float*)ptr; for (int i = 0; i < ScalarCount; i++) @@ -44,7 +49,7 @@ namespace ImageSharp.Formats public unsafe void CopyTo(Span dest) { - fixed (Vector4* ptr = &V00) + fixed (Vector4* ptr = &V0L) { float* fp = (float*)ptr; for (int i = 0; i < ScalarCount; i++) @@ -56,7 +61,7 @@ namespace ImageSharp.Formats internal unsafe void LoadFrom(Span source) { - fixed (Vector4* ptr = &V00) + fixed (Vector4* ptr = &V0L) { float* fp = (float*)ptr; for (int i = 0; i < ScalarCount; i++) @@ -68,7 +73,7 @@ namespace ImageSharp.Formats internal unsafe void CopyTo(Span dest) { - fixed (Vector4* ptr = &V00) + fixed (Vector4* ptr = &V0L) { float* fp = (float*)ptr; for (int i = 0; i < ScalarCount; i++) @@ -80,7 +85,7 @@ namespace ImageSharp.Formats public unsafe void TransposeInplace() { - fixed (Vector4* ptr = &V00) + fixed (Vector4* ptr = &V0L) { float* data = (float*) ptr; @@ -98,13 +103,13 @@ namespace ImageSharp.Formats } - public unsafe void TranposeInto(ref Buffer64 destination) + public unsafe void TranposeInto(ref Buffer8x8 destination) { - fixed (Vector4* sPtr = &V00) + fixed (Vector4* sPtr = &V0L) { float* src = (float*)sPtr; - fixed (Vector4* dPtr = &destination.V00) + fixed (Vector4* dPtr = &destination.V0L) { float* dest = (float*) dPtr; @@ -120,21 +125,26 @@ namespace ImageSharp.Formats } } - //public struct Matrix - //{ - // public Matrix4x4 A, B, C, D; + public void TransposeIntoSafe(ref Buffer8x8 d) + { + + } - // public void LoadFrom(ref Buffer64 b) - // { - // fixed (Vector4*) - // } - //} - public void TransposeIntoSafe(ref Buffer64 destination) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void PinnedTransposeInto(Buffer8x8* sourcePtr, Buffer8x8* destPtr) { - Matrix4x4 a; - + float* src = (float*)sourcePtr; + float* dest = (float*) destPtr; + for (int i = 0; i < 8; i++) + { + int i8 = i * 8; + for (int j = 0; j < 8; j++) + { + dest[j * 8 + i] = src[i8 + j]; + } + } } private static readonly Vector4 _c = new Vector4(0.1250f); @@ -142,14 +152,14 @@ namespace ImageSharp.Formats [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyAllInplace(Vector4 s) { - V00 *= s; V01 *= s; V02 *= s; V03 *= s; - V10 *= s; V11 *= s; V12 *= s; V13 *= s; - V20 *= s; V21 *= s; V22 *= s; V23 *= s; - V30 *= s; V31 *= s; V32 *= s; V33 *= s; + V0L *= s; V0R *= s; V1L *= s; V1R *= s; + V2L *= s; V2R *= s; V3L *= s; V3R *= s; + V4L *= s; V4R *= s; V5L *= s; V5R *= s; + V6L *= s; V6R *= s; V7L *= s; V7R *= s; } // ReSharper disable once InconsistentNaming - public void TransformIDCTInto(ref Buffer64 dest, ref Buffer64 temp) + public void TransformIDCTInto(ref Buffer8x8 dest, ref Buffer8x8 temp) { TranposeInto(ref temp); temp.iDCT2D8x4_LeftPart(ref dest); @@ -176,7 +186,7 @@ namespace ImageSharp.Formats private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); private static readonly Vector4 _0_765367 = new Vector4(0.765367f); - internal void iDCT2D8x4_LeftPart(ref Buffer64 d) + internal void iDCT2D8x4_LeftPart(ref Buffer8x8 d) { /* float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; @@ -193,13 +203,13 @@ namespace ImageSharp.Formats 7: 0.275899 */ - Vector4 my1 = V02; - Vector4 my7 = V32; + Vector4 my1 = V1L; + Vector4 my7 = V7L; Vector4 mz0 = my1 + my7; - Vector4 my3 = V12; + Vector4 my3 = V3L; Vector4 mz2 = my3 + my7; - Vector4 my5 = V22; + Vector4 my5 = V5L; Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; @@ -239,11 +249,11 @@ namespace ImageSharp.Formats b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; */ - Vector4 my2 = V10; - Vector4 my6 = V30; + Vector4 my2 = V2L; + Vector4 my6 = V6L; mz4 = (my2 + my6) * _0_541196; - Vector4 my0 = V00; - Vector4 my4 = V20; + Vector4 my0 = V0L; + Vector4 my4 = V4L; mz0 = my0 + my4; mz1 = my0 - my4; @@ -265,14 +275,14 @@ namespace ImageSharp.Formats a1 = z1 + z2; a2 = z1 - z2; */ - d.V00 = my0 + mb0; - d.V32 = my0 - mb0; - d.V02 = my1 + mb1; - d.V30 = my1 - mb1; - d.V10 = my2 + mb2; - d.V22 = my2 - mb2; - d.V12 = my3 + mb3; - d.V20 = my3 - mb3; + d.V0L = my0 + mb0; + d.V7L = my0 - mb0; + d.V1L = my1 + mb1; + d.V6L = my1 - mb1; + d.V2L = my2 + mb2; + d.V5L = my2 - mb2; + d.V3L = my3 + mb3; + d.V4L = my3 - mb3; /* x[0] = a0 + b0; x[7] = a0 - b0; x[1] = a1 + b1; x[6] = a1 - b1; @@ -283,7 +293,7 @@ namespace ImageSharp.Formats } - internal void iDCT2D8x4_RightPart(ref Buffer64 d) + internal void iDCT2D8x4_RightPart(ref Buffer8x8 d) { /* float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; @@ -300,13 +310,13 @@ namespace ImageSharp.Formats 7: 0.275899 */ - Vector4 my1 = V03; - Vector4 my7 = V33; + Vector4 my1 = V1R; + Vector4 my7 = V7R; Vector4 mz0 = my1 + my7; - Vector4 my3 = V13; + Vector4 my3 = V3R; Vector4 mz2 = my3 + my7; - Vector4 my5 = V23; + Vector4 my5 = V5R; Vector4 mz1 = my3 + my5; Vector4 mz3 = my1 + my5; @@ -346,11 +356,11 @@ namespace ImageSharp.Formats b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; */ - Vector4 my2 = V11; - Vector4 my6 = V31; + Vector4 my2 = V2R; + Vector4 my6 = V6R; mz4 = (my2 + my6) * _0_541196; - Vector4 my0 = V01; - Vector4 my4 = V21; + Vector4 my0 = V0R; + Vector4 my4 = V4R; mz0 = my0 + my4; mz1 = my0 - my4; @@ -372,14 +382,14 @@ namespace ImageSharp.Formats a1 = z1 + z2; a2 = z1 - z2; */ - d.V01 = my0 + mb0; - d.V33 = my0 - mb0; - d.V03 = my1 + mb1; - d.V31 = my1 - mb1; - d.V11 = my2 + mb2; - d.V23 = my2 - mb2; - d.V13 = my3 + mb3; - d.V21 = my3 - mb3; + d.V0R = my0 + mb0; + d.V7R = my0 - mb0; + d.V1R = my1 + mb1; + d.V6R = my1 - mb1; + d.V2R = my2 + mb2; + d.V5R = my2 - mb2; + d.V3R = my3 + mb3; + d.V4R = my3 - mb3; /* x[0] = a0 + b0; x[7] = a0 - b0; x[1] = a1 + b1; x[6] = a1 - b1; @@ -391,11 +401,11 @@ namespace ImageSharp.Formats public static void SuchIDCT(ref Block block) { - Buffer64 source = new Buffer64(); + Buffer8x8 source = new Buffer8x8(); source.LoadFrom(block.Data); - Buffer64 dest = new Buffer64(); - Buffer64 temp = new Buffer64(); + Buffer8x8 dest = new Buffer8x8(); + Buffer8x8 temp = new Buffer8x8(); source.TransformIDCTInto(ref dest, ref temp); dest.CopyTo(block.Data); diff --git a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs index 08368a31a..8578a4033 100644 --- a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs @@ -1814,7 +1814,7 @@ namespace ImageSharp.Formats //IDCT.Transform(ref b); //FloatIDCT.Transform(ref b); //ReferenceDCT.IDCT(ref b); - Buffer64.SuchIDCT(ref b); + Buffer8x8.SuchIDCT(ref b); byte[] dst; int offset; diff --git a/src/ImageSharp46/ImageSharp46.csproj b/src/ImageSharp46/ImageSharp46.csproj index 64b931c0d..d783ac7e5 100644 --- a/src/ImageSharp46/ImageSharp46.csproj +++ b/src/ImageSharp46/ImageSharp46.csproj @@ -227,7 +227,7 @@ - + diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs deleted file mode 100644 index be57d22d4..000000000 --- a/tests/ImageSharp.Tests46/Formats/Jpg/Buffer64Tests.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; -using ImageSharp.Formats; -using Xunit; -using Xunit.Abstractions; - -namespace ImageSharp.Tests.Formats.Jpg -{ - public class Buffer64Tests : UtilityTestClassBase - { - public Buffer64Tests(ITestOutputHelper output) : base(output) - { - } - - [Theory] - [InlineData(1)] - [InlineData(1000000)] - public void Load_Store_FloatArray(int times) - { - - - float[] data = new float[Buffer64.ScalarCount]; - float[] mirror = new float[Buffer64.ScalarCount]; - - for (int i = 0; i < Buffer64.ScalarCount; i++) - { - data[i] = i; - } - Measure(times, () => - { - Buffer64 v = new Buffer64(); - v.LoadFrom(data); - v.CopyTo(mirror); - }); - - Assert.Equal(data, mirror); - PrintLinearData((Span)mirror); - } - - [Theory] - [InlineData(1)] - [InlineData(1000000)] - public void Load_Store_IntArray(int times) - { - - - int[] data = new int[Buffer64.ScalarCount]; - int[] mirror = new int[Buffer64.ScalarCount]; - - for (int i = 0; i < Buffer64.ScalarCount; i++) - { - data[i] = i; - } - Measure(times, () => - { - Buffer64 v = new Buffer64(); - v.LoadFrom(data); - v.CopyTo(mirror); - }); - - Assert.Equal(data, mirror); - PrintLinearData((Span)mirror); - } - - [Fact] - public void TransposeInplace() - { - float[] expected = Create8x8FloatData(); - ReferenceDCT.Transpose8x8(expected); - - Buffer64 buffer = new Buffer64(); - buffer.LoadFrom(Create8x8FloatData()); - - buffer.TransposeInplace(); - - float[] actual = new float[64]; - buffer.CopyTo(actual); - - Assert.Equal(expected, actual); - - } - - [Fact] - public void TransposeInto() - { - float[] expected = Create8x8FloatData(); - ReferenceDCT.Transpose8x8(expected); - - Buffer64 source = new Buffer64(); - source.LoadFrom(Create8x8FloatData()); - - Buffer64 dest = new Buffer64(); - source.TranposeInto(ref dest); - - float[] actual = new float[64]; - dest.CopyTo(actual); - - Assert.Equal(expected, actual); - - } - - [Fact] - public void iDCT2D8x4_LeftPart() - { - float[] sourceArray = Create8x8FloatData(); - float[] expectedDestArray = new float[64]; - - ReferenceDCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); - - Buffer64 source = new Buffer64(); - source.LoadFrom(sourceArray); - - Buffer64 dest = new Buffer64(); - - source.iDCT2D8x4_LeftPart(ref dest); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - Print8x8Data(expectedDestArray); - Output.WriteLine("**************"); - Print8x8Data(actualDestArray); - - Assert.Equal(expectedDestArray, actualDestArray); - } - - [Fact] - public void iDCT2D8x4_RightPart() - { - Span sourceArray = Create8x8FloatData(); - Span expectedDestArray = new float[64]; - - ReferenceDCT.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); - - Buffer64 source = new Buffer64(); - source.LoadFrom(sourceArray); - - Buffer64 dest = new Buffer64(); - - source.iDCT2D8x4_RightPart(ref dest); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - Print8x8Data(expectedDestArray); - Output.WriteLine("**************"); - Print8x8Data(actualDestArray); - - Assert.Equal(expectedDestArray.Data, actualDestArray); - } - - [Fact] - public void IDCT() - { - float[] sourceArray = Create8x8FloatData(); - float[] expectedDestArray = new float[64]; - float[] tempArray = new float[64]; - - ReferenceDCT.iDCT8x8_llm_sse(sourceArray, expectedDestArray, tempArray); - - Buffer64 source = new Buffer64(); - source.LoadFrom(sourceArray); - - Buffer64 dest = new Buffer64(); - Buffer64 tempBuffer = new Buffer64(); - - source.TransformIDCTInto(ref dest, ref tempBuffer); - - float[] actualDestArray = new float[64]; - dest.CopyTo(actualDestArray); - - Print8x8Data(expectedDestArray); - Output.WriteLine("**************"); - Print8x8Data(actualDestArray); - Assert.Equal(expectedDestArray, actualDestArray); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/Formats/Jpg/Buffer8x8Tests.cs b/tests/ImageSharp.Tests46/Formats/Jpg/Buffer8x8Tests.cs new file mode 100644 index 000000000..1b1508f09 --- /dev/null +++ b/tests/ImageSharp.Tests46/Formats/Jpg/Buffer8x8Tests.cs @@ -0,0 +1,379 @@ +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using ImageSharp.Formats; +using Xunit; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.Formats.Jpg +{ + // ReSharper disable once InconsistentNaming + public class Buffer8x8Tests : UtilityTestClassBase + { + public Buffer8x8Tests(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData(1)] + [InlineData(1000000)] + public void Load_Store_FloatArray(int times) + { + + + float[] data = new float[Buffer8x8.ScalarCount]; + float[] mirror = new float[Buffer8x8.ScalarCount]; + + for (int i = 0; i < Buffer8x8.ScalarCount; i++) + { + data[i] = i; + } + Measure(times, () => + { + Buffer8x8 v = new Buffer8x8(); + v.LoadFrom(data); + v.CopyTo(mirror); + }); + + Assert.Equal(data, mirror); + PrintLinearData((Span)mirror); + } + + [Theory] + [InlineData(1)] + [InlineData(1000000)] + public void Load_Store_IntArray(int times) + { + + + int[] data = new int[Buffer8x8.ScalarCount]; + int[] mirror = new int[Buffer8x8.ScalarCount]; + + for (int i = 0; i < Buffer8x8.ScalarCount; i++) + { + data[i] = i; + } + Measure(times, () => + { + Buffer8x8 v = new Buffer8x8(); + v.LoadFrom(data); + v.CopyTo(mirror); + }); + + Assert.Equal(data, mirror); + PrintLinearData((Span)mirror); + } + + [Fact] + public void TransposeInplace() + { + float[] expected = Create8x8FloatData(); + ReferenceDCT.Transpose8x8(expected); + + Buffer8x8 buffer = new Buffer8x8(); + buffer.LoadFrom(Create8x8FloatData()); + + buffer.TransposeInplace(); + + float[] actual = new float[64]; + buffer.CopyTo(actual); + + Assert.Equal(expected, actual); + } + + [Fact] + public void TransposeInto() + { + float[] expected = Create8x8FloatData(); + ReferenceDCT.Transpose8x8(expected); + + Buffer8x8 source = new Buffer8x8(); + source.LoadFrom(Create8x8FloatData()); + + Buffer8x8 dest = new Buffer8x8(); + source.TranposeInto(ref dest); + + float[] actual = new float[64]; + dest.CopyTo(actual); + + Assert.Equal(expected, actual); + } + + [Fact] + public void TransposeIntoSafe() + { + float[] expected = Create8x8FloatData(); + ReferenceDCT.Transpose8x8(expected); + + Buffer8x8 source = new Buffer8x8(); + source.LoadFrom(Create8x8FloatData()); + + Buffer8x8 dest = new Buffer8x8(); + source.TransposeIntoSafe(ref dest); + + float[] actual = new float[64]; + dest.CopyTo(actual); + + Assert.Equal(expected, actual); + } + + [Fact] + public unsafe void PinnedTransposeInto() + { + float[] expected = Create8x8FloatData(); + ReferenceDCT.Transpose8x8(expected); + + Buffer8x8 source = new Buffer8x8(); + source.LoadFrom(Create8x8FloatData()); + + Buffer8x8 dest = new Buffer8x8(); + + Buffer8x8* sPtr = &source; + Buffer8x8* dPtr = &dest; + + Buffer8x8.PinnedTransposeInto(sPtr, dPtr); + + float[] actual = new float[64]; + dest.CopyTo(actual); + + Assert.Equal(expected, actual); + } + + private class BufferHolder + { + public Buffer8x8 Buffer; + } + + [Theory] + [InlineData(1)] + [InlineData(10000000)] + public void TransposeInto_Benchmark(int times) + { + BufferHolder source = new BufferHolder(); + source.Buffer.LoadFrom(Create8x8FloatData()); + BufferHolder dest = new BufferHolder(); + + Output.WriteLine($"TransposeInto_Benchmark X {times} ..."); + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < times; i++) + { + source.Buffer.TranposeInto(ref dest.Buffer); + } + + sw.Stop(); + Output.WriteLine($"TransposeInto_Benchmark finished in {sw.ElapsedMilliseconds} ms"); + } + + [Theory] + [InlineData(1)] + [InlineData(10000000)] + public unsafe void PinnedTransposeInto_Benchmark(int times) + { + BufferHolder source = new BufferHolder(); + source.Buffer.LoadFrom(Create8x8FloatData()); + BufferHolder dest = new BufferHolder(); + + fixed (Buffer8x8* sPtr = &source.Buffer) + { + fixed (Buffer8x8* dPtr = &dest.Buffer) + { + Output.WriteLine($"PinnedTransposeInto_Benchmark X {times} ..."); + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < times; i++) + { + Buffer8x8.PinnedTransposeInto(sPtr, dPtr); + } + + sw.Stop(); + Output.WriteLine($"PinnedTransposeInto_Benchmark finished in {sw.ElapsedMilliseconds} ms"); + } + } + + } + + + [Fact] + public void iDCT2D8x4_LeftPart() + { + float[] sourceArray = Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + + ReferenceDCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); + + Buffer8x8 source = new Buffer8x8(); + source.LoadFrom(sourceArray); + + Buffer8x8 dest = new Buffer8x8(); + + source.iDCT2D8x4_LeftPart(ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + Print8x8Data(expectedDestArray); + Output.WriteLine("**************"); + Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray, actualDestArray); + } + + [Fact] + public void iDCT2D8x4_RightPart() + { + Span sourceArray = Create8x8FloatData(); + Span expectedDestArray = new float[64]; + + ReferenceDCT.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); + + Buffer8x8 source = new Buffer8x8(); + source.LoadFrom(sourceArray); + + Buffer8x8 dest = new Buffer8x8(); + + source.iDCT2D8x4_RightPart(ref dest); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + Print8x8Data(expectedDestArray); + Output.WriteLine("**************"); + Print8x8Data(actualDestArray); + + Assert.Equal(expectedDestArray.Data, actualDestArray); + } + + [Fact] + public void IDCT() + { + float[] sourceArray = Create8x8FloatData(); + float[] expectedDestArray = new float[64]; + float[] tempArray = new float[64]; + + ReferenceDCT.iDCT8x8_llm_sse(sourceArray, expectedDestArray, tempArray); + + Buffer8x8 source = new Buffer8x8(); + source.LoadFrom(sourceArray); + + Buffer8x8 dest = new Buffer8x8(); + Buffer8x8 tempBuffer = new Buffer8x8(); + + source.TransformIDCTInto(ref dest, ref tempBuffer); + + float[] actualDestArray = new float[64]; + dest.CopyTo(actualDestArray); + + Print8x8Data(expectedDestArray); + Output.WriteLine("**************"); + Print8x8Data(actualDestArray); + Assert.Equal(expectedDestArray, actualDestArray); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void TransposeMatrix(ref Matrix4x4 s, ref Matrix4x4 d) + { + d.M11 = s.M11; + d.M12 = s.M21; + d.M13 = s.M31; + d.M14 = s.M41; + d.M21 = s.M12; + d.M22 = s.M22; + d.M23 = s.M32; + d.M24 = s.M42; + d.M31 = s.M13; + d.M32 = s.M23; + d.M33 = s.M33; + d.M34 = s.M43; + d.M41 = s.M14; + d.M42 = s.M24; + d.M43 = s.M34; + d.M44 = s.M44; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void TransposeMatrixPtr(float* s, float* d) + { + for (int i = 0; i < 4; i++) + { + int i4 = i*4; + for (int j = 0; j < 4; j++) + { + d[j*4 + i] = s[i4 + j]; + } + } + } + + + + + [Theory] + [InlineData(50000000)] + public void TransposeMatrix_Custom(int times) + { + Matrix4x4 s = new Matrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + Matrix4x4 d = new Matrix4x4(); + + Output.WriteLine($"TransposeMatrix_System X {times} ..."); + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < times; i++) + { + TransposeMatrix(ref s, ref d); + } + + sw.Stop(); + Output.WriteLine($"TransposeMatrix_System finished in {sw.ElapsedMilliseconds} ms"); + + Output.WriteLine(d.ToString()); + } + + + + [Theory] + [InlineData(50000000)] + public unsafe void TransposeMatrix_System(int times) + { + Matrix4x4 s = new Matrix4x4(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); + Matrix4x4 d = new Matrix4x4(); + + Output.WriteLine($"TransposeMatrix_System X {times} ..."); + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < times; i++) + { + d = Matrix4x4.Transpose(s); + } + + sw.Stop(); + Output.WriteLine($"TransposeMatrix_System finished in {sw.ElapsedMilliseconds} ms"); + + Output.WriteLine(d.ToString()); + } + + [Theory] + [InlineData(50000000)] + public unsafe void TransposeMatrix_Ptr(int times) + { + Matrix4x4 s = new Matrix4x4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + Matrix4x4 d = new Matrix4x4(); + + float* sPtr = (float*) &s; + float* dPtr = (float*) &d; + + Output.WriteLine($"TransposeMatrix_System X {times} ..."); + Stopwatch sw = Stopwatch.StartNew(); + + for (int i = 0; i < times; i++) + { + TransposeMatrixPtr(sPtr,dPtr); + } + + sw.Stop(); + Output.WriteLine($"TransposeMatrix_System finished in {sw.ElapsedMilliseconds} ms"); + + Output.WriteLine(d.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj index 07b7cf407..7f571cd70 100644 --- a/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj +++ b/tests/ImageSharp.Tests46/ImageSharp.Tests46.csproj @@ -74,7 +74,7 @@ - + From ed155aafe25366ce0fbf370ee5ee8815f9bba989 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 13 Nov 2016 14:06:39 +0100 Subject: [PATCH 03/25] auto-generated implementation for Block8x8.Transpose() --- .../Jpg/Components/Block8x8.Generated.cs | 23 +++ .../Jpg/Components/Block8x8.Generated.tt | 46 ++++++ .../Components/{Buffer8x8.cs => Block8x8.cs} | 41 +++--- .../Formats/Jpg/JpegDecoderCore.cs | 8 +- src/ImageSharp46/ImageSharp46.csproj | 16 ++- .../{Buffer8x8Tests.cs => Block8x8Tests.cs} | 136 ++++++++++++------ .../ImageSharp.Tests46.csproj | 2 +- 7 files changed, 202 insertions(+), 70 deletions(-) create mode 100644 src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.cs create mode 100644 src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.tt rename src/ImageSharp46/Formats/Jpg/Components/{Buffer8x8.cs => Block8x8.cs} (91%) rename tests/ImageSharp.Tests46/Formats/Jpg/{Buffer8x8Tests.cs => Block8x8Tests.cs} (68%) diff --git a/src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.cs b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.cs new file mode 100644 index 000000000..38ef256e3 --- /dev/null +++ b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.cs @@ -0,0 +1,23 @@ + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImageSharp.Formats +{ + public partial struct Block8x8 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TransposeInto(ref Block8x8 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; + } + } +} diff --git a/src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.tt b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.tt new file mode 100644 index 000000000..3001c351e --- /dev/null +++ b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.Generated.tt @@ -0,0 +1,46 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace ImageSharp.Formats +{ + public partial struct Block8x8 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TransposeInto(ref Block8x8 d) + { + <# + char[] coordz = new[] {'X', 'Y', 'Z', 'W'}; + //StringBuilder bld = new StringBuilder(); + 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++) + { + char srcCoord = coordz[j % 4]; + char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; + + string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; "; + //bld.Append(expression); + Write(expression); + } + //bld.AppendLine(); + WriteLine(""); + } + PopIndent(); + //Write(bld.ToString()); + #> + } + } +} diff --git a/src/ImageSharp46/Formats/Jpg/Components/Buffer8x8.cs b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs similarity index 91% rename from src/ImageSharp46/Formats/Jpg/Components/Buffer8x8.cs rename to src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs index 546c712ac..0169dce22 100644 --- a/src/ImageSharp46/Formats/Jpg/Components/Buffer8x8.cs +++ b/src/ImageSharp46/Formats/Jpg/Components/Block8x8.cs @@ -1,11 +1,11 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +// ReSharper disable InconsistentNaming namespace ImageSharp.Formats { - // ReSharper disable once InconsistentNaming - public struct Buffer8x8 + public partial struct Block8x8 { public Vector4 V0L; public Vector4 V0R; @@ -103,7 +103,10 @@ namespace ImageSharp.Formats } - public unsafe void TranposeInto(ref Buffer8x8 destination) + /// + /// Used as a reference implementation for benchmarking + /// + internal unsafe void TransposeInto_PinningImpl(ref Block8x8 destination) { fixed (Vector4* sPtr = &V0L) { @@ -125,14 +128,10 @@ namespace ImageSharp.Formats } } - public void TransposeIntoSafe(ref Buffer8x8 d) - { - - } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void PinnedTransposeInto(Buffer8x8* sourcePtr, Buffer8x8* destPtr) + public static unsafe void TransposeInto(Block8x8* sourcePtr, Block8x8* destPtr) { float* src = (float*)sourcePtr; float* dest = (float*) destPtr; @@ -147,7 +146,7 @@ namespace ImageSharp.Formats } } - private static readonly Vector4 _c = new Vector4(0.1250f); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyAllInplace(Vector4 s) @@ -159,18 +158,18 @@ namespace ImageSharp.Formats } // ReSharper disable once InconsistentNaming - public void TransformIDCTInto(ref Buffer8x8 dest, ref Buffer8x8 temp) + public void IDCTInto(ref Block8x8 dest, ref Block8x8 temp) { - TranposeInto(ref temp); + TransposeInto(ref temp); temp.iDCT2D8x4_LeftPart(ref dest); temp.iDCT2D8x4_RightPart(ref dest); - dest.TranposeInto(ref temp); + dest.TransposeInto(ref temp); temp.iDCT2D8x4_LeftPart(ref dest); temp.iDCT2D8x4_RightPart(ref dest); - dest.MultiplyAllInplace(new Vector4(0.1250f)); + dest.MultiplyAllInplace(_0_125); } private static readonly Vector4 _1_175876 = new Vector4(1.175876f); @@ -185,8 +184,10 @@ namespace ImageSharp.Formats private static readonly Vector4 _0_541196 = new Vector4(0.541196f); private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); private static readonly Vector4 _0_765367 = new Vector4(0.765367f); + private static readonly Vector4 _0_125 = new Vector4(0.1250f); - internal void iDCT2D8x4_LeftPart(ref Buffer8x8 d) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void iDCT2D8x4_LeftPart(ref Block8x8 d) { /* float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; @@ -292,8 +293,8 @@ namespace ImageSharp.Formats */ } - - internal void iDCT2D8x4_RightPart(ref Buffer8x8 d) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void iDCT2D8x4_RightPart(ref Block8x8 d) { /* float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; @@ -401,13 +402,13 @@ namespace ImageSharp.Formats public static void SuchIDCT(ref Block block) { - Buffer8x8 source = new Buffer8x8(); + Block8x8 source = new Block8x8(); source.LoadFrom(block.Data); - Buffer8x8 dest = new Buffer8x8(); - Buffer8x8 temp = new Buffer8x8(); + Block8x8 dest = new Block8x8(); + Block8x8 temp = new Block8x8(); - source.TransformIDCTInto(ref dest, ref temp); + source.IDCTInto(ref dest, ref temp); dest.CopyTo(block.Data); } } diff --git a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs index 8578a4033..25e0a0d1b 100644 --- a/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs +++ b/src/ImageSharp46/Formats/Jpg/JpegDecoderCore.cs @@ -1814,7 +1814,7 @@ namespace ImageSharp.Formats //IDCT.Transform(ref b); //FloatIDCT.Transform(ref b); //ReferenceDCT.IDCT(ref b); - Buffer8x8.SuchIDCT(ref b); + Block8x8.SuchIDCT(ref b); byte[] dst; int offset; @@ -2014,7 +2014,7 @@ namespace ImageSharp.Formats break; } - zig = this.RefineNonZeroes(b, zig, zigEnd, val0, delta); + zig = this.RefineNonZeroes(ref b, zig, zigEnd, val0, delta); if (zig > zigEnd) { throw new ImageFormatException($"Too many coefficients {zig} > {zigEnd}"); @@ -2030,7 +2030,7 @@ namespace ImageSharp.Formats if (this.eobRun > 0) { this.eobRun--; - this.RefineNonZeroes(b, zig, zigEnd, -1, delta); + this.RefineNonZeroes(ref b, zig, zigEnd, -1, delta); } } @@ -2044,7 +2044,7 @@ namespace ImageSharp.Formats /// The non-zero entry /// The low transform offset /// The - private int RefineNonZeroes(Block b, int zig, int zigEnd, int nz, int delta) + private int RefineNonZeroes(ref Block b, int zig, int zigEnd, int nz, int delta) { for (; zig <= zigEnd; zig++) { diff --git a/src/ImageSharp46/ImageSharp46.csproj b/src/ImageSharp46/ImageSharp46.csproj index d783ac7e5..2177f3683 100644 --- a/src/ImageSharp46/ImageSharp46.csproj +++ b/src/ImageSharp46/ImageSharp46.csproj @@ -227,7 +227,12 @@ - + + + True + True + Block8x8.Generated.tt + @@ -394,6 +399,15 @@ + + + TextTemplatingFileGenerator + Block8x8.Generated.cs + + + + +