- SDL3の新しい GPU API の PR #9312 が 2024年8月29日にマージされ、SDL 3.0 が近づく中でより多くのレビューを受けるため、Refresh ベースのアプローチを迅速に持ち込む流れで進められた
- 提案は MoonWorks のグラフィックスコンポーネントである Refresh を SDL_gpu の最終 API 候補とするもので、Vulkan と PS5 グラフィックス API をサポートし、D3D11 deferred context のサポートも進行中だった
- API 設計は render pass、compute pass、copy pass に作業を分けるモダンなレンダリングモデルを採用し、リソース書き込み操作はフレーム間依存を避けるため内部的に cycle できるよう構成された
- シェーダー面では、初期の オフラインコンパイル 中心の提案から、ランタイムでのシェーダー生成サポートを検証する方向へ調整され、SDL 自体がシェーダーコンパイラをラップしないよう、バックエンド別の IR フォーマットを受け取る構造が議論された
- マージ直後には API 関数名、ビルド設定マクロ、UTF-8 BOM、D3D12 swapchain の 60 FPS 制限といった後続調整が続き、thatcosmonaut にコミット権限が追加された
PRの出発点とマージ状況
- PR #9312 は SDL_gpu を素早くレビューしてもらうために出された新しい GPU API 提案として始まった
- SDL 3.0 が近づく中で「より多くの目」を早く集めることが目的だった
- 2024年8月29日に
It's merged という確認の後、マージされた
- マージ直後には、しばらく変更を保留してレビューと調整を進めようという要請があった
- その後
everything is merged 状態となり、GPU 側の変更を再びマージしてよいという案内が出た
- thatcosmonaut は incoming changes をレビューする可能性が高いという理由で、コミット権限に追加された
RefreshベースのAPI候補
- 提案の中心は MoonWorks のグラフィックスコンポーネントである Refresh を SDL_gpu の最終 API 候補にすることだった
- MoonWorks は FNA のように XNA を再実装するのではなく、XNA の後継に近いプロジェクトとして紹介された
- Refresh は FNA3D に似ているが、Vulkan のようなモダン API を対象としている
- 当時の Refresh は Vulkan と PS5 グラフィックス API をサポートしていた
- D3D11 deferred context のサポートは進行中だった
- PC とコンソール版の Samurai Gunn 2 で本番利用中だと紹介された
- PR の作成者は thatcosmonaut を主要な連絡担当とし、FNA core team も進行に関与する予定だと明かした
API設計とリソース処理
- API は deferred context を中心にしたモダンなレンダリング API として構成された
- 作業は render pass、compute pass、copy pass に分かれる
- それ以外の API は binding、render、compute dispatch 呼び出しに近い標準的な形だと説明された
- リソースへのすべての書き込み操作は cycle によってフレーム間依存を避けられる
GpuBuffers のようなグラフィックスリソースハンドルは、内部リソース参照を cycle するためのコンテナ役を担う
- その後 cycle の概念は bool に単純化され、複数の
WriteOptions enum は削除された
- 初期には AMD の D3D11 ドライバに関連するデータ API の動作問題のため、複数の
WriteOptions enum が存在していた
- D3D11 のデータ API が AMD で期待どおりに動作しない点を完全には回避できなかった、という説明があった
- その後この部分は単純化され、動作方式がコード内に文書化された
シェーダーシステムの議論
- 初期のシェーダー解法は
shaderbuild.py というスクリプトだった
- クライアントマシン上のオフラインシェーダービルドツールのフロントエンド役を果たす
- 複数フォーマットをまとめて各レンダーバックエンドに合わせて渡す構造だった
- この方式は オンラインシェーダーコンパイル を妨げない設計だと説明された
- 将来的には SDLSL ソースをバイナリに含め、
CreateShaderModule で必要なバックエンドのバイトコードへその場で変換できる、という説明があった
- 公開 API を壊さずにオンラインコンパイルを許容できる点が利点として示された
- 外部フィードバックでは、純粋なオフラインコンパイルは特定のエンジンと合わないという懸念が出た
- Python、
glslc、spirv-cross を PATH に入れておく必要がある点が、開発者には煩雑かもしれないと指摘された
- SDL エコシステムでは、別個の satellite library 形式のオフラインシェーダーコンパイルツールのほうが自然かもしれない、という意見も出た
- その後シェーダーシステムは “raw” シェーダーと “portable” シェーダーを区別する方向へ調整された
- FNA3D ポートをランタイムシェーダー生成サポートのストレステストとして利用した
- MojoShader SPIR-V emitter を使い、Vulkan では直接渡し、他のバックエンドでは任意の別ライブラリである SDL_shader で変換する構成が言及された
- SDL 自体はシェーダーがどこから来たかを知らず、気にもしない構造を目指している
バックエンドとテストの進行
- 初期の Refresh ベース実装は、Vulkan バックエンドがすでに存在する点が強みと評価された
- Vulkan バックエンドは Refresh 2.0 時点で高性能だという意見があった
- compute が第一級の機能である点も、従来の SDL_GPU 草案に対する重要な違いとして挙げられた
- 2024年3月末から4月初めにかけて、複数の実ゲーム動作例が共有された
- オフラインシェーダーコンパイルなしで動作する状態に達したと説明された
- Streets of Rage 4 が起動する current revision が共有された
- Wizorb が Metal で動作した
- Celeste が trace database のかなりの部分で in-game 状態に入った
- 機能追加も並行して進んだ
- ハードウェア instancing サポートが次の push に追加予定だと言及された
- その後 instancing と occlusion queries が入り、Metal 側の実装を埋める必要があるという状態が共有された
レビューで出たビルド・API・プラットフォームの問題
- Vulkan の include 順序のため、
VkInstance、VkSurfaceKHR typedef 再定義エラーが発生した
SDL_gpu_vulkan.c で Vulkan ヘッダと SDL_vulkan.h の include 順序を入れ替える修正案が提示された
suitableQueueFamilyIndex が未初期化の可能性があるという警告を 0 初期化で抑える修正案も提示された
- dynapi の更新必要性も提起された
src/dynapi/ 配下で gendynapi.py を実行すれば更新されるという案内があった
- 実行時にドキュメント不足警告が大量に出るという caveat もあった
- API 関数シグネチャの byte size 型を
size_t に変える提案もあったが、GPU API では Uint32 を維持するという結論になった
- バッファサイズが非常に大きくなると 32/64 ビット同時互換性が難しくなる可能性があるという意見があった
- 代案として
Uint64 の可能性も言及されたが、最終的には Uint32 維持の意見が示された
マージ後の後続調整
- マージ後、#10622 で複数の tweak が進められた
- 新しい関数名をベータユーザーが受け取れるよう、先にマージするという案内があった
- ビルド設定で
SDL_GPU_VULKAN SDL_VIDEO_VULKAN、SDL_GPU_METAL SDL_VIDEO_METAL のように右側のマクロが定義されている必要がある問題は #10622 で修正された
- GPU ソースファイルの UTF-8 BOM 問題も #10622 で修正された
- D3D12 側では
D3D12_ClaimWindow() が swapchain を VSYNC に設定し、testsprite が 60 FPS に制限される現象が確認された
- claim window 段階で SDR と VSYNC という一般的サポート用パラメータで swapchain を作成し、その後サポート可否を問い合わせて
SetSwapchainParameters を呼ぶワークフローが意図した動作だと説明された
- render driver が claim 後に
SetSwapchainParameters を呼んでいるにもかかわらず、D3D12 driver では 60 FPS 制限が残っており、調査対象となった
1件のコメント
Hacker Newsのコメント
SDL3はまだプレビュー段階だが、新しいGPU APIがメインブランチにマージされ、SDL3のメンテナーたちが最後の調整を進めているところ
私の理解では、この新しいGPU APIの核心は、グラフィックスコードとシェーダーを一度書けば、コンソールを含む複数のプラットフォームで大きな手間なく動作させられる点にある。以前はUnityやUnreal、あるいは独自のカスタム解決策が必要だった
WebGPU/WGSLも似たようなクロスプラットフォームのグラフィックススタックだが、知る限りコンソール向けバックエンドを作った人はいない。逆にSDL3 GPU APIは、現時点ではWebGPUをバックエンドとしてサポートしていないようだ
SDL3はコンソールの抽象化を導入し、GPU APIに追加の依存関係が不要になるので期待している。さらにGodotはSteam Deckを公式サポートしており、今後さらに多くのコンソールにも対応してほしい。関連してMiguel de IcazaがGodotへのSwift導入を推進していて、iPad上でエディタをSwiftUIへ移植する作業もしている。進捗 [3] が興味深い
[1] https://bkaradzic.github.io/bgfx/overview.html
[2] https://github.com/bgbernovici/myndsmith
[3] https://blog.la-terminal.net/xogot-code-editing/
追加の背景はこちらにある: https://icculus.org/finger/flibitijibibo?date=2024-06-15&time=13-14-16
この流れがどのように定着するのか楽しみ。最終的には、カスタムゲームエンジンやアプリを作るための選択肢が増えるとよい
最近Vulkanを深く掘り下げているが、学ぶのは楽しく気づきも多い一方で、Vulkanの性質上、進捗は遅く感じる。始める時点でSDL3があったなら、おそらく喜んでそちらを選んでいただろうし、投資した時間に対して見せられる成果ももっと多かったと思う
このAPIが実際に使い物になるかは、時間がたってみないと分からない。特にリソース同期とオブジェクト名の変更方法が鍵になる
WebGPUや他の抽象化より性能が良いかどうかも様子見だ。ドライバのバグを回避しなければならない状況でも、小さく保ち続けられるかも見守る必要がある
シェーディング言語向けの新しいバイトコードにも懐疑的だ。WebGPUではランタイムのシェーダーパーシングは懸念事項ではなく、非常に高速だった。ネイティブシェーダーの生成も速い [1]。遅いのはパイプライン生成であり、このバイトコードはそこには役立たない
[1] http://kvark.github.io/naga/shader/2022/02/17/shader-translation-benchmark.html
どうやってこんなに早く成し遂げたのか気になる。WebGPU ネイティブは開発期間が長く、いまだに最終確定していないのに、SDL GPU API はより多くのプラットフォームをサポートするので、むしろもっと時間がかかりそうに見えた
クロスプラットフォームのシェーディング言語向けの姉妹プロジェクト [1] と、既存言語同士を相互変換する別のプロジェクト [2] はあるが、それらは完成したら完成したというだけで、API の残りの部分がそれを待つ必要はない
WebGPU はベンダーや言語ローヤー、あるいは標準化ローヤーたちで構成された委員会が、政治と官僚主義の中で作った成果物であり、それが見て取れる。SDL_GPU は何より実用性を重視するゲーム開発者たちが作ったもので、そのため象牙の塔から見下されることも多い
[1]: https://github.com/libsdl-org/SDL_shader_tools
[2]: https://github.com/flibitijibibo/SDL_gpu_shadercross
dx12 部分に貢献できてうれしい :)
一度使ってみるかもしれない。SDL は品質の高いソフトウェアだと感じてきた。コンパイルが速く、複数のプラットフォームで簡単にコンパイルでき、いつもきちんと動く。だからこの新しい API にも期待している
全体的に SDL の大ファン
クロスプラットフォームのゲームライブラリを探したとき、SDL とその API はちょうどよいバランスに感じられた。ウィンドウとグラフィックスコンテキストを作るために呼べる C/C++ ライブラリ、そして高速なスプライトレンダリングフレームワークだけが欲しかった。丸ごとの IDE や肥大化したライブラリは必要なかったし、新しい言語を学びたくもなかった
SDL3 は セカンドシステム効果 に陥っているように感じる。SDL2 は明示的なウィンドウハンドルが付いた SDL1 に近かったので、SDL3 は 3 番目ではなく 2 番目のシステムだ。SDL1/2 は、ウィンドウを開いて入力イベントを処理するプラットフォーム別のボイラープレートを包む薄いレイヤーで、実際に書きたい OpenGL レンダリングコードへ素早く入れるようにしてくれた
しかし Apple の OS までサポートするなら OpenGL 4.1 に縛られる。Apple が 5 年前に正式に非推奨予定とした状態なので、コンピュートシェーダーのような現代的な GPU 機能は使えない
Vulkan ルートに進み、Apple システムでは MoltenVK を使うこともできるが、Vulkan は OpenGL よりかなり複雑になる。よく言われるように「三角形 1 つにコード 1000 行」レベルだ。SDL3 の GPU API の目標は、より近づきやすく、それでいて十分に柔軟な代替を提供すること
コンソールもおそらく似たような話だろう
多くの人が「SDL_render のようなものに、すべてのプラットフォームで動作するシェーダー対応を追加できないか」と要望し、それが出発点だったという
SDL3 はより高レベルなオーディオ API も追加するが、その利点はよく分からない
また SDL2 は 2.0.0 以降かなり進化しており、SDL3 は API 互換性を壊す変更を許容しながらその進化を引き継いでいる。SDL3 は一から書き直されたものではなく、SDL ユーザーの立場では SDL2 から SDL3 へ移行するのはそれほど難しくないと思う
そして SDL1/2 も、独自の高レベルなグラフィックスシステムがないほど「薄いだけ」だったことはない。新規ユーザーや基本的なユーザーがすぐ画面に何かを表示できるよう、標準で備わっているのは有用だ
ahefner が指摘したように、SDL1 は現代の基準ではかなり「薄かった」が、ピクセル演算を自分で書かなくても画面に基本的なものを描ける程度のものは提供しており、90 年代にはそれがかなり役に立った
それでもこの抽象化は、単純な 2D ゲームを超える要求を満たしつつ、残念ながらますます断片化していくグラフィックス API エコシステムを対象にできる可能性がある。普遍的な OpenGL(Next) の未来という夢は消えた。ただし最も難しい部分である シェーダー変換 は、まだ存在しないように見える
このライブラリを使ったことはないが、リンク先のスレッドからの理解が正しければ、今回提供される クロスプラットフォーム GPU コンピュート 機能の例を見てみたい。どこから始めるのがよいか、おすすめがあるか気になる