diff --git a/.gitignore b/.gitignore index 58ebfef..158715f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ - # Godot-specific ignores -.import/ +.godot/ export.cfg export_presets.cfg +logs/ # Imported translations (automatically generated from CSV files) *.translation diff --git a/benchmark.gd b/benchmark.gd index 00d8b67..7ece5b4 100644 --- a/benchmark.gd +++ b/benchmark.gd @@ -9,6 +9,7 @@ class_name Benchmark @export var test_render_gpu : = false @export var test_idle : = false @export var test_physics : = false +@export var time_limit := true # Called when the node enters the scene tree for the first time. func _process(delta): @@ -18,7 +19,7 @@ func _process(delta): if (test_render_gpu): txt+=str("GPU: ",RenderingServer.viewport_get_measured_render_time_gpu(get_tree().root.get_viewport_rid()) ,"\n") text = txt - + func _ready(): add_to_group("bechnmark_config") if (Manager.is_recording()): diff --git a/gdscript/packed_string_array.gd b/gdscript/packed_string_array.gd new file mode 100644 index 0000000..5624d7e --- /dev/null +++ b/gdscript/packed_string_array.gd @@ -0,0 +1,20 @@ +extends Node + +const ITERATIONS = 80_000 + + +func _ready(): + var array: PackedStringArray = PackedStringArray() + for i in ITERATIONS: + # Insert elements. + array.push_back(str("Godot ", i)) + + for i in ITERATIONS: + # Update elements in order. + array[i] = "" + + for _i in ITERATIONS: + # Delete elements from the front (non-constant complexity). + array.remove_at(0) + + Manager.end_test() diff --git a/gdscript/packed_string_array.tscn b/gdscript/packed_string_array.tscn new file mode 100644 index 0000000..bee9753 --- /dev/null +++ b/gdscript/packed_string_array.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://benchmark.gd" type="Script" id=1] +[ext_resource path="res://gdscript/packed_string_array.gd" type="Script" id=2] + +[node name="PackedStringArray" type="Node"] +script = ExtResource( 2 ) + +[node name="Benchmark" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 14.0 +structured_text_bidi_override_options = [ ] +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +time_limit = false diff --git a/gdscript/typed_int_array.gd b/gdscript/typed_int_array.gd new file mode 100644 index 0000000..8bccfac --- /dev/null +++ b/gdscript/typed_int_array.gd @@ -0,0 +1,20 @@ +extends Node + +const ITERATIONS = 80_000 + + +func _ready(): + var array: Array[int] = [] + for i in ITERATIONS: + # Insert elements. + array.push_back(i) + + for i in ITERATIONS: + # Update elements in order. + array[i] = 0 + + for i in ITERATIONS: + # Delete elements from the front (non-constant complexity). + array.pop_front() + + Manager.end_test() diff --git a/gdscript/typed_int_array.tscn b/gdscript/typed_int_array.tscn new file mode 100644 index 0000000..87340ea --- /dev/null +++ b/gdscript/typed_int_array.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://benchmark.gd" type="Script" id=1] +[ext_resource path="res://gdscript/typed_int_array.gd" type="Script" id=2] + +[node name="TypedIntArray" type="Node"] +script = ExtResource( 2 ) + +[node name="Benchmark" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 14.0 +structured_text_bidi_override_options = [ ] +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +time_limit = false diff --git a/gdscript/typed_string_array.gd b/gdscript/typed_string_array.gd new file mode 100644 index 0000000..6ea0901 --- /dev/null +++ b/gdscript/typed_string_array.gd @@ -0,0 +1,20 @@ +extends Node + +const ITERATIONS = 80_000 + + +func _ready(): + var array: Array[String] = [] + for i in ITERATIONS: + # Insert elements. + array.push_back(str("Godot ", i)) + + for i in ITERATIONS: + # Update elements in order. + array[i] = "" + + for _i in ITERATIONS: + # Delete elements from the front (non-constant complexity). + array.pop_front() + + Manager.end_test() diff --git a/gdscript/typed_string_array.tscn b/gdscript/typed_string_array.tscn new file mode 100644 index 0000000..acb0421 --- /dev/null +++ b/gdscript/typed_string_array.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://benchmark.gd" type="Script" id=1] +[ext_resource path="res://gdscript/typed_string_array.gd" type="Script" id=2] + +[node name="TypedStringArray" type="Node"] +script = ExtResource( 2 ) + +[node name="Benchmark" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 14.0 +structured_text_bidi_override_options = [ ] +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +time_limit = false diff --git a/gdscript/untyped_int_array.gd b/gdscript/untyped_int_array.gd new file mode 100644 index 0000000..5ab136b --- /dev/null +++ b/gdscript/untyped_int_array.gd @@ -0,0 +1,20 @@ +extends Node + +const ITERATIONS = 80_000 + + +func _ready(): + var array = [] + for i in ITERATIONS: + # Insert elements. + array.push_back(i) + + for i in 50_000: + # Update elements in order. + array[i] = 0 + + for _i in ITERATIONS: + # Delete elements from the front (non-constant complexity). + array.pop_front() + + Manager.end_test() diff --git a/gdscript/untyped_int_array.tscn b/gdscript/untyped_int_array.tscn new file mode 100644 index 0000000..738827e --- /dev/null +++ b/gdscript/untyped_int_array.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://benchmark.gd" type="Script" id=1] +[ext_resource path="res://gdscript/untyped_int_array.gd" type="Script" id=2] + +[node name="UntypedIntArray" type="Node"] +script = ExtResource( 2 ) + +[node name="Benchmark" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 14.0 +structured_text_bidi_override_options = [ ] +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +time_limit = false diff --git a/gdscript/untyped_string_array.gd b/gdscript/untyped_string_array.gd new file mode 100644 index 0000000..feb03cc --- /dev/null +++ b/gdscript/untyped_string_array.gd @@ -0,0 +1,20 @@ +extends Node + +const ITERATIONS = 80_000 + + +func _ready(): + var array = [] + for i in ITERATIONS: + # Insert elements. + array.push_back(str("Godot ", i)) + + for i in ITERATIONS: + # Update elements in order. + array[i] = "" + + for _i in ITERATIONS: + # Delete elements from the front (non-constant complexity). + array.pop_front() + + Manager.end_test() diff --git a/gdscript/untyped_string_array.tscn b/gdscript/untyped_string_array.tscn new file mode 100644 index 0000000..81388e7 --- /dev/null +++ b/gdscript/untyped_string_array.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://benchmark.gd" type="Script" id=1] +[ext_resource path="res://gdscript/untyped_string_array.gd" type="Script" id=2] + +[node name="UntypedStringArray" type="Node"] +script = ExtResource( 2 ) + +[node name="Benchmark" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 14.0 +structured_text_bidi_override_options = [ ] +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} +time_limit = false diff --git a/main.gd b/main.gd index 7545159..01fbce3 100644 --- a/main.gd +++ b/main.gd @@ -2,15 +2,16 @@ extends Panel var items = [] func _ready(): - $Tree.columns = 5 + $Tree.columns = 6 $Tree.set_column_titles_visible(true) $Tree.set_column_title(0,"Test Name") $Tree.set_column_title(1,"Render CPU") $Tree.set_column_title(2,"Render GPU") $Tree.set_column_title(3,"Idle") $Tree.set_column_title(4,"Physics") + $Tree.set_column_title(5,"Wall Clock Time") - var root = $Tree.create_item() + var root = $Tree.create_item() var categories = {} for i in range(Manager.get_test_count()): var name = Manager.get_test_name(i) @@ -20,7 +21,7 @@ func _ready(): var c = $Tree.create_item(root) as TreeItem c.set_text(0,category) categories[category]=c - + var item = $Tree.create_item(categories[category]) as TreeItem item.set_cell_mode(0,TreeItem.CELL_MODE_CHECK) item.set_text(0,name) @@ -34,7 +35,9 @@ func _ready(): item.set_text(3,str(results.idle," ms")) if (results.physics): item.set_text(4,str(results.physics," ms")) - + if (results.time): + item.set_text(5,str(results.time," ms")) + items.append(item) @@ -67,14 +70,15 @@ func _on_CopyJSON_pressed(): json+='\t"render_cpu":'+str(results.render_cpu)+',\n' json+='\t"render_gpu":'+str(results.render_gpu)+',\n' json+='\t"idle":'+str(results.idle)+',\n' - json+='\t"physics":'+str(results.physics)+'\n' + json+='\t"physics":'+str(results.physics)+',\n' + json+='\t"time":'+str(results.time)+'\n' else: json+='\n' json+="}\n" json+="]\n" DisplayServer.clipboard_set(json) - - + + func _on_Run_pressed(): @@ -85,10 +89,10 @@ func _on_Run_pressed(): for it in items: if (it.is_checked(0)): queue.append(idx) - idx+=1 + idx+=1 if (idx==0): return - + Manager.benchmark(queue,$TestTime.value,"res://main.tscn") diff --git a/main.tscn b/main.tscn index 59e8ded..8381585 100644 --- a/main.tscn +++ b/main.tscn @@ -1,11 +1,11 @@ -[gd_scene load_steps=2 format=2] +[gd_scene load_steps=2 format=3 uid="uid://bcw2442lg1ymu"] -[ext_resource path="res://main.gd" type="Script" id=1] +[ext_resource type="Script" path="res://main.gd" id="1"] [node name="Main" type="Panel"] anchor_right = 1.0 anchor_bottom = 1.0 -script = ExtResource( 1 ) +script = ExtResource( "1" ) __meta__ = { "_edit_use_anchors_": false } @@ -16,8 +16,6 @@ offset_top = 16.0 offset_right = 146.0 offset_bottom = 56.0 text = "Available Benchmarks:" -structured_text_bidi_override_options = [ ] -script = null __meta__ = { "_edit_use_anchors_": false } @@ -29,7 +27,9 @@ offset_left = 24.0 offset_top = 40.0 offset_right = -18.0 offset_bottom = -45.0 -script = null +__meta__ = { +"_edit_use_anchors_": false +} [node name="SelectAll" type="Button" parent="."] anchor_left = 1.0 @@ -40,9 +40,8 @@ offset_left = -520.0 offset_top = -32.0 offset_right = -438.0 offset_bottom = -12.0 -rect_pivot_offset = Vector2( 41, 20 ) +rect_pivot_offset = Vector2(41, 20) text = "Select All" -script = null [node name="SelectNone" type="Button" parent="."] anchor_left = 1.0 @@ -53,9 +52,8 @@ offset_left = -424.0 offset_top = -32.0 offset_right = -335.0 offset_bottom = -12.0 -rect_pivot_offset = Vector2( 41, 20 ) +rect_pivot_offset = Vector2(41, 20) text = "Select None" -script = null [node name="CopyJSON" type="Button" parent="."] anchor_left = 1.0 @@ -66,9 +64,8 @@ offset_left = -320.0 offset_top = -32.0 offset_right = -162.0 offset_bottom = -12.0 -rect_pivot_offset = Vector2( 41, 20 ) +rect_pivot_offset = Vector2(41, 20) text = "Copy JSON to clipboard" -script = null [node name="Run" type="Button" parent="."] anchor_left = 1.0 @@ -79,11 +76,10 @@ offset_left = -104.0 offset_top = -32.0 offset_right = -22.0 offset_bottom = -12.0 -rect_pivot_offset = Vector2( 41, 20 ) +rect_pivot_offset = Vector2(41, 20) disabled = true text = "Run " -script = null __meta__ = { "_edit_use_anchors_": false } @@ -94,8 +90,6 @@ offset_top = 572.436 offset_right = 132.118 offset_bottom = 612.436 text = "Test Time (sec)" -structured_text_bidi_override_options = [ ] -script = null __meta__ = { "_edit_use_anchors_": false } @@ -107,7 +101,6 @@ offset_right = 326.0 offset_bottom = 592.0 min_value = 1.0 value = 8.0 -script = null __meta__ = { "_edit_use_anchors_": false } diff --git a/manager.gd b/manager.gd index 1b28d52..ee2aeb2 100644 --- a/manager.gd +++ b/manager.gd @@ -9,6 +9,7 @@ class Results: var render_gpu := 0.0 var idle := 0.0 var physics := 0.0 + var time := 0.0 var frames_captured := 0 @@ -21,7 +22,7 @@ class Test: name = p_name category = p_category path = p_path - + var results : Results = null var tests=[ @@ -30,16 +31,23 @@ var tests=[ Test.new("Static Lights Cull","Culling","res://rendering/culling/static_light_cull.tscn"), Test.new("Dynamic Lights Cull","Culling","res://rendering/culling/dynamic_light_cull.tscn"), Test.new("Directional Light Cull","Culling","res://rendering/culling/directional_light_cull.tscn"), + + Test.new("Untyped Int Array","GDScript","res://gdscript/untyped_int_array.tscn"), + Test.new("Typed Int Array","GDScript","res://gdscript/typed_int_array.tscn"), + Test.new("Untyped String Array","GDScript","res://gdscript/untyped_string_array.tscn"), + Test.new("Typed String Array","GDScript","res://gdscript/typed_string_array.tscn"), + Test.new("Packed String Array","GDScript","res://gdscript/packed_string_array.tscn"), ] var recording := false +var begin_time := 0.0 var remaining_time := 5.0 func is_recording(): return recording - + func get_test_count() -> int: - return tests.size() + return tests.size() func get_test_name(idx) -> String: return tests[idx].name @@ -67,11 +75,13 @@ var record_render_gpu := false var record_render_cpu := false var record_idle := false var record_physics := false +var time_limit := true -func begin_test(): +func begin_test(): results = Results.new() recording = true results = Results.new() + begin_time = Time.get_ticks_usec() * 0.001 remaining_time = test_time set_process(true) get_tree().change_scene(tests[tests_queue[0]].path) @@ -82,23 +92,26 @@ func begin_test(): record_render_gpu = bm.test_render_gpu record_idle = bm.test_idle record_physics = bm.test_physics + time_limit = bm.time_limit else: record_render_cpu = true record_render_gpu = true record_idle = true record_physics = true - + time_limit = true + skip_first = true frames_captured = 0 - - + + func end_test(): recording = false - results.render_cpu /= float(frames_captured) - results.render_gpu /= float(frames_captured) - results.idle /= float(frames_captured) - results.physics /= float(frames_captured) - + results.render_cpu /= float(max(1.0, float(frames_captured))) + results.render_gpu /= float(max(1.0, float(frames_captured))) + results.idle /= float(max(1.0, float(frames_captured))) + results.physics /= float(max(1.0, float(frames_captured))) + results.time = Time.get_ticks_usec() * 0.001 - begin_time + tests[tests_queue[0]].results = results results = null tests_queue.pop_front() @@ -107,11 +120,11 @@ func end_test(): else: get_tree().change_scene(return_to_scene) return_to_scene="" - - + + func _process(delta): if (not recording): - return + return if (skip_first): skip_first=false return @@ -125,13 +138,16 @@ func _process(delta): if (record_physics): results.physics += 0.0 frames_captured += 1 - remaining_time -= delta - if (remaining_time < 0.0): - end_test() - + + if time_limit: + # Some benchmarks (such as scripting) may not have a time limit. + remaining_time -= delta + if (remaining_time < 0.0): + end_test() + func report_time(delta,render_cpu,render_gpu,idle_cpu,physics_cpu): if (not recording): - return + return # Called when the node enters the scene tree for the first time. func _ready(): diff --git a/project.godot b/project.godot index b4cb5c6..24d944f 100644 --- a/project.godot +++ b/project.godot @@ -6,21 +6,22 @@ ; [section] ; section goes between [] ; param=value ; assign values to parameters -config_version=4 +config_version=5 -_global_script_classes=[ { +_global_script_classes=[{ "base": "Label", -"class": @"Benchmark", -"language": @"GDScript", +"class": &"Benchmark", +"language": &"GDScript", "path": "res://benchmark.gd" -} ] +}] _global_script_class_icons={ -@"Benchmark": "" +"Benchmark": "" } [application] run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.0") [autoload] diff --git a/rendering/culling/basic_cull.tscn b/rendering/culling/basic_cull.tscn index ede03ec..528ca85 100644 --- a/rendering/culling/basic_cull.tscn +++ b/rendering/culling/basic_cull.tscn @@ -21,3 +21,4 @@ __meta__ = { "_edit_use_anchors_": false } test_render_cpu = true +test_render_gpu = true diff --git a/rendering/culling/culling_base.gd b/rendering/culling/culling_base.gd index 2cc5984..6ce5708 100644 --- a/rendering/culling/culling_base.gd +++ b/rendering/culling/culling_base.gd @@ -8,7 +8,7 @@ var light_instance_xforms = [] var meshes = [] func fill_with_objects(object_amount,unshaded = false): - + var m = BoxMesh.new() meshes.append(m) m = SphereMesh.new() @@ -21,9 +21,9 @@ func fill_with_objects(object_amount,unshaded = false): meshes.append(m) for m in meshes: - + var s = Shader.new() - + var st = "shader_type spatial; "+("render_mode unshaded;" if unshaded else "")+"void fragment() { ALBEDO = vec3("+str(randf(),",",randf(),",",randf())+"); }" print(st) s.code = st @@ -37,18 +37,18 @@ func fill_with_objects(object_amount,unshaded = false): var ss = get_tree().root.size var from = cam.project_position(Vector2(0,ss.y),zextent) var extents = cam.project_position(Vector2(ss.x,0),zextent) - from - + for i in range(object_amount): - var xf = Transform() + var xf = Transform3D() xf.origin = Vector3(from.x + randf() * extents.x,from.y + randf() * extents.y, - (zn + zextent * randf())) var ins = RenderingServer.instance_create() RenderingServer.instance_set_base(ins,meshes[i % meshes.size()].get_rid()) RenderingServer.instance_set_scenario(ins,get_world_3d().scenario) RenderingServer.instance_set_transform(ins,xf) - objects.append(ins) + objects.append(ins) object_xforms.append(xf) - + func fill_with_omni_lights(amount,use_shadows=true): var cam := ($Camera3D as Camera3D) @@ -64,20 +64,20 @@ func fill_with_omni_lights(amount,use_shadows=true): RenderingServer.light_omni_set_shadow_mode(l,RenderingServer.LIGHT_OMNI_SHADOW_DUAL_PARABOLOID) # faster lights.append(l) for i in range(amount): - var xf = Transform() + var xf = Transform3D() xf.origin = Vector3(from.x + randf() * extents.x,from.y + randf() * extents.y, - (zn + zextent * randf())) var ins = RenderingServer.instance_create() RenderingServer.instance_set_base(ins,l) RenderingServer.instance_set_scenario(ins,get_world_3d().scenario) RenderingServer.instance_set_transform(ins,xf) - light_instances.append(ins) + light_instances.append(ins) light_instance_xforms.append(xf) func _exit_tree(): for o in objects: RenderingServer.free_rid(o) - + for l in lights: RenderingServer.free_rid(l) @@ -85,5 +85,5 @@ func _exit_tree(): RenderingServer.free_rid(l) meshes.clear() - - + + diff --git a/rendering/culling/directional_light_cull.tscn b/rendering/culling/directional_light_cull.tscn index b3d28e8..5eacc29 100644 --- a/rendering/culling/directional_light_cull.tscn +++ b/rendering/culling/directional_light_cull.tscn @@ -21,6 +21,7 @@ __meta__ = { "_edit_use_anchors_": false } test_render_cpu = true +test_render_gpu = true [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform( 0.933154, 0, 0.359476, -0.242196, 0.738961, 0.628711, -0.265639, -0.673748, 0.689565, 2.09069, 0, 0 ) diff --git a/rendering/culling/dynamic_cull.tscn b/rendering/culling/dynamic_cull.tscn index f31cf5a..afa1b14 100644 --- a/rendering/culling/dynamic_cull.tscn +++ b/rendering/culling/dynamic_cull.tscn @@ -21,3 +21,4 @@ __meta__ = { "_edit_use_anchors_": false } test_render_cpu = true +test_render_gpu = true diff --git a/rendering/culling/dynamic_light_cull.gd b/rendering/culling/dynamic_light_cull.gd index 3b678c1..638df62 100644 --- a/rendering/culling/dynamic_light_cull.gd +++ b/rendering/culling/dynamic_light_cull.gd @@ -9,7 +9,7 @@ func _process(delta): var angle = i * PI * 2.0 / light_instances.size() xf.origin += Vector3(sin(angle),cos(angle),0.0) * sin(time_accum) * 2.0 RenderingServer.instance_set_transform(light_instances[i],xf) - + func _ready(): fill_with_objects(10000) fill_with_omni_lights(100,use_shadows) diff --git a/rendering/culling/dynamic_light_cull.tscn b/rendering/culling/dynamic_light_cull.tscn index 070126f..8cfa60e 100644 --- a/rendering/culling/dynamic_light_cull.tscn +++ b/rendering/culling/dynamic_light_cull.tscn @@ -21,3 +21,4 @@ __meta__ = { "_edit_use_anchors_": false } test_render_cpu = true +test_render_gpu = true diff --git a/rendering/culling/dynamic_light_cull_withs_shadows.tscn b/rendering/culling/dynamic_light_cull_withs_shadows.tscn index 2c2af78..6aac53c 100644 --- a/rendering/culling/dynamic_light_cull_withs_shadows.tscn +++ b/rendering/culling/dynamic_light_cull_withs_shadows.tscn @@ -22,3 +22,4 @@ __meta__ = { "_edit_use_anchors_": false } test_render_cpu = true +test_render_gpu = true diff --git a/rendering/culling/static_light_cull.tscn b/rendering/culling/static_light_cull.tscn index dace2ba..21bee10 100644 --- a/rendering/culling/static_light_cull.tscn +++ b/rendering/culling/static_light_cull.tscn @@ -21,3 +21,4 @@ __meta__ = { "_edit_use_anchors_": false } test_render_cpu = true +test_render_gpu = true