Godot 4.7レンダリングアーキテクチャにおけるCPUボトルネックの理論的および技術的分析
Godot

Godot 4.7レンダリングアーキテクチャにおけるCPUボトルネックの理論的および技術的分析

著者GameDev Base
日付2026年10月28日
21分で読める

Godot 4.7 レンダリングアーキテクチャ:CPUボトルネックの理論的および技術的分析

一般的なアーキテクチャのフレームワークとCPU-GPU同期ポイント

Godotゲームエンジンのモダンなレンダリングアーキテクチャは、特に3Dおよび高度な2Dグラフィックスをサポートするバージョンにおいて、プラットフォームの独立性と低レベルグラフィックスAPI(Vulkan、Direct3D 12、Metal)へのアクセスを確保するために、複雑なレイヤー構造に基づいています。このアーキテクチャの要石は、すべてのモダンなレンダラーで使用されるRenderingDeviceと呼ばれる抽象化レイヤーです。RenderingDeviceは、異なるグラフィックスバックエンドのAPIを共通のインターフェースに収めることを目的としており、同じコードベースで複数のプラットフォーム向けにコンパイルすることを可能にします。このレイヤーは、RenderingServerによって管理される高レベルな視覚アセット(メッシュ、マテリアル、ライト)をGPUへ効率的に送信するためのコマンドリスト(draw_list、compute_list、raytracing_list)といった基本的なGPUリソースと処理を管理します。このアーキテクチャのアプローチは、コードの重複を減らし、新しいバックエンドの統合を容易にすることを目指していますが、同時に各API呼び出しの背後に隠れた重要なCPU負荷も伴います。各レンダリングコマンド、シェーダーの変更、または状態の変更は、GPUが現在の構成を検証するためにかなりの量のCPU処理を必要とします。そのため、レンダリング最適化の主な目的である高コストなAPI呼び出しを最小限に抑え、類似したオブジェクトを単一の処理でレンダリングするための「バッチング」技術の使用が不可欠となります。

CPUとGPU間の同期は、レンダリングプロセスがスムーズに動作するために極めて重要ですが、同時にCPUボトルネックの大きな潜在的要因でもあります。Godotは、同期および非同期の両方の同期メカニズムをサポートしています。RenderingDeviceクラスは、CPUを待機させる明示的な同期ポイントを提供します。例えば、sync()メソッドは、呼び出し元のスレッドをGPUがすべてのタスクを完了するまで待機させ、ブロックします。これはパフォーマンスの観点からコストがかかります。同様に、force_sync()メソッドも強制的なCPU-GPU同期ポイントを作成し、パフォーマンス低下を引き起こす可能性があります。一方、非同期処理は、例えばGPUからテクスチャを読み取るために使用されるtexture_get_data_asyncのような関数であり、CPUが525 7 533 1619 533 21

GPUの終了を待機している間にアイドル時間を費やすことを防ぎます。しかし、このような処理にも遅延(フレーム遅延)があり、この遅延はrendering/ rendering_device/vsync/frame_queue_sizeプロジェクト設定で決定される値の分だけCPU時間を消費する可能性があります。さらに、各texture_get_data_async呼び出しは約4msのCPU時間を要する可能性があり、多数のリクエストを同時に開始すると、合計CPU負荷が増加し、逆にパフォーマンスが低下する可能性があります。これは、非同期プログラミングにおいて慎重なバランスを取ることの重要性を示しています。

最も注目すべき発見の一つは、Vulkanバックエンドに存在する同期ロジックに根本的な欠陥があることです。言及されたリソースにおいて、RenderingDeviceVulkan::swap_buffers関数が正しい順序に従っておらず、新しいフレームが準備される際にGPUが前のフレームのリソースを解放するのを待機しないため、潜在的な「use-after-free」エラーやDEVICE_LOSTのような深刻なクラッシュ状態を引き起こす可能性があることが明らかになりました。この欠陥は、単なるパフォーマンスの問題ではなく、エンジンの安定性と信頼性を危険にさらす可能性のあるアーキテクチャ上の欠如として際立っています。結論として、RenderingDeviceレイヤーはプラットフォームの独立性を提供しますが、そこを通過するすべてのコマンドはCPU負荷であり、同期ロジックの誤りや非同期APIの使用は、CPUパフォーマンスを決定する繊細なバランスゲームなのです。

同期メソッド / ポイント

