macOS Mojave(10.14.2)系统上编译LunarG/VulkanTools工程的Android版本

$ cd ~

$ git clone https://github.com/LunarG/VulkanTools.git

$ cd VulkanTools

$ cd build-android

$ export ANDROID_SDK_HOME=~/Library/Android/sdk

$ export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk-bundle

# 当前的版本需要Android SDK 23.0.0,但是需要Java 11以下版本的Java才能执行SDK里面的sdkmanager

# 显示可以安装的Java版本号信息,需要检出这个内容
$ brew tap caskroom/versions

$ brew search "java*"
==> Formulae
app-engine-java            javarepl                   libreadline-java
google-java-format         jslint4java

==> Casks
charles-applejava          java-beta                  netbeans-java-se
eclipse-java               java6                      oracle-jdk-javadoc
eclipse-javascript         java8                      yourkit-java-profiler
java                       netbeans-java-ee
==> Did you mean to perform a regular expression search?
Surround your query wit

# 可以看到上面存在Java8可以安装,我们安装Java8
$ brew cask install java8

# 指定默认的Java版本
$ export JAVA_HOME=`/usr/libexec/java_home -v 1.8`

$ $ANDROID_SDK_HOME/tools/bin/sdkmanager --update

# 目前的代码需要android-23的SDK
$ $ANDROID_SDK_HOME/tools/bin/sdkmanager "platforms;android-23"

$ $ANDROID_SDK_HOME/tools/bin/sdkmanager "platforms;android-24"

$ $ANDROID_SDK_HOME/tools/bin/sdkmanager "build-tools;24.0.3"

# 可选安装
# $ANDROID_SDK_HOME/tools/bin/sdkmanager "platform-tools" "platforms;24.0.3"

# 需要Android 7.0以上才支持Vulkan,因此,我们需要更高版本的编译工具
$ export PATH=$ANDROID_NDK_HOME:$ANDROID_SDK_HOME/build-tools/24.0.3/:$PATH

# 启用 ccache 减少二次编译的时间 
$ brew install ccache

$ export NDK_CCACHE=ccache
 
$ export USE_CCACHE=1

$ export VULKAN_HEADERS_INSTALL_DIR=./third_party/Vulkan-Headers

$ bash build_all.sh

# 编译完成后,执行 bash test_APK.sh 测试功能,不过话说,这个工具是做什么的,有点看不懂了
# 难不成是厂家测试驱动用的?
$ bash test_APK.sh

参考链接


OpenCL代码编译成Vulkan代码的工具clspv

最近接到个任务,就是把OpenCL.cl代码编译成Vulkan程序。

使用Google开源的工具clspv实现这个功能即可。

$ git clone https://github.com/google/clspv.git

$ cd clspv

$ python utils/fetch_sources.py

$ mkdir build

$ cd build

$ cmake ..

$ make -j8

编译完成后,在 bin 目录下生成 clspv 这个可执行程序。

使用的时候参考项目的文档 OpenCL C 1.2 Language on Vulkan

只是需要注意的是,如果使用了 image 类型,比如image2d_t 则需要提供 -samplermap 这个参数,这个参数指向一个文件,文件内容可能如下:

CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST,
CLK_NORMALIZED_COORDS_TRUE  | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR

具体的定义跟在 OpenCL 中调用 read_image/write_image 函数时候指定的 sampler 参数一致即可。

参考链接


OpenCL C 1.2 Language on Vulkan

OpenCL代码编译成Vulkan代码(SPIR-V)的工具

最近接到个任务,就是把OpenCL.cl代码编译成Vulkan程序。

请使用 OpenCL代码编译成Vulkan代码的工具clspv 实现这部分功能,官方提供的转换库,目前看来暂时没办法使用。

根据官方文档,Vulkan 1.0支持OpenCL 1.0/2.1的代码直接编译成Vulkan程序。

官方提供了一个名为 KhronosGroup/SPIR 的开源项目,支持OpenCL编译成SPIR-V代码的功能(Vulkan使用SPIR-V)。

