mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-12 03:22:32 +02:00
331d37be7c
- **Settings:** - (Add) Remove source file after automatic conversion (#444) - (Add) Remove source file after manual conversion (#444) - (Add) **Average resin bottle cost:** The average cost per one resin bottle of 1000ml. Used to calculate the material cost when the file lacks that information. Use 0 to disable this feature and only show the cost if file have that information. If this value is changed, you need to reload the current file to update the cost. - (Change) Move "Expand and show tool descriptions by default" to From `General` to `Tools` tab (Setting will reset to default) - **File formats:** - (Add) Property `StartingMaterialMilliliters`: Gets the starting material milliliters when the file was loaded - (Add) Property `StartingMaterialCost`: Gets the starting material cost when the file was loaded - (Add) Property `MaterialMilliliterCost`: Gets the material cost per one milliliter - (Improvement) Update `MaterialCost` when `MaterialMilliliters` changes (#449) - **Raft relief:** - (Add) Linked lines: Remove the raft, keep supports and link them with lines - (Improvement) Change the supports detection parameters to be more effective and precise on detect the starting layer - (Fix) Brightness percentage not getting updated - (Fix) Remove anti-aliased edges from Tabs - (Improvement) Core: Minor clean-up - (Fix) Issue repair error when 'Auto repair layers and issues on file load' is enabled (#446) - (Fix) UI: Selecting a object with ROI when flip is verically, will cause 1px up-shift on the preview - (Fix) macOS permission error due loss of attributes on the build script, WSL have bug with mv, using ln&rm instead
560 lines
18 KiB
C#
560 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.Threading;
|
|
using Emgu.CV;
|
|
using Emgu.CV.CvEnum;
|
|
using UVtools.Core.Extensions;
|
|
using UVtools.Core.Layers;
|
|
using UVtools.WPF.Controls;
|
|
using UVtools.WPF.Extensions;
|
|
using UVtools.WPF.Structures;
|
|
|
|
namespace UVtools.WPF.Windows;
|
|
|
|
public class BenchmarkWindow : WindowEx
|
|
{
|
|
private int _testSelectedIndex;
|
|
private string _singleThreadTdps = $"0 {RunsAbbreviation}";
|
|
private string _multiThreadTdps = $"0 {RunsAbbreviation}";
|
|
private string _devSingleThreadTdps = $"{Tests[0].DevSingleThreadResult} {RunsAbbreviation} ({SingleThreadTests} tests / {Math.Round(SingleThreadTests / Tests[0].DevSingleThreadResult, 2)}s)";
|
|
private string _devMultiThreadTdps = $"{Tests[0].DevMultiThreadResult} {RunsAbbreviation} ({MultiThreadTests} tests / {Math.Round(MultiThreadTests / Tests[0].DevMultiThreadResult, 2)}s)";
|
|
private bool _isRunning;
|
|
private string _startStopButtonText = "Start";
|
|
private const ushort SingleThreadTests = 200;
|
|
private const ushort MultiThreadTests = 5000;
|
|
|
|
public const string RunsAbbreviation = "TDPS";
|
|
public const string StressCPUTestName = "Stress CPU (Run until stop)";
|
|
|
|
private readonly Dictionary<BenchmarkResolution, Mat> Mats = new();
|
|
|
|
//private readonly RNGCryptoServiceProvider _randomProvider = new();
|
|
|
|
private CancellationTokenSource _tokenSource;
|
|
private CancellationToken _token => _tokenSource.Token;
|
|
|
|
public enum BenchmarkResolution
|
|
{
|
|
Resolution4K,
|
|
Resolution8K
|
|
}
|
|
|
|
public static BenchmarkTest[] Tests =>
|
|
new[]
|
|
{
|
|
new BenchmarkTest("CBBDLP 4K Encode", "TestCBBDLPEncode", BenchmarkResolution.Resolution4K, 108.70f, 912.41f),
|
|
new BenchmarkTest("CBBDLP 8K Encode", "TestCBBDLPEncode", BenchmarkResolution.Resolution8K, 27.47f, 226.76f),
|
|
new BenchmarkTest("CBT 4K Encode", "TestCBTEncode", BenchmarkResolution.Resolution4K, 86.96f, 782.47f),
|
|
new BenchmarkTest("CBT 8K Encode", "TestCBTEncode", BenchmarkResolution.Resolution8K, 21.86f, 196.15f),
|
|
new BenchmarkTest("PW0 4K Encode", "TestPW0Encode",BenchmarkResolution.Resolution4K, 84.03f, 886.53f),
|
|
new BenchmarkTest("PW0 8K Encode", "TestPW0Encode", BenchmarkResolution.Resolution8K, 21.05f, 221.63f),
|
|
|
|
new BenchmarkTest("PNG 4K Compress", "TestPNGCompress", BenchmarkResolution.Resolution4K, 55.25f, 501.00f),
|
|
//new BenchmarkTest("PNG 4K Decompress", "TestPNGDecompress", BenchmarkResolution.Resolution4K, 4.07f, 26.65f),
|
|
new BenchmarkTest("PNG 8K Compress", "TestPNGCompress", BenchmarkResolution.Resolution8K, 14.28f, 124.10f),
|
|
//new BenchmarkTest("PNG 8K Decompress", "TestPNGDecompress", BenchmarkResolution.Resolution8K, 4.07f, 26.65f),
|
|
|
|
new BenchmarkTest("GZip 4K Compress", "TestGZipCompress", BenchmarkResolution.Resolution4K, 169.49f, 1506.02f),
|
|
//new BenchmarkTest("GZip 4K Decompress", "TestGZipDecompress", BenchmarkResolution.Resolution4K, 4.07f, 26.65f),
|
|
new BenchmarkTest("GZip 8K Compress", "TestGZipCompress", BenchmarkResolution.Resolution8K, 45.77f, 397.47f),
|
|
//new BenchmarkTest("GZip 8K Decompress", "TestGZipDecompress", BenchmarkResolution.Resolution8K, 4.07f, 26.65f),
|
|
|
|
new BenchmarkTest("Deflate 4K Compress", "TestDeflateCompress", BenchmarkResolution.Resolution4K, 170.94f, 1592.36f),
|
|
//new BenchmarkTest("Deflate 4K Decompress", "TestDeflateDecompress", BenchmarkResolution.Resolution4K, 4.07f, 26.65f),
|
|
new BenchmarkTest("Deflate 8K Compress", "TestDeflateCompress", BenchmarkResolution.Resolution8K, 46.30f, 406.50f),
|
|
//new BenchmarkTest("Deflate 8K Decompress", "TestDeflateDecompress", BenchmarkResolution.Resolution8K, 4.07f, 26.65f),
|
|
|
|
new BenchmarkTest("LZ4 4K Compress", "TestLZ4Compress", BenchmarkResolution.Resolution4K, 665.12f, 2762.43f),
|
|
//new BenchmarkTest("LZ4 4K Decompress", "TestLZ4Decompress", BenchmarkResolution.Resolution4K, 4.07f, 26.65f),
|
|
new BenchmarkTest("LZ4 8K Compress", "TestLZ4Compress", BenchmarkResolution.Resolution8K, 148.15f, 907.44f),
|
|
//new BenchmarkTest("LZ4 8K Decompress", "TestLZ4Decompress", BenchmarkResolution.Resolution8K, 4.07f, 26.65f),
|
|
|
|
new BenchmarkTest(StressCPUTestName, "TestCBTEncode", BenchmarkResolution.Resolution4K, 0, 0),
|
|
};
|
|
|
|
public string Description => "Benchmark your machine against pre-defined tests.\n" +
|
|
"This will use all compution power avaliable, CPU will be exhausted.\n" +
|
|
"Run the test while your PC is idle or not in heavy load.\n" +
|
|
"Results are in 'tests done per second' (TDPS)\n" +
|
|
"\n" +
|
|
"To your reference you are competing against developer system:\n"+
|
|
$"CPU: {BenchmarkTest.DEVCPU}\n" +
|
|
$"RAM: {BenchmarkTest.DEVRAM}";
|
|
|
|
public int TestSelectedIndex
|
|
{
|
|
get => _testSelectedIndex;
|
|
set
|
|
{
|
|
if(!RaiseAndSetIfChanged(ref _testSelectedIndex, value)) return;
|
|
DevSingleThreadTDPS = $"{Tests[_testSelectedIndex].DevSingleThreadResult} {RunsAbbreviation} ({SingleThreadTests} tests / {Math.Round(SingleThreadTests / Tests[_testSelectedIndex].DevSingleThreadResult, 2)}s)";
|
|
DevMultiThreadTDPS = $"{Tests[_testSelectedIndex].DevMultiThreadResult} {RunsAbbreviation} ({MultiThreadTests} tests / {Math.Round(MultiThreadTests / Tests[_testSelectedIndex].DevMultiThreadResult, 2)}s)";
|
|
}
|
|
}
|
|
|
|
public string SingleThreadTDPS
|
|
{
|
|
get => _singleThreadTdps;
|
|
set => RaiseAndSetIfChanged(ref _singleThreadTdps, value);
|
|
}
|
|
|
|
public string MultiThreadTDPS
|
|
{
|
|
get => _multiThreadTdps;
|
|
set => RaiseAndSetIfChanged(ref _multiThreadTdps, value);
|
|
}
|
|
|
|
public string DevSingleThreadTDPS
|
|
{
|
|
get => _devSingleThreadTdps;
|
|
set => RaiseAndSetIfChanged(ref _devSingleThreadTdps, value);
|
|
}
|
|
|
|
public string DevMultiThreadTDPS
|
|
{
|
|
get => _devMultiThreadTdps;
|
|
set => RaiseAndSetIfChanged(ref _devMultiThreadTdps, value);
|
|
}
|
|
|
|
public string StartStopButtonText
|
|
{
|
|
get => _startStopButtonText;
|
|
set => RaiseAndSetIfChanged(ref _startStopButtonText, value);
|
|
}
|
|
|
|
public bool IsRunning
|
|
{
|
|
get => _isRunning;
|
|
set
|
|
{
|
|
if(!RaiseAndSetIfChanged(ref _isRunning, value)) return;
|
|
StartStopButtonText = _isRunning ? "Stop" : "Start";
|
|
}
|
|
}
|
|
|
|
public BenchmarkWindow()
|
|
{
|
|
InitializeComponent();
|
|
|
|
DataContext = this;
|
|
|
|
|
|
foreach (var resolution in Enum.GetValues<BenchmarkResolution>())
|
|
{
|
|
Mats.Add(resolution, GetBenchmarkMat(resolution));
|
|
}
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
}
|
|
|
|
protected override bool HandleClosing() => IsRunning;
|
|
|
|
public void StartStop()
|
|
{
|
|
if (IsRunning)
|
|
{
|
|
if (!_token.CanBeCanceled || _token.IsCancellationRequested) return;
|
|
_tokenSource.Cancel();
|
|
}
|
|
else
|
|
{
|
|
var benchmark = Tests[_testSelectedIndex];
|
|
SingleThreadTDPS = $"Running {SingleThreadTests} tests";
|
|
MultiThreadTDPS = $"Running {MultiThreadTests} tests";
|
|
|
|
_tokenSource = new CancellationTokenSource();
|
|
var theMethod = GetType().GetMethod(benchmark.FunctionName);
|
|
|
|
Task.Factory.StartNew(() =>
|
|
{
|
|
var sw = Stopwatch.StartNew();
|
|
try
|
|
{
|
|
if (benchmark.Name.Equals(StressCPUTestName))
|
|
{
|
|
while (true)
|
|
{
|
|
Parallel.For(0, MultiThreadTests, new ParallelOptions{CancellationToken = _tokenSource.Token }, i =>
|
|
{
|
|
theMethod.Invoke(this, new object[]{benchmark.Resolution});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
for (int i = 0; i < SingleThreadTests; i++)
|
|
{
|
|
if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
|
|
theMethod.Invoke(this, new object[] { benchmark.Resolution });
|
|
}
|
|
|
|
sw.Stop();
|
|
var singleMiliseconds = sw.ElapsedMilliseconds;
|
|
Dispatcher.UIThread.InvokeAsync(() => UpdateResults(true, singleMiliseconds));
|
|
|
|
if (_token.IsCancellationRequested) _token.ThrowIfCancellationRequested();
|
|
|
|
sw.Restart();
|
|
Parallel.For(0, MultiThreadTests, new ParallelOptions { CancellationToken = _tokenSource.Token }, i =>
|
|
{
|
|
theMethod.Invoke(this, new object[] { benchmark.Resolution });
|
|
});
|
|
|
|
sw.Stop();
|
|
|
|
var multiMiliseconds = sw.ElapsedMilliseconds;
|
|
Dispatcher.UIThread.InvokeAsync(() => UpdateResults(false, multiMiliseconds));
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Dispatcher.UIThread.InvokeAsync(() => this.MessageBoxError(ex.ToString(), "Error"));
|
|
|
|
}
|
|
finally
|
|
{
|
|
Dispatcher.UIThread.InvokeAsync(() => IsRunning = !IsRunning);
|
|
}
|
|
}, _token);
|
|
|
|
IsRunning = !IsRunning;
|
|
}
|
|
}
|
|
|
|
private void UpdateResults(bool isSingleThread, long milliseconds)
|
|
{
|
|
decimal seconds = Math.Round(milliseconds / 1000m, 2);
|
|
//var text = (isSingleThread ? "Single" : "Multi") + $" Thread: {Math.Round(SingleThreadTests / seconds, 2)} OPS ({seconds}s)";
|
|
|
|
if (isSingleThread)
|
|
SingleThreadTDPS = $"{Math.Round(SingleThreadTests / seconds, 2)} {RunsAbbreviation} ({SingleThreadTests} tests / {seconds}s)";
|
|
else
|
|
MultiThreadTDPS = $"{Math.Round(MultiThreadTests / seconds, 2)} {RunsAbbreviation} ({MultiThreadTests} tests / {seconds}s)";
|
|
}
|
|
|
|
#region Tests
|
|
public byte[] EncodeCbddlpImage(Mat image, byte bit = 0)
|
|
{
|
|
List<byte> rawData = new();
|
|
var span = image.GetDataByteSpan();
|
|
|
|
bool obit = false;
|
|
int rep = 0;
|
|
|
|
//ngrey:= uint16(r | g | b)
|
|
// thresholds:
|
|
// aa 1: 127
|
|
// aa 2: 255 127
|
|
// aa 4: 255 191 127 63
|
|
// aa 8: 255 223 191 159 127 95 63 31
|
|
byte threshold = (byte)(256 / 1 * bit - 1);
|
|
|
|
void AddRep()
|
|
{
|
|
if (rep <= 0) return;
|
|
|
|
byte by = (byte)rep;
|
|
|
|
if (obit)
|
|
{
|
|
by |= 0x80;
|
|
//bitsOn += uint(rep)
|
|
}
|
|
|
|
rawData.Add(by);
|
|
}
|
|
|
|
for (int pixel = 0; pixel < span.Length; pixel++)
|
|
{
|
|
var nbit = span[pixel] >= threshold;
|
|
|
|
if (nbit == obit)
|
|
{
|
|
rep++;
|
|
|
|
if (rep == 0x7d)
|
|
{
|
|
AddRep();
|
|
rep = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddRep();
|
|
obit = nbit;
|
|
rep = 1;
|
|
}
|
|
}
|
|
|
|
// Collect stragglers
|
|
AddRep();
|
|
|
|
|
|
return rawData.ToArray();
|
|
}
|
|
|
|
private byte[] EncodeCbtImage(Mat image)
|
|
{
|
|
List<byte> rawData = new();
|
|
byte color = byte.MaxValue >> 1;
|
|
uint stride = 0;
|
|
var span = image.GetDataByteSpan();
|
|
|
|
void AddRep()
|
|
{
|
|
if (stride == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (stride > 1)
|
|
{
|
|
color |= 0x80;
|
|
}
|
|
rawData.Add(color);
|
|
|
|
if (stride <= 1)
|
|
{
|
|
// no run needed
|
|
return;
|
|
}
|
|
|
|
if (stride <= 0x7f)
|
|
{
|
|
rawData.Add((byte)stride);
|
|
return;
|
|
}
|
|
|
|
if (stride <= 0x3fff)
|
|
{
|
|
rawData.Add((byte)((stride >> 8) | 0x80));
|
|
rawData.Add((byte)stride);
|
|
return;
|
|
}
|
|
|
|
if (stride <= 0x1fffff)
|
|
{
|
|
rawData.Add((byte)((stride >> 16) | 0xc0));
|
|
rawData.Add((byte)(stride >> 8));
|
|
rawData.Add((byte)stride);
|
|
return;
|
|
}
|
|
|
|
if (stride <= 0xfffffff)
|
|
{
|
|
rawData.Add((byte)((stride >> 24) | 0xe0));
|
|
rawData.Add((byte)(stride >> 16));
|
|
rawData.Add((byte)(stride >> 8));
|
|
rawData.Add((byte)stride);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
for (int pixel = 0; pixel < span.Length; pixel++)
|
|
{
|
|
var grey7 = (byte)(span[pixel] >> 1);
|
|
|
|
if (grey7 == color)
|
|
{
|
|
stride++;
|
|
}
|
|
else
|
|
{
|
|
AddRep();
|
|
color = grey7;
|
|
stride = 1;
|
|
}
|
|
}
|
|
|
|
AddRep();
|
|
|
|
|
|
return rawData.ToArray();
|
|
}
|
|
|
|
public byte[] EncodePW0Image(Mat image)
|
|
{
|
|
List<byte> rawData = new();
|
|
var span = image.GetDataByteSpan();
|
|
|
|
int lastColor = -1;
|
|
int reps = 0;
|
|
|
|
void PutReps()
|
|
{
|
|
while (reps > 0)
|
|
{
|
|
int done = reps;
|
|
|
|
if (lastColor == 0 || lastColor == 0xf)
|
|
{
|
|
if (done > 0xfff)
|
|
{
|
|
done = 0xfff;
|
|
}
|
|
//more:= []byte{ 0, 0}
|
|
//binary.BigEndian.PutUint16(more, uint16(done | (color << 12)))
|
|
|
|
//rle = append(rle, more...)
|
|
|
|
ushort more = (ushort)(done | (lastColor << 12));
|
|
rawData.Add((byte)(more >> 8));
|
|
rawData.Add((byte)more);
|
|
}
|
|
else
|
|
{
|
|
if (done > 0xf)
|
|
{
|
|
done = 0xf;
|
|
}
|
|
rawData.Add((byte)(done | lastColor << 4));
|
|
}
|
|
|
|
reps -= done;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < span.Length; i++)
|
|
{
|
|
int color = span[i] >> 4;
|
|
|
|
if (color == lastColor)
|
|
{
|
|
reps++;
|
|
}
|
|
else
|
|
{
|
|
PutReps();
|
|
lastColor = color;
|
|
reps = 1;
|
|
}
|
|
}
|
|
|
|
PutReps();
|
|
|
|
return rawData.ToArray();
|
|
}
|
|
|
|
public static Mat RandomMat(int width, int height)
|
|
{
|
|
Mat mat = new(new Size(width, height), DepthType.Cv8U, 1);
|
|
CvInvoke.Randu(mat, EmguExtensions.BlackColor, EmguExtensions.WhiteColor);
|
|
return mat;
|
|
}
|
|
|
|
public static Mat GetBenchmarkMat(BenchmarkResolution resolution = default)
|
|
{
|
|
using var stream = App.GetAsset("/Assets/benchmark.png");
|
|
var mat4K = new Mat();
|
|
CvInvoke.Imdecode(stream.ToArray(), ImreadModes.Grayscale, mat4K);
|
|
switch (resolution)
|
|
{
|
|
case BenchmarkResolution.Resolution4K:
|
|
return mat4K;
|
|
case BenchmarkResolution.Resolution8K:
|
|
var mat8K = new Mat();
|
|
CvInvoke.Repeat(mat4K, 2, 2, mat8K);
|
|
mat4K.Dispose();
|
|
return mat8K;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(resolution), resolution, null);
|
|
}
|
|
}
|
|
|
|
public void Test4KRandomCBBDLPEncode()
|
|
{
|
|
using var mat = RandomMat(3840, 2160);
|
|
EncodeCbddlpImage(mat);
|
|
}
|
|
|
|
public void Test8KRandomCBBDLPEncode()
|
|
{
|
|
using var mat = RandomMat(7680, 4320);
|
|
EncodeCbddlpImage(mat);
|
|
}
|
|
|
|
public void TestCBBDLPEncode(BenchmarkResolution resolution)
|
|
{
|
|
EncodeCbddlpImage(Mats[resolution]);
|
|
}
|
|
|
|
public void Test4KRandomCBTEncode()
|
|
{
|
|
using var mat = RandomMat(3840, 2160);
|
|
EncodeCbtImage(mat);
|
|
}
|
|
|
|
public void Test8KRandomCBTEncode()
|
|
{
|
|
using var mat = RandomMat(7680, 4320);
|
|
EncodeCbtImage(mat);
|
|
}
|
|
|
|
public void TestCBTEncode(BenchmarkResolution resolution)
|
|
{
|
|
EncodeCbtImage(Mats[resolution]);
|
|
}
|
|
|
|
public void Test4KRandomPW0Encode()
|
|
{
|
|
using var mat = RandomMat(3840, 2160);
|
|
EncodePW0Image(mat);
|
|
}
|
|
|
|
public void Test8KRandomPW0Encode()
|
|
{
|
|
using var mat = RandomMat(7680, 4320);
|
|
EncodePW0Image(mat);
|
|
}
|
|
|
|
public void TestPW0Encode(BenchmarkResolution resolution)
|
|
{
|
|
EncodePW0Image(Mats[resolution]);
|
|
}
|
|
|
|
public void TestPNGCompress(BenchmarkResolution resolution)
|
|
{
|
|
Layer.CompressMat(Mats[resolution], LayerCompressionCodec.Png);
|
|
}
|
|
|
|
public void TestPNGDecompress(BenchmarkResolution resolution)
|
|
{ }
|
|
|
|
public void TestGZipCompress(BenchmarkResolution resolution)
|
|
{
|
|
Layer.CompressMat(Mats[resolution], LayerCompressionCodec.GZip);
|
|
}
|
|
|
|
public void TestGZipDecompress(BenchmarkResolution resolution)
|
|
{ }
|
|
|
|
public void TestDeflateCompress(BenchmarkResolution resolution)
|
|
{
|
|
Layer.CompressMat(Mats[resolution], LayerCompressionCodec.Deflate);
|
|
}
|
|
|
|
public void TestDeflateDecompress(BenchmarkResolution resolution)
|
|
{ }
|
|
|
|
public void TestLZ4Compress(BenchmarkResolution resolution)
|
|
{
|
|
Layer.CompressMat(Mats[resolution], LayerCompressionCodec.Lz4);
|
|
}
|
|
|
|
public void TestLZ4Decompress(BenchmarkResolution resolution)
|
|
{ }
|
|
|
|
#endregion
|
|
} |