Edited at

【Unity】ハードウェアアクセラレーションが有効にならない、AndroidManifestを書き換えても無効になってしまう問題の解決


問題の概要

AndroidManifestで有効にしたHardware accelerationが反映されない問題。

AndroidManifestでHardware accelerationのactivityをtrueにしても反映されない。

あまり日本語の記事が無かったので投稿。


予備知識

https://developer.android.com/guide/topics/graphics/hardware-accel

https://usedoor.jp/howto/web/code/android-dev-webview-scroll-kousokuka-hardwareaccelerated/

https://www.teradas.net/archives/12229/

Hardware accelerationがOFFになっているとHTML5のビデオ再生が使えなくなったり、WebViewのスクロールが重くなってガクガクになる。

ただこれをONにすることによってGPUレンダリングされることになるので、パフォーマンスが落ちたりするケースがある。(電池消費が激しくなったり)


問題の原因

Unity2018から?強制的に、Hardware accelerationがfalseに書き換わっているとのこと。

Unityの中の人がパフォーマンスが低下してしまうため、強制的に無効化するようにしたらしい。 (えぇ....)

そのことについてのフォーラムページ

https://forum.unity.com/threads/android-hardwareaccelerated-is-forced-false-in-all-activities.532786/?gq=NativeActivity


どのように解決したか


If you are absolutely sure you need the hardware acceleration, you can to the following:

- Extract your native UI into a separate activity and enable hardware acceleration there (preferred way)

- Modify the Unity manifest, for examply by utilizing IPostGenerateGradleAndroidProject interface (sample here: https://github.com/Over17/UnityAndroidManifestCallback)

※フォーラムから抜粋


Gradleビルドするときに呼ばれるインターフェース(IPostGenerateGradleAndroidProject)を実装して、そのコールバックにアクセラレーションを有効にするManifestを書き換えればOK。このインターフェースはAndroidManifestをUnityが強制的に書き換えた後に呼ばれるので、書き換えが可能になる。

BuildSystemが「Internal」だったら「Gradle」にすること。

このインターフェースを実装しているsampleがgithubにあるので、参考にしました。

GitHub - Over17/UnityAndroidManifestCallback: Apply your changes to Android Manifest in Unity 2018.1+ without having to override the main manifest of the whole project

以下のスクリプトをAssets/Editor/に入れる。


ModifyUnityAndroidAppManifest.cs

public class ModifyUnityAndroidAppManifest : IPostGenerateGradleAndroidProject

{
public readonly string ActivityName = "com.hoge.ui.UiActivity";
public readonly string ThemeName = "@style/FloatingActivityTheme";

public void OnPostGenerateGradleAndroidProject(string basePath)
{
var androidManifest = new AndroidManifest(GetManifestPath(basePath));

Debug.LogFormat("**************OnPostGenerateGradleAndroidProject BasePath={0}, ManifestPath={1}**************", basePath, GetManifestPath(basePath));
// 必要であれば
//androidManifest.SetApplicationTheme(ThemeName);
//androidManifest.SetStartingActivityName(ActivityName);

// manifestを書き換えるハードウェアアクセラレーションを有効にする
androidManifest.SetHardwareAccel();
// Add your XML manipulation routines

androidManifest.Save();
Debug.LogFormat("**************OnPostGenerateGradleAndroidProject Done!**************");
}

public int callbackOrder { get { return 1; } }

private string _manifestFilePath;

private string GetManifestPath(string basePath)
{
if (string.IsNullOrEmpty(_manifestFilePath))
{
var pathBuilder = new StringBuilder(basePath);
pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml");
_manifestFilePath = pathBuilder.ToString();
}
return _manifestFilePath;
}
}

internal class AndroidXmlDocument : XmlDocument
{
private string m_Path;
protected XmlNamespaceManager nsMgr;
public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
public AndroidXmlDocument(string path)
{
m_Path = path;
using (var reader = new XmlTextReader(m_Path))
{
reader.Read();
Load(reader);
}
nsMgr = new XmlNamespaceManager(NameTable);
nsMgr.AddNamespace("android", AndroidXmlNamespace);
}

public string Save()
{
return SaveAs(m_Path);
}

public string SaveAs(string path)
{
using (var writer = new XmlTextWriter(path, new UTF8Encoding(false)))
{
writer.Formatting = Formatting.Indented;
Save(writer);
}
return path;
}
}

internal class AndroidManifest : AndroidXmlDocument
{
private readonly XmlElement ApplicationElement;

public AndroidManifest(string path) : base(path)
{
ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
}

private XmlAttribute CreateAndroidAttribute(string key, string value)
{
XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
attr.Value = value;
return attr;
}

internal XmlNode GetActivityWithLaunchIntent()
{
return SelectSingleNode("/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and " +
"intent-filter/category/@android:name='android.intent.category.LAUNCHER']", nsMgr);
}

internal void SetApplicationTheme(string appTheme)
{
ApplicationElement.Attributes.Append(CreateAndroidAttribute("theme", appTheme));
}

internal void SetStartingActivityName(string activityName)
{
GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("name", activityName));
}

// ハードウェアアクセラレーションの有効
internal void SetHardwareAccel()
{
GetActivityWithLaunchIntent().Attributes.Append(CreateAndroidAttribute("hardwareAccelerated", "true"));
}
}



備考


  • VoxelBustersの中の人が上記のフォーラムページにて「いいね!良い情報をもらったので、修正できるよ!!」と言っているが今現在実装してなさそう。(2019/4/18)

  • IPostGenerateGradleAndroidProject は2018.3からしか使えないらしい。 これ以外のバージョンはアップデートをするかパッケージを用意してAndroidプロジェクトにエクスポートしないといけない(面倒くさい)