这个工具在 macOS Mojave(10.14.2) 系统上使用 Xcode Version 10.1 (10B61) 编译流程如下:

$ cd ~

$ git clone -b khronos/spirv-3.6.1 https://github.com/KhronosGroup/SPIRV-LLVM.git llvm

$ cd llvm/tools

# 默认分支要求检出 spir_12 分支,但是貌似执行完成 clone 之后,就已经在这个分支上了
# git checkout --track -b spir_12 remotes/origin/spir_12
# 默认是 SPIR 的编译,Vulkan 需要 SPIR-V 两者是不同的
# Vulkan 需要编译 SPIR-V 我们需要手工切换到 spirv-1.0 分支上
$ git clone -b spirv-1.0 https://github.com/KhronosGroup/SPIR clang

$ cd ..

$ mkdir build

$ cd build

$ cmake ..

# 多线程同时编译,提高编译速度
$ make -j8

 

编译完成后的使用方式如下:

$ ~/llvm/build/bin/clang -cc1 -emit-spirv -triple <triple> -cl-std=<CLversion> -include opencl.h -x cl -o <output> <input>

# 反汇编查看编译结果
$ spirv-dis <output>
  • <triple>: for 32 bit SPIR-V use spir-unknown-unknown, for 64 bit SPIR-V use spir64-unknown-unknown.
  • -D: to enable support for extension. e.g. -Dcl_khr_fp16 compile option will enable half support.
  • -O: -O0 (default) is the only tested option value at the moment. It's assumed by design that all optimizations are executed by SPIR-V consumer.

比如:

# 编译kernel.cl 到 kernel.spir
$ ~/llvm/build/bin/clang -cc1 -emit-spirv -triple=spir-unknown-unknown -cl-std=CL2.0 -include opencl.h kernel.cl -o kernel.spir

# 反汇编查看编译结果
$ spirv-dis kernel.spir

继续阅读OpenCL代码编译成Vulkan代码(SPIR-V)的工具

基于Vulkan的GPGPU计算框架Vuh

发现一个写的比较好的基于VulkanGPGPU计算框架Vuhgithub上的代码地址为:

https://github.com/Glavnokoman/vuh.git

macOS Mojave(10.14.2) 中编译方式:

$ git clone https://github.com/Glavnokoman/vuh.git

$ cd vuh/config

$ pip install --user cget

$ export PATH=/Users/`whoami`/Library/Python/2.7/bin:$PATH

$ export CGET_PREFIX=.

# 安装Vulkan相关依赖库
$ bash install_dependencies.sh

# 指定Vulkan相关环境变量,后续的CMake文件中的FindVulkan.cmake会使用这个
$ export VULKAN_SDK=`pwd`

# 我们刚刚安装的Catch2的路径,CMake需要
$ export Catch2_DIR=`pwd`/lib/cmake/Catch2

$ export ARGS=-DCatch2_DIR=$Catch2_DIR

# 安装其他依赖
$ brew install spdlog

$ brew install doxygen

$ cd ..

$ mkdir build

$ cd build

$ cmake .. $ARGS

$ make

如果编译的时候报错

Scanning dependencies of target vuh_example_spdlog
[ 60%] Building CXX object doc/examples/spdlog/CMakeFiles/vuh_example_spdlog.dir/main.cpp.o
/Users/xxx/Source/vuh/doc/examples/spdlog/main.cpp:14:27: error: no member named
      'basic_logger_mt' in namespace 'spdlog'
static auto logger = spd::basic_logger_mt("logger", "vuh.log");
                     ~~~~~^
1 error generated.
make[2]: *** [doc/examples/spdlog/CMakeFiles/vuh_example_spdlog.dir/main.cpp.o] Error 1
make[1]: *** [doc/examples/spdlog/CMakeFiles/vuh_example_spdlog.dir/all] Error 2
make: *** [all] Error 2

这个原因是由于新版本的spdlog-1.2.1变更了basic_logger_mt这个类的头文件。

在源代码的vuh/doc/examples/spdlog/main.cpp中,包含如下头文件即可:

#include <spdlog/sinks/basic_file_sink.h>

 

如果需要编译 Android 版本,那么如下方式进行编译:

$ git clone https://github.com/Glavnokoman/vuh.git

$ cd vuh/config

$ pip install --user cget

$ export PATH=/Users/`whoami`/Library/Python/2.7/bin:$PATH

$ export CGET_PREFIX=.

# 安装Vulkan相关依赖库
$ bash install_dependencies.sh

$ export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk-bundle

$ cd ..

$ mkdir build

$ cd build

$ cmake .. \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="armeabi-v7a" \
-DCMAKE_BUILD_TYPE=Release \
-DANDROID_STL=c++_static \
-DANDROID_NATIVE_API_LEVEL=android-24  \
-DANDROID_TOOLCHAIN=clang \
-DVUH_BUILD_TESTS=OFF \
-DVUH_BUILD_DOCS=OFF \
-DVUH_BUILD_EXAMPLES=OFF \
-DCMAKE_CXX_FLAGS="-DVK_USE_PLATFORM_ANDROID_KHR=1 -DVULKAN_HPP_TYPESAFE_CONVERSION=1 -I$ANDROID_NDK_HOME/sources/third_party/vulkan/src/include/"

# 最后生成 libvuh.so ,然后自己写代码调用这个库即可,简单的直接修改代码中的例子即可
$ make

注意最后几个参数,文档,测试用例,例子都不参与编译,原因在于这几个工程不适合 Android 上运行,最后需要定义几个 C++ 的编译宏,否则编译不通过。

如果上面编译的时候,提示如下错误信息:

[ 20%] Building CXX object src/CMakeFiles/vuh.dir/instance.cpp.o
vuh/src/instance.cpp:127:33: error: incompatible operand
      types ('vuh::debug_reporter_t' (aka 'unsigned int (*)(unsigned int,
      VkDebugReportObjectTypeEXT, unsigned long long, unsigned int, int, const
      char *, const char *, void *) __attribute__((pcs("aapcs-vfp")))') and
      'auto (*)(VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t,
      size_t, int32_t, const char *, const char *, void *) -> VkBool32' (aka
      'auto (*)(unsigned int, VkDebugReportObjectTypeEXT, unsigned long long,
      unsigned int, int, const char *, const char *, void *) -> unsigned int'))
           , _reporter(report_callback ? report_callback : debugReporter)
                                       ^ ~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~
vuh/src/instance.cpp:128:49: warning: field '_reporter' is
      uninitialized when used here [-Wuninitialized]
           , _reporter_cbk(registerReporter(_instance, _reporter))
                                                       ^
1 warning and 1 error generated.
make[2]: *** [src/CMakeFiles/vuh.dir/instance.cpp.o] Error 1
make[1]: *** [src/CMakeFiles/vuh.dir/all] Error 2
make: *** [all] Error 2

这个错误的原因是 vuh/src/instance.cpp 中定义

/// Default debug reporter used when user did not care to provide his own.
static auto debugReporter(
        VkDebugReportFlagsEXT , VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t
        , const char*                pLayerPrefix
        , const char*                pMessage
        , void*                      /*pUserData*/
        )-> VkBool32
{
    std::cerr << "[Vulkan]:" << pLayerPrefix << ": " << pMessage << "\n";
    return VK_FALSE;
}

的时候,缺少 VKAPI_ATTR 这个宏。貌似只有在 Android 平台上这个宏被赋值,其他平台都是空。因此其他平台编译的时候,没有这个参数也是没问题的。

修改后的结果为:

/// Default debug reporter used when user did not care to provide his own.
static auto VKAPI_ATTR debugReporter(
        VkDebugReportFlagsEXT , VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t
        , const char*                pLayerPrefix
        , const char*                pMessage
        , void*                      /*pUserData*/
        )-> VkBool32
{
    std::cerr << "[Vulkan]:" << pLayerPrefix << ": " << pMessage << "\n";
    return VK_FALSE;
}

