在 Apple iOS 上使用 Vulkan 进行开发的教程

Vulkan® Portability™ 是 Khronos® 的一项倡议,旨在促进 Vulkan 功能的一致使用,这些功能被分层覆盖在其他底层 API 上,以便在没有 Vulkan 原生驱动程序的平台(如苹果的 macOS 和 iOS)上可移植地部署 Vulkan 应用程序。2024 年 3 月,来自 LunarG 的 Richard Wright 更新了Apple 设备上的 Vulkan 状态白皮书,以反映 Vulkan SDK 在苹果平台上的最新可用性,以及其用于开发与苹果 App Store 完全兼容的应用程序的能力。

在本教程中,来自 Holochip 的 Khronos 成员 Steve Winston 将使用最新的 Khronos Vulkan 示例来说明如何在 iOS 上使用 Vulkan。

iOS 上的 MoltenVK

在 iOS 上使用 Vulkan 意味着使用MoltenVK™。 MoltenVK 是一个 Khronos 托管的开源项目,它将 Vulkan 功能置于 Apple Metal API 之上,包括将 Vulkan SPIR-V 着色器转换为 Metal 着色语言。 MoltenVK 提供接近 Vukan 1.2 的功能,并正在进行构建 Vulkan 1.3 的工作,同时保持与原生 Metal 竞争的性能特征。

Apple 捆绑包和 Vulkan SDK

Apple 平台的应用程序打包要求运行程序的所有内容都包含在捆绑包中,并且尽可能不需要系统级别的更改来安装/卸载应用程序,从而可以通过删除应用程序的文件夹来有效卸载应用程序。这与其他平台形成鲜明对比,其他平台通常依赖于在已知系统位置安装资源和驱动程序。

此限制在 MacOS 上有所放松,以便更轻松地移植 Unix 和 FreeBSD 风格的应用程序。然而,iOS 的安全问题要求应用程序包包含所有内容,而无需安装到系统位置。这意味着 Vulkan SDK 元素(例如 Vulkan 加载器和验证层)需要作为应用程序包本身的一部分进行打包和安装,而不是单独安装 SDK。

以下部分概述了开发人员如何确保所有依赖项与其应用程序正确打包,以确保 Vulkan 加载器和 iOS 层的正确运行。

MoltenVK的封装和使用

这是开发人员创建应用程序包以使用 MoltenVK 和 Vulkan 验证层的方式:

