見出し画像

Godot ノードと百万回の更新

Godotエンジン、ノード多いと処理重いはず。でもどれくらい?

やってみたこと

次の8種類のノードをそれぞれ10万個(描画系は1万個)作って、フレームが1周するのに何マイクロ秒かかるか測ってみました。

Node

組み込みクラスです。

Node3D

組み込みクラスです。

Node派生(NoDef)

_processを定義していないNode派生クラスです。

Node派生(pass)

Node派生クラスです。_processを定義してますが、新規作成されたスクリプトと同じで、冒頭でpassするだけで何もしてません。

 Node派生(Min)

Node派生クラスです。_processを定義して、メンバ変数をインクリメント(+=1)してます。1行だけの関数。最小の処理という意味でMinです。

Node派生(Itr)

Node派生クラスです。_processを定義して、メンバ変数を100回インクリメント(+=1)してます。for文を使わずに100行使って+1してます。繰り返しのItrです。

Mesh(画面内)

MeshInstance3Dです。meshにsize=0.1のBoxMeshを設定してます。画面には米粒くらいの大きさで描かれます。MeshInstance3Dは10万個作ると、Godotが応答しなくなったので、これは1万個だけ作ってます。

Mesh(画面外)

MeshInstance3Dです。meshにsize=0.1のBoxMeshを設定してます。カメラの後ろに配置してます。こうすることで、カリングされて描画発行されない、もしくは頂点シェーダーだけが走ってピクセルシェーダーに至らない、という狙いです。これも10万個ではなく、1万個だけ作ってます。

# 1回だけの処理 -------------

# 通常のノード作成の場合
for i in range(0, GDSpeedNode.NODE_COUNT):
	add_child(NodeProcPass.new())

# 描画系ノードの作成の場合
for i in range(0, GDSpeedNode.NODE_COUNT / 10):
	var mesh_inst: MeshInstance3D = MeshInstance3D.new()
	mesh_inst.mesh = mesh
	mesh_inst.position = Vector3(0.0, 0.0, 8.0) # 画面外
	add_child(mesh_inst)

# 毎フレームの処理 --------------

# プロジェクト設定の「垂直同期モード」をDisableにしてます。

var time: int = Time.get_ticks_usec()
_time_sum += (time - _last_time)
_last_time = time

_time_count += 1
if _time_count > 100 or _time_sum > 1_000_000:
	var time_ave: int = int(float(_time_sum) / float(_time_count))
	print("time[us] %d" % time_ave)
	_time_sum = 0
	_time_count = 0

結果

2台のパソコンで実行してみました。ビルドとかしてなくて、エディタ上での実行です。エディタ上で「遊べる」重さで動くことは、制作初期段階でも重要だと思うので。

パソコンA: Ryzen 5 7520U 2.8GHz
パソコンB: Ryzen 3 2200G 3.5GHz

1フレームにかかる時間(垂直同期オフ)

わかったこと

  • 単なるNodeやNode3Dを作ってツリーに加えただけでは、速度に影響しないようです。

  • 組み込みのNodeと、自作の派生クラスでは、_processを定義しない限り、差は見られませんでした。

  • _processを定義していると、たとえpassするだけの関数であっても、顕著に影響が出ます。10万(100k)個あると、_processの呼び出しに90msかかってしまうので、10f/sくらいまで落ち込む見込みです。

  • MeshInstance3Dは、たとえ画面に映っていたとしても、自作の_processほどは時間は取らないようです。とはいえ、1万個で数msかかってるので、100 x 100や20 x 20 x 20みたいに、何かをずらーっと並べようとすると、それなりに負荷がかかると思ってよさそうです。

大量に出現するノードは、_process無いほうが良さそうです。

↓ 要らないなら消そう~!

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass

以上です~~

この記事が気に入ったらサポートをしてみませんか?