0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WSL2のChromeでローカルGemini(Gemini Nano)を動かす 〜Mesa Dozenにパッチを当ててfullDrawIndexUint32の壁を越える〜

0
Posted at

はじめに

WSL2 上の Chrome で、Google のオンデバイス AI Gemini Nanowindow.LanguageModel などの Prompt API で叩けるやつ)を動かしたかった。

が、普通にやると GPU がソフトウェアレンダリングに落ちて完全に詰む。色々調べて格闘した末、最終的に Mesa のドライバにパッチを当てて自前ビルドするという結論に至り、最後は本当に動いた。

この記事は、その過程で見つけた GitHub リポジトリと、そこから得たヒントをもとに、自分の環境(NVIDIA GeForce RTX 3060 Laptop)でローカル Gemini を動かすまでの記録。

結論を先に置いておくと、ハマりどころは「WSL で Vulkan が実 GPU を引けない」ことと、「Mesa Dozen が fullDrawIndexUint32false と報告して Dawn に弾かれる」ことの二段構えだった。

環境

  • Windows 11 + WSL2(Ubuntu 24.04, x86_64)
  • NVIDIA GeForce RTX 3060 Laptop GPU(VRAM 6GB)
  • Windows 側に NVIDIA ドライバ導入済み(WSL の GPU サポート有効)

1. まず詰まる:SwiftShader で死ぬ

chrome://on-device-internals からオンデバイス機能を有効化しようとすると、モデルサービスがこう吐いて落ちる。

Selected adapter: SwiftShader Device (Subzero), backend=Vulkan, adapterType=CPU / Software
Terminating On-Device Model Service: Failed creation: Failed to create device:
Required limit (14336) is greater than the supported limit (8192).
 - While validating maxTextureDimension2D

オンデバイスモデルは maxTextureDimension2D = 14336 を要求するが、SwiftShader(CPU ソフトウェア Vulkan)の上限は 819214336 > 8192 でデバイス生成に失敗している。

なぜ SwiftShader なのか。Vulkan が実 GPU を引けていないからだ。

vulkaninfo | grep deviceName
# deviceName = llvmpipe (LLVM ...)

llvmpipe。これも CPU ソフト実装。CUDA は通る(nvidia-smi も動く)のに、グラフィックス系の Vulkan には実 GPU が出てこない。 WSL では計算(CUDA)とグラフィックス(Vulkan)が別レイヤーで、Chrome のオンデバイスモデル(内部で Dawn/WebGPU を使う)が要求するのは後者なので、CUDA が動いていても無関係に詰む。

2. 真因その1:WSL に入れてはいけないドライバ

調べると、システムにネイティブ Linux 版の NVIDIA ドライバがフルセットで入っていた。

dpkg -l | grep nvidia
# nvidia-driver-580, nvidia-dkms-580, libnvidia-gl-580, xserver-xorg-video-nvidia-580 ...

これらは物理 Linux マシン用で、/dev/nvidia0 やカーネルモジュールの存在を前提にする。WSL にはそれが無いため初期化に失敗し、Vulkan が llvmpipe に落ちていた。

ls /dev/nvidia*       # No such file or directory
lsmod | grep nvidia   # 何も出ない

WSL で GPU を使う正しい仕組みは /usr/lib/wsl/lib/ 配下のドライバ(DXGI 経由で Windows 側 GPU を叩く)だけ。ネイティブドライバは害なので除去する。

sudo apt-get remove --purge '^nvidia-driver-.*' '^nvidia-dkms-.*' \
  '^xserver-xorg-video-nvidia.*' '^libnvidia-gl-.*' \
  '^nvidia-kernel-.*' '^libnvidia-cfg.*' nvidia-settings nvidia-prime

nvidia-smi/usr/lib/wsl/lib/nvidia-smi(dpkg 管理外の WSL 正規版)が残るので CUDA 経路は無傷。

3. Dozn(D3D12 → Vulkan)を導入する

WSL には libd3d12.so があり D3D12 は通る。Mesa の Dozn (Dozen / dzn) は D3D12 の上に Vulkan を実装するドライバなので、これ経由なら実 GPU を Vulkan に見せられる。

sudo add-apt-repository ppa:kisak/kisak-mesa -y
sudo apt update
sudo apt install -y mesa-vulkan-drivers

VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/dzn_icd.json \
  LD_LIBRARY_PATH=/usr/lib/wsl/lib \
  vulkaninfo | grep -A2 deviceName
deviceName = Microsoft Direct3D12 (NVIDIA GeForce RTX 3060 Laptop GPU)
maxImageDimension2D = 16384

実 GPU が Vulkan に現れ、maxImageDimension2D = 16384 で 14336 の要件も満たす。勝ったと思った。

4. 真因その2:fullDrawIndexUint32 で弾かれる

