VkQuality Unity 引擎插件

适用于 Unity 引擎的 VkQuality 插件可提供图形 API(Vulkan 或 OpenGL ES)的启动时建议,以在特定设备上用于您的游戏。

VkQuality 建议在一组比 Unity 引擎默认许可名单更受限的设备上使用 Vulkan。使用 VkQuality 来获得 Vulkan 的性能优势,同时仅允许搭载较新显卡驱动程序的新款设备使用 Vulkan,从而减少游戏面临的驱动程序问题。VkQuality 只提供质量建议,而不保证质量,因为在推荐设备上仍有可能遇到驱动程序问题。VkQuality 支持自定义列表,让您能够为游戏添加或移除设备推荐。

在 Unity 引擎游戏中启用 Vulkan

VkQuality 要求游戏在 Unity 项目设置中同时启用 OpenGL ES 和 Vulkan 渲染程序。使用 Auto Graphics API 选项或手动设置图形 API 来启用渲染程序。

获取适用于 Unity 引擎的 VkQuality 插件

从 GitHub 下载 VkQuality 插件。该插件与 Unity 2021 及更高版本兼容。使用 Unity 2021 LTS 或更高版本,以在 Android 上启用 Vulkan。插件软件包包含一个基本示例项目,该项目使用插件在启动时设置图形 API,然后显示一个设置为设备活跃图形 API 的字符串。

管理 VkQuality Vulkan 建议列表

VkQuality 包含受支持设备的默认推荐列表。如需了解如何使用自定义建议列表,请参阅使用自定义建议列表部分。

建议列表包括三个类别:

  • Vulkan 设备许可名单
  • GPU 建议许可名单
  • GPU 建议拒绝列表

设备许可名单匹配项

VkQuality 首先检查活跃设备是否包含在设备许可名单中,以及它是否运行的是该设备许可名单中指定的最低 Android 版本和 Vulkan 驱动程序版本。如果满足这些条件,VkQuality 就会返回 RECOMMENDATION_VULKAN_BECAUSE_DEVICE_MATCH 枚举值来推荐使用 Vulkan。

如果设备在许可名单中,但其运行的 Android 版本或驱动程序版本低于许可名单中指定的最低要求,VkQuality 会返回 RECOMMENDATION_GLES_BECAUSE_OLD_DRIVER 来建议 OpenGL ES。

GPU 建议匹配项

如果在设备许可名单中未找到匹配的设备,VkQuality 会根据 GPU 建议允许和拒绝列表评估 GPU 型号和驱动程序版本。如果 GPU 型号和驱动程序版本与 GPU 建议许可名单中的某个条目匹配,VkQuality 会返回 RECOMMENDATION_VULKAN_BECAUSE_PREDICTION_MATCH 枚举常量,从而推荐 Vulkan。

如果 GPU 型号和驱动程序版本与 GPU 建议拒绝列表中的条目匹配,VkQuality 会返回 RECOMMENDATION_GLES_BECAUSE_PREDICTION_MATCH 来推荐 OpenGL ES。

无匹配项的推荐内容

如果未找到匹配项,当正在运行的设备的 Android API 级别等于或高于推荐列表中的 Future API 级别时,VkQuality 会推荐使用 Vulkan。默认建议列表的 Future API 级别为 36,这意味着在搭载 API 级别 36 或更高级别的不匹配设备上,VkQuality 会返回 RECOMMENDATION_VULKAN_BECAUSE_FUTURE_ANDROID 枚举常量。

如果在设备许可名单或 GPU 推荐列表中未找到匹配项,并且设备的 API 级别低于 Future API 级别,VkQuality 就会返回 RECOMMENDATION_GLES_BECAUSE_NO_DEVICE_MATCH 来推荐 OpenGL ES。

将 VkQuality 归档文件添加到您的项目中

VkQuality 插件是 VkQuality-1.x.x.aar 文件,位于下载的软件包归档的 Assets/Android/Plugins 目录中。.aar 文件的实际版本号与软件包名称的版本号一致。如需安装该插件,请执行以下步骤:

  1. 将 .aar 文件复制到项目的 Assets/Android/Plugins 目录中。(如果所需的 AndroidPlugins 子目录不存在,请创建这些子目录。)
所需项目目录中的 VkQuality .aar 文件。
图 1. 所需项目目录中的 VkQuality .aar 文件。
  1. 在 Unity 的 Project 层次结构中选择 VkQuality-1.x.x 插件文件,在 Inspector 窗格中显示其 Import Settings。确保已选中 Android 平台。
图 2. VkQuality 插件平台导入设置。
图 2. VkQuality 插件平台导入设置。

使用自定义 activity 调用 VkQuality

