0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Unreal Engine マテリアル更新のボトルネック

Last updated at Posted at 2023-01-11

はじめに

備忘録のようなものは需要がないと思いますが, 少しの価値はありそうなので.
Epic Gamesに直接要望だせばと思われるかもしれませんが, 明らかな不具合でない限り改善されるかどうか不確かですし, そもそも直るとも思えないので改造してしまった方が早いでしょう.

マテリアルのパラメータ更新

UEのマテリアルはランタイムではイミュータブルになります. これを更新可能にするには, マテリアルからUMaterialInstanceDynamicを作成します. UMaterialInstanceDynamicがパラメータを上書きするプロキシになるわけです.

このUMaterialInstanceDynamicを通してパラメータ更新をするとき, 現在のパラメータ値と異なる値で更新しようとしたとき, 最後はMaterialInstanceSupport.hFMaterialInstanceResource::RenderThread_FindParameterByNameInternalに到達します.
ここが5.03以降のボトルネックになります.

5.03

問題はなさそうとはいえ, なんでint使ってんだなどのツッコミどころはいろいろありますが, なんとなくカッコイイアルゴリズムのように見えます.

template <typename ValueType>
int RenderThread_FindParameterByNameInternal(const FHashedMaterialParameterInfo& ParameterInfo, bool& OutWasFound) const
{
    const TArray<TNamedParameter<ValueType> >& ValueArray = GetValueArray<ValueType>();

    TNamedParameter<ValueType> SearchParam;
    SearchParam.Info = ParameterInfo;

    int Index = Algo::LowerBound(ValueArray, SearchParam,
            [](const TNamedParameter<ValueType>& Left, const TNamedParameter<ValueType>& Right)
            {
            return GetTypeHash(Left.Info) < GetTypeHash(Right.Info);
            });

    uint32 SearchHash = GetTypeHash(ParameterInfo);

    while (ValueArray.IsValidIndex(Index) && GetTypeHash(ValueArray[Index].Info) == SearchHash)
    {
        if (ValueArray[Index].Info == ParameterInfo)
        {
            OutWasFound = true;
            return Index;
        }

        Index++;
    }
    OutWasFound = false;
    return Index;
}

問題なのはGetTypeHashで, FHashedMaterialParameterInfoのハッシュ計算には, GetTypeHash(FName)HashCombine(uint32,uint32)が2回必要です. GetTypeHash(FName)は, 加算4回+シフト3回, HashCombine(uint32,uint32)は, 加算1回+減算18回+排他論理和9回+シフト9回です.

GetTypeHash(FHashedMaterialParameterInfo)は, 加算6回+減算36回+排他論理和18回+シフト21回になります.

ハッシュ値計算やデータ構造の複雑さからくるオーバーヘッドの実験はこちら, STLでFNVを使用している場合やはりハッシュ値計算は遅いです.
小さいサイズの辞書は線形探索でいいかも

4.XX以前

更新されるパラメータが数百個に及ばない限り, 何もする必要がなかったのです. 線形探索しているだけなので遅いように見えますが, FHashedMaterialParameterInfoの比較は高々整数の比較4回なので, 多くの環境でハッシュ値を計算している間に数十個の要素を比較できることでしょう.

TArray<TNamedParameter<ValueType> >& ValueArray = GetValueArray<ValueType>();
const int32 ParameterCount = ValueArray.Num();
for (int32 ParameterIndex = 0; ParameterIndex < ParameterCount; ++ParameterIndex)
{
    TNamedParameter<ValueType>& Parameter = ValueArray[ParameterIndex];
    if (Parameter.Info == ParameterInfo)
    {
        Parameter.Value = Value;
        return;
    }
}

5.1以降

やはりまずいと思ったのか, 大改造されました. TMapではなく, ここだけの専用のハッシュマップとハッシュ関数に変わりました. 計測はしていませんが, 数十のパラメータ更新ならまだ線形探索の方が速いでしょう.

まとめ

ハッシュマップからの検索には, ハッシュ値の計算が最低でも1回必要ということが知られていないと思います. 線形探索した方が速いことはよくあります.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?