littlewing

人間とコンピューターとメディアの接点をデザインするために考えたこと

MagicLeap用にUnity Native Pluginをビルドする(libjpeg-turbo)

Unity で作成したMagicLeapアプリでturbojpegライブラリを利用したくて、試行錯誤したのでメモ。

概要

背景

  • MagicLeapでNativePluginを利用するにはAArch64(ARM 64bit)でビルドした共有ライブラリ(.so)が必要となります。
  • Third-Partyライブラリにおいてビルド済みバイナリが配流されていることはほぼないので、自分でビルドする必要があります。
  • Android用として配布されているバイナリがそのまま使えることもあるが、ダメな場合もある(その辺よくわからない)

環境の説明

  1. Windows 10
  2. Unity 2019.1.14f
  3. MagicLeap SDK 0.21
  4. CMake 3.15.3
  5. make-3.81
  6. nasm-2.14.02 ※これはlibjpeg-turboが必要とするものなので、通常はいらないかも。
  7. libjpeg-turbo (実施時点では2.0.3 masterブランチ)
  8. WSLのUbuntu 18.4 LTS ※最後に確認に使ってるけど無くても良い

ビルド手順

  • 環境は以下を想定.
名前 実際のパス(例)
{MLSDK_DIR} C:\User\hoge\MagicLeap\mlsdk\0.21.0\
{libjpeg-turbo_DIR} C:\GitHub\libjpeg-turbo\
  • MINGW64 とコマンドを使い分けてるのでパス表記は適宜読み替えてください。

1. 「環境の説明」の内容を諸々インストールして準備する

  • CMake とMakeはパスを環境変数に登録しておくと便利

2. magicleap.toolchain.cmake を作成する。

$ cd  {MLSDK_DIR}

$ ./mabu -t lumin --create-cmake-toolchain magicleap.toolchain.cmake

mabu: Generated a cmake toolchain file to magicleap.toolchain.cmake.
This toolchain is entirely independent of mabu, but, depends on the current SDK location;
override the MLSDK cmake variable as needed.
mabu: Example usage:
        del CMakeCache.txt
        cmake -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=magicleap.toolchain.cmake [-DMLSDK=...] ...

3. CMakeの実行

  • クローンしたリポジトリ配下に Build (任意の名前)フォルダを作成して、その中でCMakeを実行
#Buildフォルダを作成/移動
$ cd  {libjpeg-turbo_DIR}
$ mkdir Build
$ cd Build

#Cmake実行
$ cmake -G"Unix Makefiles"   \
-DCMAKE_TOOLCHAIN_FILE="{MLSDK_DIR}magicleap.toolchain.cmake"\
 "{libjpeg-turbo_DIR}"

4. CMakeCache.txt の調整

  • これは通常必要ないかもですが、CMakeが生成した、CMakeCache.txtを一部編集しました。(必要ないと思います)
//Build static libraries
ENABLE_STATIC:BOOL=OFF  //静的ライブラリの生成をOFFに

//Generate a fatal error if SIMD extensions are not available for
// this platform (default is to fall back to a non-SIMD build)
REQUIRE_SIMD:BOOL=OFF //SIMD extensionsをOFFに

5. Makeの実行

BuildフォルダにてMakeを実行する

C:\GitHub\libjpeg-turbo\builds\ARM64-lumin>make
-- CMAKE_BUILD_TYPE = Release
-- VERSION = 2.0.3, BUILD = 20191016
-- 64-bit build (arm64)
-- CMAKE_INSTALL_PREFIX = /opt/libjpeg-turbo
-- CMAKE_INSTALL_BINDIR = bin (/opt/libjpeg-turbo/bin)
-- CMAKE_INSTALL_DATAROOTDIR =  (/opt/libjpeg-turbo)
-- CMAKE_INSTALL_DOCDIR = doc (/opt/libjpeg-turbo/doc)
-- CMAKE_INSTALL_INCLUDEDIR = include (/opt/libjpeg-turbo/include)
-- CMAKE_INSTALL_LIBDIR = lib64 (/opt/libjpeg-turbo/lib64)
-- CMAKE_INSTALL_MANDIR = man (/opt/libjpeg-turbo/man)
-- Shared libraries enabled (ENABLE_SHARED = 1)
-- Static libraries disabled (ENABLE_STATIC = 0)
-- 12-bit JPEG support disabled (WITH_12BIT = 0)
-- Arithmetic decoding support enabled (WITH_ARITH_DEC = 1)
-- Arithmetic encoding support enabled (WITH_ARITH_ENC = 1)
-- TurboJPEG API library enabled (WITH_TURBOJPEG = 1)
-- TurboJPEG Java wrapper disabled (WITH_JAVA = 0)
-- In-memory source/destination managers enabled (WITH_MEM_SRCDST = 1)
-- Emulating libjpeg API/ABI v6.2 (WITH_JPEG7 = 0, WITH_JPEG8 = 0)
-- libjpeg API shared library version = 62.3.0
-- Compiler flags =  -O3 -DNDEBUG
-- Linker flags = -Wl,--start-group -Bdynamic -lc -lm -lc++_shared -landroid_support -Wl,--end-group --sysroot=C:/Users/hayas/MagicLeap/mlsdk/v0.21.0/lumin -pie -Wl,--gc-sections -Wl,-z,nocopyreloc -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--build-id -LC:/Users/hayas/MagicLeap/mlsdk/v0.21.0/lumin/stl/libc++/lib
-- Compiler supports pointers to undefined structures.
-- INLINE = __inline__ __attribute__((always_inline)) (FORCE_INLINE = 1)
-- Linker supports GNU-style version scripts
-- CMAKE_EXECUTABLE_SUFFIX =
-- CMAKE_ASM_FLAGS =   -O3 -DNDEBUG
-- GAS is working properly
-- SIMD extensions: arm64 (WITH_SIMD = 1)
-- FLOATTEST = 64bit
-- RPM architecture = aarch64, DEB architecture = arm64
-- Configuring done
-- Generating done
-- Build files have been written to: C:/GitHub/libjpeg-turbo/builds/ARM64-lumin
Scanning dependencies of target simd