与典型的 Unity 引擎插件不同,在初始化 Unity 引擎之前,必须执行 VkQuality 以获取图形 API 建议。然后,使用 Unity 播放器命令行参数功能,根据 VkQuality 建议来设置图形 API。在 Android 上,传递命令行参数需要通过创建自定义 activity 来替换 UnityPlayerActivity 的默认行为。

如果您的游戏已在使用自定义 activity,请参阅将 VkQuality 添加到现有的自定义 activity 部分。如需为游戏创建新的自定义 activity,请参阅向 Unity 项目添加自定义 activity(下文)。

将自定义 activity 添加到 Unity 引擎项目

Assets/Plugins/Android/VkQualityTestActivity.java 中的插件软件包中包含使用 VkQuality 的自定义 activity 示例。如需自定义该文件并在游戏中使用,请执行以下步骤:

  1. VkQualityTestActivity.java 文件复制到 Assets/Plugins/Android 目录中。
  2. 将其重命名为适合游戏的名称(例如 MyGameActivity.java)。
  3. 用文本编辑器打开该文件。
  4. 将类名称从 VkQualityTestActivity 更改为您为文件指定的名称(例如 MyGameActivity.java)。
  5. 更改 com.google.android.games.VkQualityTest 中的软件包名称,以匹配 Unity 项目设置 Player 类别中 Other Settings 下的 Package Name 字段的值(例如 com.mycompany.mygame)。
  6. 保存并关闭该文件。

添加引用您的自定义 activity 的自定义清单文件,并告知 Unity 使用您的自定义清单文件:

  1. AndroidManifest.xml 文件从插件软件包的 Assets/Plugins/Android 目录复制到项目的 Asset/Plugins/Android 目录中。
  2. 用文本编辑器打开该文件。
  3. activity android:name 设置的值从 com.google.android.games.VkQualityTest.VkQualityTestActivity 更改为您在前面步骤中使用的软件包和 activity 名称(例如 com.mycompany.mygame.MyGameActivity)。
  4. 保存并关闭该文件。
  5. 打开 Unity 设置窗口,然后选择 Player 设置。展开发布设置部分,然后选中自定义主清单复选框。
图 3. Unity Player 设置中的“Custom Main Manifest”选项。
图 3. Unity Player 设置中的 Custom Main Manifest 选项。

您的项目现已设置为使用自定义 activity,该 activity 会在启动时调用 VkQuality,并根据 VkQuality 建议选择 Vulkan 或 OpenGL ES。

将 VkQuality 添加到现有的自定义 activity

如果您的游戏已有替换默认 UnityPlayerActivity 的自定义 activity,请添加以下代码来集成 VkQuality 建议:

首先,将 VkQuality 导入语句添加到自定义 activity 文件顶部的导入列表中:

Kotlin

import com.google.android.games.vkquality.VKQuality;

Java

import com.google.android.games.vkquality.VKQuality;

接下来,在 Activity 类的正文中为图形 API 选项创建一些常量:

Kotlin

companion object {
  private const val OVERRIDE_NONE = 0
  private const val OVERRIDE_GLES = 1
  private const val OVERRIDE_VULKAN = 2

Java

private static final int OVERRIDE_NONE = 0;
private static final int OVERRIDE_GLES = 1;
private static final int OVERRIDE_VULKAN = 2;

创建一个变量来跟踪 API 选择:

Kotlin

private var apiOverride = OVERRIDE_NONE

Java

private int apiOverride = OVERRIDE_NONE;

将以下函数添加到 Activity 类:

Kotlin

private fun CheckVkQuality() {
    val vkQuality = VKQuality(this)
    val startResult = vkQuality.StartVkQuality("")
    if (startResult == VKQuality.INIT_SUCCESS) {
        // In the current release, we can assume GetVkQuality is
        // ready as soon as StartVkQuality has returned success.
        val getResult = vkQuality.GetVkQuality()
        LogVkQualityResult(getResult)
        apiOverride =
            when (getResult) {
                VKQuality.RECOMMENDATION_VULKAN_BECAUSE_DEVICE_MATCH,
                VKQuality.RECOMMENDATION_VULKAN_BECAUSE_PREDICTION_MATCH,
                VKQuality.RECOMMENDATION_VULKAN_BECAUSE_FUTURE_ANDROID -> OVERRIDE_VULKAN
                VKQuality.RECOMMENDATION_GLES_BECAUSE_OLD_DEVICE,
                VKQuality.RECOMMENDATION_GLES_BECAUSE_OLD_DRIVER,
                VKQuality.RECOMMENDATION_GLES_BECAUSE_NO_DEVICE_MATCH,
                VKQuality.RECOMMENDATION_GLES_BECAUSE_PREDICTION_MATCH -> OVERRIDE_GLES
                else -> OVERRIDE_GLES
            }
        vkQuality.StopVkQuality()
    } else {
        Log.e("VKQUALITY", "VkQuality start failed with result: $startResult")
    }
}

Java

private void CheckVkQuality() {
  VKQuality vkQuality = new VKQuality(this);
  // An empty string specifies use of the default
  // built-in device list file.
  int startResult = vkQuality.StartVkQuality("");
  if (startResult == VKQuality.INIT_SUCCESS) {
      // In the current release, we can assume GetVkQuality is
      // ready as soon as StartVkQuality has returned success.
      int getResult = vkQuality.GetVkQuality();

      switch (getResult) {
          case VKQuality.RECOMMENDATION_VULKAN_BECAUSE_DEVICE_MATCH:
          case VKQuality.RECOMMENDATION_VULKAN_BECAUSE_PREDICTION_MATCH:
          case VKQuality.RECOMMENDATION_VULKAN_BECAUSE_FUTURE_ANDROID:
              apiOverride = OVERRIDE_VULKAN;
              break;
          case VKQuality.RECOMMENDATION_GLES_BECAUSE_OLD_DEVICE:
          case VKQuality.RECOMMENDATION_GLES_BECAUSE_OLD_DRIVER:
          case VKQuality.RECOMMENDATION_GLES_BECAUSE_NO_DEVICE_MATCH:
          case VKQuality.RECOMMENDATION_GLES_BECAUSE_PREDICTION_MATCH:
          default:
              apiOverride = OVERRIDE_GLES;
              break;
      }
      vkQuality.StopVkQuality();
  } else {
      Log.e("VKQUALITY", "VkQuality start failed with result: " + startResult);
  }
}

在调用基类实现之前,先从 onCreate() 替换函数的顶部调用 CheckVkQuality 函数:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
  CheckVkQuality()
  super.onCreate(savedInstanceState)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    CheckVkQuality();
    super.onCreate(savedInstanceState);
}