VKAPI_ATTR 这个宏在 vk_platform.h这个文件中被定义,定义的具体内容如下:

/*
***************************************************************************************************
*   Platform-specific directives and type declarations
***************************************************************************************************
*/

/* Platform-specific calling convention macros.
 *
 * Platforms should define these so that Vulkan clients call Vulkan commands
 * with the same calling conventions that the Vulkan implementation expects.
 *
 * VKAPI_ATTR - Placed before the return type in function declarations.
 *              Useful for C++11 and GCC/Clang-style function attribute syntax.
 * VKAPI_CALL - Placed after the return type in function declarations.
 *              Useful for MSVC-style calling convention syntax.
 * VKAPI_PTR  - Placed between the '(' and '*' in function pointer types.
 *
 * Function declaration:  VKAPI_ATTR void VKAPI_CALL vkCommand(void);
 * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void);
 */
#if defined(_WIN32)
    // On Windows, Vulkan commands use the stdcall convention
    #define VKAPI_ATTR
    #define VKAPI_CALL __stdcall
    #define VKAPI_PTR  VKAPI_CALL
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
    #error "Vulkan isn't supported for the 'armeabi' NDK ABI"
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
    // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat"
    // calling convention, i.e. float parameters are passed in registers. This
    // is true even if the rest of the application passes floats on the stack,
    // as it does by default when compiling for the armeabi-v7a NDK ABI.
    #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp")))
    #define VKAPI_CALL
    #define VKAPI_PTR  VKAPI_ATTR
#else
    // On other platforms, use the default calling convention
    #define VKAPI_ATTR
    #define VKAPI_CALL
    #define VKAPI_PTR
#endif

Vulkan官方API文档Vulkan® 1.0.95 - A Specification

Vulkan官方API文档 Vulkan® 1.0.95 - A Specification 对于开发者来说,非常有用。

由于官方网站访问非常缓慢,建议下载PDF版本到本地来查看。

官方文档地址:  Vulkan® 1.0.95 - A Specification - Khronos Group

官方PDF版本地址: Vulkan® 1.0.95 - A Specification - Khronos Group

本站的一份PDF版拷贝
继续阅读Vulkan官方API文档Vulkan® 1.0.95 - A Specification

Vulkan中的"#version 430 core"的理解

最近在学习Vulkan,结果在查看示例代码的时候:

#version 430 core
layout (local_size_x = 64) in;


layout(std430, binding=4 ) buffer INFO 
{
        vec2 info[];
};


void main()
{
    uint gid = gl_GlobalInvocationID.x;
    info[gid].x += 1.0;
    info[gid].y += 1.0;
    memoryBarrier();
}

对于如下语句出现了疑问

#version 430 core

这句话的意思是OpenGL必须是4.3以及以上的版本。

那么是不是意味着,如果设备上的OpenGL低于这个版本,那么我们编写的Vulkan代码就不能执行呢?

答案是否定的,目前我们开发Vulkan默认是使用GLSLOpenGL Shading Language)语言,然后编译完成后的操作符被直接映射成Vulkan的定义操作符。

换句话说,我们只要执行如下命令,

$ glslangValidator xx.comp --target-env vulkan1.0

能编译通过,就可以随意指定#version的版本。

这个版本号仅仅是GLSL语言在进行语法检查的时候需要的,而Vulkan是没有这个版本号需要的。

如果不使用GLSL语言编写,上述的#version应该都不会在语法中出现。

继续阅读Vulkan中的"#version 430 core"的理解

Vulkan中的gl_GlobalInvocationID, local_size_x的理解

最近在学习Vulkan,结果在查看示例代码的时候,对于如下两句出现了疑问:

layout(local_size_x = X​, local_size_y = Y​, local_size_z = Z​) in;
ivec3 pos = ivec3(gl_GlobalInvocationID);

首先是Invocation这个单词的理解,计算机语言中他的意思是 "the act of making a particular function start" ,中文意思是 "调用,启用"。

其中

layout(local_size_x = X​, local_size_y = Y​, local_size_z = Z​) in;

