HiDPIな環境でWSLgを使ってDear ImGuiを表示する

# はじめに
Win11では新機能WSLgが追加され、macosのようにlinux/unixのソフトウェアをWSL環境で動かして、そのGUIをWindowsのデスクトップに表示できるようになりました。と思って[Dear ImGui](https://github.com/ocornut/imgui)を使うプログラムを書いたら思わぬところで嵌ったので、問題とその解決策を忘備録も兼ねて紹介します。

# 環境
Windows 11 Insider Preview Build 22581
WSL 0.56.2.0
WSLg 1.0.30
Ubuntu 20.04
NVIDIA Driver 512.15

# imguiのサンプルを動かす
## WSLgの準備
[公式のチュートリアル](https://docs.microsoft.com/en-us/windows/wsl/tutorials/gui-apps)に従ってWSLのアップデートとGPUドライバをインストール。チュートリアルには無いですが、
~~~
sudo apt install xfce4-terminal
~~~
ターミナルを入れるとコピペが簡単で作業が捗ります。
PowershellからWSLに入ってターミナルが起動することを確認しましょう。
~~~
>wsl -d Ubuntu-20.04
xfce4-terminal &
~~~

## imguiのビルド
作業用に適当なディレクトリを作ってclone
~~~
mkdir imguitest
cd imguitest
git clone https://github.com/ocornut/imgui.git
~~~
バックエンドはSDL+OpenGLが無難です。サンプルをビルドするには
~~~
cd examples/example_sdl_opengl3/
sudo apt install libsdl2-dev
make
~~~
で実行ファイル`example_sdl_opengl3`ができます
CMakeを使いたい場合は
~~~cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(sample VERSION 0.0.0)

#CMP0072: prefer libOpenGL.so/libGLX.so to libGL.so
cmake_policy(SET CMP0072 NEW)

find_package(OpenGL REQUIRED)
find_package(SDL2 REQUIRED)
set(IMGUI_SOURCE_DIR ${CMAKE_SOURCE_DIR}/imgui)
set(IMGUI_BACKENDS_DIR ${IMGUI_SOURCE_DIR}/backends)
set(IMGUI_LIBS_DIR ${IMGUI_SOURCE_DIR}/examples/libs)

set(SOURCE_FILES
    ${IMGUI_SOURCE_DIR}/examples/example_sdl_opengl3/main.cpp
    ${IMGUI_SOURCE_DIR}/imgui_demo.cpp
    ${IMGUI_SOURCE_DIR}/imgui_draw.cpp
    ${IMGUI_SOURCE_DIR}/imgui_tables.cpp
    ${IMGUI_SOURCE_DIR}/imgui.cpp
    ${IMGUI_SOURCE_DIR}/imgui_widgets.cpp
    ${IMGUI_BACKENDS_DIR}/imgui_impl_opengl3.cpp
    ${IMGUI_BACKENDS_DIR}/imgui_impl_sdl.cpp
)

add_executable(sample ${SOURCE_FILES})

target_include_directories(sample PRIVATE
    ${IMGUI_SOURCE_DIR}
    ${IMGUI_BACKENDS_DIR}
    ${SDL2_INCLUDE_DIRS}
)

target_link_libraries(sample
    OpenGL::GL
    ${SDL2_LIBRARIES}
    ${CMAKE_DL_LIBS}
)
~~~
を作業用ディレクトリ直下に置いてビルドすればOK
実行するとこんな感じでウィンドウが出るはず。
![example_sdl_opengl3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609356/765d3814-a63c-7247-04c8-c3dc3043b87b.png)


# 4Kの悩み
## 問題
筆者は4KのディスプレイをWindowsの機能でUIを150%拡大して使用しています。
![4K](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609356/cfad7160-6b3d-2616-aeee-2ff70ee13e2e.png)
この環境だと先程のサンプルプログラムは2倍に拡大されて半分の解像度で画面に出力され、ベクターにはジャギが出て、TTFフォントは滲んでしまいます。原因はWin側の拡大縮小機能をWSL側にも自動で適用する仕組みが、WSL側のスクリーンの論理寸法を半分に縮小→Win側で拡大して表示というやり方で実装されているため。確かに、こうすれば拡大表示は可能ですが…
しかしそれでもgeditやxfce4-terminalはちゃんと4Kの解像度でレンダリングされ、文字もくっきり見やすく表示されます。仕組みを調べるとWSLgはリモートデスクトップとして実装されていて、WSL側からWin側にWaylandプロトコルを使ってGUIを転送しているのですが、このプロトコルには転送する際に[論理寸法のn倍のフレームバッファを送る機能](https://wayland-book.com/surfaces-in-depth/hidpi.html)がある模様。先程のサンプルプログラムではWaylandプロトコルではなく古いX11のプロトコルで伝送しているため、半分の解像度で表示されてしまったのでした。

## Wayland化
先程のプログラムをWaylandで動かすのは簡単で、SDLのバージョンが2.0.10以上ならば[HiDPIに対応している](https://www.phoronix.com/scan.php?page=news_item&px=SDL2-Wayland-HiDPI-Support)ので環境変数を弄るだけでそのままWaylandで起動します
~~~
export SDL_VIDEODRIVER=wayland
~~~
![wayland](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609356/95bd8ac4-71a0-5e1d-b404-42f2cba98d7f.png)
4Kの解像度でレンダリングされるようになりました。しかし、ウィンドウタイトルと外枠が消えてしまいました、このままではウィンドウを動かすことも出来ません。

## libdecor導入
SDL2.0.16からSDLに[libdecor](https://gitlab.gnome.org/jadahl/libdecor)を組み込むことでタイトルバーを付けられるように[なりました。](https://github.com/libsdl-org/SDL/pull/4068)
libdecorのパッケージは2022.3.30現在、Ubuntu 20.04のスイートに[入っていない](https://packages.ubuntu.com/search?suite=all&section=all&arch=any&keywords=libdecor&searchon=names)のでlibdecorをビルドして~~最新の~~SDL2.0.20をビルドする必要があります。
~~~
sudo apt remove --purge libsdl2-dev
mkdir sdl
cd sdl
curl https://www.libsdl.org/release/SDL2-2.0.20.tar.gz -O
tar -xzvf SDL2-2.0.20.tar.gz
~~~
`docs/README-linux.md`を参考に依存するライブラリを導入
~~~
sudo apt-get install build-essential git make cmake autoconf automake libtool pkg-config libasound2-dev libpulse-dev libaudio-dev libjack-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxinerama-dev libxxf86vm-dev libxss-dev libgl1-mesa-dev libdbus-1-dev libudev-dev libgles2-mesa-dev libegl1-mesa-dev libibus-1.0-dev fcitx-libs-dev libsamplerate0-dev libsndio-dev libwayland-dev libxkbcommon-dev libdrm-dev libgbm-dev
~~~

先にlibdecorをビルドしてインストールします。
~~~
sudo apt install meson libwayland-dev wayland-protocols libpango1.0-dev libdbus-1-dev libegl-dev libopengl-dev libxkbcommon-dev
git clone https://gitlab.gnome.org/jadahl/libdecor.git
cd libdecor
meson build --buildtype release
sudo meson install -C build
~~~
この手順で正しいのか正直不明です。SDLのビルドは通ったのにタイトルバーが付かず、WSLを一度シャットダウンしたらタイトルバーが付くようになったりしました。README.mdに沿ってlibdecorのデモもビルドして動作確認するのも良いでしょう。
libdecorをインストールしたらSDLをビルドします。
~~~
cd SDL2-2.0.20
mkdir build
cd build
cmake ../
make
sudo make install
~~~
cmakeがlibdecorを検出できているどうかに注意してください。
先程のサンプルプログラムを再ビルドして実行すると
![libdercor](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609356/9da9da61-1fb2-7011-bf68-764dfe1f6e09.png)
タイトルバーが付いていれば成功です。どうもライブラリのキャッシュとかがあるっぽいので上手くいかないときはWSLを再起動してみてください。

# HiDPIなフォントにする
デフォルトのビットマップなフォントのままだとHiDPIの恩恵が少ないので適当なTTFに変えると良いでしょう。
~~~C++
    ImFont* font = io.Fonts->AddFontFromFileTTF("/mnt/c/Windows/Fonts/arial.ttf", 24.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
    IM_ASSERT(font != nullptr);
    io.FontGlobalScale = 0.5f;
~~~
大きめのフォントサイズで読み込んで`io.FontGlobalScale = 0.5f;`で縮小すると綺麗に表示されます。(元から半分のサイズで読み込んで表示だとぼやけてしまう)
![ss5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609356/cdf7cfd1-7f78-8dda-0ee1-89737dfb8339.png)
毎フレーム全部描画しなおしてるので重いし電気を食う点に目を瞑れば、.NET等と比べても遜色ないGUIが得られました。

# 追記(2022/4/9)
## 環境
Windows 11 Insider Preview Build 22593
WSL 0.58.0.0
WSLg 1.0.32
Ubuntu 20.04
NVIDIA Driver 512.15

自分の環境だけかもしれないですが、WSLのアップデートでScalingの挙動が変わったらしく、Windows側で150%のScalingだとWSLg側は100%のScalingで表示されるようになりました。字が小さすぎて読めない。
`c:\Users\[user name]\.wslgconfig` (.wslconfigではないので注意)に
~~~
[system-distro-env]
;hi-dpi
WESTON_RDP_DISABLE_HI_DPI_SCALING=false
WESTON_RDP_DISABLE_FRACTIONAL_HI_DPI_SCALING=false
;100 to 500
WESTON_RDP_DEBUG_DESKTOP_SCALING_FACTOR=200
~~~
と書いて`wsl --shutdown`で元の挙動に戻りました。

# 参考にしたページなど
[Dear ImGui CMake対応 | いろいろやるブログ](https://iroiroyaru.netlify.app/2021-05-30_imgui_cmake-sample/)
[Using SDL2 with CMake | Trenki’s Dev Blog](https://trenki2.github.io/blog/2017/06/02/using-sdl2-with-cmake/)
[Primitive rendering on High DPI/Retina · Issue #1786 · ocornut/imgui · GitHub](https://github.com/ocornut/imgui/issues/1786)