最后,添加 updateUnityCommandLineArguments() 函数的替换项,该函数使用 apiOverride 的值将命令行参数传递给 Unity 引擎,以指定要使用的图形 API:

Kotlin

override fun updateUnityCommandLineArguments(cmdLine: String): String {
  if (apiOverride == OVERRIDE_VULKAN) {
      Log.i("VKQUALITY", "Passing -force-vulkan")
      return appendCommandLineArgument(cmdLine, "-force-vulkan")
  } else if (apiOverride == OVERRIDE_GLES) {
      Log.i("VKQUALITY", "Passing -force-gles")
      return appendCommandLineArgument(cmdLine, "-force-gles")
  }
  Log.i("VKQUALITY", "No override passed")
  // let Unity pick the Graphics API based on PlayerSettings
  return cmdLine
}

private fun appendCommandLineArgument(cmdLine: String, arg: String?): String {
    return if (arg == null || arg.isEmpty()) cmdLine
    else if (cmdLine == null || cmdLine.isEmpty()) arg else "$cmdLine $arg"
}

Java

@Override protected String updateUnityCommandLineArguments(String cmdLine)
{
    if (apiOverride == OVERRIDE_VULKAN) {
        Log.i("VKQUALITY", "Passing -force-vulkan");
        return appendCommandLineArgument(cmdLine, "-force-vulkan");
    }
    else if (apiOverride == OVERRIDE_GLES) {
        Log.i("VKQUALITY", "Passing -force-gles");
        return appendCommandLineArgument(cmdLine, "-force-gles");
    }
    Log.i("VKQUALITY", "No override passed");
    // let Unity pick the Graphics API based on PlayerSettings
    return cmdLine;
}

private String appendCommandLineArgument(String cmdLine, String arg) {
    if (arg == null || arg.isEmpty())
        return cmdLine;
    else if (cmdLine == null || cmdLine.isEmpty())
        return arg;
    else
        return cmdLine + " " + arg;
}

您的自定义 activity 现在会在启动时调用 VkQuality,并根据 VkQuality 建议选择 Vulkan 或 OpenGL ES。

使用自定义建议列表

通过将包含该列表的文件的名称传递给 StartVkQuality()(而不是传递空字符串)来指定自定义推荐列表文件:

Kotlin

val startResult = vkQuality.StartVkQuality("CUSTOM_FILE_NAME.vkq")

Java

int startResult = vkQuality.StartVkQuality("CUSTOM_FILE_NAME.vkq");

VkQuality 首先在应用的内部存储目录中查找该文件。如果该文件不在内部存储空间中,VkQuality 会尝试从您的 app bundle 的资源中加载该文件。如果文件不在上述任一位置,VkQuality 会返回 ERROR_MISSING_DATA_FILE 枚举值。

如需创建自定义推荐列表文件,请使用 GitHub 代码库中的 VkQuality List Editor 工具。该工具的文档位于其 README 中。