説明

潜在的なCPUボトルネック

RenderingDevice.submit() (ローカルデバイス用)

プライマリレンダリングスレッド以外で動作するワーカースレッドからコマンドリストをGPUに送信します。

スレッド同期およびコマンド送信のコスト。

RenderingDevice.sync() (ローカルデバイス用)

プライマリレンダリングスレッド以外で動作するワーカースレッドの完了を待機します。

CPUがGPUの処理終了を待ってブロックされることによる、CPU時間の損失。

RenderingDevice.force_sync()

強制的なCPU-GPU同期ポイントをトリガーします。

CPUがGPUの現在の処理終了を待機するストール。非常に高コストです。

RenderingDevice.texture_get_data_async()

GPU上のテクスチャデータを非同期でCPUに取得します。

遅延、GPUがデータを準備するのを待機している間のCPUの非アクティブ状態。

RenderingDevice.buffer_get_data_async()

GPU上のバッファデータを非同期でCPUに取得します。

CPUがデータの到着を待機している間の待機時間。

Vulkanバックエンド同期エラー

swap_buffersが正しい順序で待機せずに新しいフレームを開始する可能性があります。

GPUがまだ終了していないリソースを解放しようとすることによる「use-after-free」リスクおよびDEVICE_LOST。

アーキテクチャにおけるもう一つの重要なレイヤーはRenderingServerです。このサーバーは、ノードベースのシーンツリーとは独立して動作し、すべての視覚コンテンツの作成と管理に使用されます。RenderingServerのAPIは、RID (Resource ID) と呼ばれる32 33 32 4 533 21 32 33 5 4 821

一意の識別子に基づいています。これらのIDは、実際のメモリ参照ではなく不透明なポインタであり、リソースの作成、更新、削除を可能にします。この設計により、シーンツリーを介さずにRenderingServerを直接使用する開発者は、ツリーシステムのコスト(ノードのライフサイクル通知、トランスフォームの伝播など)から解放され、より効率的なシステムで作業できます。例えば、instance_set_transform()を使用してRIDベースのオブジェクトのトランスフォームを更新することは、Node3D.set_transform()で同じことを行うよりもはるかに高速です。この分離は、CPUパフォーマンスを最大化したいアプリケーションにとって重要なツールを提供します。

RenderingServerはさらに、call_on_render_thread(Callable)のようなメカニズムにより、メインアプリケーションスレッドから安全に呼び出されることを可能にします。これにより、メインスレッドがレンダリング処理専用のスレッドにタスクを送信できますが、この送信処理も同期ポイントであり、CPU時間を消費します。したがって、RenderingServerはより効率的なパスを提供する一方で、それを使用するために必要なメカニズム自体もCPUコストを伴うことを忘れてはなりません。CPU時間の測定は、get_frame_setup_time_cpu()のような関数で行うことができ、これはフレームの準備に費やされた合計CPU時間を提供します。これはRenderingServer内の処理の全体的な影響を理解するための貴重な指標です。結論として、Godotのレンダリングアーキテクチャは、プラットフォームの独立性と柔軟性を提供する一方で、CPU-GPU同期において精度を必要とし、潜在的なエラーを抱え、適切な最適化戦略を必要とする複雑な構造です。

2DレンダリングプロセスにおけるCPUボトルネックと最適化のトレードオフ

Godotにおける2Dレンダリングプロセスは、通常、より単純なジオメトリセットと少ないマテリアルで動作するため、3Dレンダリングと比較して異なるパフォーマンスダイナミクスを示します。2Dゲーム、特に数千のスプライトが同時に画面に表示されるトップダウンやサイドスクローラー形式のプロジェクトでは、CPU側でボトルネックが発生する傾向があります。この状況は、2Dレンダリングプロセスにおける最も基本的なCPUボトルネックが、数百または数千の個別のオブジェクトに対して個別にGPU Draw Callを送信することであることを示しています。各Draw Callは、APIインターフェース(OpenGL、Vulkanなど)を通過する際、GPUの現在の状態(アクティブなシェーダー、マテリアル、頂点フォーマットなど)を検証するためにかなりの量のCPU処理を必要とします。これらの検証処理は、APIの背後で実行されるバックグラウンドホスティングと見なされ、このコストはオブジェクトごとに行われる状態変更(state switching)によって指数関数的に増加します。したがって、2Dレンダリング最適化の主な目的は、これらの高コストな721 22 22 8 8 11 1619 19

