Start of test and benchmark

This commit is contained in:
Simon Lübeß
2025-08-11 22:09:54 +02:00
parent 4992511112
commit fca126c960
7 changed files with 942 additions and 5 deletions
@@ -0,0 +1,401 @@
# Ignore user Settings
project_user.bon
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
@@ -0,0 +1,248 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using GlitchyEngine;
using GlitchyEngine.Core;
using GlitchyEngine.Editor;
using GlitchyEngine.Extensions;
using GlitchyEngine.Physics;
using GlitchyEngine.Serialization;
namespace TestProject;
class AClass
{
public AStruct MyStruct = new AStruct();
public bool Test;
}
public class BClass
{
public float F;
private BClass()
{
}
public BClass(float f)
{
F = f;
}
}
enum MyTestEnum
{
Case1 = 0,
[Label("What have I done!?")]
Yes = 1,
No = 17,
Maybe = 12,
OhHellNaw = 33,
[Label(null)]
___OhHellNaw2,
___OhH_e123llNaw3,
}
struct AStruct
{
public double Do = 5.5;
public string Hello = "World!";
public AStruct()
{
}
}
class SerializationTest : Entity
{
public static int StaticNumber;
public float Floaty1 = 1; // Serialized, Visible
[DontSerializeField]
public float Floaty2 = -2.5f; // NOT Serialized, Visible
[HideInEditor]
public float Floaty3 = 1; // Serialized, Hidden
[HideInEditor, DontSerializeField]
public float Floaty4 = 1; // NOT Serialized, Hidden
// "private", "protected" and "internal" work the same way
private float Floaty5 = 1; // NOT Serialized, Hidden
[SerializeField]
private float Floaty6 = 7; // Serialized, Hidden
[ShowInEditor]
private float Floaty7 = 12.4f; // Serialized, Visible
[ShowInEditor, DontSerializeField]
private float Floaty8 = 69; // NOT Serialized, Visible
public double Doubly = 1337;
public double Doubly2 = 1337.69;
[SerializeField]
public decimal Decimaly = 420.69m;
public AClass Classy = new AClass();
public AClass NullClass = null;
public MyTestEnum Enumy = MyTestEnum.Maybe;
public List<BClass> Listy = new List<BClass>()
{
new BClass(1),
new BClass(2),
new BClass(3),
};
public List<BClass> NullList = null;
public List<List<BClass>> JaggeredList = null;
public BClass SecondReference;
public int[] ArrayInts = new[] { 4, 5, 6 };
public int[] NullArray = null;
public AStruct MyStruct = new ();
public List<AStruct> StructList = new List<AStruct>()
{
new AStruct(),
new AStruct()
{
Do = 15,
Hello = "Hi"
}
};
public AStruct[] StructArray = {
new()
{
Do = 123,
Hello = "How are you doing?"
},
new()
};
public BClass[] ClassArray = {
null,
new(5),
null
};
public Entity JustAnEntity;
public Rigidbody2D RocketsRigidBody;
public Dictionary<string, int> TestDictionary = null;
public SerializationTest()
{
SecondReference = Listy[0];
ClassArray[0] = SecondReference;
}
// TODO: Dictionary mit custom serializer?
[ShowButton("Try Serialization")]
void Serialize()
{
TestDictionary = new()
{
{ "Test", 1 },
{ "Taste", 2 }
};
//try
//{
// //EntitySerializer.SerializationContext ctx = new();
// //EntitySerializer.Serialize(this, ctx);
// //string ser = ctx.ToString();
//}
//catch (Exception e)
//{
// Console.WriteLine(e);
// throw;
//}
}
protected override void OnCreate()
{
//Log.Info($"Entity: {JustAnEntity?.UUID}");
//Log.Info($"TheRocket: {TheRocket?.UUID}");
//Log.Info($"RocketsRigidBody: {RocketsRigidBody?.UUID}");
//if (TestDictionary != null)
//{
// foreach (var (key, value) in TestDictionary)
// {
// Log.Info($"Key: {key}, Value: {value}");
// }
//}
}
}
/*
Expected output:
Object 0:
Type = SerializationTest
Floaty1 (float) = 1
Floaty3 (float) = 1
Floaty7 (float) = 12
Classy (Object Reference) = (REF: Object 1)
NullClass (Object Reference) = NULL
Enumy (Enum) = MyTestEnum.Maybe
Listy (List) = (REF: List 0)
NullList (List) = NULL
SecondReference (Object Reference) = (REF: Object 2) // Notice, how we reference the same Object as Listy[0]
ArrayInts (List) = (REF: List 1)
NullArray (List) = NULL
MyStruct.Do (double) = 5.5
MyStruct.Hello (string) = "World!"
StructList (List) = (REF: List 2)
Object 1: // Classy
Type = AClass
Object 2: // Listy[0]
Type = BClass
F (float) = 1.0f
Object 3: // Listy[1]
Type = BClass
F (float) = 2.0f
Object 4: // Listy[2]
Type = BClass
F (float) = 3.0f
List 0:
ElementType = BClass
Serialization Type = Object Reference
Element 0 = (REF: Object 2)
Element 1 = (REF: Object 3)
Element 2 = (REF: Object 4)
List 1:
ElementType = int32
Serialization Type = int32
Element 0 = 4
Element 1 = 5
Element 2 = 6
List 2:
ElementType = AStruct
Serialization Type = Struct
Element 0:
Do (double) = 5.5
Hello (string) = "World!"
Element 1:
Do (double) = 15
Hello (string) = "Hi"
*/
@@ -0,0 +1,33 @@
using GlitchyEngine;
using GlitchyEngine.Math;
using System;
using System.Runtime.InteropServices;
using static GlitchyEngine.Math.Math;
namespace TestProject;
public class TestScript : Entity
{
/// <summary>
/// OnCreate is called once before the first call to OnUpdate.
/// </summary>
protected override void OnCreate()
{
}
/// <summary>
/// OnUpdate is called once every frame.
/// </summary>
protected override void OnUpdate(float deltaTime)
{
}
}
public static class Test
{
[UnmanagedCallersOnly]
public static void TestFunc()
{
}
}
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- General configuration -->
<AssemblyTitle>TestProject</AssemblyTitle>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Library</OutputType>
<Nullable>enable</Nullable>
<!-- Define the output path -->
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputPath>./.cache/bin/</OutputPath>
<!-- Generates a runtimeconfig-file and copies nuget references to the output -->
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="D:\Development\Projects\Beef\GlitchyEngine\ScriptCore\ScriptCore.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="true"/>
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<Solution>
<Project Path="TestProject.csproj"/>
<Project Path="D:\Development\Projects\Beef\GlitchyEngine\ScriptCore\ScriptCore.csproj"/>
</Solution>
@@ -0,0 +1,3 @@
{
_projectName = "TestProject"
}
@@ -1,21 +1,252 @@
using System;
using System.Linq;
using GlitchyEngine.Scripting;
using System.Diagnostics;
using System.IO;
using GlitchyEngine.World;
using System.Collections;
namespace GlitchyEngine.Test.Base;
class ScriptEngineTestBase
{
[Test]
public static void TestSomeScriptStuff()
public static void Setup()
{
Debug.WriteLine("Starting up ScriptEngine...");
Debug.WriteLine(Directory.GetCurrentDirectory(.. scope .()));
ScriptEngine.Init();
ScriptEngine.SetAppAssemblyPath(@"..\GlitchyEngine.Test\content\TestProject\.cache\bin\TestProject.dll");
}
protected static void Shutdown()
{
Debug.WriteLine("Shutting down ScriptEngine...");
ScriptEngine.Shutdown();
}
private function void LoadScriptAssemblyFunc(uint8* appData, int64 appLength, uint8* pdbData, int64 pdbLength);
private function void TestFunc();
//[Test]
public static void TestSomeScriptStuff()
{
Setup();
Scene scene = new Scene();
defer scene.ReleaseRef();
readonly Entity entity = scene.CreateEntity("Test");
let script = entity.AddComponent<ScriptComponent>();
script.ScriptClassName = "TestProject.TestScript";
Test.Assert(script.Instance == null);
scene.Start(true, true);
Test.Assert(script.Instance != null);
Test.Assert(script.Instance.IsInitialized);
Test.Assert(!script.Instance.IsCreated);
scene.Update(scope GameTime(), .Scripts);
Test.Assert(script.Instance.IsCreated);
scene.Stop();
Test.Assert(script.Instance == null);
Shutdown();
}
//[Test]
public static void BenchmarkSerializeEmptyScene()
{
Setup();
Scene scene = new Scene();
defer scene.ReleaseRef();
ScriptInstanceSerializer serializer = scope .();
scene.Start(true, true);
Stopwatch time = scope Stopwatch(startNow: true);
serializer.SerializeScriptInstances();
time.Stop();
Debug.WriteLine($"Serialized in {time.ElapsedMicroseconds}μs ({time.ElapsedMilliseconds}ms)");
scene.Stop();
Shutdown();
}
class Benchmark
{
public Action BeforeAll;
public Action BeforeRun;
public Action Run;
public Action AfterRun;
public Action AfterAll;
private TimeSpan[] _runDurations;
public void Run(int warmupRuns = 10, int runs = 100)
{
BeforeAll?.Invoke();
Stopwatch watch = scope .();
watch.Start();
for (int i < warmupRuns)
{
Debug.WriteLine($"Warmup run {i + 1}/{warmupRuns}...");
TimeSpan duration = DoRun();
Debug.WriteLine($"Warmup run {i + 1} finished after {duration.TotalMilliseconds}ms.");
}
watch.Stop();
Debug.WriteLine($"Warmup finished after {watch.Elapsed.TotalMilliseconds}ms");
_runDurations = new TimeSpan[runs];
defer delete _runDurations;
for (int i < runs)
{
Debug.WriteLine($"Starting run {i + 1}/{runs}...");
watch.Restart();
_runDurations[i] = DoRun();
watch.Stop();
Debug.WriteLine($"Run {i + 1}: {_runDurations[i]} (total: {watch.Elapsed.TotalMilliseconds}ms).");
}
TimeSpan totalRunTime = _runDurations.Sum();
TimeSpan meanRunTime = TimeSpan(totalRunTime.Ticks / runs);
double varianceMs = _runDurations.Select(scope (d) => Math.Pow((d - meanRunTime).TotalMilliseconds, 2)).Sum() / runs;
TimeSpan stdDeviation = TimeSpan.FromMilliseconds(Math.Sqrt(varianceMs));
Debug.WriteLine($"Results:");
Debug.WriteLine($"Total Time ({runs} runs): {totalRunTime.TotalMilliseconds}ms");
Debug.WriteLine($"Average run Time: {meanRunTime.TotalMilliseconds}ms");
Debug.WriteLine($"Std deviation: {stdDeviation.TotalMilliseconds}ms");
AfterAll?.Invoke();
}
private TimeSpan DoRun()
{
Stopwatch watch = scope .();
BeforeRun?.Invoke();
watch.Start();
Run.Invoke();
watch.Stop();
AfterRun?.Invoke();
return watch.Elapsed;
}
}
[Test]
public static void BenchmarkSerializer()
{
// Before benchmark
Setup();
Scene scene = new Scene();
defer scene.ReleaseRef();
ScriptInstanceSerializer serializer = null;
let benchmark = scope Benchmark();
benchmark.BeforeAll = scope [&]() => {
scene.Start(true, true);
};
benchmark.BeforeRun = scope [&]() => {
serializer = new .();
};
benchmark.Run = scope [&]() => {
serializer.SerializeScriptInstances();
};
benchmark.AfterRun = scope [&]() => {
delete serializer;
};
benchmark.AfterAll = scope [&]() => {
scene.Stop();
};
Debug.WriteLine("Benchmark empty scene:");
{
benchmark.Run();
}
List<Entity> references = new List<Entity>(1000);
defer delete references;
void BenchmarkTrivialEntity(int entityCount)
{
Debug.WriteLine($"Benchmark {entityCount} Trivial Entity:");
for (int i < entityCount)
{
readonly Entity entity = scene.CreateEntity("Test");
let script = entity.AddComponent<ScriptComponent>();
script.ScriptClassName = "TestProject.TestScript";
references.Add(entity);
}
benchmark.Run();
for (Entity e in references)
{
scene.DestroyEntity(e);
}
references.Clear();
}
BenchmarkTrivialEntity(1);
BenchmarkTrivialEntity(10);
BenchmarkTrivialEntity(100);
BenchmarkTrivialEntity(1000);
void BenchmarkLargeEntity(int entityCount)
{
Debug.WriteLine($"Benchmark {entityCount} Large Entity:");
for (int i < entityCount)
{
readonly Entity entity = scene.CreateEntity("Test");
let script = entity.AddComponent<ScriptComponent>();
script.ScriptClassName = "TestProject.SerializationTest";
references.Add(entity);
}
benchmark.Run();
for (Entity e in references)
{
scene.DestroyEntity(e);
}
references.Clear();
}
BenchmarkLargeEntity(1);
BenchmarkLargeEntity(10);
BenchmarkLargeEntity(100);
BenchmarkLargeEntity(1000);
// After Bench
Shutdown();
}
}