VkQuality Unity引擎插件

VkQuality Unity引擎插件为特定设备上的游戏提供图形API(Vulkan或OpenGL ES)的启动时建议。

VkQuality在比Unity引擎的默认允许列表更受限制的设备集上推荐Vulkan。使用VkQuality可以获得Vulkan的性能优势,同时将Vulkan的使用限制在具有较新图形驱动程序的较新设备上,从而限制了游戏暴露于驱动程序问题的可能性。VkQuality仅提供质量建议,而非保证,因为在推荐设备上仍然可能遇到驱动程序问题。VkQuality支持自定义列表,使您能够为游戏添加或删除设备建议。

在Unity引擎游戏中启用Vulkan

VkQuality要求您的游戏在Unity项目设置中同时启用OpenGL ES和Vulkan渲染器。通过使用自动图形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级别等于或高于建议列表中的未来API级别,则VkQuality会推荐Vulkan。默认建议列表的未来API级别为36,这意味着在未匹配的设备上运行API级别36或更高版本,VkQuality会返回RECOMMENDATION_VULKAN_BECAUSE_FUTURE_ANDROID枚举常量。

如果在设备允许列表或GPU建议列表中未找到匹配项,并且设备的API级别低于未来API级别,则VkQuality通过返回RECOMMENDATION_GLES_BECAUSE_NO_DEVICE_MATCH来推荐OpenGL ES。

将VkQuality存档文件添加到项目中

VkQuality插件是位于下载的包存档的Assets/Android/Plugins目录中的VkQuality-1.x.x.aar文件。.aar文件的实际版本号与包存档名称的版本号匹配。要安装插件,请执行以下步骤

  1. 将.aar文件复制到项目的Assets/Android/Plugins目录。(如果不存在,请创建所需的AndroidPlugins子目录。)
The VkQuality .aar file in the required project directory.
图1.所需项目目录中的VkQuality .aar文件。
  1. 在Unity 项目层次结构中选择VkQuality-1.x.x插件文件,以在检查器窗格中显示其导入设置。确保选中Android平台。
Figure 2. The VkQuality plugin platform import settings.
图2.VkQuality插件平台导入设置。

使用自定义活动来调用VkQuality

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

如果您的游戏已在使用自定义活动,请参阅将VkQuality添加到现有自定义活动部分。要为您的游戏创建新的自定义活动,请参阅将自定义活动添加到Unity项目,该部分紧随其后。

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

插件包中Assets/Plugins/Android/VkQualityTestActivity.java中包含一个使用VkQuality的自定义活动示例。插件包。要自定义文件并在游戏中使用它,请执行以下步骤

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

添加一个引用自定义活动的自定义清单文件,并告诉Unity使用您的自定义清单文件

  1. 将插件包的 Assets/Plugins/Android 目录下的 AndroidManifest.xml 文件复制到项目 Asset/Plugins/Android 目录。
  2. 在文本编辑器中打开文件。
  3. activity android:name 设置的值从 com.google.android.games.VkQualityTest.VkQualityTestActivity 更改为在前面步骤中使用的包名和活动名(例如,com.mycompany.mygame.MyGameActivity)。
  4. 保存并关闭文件。
  5. 打开 Unity 设置窗口并选择 Player 设置。展开 Publishing Settings 部分,并选中 Custom Main Manifest 复选框。
Figure 3.The Custom Main Manifest option in the Unity Player settings.
图 3.Unity Player 设置中的 Custom Main Manifest 选项。

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

将 VkQuality 添加到现有的自定义活动

如果您的游戏已经有一个自定义活动覆盖了默认的 UnityPlayerActivity,可以通过添加以下代码来集成 VkQuality 建议

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

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;
}

您的自定义活动现在会在启动时调用 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 会尝试从应用程序包的资产中加载该文件。如果这两个位置都没有该文件,VkQuality 会返回 ERROR_MISSING_DATA_FILE 枚举值。

要创建自定义建议列表文件,请使用位于 GitHub 存储库 中的 VkQuality 列表编辑器工具。该工具的文档位于其 README 中。