API呼び出しを最小限に抑え、類似したオブジェクトを単一の処理でレンダリングするために「バッチング」(batching)技術を使用することです。Godotは、この問題の解決策としてUse Batching機能を提供します。この機能は、類似したマテリアルを持つスプライト、Polygon2D、またはCollisionPolygon2Dなどの他の2D要素を結合し、単一の頂点バッファに集約することで動作します。これにより、多くの個別のスプライトを単一のDraw Callでレンダリングできるようになり、結果としてDraw Callの数、ひいてはAPI検証の負荷が大幅に削減されます。この処理は、瞬間的に一定のCPUコストを必要とします。なぜなら、各フレームでスプライトのトランスフォームを取得し、頂点データを準備し、単一のバッファに配置する必要があるからです。しかし、この瞬間的なCPUコストは、Draw Callが大幅に削減されることで得られる利益よりもはるかに低く、通常は全体的なCPUパフォーマンスを向上させます。

しかし、バッチング技術自体も、CPU側で新たな潜在的ボトルネックを生み出すリスクを伴います。特に「バッファオーファニング」(buffer orphaning)と呼ばれる手法が使用されます。この手法では、同じ頂点バッファを1フレーム内で数百回再利用できます。これは、動的なコンテンツを持つシーンにおいて、メモリの割り当て/解放のコストを削減する効果的な方法です。しかし、この技術は、OpenGL標準の動的バッファ管理に関する不確実性のため、プラットフォームやグラフィックスドライバーによって一貫性のない結果をもたらす可能性があります。一部のドライバーは、この継続的な再利用を最適化できず、GPUドライバーのストールを引き起こし、パフォーマンスを低下させる可能性があります。これは、最適化がプラットフォーム固有の動作を示す可能性があり、プロジェクトごとに特定のテストを行う必要があることを強調しています。

例えば、あるユーザーの報告によると、Intel UHD 620統合GPUを搭載したWindows環境ではバッチングによってパフォーマンスが60 FPSから45 FPSに低下したのに対し、同じハードウェアのLinux環境では負の影響が全くなかったことが示されています。これは、2Dバッチングが強力な最適化ツールである一方で、開発者がこの機能の影響と潜在的なリスクを慎重に評価する必要があることを示しています。バッチングのCPUコストは、数万個のアイテムが処理されない限り通常は小さく留まりますが、ボトルネックは通常、頂点バッファのアップロード段階でグラフィックスドライバーレベルにシフトします。

最も顕著で、開発者が簡単に見落としがちな2D CPUボトルネックの例は、Polygon2Dクラスにおいてアニメーション中にメッシュが毎フレーム再生成されることです。この状況は「メッシュチャーン」(mesh churn)と呼ばれ、莫大なCPUコストを生み出す可能性があります。あるテストシナリオでは、AnimationPlayerでアニメーション化されたPolygon2Dオブジェクトにおいて、毎フレーム既存のメッシュを解放(free)し、新しい頂点データと共に再割り当て(作成)したところ、15.7ミリ秒のCPUコストが発生し、FPSを28から83に向上させる最適化の前には35 ms/フレームのフレームタイムを引き起こしていました。これは、パフォーマンスを最適化することが、時には直感的ではないものの、基本的なデータ構造の操作に焦点を当てる必要があるため、極めて重要であるという例です。(116 1 2 1 11 1 1 1 1 1 1130)

最適化が既存の頂点データを更新するのではなく新しいメッシュを作成する形で行われた場合、パフォーマンスは200%を超える向上が見られました。この状況は、データ構造がどのように管理されるかがCPUパフォーマンスに与える影響がいかに決定的であるかを証明しています。キャッシュフレンドリーなデータ構造と、ゴミを生成しないデータ更新は、特に2DレンダリングにおいてCPUボトルネックを防ぐための基本的な原則です。

バッチングは、メッシュチャーンのような根本的な構造的欠陥には対処できない場合があります。なぜなら、バッチングはすでに準備された頂点データをグループ化することを目的としているのに対し、メッシュチャーンは毎フレームこれらのデータがどのように作成されるかを誤って処理しているからです。結論として、2Dレンダリング最適化は、Draw Callの数を減らすことを目的としたトレードオフです。バッチングはこのトレードオフの最も一般的なツールですが、プラットフォーム固有のコストとリスクがあります。開発者にとって最大のCPUリスクは、データ構造を誤って管理し、「メッシュチャーン」のような決定的なボトルネックを生み出すことです。このようなエラーは、通常、直感的ではなく複雑に見える最適化ではなく、基本的なデータ操作の実践に焦点を当てることで防ぐべきです。

