6
4

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 5 years have passed since last update.

[Unity]GeometryUtiliy.CalculateFrustumPlanesのメモリアロケート回避

Last updated at Posted at 2016-10-27

#経緯
OnBecameInvisible系はSceneカメラも判定されて扱いづらいので明示的な視錘台カリングを行いたかった。
GeometryUtiliy.CalculateFrustumPlanesなる汎用関数が用意されていて嬉々として採用。
しかしコールする度にnew Plane[6]のメモリアロケートがされていたことがわかり発狂した。
中を切り開いたらラッパー関数が邪魔だったので
カッとなってInternal Methodのデリゲートを取得して扱うようにした

#元のコード

UnityEngine.GeometryUtility
using System;
using System.Runtime.CompilerServices;

namespace UnityEngine
{
    public sealed class GeometryUtility
    {
        public static Plane[] CalculateFrustumPlanes(Camera camera)
        {
            return GeometryUtility.CalculateFrustumPlanes(camera.projectionMatrix * camera.worldToCameraMatrix);
        }

        public static Plane[] CalculateFrustumPlanes(Matrix4x4 worldToProjectionMatrix)
        {
            Plane[] array = new Plane[6];    // Why Unity People!!!???
            GeometryUtility.Internal_ExtractPlanes(array, worldToProjectionMatrix);
            return array;
        }

        private static void Internal_ExtractPlanes(Plane[] planes, Matrix4x4 worldToProjectionMatrix)
        {
            GeometryUtility.INTERNAL_CALL_Internal_ExtractPlanes(planes, ref worldToProjectionMatrix);
        }

        [WrapperlessIcall]
        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern void INTERNAL_CALL_Internal_ExtractPlanes(Plane[] planes, ref Matrix4x4 worldToProjectionMatrix);

        public static bool TestPlanesAABB(Plane[] planes, Bounds bounds)
        {
            return GeometryUtility.INTERNAL_CALL_TestPlanesAABB(planes, ref bounds);
        }

        [WrapperlessIcall]
        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern bool INTERNAL_CALL_TestPlanesAABB(Plane[] planes, ref Bounds bounds);
    }
}

#対策コード

Test.cs
using UnityEngine;
using System.Reflection;

public class Test : MonoBehaviour {
    // 定義が面倒ならSystem.Action<Plane[], Matrix4x4>でもいい
    private delegate void FrustmCallback(Plane[] planes, Matrix4x4 worldToProjectMatrix);
    private FrustmCallback CalculateFrustumPlanes = null;

    private Plane[] planes = new Plane[6]; // Plane配列を使いまわして余計なヒープ確保をさせない

    void Awake () {
        // MEMO: 自前でやってもいいのだがInternalはネイティブコードを呼び出すのでこっちの方が早い・・・と思うのだけれど
        var meth = typeof(GeometryUtility).GetMethod("Internal_ExtractPlanes",
                                                     BindingFlags.Static | BindingFlags.NonPublic,
                                                     null,
                                                     new System.Type[] { typeof(Plane[]), typeof(Matrix4x4) },
                                                     null);
        this.CalculateFrustumPlanes = System.Delegate.CreateDelegate(typeof(FrustmCallback), meth) as FrustmCallback;
    }

    void Update () {
        // カメラの視錘台を取得
        this.CalculateFrustumPlanes(this.planes, Camera.main.projectionMatrix * Camera.main.worldToCameraMatrix);
        // Boundsサイズは任意
        Bounds bound = new Bounds(this.transform.position, Vector3.one);
        // 視錘台判定はそのまま使う
        if (!GeometryUtility.TestPlanesAABB(this.planes, bound))
            Debug.LogWarning("Out of Camera");
    }
}
link.xml
<linker>
	<assembly fullname="UnityEngine">
		<type fullname="UnityEngine.GeometryUtility" preserve="all"/>
	</assembly>
</linker>

#備考
IL2CPPを有効にした場合、link.xmlでコードストリッピングを明示的に回避しておかないと
GetMethodの戻り値がnullになってしまう。
特にiOSはIL2CPP必須なので要注意。
ファイルの置き場はAssets以下のどこでも構わないようだ。

#結論
メモリアロケートを行わないIL2CPP対応のCalculateFrustumPlanesを実装できた。
こんなアングラなことするぐらいなら自前で計算すればいいんじゃって異論は認める。

#参考URL
[Internal Methodの取得]
https://community.unity.com/t5/Scripting/CalculateFrustumPlanes-without-allocations/m-p/2411237
[link.xmlについて]
https://docs.unity3d.com/ja/current/Manual/iphone-playerSizeOptimization.html
[UnityEngineアセンブリのlink.xml定義サンプル]
https://github.com/aws/aws-sdk-net/blob/master/Unity.README.md#unity-sdk-fundamentals

6
4
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
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?