6. 生成されたライブラリをUnityにコピー

Buildフォルダに libturbojpeg.soが生成されていると思います。 これをUnityのPlugins フォルダにコピーしてLumin向けの設定にします。 f:id:pigshape:20191016125016p:plain

尚、Androidもそうですが、C#呼び出す時、ライブラリの名称からlibは省略するので注意。 あと、この辺のC#からの呼び出し方法の記載は省略します。

    [DllImport("turbojpeg")]   //libturbojpegじゃないので注意!!
    public static extern IntPtr tjInitDecompress();

何度かライブラリを生成して入れ替えてmpkをビルドしていたら、mpk内の.soファイルが入れ替わってないときがあった。 同じmpkファイルに対して、ビルドする場合は注意。消した方が良いかも

7.プラットフォームの確認

  • ちゃんと、目的としたプラットフォーム向けに、ライブラリが作成されているかは readelf コマンドで確認できます。Windowsで使えるのか知らないですが WSLでインストールしたUbuntu18.4で実行できます。

特に、Class ,OS/ABI, Machine: がどうなっているか確認してください。

  • 以下のようになっていればOK。Machineが x86 とかだとダメです。
littlewing@:ARM64-lumin$ readelf -h libturbojpeg.so
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0x2c30
  Start of program headers:          64 (bytes into file)
  Start of section headers:          368824 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         5
  Size of section headers:           64 (bytes)
  Number of section headers:         25
  Section header string table index: 22
  • ちなみに、32bit-ARMのAndroid版だと、以下のようになります。
littlewing@:armeabi-v7a$ readelf -h libjpegturbo.so
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          176736 (bytes into file)
  Flags:                             0x5000200, Version5 EABI, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         27
  Section header string table index: 26

所感

  • 最初Ubuntu上にAndroidNDKとか入れて、Android64bit版を作成してそれを使ってやろうとしたけど、だめで、 結局あきらめて、Windows上でやったらうまくいきました。
  • こうやって書くと、簡単そうだけど、ここまでが大変だった。。
  • こういうことするなら、Macのが楽だな。※MagicLeapSDKはWin/Mac用しかなくてLinux/Ubuntu用が無い

[おまけ] magicleap.toolchain.cmakeの中身はこんな感じだった。

  • magicleap.toolchain.cmake
set(LUMIN TRUE)

set(MLSDK "C:/Users/hoge/MagicLeap/mlsdk/v0.21.0" CACHE PATH "Path to Lumin SDK")

# canonicalize
string(REPLACE "\\" "/" LUMIN_SDK "${MLSDK}")

set(LUMIN_SDK_SYSROOT "${LUMIN_SDK}/lumin/")

set(CMAKE_SYSTEM_NAME    Linux)
set(CMAKE_SYSTEM_VERSION 1)

SET(TOOL_OS_SUFFIX "")
IF(CMAKE_HOST_WIN32)
  SET(TOOL_OS_SUFFIX ".cmd")
ENDIF()

set(CMAKE_SYSTEM_PROCESSOR "aarch64")
set(CMAKE_SYSROOT          "${LUMIN_SDK_SYSROOT}")

set(CMAKE_MAKE_PROGRAM     "${LUMIN_SDK}/tools/mabu/tools/mingw/msys/1.0/bin/make" CACHE PATH "make executable" FORCE)
set(CMAKE_C_COMPILER       "${LUMIN_SDK}/tools/toolchains/bin/aarch64-linux-android-clang${TOOL_OS_SUFFIX}"  CACHE PATH "C compiler" FORCE)
set(CMAKE_CXX_COMPILER     "${LUMIN_SDK}/tools/toolchains/bin/aarch64-linux-android-clang++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" FORCE)
set(CMAKE_AR               "${LUMIN_SDK}/tools/toolchains/bin/aarch64-linux-android-gcc-ar.exe" CACHE PATH "C/C++ archiver" FORCE)

# Configure the behaviour of the find commands
set(CMAKE_PROGRAM_PATH "${LUMIN_SDK}/tools/toolchains/bin")
set(CMAKE_FIND_ROOT_PATH "${LUMIN_SDK}/lumin/")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_CXX_FLAGS "--sysroot=${LUMIN_SDK}/lumin -march=armv8-a -mcpu=cortex-a57+crypto -fPIE -nostdinc++ -I${LUMIN_SDK}/lumin/stl/libc++/include" CACHE INTERNAL "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--start-group -Bdynamic -lc -lm -lc++_shared -landroid_support -Wl,--end-group --sysroot=${LUMIN_SDK}/lumin -pie -Wl,--gc-sections -Wl,-z,nocopyreloc -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--build-id -L${LUMIN_SDK}/lumin/stl/libc++/lib" CACHE INTERNAL "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--start-group -Bdynamic -lc -lm -lc++_shared -landroid_support -Wl,--end-group --sysroot=${LUMIN_SDK}/lumin -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--build-id -L${LUMIN_SDK}/lumin/stl/libc++/lib" CACHE INTERNAL "" FORCE)
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE INTERNAL "" FORCE)
set(CMAKE_LIBRARY_PATH "${LUMIN_SDK}/lumin/stl/libc++/lib" CACHE INTERNAL "" FORCE)

include_directories(SYSTEM )

参考