最適化技術

目的

CPUコスト (瞬間的)

CPUコスト (長期的)

リスクとプラットフォーム依存性

Draw Call数の削減

APIインターフェースの検証および状態変更のコストを削減する。

低 (通常は無視できる程度)

非常に高い (パフォーマンス向上)

情報なし

2Dバッチング

類似したマテリアルを持つスプライトを単一の頂点バッファに集約し、単一のDraw Callでレンダリングする。

中 (頂点データの準備、集約)

非常に高い (Draw Callの大幅な削減)

GPUドライバーに依存するパフォーマンスの変動、バッファオーファニングによるドライバーのストール。

スプライトアトラス

複数のスプライトを単一の大きなテクスチャ(アトラス)上に配置する。

低 (アトラス座標の計算)

高い (マテリアル変更回数を減らすことでDraw Call数を削減する)。

アトラスサイズの制限、アトラス管理の複雑さ。

メッシュチャーン (悪い習慣)

アニメーションや動的な変更により、毎フレームメッシュを解放して再作成する。

非常に高い (頂点配列の継続的な解放/割り当て)

負 (莫大なCPUコスト、FPS低下)

シーン内の任意の2Dオブジェクトでアニメーションを行う場合に発生する可能性がある。

3DレンダリングプロセスにおけるCPUボトルネック:カリング、物理演算、変換管理

3Dレンダリングプロセスは、2Dと比較してはるかに複雑なCPU集約型の処理セットを含んでおり、ボトルネックもこの複雑さに起因します。これらのボトルネックは、大きく分けて3つの主要なカテゴリに分類できます。不可視オブジェクトのフィルタリング(カリング)、物理シミュレーションと同期、そしてシーン内のオブジェクトとライトの管理です。カリングは、見えないオブジェクトがGPUに送信されるのを防ぐことで、大幅なパフォーマンス向上をもたらすプロセスです。

しかし、これらのカリング処理は、特に3Dシーンに数十万のオブジェクトが含まれる場合、重大なCPU負荷を引き起こす可能性があります。カリング処理は通常、CPUによって実行されます。まず、カメラの視野(Frustum)の外側にあるオブジェクトがフィルタリングされます。この処理は通常非常に高速ですが、大規模なシーンでは数十万のオブジェクトに対して適用されるためコストがかかります。より洗練された技術であるオクルージョンカリング(遮蔽カリング)は、最も効果的ですが、最もコストのかかるカリング技術です。

Godotは、この処理をCPU上でEmbreeと呼ばれるソフトウェアレイトレーシングライブラリを使用して実行します。エンジンは、OccluderInstance3Dノードでマークされたオクルーダー(遮蔽物)メッシュを、CPU側で低解像度の深度バッファにラスタライズします。その後、このバッファを参照して、他のオブジェクト(オクルーディー)が完全に隠れているかどうかをチェックします。オブジェクトの軸平行境界ボックス(AABB)が完全にオクルーダーによって隠されている場合、それはレンダリングされません。この処理は、特に複雑なオクルーダーメッシュやシーン内の動的なオクルーダーにとって、非常にCPU負荷の高い処理です。

オクルーダーのベイク処理や、動的なオクルーダーのBVH(Bounding Volume Hierarchy)構造を毎フレーム更新することは、重大なCPU負荷を生じさせます。そのため、オクルージョンカリングは屋内や複数の部屋がある環境で最も効果的であり、屋外のシーンではあまり有用ではありません。さらに、オクルージョンカリングの利点は使用するレンダラーに依存します。Forward+レンダラーは、オーバードローを減らすためにすでに深度プリパス(depth prepass)を実行しています。そのため、オクルージョンカリングは追加の利点を提供しますが、深度プリパスを行わないモバイルレンダラーの方が、より大きなパフォーマンスの差を生み出します。11 14 1619 111 1 2 1130

