Hyper-VでGPU(3Dアクセラレーション)を利用する方法 Windows 10以降編 (GPU-P)

## はじめに

**この記事は,旧バージョンです.新バージョンは[こちら](https://qiita.com/Hyper-W/items/e189790fd4534d9d51ad)です.こちらの記事は更新されません。記録用と引用元として利用されているために残されています.この記事は更新されません.**

**[`Ubuntu`版](https://qiita.com/Hyper-W/items/5ddfc93891f7b620da8a)も投稿しました.**

Hyper-Vで,GPUを利用する方法として現在[公式](https://docs.microsoft.com/ja-jp/windows-server/virtualization/hyper-v/plan/plan-for-gpu-acceleration-in-windows-server)では,DDAとRemoteFXが案内されていますが,RemoteFXは[脆弱性](https://www.atmarkit.co.jp/ait/articles/2008/12/news006.html)により,更新で無効化されました.

DDAは,Windows 10では利用できないし,Windows Server 2019はサポート対象であるにも関わらず,動かないようです.

つまり,現在公式資料にある方法ではHyper-Vで3Dアクセラレーションを利用出来ない状態です.

しかし,公式資料には今の所,利用方法は存在しませんがRemoteFXに変わる技術として,GPU-Pが発表されています.

既に,Windows SandboxのGPU仮想化,[Azure](https://azure.microsoft.com/ja-jp/blog/power-your-azure-gpu-workstations-with-flexible-gpu-partitioning/)で利用されているようです.又,おそらくWindows11以降で実装されるWSL2のGPU仮想化技術GPU-PVで同種の技術が使われるようになります.

既に利用されているということは,Windowsに実装されているということです.

実際に,PowershellでHyper-Vに関するコマンドを調べると,

```Powershell
> Get-Command -module Hyper-V

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
                (前略)
Cmdlet          Add-VMGpuPartitionAdapter                          2.0.0.0    Hyper-V
                (中略)
Cmdlet          Get-VMGpuPartitionAdapter                          2.0.0.0    Hyper-V
                (中略)
Cmdlet          Remove-VMGpuPartitionAdapter                       2.0.0.0    Hyper-V
                (中略)
Cmdlet          Set-VMGpuPartitionAdapter                          2.0.0.0    Hyper-V
                (後略)
```

のように既に実装されています.

Get-Helpコマンドで調べると構文は出てきますが,説明はまだ公式サポートされていないので,出てきません.(Powershell 7で実行すると更に詳しい構文が出てきます)

## 動作確認環境

親:
```
OS:Windows 10
バージョン:2004
GPU:Nvidia Geforce GTX 1070(固定)
```

子:
```
OS:Windows 10
バージョン:2004
世代:第二世代
```

## 実際に利用できるようにする.

**注意**
**この機能は,まだ公式にサポートされていない方法です.なにか問題が起きても自己責任です.**

- チェックポイントを適用・削除する.(追記)

GPU-Pでは,チェックポイントが利用出来ません.(現状サポートされていません.)

また,有効化するためにはチェックポイントを無効化する必要があります.

そのため,自動チェックポイントを使っている場合は無効に,チェックポイントが既に存在する場合は,チェックポイントの削除または適用を,バックアップにチェックポイントを利用しないことが必要です

- GPU Partition Adapterを追加し,設定する.

まずは,先程のコマンドを利用して仮想マシンにGPU Partition Adapterを追加し,設定します.

Powershellを**管理者権限**で実行して,下記コマンドを実行します.

```Powershell
> Add-VMGpuPartitionAdapter -VMName "仮想マシン名"

> Set-VMGpuPartitionAdapter -VMName "仮想マシン名" -MinPartitionVRAM 80000000 -MaxPartitionVRAM 100000000 -OptimalPartitionVRAM 100000000 -MinPartitionEncode 80000000 -MaxPartitionEncode 100000000 -OptimalPartitionEncode 100000000 -MinPartitionDecode 80000000 -MaxPartitionDecode 100000000 -OptimalPartitionDecode 100000000 -MinPartitionCompute 80000000 -MaxPartitionCompute 100000000 -OptimalPartitionCompute 100000000

> Set-VM -GuestControlledCacheTypes $true -VMName "仮想マシン名"

> Set-VM -LowMemoryMappedIoSpace 1GB -VMName "仮想マシン名"

> Set-VM –HighMemoryMappedIoSpace 32GB –VMName "仮想マシン名"
```

私は,以下のようにbatファイルにまとめています.

```bat
@set @dummy=0/*
@echo off
NET FILE 1>NUL 2>NUL
if "%ERRORLEVEL%" neq "0" (
  cscript //nologo //E:JScript "%~f0" %*
  exit /b %ERRORLEVEL%
)

@echo on
@set /P VMName="VMName: "
powershell -Command "Add-VMGpuPartitionAdapter -VMName %VMName%"
powershell -Command "Set-VMGpuPartitionAdapter -VMName %VMName% -MinPartitionVRAM 80000000 -MaxPartitionVRAM 100000000 -OptimalPartitionVRAM 100000000 -MinPartitionEncode 80000000 -MaxPartitionEncode 100000000 -OptimalPartitionEncode 100000000 -MinPartitionDecode 80000000 -MaxPartitionDecode 100000000 -OptimalPartitionDecode 100000000 -MinPartitionCompute 80000000 -MaxPartitionCompute 100000000 -OptimalPartitionCompute 100000000"
powershell -Command "Set-VM -GuestControlledCacheTypes $true -VMName %VMName%"
powershell -Command "Set-VM -LowMemoryMappedIoSpace 1GB -VMName %VMName%"
powershell -Command "Set-VM -HighMemoryMappedIoSpace 32GB -VMName %VMName%"
@pause

@echo off
goto :EOF
*/
var cmd = '"/c ""' + WScript.ScriptFullName + '" ';
for (var i = 0; i < WScript.Arguments.Length; i++) cmd += '"' + WScript.Arguments(i) + '" ';
(new ActiveXObject('Shell.Application')).ShellExecute('cmd.exe', cmd + ' "', '', 'runas', 1);
```

こうすることで,batファイルをダブルクリックすると,自動でUACを出して管理者権限で実行する事ができるようになり,VMName: と聞かれるので'(シングルクォート)で仮想マシン名を囲ったものを入力すると,Adapterを自動的に追加し,設定することが出来ます.

- 子マシン用のGPUドライバーを追加する.

上記の設定で既に,Adapterは子マシンから確認出来るようになっていますが,この時点でまだ動作はしていません.

子マシンにドライバーを追加する必要があります.

このとき,子マシンからドライバーを普通にインストールするとエラーが出てインストールできないと思うので,親マシンから特定の場所に必要なドライバーファイルをコピーする必要があります.~~(ここで,私はNvidia以外方法がわからなかったのでNvidiaとしています)~~

### Nvidia (Cuda)で利用する方法.

まず,親からのCドライブで,nvapi64.dllを検索します.

そうすると,親のC:\Windows\System32\直下と,C:\Windows\System32\DriverStore\FileRepository\以下の2つが,見つかると思います.

親のC:\Windows\System32\にあったnvapi64.dllは,同じく子のC:\Windows\System32\直下に配置します.

親のC:\Windows\System32\DriverStore\FileRepository\以下にあったものは,子のC:\Windows\System32\HostDriverStore\FileRepository\にnvapi64.dllのあった**フォルダごと**コピーして配置します.

**追記**

### Nvidia以外でも利用する方法.

上記の様にすれば,NvidiaのGPUであれば導入出来るが,この機能はそもそも,Windows Sandboxで利用出来るのでWindows Sandboxからコピペしてこれば,GPUは関係ない.

Nvidia GPUでも,この方法で利用可能になるが,Cudaを利用したアプリケーションを実行する場合は,[上記](#nvidia-cudaで利用する方法)と同じ様に,親のC:\Windows\System32\直下のnvapi64.dllを子のC:\Windows\System32\直下にコピーする必要があります(HostDriverStore周りは不要).

記事の初投稿から,数ヶ月経過しており,検証環境のWindowsのバージョンが親,子共に2004->20H2に変化してますが,おそらく問題ないので該当部分のみ追記します.

まずは,Windows Sandboxを有効化します.

Windowsの機能の有効化または無効化から,Windows Sandboxのチェックボックスにチェックを入れokを押して再起動します.

もし,Powershellでやるなら,

```Powershell
> Enable-WindowsOptionalFeature –FeatureName "Containers-DisposableClientVM" -All -Online
```

とし,再起動します.

次に,ファイルをコピーするために,GPUを追加したい仮想マシンの仮想HDDをマウントします.

コピーする方法はできれば,何でも良いのですが,今回は,親マシンにマウントした仮想HDDをWindows Sandboxにマッピングする形でコピーします.

マッピングせずにコピペしようとすると,死ぬほど時間が掛かりそうだったのでおすすめしません.

今回マウントした仮想HDDは,Eドライブにマウントした事とします.

次に,マウントした仮想ディスクにGPU-Pで利用するドライバーを配置するフォルダを作成します.

E:\Windows\System32\に移動し,HostDriverStoreという名前のフォルダを作成します.

この操作には管理者権限が必要です.

Eドライブ以外にマウントした場合は各自そのドライブに置き換えてください.

次に,GPUと仮想HDDをマッピングしたWindows Sandboxの設定ファイルを作成します.

設定ファイルは以下の通りです.

```xml
<Configuration>
<VGpu>Enable</VGpu>
<MappedFolders>
   <MappedFolder>
       <HostFolder>E:\Windows\System32\HostDriverStore</HostFolder>
       <ReadOnly>false</ReadOnly>
   </MappedFolder>
</MappedFolders>
</Configuration>
```

これをwsbの拡張子を持ったファイルとして保存します.

先程作成したGPU-P用のドライバーの配置フォルダをマウントして直接コピーできる様にマウントしています.

ここでも,Eドライブ以外にマウントした場合は各自そのドライブに置き換えてください.

このファイルから直接起動してしまうと,権限の問題でマッピングしたフォルダーにファイルを作成出来ないので,コマンドプロンプトから起動します.

管理者権限に昇格したコマンドプロンプトで,作成した設定ファイルのある場所に移動して実行します.

```cmd
.\設定ファイルの名前.wsb
```

起動するとデスクトップにマウントしたHostDriverStoreフォルダがあるので,それをエクスプローラーで開き,もう一つのエクスプローラーでWindows SandboxにあるGPU-P用ドライバーを配置しているフォルダであるC:\Windows\System32\HostDriverStoreを開きます.

このC:\Windows\System32\HostDriverStoreにあるFileRepositoryというフォルダをマウントしたHostDriverStoreにコピペします.

この記事の修正前の様に,必要なファイルが分かる場合は,FileRepositoryの中身をすべてコピペする必要はありません.

コピーが終われば,Windows Sandboxを閉じ,マウントした仮想HDDをアンマウントしてください.

**追記終了**

これで,次に仮想マシンを起動すると,GPUを認識し3Dアクセラレーションを利用できるようになりました.

![GPU-P_Enable.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/cfd9b1c5-ae47-2063-f889-c7ebd45611a9.jpeg)

Windows Terminalは3Dアクセラレーションが有効でないと透過されないので3Dアクセラレーションが利用できている事がわかると思います.

![GPU-P_Disable.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/09d1e702-0f7d-3f32-3ad2-bbc1bb2beb26.jpeg)

上は認識していない時です.

ゲーム(ベンチマーク)でも,認識しました.

![GPU-P_PSO2ベンチ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/5b8c8066-74c4-4016-fdf5-4713704204a8.jpeg)
![GPU-P_FFベンチ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/ab71a36b-6ef2-8e57-a032-eebf0fbb6d00.jpeg)

PSO2ベンチとFFベンチを回しました.CPUの割当は4コア,メモリは固定8GB,可変vhdx(SSD上)です(ドラクエベンチは起動しませんでした).

親で計測した時よりもかなり落ちていますが,リソースが制限されている事,親と子でリソースを共有している事を考えるとこんなものかという感じですね.

それでも,快適,やや快適なのでそれなりには使えるのかな?

## GPU Partition Adapterの削除.

**管理者権限のPowershell**で,以下のコマンドを実行.

```Powershell
> Remove-VMGpuPartitionAdapter -VMName "仮想マシン名"
```

削除すると,チェックポイントも再び使用可能になる.

## CUDAの動作確認.

仮想マシンにVisual Studio 2019と,CUDAをインストールしてdeviceQueryをビルドしました.

CUDAのインストールも出来たし,deviceQueryのビルドも成功して実行すると以下のように認識していたのでCUDAも使える?ということでいいのかな(よくわかってない).

![GPU-P_CUDA.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/82ebb3f8-175e-fc46-8a57-1c817a6ffae7.png)
![GPU-P_CUDA2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/85626aa1-78b3-4485-2eff-5e75a8c40a26.png)

## iGPUを有効化した場合の挙動.(複数のGPUを認識している場合の挙動)

SLIの場合はどうなるかわかりませんが,複数のGPUを認識している場合,Add-VMGpuPartitionAdapterのコマンドが実行された時点で,親タスクマネージャーにおいて,GPU0と認識されているGPUのアダプターが割当られるようです.

親の方でGPUで認識されるGPUの番号が変化してGPU1として認識されても一旦割り当てられると正常に前のGPU0(現GPU1)を認識するようです.

![GPU-P_iGPU.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/1cf20245-099e-e92d-9dae-12ca6903478c.jpeg)

画像でGPUが両方とも動作していないのは,ドライバを入れていないためです(入れればGeforceの方は動作するはず).
GPUを単一に固定できるPCならば任意のGPUを割り当てる事が出来る思います.

## Windows 10以外の子マシンでの動作確認.

Windows 8.1(第二世代):以下のように認識はしますが,動作はしません.(GPU Partition Adapterを追加すると,以下のようにUSBのようなリムーバブルデバイスとして表示されます.アンマウントするとGPUを認識しなくなり,3Dアクセラレーションが利用できなくなります)

![GPU-P_Windows8.1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/0e6cb9d3-507d-c159-6da3-1b982ef440ce.jpeg)

Hyper-V Server 2019(第二世代):同様に認識はしますが,動作はしません.

Windows Server 2019 デスクトップエクスペリエンス(第二世代):Hyper-V Server 2019と同じ.(親と同じバージョンでないとだめ? もしくは,もっと新しいバージョンでないとだめ?)

Ubuntu 18.04 LTS(第二世代):lspciをすると,MicrosoftのGPUとして認識します(が,動作していません).

Windows 7(第一世代):認識も動作はしません.(第一世代仮想マシンは認識すらしない?)

~~現状,親と同じバージョンのWinodws 10でないと動作しない?~~

### Windows 10 (21H1)の親マシンでの検証

Windows Server 2022 デスクトップエクスペリエンス(第二世代): Windows 10と同様に正常に動作.

![GPU-P-WS0222.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/5035daba-27a8-8146-1f7a-dac05bb3e751.png)

Windows 11 21H2 正式版(第二世代): Windows 10と同様に正常に動作.

![GPU-P-W11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743928/df97b0e4-5e8c-1192-1efa-a522c795cf23.png)

### Windows 11 (21H2)の親マシンでの検証

Hyper-Vの構成バージョンは10.0です(それまでに動いたもののみで検証).

Windows 10 21H1(第二世代): 今までと同様に動作.

Windows 11 21H2(第二世代): 今までと同様に動作.

Windows Server 2022 デスクトップエクスペリエンス(第二世代): 今までと同様に動作.

新しいバージョンの子マシンなら動作する?

## 追記

よくよく考えれば,当たり前なのですが,Windows Sandboxで利用されている技術であるため,Windows Sandboxからコピーすれば,Nvidiaとか関係ないと気付いたので,タイトルからNvidiaを削除し,ドライバー導入の手順を更新しました.

前からですが,現状,Windows 10しか動いてないのでWindows 10編にタイトルを変更しました.

チェックポイントについて追記.

GPU Partition Adapterの削除について追記.

修正版の方法だとCudaが利用できなくなったので,再修正(nvapi64.dllがC:\Windows\System32\直下に無いため.Windows SandboxでvGPUを有効化してもCudaが実行できないのはこのため.ファイルをコピー後再起動が必要なのでWindows SandboxではCudaは利用できない.Cudaの様な各GPU固有のものについては各々他にコピーするファイルが必要かも?).

21H1で実装されなかったため,修正.

21H2ではなく,Windows11になったため修正.

Windows Server 2022,Windows 11(21H2)の子マシンでの検証を追加(Windows 10 21H1親マシン).

Windows 11でも動作を確認したので,Windows 10以降編にタイトルを変更.

Windows11の親マシンでの検証を追加.