役に立たない序文
昔、DDC のコードを観察して表面的な知識を学びました。
最近、DDC関連の問題に遭遇したので、以前書いたものを見直してみました。同時に、この問題に関する私の最近の研究の重要な部分も備忘録として記録しておきます。
目標
StaticMesh が対応する DDC ファイルをロードするプロセスを観察し、重要なポイントのいくつかを記録します。
(注: ここでのエンジンのバージョンは 4.26 であり、UE5 の DDC 関連のコードは大幅に調整されています)
0. コードの開始位置
DDC インターフェイスのGetSynchronous
sum関数GetAsynchronous
は、DDC データを取得するための統合インターフェイスであり、ここでブレークポイントをトリガーする必要があります。
ただし、今は特定の StaticMesh の DDC だけを観察したいので、最初から内側GetSynchronous
と内側のブレークポイントにヒットすると、他の多くのリソースが壊れてしまいます。したがって、最初に StaticMesh が DDC インターフェイスを呼び出す場所にブレークポイントを設定し、次に次のように観察したい StaticMesh リソースをダブルクリックしてロードすることを選択します。 その後、ブレークポイントがトリガーされます: ( Ownerの値を通じて StaticMesh リソースをトリガーしました。これは観察したいものです) StaticMesh が DDC データを取得する準備ができた瞬間です。GetAsynchronous
次に、GetSynchronous
関数内にブレークポイントを設定してトリガーします: (このリソースはCacheKey値で確認できます)
ここで呼び出される関数は Get Synchronousで、これは同期方法であるため、このタスクは (別のスレッドで実行されるのではなく) すぐに実行されます。
GetSynchronous 関数の中心は、FAsyncTask<FBuildAsyncWorker>
タスクを実行するための を作成することです。
1.FBuildAsyncWorker
FAsyncTask
非同期(または同期)実行できるタスクが整理されており、基本的な使い方は前回の記事 にあります。
タスクの具体的な内容はFBuildAsyncWorker
定義で定義されており、いくつかのメンバー変数があり、それらの説明は次のコメントで参照できます。
/** true in the case of a cache hit, otherwise the result of the deriver build call **/
bool bSuccess;
/** true if we should record the timing **/
bool bSynchronousForStats;
/** true if we had to build the data */
bool bDataWasBuilt;
/** Data dervier we are operating on **/
FDerivedDataPluginInterface* DataDeriver;
/** Cache key associated with this build **/
FString CacheKey;
/** Data to return to caller, later **/
TArray<uint8> Data;
実行ロジックはDoWork()
関数内にあり、その中で最も重要なのはFDerivedDataBackend
DDC データを取得することです。
- 成功すると、ローカル変数が
bGetResult
true として記録され、FBuildAsyncWorker
その変数自体のメンバーもbSuccess
true になり、メンバーにData
も値が含まれます。 - 失敗した場合は
bSuccess
false、Data
または空になります。
このようにして、タスクが終了すると、DDC が正常に取得できたかどうか、および DDC データが何であるかを、FBuildAsyncWorker
自身のメンバー変数の値bSuccess
と値を通じて知ることができます。Data
次に見る必要があるのは、FDerivedDataBackend::Get().GetRoot().GetCachedData
2. DerivedDataBackend がたくさんありますか?
「DerivedDataBackendInterface のサブクラス間の関係の観察」でわかるように、DerivedDataBackend は複数のサブクラスで構成されており、それらは並列ではなく入れ子になっています。
したがって、ここでのルートとしての DerivedDataBackend は DerivedDataLimitKeyLengthWrapper:
ですが、その後、他の DerivedDataBackend を呼び出して DDC データを取得しようとします。
UE がこのような多数の Backend の構造で設計されているのには理由があるはずですが、それは明確に理解していないため、説明しません。
ただし、1 つ確かなことは、DDC データが最終的にファイルを通じて取得された場合は、それを渡す必要があるということですFFileSystemDerivedDataBackend
。
3. FFileSystemDerivedDataBackend::GetCachedData の下のブレークポイントで、対応するファイルのパスを取得します
次に、ブレークポイントFFileSystemDerivedDataBackend::GetCachedData
の下で、Filename
値を通じて対応する DDC ファイル パスを知ることができます。
このファイルはディスク上で直接見つけることができます。
4*. キャッシュ欠落の再構築
後でこのファイルを手動で削除して、DDC ファイルが見つからない状況をシミュレートすることもできます。
この時点で見えるFBuildAsyncWorker
ものはDoWork()
ここに行き、終わった後はbSuccess
偽りで空虚なものになります。yes を返すため、 false を返します。Data
GetSynchronous
PendingTask.GetTask().bSuccess
その後、StaticMesh は呼び出し後に別のブランチに移動します。
その後、DDC データが再構築されます。
最後に、GetDerivedDataCacheRef().Put
電話した後:
DDC ファイルは再度保存されます (この呼び出しは非同期である必要があるため、この関数が戻った直後にはファイルは生成されません)。
要約する
- StaticMesh は DDC の共通インターフェイスを呼び出して
GetSynchronous
DDC データを取得します GetSynchronous
FAsyncTask<FBuildAsyncWorker>
タスクを実行するために内部的に を作成します。FBuildAsyncWorker
がDoWork()
そのコアであり、DerivedDataBackend を通じて DDC データを取得していることがわかります。- 多数の DerivedDataBackend が相互に接続され、DDC データを検索するロジックを定義します。
FFileSystemDerivedDataBackend::GetCachedData
次のブレークポイントでリソースをロードする DDC ファイルのパスを知ることができます。
最近の質問に注意してください
スタックで発生した最新の問題は、StaticMesh によってロードされた DDC データ内の配列の要素サイズが現在の定義と一致しないことです。一般に、DDC の問題に対して最初に試みることは、DDC をクリアして再生成することです。しかし、クリアした(と思った)後も、同じエラーが発生しました。
最後に、StaticMesh に対応する DDC ファイルの特定のパスを見つけることでわかりました。データを取得するためのファイルはクリアされず、ローカルではなく共有の場所にありました。そして、この場所はかなり前に非推奨になっています。この場所が見つかった理由は、環境変数が以前に構成されていたためです。