2番目の大きなCPUボトルネックは、物理シミュレーションとそれとシーンツリーとの同期であり、特に動的なシーンにおいて極めて重要です。Godot 4.6でJolt Physicsがデフォルトになったことは、速度と安定性の両面で大きな改善となりました。Joltは、より高度な衝突検出アルゴリズム(GJK/EPA)、より安定した制約ソルバー、および内部的なマルチスレッドサポートを提供します。

これにより、5,000個のアクティブな物理ボディを含むシーンで4.7倍高いFPSを達成しました。しかし、Joltがもたらすパフォーマンス向上にもかかわらず、PhysicsServerとシーンツリーの間で行われる状態同期(state sync)メカニズム自体がボトルネックとなる可能性があります。flush_queriesと呼ばれるこのプロセスは、アクティブな各物理オブジェクトに対してコールバック関数(_sync_body_state)をトリガーし、GDScript/C++側のノード(例えばRigidBody3D)の変換情報を更新します。

これは、オブジェクトごとに行われる個別のCallable呼び出しのため、特に3,000個以上のオブジェクトを含む大規模なシーンでは、物理ステップ関数のコストと同等のCPU負荷を生み出す可能性があります。(23 2428 24 29 2428 24 2428 12 12 15) この問題の解決策は、個別のクエリに対するAPIではなく、すべてのアクティブなオブジェクトの変換、速度などを単一の生データとして取得できる「バルククエリ」関数を追加することにあります。これにより、コールバックのコストとAPIインターフェースの煩雑さが解消され、大幅なCPU改善が実現します。開発者は、この自動状態同期をbody_set_state_sync_callback(Callable())で無効にし、手動で制御することも可能です。

3番目の重要なCPUボトルネックは、動的なシーンにおいて各オブジェクトのワールド変換をGPUに送信し、各オブジェクトを照らすために必要なライトとペアリングすることです。動的なオブジェクトの変換を更新するには、instance_set_transform()のようなRenderingServerの呼び出しが含まれます。これは、特に6,500個以上の動的メッシュを含むシーンにおいて、「Preparing to Render 3D scene」ステップのCPU使用率を50%削減できる可能性があります。

これは、ノードシステムの下にあるRIDベースのシステムと比較して、より効率的であるべきであることを意味します。オブジェクトとライトのペアリング(Object-Light Pairing)もCPUで行われます。各オブジェクトがシーン内のすべてのライトとオーバーラップおよび可視性マスクのチェックを行うことは、特に多数のライトと動的オブジェクトが存在するシーンではコストのかかる処理です。この計算は、最終的にレンダリングされない場合でもCPU時間を消費します。これらのボトルネックを克服する鍵は、MultiMeshInstanceのようなより効率的なAPIを使用することです。

MultiMeshInstanceは、数千個の同一メッシュを異なる変換でレンダリングすることをはるかに効率的に行い、その結果、6,500個の動的メッシュを単一のMMIで表現することで、パフォーマンスを平面からレンダリングするようなレベルに引き上げることができます。これは、SceneTreeを介さずにRenderingServerを直接使用することで得られるパフォーマンス向上と一致しています。

結論として、3DのCPUボトルネックは、カリング、物理演算、動的オブジェクト/ライト管理といった、より広範で複雑なシステムで構成されています。オクルージョンカリングは強力な最適化ですが、CPUを大量に消費します。物理演算と変換管理は、特にflush_queriesのようなAPIの制限により、潜在的なボトルネックを抱えています。これらのボトルネックを克服する鍵は、MultiMeshInstanceのようなより効率的なAPIを使用し、可能であればシーンツリーを介さずにRenderingServerを直接使用することです。 15 15 15 27 22 27 27 22 2227

ボトルネックカテゴリ

特定のプロセス

CPUコストの源泉

最適化手法

カリング

視錐台カリング

オブジェクトのAABBと視錐台の比較。

シンプルで高速であり、大規模なシーンでも効果的です。

カリング

オクルージョンカリング

CPU上でのEmbreeによるラスタライズ、オクルーダーBVHの構築と更新。

最もコストのかかるカリング技術であり、複雑なオクルーダーメッシュで悪化します。

物理演算と同期

物理シミュレーション (Jolt)

Joltのアルゴリズムの複雑さとマルチスレッド管理。

通常は効率的ですが、大規模なシーンでは依然として重大なCPU使用率になる可能性があります。

物理演算と同期

状態同期 (flush_queries)

アクティブな各物理オブジェクトに対する個別のCallableコールバックコスト。

バルククエリ関数(提案)、自動同期の無効化。

