TFLite Unity Plugin の対応プラットフォーム追加
私が公開しているリポジトリ
へ対応プラットフォームを追加してほしいとの依頼があった。仕事だがオープンソースのままで良いとのこと。(ありがたい。)Unity のネイティブプラグイン開発は情報が少ないので、せっかくなので作業ログも残す。やってることはTensorFlow Liteのビルドですが。
今まで対応したやつ
- macOS x86_64 CPU, MetalDelegate
- macOS arm64 CPU, MetalDelegate
- Windows x86_64 CPU
- Linux x86_64
- iOS Armv7, CPU, MetalDelegate
- iOS Arm64, CPU, MetalDelegate
- Android Arm64, CPU, GPUDelegateV2, OpenGLDeleate
追加で対応予定プラットフォーム
- Android Armv7: CPUライブラリ
- Android Armv7: GPUDelegatev2, OpenGLESDelegate
- Linux Arm64: CPU
-
Linux Arm64: GPUDelegatev2
- ビルド通ることまで確認して、OpenCL Delegateまでつくった。デバイスごとの環境依存が多そうなのでちょっと考え中。
v2.9からビルドが通らなくなったので修正が必要なやつ
- macOS: MetalDelegate
もし動作しそうなら以下もおまけでやろうかな
- Linux x86_64: GPUDelegatev2
- Windows x86_64 GPUDelegatev2
作業中 in PR
- Android Armv7: CPUライブラリ
- Android Armv7: GPUDelegatev2
については、複数のcpuビルドをAndroidのaar形式にパッケージングするbazelを発見したので、soライブラリの代わりに、aar形式で組み込むことに。
Deprecated なOpenGLESを使ったGpuDelegate は対応してないので、arm64とarmv6、個別にビルドして、Unity側でCPUの設定をする。
Linux CPU Arm64はおそらくbazelのコンパイルオプションでできた…気がする…。
Windows CPUはデフォルトでXNNDelegate付きでアップデートした。Draft PR作って作業中。
macOSはCPUは問題なくビルドできるものの、GPUDelegateがv2.9.0から壊れていることを確認。
ERROR: /Users/ibu/Projects/ML/tensorflow/tensorflow/lite/delegates/gpu/BUILD:166:12: Linking tensorflow/lite/delegates/gpu/tensorflow_lite_gpu_dylib_bin failed: (Aborted): wrapped_clang_pp failed: error executing command external/local_config_cc/wrapped_clang_pp @bazel-out/macos-arm64-min10.13-applebin_macos-darwin_arm64-opt-ST-4d5b244fa833/bin/tensorflow/lite/delegates/gpu/tensorflow_lite_gpu_dylib_bin-2.params
Undefined symbols for architecture arm64:
"absl::lts_20211102::UnknownError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
tflite::gpu::metal::(anonymous namespace)::AllocateTensorMemory(id<MTLDevice>, tflite::gpu::StrongShape<(tflite::gpu::Layout)12> const&, tflite::gpu::TensorDescriptor const&, void const*, id<MTLBuffer> __autoreleasing*, id<MTLTexture> __autoreleasing*) in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::(anonymous namespace)::CreateTextureBuffer(id<MTLBuffer>, unsigned long long, tflite::gpu::StrongShape<(tflite::gpu::Layout)12> const&, tflite::gpu::TensorDescriptor const&, id<MTLTexture> __autoreleasing*) in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::CreateSharedImage2DBufferTensor(id<MTLBuffer>, tflite::gpu::StrongShape<(tflite::gpu::Layout)12> const&, tflite::gpu::TensorDescriptor const&, int, tflite::gpu::metal::MetalSpatialTensor*, unsigned long long) in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::LinearStorage::CreateFromTensorLinearDescriptor(tflite::gpu::TensorLinearDescriptor const&, id<MTLDevice>) in liblinear_storage.a(linear_storage.o)
tflite::gpu::metal::Texture2D::CreateFromTexture2DDescriptor(tflite::gpu::Texture2DDescriptor const&, id<MTLDevice>) in libtexture2d.a(texture2d.o)
tflite::gpu::metal::(anonymous namespace)::CreateTexture2D(int, int, tflite::gpu::DataType, void*, id<MTLDevice>, tflite::gpu::metal::Texture2D*) in libtexture2d.a(texture2d.o)
"absl::lts_20211102::DataLossError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
tflite::gpu::metal::InferenceContext::RestoreDeserialized(absl::lts_20211102::Span<unsigned char const>, id<MTLDevice>, tflite::gpu::CreateGpuModelInfo*) in libinference_context.a(inference_context.o)
"absl::lts_20211102::InternalError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
tflite::gpu::metal::(anonymous namespace)::DelegatePrepare(TfLiteContext*, TfLiteDelegate*)::$_1::__invoke(TfLiteContext*, char const*, unsigned long) in libmetal_delegate.a(metal_delegate.o)
tflite::gpu::metal::(anonymous namespace)::DelegatePrepare(TfLiteContext*, TfLiteDelegate*)::$_4::__invoke(TfLiteContext*, TfLiteNode*) in libmetal_delegate.a(metal_delegate.o)
tflite::gpu::metal::ComputeTask::CompileProgram(tflite::gpu::metal::MetalDevice*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > const&) in libcompute_task.a(compute_task.o)
tflite::gpu::metal::(anonymous namespace)::AllocateTensorMemory(id<MTLDevice>, tflite::gpu::StrongShape<(tflite::gpu::Layout)12> const&, tflite::gpu::TensorDescriptor const&, void const*, id<MTLBuffer> __autoreleasing*, id<MTLTexture> __autoreleasing*) in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::MetalSpatialTensor::ReadData(id<MTLDevice>, void*) const in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::MetalSpatialTensor::WriteData(id<MTLDevice>, void const*) in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::CreateComputeProgram(id<MTLDevice>, std::__1::basic_string<char, id<MTLDevice>::char_traits<char>, id<MTLDevice>::allocator<char> > const&, std::__1::basic_string<char, id<MTLDevice>::char_traits<char>, id<MTLDevice>::allocator<char> > const, id<MTLDevice>::map<id<MTLDevice>::allocator<char>, id<MTLDevice>::allocator<char>, id<MTLDevice>::less<id<MTLDevice>::allocator<char> >, id<MTLDevice>::char_traits<char><id<MTLDevice>::pair<std::__1::basic_string<char, id<MTLDevice>::char_traits<char>, id<MTLDevice>::allocator<char> >, id<MTLDevice>::allocator<char> > > > const&, id<MTLComputePipelineState> __autoreleasing*) in libcommon.a(common.o)
...
"absl::lts_20211102::NotFoundError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
_TFLGpuDelegateBindMetalBufferToTensor in libmetal_delegate.a(metal_delegate.o)
tflite::gpu::metal::(anonymous namespace)::DelegatePrepare(TfLiteContext*, TfLiteDelegate*)::$_1::__invoke(TfLiteContext*, char const*, unsigned long) in libmetal_delegate.a(metal_delegate.o)
tflite::gpu::metal::ComputeTask::Tune(tflite::gpu::TuningType, tflite::gpu::metal::MetalDevice*) in libcompute_task.a(compute_task.o)
tflite::gpu::metal::MetalArguments::SetInt(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int) in libmetal_arguments.a(metal_arguments.o)
tflite::gpu::metal::MetalArguments::SetFloat(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, float) in libmetal_arguments.a(metal_arguments.o)
tflite::gpu::metal::MetalArguments::SetHalf(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, tflite::gpu::half) in libmetal_arguments.a(metal_arguments.o)
tflite::gpu::metal::MetalArguments::SetObjectRef(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, tflite::gpu::metal::GPUObject const&) in libmetal_arguments.a(metal_arguments.o)
...
"absl::lts_20211102::strings_internal::CatPieces(std::initializer_list<std::__1::basic_string_view<char, std::__1::char_traits<char> > >)", referenced from:
tflite::gpu::metal::MetalArguments::GetArgumentBufferStructDefinition(bool) in libmetal_arguments.a(metal_arguments.o)
tflite::gpu::metal::MetalArguments::GetListOfArgs(int, int) in libmetal_arguments.a(metal_arguments.o)
"absl::lts_20211102::UnimplementedError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
tflite::gpu::GPUObjectDescriptor::PerformConstExpr(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libmetal_delegate.a(metal_delegate.o)
tflite::gpu::GPUObjectDescriptor::PerformSelector(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libmetal_delegate.a(metal_delegate.o)
tflite::gpu::GPUObjectDescriptor::PerformConstExpr(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libinference_context.a(inference_context.o)
tflite::gpu::GPUObjectDescriptor::PerformSelector(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libinference_context.a(inference_context.o)
tflite::gpu::GPUObjectDescriptor::PerformConstExpr(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::GPUObjectDescriptor::PerformSelector(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::GPUObjectDescriptor::PerformConstExpr(tflite::gpu::GpuInfo const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const in libmetal_arguments.a(metal_arguments.o)
...
"absl::lts_20211102::substitute_internal::SubstituteAndAppendArray(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > const*, unsigned long)", referenced from:
tflite::gpu::metal::MetalArguments::Init(bool, tflite::gpu::metal::MetalDevice*, tflite::gpu::Arguments*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) in libmetal_arguments.a(metal_arguments.o)
"absl::lts_20211102::InvalidArgumentError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
tflite::gpu::metal::ComputeTask::UpdateParams() in libcompute_task.a(compute_task.o)
tflite::gpu::metal::InferenceContext::InitFromGraph(tflite::gpu::CreateGpuModelInfo const&, tflite::gpu::GraphFloat32 const&, id<MTLDevice>, std::__1::vector<unsigned char, id<MTLDevice>::allocator<unsigned char> >*) in libinference_context.a(inference_context.o)
tflite::gpu::metal::InferenceContext::RestoreDeserialized(absl::lts_20211102::Span<unsigned char const>, id<MTLDevice>, tflite::gpu::CreateGpuModelInfo*) in libinference_context.a(inference_context.o)
tflite::gpu::metal::InferenceContext::SetTensor(unsigned int const&, tflite::gpu::metal::MetalSpatialTensor*) in libinference_context.a(inference_context.o)
tflite::gpu::metal::MetalSpatialTensor::GetGPUResources(tflite::gpu::GPUObjectDescriptor const*, tflite::gpu::metal::GPUResourcesWithValue*) const in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::MetalSpatialTensor::IsValid(tflite::gpu::StrongShape<(tflite::gpu::Layout)10> const&) const in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
tflite::gpu::metal::MetalSpatialTensor::IsValid(tflite::gpu::StrongShape<(tflite::gpu::Layout)12> const&) const in libmetal_spatial_tensor.a(metal_spatial_tensor.o)
...
"absl::lts_20211102::ResourceExhaustedError(std::__1::basic_string_view<char, std::__1::char_traits<char> >)", referenced from:
tflite::gpu::metal::InferenceContext::AllocateMemoryForBuffers(tflite::gpu::metal::MetalDevice*) in libinference_context.a(inference_context.o)
ld: symbol(s) not found for architecture arm64
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
Error in child process '/usr/bin/xcrun'. 1
Target //tensorflow/lite/delegates/gpu:tensorflow_lite_gpu_dylib failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 29.955s, Critical Path: 4.50s
INFO: 417 processes: 3 internal, 414 local.
FAILED: Build did NOT complete successfully
MetalSpatialTensorは多分2.9系から導入されたやつかな?iOSだけ対応で、macOSで動かないっぽいエラー。macOS版のMetal Delegateは恐らく私しか使って使ってないので、自分で直さないといけないんだろうな。後ほどissue建てるか…。
MetalSpatialTensorに関するビルドエラーの件、
MediaPipeのIssueを参考に、 --cxxopt=--std=c++17
をつけることでビルド通るっぽいことを確認。目処が立ちそう。TensorFlow Liteのビルドしてるのに、TensorFlowよりMediaPipeのイシューの方が参考になるのが謎。
GpuDelegate をLinuxでビルドするのを試し中。
bazel build --config=linux -c opt --copt -Os --copt -DTFLITE_GPU_BINARY_RELEASE --copt -fvisibility=default --linkopt -s --strip always //tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_delegate.so --verbose_failures
だと、EGL と OpenCLないってエラー。
以下にあるようにEGLへのフォールバックしないオプション足してもEGLへリンク残ってるっぽい??
のでとりあえずEGL, OpenCLを入れる。
# Need to install dependencies EGL and OpenCL?
sudo apt-get install libegl1-mesa-dev
sudo apt install ocl-icd-opencl-dev
MediaPipeのほうがやろうとしてること近いし、似てるissueがあるかも。
お、MediaPipe参考にしたらこれ通ったかも。EGLからX11抜くオプション付き。
bazel build --config=linux -c opt --copt -Os --copt -DMESA_EGL_NO_X11_HEADERS --copt -DEGL_NO_X11 --copt -fvisibility=default --linkopt -s --strip always //tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_delegate.so
ビルドしたGpuDelegate for LinuxをUnity上で読み込もうとするとEGLがリンクされてないよというエラー
原因としてはこちらでAndroid以外のビルドからはEGLが除外されているため。とりあえずAndroid以外でもEGLをリンクして上げるとGpuDelegateがUnity上で動作することを確認した。
"//conditions:default": [
"-lEGL",
"-lGLESv2",
],
Linux GPU delegate 動いた!!!NVIDIA GeForce 1070つないだやつで動作確認するとCPUはCore i5なのに無茶早いぜ。
arm64 embedded linuxの方はクロスコンパイル中にEGLが見つからなくて失敗するな。これは環境いろいろありそう(OpenCL, EGLの対応可否が)なので、あまり深堀しないようにして、実機でそれぞれビルドするのが正解な気もする。
cross-platformなarrを使ったAndroidアプリ。正常に動いていて、動作には影響は無いものの、実機で以下のようなエラーが出るっぽい。
06-16 10:40:26.829 22265 24289 D Unity : Failed to load native plugin: Unable to load library '/data/app/~~jKYV1xoUoH0hkbgdPssPeA==/com.DefaultCompany.jazz-_hiuZw0JIfq3WfJPpcWKgw==/lib/arm64/libtensorflowlite_jni.so', error 'java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.ClassLoader java.lang.Class.getClassLoader()' on a null object reference'
06-16 10:40:26.832 22265 24289 D Unity : Failed to load native plugin: Unable to lookup library path for 'tensorflowlite_gpu_jni.so'.
06-16 10:40:26.833 22265 24289 D Unity : Failed to load native plugin: Unable to load library '/data/app/~~jKYV1xoUoH0hkbgdPssPeA==/com.DefaultCompany.jazz-_hiuZw0JIfq3WfJPpcWKgw==/lib/arm64/libtensorflowlite_gpu_jni.so', error 'java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.ClassLoader java.lang.Class.getClassLoader()' on a null object reference'
06-16 10:40:26.834 22265 24289 I tflite : Created TensorFlow Lite delegate for GPU.
恐らくaar内でJavaのクラスへの参照が残っているけど、Unityアプリでは見つからないってエラー。依存を消せると良いけど。
ちょっと確かめてみる。
iOSで出ていたエラー↓は
TensorFlowに来ているこちらのPRをパッチとして当てればビルドできるっぽい。
Windows のGPUDelegateは大変そうな予感。ひとまず以下のコマンドを試す。
bazel build -c opt --copt -Os --copt -DCL_DELEGATE_NO_GL --copt -DMESA_EGL_NO_X11_HEADERS --copt -DEGL_NO_X11 --copt -DCL_TARGET_OPENCL_VERSION=210 --copt -DTFLITE_GPU_BINARY_RELEASE --linkopt -s --strip always //tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_delegate.so
と以下のエラーがでる。
# Execution platform: @local_execution_config_platform//:platform
cl : Command line warning D9035 : option 'experimental:preprocessor' has been deprecated and will be removed in a future release
cl : Command line warning D9036 : us'Zc:preprocessor' instead of 'experimental:preprocessor'
.\tensorflow/lite/delegates/gpu/gl_delegate.h(19): fatal error C1083: Cannot open include file: 'GLES3/gl31.h': No such file or directory
Target //tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_gl.so failed to build
INFO: Elapsed time: 1.696s, Critical Path: 0.51s
INFO: 6 processes: 6 internal.
OpenGLESをWindowsでもリンクしようとしているみたい。GLDelegateV2は基本OpenCLで動作して、OpenGLESはフォールバック。Windowsでは基本OpenCLはインストールされているものとして、OpenGLESの依存を消す戦略がよさそう。
もしくは以下のリンクのようにOpenGLESをインストールしてしまうか。
/tensorflow/lite/delegates/gpu/api.h
自体が結構ヘビーにOpenGLESの実装に依存しているので、難しそうだ。
恐らくWindows用のEGLとOpenGLESを
からコンパイルするのが正解な気がするけど、TensorFlow LiteでもMeidaPipeでもWindowsのGPU Delegateは公式に対応されてないので、これはまたいつかの機会に挑戦します。
このIssueをみたらMesa経由でWindows用のOpenGLESをインストールして成功したって書いてあるな…。OpenGLESの依存がなくなればいいのに。