mirror of
https://github.com/godotengine/godot-benchmarks.git
synced 2026-01-06 14:10:20 +03:00
Added support for C# benchmarks (#46)
This commit is contained in:
9
Benchmark.cs
Normal file
9
Benchmark.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
public partial class Benchmark : RefCounted
|
||||||
|
{
|
||||||
|
public bool test_render_cpu = false;
|
||||||
|
public bool test_render_gpu = false;
|
||||||
|
public bool test_idle = false;
|
||||||
|
public bool test_physics = false;
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@ Thank you for your interest in contributing!
|
|||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> This project only supports Godot's `master` branch (4.0's development branch),
|
> This project only supports Godot's `master` branch (4.0's development branch),
|
||||||
> not Godot 3.x. Attempting to open this project in Godot 3.x will result in errors.
|
> not Godot 3.x.
|
||||||
|
> Attempting to open this project in Godot 3.x will result in errors.
|
||||||
|
|
||||||
## Adding new benchmarks
|
## Adding new benchmarks
|
||||||
|
|
||||||
@@ -23,14 +24,15 @@ you can also create a new folder in the repository's root folder.
|
|||||||
In the `_init()` function of your script, configure your benchmark by
|
In the `_init()` function of your script, configure your benchmark by
|
||||||
initializing any desired `Benchmark` variables to true:
|
initializing any desired `Benchmark` variables to true:
|
||||||
|
|
||||||
- **test_render_cpu:** Enable this for rendering benchmarks.
|
- `test_render_cpu`: Enable this for rendering benchmarks.
|
||||||
Leave it disabled for other benchmarks.
|
Leave it disabled for other benchmarks.
|
||||||
- **test_render_gpu:** Enable this for rendering benchmarks.
|
- `test_render_gpu`: Enable this for rendering benchmarks.
|
||||||
Leave it disabled for other benchmarks.
|
Leave it disabled for other benchmarks.
|
||||||
- **test_idle:** Enable this for non-rendering CPU-intensive benchmarks.
|
- `test_idle`: Enable this for non-rendering CPU-intensive benchmarks.
|
||||||
Leave it disabled for other benchmarks.
|
Leave it disabled for other benchmarks.
|
||||||
- **test_physics:** Enable this for physics benchmarks.
|
- `test_physics`: Enable this for physics benchmarks.
|
||||||
Leave it disabled for other benchmarks.
|
Leave it disabled for other benchmarks.
|
||||||
|
- > Leaving all of these disabled will only time the initial call to your `benchmark_` function (see below).
|
||||||
|
|
||||||
### Implement the benchmark
|
### Implement the benchmark
|
||||||
|
|
||||||
@@ -57,6 +59,9 @@ Remember to follow the
|
|||||||
when writing new scripts. Adding type hints is recommended whenever possible,
|
when writing new scripts. Adding type hints is recommended whenever possible,
|
||||||
unless you are specifically benchmarking non-typed scripts.
|
unless you are specifically benchmarking non-typed scripts.
|
||||||
|
|
||||||
|
> C# benchmark functions must begin with `Benchmark`, instead of `benchmark_`.
|
||||||
|
> For C# benchmarks to be available, you must use the .NET version of the engine.
|
||||||
|
|
||||||
### Test the benchmark
|
### Test the benchmark
|
||||||
|
|
||||||
1. Run the project in the editor.
|
1. Run the project in the editor.
|
||||||
|
|||||||
9
Godot Benchmarks.csproj
Normal file
9
Godot Benchmarks.csproj
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<Project Sdk="Godot.NET.Sdk/4.3.0-dev.2">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
|
||||||
|
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'ios' ">net8.0</TargetFramework>
|
||||||
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
<RootNamespace>GodotBenchmarks</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
19
Godot Benchmarks.sln
Normal file
19
Godot Benchmarks.sln
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 2012
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot Benchmarks", "Godot Benchmarks.csproj", "{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
ExportDebug|Any CPU = ExportDebug|Any CPU
|
||||||
|
ExportRelease|Any CPU = ExportRelease|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
|
||||||
|
{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
|
||||||
|
{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
|
||||||
|
{A7FD1DC6-AE2B-4F17-B4BA-2FD940E01B47}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -1,92 +1,76 @@
|
|||||||
extends Benchmark
|
extends Benchmark
|
||||||
|
|
||||||
const ITERATIONS = 10_000_000
|
const ITERATIONS = 10_000_000
|
||||||
const RANDOM_SEED = preload("res://main.gd").RANDOM_SEED
|
|
||||||
|
|
||||||
var rng := RandomNumberGenerator.new()
|
var rng := RandomNumberGenerator.new()
|
||||||
|
|
||||||
func benchmark_global_scope_randi() -> void:
|
func benchmark_global_scope_randi() -> void:
|
||||||
# Reset the random seed to improve reproducibility of this benchmark.
|
seed(Manager.RANDOM_SEED)
|
||||||
seed(RANDOM_SEED)
|
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
randi()
|
randi()
|
||||||
|
|
||||||
# Reset the random seed again to improve reproducibility of other benchmarks.
|
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_randi() -> void:
|
func benchmark_randi() -> void:
|
||||||
rng.seed = RANDOM_SEED
|
rng.seed = Manager.RANDOM_SEED
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
rng.randi()
|
rng.randi()
|
||||||
rng.seed = RANDOM_SEED
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_global_scope_randf() -> void:
|
func benchmark_global_scope_randf() -> void:
|
||||||
seed(RANDOM_SEED)
|
seed(Manager.RANDOM_SEED)
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
randf()
|
randf()
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_randf() -> void:
|
func benchmark_randf() -> void:
|
||||||
rng.seed = RANDOM_SEED
|
rng.seed = Manager.RANDOM_SEED
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
rng.randf()
|
rng.randf()
|
||||||
rng.seed = RANDOM_SEED
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_global_scope_randi_range() -> void:
|
func benchmark_global_scope_randi_range() -> void:
|
||||||
seed(RANDOM_SEED)
|
seed(Manager.RANDOM_SEED)
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
randi_range(1234, 5678)
|
randi_range(1234, 5678)
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_randi_range() -> void:
|
func benchmark_randi_range() -> void:
|
||||||
rng.seed = RANDOM_SEED
|
rng.seed = Manager.RANDOM_SEED
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
rng.randi_range(1234, 5678)
|
rng.randi_range(1234, 5678)
|
||||||
rng.seed = RANDOM_SEED
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_global_scope_randf_range() -> void:
|
func benchmark_global_scope_randf_range() -> void:
|
||||||
seed(RANDOM_SEED)
|
seed(Manager.RANDOM_SEED)
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
randf_range(1234.0, 5678.0)
|
randf_range(1234.0, 5678.0)
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_randf_range() -> void:
|
func benchmark_randf_range() -> void:
|
||||||
rng.seed = RANDOM_SEED
|
rng.seed = Manager.RANDOM_SEED
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
rng.randf_range(1234.0, 5678.0)
|
rng.randf_range(1234.0, 5678.0)
|
||||||
rng.seed = RANDOM_SEED
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_global_scope_randfn() -> void:
|
func benchmark_global_scope_randfn() -> void:
|
||||||
seed(RANDOM_SEED)
|
seed(Manager.RANDOM_SEED)
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
randfn(10.0, 2.0)
|
randfn(10.0, 2.0)
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_randfn() -> void:
|
func benchmark_randfn() -> void:
|
||||||
rng.seed = RANDOM_SEED
|
rng.seed = Manager.RANDOM_SEED
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
rng.randfn(10.0, 2.0)
|
rng.randfn(10.0, 2.0)
|
||||||
rng.seed = RANDOM_SEED
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_global_scope_randomize() -> void:
|
func benchmark_global_scope_randomize() -> void:
|
||||||
seed(RANDOM_SEED)
|
seed(Manager.RANDOM_SEED)
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
randomize()
|
randomize()
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
|
|
||||||
func benchmark_randomize() -> void:
|
func benchmark_randomize() -> void:
|
||||||
rng.seed = RANDOM_SEED
|
rng.seed = Manager.RANDOM_SEED
|
||||||
for i in ITERATIONS:
|
for i in ITERATIONS:
|
||||||
rng.randomize()
|
rng.randomize()
|
||||||
rng.seed = RANDOM_SEED
|
|
||||||
|
|||||||
18
benchmarks/csharp/Array.cs
Normal file
18
benchmarks/csharp/Array.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Similar to GDScript Array benchmarks, but using C# Array instead
|
||||||
|
|
||||||
|
public partial class Array : Benchmark
|
||||||
|
{
|
||||||
|
public void BenchmarkFillLoop()
|
||||||
|
{
|
||||||
|
int[] array = new int[10_000_000];
|
||||||
|
|
||||||
|
for(int i = 0; i < array.Length; i++)
|
||||||
|
{ array[i] = 1234; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkFillMethod()
|
||||||
|
{
|
||||||
|
int[] array = new int[10_000_000];
|
||||||
|
System.Array.Fill(array, 1234);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
benchmarks/csharp/Control.cs
Normal file
7
benchmarks/csharp/Control.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
public partial class Control : Benchmark
|
||||||
|
{
|
||||||
|
public void BenchmarkControl()
|
||||||
|
{
|
||||||
|
// An empty test, to act as a control
|
||||||
|
}
|
||||||
|
}
|
||||||
26
benchmarks/csharp/ForLoop.cs
Normal file
26
benchmarks/csharp/ForLoop.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Similar to GDScript for_loop benchmarks, but using C#
|
||||||
|
|
||||||
|
public partial class ForLoop : Benchmark
|
||||||
|
{
|
||||||
|
private const int ITERATIONS = 1_000_000;
|
||||||
|
|
||||||
|
private int number = 0;
|
||||||
|
|
||||||
|
public void BenchmarkLoopAdd()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{
|
||||||
|
number += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkLoopCall()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{
|
||||||
|
Function();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Function(){}
|
||||||
|
}
|
||||||
120
benchmarks/csharp/List.cs
Normal file
120
benchmarks/csharp/List.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
// Similar to GDScript Array benchmarks, but using C# List instead
|
||||||
|
|
||||||
|
public partial class List : Benchmark
|
||||||
|
{
|
||||||
|
private const int ITERATIONS = 2_000_000;
|
||||||
|
|
||||||
|
public void BenchmarkInt32List()
|
||||||
|
{
|
||||||
|
List<int> list = new List<int>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(i); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = 0; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkInt64List()
|
||||||
|
{
|
||||||
|
List<long> list = new List<long>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(i); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = 0; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkFloat32List()
|
||||||
|
{
|
||||||
|
List<float> list = new List<float>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(i); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = 0; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkFloat64List()
|
||||||
|
{
|
||||||
|
List<double> list = new List<double>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(i); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = 0; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkVector2List()
|
||||||
|
{
|
||||||
|
List<Godot.Vector2> list = new List<Godot.Vector2>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(new Godot.Vector2(i, i)); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = Godot.Vector2.Zero; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkVector3List()
|
||||||
|
{
|
||||||
|
List<Godot.Vector3> list = new List<Godot.Vector3>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(new Godot.Vector3(i, i, i)); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = Godot.Vector3.Zero; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkColorList()
|
||||||
|
{
|
||||||
|
List<Godot.Color> list = new List<Godot.Color>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add(new Godot.Color(i, i, i, 1.0f)); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = Godot.Colors.Black; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BenchmarkStringList()
|
||||||
|
{
|
||||||
|
List<string> list = new List<string>();
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.Add("Godot " + i.ToString()); }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list[i] = ""; }
|
||||||
|
|
||||||
|
for(int i = 0; i < ITERATIONS; i++)
|
||||||
|
{ list.RemoveAt(list.Count - 1); }
|
||||||
|
}
|
||||||
|
}
|
||||||
128
benchmarks/csharp/physics/RigidBody2D.cs
Normal file
128
benchmarks/csharp/physics/RigidBody2D.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
// Identical benchmark to "rigid_body_2d.gd" but written in C#
|
||||||
|
|
||||||
|
public partial class RigidBody2D : Benchmark
|
||||||
|
{
|
||||||
|
const bool VISUALIZE = true;
|
||||||
|
const double SPREAD_H = 1600.0f;
|
||||||
|
const double SPREAD_V = 800.0f;
|
||||||
|
|
||||||
|
private WorldBoundaryShape2D boundary_shape = new WorldBoundaryShape2D();
|
||||||
|
private RectangleShape2D SquareShape = new RectangleShape2D();
|
||||||
|
private CircleShape2D CircleShape = new CircleShape2D();
|
||||||
|
private QuadMesh SquareMesh = new QuadMesh();
|
||||||
|
private SphereMesh CircleMesh = new SphereMesh();
|
||||||
|
|
||||||
|
public RigidBody2D()
|
||||||
|
{
|
||||||
|
SquareMesh.Size = new Vector2(20.0f, 20.0f);
|
||||||
|
CircleMesh.Radius = 10.0f;
|
||||||
|
CircleMesh.Height = 20.0f;
|
||||||
|
test_physics = true;
|
||||||
|
test_idle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D SetupScene(Func<bool, Godot.RigidBody2D.CcdMode, Godot.RigidBody2D> create_body_func, bool unique_shape, Godot.RigidBody2D.CcdMode ccd_mode, bool boundary, int num_shapes)
|
||||||
|
{
|
||||||
|
Node2D scene_root = new Node2D();
|
||||||
|
|
||||||
|
if(VISUALIZE)
|
||||||
|
{
|
||||||
|
Camera2D camera = new Camera2D() { Position = new Vector2(0.0f, -100.0f), Zoom = new Vector2(0.5f, 0.5f) };
|
||||||
|
scene_root.AddChild(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundary)
|
||||||
|
{
|
||||||
|
StaticBody2D pit = new StaticBody2D();
|
||||||
|
pit.AddChild(CreateWall(new Vector2((float)SPREAD_H, 0.0f), -0.1f));
|
||||||
|
pit.AddChild(CreateWall(new Vector2(-(float)SPREAD_H, 0.0f), 0.1f));
|
||||||
|
scene_root.AddChild(pit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < num_shapes; i++)
|
||||||
|
{
|
||||||
|
Godot.RigidBody2D body = create_body_func(unique_shape, ccd_mode);
|
||||||
|
body.Position = new Vector2((float)GD.RandRange(-SPREAD_H, SPREAD_H), (float)GD.RandRange(0.0d, -SPREAD_V));
|
||||||
|
scene_root.AddChild(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scene_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollisionShape2D CreateWall(Vector2 position, float rotation)
|
||||||
|
{
|
||||||
|
return new CollisionShape2D() {Shape = boundary_shape, Position = position, Rotation = rotation };
|
||||||
|
}
|
||||||
|
|
||||||
|
public Godot.RigidBody2D CreateRandomShape(bool unique_shape, Godot.RigidBody2D.CcdMode ccd_mode)
|
||||||
|
{
|
||||||
|
switch(GD.RandRange(0,1))
|
||||||
|
{
|
||||||
|
case 0: return CreateSquare(unique_shape, ccd_mode);
|
||||||
|
default: return CreateCircle(unique_shape, ccd_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Godot.RigidBody2D CreateSquare(bool unique_shape, Godot.RigidBody2D.CcdMode ccd_mode)
|
||||||
|
{
|
||||||
|
Godot.RigidBody2D rigid_body = new Godot.RigidBody2D();
|
||||||
|
CollisionShape2D collision_shape = new CollisionShape2D();
|
||||||
|
rigid_body.ContinuousCd = ccd_mode;
|
||||||
|
|
||||||
|
if(VISUALIZE) { rigid_body.AddChild(new MeshInstance2D(){ Mesh = SquareMesh }); }
|
||||||
|
|
||||||
|
if (unique_shape) { collision_shape.Shape = new RectangleShape2D(); }
|
||||||
|
else { collision_shape.Shape = SquareShape; }
|
||||||
|
|
||||||
|
rigid_body.AddChild(collision_shape);
|
||||||
|
return rigid_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Godot.RigidBody2D CreateCircle(bool unique_shape, Godot.RigidBody2D.CcdMode ccd_mode)
|
||||||
|
{
|
||||||
|
Godot.RigidBody2D rigid_body = new Godot.RigidBody2D();
|
||||||
|
CollisionShape2D collision_shape = new CollisionShape2D();
|
||||||
|
rigid_body.ContinuousCd = ccd_mode;
|
||||||
|
|
||||||
|
if(VISUALIZE) { rigid_body.AddChild(new MeshInstance2D(){ Mesh = CircleMesh }); }
|
||||||
|
|
||||||
|
if (unique_shape) { collision_shape.Shape = new CircleShape2D(); }
|
||||||
|
else { collision_shape.Shape = CircleShape; }
|
||||||
|
|
||||||
|
rigid_body.AddChild(collision_shape);
|
||||||
|
return rigid_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D Benchmark2000RigidBody2DSquares()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateSquare, false, Godot.RigidBody2D.CcdMode.Disabled, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D Benchmark2000RigidBody2DCircles()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateCircle, false, Godot.RigidBody2D.CcdMode.Disabled, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D Benchmark2000RigidBody2DMixed()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, false, Godot.RigidBody2D.CcdMode.Disabled, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D Benchmark2000RigidBody2DUnique()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, true, Godot.RigidBody2D.CcdMode.Disabled, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D Benchmark2000RigidBody2DContinuous()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, false, Godot.RigidBody2D.CcdMode.CastShape, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node2D Benchmark2000RigidBody2DUnbound()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, false, Godot.RigidBody2D.CcdMode.Disabled, false, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
benchmarks/csharp/physics/RigidBody3D.cs
Normal file
128
benchmarks/csharp/physics/RigidBody3D.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
// Identical benchmark to "rigid_body_3d.gd" but written in C#
|
||||||
|
|
||||||
|
public partial class RigidBody3D : Benchmark
|
||||||
|
{
|
||||||
|
const bool VISUALIZE = true;
|
||||||
|
const double SPREAD_H = 20.0f;
|
||||||
|
const double SPREAD_V = 10.0f;
|
||||||
|
|
||||||
|
private WorldBoundaryShape3D boundary_shape = new WorldBoundaryShape3D();
|
||||||
|
private BoxShape3D BoxShape = new BoxShape3D();
|
||||||
|
private SphereShape3D SphereShape = new SphereShape3D();
|
||||||
|
private BoxMesh BoxMesh = new BoxMesh();
|
||||||
|
private SphereMesh SphereMesh = new SphereMesh();
|
||||||
|
|
||||||
|
public RigidBody3D()
|
||||||
|
{
|
||||||
|
test_physics = true;
|
||||||
|
test_idle = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D SetupScene(Func<bool, bool, Godot.RigidBody3D> create_body_func, bool unique_shape, bool ccd_mode, bool boundary, int num_shapes)
|
||||||
|
{
|
||||||
|
Node3D scene_root = new Node3D();
|
||||||
|
|
||||||
|
if(VISUALIZE)
|
||||||
|
{
|
||||||
|
Camera3D camera = new Camera3D() { Position = new Vector3(0.0f, 20.0f, 20.0f) };
|
||||||
|
camera.RotateX(-0.8f);
|
||||||
|
scene_root.AddChild(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundary)
|
||||||
|
{
|
||||||
|
StaticBody3D pit = new StaticBody3D();
|
||||||
|
pit.AddChild(CreateWall(new Vector3((float)SPREAD_H, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 0.2f)));
|
||||||
|
pit.AddChild(CreateWall(new Vector3(0.0f, 0.0f, (float)SPREAD_H), new Vector3(-0.2f, 0.0f, 0.0f)));
|
||||||
|
pit.AddChild(CreateWall(new Vector3(-(float)SPREAD_H, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, -0.2f)));
|
||||||
|
pit.AddChild(CreateWall(new Vector3(0.0f, 0.0f, -(float)SPREAD_H), new Vector3(0.2f, 0.0f, 0.0f)));
|
||||||
|
scene_root.AddChild(pit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < num_shapes; i++)
|
||||||
|
{
|
||||||
|
Godot.RigidBody3D body = create_body_func(unique_shape, ccd_mode);
|
||||||
|
body.Position = new Vector3((float)GD.RandRange(-SPREAD_H, SPREAD_H), (float)GD.RandRange(0.0d, SPREAD_V), (float)GD.RandRange(-SPREAD_H, SPREAD_H));
|
||||||
|
scene_root.AddChild(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scene_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollisionShape3D CreateWall(Vector3 position, Vector3 rotation)
|
||||||
|
{
|
||||||
|
return new CollisionShape3D() {Shape = boundary_shape, Position = position, Rotation = rotation };
|
||||||
|
}
|
||||||
|
|
||||||
|
public Godot.RigidBody3D CreateRandomShape(bool unique_shape, bool ccd_mode)
|
||||||
|
{
|
||||||
|
switch(GD.RandRange(0,1))
|
||||||
|
{
|
||||||
|
case 0: return CreateBox(unique_shape, ccd_mode);
|
||||||
|
default: return CreateSphere(unique_shape, ccd_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Godot.RigidBody3D CreateBox(bool unique_shape, bool ccd_mode)
|
||||||
|
{
|
||||||
|
Godot.RigidBody3D rigid_body = new Godot.RigidBody3D();
|
||||||
|
CollisionShape3D collision_shape = new CollisionShape3D();
|
||||||
|
rigid_body.ContinuousCd = ccd_mode;
|
||||||
|
|
||||||
|
if(VISUALIZE) { rigid_body.AddChild(new MeshInstance3D(){ Mesh = BoxMesh }); }
|
||||||
|
|
||||||
|
if (unique_shape) { collision_shape.Shape = new BoxShape3D(); }
|
||||||
|
else { collision_shape.Shape = BoxShape; }
|
||||||
|
|
||||||
|
rigid_body.AddChild(collision_shape);
|
||||||
|
return rigid_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Godot.RigidBody3D CreateSphere(bool unique_shape, bool ccd_mode)
|
||||||
|
{
|
||||||
|
Godot.RigidBody3D rigid_body = new Godot.RigidBody3D();
|
||||||
|
CollisionShape3D collision_shape = new CollisionShape3D();
|
||||||
|
rigid_body.ContinuousCd = ccd_mode;
|
||||||
|
|
||||||
|
if(VISUALIZE) { rigid_body.AddChild(new MeshInstance3D(){ Mesh = SphereMesh }); }
|
||||||
|
|
||||||
|
if (unique_shape) { collision_shape.Shape = new SphereShape3D(); }
|
||||||
|
else { collision_shape.Shape = SphereShape; }
|
||||||
|
|
||||||
|
rigid_body.AddChild(collision_shape);
|
||||||
|
return rigid_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D Benchmark2000RigidBody3DSquares()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateBox, false, false, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D Benchmark2000RigidBody3DCircles()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateSphere, false, false, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D Benchmark2000RigidBody3DMixed()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, false, false, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D Benchmark2000RigidBody3DUnique()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, true, false, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D Benchmark2000RigidBody3DContinuous()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, false, true, true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node3D Benchmark2000RigidBody3DUnbound()
|
||||||
|
{
|
||||||
|
return SetupScene(CreateRandomShape, false, false, false, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
benchmarks/gdscript/control.gd
Normal file
6
benchmarks/gdscript/control.gd
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
extends Benchmark
|
||||||
|
|
||||||
|
# An empty test, to act as a control
|
||||||
|
|
||||||
|
func benchmark_control():
|
||||||
|
pass
|
||||||
119
benchmarks/physics/rigid_body_2d.gd
Normal file
119
benchmarks/physics/rigid_body_2d.gd
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
extends Benchmark
|
||||||
|
|
||||||
|
const VISUALIZE := true
|
||||||
|
const SPREAD_H := 1600.0;
|
||||||
|
const SPREAD_V := 800.0;
|
||||||
|
|
||||||
|
var boundary_shape := WorldBoundaryShape2D.new()
|
||||||
|
var square_shape := RectangleShape2D.new()
|
||||||
|
var circle_shape := CircleShape2D.new()
|
||||||
|
var square_mesh := QuadMesh.new()
|
||||||
|
var circle_mesh := SphereMesh.new()
|
||||||
|
|
||||||
|
func _init() -> void:
|
||||||
|
square_mesh.size = Vector2(20.0, 20.0);
|
||||||
|
circle_mesh.radius = 10.0;
|
||||||
|
circle_mesh.height = 20.0;
|
||||||
|
test_physics = true
|
||||||
|
test_idle = true
|
||||||
|
|
||||||
|
|
||||||
|
func setup_scene(create_body_func: Callable, unique_shape: bool, ccd_mode: RigidBody2D.CCDMode, boundary: bool, num_shapes: int) -> Node2D:
|
||||||
|
var scene_root := Node2D.new()
|
||||||
|
|
||||||
|
if VISUALIZE:
|
||||||
|
var camera := Camera2D.new()
|
||||||
|
camera.position = Vector2(0.0, -100.0)
|
||||||
|
camera.zoom = Vector2(0.5, 0.5)
|
||||||
|
scene_root.add_child(camera)
|
||||||
|
|
||||||
|
if boundary:
|
||||||
|
var pit := StaticBody2D.new();
|
||||||
|
pit.add_child(create_wall(Vector2(SPREAD_H, 0.0), -0.1))
|
||||||
|
pit.add_child(create_wall(Vector2(-SPREAD_H, 0.0), 0.1))
|
||||||
|
scene_root.add_child(pit);
|
||||||
|
|
||||||
|
for _i in num_shapes:
|
||||||
|
var body: RigidBody2D = create_body_func.call(unique_shape, ccd_mode)
|
||||||
|
body.position = Vector2(randf_range(-SPREAD_H, SPREAD_H), randf_range(0.0, -SPREAD_V))
|
||||||
|
scene_root.add_child(body)
|
||||||
|
|
||||||
|
return scene_root
|
||||||
|
|
||||||
|
|
||||||
|
func create_wall(position: Vector2, rotation: float) -> CollisionShape2D:
|
||||||
|
var wall := CollisionShape2D.new()
|
||||||
|
wall.shape = boundary_shape
|
||||||
|
wall.position = position
|
||||||
|
wall.rotation = rotation
|
||||||
|
return wall
|
||||||
|
|
||||||
|
|
||||||
|
func create_random_shape(unique_shape: bool, ccd_mode: RigidBody2D.CCDMode) -> RigidBody2D:
|
||||||
|
match randi_range(0, 1):
|
||||||
|
0: return create_square(unique_shape, ccd_mode)
|
||||||
|
_: return create_circle(unique_shape, ccd_mode)
|
||||||
|
|
||||||
|
|
||||||
|
func create_square(unique_shape: bool, ccd_mode: RigidBody2D.CCDMode) -> RigidBody2D:
|
||||||
|
var rigid_body := RigidBody2D.new()
|
||||||
|
var collision_shape := CollisionShape2D.new()
|
||||||
|
rigid_body.continuous_cd = ccd_mode
|
||||||
|
|
||||||
|
if VISUALIZE:
|
||||||
|
var mesh_instance := MeshInstance2D.new()
|
||||||
|
mesh_instance.mesh = square_mesh
|
||||||
|
rigid_body.add_child(mesh_instance)
|
||||||
|
|
||||||
|
if unique_shape:
|
||||||
|
collision_shape.shape = RectangleShape2D.new()
|
||||||
|
else:
|
||||||
|
# Reuse existing shape.
|
||||||
|
collision_shape.shape = square_shape
|
||||||
|
rigid_body.add_child(collision_shape)
|
||||||
|
|
||||||
|
return rigid_body
|
||||||
|
|
||||||
|
|
||||||
|
func create_circle(unique_shape: bool, ccd_mode: RigidBody2D.CCDMode) -> RigidBody2D:
|
||||||
|
var rigid_body := RigidBody2D.new()
|
||||||
|
var collision_shape := CollisionShape2D.new()
|
||||||
|
rigid_body.continuous_cd = ccd_mode
|
||||||
|
|
||||||
|
if VISUALIZE:
|
||||||
|
var mesh_instance := MeshInstance2D.new()
|
||||||
|
mesh_instance.mesh = circle_mesh
|
||||||
|
rigid_body.add_child(mesh_instance)
|
||||||
|
|
||||||
|
if unique_shape:
|
||||||
|
collision_shape.shape = CircleShape2D.new()
|
||||||
|
else:
|
||||||
|
# Reuse existing shape.
|
||||||
|
collision_shape.shape = circle_shape
|
||||||
|
rigid_body.add_child(collision_shape)
|
||||||
|
|
||||||
|
return rigid_body
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_2d_squares() -> Node2D:
|
||||||
|
return setup_scene(create_square, false, RigidBody2D.CCD_MODE_DISABLED, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_2d_circles() -> Node2D:
|
||||||
|
return setup_scene(create_circle, false, RigidBody2D.CCD_MODE_DISABLED, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_2d_mixed() -> Node2D:
|
||||||
|
return setup_scene(create_random_shape, false, RigidBody2D.CCD_MODE_DISABLED, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_2d_unique() -> Node2D:
|
||||||
|
return setup_scene(create_random_shape, true, RigidBody2D.CCD_MODE_DISABLED, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_2d_continuous() -> Node2D:
|
||||||
|
return setup_scene(create_random_shape, false, RigidBody2D.CCD_MODE_CAST_SHAPE, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_2d_unbound() -> Node2D:
|
||||||
|
return setup_scene(create_random_shape, false, RigidBody2D.CCD_MODE_DISABLED, false, 2000)
|
||||||
@@ -1,34 +1,69 @@
|
|||||||
extends Benchmark
|
extends Benchmark
|
||||||
|
|
||||||
|
const VISUALIZE := true
|
||||||
|
const SPREAD_H := 20.0;
|
||||||
|
const SPREAD_V := 10.0;
|
||||||
|
|
||||||
|
var boundary_shape := WorldBoundaryShape3D.new()
|
||||||
var box_shape := BoxShape3D.new()
|
var box_shape := BoxShape3D.new()
|
||||||
var sphere_shape := SphereShape3D.new()
|
var sphere_shape := SphereShape3D.new()
|
||||||
|
var box_mesh := BoxMesh.new()
|
||||||
|
var sphere_mesh := SphereMesh.new()
|
||||||
|
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
test_physics = true
|
test_physics = true
|
||||||
test_idle = true
|
test_idle = true
|
||||||
|
|
||||||
|
|
||||||
func setup_scene(create_body_func: Callable, unique_shape: bool, num_shapes: int) -> Node3D:
|
func setup_scene(create_body_func: Callable, unique_shape: bool, ccd_mode: bool, boundary: bool, num_shapes: int) -> Node3D:
|
||||||
var scene_root := Node3D.new()
|
var scene_root := Node3D.new()
|
||||||
var camera := Camera3D.new()
|
|
||||||
camera.position.y = 0.3
|
if VISUALIZE:
|
||||||
camera.position.z = 1.0
|
var camera := Camera3D.new()
|
||||||
camera.rotate_x(-0.8)
|
camera.position = Vector3(0.0, 20.0, 20.0)
|
||||||
scene_root.add_child(camera)
|
camera.rotate_x(-0.8)
|
||||||
|
scene_root.add_child(camera)
|
||||||
|
|
||||||
|
if boundary:
|
||||||
|
var pit := StaticBody3D.new();
|
||||||
|
pit.add_child(create_wall(Vector3(SPREAD_H, 0.0, 0.0), Vector3(0.0, 0.0, 0.2)))
|
||||||
|
pit.add_child(create_wall(Vector3(0.0, 0.0, SPREAD_H), Vector3(-0.2, 0.0, 0.0)))
|
||||||
|
pit.add_child(create_wall(Vector3(-SPREAD_H, 0.0, 0.0), Vector3(0.0, 0.0, -0.2)))
|
||||||
|
pit.add_child(create_wall(Vector3(0.0, 0.0, -SPREAD_H), Vector3(0.2, 0.0, 0.0)))
|
||||||
|
scene_root.add_child(pit);
|
||||||
|
|
||||||
for _i in num_shapes:
|
for _i in num_shapes:
|
||||||
var box: RigidBody3D = create_body_func.call(unique_shape)
|
var body: RigidBody3D = create_body_func.call(unique_shape, ccd_mode)
|
||||||
box.position.x = randf_range(-50, 50)
|
body.position = Vector3(randf_range(-SPREAD_H, SPREAD_H), randf_range(0.0, SPREAD_V), randf_range(-SPREAD_H, SPREAD_H))
|
||||||
box.position.z = randf_range(-50, 50)
|
scene_root.add_child(body)
|
||||||
scene_root.add_child(box)
|
|
||||||
|
|
||||||
return scene_root
|
return scene_root
|
||||||
|
|
||||||
|
|
||||||
func create_box(unique_shape: bool) -> RigidBody3D:
|
func create_wall(position: Vector3, rotation: Vector3) -> CollisionShape3D:
|
||||||
var rigid_body := RigidBody3D.new()
|
var wall := CollisionShape3D.new()
|
||||||
|
wall.shape = boundary_shape
|
||||||
|
wall.position = position
|
||||||
|
wall.rotation = rotation
|
||||||
|
return wall
|
||||||
|
|
||||||
|
|
||||||
|
func create_random_shape(unique_shape: bool, ccd_mode: bool) -> RigidBody3D:
|
||||||
|
match randi_range(0, 1):
|
||||||
|
0: return create_box(unique_shape, ccd_mode)
|
||||||
|
_: return create_sphere(unique_shape, ccd_mode)
|
||||||
|
|
||||||
|
|
||||||
|
func create_box(unique_shape: bool, ccd_mode: bool) -> RigidBody3D:
|
||||||
|
var rigid_body := RigidBody3D.new()
|
||||||
var collision_shape := CollisionShape3D.new()
|
var collision_shape := CollisionShape3D.new()
|
||||||
|
rigid_body.continuous_cd = ccd_mode
|
||||||
|
|
||||||
|
if VISUALIZE:
|
||||||
|
var mesh_instance := MeshInstance3D.new()
|
||||||
|
mesh_instance.mesh = box_mesh
|
||||||
|
rigid_body.add_child(mesh_instance)
|
||||||
|
|
||||||
if unique_shape:
|
if unique_shape:
|
||||||
collision_shape.shape = BoxShape3D.new()
|
collision_shape.shape = BoxShape3D.new()
|
||||||
else:
|
else:
|
||||||
@@ -39,10 +74,16 @@ func create_box(unique_shape: bool) -> RigidBody3D:
|
|||||||
return rigid_body
|
return rigid_body
|
||||||
|
|
||||||
|
|
||||||
func create_sphere(unique_shape: bool) -> RigidBody3D:
|
func create_sphere(unique_shape: bool, ccd_mode: bool) -> RigidBody3D:
|
||||||
var rigid_body := RigidBody3D.new()
|
var rigid_body := RigidBody3D.new()
|
||||||
|
|
||||||
var collision_shape := CollisionShape3D.new()
|
var collision_shape := CollisionShape3D.new()
|
||||||
|
rigid_body.continuous_cd = ccd_mode
|
||||||
|
|
||||||
|
if VISUALIZE:
|
||||||
|
var mesh_instance := MeshInstance3D.new()
|
||||||
|
mesh_instance.mesh = sphere_mesh
|
||||||
|
rigid_body.add_child(mesh_instance)
|
||||||
|
|
||||||
if unique_shape:
|
if unique_shape:
|
||||||
collision_shape.shape = SphereShape3D.new()
|
collision_shape.shape = SphereShape3D.new()
|
||||||
else:
|
else:
|
||||||
@@ -53,17 +94,25 @@ func create_sphere(unique_shape: bool) -> RigidBody3D:
|
|||||||
return rigid_body
|
return rigid_body
|
||||||
|
|
||||||
|
|
||||||
func benchmark_7500_rigid_body_3d_shared_box_shape() -> Node3D:
|
func benchmark_2000_rigid_body_3d_boxes() -> Node3D:
|
||||||
return setup_scene(create_box, false, 7500)
|
return setup_scene(create_box, false, false, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
func benchmark_7500_rigid_body_3d_unique_box_shape() -> Node3D:
|
func benchmark_2000_rigid_body_3d_spheres() -> Node3D:
|
||||||
return setup_scene(create_box, true, 7500)
|
return setup_scene(create_sphere, false, false, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
func benchmark_7500_rigid_body_3d_shared_sphere_shape() -> Node3D:
|
func benchmark_2000_rigid_body_3d_mixed() -> Node3D:
|
||||||
return setup_scene(create_sphere, false, 7500)
|
return setup_scene(create_random_shape, false, false, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
func benchmark_7500_rigid_body_3d_unique_sphere_shape() -> Node3D:
|
func benchmark_2000_rigid_body_3d_unique() -> Node3D:
|
||||||
return setup_scene(create_sphere, true, 7500)
|
return setup_scene(create_random_shape, true, false, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_3d_continuous() -> Node3D:
|
||||||
|
return setup_scene(create_random_shape, false, true, true, 2000)
|
||||||
|
|
||||||
|
|
||||||
|
func benchmark_2000_rigid_body_3d_unbound() -> Node3D:
|
||||||
|
return setup_scene(create_random_shape, false, false, false, 2000)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ func _init():
|
|||||||
|
|
||||||
class TestScene extends Node3D:
|
class TestScene extends Node3D:
|
||||||
|
|
||||||
var scene = preload("res://benchmarks/rendering/very_large_scene.tscn")
|
var scene = preload("res://supplemental/very_large_scene.tscn")
|
||||||
var instance
|
var instance
|
||||||
var world_env: WorldEnvironment = null
|
var world_env: WorldEnvironment = null
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ func _init():
|
|||||||
|
|
||||||
class TestScene extends Node3D:
|
class TestScene extends Node3D:
|
||||||
|
|
||||||
var sponza_scene = preload("res://benchmarks/rendering/sponza.tscn")
|
var sponza_scene = preload("res://supplemental/sponza.tscn")
|
||||||
var sponza
|
var sponza
|
||||||
var world_env: WorldEnvironment = null
|
var world_env: WorldEnvironment = null
|
||||||
|
|
||||||
|
|||||||
15
main.gd
15
main.gd
@@ -1,7 +1,5 @@
|
|||||||
extends Panel
|
extends Panel
|
||||||
|
|
||||||
const RANDOM_SEED = 0x60d07
|
|
||||||
|
|
||||||
var items := []
|
var items := []
|
||||||
|
|
||||||
# Prefix variables with `arg_` to have them automatically be parsed from command line arguments
|
# Prefix variables with `arg_` to have them automatically be parsed from command line arguments
|
||||||
@@ -11,11 +9,9 @@ var arg_save_json := ""
|
|||||||
var arg_run_benchmarks := false
|
var arg_run_benchmarks := false
|
||||||
|
|
||||||
@onready var tree := $Tree as Tree
|
@onready var tree := $Tree as Tree
|
||||||
|
var categories := {}
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# Use a fixed random seed to improve reproducibility of results.
|
|
||||||
seed(RANDOM_SEED)
|
|
||||||
|
|
||||||
# Parse valid command-line arguments of the form `--key=value` into member variables.
|
# Parse valid command-line arguments of the form `--key=value` into member variables.
|
||||||
for argument in OS.get_cmdline_user_args():
|
for argument in OS.get_cmdline_user_args():
|
||||||
if not argument.begins_with("--"):
|
if not argument.begins_with("--"):
|
||||||
@@ -50,7 +46,6 @@ func _ready() -> void:
|
|||||||
tree.set_column_title(5, "Main Thread Time")
|
tree.set_column_title(5, "Main Thread Time")
|
||||||
|
|
||||||
var root := tree.create_item()
|
var root := tree.create_item()
|
||||||
var categories := {}
|
|
||||||
|
|
||||||
for test_id in Manager.get_test_ids():
|
for test_id in Manager.get_test_ids():
|
||||||
var test_name := test_id.pretty_name()
|
var test_name := test_id.pretty_name()
|
||||||
@@ -75,10 +70,10 @@ func _ready() -> void:
|
|||||||
# from the interface after the fact.
|
# from the interface after the fact.
|
||||||
item.set_checked(0, true)
|
item.set_checked(0, true)
|
||||||
if arg_include_benchmarks:
|
if arg_include_benchmarks:
|
||||||
if not path.match(arg_include_benchmarks):
|
if not path.matchn(arg_include_benchmarks):
|
||||||
item.set_checked(0, false)
|
item.set_checked(0, false)
|
||||||
if arg_exclude_benchmarks:
|
if arg_exclude_benchmarks:
|
||||||
if path.match(arg_exclude_benchmarks):
|
if path.matchn(arg_exclude_benchmarks):
|
||||||
item.set_checked(0, false)
|
item.set_checked(0, false)
|
||||||
|
|
||||||
var results := Manager.get_test_result_as_dict(test_id)
|
var results := Manager.get_test_result_as_dict(test_id)
|
||||||
@@ -100,12 +95,16 @@ func _ready() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _on_SelectAll_pressed() -> void:
|
func _on_SelectAll_pressed() -> void:
|
||||||
|
for category in categories.values():
|
||||||
|
category.collapsed = false
|
||||||
for item in items:
|
for item in items:
|
||||||
item.set_checked(0, true)
|
item.set_checked(0, true)
|
||||||
_on_Tree_item_edited()
|
_on_Tree_item_edited()
|
||||||
|
|
||||||
|
|
||||||
func _on_SelectNone_pressed() -> void:
|
func _on_SelectNone_pressed() -> void:
|
||||||
|
for category in categories.values():
|
||||||
|
category.collapsed = true
|
||||||
for item in items:
|
for item in items:
|
||||||
item.set_checked(0, false)
|
item.set_checked(0, false)
|
||||||
_on_Tree_item_edited()
|
_on_Tree_item_edited()
|
||||||
|
|||||||
63
manager.gd
63
manager.gd
@@ -1,5 +1,7 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
|
const RANDOM_SEED = 0x60d07
|
||||||
|
|
||||||
class Results:
|
class Results:
|
||||||
var render_cpu := 0.0
|
var render_cpu := 0.0
|
||||||
var render_gpu := 0.0
|
var render_gpu := 0.0
|
||||||
@@ -10,6 +12,7 @@ class Results:
|
|||||||
class TestID:
|
class TestID:
|
||||||
var name : String
|
var name : String
|
||||||
var category : String
|
var category : String
|
||||||
|
var language : String
|
||||||
|
|
||||||
func pretty_name() -> String:
|
func pretty_name() -> String:
|
||||||
return name.capitalize()
|
return name.capitalize()
|
||||||
@@ -24,24 +27,29 @@ class TestID:
|
|||||||
|
|
||||||
func test_ids_from_path(path: String) -> Array[TestID]:
|
func test_ids_from_path(path: String) -> Array[TestID]:
|
||||||
var rv : Array[TestID] = []
|
var rv : Array[TestID] = []
|
||||||
if not path.ends_with(".gd"):
|
|
||||||
return rv
|
|
||||||
|
|
||||||
var script = load(path).new()
|
# Check for runnable tests.
|
||||||
if not (script is Benchmark):
|
for extension in languages.keys():
|
||||||
return rv
|
if not path.ends_with(extension):
|
||||||
|
|
||||||
var bench_script : Benchmark = script
|
|
||||||
for method in bench_script.get_method_list():
|
|
||||||
if not method.name.begins_with("benchmark_"):
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var test_id := TestID.new()
|
var bench_script = load(path).new()
|
||||||
test_id.name = method.name.trim_prefix("benchmark_")
|
for method in bench_script.get_method_list():
|
||||||
test_id.category = path.trim_prefix("res://benchmarks/").trim_suffix(".gd")
|
if not method.name.begins_with(languages[extension]["test_prefix"]):
|
||||||
rv.push_back(test_id)
|
continue
|
||||||
return rv
|
|
||||||
|
# This method is a runnable test. Push it onto the result
|
||||||
|
var test_id := TestID.new()
|
||||||
|
test_id.name = method.name.trim_prefix(languages[extension]["test_prefix"])
|
||||||
|
test_id.category = path.trim_prefix("res://benchmarks/").trim_suffix(extension)
|
||||||
|
test_id.language = extension
|
||||||
|
rv.push_back(test_id)
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
# List of supported languages and their styles.
|
||||||
|
var languages := {".gd": {"test_prefix": "benchmark_"}}
|
||||||
|
|
||||||
# List of benchmarks populated in `_ready()`.
|
# List of benchmarks populated in `_ready()`.
|
||||||
var test_results := {}
|
var test_results := {}
|
||||||
@@ -72,6 +80,10 @@ func _ready():
|
|||||||
RenderingServer.viewport_set_measure_render_time(get_tree().root.get_viewport_rid(),true)
|
RenderingServer.viewport_set_measure_render_time(get_tree().root.get_viewport_rid(),true)
|
||||||
set_process(false)
|
set_process(false)
|
||||||
|
|
||||||
|
# Register script language compatibility
|
||||||
|
if Engine.has_singleton("GodotSharp"):
|
||||||
|
languages[".cs"] = {"test_prefix": "Benchmark"}
|
||||||
|
|
||||||
# Register contents of `benchmarks/` folder automatically.
|
# Register contents of `benchmarks/` folder automatically.
|
||||||
for benchmark_path in dir_contents("res://benchmarks/"):
|
for benchmark_path in dir_contents("res://benchmarks/"):
|
||||||
for test_id in test_ids_from_path(benchmark_path):
|
for test_id in test_ids_from_path(benchmark_path):
|
||||||
@@ -93,7 +105,9 @@ func benchmark(test_ids: Array[TestID], return_path: String) -> void:
|
|||||||
for i in range(test_ids.size()):
|
for i in range(test_ids.size()):
|
||||||
DisplayServer.window_set_title("%d/%d - Running %s" % [i + 1, test_ids.size(), test_ids[i].pretty()])
|
DisplayServer.window_set_title("%d/%d - Running %s" % [i + 1, test_ids.size(), test_ids[i].pretty()])
|
||||||
print("Running benchmark %d of %d: %s" % [i + 1, test_ids.size(), test_ids[i]])
|
print("Running benchmark %d of %d: %s" % [i + 1, test_ids.size(), test_ids[i]])
|
||||||
|
seed(RANDOM_SEED)
|
||||||
await run_test(test_ids[i])
|
await run_test(test_ids[i])
|
||||||
|
print("Result: %s\n" % get_result_as_string(test_ids[i]))
|
||||||
|
|
||||||
DisplayServer.window_set_title("[DONE] %d benchmarks - Godot Benchmarks" % test_ids.size())
|
DisplayServer.window_set_title("[DONE] %d benchmarks - Godot Benchmarks" % test_ids.size())
|
||||||
print_rich("[color=green][b]Done running %d benchmarks.[/b] Results JSON:[/color]\n" % test_ids.size())
|
print_rich("[color=green][b]Done running %d benchmarks.[/b] Results JSON:[/color]\n" % test_ids.size())
|
||||||
@@ -111,6 +125,7 @@ func benchmark(test_ids: Array[TestID], return_path: String) -> void:
|
|||||||
if return_path:
|
if return_path:
|
||||||
get_tree().change_scene_to_file(return_path)
|
get_tree().change_scene_to_file(return_path)
|
||||||
else:
|
else:
|
||||||
|
get_tree().queue_delete(get_tree())
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
|
|
||||||
@@ -128,13 +143,15 @@ func run_test(test_id: TestID) -> void:
|
|||||||
# Add a dummy child so that the above check works for subsequent reloads
|
# Add a dummy child so that the above check works for subsequent reloads
|
||||||
get_tree().current_scene.add_child(Node.new())
|
get_tree().current_scene.add_child(Node.new())
|
||||||
|
|
||||||
var benchmark_script : Benchmark = load("res://benchmarks/%s.gd" % test_id.category).new()
|
var bench_script = load("res://benchmarks/%s%s" % [test_id.category, test_id.language]).new()
|
||||||
|
|
||||||
var results := Results.new()
|
var results := Results.new()
|
||||||
|
|
||||||
|
# Call and time the function to be tested
|
||||||
var begin_time := Time.get_ticks_usec()
|
var begin_time := Time.get_ticks_usec()
|
||||||
var bench_node = benchmark_script.call("benchmark_" + test_id.name)
|
var bench_node = bench_script.call(languages[test_id.language]["test_prefix"] + test_id.name)
|
||||||
results.time = (Time.get_ticks_usec() - begin_time) * 0.001
|
results.time = (Time.get_ticks_usec() - begin_time) * 0.001
|
||||||
|
|
||||||
|
# Continue benchmarking if the function call has returned a node
|
||||||
var frames_captured := 0
|
var frames_captured := 0
|
||||||
if bench_node:
|
if bench_node:
|
||||||
get_tree().current_scene.add_child(bench_node)
|
get_tree().current_scene.add_child(bench_node)
|
||||||
@@ -166,11 +183,21 @@ func run_test(test_id: TestID) -> void:
|
|||||||
# metrics calculated on a per-second basis.
|
# metrics calculated on a per-second basis.
|
||||||
|
|
||||||
for metric in results.get_property_list():
|
for metric in results.get_property_list():
|
||||||
if benchmark_script.get("test_" + metric.name) == false: # account for null
|
if bench_script.get("test_" + metric.name) == false: # account for null
|
||||||
results.set(metric.name, 0.0)
|
results.set(metric.name, 0.0)
|
||||||
|
|
||||||
test_results[test_id] = results
|
test_results[test_id] = results
|
||||||
|
|
||||||
|
func get_result_as_string(test_id: TestID) -> String:
|
||||||
|
# Returns all non-zero metrics formatted as a string
|
||||||
|
var rd := get_test_result_as_dict(test_id)
|
||||||
|
|
||||||
|
for key in rd.keys():
|
||||||
|
if rd[key] == 0.0:
|
||||||
|
rd.erase(key)
|
||||||
|
|
||||||
|
return JSON.stringify(rd)
|
||||||
|
|
||||||
func get_test_result_as_dict(test_id: TestID) -> Dictionary:
|
func get_test_result_as_dict(test_id: TestID) -> Dictionary:
|
||||||
var result : Results = test_results[test_id]
|
var result : Results = test_results[test_id]
|
||||||
var rv := {}
|
var rv := {}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ window/size/viewport_width=1920
|
|||||||
window/size/viewport_height=1080
|
window/size/viewport_height=1080
|
||||||
window/stretch/mode="viewport"
|
window/stretch/mode="viewport"
|
||||||
|
|
||||||
|
[dotnet]
|
||||||
|
|
||||||
|
project/assembly_name="Godot Benchmarks"
|
||||||
|
|
||||||
[gui]
|
[gui]
|
||||||
|
|
||||||
theme/default_theme_scale=1.5
|
theme/default_theme_scale=1.5
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[gd_scene load_steps=6 format=3 uid="uid://33dqw5jcha0h"]
|
[gd_scene load_steps=6 format=3 uid="uid://33dqw5jcha0h"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://benchmarks/rendering/camera_move.gd" id="1_vqkte"]
|
[ext_resource type="Script" path="res://supplemental/camera_move.gd" id="1_vqkte"]
|
||||||
|
|
||||||
|
|
||||||
[sub_resource type="Environment" id="Environment_itcjv"]
|
[sub_resource type="Environment" id="Environment_itcjv"]
|
||||||
|
|
||||||
Reference in New Issue
Block a user