オブジェクトとライト管理

変換の更新

各動的オブジェクトのinstance_set_transform()のようなサーバー呼び出し。

MultiMeshInstanceの使用、RIDベースのRenderingServerの直接使用。

オブジェクトとライト管理

オブジェクトとライトのペアリング

各オブジェクトがシーン内のすべてのライトとオーバーラップおよびマスクチェックを行うこと。

ライト数の削減、遠距離/シャドウキャスティングライトの制限、LODの使用。

データ構造、スクリプトのパフォーマンス、および基本的な最適化戦略

GodotエンジンのCPUパフォーマンスは、レンダリングパイプライン内のステップだけでなく、開発者がシーン構造やスクリプトを記述する際に従う基本的な原則にも深く依存しています。CPUパフォーマンスの基盤は、アルゴリズムだけでなく、使用されるデータ構造のCPUキャッシュへの適合性によって形成されます。ランダムなメモリアクセスパターンは「キャッシュミス」と呼ばれる状況を引き起こし、CPUが低速なRAMからデータを取得するのを待機するアイドル時間を生じさせます。そのため、データをメモリ上に連続的かつ局所的に保持することで、キャッシュヒット率が向上し、結果としてパフォーマンスが大幅に改善されます。

GodotのサービスAPI(RenderingServerなど)は、機能レイヤーでこのような最適化を処理しますが、開発者が自身のコード(特にGDExtensionや複雑なGDScript構造)において、キャッシュに適したデータ構造を選択することが極めて重要です。2D CPUボトルネックの最も顕著な例は、Polygon2Dクラスにおいてアニメーション中に毎フレームメッシュを再構築することです。これは、パフォーマンスの最適化が、時には直感的ではないものの、基本的なデータ構造の操作に焦点を当てる必要があることを示す重要な例です。最適化を既存の頂点データを更新する形で行った場合、パフォーマンスが200%を超える向上が見られました。 23 2428 12 15 2227 27 14 14 14 1130 11

これは、データ構造の管理方法がCPUパフォーマンスに与える影響がいかに重要であるかを証明しています。同様に、シーン内に多数のノード(数千から数万)が存在する場合、SceneTreeが各ノードに_process()および_physics_process()の更新通知を伝播することもCPUコストとなります。これらのノードをシーン外に配置(Node.remove_child)したり、一時停止(set_process(false))したりすることで、このコストを削減できます。スクリプト言語の選択も、CPUパフォーマンスに大きく影響する要因です。GDScriptは、速度優先の言語というよりも、使いやすさを優先した言語です。これは、VM(仮想マシン)を介して解釈され、直接マシンコードにコンパイルされないことを意味します。

これは、C#や特にC++モジュール(GDExtensions)と比較して、より高いCPUコストを意味します。C#は.NETベースであるため、JITコンパイルのおかげで良好なパフォーマンスを提供しますが、GC(ガベージコレクション)のインターバルで待機が発生する可能性があります。C++は最も高速な選択肢ですが、より複雑なデプロイプロセスが必要です。これらのパフォーマンスの違いは、特に頻繁に呼び出されるループや重要なアルゴリズムにおいて、適切なスクリプト言語の選択を必要とします。GDScriptのパフォーマンスを向上させるために、オフラインコンパイラの利用が提案されています。このアプローチは、エクスポート時にGDScriptコードをC ABIと通信可能な共有ライブラリ(.dll、.soなど)にコンパイルすることで、VMの追加コストを排除します。しかし、これはiOS、WebAssembly、コンソールプラットフォームなど、動的なコード生成をサポートしていないプラットフォームにとっては大きな障壁となります。これは、スクリプトベースのゲーム開発と、高いパフォーマンスを必要とするエンジン内部のコーディングとの間の根本的な対立を浮き彫りにしています。

これらの基本的な要因を考慮すると、開発者がCPUパフォーマンスを向上させるために従うべき基本的な最適化戦略が明確になります。第一に、RenderingServerとPhysicsServerを直接使用して、SceneTreeのコストを回避することです。SceneTreeが各ノードに対して行うハウスキーピング(ライフサイクル通知、トランスフォームの伝播など)は、数千のオブジェクトを含む大規模なシーンでは大きなCPU負荷となります。RenderingServerを使用してRIDベースの軽量なエンティティを作成することで、このコストを大幅に削減できます。このアプローチは、特にAIキャラクター、パーティクル、その他の高密度オブジェクトに最適です。