VulkanApplication
	/Assets ← folder containing UI assets such as fonts, sound files, and textures
	/Assets/info.plist ← catalogs the contents of the Assets folder
	/VulkanApplication ← the executable file
	/*.mobileprovision ← the provisioning profile which is necessary to run on devices.
	/Frameworks ← folder that holds all frameworks used by the application
	/Frameworks/MoltenVK.framework
	/Frameworks/vulkan.framework
	/Frameworks/VkLayer_khronos_validation.framework
	Info.plist ← required and standard for all Apple applications.
	*.spv ← compiled SPIRV shaders required by the Vulkan Application
	/vulkan/explicit_layer.d ← contains the JSON that tells the loader how to load some explicit layers
	/vulkan/explicit_layer.d/VkLayer_khronos_validation.json ← contains path to validation.framework
	/vulkan/icd.d ← contains the vulkan drivers or in this case, MoltenVK_icd.json
	/vulkan/icd.d/MoltenVK_icd.json ← tells the loader where to find the libMoltenVK.framework

assets 目录用于 UI Storyboard 需要直接访问的资产。该文件夹用于创建 info.plist 资产目录。由于其使用方式非常特殊,因此最好不要将编译好的着色器放在 assets 目录中,这样编译和安装会更快更容易。

在 /vulkan 文件夹中有多个 json 文件,每个文件都指向动态库。这些 json 文件中的文件路径与 json 文件本身的位置相对。因此,如果加载器遇到 vulkan/icd.d/MoltenVK_icd.json,那么它必须向上移动两个目录到 VulkanApplication 的顶层,然后进入 Frameworks,再识别满足 icd 库的库。换句话说,在这里的例子中 “…/…/Frameworks/MoltenVK.framework”。

这意味着 json 文件 /vulkan/icd.d/MoltenVK.json 可能如下所示:

{
  "file_format_version": "1.0.0",
  "ICD": {
    "library_path": "../../Frameworks/MoltenVK.framework/MoltenVK",
    "api_version": "1.2.0",
    "is_portability_driver": true
  }
}

同样,json 应指示在哪里可以找到层动态库,因此 /vulkan/explicit_layer.d/VkLayer_khronos_validation.json 的前 7 行如下所示:

{
  "file_format_version": "1.2.0",
  "layer": {
    "name": "VK_LAYER_KHRONOS_validation",
    "type": "GLOBAL",
    "library_path": "../../Frameworks/VkLayer_khronos_validation.framework/VkLayer_khronos_validation",
    "api_version": "1.3.280",

在这两种情况下,library_path 都用于确定动态库在打包捆绑包中的位置。

在设备上进行测试

传统上,在 iOS 上使用 MoltenVK 的应用程序会静态链接 MoltenVK 库。新发布的 Vulkan SDK 1.3.280.0 根据需要提供了对每个框架的访问,因此应用程序可以动态使用 Vulkan 加载器,这提供了使用层(如 Vulkan 验证层)的能力,而静态链接则提供了正常的运行时。静态链接和动态链接都能与 TestFlight 和苹果应用商店配合使用。

这些不同的构建类型可以通过构建工具轻松实现。让我们看看如何使用现代 CMake 构建 iOS 版 Vulkan。

CMake 编译配置

下面是一些推荐的 CMake 配置,并解释了它们各自的作用。

首先,我们希望 CMake 生成默认方案,这样 XCode 在每次运行 CMake 的配置阶段时就无需重新生成该方案。这也能让 -build 参数发挥作用,从而实现 CI 命令行编译。

set(CMAKE_XCODE_GENERATE_SCHEME TRUE)

以下是一些推荐的 CMake 配置,并解释了每个配置的用途。

我们首先希望 CMake 生成默认方案,这样 XCode 就不需要在每次运行 CMake 的配置阶段时重新生成它。这也允许 –build 参数起作用,从而允许 CI 命令行构建。

set(CMAKE_XCODE_GENERATE_SCHEME TRUE)

请注意,创建项目后,生成默认方案不会破坏手动对该方案所做的任何更改。这些设置可以在这里找到:

https://cmake.org/cmake/help/latest/prop_tgt/XCODE_GENERATE_SCHEME.html

此外, MoltenVK 运行时用户指南中的构建和运行时要求建议在方案中禁用 Metal API 验证和 GPU 帧捕获。因此,让我们在 CMake 设置中执行此操作:

set(CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION FALSE)
set(CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE DISABLED)

接下来,CMake 通常配置为在解析 find_package 和 find_library 调用时查看系统路径。但是,这在这种情况下不起作用,因为开发计算机的系统路径上的库不是为移动设备的体系结构编译和链接的库,因此我们需要在每个 find_library / find_package 上添加指令调用以忽略系统库路径或全局调整 find_* 调用用于解析库位置的方法:

set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)

现在,我们需要设置 XCode 以了解我们的目标是 iOS,方法是将 XCode 属性目标设备系列更改为 1,2,以便覆盖 iPhone 和 iPad 设备。此外,我们还可以通过 find_library 指定要查找和使用 Apple iPhone SDK 中的哪些框架。以下是如果不使用自动链接框架以及启用 C 和 Objective C 模块所需的最少库;为了方便起见,我们建议启用自动框架链接设置,但在此处显示手动链接以演示框架链接的工作原理。

if(IOS)
 set(CMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2")
 find_library(UIKIT_LIB UiKit)
 find_library(COREMEDIA_LIB CoreMedia)
 find_library(METAL_LIB Metal)
endif()

接下来,我们需要告诉 CMake 如何查找 FindVulkan.cmake 文件,该文件指示 Vulkan SDK 中的所有内容。为此,我们需要从 Vulkan 示例中引入它,可以在此处找到:VulkanSamples/bldsys/cmake/module/FindVulkan.cmake

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

现在我们需要找到 MoltenVK 包。可以这样做:

find_package(Vulkan REQUIRED COMPONENTS MoltenVK)

CMake 构建应用程序目标

当我们定义 iOS 应用程序目标时,我们会包含 SDK 中的 iOS/share/vulkan 目录,其中包含我们希望在应用程序捆绑包中使用的 json 文件。将其包含在源代码属性中,Vulkan 目录就会被复制到捆绑包中的正确位置:

add_executable(VulkanApplication MACOSX_BUNDLE
    main.m
    AppDelegate.m
    ViewController.m
    basic.storyboard
    launch.storyboard
    Media.xcassets
    Assets
    $ENV{VULKAN_SDK}/../iOS/share/vulkan
)

现在,需要告诉 CMake 其中一些源文件实际上是资源,因此它们只会被复制:

set_source_files_properties(basic.storyboard launch.storyboard Media.xcassets Assets
    $ENV{VULKAN_SDK}/../iOS/share/vulkan
    PROPERTIES
    MACOSX_PACKAGE_LOCATION Resources
)

接下来,需要指定项目包的 info.plist 应使用模板 info.plist 进行项目配置:

set_target_properties(VulkanApplication PROPERTIES
    MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/Info.plist.in
)

请注意,Info.plist 文件已配置为如果 ${PROJECT_SOURCE_DIR}/Info.plist 不包含对它们的引用,则大多数 MACOSX_BUNDLE_ 属性将不起作用。可以不指定模板 plist 并使用 plist 生成默认模板。

现在,链接我们需要的库和框架。因为我们正在演示显式定义上面使用的框架,以下是如何链接:

target_link_libraries(VulkanApplication PUBLIC ${UIKIT_LIB} ${COREMEDIA_LIB} ${METAL_LIB})

随着 Vulkan 1.3.280 版本的发布,我们可以像在其他平台上一样直接链接到 Vulkan 加载器,并且 App Store 将接受我们的新框架设置:

target_link_libraries(VulkanApplication PUBLIC Vulkan::Vulkan)

接下来,需要设置定义应用程序的所有其他 XCode 属性设置:

set(CMAKE_MACOSX_BUNDLE YES)
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.khronos.vulkanapplication)
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") ← this isn’t required unless no custom libraries are in the application.
set_target_properties(VulkanApplication PROPERTIES
    BUNDLE_IDENTIFIER com.khronos.vulkanapplication
    XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.khronos.vulkanapplication
    XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "YES"
    XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
    XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
    XCODE_ATTRIBUTE_CODE_SIGN_STYLE "Automatic" # already default value
    XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer"
    MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0.0
    MACOSX_BUNDLE_BUNDLE_VERSION 1.0.0
    XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "XXX" ← replace this with your TEAM id.
    XCODE_EMBED_FRAMEWORKS "${Vulkan_MoltenVK_LIBRARY};${Vulkan_LIBRARIES};"
    XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY		"YES"
    XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY	"YES"
    XCODE_ATTRIBUTE_SKIP_INSTALL NO
    XCODE_ATTRIBUTE_INSTALL_PATH "$(LOCAL_APPS_DIR)"
    XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
    XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING NO
 )

请注意,使用上述设置,如果希望运行一个图层,我们需要将其添加到 XCODE_EMBED_FRAMEWORKS 列表中,以便进行验证,它看起来像:

"${Vulkan_MoltenVK_LIBRARY};${Vulkan_LIBRARIES};${Vulkan_Layer_VALIDATION}"

最后,唯一要做的就是确保将构建的着色器安装到应用程序包中。这可以像这样完成:

function(target_add_spirv_shader TARGET INPUT_FILE)
  find_program(GLSLC glslc)
  set(optionalArgs ${ARGN})
  set(OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/shader-spv")
  list(LENGTH optionalArgs numArgs)
  if(${numArgs} GREATER 1)
    message(FATAL_ERROR "target_add_spirv_shader called incorrectly, add_spirv_shader(target INPUT_FILE [OUTPUT_FILE])")
  endif()
  if(${numArgs} EQUAL 1)
    list(GET optionalArgs 0 OUTPUT_DIR)
  endif()
  file(MAKE_DIRECTORY ${OUTPUT_DIR})
  get_filename_component(bare_name ${INPUT_FILE} NAME_WE)
  get_filename_component(extension ${INPUT_FILE} LAST_EXT)
  string(REGEX REPLACE "[.]+" "" extension ${extension})
  set(OUTPUT_FILE "${OUTPUT_DIR}/${bare_name}-${extension}.spv")
  add_custom_command( PRE_BUILD
      OUTPUT ${OUTPUT_FILE}
      COMMAND ${GLSLC} --target-env=vulkan1.2 ${INPUT_FILE} -o ${OUTPUT_FILE}
      MAIN_DEPENDENCY ${INPUT_FILE}
      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
      )
  add_custom_target(${bare_name}-${extension} DEPENDS ${OUTPUT_FILE})
  add_dependencies(${TARGET} ${bare_name}-${extension})
  target_sources(${TARGET} PUBLIC ${OUTPUT_FILE})
  set_source_files_properties(${OUTPUT_FILE} PROPERTIES
      MACOSX_PACKAGE_LOCATION Resources
  )
endfunction()

定义如上的 CMake 函数后,可以像这样调用它:

target_add_spirv_shader(basic ${CMAKE_CURRENT_SOURCE_DIR}/Shaders/texture.vert)
target_add_spirv_shader(basic ${CMAKE_CURRENT_SOURCE_DIR}/Shaders/texture.frag)

这将在应用程序包中生成texture-vert.spv和texture-frag.spv。

Vulkan 可移植性扩展

使用 MoltenVK 时,能够确定 Vulkan 功能的哪些子集在目标设备上运行非常重要。这可以使用Vulkan VK_KHR_portability_subset扩展来实现。

首先,需要启用可移植性枚举标志,以便 Vulkan 加载器知道允许列出不完全一致的驱动程序,例如 MoltenVK 驱动程序:

VkInstanceCreateInfo instanceCreateInfo = {};
 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 instanceCreateInfo.pNext = nullptr;
 instanceCreateInfo.pApplicationInfo = &appInfo;

#if TARGET_OS_IOS
  instanceCreateInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif
std::vector<const char*> instanceExtensions = {VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME };
  instanceCreateInfo.enabledExtensionCount = instanceExtensions.size();
  instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.data();

请注意,TARGET_OS_IOS 将通过包含 TargetConditionals.h 来定义。

构建应用程序

现在,我们可以使用 CMake 从命令行创建 XCode 项目,如下所示:

cd VulkanApplicationSrc
mkdir build && cd build
Set environment variable VULKAN_SDK:
source ~/VulkanSDK/1.3.280.1/iOS/setup-env.sh
cmake -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCMAKE_IOS_INSTALL_COMBINED=NO ..

构建目录现在将有一个 XCode 项目。打开它并从 XCode 构建它,或者您可以继续使用命令行,如下所示:

cmake --target VulkanApplication --config Debug -- -sdk iphoneos -allowProvisioningUpdates

运行应用程序

从这里,开发人员可以使用 XCode 通过xcodebuild install来构建和调试应用程序。开发人员还可以选择使用LLDB 调试器来调试应用程序。然而,一旦 CMake 配置完成,使用 XCode 作为 IDE 通常会更容易。但是,CMakesetup 可以在 CI/CD 环境中使用,因此除了提供随 xcode-select 安装的 XCode 工具之外,不需要 XCode IDE。

结论

Vulkan 是一种开放标准 API,提供了跨多平台(包括 Android、Windows、Linux 和 MacOS)的应用程序可移植性。借助新的 Vulkan SDK 和上述说明,现在在 iOS 上部署 Vulkan 应用程序也比以往更加容易。我们希望本文能提供有用的信息,并鼓励每个人查看现在可以在 iOS 上运行的Khronos Vulkan 示例!

作者:Steve Winston,Holochip
译自:https://www.khronos.org/blog/developing-with-vulkan-on-apple-ios

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/46737.html

(0)

相关推荐

发表回复

登录后才能评论