ところが Chrome のモデルサービスに Dozn を渡しても同じ場所で死ぬ。

Warning: Vulkan fullDrawIndexUint32 feature required.
 - While initializing adapter (backend=BackendType::Vulkan)
   at PhysicalDeviceVk.cpp:253
Selected adapter: SwiftShader Device (Subzero)
Required limit (14336) > supported limit (8192)

モデルサービスは内部で Dawn(WebGPU 実装) を使い、Dawn は初期化時に fullDrawIndexUint32必須要件として要求する。

vulkaninfo | grep -E "fullDrawIndexUint32|driverName"
# driverName = Dozen
# fullDrawIndexUint32 = false

Dozn がこれを false で返すため、Dawn は Dozn を却下し SwiftShader にフォールバック、8192 の壁で死ぬ。

Dozn(NVIDIA, 16384) → fullDrawIndexUint32 が無い → Dawn が却下
SwiftShader(8192)   → 機能はあるが上限8192     → サイズ要件で却下
→ どちらも通らず詰み

5. 同じ壁にぶつかった人たち

調べると、この fullDrawIndexUint32 の問題は自分だけのものではなかった。

Gemini CLI の公式リポジトリに、まさに同じ症状の Issue が上がっていた(google-gemini/gemini-cli #27182)。NVIDIA 搭載の WSL2 で gemini gemma を動かそうとすると、Dawn が fullDrawIndexUint32 を要求し、Dozen がそれを満たせず Found 0 adapters で落ちる、という内容。ログも自分が見たものとほぼ同一だった。

Warning: Vulkan fullDrawIndexUint32 feature required.
 - While initializing adapter (backend=BackendType::Vulkan)
   at PhysicalDeviceVk.cpp:253
Found 0 adapters
Failed to create engine: INTERNAL: No adapters found

この Issue は kind/bug / priority/p2 でオープンのまま(執筆時点)。「Dawn がグラフィックス機能 fullDrawIndexUint32 を必須化していて、仮想化環境の Dozen には無い」ことが根本原因と整理されていた。つまり設定で回避できる類ではない、既知の構造的な問題ということ。

次に wsl2 dozn fullDrawIndexUint32 で検索を続けて、Qualcomm Adreno(Snapdragon X Elite)を WSL2 で動かすためのリポジトリ(Mycomembranes/adreno-wsl2-gpu)に辿り着いた。Mesa Dozen にパッチを当てるもので、パッチノートにこうあった。

fullDrawIndexUint32 = true に変更。
D3D12 は常に 32bit インデックスをサポートしている。このフラグは不必要に保守的だった。

つまり、false は嘘というか、過度に保守的な値だった。D3D12 は本来 32bit インデックス描画に対応しているのに、Dozn が控えめに無効化していただけ。

このリポジトリ自体は ARM (aarch64) / Adreno 向けだが、パッチが触る src/microsoft/vulkan/dzn_device.c の変更点は Adreno 固有の処理ではない。D3D12 抽象を見ているだけなので、x86_64 + NVIDIA でもそのまま当たる(実際に当たった)。

- .fullDrawIndexUint32 = false,
+ .fullDrawIndexUint32 = true,    # D3D12 は 32bit インデックスをサポート
- .logicOp = false,
+ .logicOp = true,                # D3D12 FL11.0+ は logic op をサポート
- .major = 0, .minor = 0,         # conformance 0.0 → 1.2 でローダー却下を回避
+ .major = 1, .minor = 2,
- vk_warn_non_conformant_implementation("dzn");
+ /* コメントアウト */

6. Mesa Dozen を自前ビルドする

ビルドツールを入れる(libllvmspirvlib-dev はパッケージ名が変わっているので除外)。

sudo apt install -y meson ninja-build gcc g++ pkg-config git glslang-tools \
  llvm-dev libdrm-dev libx11-dev libxcb1-dev libxrandr-dev \
  zlib1g-dev libexpat1-dev python3 python3-mako python3-yaml \
  libwayland-dev wayland-protocols libxext-dev bison flex cython3 libxcb-randr0-dev

パッチが想定するバージョンの Mesa を取得して適用、Dozn だけ最小構成でビルドする。

git clone --depth=1 --branch mesa-25.0.5 \
  https://gitlab.freedesktop.org/mesa/mesa.git ~/mesa
cd ~/mesa
# パッチ適用(リポジトリの .patch を git apply、または該当行を直接書き換え)

meson setup builddir \
  --prefix=$HOME/mesa-dzn \
  -Dvulkan-drivers=microsoft-experimental,swrast \
  -Dgallium-drivers=swrast \
  -Dplatforms=[] \
  -Dglx=disabled -Dopengl=false \
  -Dgles1=disabled -Dgles2=disabled \
  -Degl=disabled -Dgbm=disabled \
  -Dllvm=enabled \
  -Dbuildtype=release -Db_ndebug=true

ninja -C builddir
ninja -C builddir install

ハマりどころメモ:

  • -Dcpp_rtti=false は Ubuntu の LLVM(RTTI 有効)と衝突する。Remove cpp_rtti disable switch と言われたら外す
  • -Dplatforms=x11 は X11 dev パッケージを芋づるで要求する。計算用途なら -Dplatforms=[] でよい
  • ただし platforms=[] のときは GLX も明示的に disabled にしないと矛盾でこける

確認:

VK_ICD_FILENAMES=$HOME/mesa-dzn/share/vulkan/icd.d/dzn_icd.x86_64.json \
LD_LIBRARY_PATH=/usr/lib/wsl/lib:$HOME/mesa-dzn/lib/x86_64-linux-gnu \
vulkaninfo | grep -E "deviceName|fullDrawIndexUint32"
deviceName = Microsoft Direct3D12 (NVIDIA GeForce RTX 3060 Laptop GPU)
fullDrawIndexUint32 = true

false だった値が true になった。

7. Chrome に自前 Dozn を食わせる

VK_ICD_FILENAMES=$HOME/mesa-dzn/share/vulkan/icd.d/dzn_icd.x86_64.json \
LD_LIBRARY_PATH=/usr/lib/wsl/lib:$HOME/mesa-dzn/lib/x86_64-linux-gnu \
google-chrome --enable-features=OptimizationGuideOnDeviceModel

ログから SwiftShader が消え、実 GPU が選択される。

Selected adapter: Microsoft Direct3D12 (NVIDIA GeForce RTX 3060 Laptop GPU),
                  arch=ampere, vendor=nvidia, backend=Vulkan, adapterType=Discrete GPU

14336 > 8192fullDrawIndexUint32 required も消えた。chrome://components の "Optimization Guide On Device Model" を Update するとモデル本体がダウンロードされ、ステータスが「最新」になる。

chrome://flags で以下を有効化:

  • #optimization-guide-on-device-model → Enabled (BypassPerfRequirement)
  • #prompt-api-for-gemini-nano → Enabled

8. Prompt API で動作確認

DevTools のコンソールで確認する。貼り付け保護がかかったら allow pasting(日本語 UI なら「貼り付けを許可」)を入力。WSL に日本語 IME が無い場合は DevTools の言語を英語にすると英字だけで抜けられる。

await LanguageModel.availability();
// "downloadable" or "available"

const session = await LanguageModel.create({ outputLanguage: "ja" });
const result = await session.prompt("やあ。日本語で自己紹介して。");
console.log(result);

応答が返り、別ターミナルの watch -n 1 /usr/lib/wsl/lib/nvidia-smi で GPU メモリ使用量が動けば、ローカルの GPU 上で推論が走っている。

まとめ

WSL2 でローカル Gemini を動かす障壁は二段構えだった。

  1. ネイティブ Linux 版 GPU ドライバが WSL に混入し、Vulkan が llvmpipe に落ちていた → 除去して WSL 正規ドライバに任せる
  2. Dozn が fullDrawIndexUint32 を保守的に false 報告し、Dawn に弾かれていた → ソースを true に書き換えて自前ビルド

押さえておきたいポイント:

  • WSL の「GPU 計算(CUDA)」と「グラフィックス(Vulkan)」は別レイヤー
  • ドライバが報告する機能フラグは、必ずしもハードウェアの真の能力ではない
  • OSS の強さは、別ハードウェア向けのパッチが自分の環境でもそのまま効くこと

検索でたまたま見つけた Adreno 向けのパッチが、まったく別の NVIDIA 環境を救ってくれた。先人に感謝。


参考

  • google-gemini/gemini-cli #27182 — 同じ症状(fullDrawIndexUint32 欠如で Dawn が Found 0 adapters)の公式 Issue
  • Mycomembranes/adreno-wsl2-gpu — Adreno 向けだが、fullDrawIndexUint32 = true 化の Mesa Dozen パッチが流用できる(Apache-2.0、リポジトリ表記による)
  • Mesa Dozen ドライバ(src/microsoft/vulkan/dzn_device.c
  • 検索のきっかけになったキーワード: wsl2 dozn fullDrawIndexUint32
  • 一連の手順をまとめた自前スクリプトも別途用意した(環境変数 SKIP_GEMINI=1 で「パッチ済み Dozn の汎用 Vulkan 環境」までで止められる)

クレジット: fullDrawIndexUint32 = true 化のアイデアと具体的なパッチは Mycomembranes/adreno-wsl2-gpu に基づく。ARM/Adreno 向けに書かれたものが、x86_64/NVIDIA の環境を救ってくれた。先人に感謝。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?