第二の戦略は、MultiMeshInstanceのような組み込みのGPU支援最適化を最大化することです。数千の同一モデル(草、木、発射された弾丸など)をレンダリングする必要がある場合、MultiMeshInstanceはこれらのオブジェクトを単一のドローコールでレンダリングする機会を提供し、これは動的なシーンにおけるトランスフォームの更新やオブジェクトとライトのペアリングといったCPUボトルネックを解消する最も効果的な方法の一つです。第三の戦略は、データ構造とアルゴリズムをキャッシュに適したものにすることです。頂点データを絶えず再構築しないこと、データをメモリ上に線形に配置すること、不必要なメモリ割り当てや変換を避けることは、CPUのアイドル時間を削減します。 14 14 14 14 14 14 17 17 17 1422 22 2327

最後に、プロファイリングは、これらの最適化をどこで行うべきかを判断する唯一の信頼できる方法です。Godotの組み込みプロファイラー、特にFrame Time内のRenderingセクションは、レンダリング関連のCPUコストを分析するために使用できます。ただし、このプロファイラーは一部のサービス(物理、オーディオ)の待機時間を正確に測定できないことが報告されています。より詳細な分析には、Time.get_ticks_usec()のような高解像度タイマーを使用して独自のコードブロックを計測するか、Valgrind (Callgrind) のような外部のC++プロファイラーを使用してCPUホットスポットを特定する方が、より正確な結果が得られる可能性があります。

プロファイリングは、「最適化する前に測定せよ」という原則の最も具体的な適用であり、開発者が時間を最も効率的に費やすことを可能にします。これらの戦略は、CPUパフォーマンスを向上させるための予算管理のように考えることができます。シーンツリーのコストを節約し、RenderingServerを使用してより効率的なリソースを購入し、キャッシュに適したコードを書くことでプロセッサの各クロックサイクルを最大限に活用するのです。

最適化戦略

目的

適用範囲

期待されるCPUゲイン

SceneTreeコストの回避

ノードのライフサイクル通知、トランスフォームの伝播、およびその他のシーンツリーのオーバーヘッドを排除する。

高密度オブジェクト(AI、パーティクル、弾丸)。

シーンツリーのコストが節約され、特に数千のノードで重要。

MultiMeshInstanceの利用

数千の同一メッシュを異なるトランスフォームでレンダリングする処理を最適化する。

草、木、発射された弾丸、群衆。

トランスフォーム更新とオブジェクト・ライトペアリングのコストが解消され、ドローコール数が劇的に減少する。

データ構造のキャッシュ適合化

キャッシュミスを減らし、CPUのアイドル時間を最小化する。

アプリケーション全体、特に頂点操作、アルゴリズム。

CPUの各クロックサイクルをより効率的に使用し、全体的なパフォーマンスが向上する。

スクリプト言語の最適化

VMやGCなどの追加レイヤーによるCPUコストを排除する。

重要なアルゴリズム、頻繁に呼び出される関数。

C++ (GDExtension) > C# > GDScriptの順でパフォーマンスが向上。

プロファイリングによる集中

パフォーマンスに最も影響を与える領域(ホットスポット)を特定し、最適化の努力を向ける。

アプリケーション全体。

時間と労力を最大のCPUボトルネックに集中させ、不要な最適化を避ける。

合成:CPUパフォーマンスを決定する複雑な相互作用と将来の展望

この詳細な分析の結果、Godot 4.7のCPU側レンダリングアーキテクチャは、さまざまな要因の複雑な相互作用によって形成される動的なシステムとして浮かび上がります。 11 14 10 14 1014 1422 2327 1114 1417 1014。調査の主な目的である特定のCPUボトルネックを理解することは、全体的な合成と評価することで、開発者がエンジンの挙動を把握するのに役立ついくつかの基本的な結論を導き出せます。第一に、CPUとGPUの間の基本的なバランスが、レンダリング最適化の中心にあります。