意思是初始化,X * Y * Z 个计算单元供我们的代码调用,可以简单理解成线程数。如果不设置这几个值,默认值是 1,也就是只提供一个计算单元(线程)。

而使用如下的代码

ivec3 pos = ivec3(gl_GlobalInvocationID);

意思是获取当前代码运行的计算单元的编号,也可以理解成获取当前线程的索引。

下面的代码都使用如下的命令编译成Vulkan使用的SPIR-V格式的代码

$ glslangValidator xx.comp --target-env vulkan1.0

比如下面的代码,就是一个简单的利用gl_GlobalInvocationID,进行并行计算的例子:

#version 430 core
layout (local_size_x = 64) in;


layout(std430, binding=4 ) buffer INFO 
{
        vec2 info[];
};


void main()
{
    uint gid = gl_GlobalInvocationID.x;
    info[gid].x += 1.0;
    info[gid].y += 1.0;
    memoryBarrier();
}

但是,如果传入的数组的大小超过我们设置的计算单元的数量的情况,上述的代码是处理不了的。

可以如下方式处理上述情况:

/*atomicAdd 从4.30版本的opengl开始提供,之前的版本没有这个函数*/
#version 430 core
layout (local_size_x = 64) in;

layout(std430, binding=4 ) buffer INFO 
{
        vec2  info[];
};

layout(std430, binding=5 ) buffer LEN 
{
        uint   len;
};

uint counter = 0;

void main()
{
    uint gid = atomicAdd(counter,1);
    if  (gid <= len ) { 
        info[gid].x += 1.0;
        info[gid].y += 1.0;
        memoryBarrier();
    }
}


void main()
{
    uint gid = atomicAdd(counter,1);
    if  (gid <= len ) { 
        info[gid].x += 1.0;
        info[gid].y += 1.0;
        memoryBarrier();
    }
}

如果想动态调整计算单元的数量,增加处理灵活性,可以参考下面的代码:

#version 450
layout(local_size_x_id = 0) in;               
layout(local_size_y_id = 1) in;
layout(local_size_z_id = 2) in;

//rest of the shader

外部通过

VkResult vkCreateComputePipelines(
	VkDevice device,
	VkPipelineCache pipelineCache,
	uint32_t createInfoCount,
	const VkComputePipelineCreateInfo* pCreateInfos,
	const VkAllocationCallbacks* pAllocator,
	VkPipeline* pPipelines);

函数调用的时候,指定

typedef struct VkComputePipelineCreateInfo {
	VkStructureType sType;
	const void* pNext;
	VkPipelineCreateFlags flags;
	VkPipelineShaderStageCreateInfo stage;
	VkPipelineLayout layout;
	VkPipeline basePipelineHandle;
	int32_t basePipelineIndex;
} VkComputePipelineCreateInfo;

参数中的

typedef struct VkPipelineShaderStageCreateInfo {
	VkStructureType sType;
	const void* pNext;
	VkPipelineShaderStageCreateFlags flags;
	VkShaderStageFlagBits stage;
	VkShaderModule module;
	const char* pName;
	const VkSpecializationInfo* pSpecializationInfo;
} VkPipelineShaderStageCreateInfo;

参数中的

typedef struct VkSpecializationInfo {
	uint32_t mapEntryCount;
	const VkSpecializationMapEntry* pMapEntries;
	size_t dataSize;
	const void* pData;
} VkSpecializationInfo;

参数中的

typedef struct VkSpecializationMapEntry {
	uint32_t constantID;
	uint32_t offset;
	size_t size;
} VkSpecializationMapEntry;

指定的数值来动态调整所需要的计算单元的数量。

整个参数的设置流程特别长,非常难掌握。具体的使用例子参考 Glavnokoman/vuh以及 Vulkan® 1.0.95 - A Specification - Khronos Group文档中的上述参数的使用例子。

如果内部不指定,也可以通过外部调用

void vkCmdDispatch(
	VkCommandBuffer commandBuffer,
	uint32_t groupCountX,
	uint32_t groupCountY,
	uint32_t groupCountZ);

