モチベ
VOICEVOX_COREの中で使用するGPUは0番目に固定されています
Multi-GPU環境とかだとVOICEVOXで使用するGPUが選べると便利だとおもいませんか?
VOICEVOX_COREのビルドは現状ヒホ氏しかできないので、ONNXRuntimeのほうを改造します
※いずれGPUを選択できるAPIが実装されるかもしれませんが暫定対応ということで
調査
onnxruntime-rsの session.rs を見る
↓
with_append_execution_provider_directml 関数定義を見る
↓
onnxruntime.dll の OrtSessionOptionsAppendExecutionProvider_DML コールされている
↓
なんか2番目 device_id がGPUを指定する番号っぽい!
//https://github.com/VOICEVOX/onnxruntime-rs/blob/master/onnxruntime/src/session.rsより抜粋
mod directml {
use super::*;
impl<'a> SessionBuilder<'a> {
pub fn with_append_execution_provider_directml(
self,
device_id: usize,
) -> Result<SessionBuilder<'a>> {
let status = unsafe {
sys::OrtSessionOptionsAppendExecutionProvider_DML(
self.session_options_ptr,
device_id as ::std::os::raw::c_int,
)
};
status_to_result(status).map_err(OrtError::SessionOptions)?;
Ok(self)
}
}
}
ちなみにVOICEVOX同封の onnxruntime.dll はv1.13.1っぽい
念のため改造するときはこのバージョンを使う
とりあえずビルドしてみる
ひつようなもの
- Visual Studio 2019
※C++によるデスクトップ開発が入っていればOK
※ONNX Runtimeの比較的新しいバージョンを試したいって人はVisual Studio 2022のほうがいいかも
※極力不要なものはインストールしたくない人はBuild Toolsだけで行けるかも
- Git
- CMake
- Python3※python3.12は上手くいかないかもしれない
ビルド
コマンドプロンプトで下記を実行
git clone --recursive https://github.com/microsoft/onnxruntime.git -b v1.13.1
cd onnxruntime
build.bat --cmake_extra_defines CMAKE_SYSTEM_NAME=Windows CMAKE_SYSTEM_PROCESSOR=x86_64 --config Release --parallel --update --build --build_shared_lib --use_dml
ビルドは通った!
動作確認(結果は失敗)
onnxruntime\build\Windows\Release\Release に
- onnxruntime.dll
- DirectML.dll
があるので、VOICEVOXのインストールパスにオリジナルのDLLを退避しつつ差し替える
差し替えた後VOICEVOXを起動してみたのだが上手くいかなかった
原因調査
Dependencies でオリジナルと今回ビルドした onnxruntime.dll を比較してみる
OrtGetWinMLAdapter なる関数が足りてない・・・
RustからC++のDLLを読み込むメカニズムはよくわかってないけど、これが原因?
Rustとか関係なく序数による関数のexportによる方法があることを知りませんでした…
序数がずれたらそりゃ読み込めんですな
OrtGetWinMLAdapterについて少し調べる
OrtGetWinMLAdapter だが onnxruntime\winml\adapter の winml_adapter_c_api.cpp に定義されてる
//https://github.com/microsoft/onnxruntime/blob/main/winml/adapter/winml_adapter_c_api.cppより抜粋
const WinmlAdapterApi* ORT_API_CALL OrtGetWinMLAdapter(_In_ uint32_t ort_version) NO_EXCEPTION {
if (ort_version >= 2) {
return &winml_adapter_api_1;
}
return nullptr;
}
どうやら、WinML(Windows Machine Learning )関係の関数っぽい
build.bat のオプションに --use_winml があるのだが、これを指定するとどうやってもビルドが通らない
いろいろやってみたが・・・無理だったのであきらめた
そもそもVOICEVOX側でこの関数使ってるのか?
うん、たぶん使ってない!
よしダミー関数でもでっち上げよう!
2024/2/2
※後述しますがbuildオプションを追加したらいけました・・・
再ビルド(ボツ案)
2024/2/2
※この方法は邪道かつ力技なので、無視してください
※とはいっても、今後役に立つこともあるかもしれないので備忘として
いくつかファイルに修正をいれる
onnxruntime\onnxruntime\core\providers\cpu\symbols.txt を下記のように修正
OrtGetApiBase
OrtSessionOptionsAppendExecutionProvider_CPU
↓
OrtGetApiBase
OrtGetWinMLAdapter
OrtSessionOptionsAppendExecutionProvider_CPU
onnxruntime\core\session\provider_bridge_ort.cc の末尾に下記を追加
//nullptrを返すだけのOrtGetWinMLAdapter関数をでっち上げる
struct WinmlAdapterApi;
typedef struct WinmlAdapterApi WinmlAdapterApi;
const WinmlAdapterApi* ORT_API_CALL OrtGetWinMLAdapter(_In_ uint32_t ort_version) NO_EXCEPTION {
return nullptr;
}
準備が整ったので再びビルドする
再ビルド(正攻法)※2024/2/2 追記
build.batのオプションに --enable_wcos --use_winml を追加するだけでよかった・・・
build.bat --cmake_extra_defines CMAKE_SYSTEM_NAME=Windows CMAKE_SYSTEM_PROCESSOR=x86_64 --config Release --parallel --update --build --build_shared_lib --use_dml --enable_wcos --use_winml
動作確認
まずは、Dependencies で確認してみる・・・大丈夫そう!
VOICEVOXも・・・起動できた!
GPUを選択できるよう修正して再ビルド
お手軽に環境変数でコントロールしようかと
OrtSessionOptionsAppendExecutionProvider_DML は下記の部分ですね
//https://github.com/microsoft/onnxruntime/blob/6d7ac9c93ae7cd1f979b35e7f6d7af207962cd99/onnxruntime/core/providers/dml/dml_provider_factory.ccより抜粋
ORT_API_STATUS_IMPL(OrtSessionOptionsAppendExecutionProvider_DML, _In_ OrtSessionOptions* options, int device_id) {
API_IMPL_BEGIN
options->provider_factories.push_back(onnxruntime::DMLProviderFactoryCreator::Create(device_id));
API_IMPL_END
return nullptr;
}
これをこんな感じに変えてみます
#include <charconv>
ORT_API_STATUS_IMPL(OrtSessionOptionsAppendExecutionProvider_DML, _In_ OrtSessionOptions* options, int device_id) {
API_IMPL_BEGIN
std::size_t len;
char buff[1024];
if(!getenv_s(&len, buff, sizeof(buff), "ONNX_DML_DEVICE")){
if(len > 0 && buff[len - 1] == '\0') len--;
auto first = buff;
auto last = buff + len;
decltype(device_id) val;
auto [ptr, ec] = std::from_chars(first, last, val);
if(ec == std::errc{} && ptr == last){
device_id = val;
}
}
options->provider_factories.push_back(onnxruntime::DMLProviderFactoryCreator::Create(device_id));
API_IMPL_END
return nullptr;
}
再ビルドして、DLLを差し替えます
動作確認(最終)
うちの環境では、0番目に内蔵GPU、1番目に外付けGPUという環境です
VOICEVOXのエディターのほうではよく確認できなかったので自作のDiscord TTS-Botで確認します。
https://qiita.com/kmatsumoto630823/items/6bc0e1f85be27e026849
まずVOICEVOXエンジンを普通に起動します
"C:\Program Files\VOICEVOX.\run.exe" --use_gpu
内蔵GPUが貧弱すぎてTTS-Botはうんともすんとも言いません
環境変数をセットして起動します
set ONNX_DML_DEVICE=1
"C:\Program Files\VOICEVOX.\run.exe" --use_gpu
TTS-Botから声が出るようになりました!
最後に
いずれはVOICEVOXにもGPUを選択できるようなAPIが実装されるかもしれませんが
それまではこの暫定対応で行こうかと思います!