2Dレンダリングでは、CPUがフレームを準備するために必要な時間(ドローコールの準備、カリングなど)がGPUのピクセルシェーディング時間よりも長くなることが多いため、CPUボトルネックがより一般的です。この文脈において、2Dバッチングのような技術は、CPUコストをGPUコストに変換する効果的なトレードオフです。3Dレンダリングでは状況がより複雑で、CPUとGPUのボトルネックが交互に発生する可能性があります。CPUはオブジェクトやライトをレンダリングするかどうかを決定するカリング処理で忙しく、GPUは可視ポリゴンの深度やライティングで忙しい状態です。この二重の関係は、各最適化が他の場所に影響を与えることを示しています。例えば、オクルージョンカリングはCPUに負荷をかけてGPUのシェーディング負荷を軽減できますが、これはPolygon2Dのような状況ではメッシュチャーンのような決定的なCPUボトルネックを引き起こす可能性があります。第二に、すべての最適化はトレードオフであり、開発者がシーンのニーズに応じて適切なバランスを見つけることが重要です。2DバッチングはCPUコストをGPUコストに変換しますが、高いLOD(詳細レベル)はCPUを酷使する代わりにGPUを酷使します。透明なオブジェクトはバック・トゥ・フロントのレンダリング順序を必要とするため、不透明なオブジェクトのようにマテリアルでバッチ処理できず、本質的にコストが高くなります。

これらのトレードオフはプロジェクトごとに異なる結果をもたらす可能性があり、最適な戦略は通常、シーンの内容、ターゲットプラットフォームの容量、ユーザーの美的要求を考慮して実験的に決定されるべきであることを意味します。RenderingDeviceやRenderingServerのような抽象化レイヤーは、開発者により多くの制御とパフォーマンスを提供しますが、実際には別のCPUコストを生み出します。これらのレイヤーはSceneTreeを覆い隠し、より効率的なRIDベースのアプローチへの移行の必要性と、そのアプローチがもたらす複雑さ(例:ワールドトランスフォームを手動で管理する)を明らかにします。これは、抽象化が提供する容易さが、パフォーマンスが重要である場合にはより深いアーキテクチャの理解を必要とすることを示しています。

第三に、CPUパフォーマンスは、アルゴリズムだけでなく、使用されるデータ構造の基本的な特性にも依存します。Polygon2Dの例は、適切なデータ操作がいかに重要であるかを証明しています。データは繰り返し作成されるのではなく更新されるべきであり、メモリ上に連続して配置され、CPUキャッシュの効率的な使用を確保する必要があります。これらの原則は、特にGDExtensionのようなC++ベースの高性能なコードを書く際に不可欠です。さらに、GDScriptのようなスクリプト言語のVMベースの構造は、C++モジュールと比較して自然なCPUコストをもたらし、これは特にパフォーマンスが重要なアルゴリズムにおいてGDExtensionの使用を強制する可能性があります。これらの基本的な構造的要因は、レンダリングパイプライン自体よりも前、または同時に最適化される必要があります。

将来の展望を検討すると、レンダリング技術の進歩は、レンダリングパイプラインにおけるCPUの役割を変えることを目指しています。GPU駆動レンダリングのような高度な最適化に向けた研究は、CPUがフレームを準備するために必要なすべてのディスパッチ(オブジェクト、ライト、パス用)をGPUに送信し、その後はGPUが処理を完了するのを待つことを目的としています。このアーキテクチャは、CPUのアイドル時間を最小限に抑えることで、潜在的に大きなパフォーマンス向上をもたらす可能性があります。しかし、このアプローチは、現在のCPU駆動の透明レンダリングパスのような一部の処理をCPUで継続する必要性を生み出し、これがアーキテクチャの新たなボトルネックとなる可能性があります。このようなアーキテクチャの変更は、大規模な再構築と既存のゲームフローの変更を伴うでしょう。

結論として、提供された資料に照らすと、GodotのCPU側レンダリングアーキテクチャは、プラットフォームの独立性を確保するために設計された柔軟な構造を表していますが、同時に同期エラー、プラットフォーム固有の挙動、複雑な最適化のトレードオフなど、かなりの量のCPUパフォーマンスリスクを抱えています。開発者は、このアーキテクチャとボトルネックを理解することで、より効率的で、よりスムーズで、より高性能なゲームを作成するための正しい戦略に従うことができます。211 2327 1124 16 1619 522 22 1130 14 14

*情報:画像はGoogle Geminiによって生成されました。

Godot4.7GDScriptDrawCallJoltPhysicsOptimizationVulkan
ディープリサーチツール
Qwen (Alibaba Cloud)
Qwen (Alibaba Cloud)
翻訳ツール
Google Gemini
Google Gemini

共有