Software render で OpenGL なしに GUI + 画像を描画する UI ライブラリ(C/C++)のメモ

## 背景

* レイトレやビジョン系機械学習(顔認識とか)で画像を出す + GUI がほしい. C/C++ で記述されたもの.
* ピクセルデータ(配列)から画像データが表示できること
* cross platform(Linux, Windows, macOS)対応
* X11 で画面を飛ばして描画したいなどで, OpenGL を使わないようにしたい
  *  x11vnc とかだと画面全体を転送で遅い(=> 10GbE にすればある程度解決するかもだが?)
* ミニマルな構成 and 環境整備が楽であることが理想
  * いろいろ開発マシンを変えて開発したり, 各人の PC でも問題なくコンパイルできるように, すぐに環境整えられる or git sumodule で追加できるのが理想 
  * cmake, meson でサクッと追加できる

## 候補

クロスプラットフォームで Software 描画で, そこそこ機能が充実しているものとして, 4 つ候補があります.

### libui

Windows, Linux, macOS で各ネイティブの UI を使う. ボタンとかは出すのは簡単だが, 画像を出すには Table widget を経由して作る必要がありめんどい. 

https://github.com/andlabs/libui/blob/master/ui.h#L1282

Table の callback 関数に user data pointer が無いので, 基本的に画像データをグローバル変数 or グローバルなシングルトン関数経由で渡す必要があってめんどい)

画像を使うサンプルコードがほとんど無いので手探りなってしまいつらい(一応動いたが, Linux だと gtk warning が発生してしまう). カラーピッカーなどはないので自作する必要がある(=> めんどい)

あとは, バックエンド依存であるが uiArea で DrawContext を取得して画像を描画でしょうか(Linux の場合は `cairo_t`)

### imgui

みなさんおなじみ. imgui 自体はバックエンドの描画エンジンは定義していないので, バックエンドを書くことで OpenGL で描画したり DirectX で描画したりを切り替えることができます.

SDL2 software render に移植されているのがあります.

https://github.com/Tyyppi77/imgui_sdl

以下のような感じにすれば, 画像が表示できるのも確認しました.
(レンダリング画像を出す場合は, `SDL_Surface` をピクセルデータから作ればいけるはず)

```
  SDL_Surface *bmpSurf = SDL_LoadBMP("image.bmp");
  SDL_Texture *bmpTex = SDL_CreateTextureFromSurface(renderer, bmpSurf);
  SDL_FreeSurface(bmpSurf);

  SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 100, 100);
  {
    SDL_SetRenderTarget(renderer, texture);
    //SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, bmpTex, nullptr, nullptr);
    SDL_SetRenderTarget(renderer, nullptr);
  }
```

X11 での画面転送も動きました.

問題としては, imgui の構造上毎フレーム描画をすることになるので, widget が増えると CPU 消費が激しいです(使っているのは 1 コアななので, マルチコア環境ではあまり問題にはならないかもだが). 

CPU 消費がきになる場合は, `SDL_Delay(33)` で 30 fps くらいに落としたり, widget が変化しないのを app 側でトラッキングしておき, 必要な時だけ描画させる機構を実装する必要があります.

2021/10/25 追記: SDL2 2.0.18(2021 冬ごろリリース?)で SDL2 自体に Renderer(RenderGeometry)の機能が付きました.

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Dear ImGui 1.85 released!<a href="https://t.co/55za546Bv7">https://t.co/55za546Bv7</a><br>- New &quot;stack tool&quot; to identify/debug identifiers<br>- Fix SetKeyboardFocusHere() on clipped items<br>- Misc nav (keyboard/gamepad), docking, viewport fixes<br>- SDL_Renderer backend (for upcoming SDL 2.0.18)<br>- Many more things &amp; fixes <a href="https://t.co/wCJ4d5C7E8">pic.twitter.com/wCJ4d5C7E8</a></p>&mdash; Omar (@ocornut) <a href="https://twitter.com/ocornut/status/1447901057955282945?ref_src=twsrc%5Etfw">October 12, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

GPU で 2D accelerated, Software によるレンダーの両方使えます.

imgui もそれに合わせて `example_sdl_sdlrenderer` ができています.

したがっていずれは imgui + SDL2 で native に(他のライブラリなしに) software 描画ができるようになります!
   

### NanoGUI + SDL2

NanoGUI の SDL ポート

https://github.com/dalerank/nanogui-sdl

SDL software renderer でも動くのを確認しました. C++14 要求するので注意.

また, color wheel widget は描画されませんでした.

### Qt

OSS 開発であれば一番無難でしょうか. ただしインストールやコンパイルのための環境設定がめんどい.
(最近だと minimal SDK みたいなのがなくなったようで, SDK インストールだと不要なものが数ギガバイトが入ってしまう)

Windows だと minimal prebuilt があるが, linux/macOS は無い(apt で入れる手もあるが...)

cmake, meson への組み込みは以下を参考にすることで比較的楽にできると思います.

C++11 アプリに CMake or Meson 既存ビルドツールを使って Qt5 で GUI を追加するメモ
https://qiita.com/syoyo/items/a1ca89f638f9032c5035


### まとめ

用途にもよりますが, おすすめ順は以下です.

* (imgui or NanoGUI) + SDL2 software render color wheel など widget がたくさん. CPU 消費がちょっとネック(うまくキャッシュする手もあるかも?)
* libui. 描画が軽い + ネイティブの UI. widget は基本的なものはあるが, 画像を出すのがめんどい. 頂点データをテーブルで表示など, 多量に数値を表示するときには native UI 表示がいいかも?
* Qt5. 最後の選択肢 or きちんとした UI 作りたいひと向け. 環境整備がめんどい.

### 今後の期待

Flutter Desktop という手もあります. ただ, skia 自体は CPU 描画できるはずだが, Flutter Desktop は現状 GPU で Skia 描画必須っぽいようです.

native コードとの組み合わせはこちら https://qiita.com/syoyo/items/7e2c17261089e0027c30  

問題は, Flutter Desktop はまだ出始めである + C/C++ 用 GUI としての利用には, 最小の環境整備がまだ整っていない(flutter 入れると数十ギガバイト消費してしまう)

## おまけ

NanoRT で imgui を CPU ソフトウェアレイトレで描画するのもありますが, 遅いです.

https://github.com/syoyo/imgui/tree/nanort

## TODO

* Jupyter lab のようにブラウザベースも考えてみる. UI はブラウザで描画し, 画像は JSON-RCP or websocket で転送