的时候设置,但是这样的设置存在一定的灵活性问题,可能需要多个独立的ComputePipeline来配合。

继续阅读Vulkan中的gl_GlobalInvocationID, local_size_x的理解

Android Studio 3.2.1编译Vulkan示例项目

最近在学习Vulkan,根据Google官方给出的文档,是存在一些问题的,总结一下:

# 官方文档说要检出 git clone https://github.com/googlesamples/vulkan-basic-samples.git
# 实际上 vulkan-basic-samples.git 是 VulkanSamples.git 的一个fork,google简化了部分编译流程
# vulkan-basic-samples.git检出后,直接用 Android Studio打开即可
# https://github.com/SaschaWillems/Vulkan.git也存在一个例子工程
# 但是貌似根正苗红的是VulkanSamples这个例子

$ git clone https://github.com/LunarG/VulkanSamples.git

$ cd VulkanSamples

$ cd API-Samples

# 最后在当前目录下生成一个 android 的目录,用Android Studio导入这个目录下的工程
# 也就是 "VulkanSamples⁩/API-Samples⁩/android⁩" 这个目录是我们的工程目录
$ cmake -DANDROID=ON -DABI_NAME=armeabi-v7a

如果代码下载不成功,可以本站下载一份拷贝。 点击这里下载 VulkanSamples vulkan-basic-samples
继续阅读Android Studio 3.2.1编译Vulkan示例项目

macOS Mojave(10.14.1)安装Vulkan依赖的工具链SPIR-V Toolchain

目前在研究学习VulkanVulkan使用SPIR-V工具链进行编译,在macOS Mojave(10.14.1)中使用如下命令安装工具链:

$ brew info glslang
glslang: stable 7.10.2984 (bottled), HEAD
OpenGL and OpenGL ES reference compiler for shading languages
https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/glslang.rb
==> Dependencies
Build: cmake ✘
==> Options
--HEAD
	Install HEAD version
==> Analytics
install: 1,147 (30 days), 1,770 (90 days), 2,664 (365 days)
install_on_request: 383 (30 days), 696 (90 days), 1,537 (365 days)
build_error: 0 (30 days)

$  brew install glslang
==> Downloading https://homebrew.bintray.com/bottles/glslang-7.10.2984.mojave.bo
######################################################################## 100.0%
==> Pouring glslang-7.10.2984.mojave.bottle.tar.gz
?  /usr/local/Cellar/glslang/7.10.2984: 58 files, 6.7MB

$ glslangValidator -v
Glslang Version: 7.10.2984
ESSL Version: OpenGL ES GLSL 3.20 glslang Khronos. 10.2984
GLSL Version: 4.60 glslang Khronos. 10.2984
SPIR-V Version 0x00010300, Revision 1
GLSL.std.450 Version 100, Revision 1
Khronos Tool ID 8
SPIR-V Generator Version 7
GL_KHR_vulkan_glsl version 100
ARB_GL_gl_spirv version 100

但是,需要注意的是,使用HomeBrew安装的版本,缺少部分功能,如果使用"-Os"(代码大小优化)参数的时候,会报告错误,如下:

$ glslangValidator -V -Os
glslangValidator: Error -Os not available; optimizer not linked (use -h for usage)

这是由于代码大小优化部分需要spirv-tools执行,然而HomeBrew编译的版本没有关联spirv-tools导致安装的程序没办法使用这个功能。

如果需要这部分功能,我们只能手动编译安装:

# 移除brew安装的版本
$ brew remove glslang

# 编译安装 glslang
$ git clone https://github.com/KhronosGroup/glslang.git

$ cd glslang

# 目前(2018.12.17)的正式版,最稳定的版本,试过最新的版本,编译部分代码存在问题
$ git checkout 7.10.2984

# 下载 spirv-tools 部分的功能代码
$ python update_glslang_sources.py 

$ mkdir build

$ cd build

$ cmake ..

$ make

$ sudo make install

继续阅读macOS Mojave(10.14.1)安装Vulkan依赖的工具链SPIR-V Toolchain