SDLがDOSをサポートするように
(github.com/libsdl-org)- DJGPPベースのDOSポートがSDLに追加され、ビデオ・オーディオ・入力・スレッディング・タイマー・ファイルシステム・ビルドチェーンまで幅広く備えた状態でmainにマージされた
- VGAとVESA 1.2+ビデオ、Sound Blaster系オーディオ再生、PS/2キーボードとマウス、BIOSベースのジョイスティック、協調型スレッディングとPITタイマーまで、DOS環境に合わせた実装が入った
- DJGPPのseek/read問題は、seek時にバッファをダンプして復元する方式で回避し、
SDL_LoadWAVの誤読込みと数秒の遅延を解消、オーディオ経路もIRQ再入とstutterを減らす方向に調整された - fullscreenの黒画面はINDEX8モード選択に結びついており、DOS専用hintを置く代わりにモード順を論理的に並べ替える方向に調整され、カーソル透明度の問題も追加コミットで修正された
- 実機テストは限定的で、音声録音、
SDL_LoadObject、SDL_TIMEのDOS専用実装など一部機能は未実装だが、46件のチェックを通過してマージされ、3.6.0機能として扱われる
DOSプラットフォームのサポート範囲
- SDLにDOSポーティングが追加され、DJGPPベースで比較的完成度の高い状態に到達した
- 作業は複数人で分担して進められ、最終段階で安定性修正と不足機能の補完が加わった
- DevilutionXをDOSBoxで広範囲にテストしたが、実機テストはない
- テスト方法が適切でない機能は、意図的に除外された部分もある
- サポート機能が具体的に整理されている
- ビデオはVGAとVESA 1.2+フレームバッファ、RGBと8ビットインデックスカラー、VGA DACパレットプログラミング、vsyncとハードウェアページフリッピング、終了時のVBE状態保存と復元を含む
- オーディオはSound Blaster 16、Sound Blaster Pro、Sound Blaster 2.0/1.xをサポートし、IRQベースDMAとダブルバッファauto-init経路を使う
- 入力は拡張スキャンコードを含むPS/2キーボード、INT 33hマウス、BIOS INT 15hベースのgameportジョイスティックを含む
- スレッディングは
setjmp/longjmpとスタックパッチングを使った協調型スケジューラを用い、mutex、semaphore、TLS、condition variableの一般的なfallbackも備える - イベントポンプとdelay関数にyieldポイントを入れて、オーディオと他スレッドの応答性を維持する
- タイマーはDJGPPの
uclock()を使ったPITベース実装で、およそ1.19 MHzの分解能を持つ - ファイルシステムは
GetBasePathとGetPrefPathをDJGPPのsearchpath()で処理し、POSIXファイルシステム操作のfallbackも含む - ビルドはCMakeクロスコンパイルツールチェーンファイル、DJGPP CIジョブ、高速configureのためのpreseed cacheを含む
- 含まれていない機能も明確に記されている
- 音声録音は未対応で、再生のみサポートする
SDL_TIMEのネイティブ実装はなく、DJGPPのPOSIXレイヤー経由でUnixgettimeofdayを再利用する- 共有ライブラリ読み込みは未対応のため
SDL_LoadObjectはない
実装過程と主要な変更
- DOSポートは複数のコミットを経て、ビデオ、オーディオ、入力、タイマー、スレッディング、ビルドチェーンが段階的に追加された
- 初期作業の後、stdioバッファリング、オーディオ実装、ビデオサブシステム、ファイルシステム、マウス、キーボード、CI、プラットフォーム検出まで順に補完された
- stdioとファイルアクセスではDJGPP特有のseek/read問題を回避した
- stdio SDL_IOStreamsのバッファを無効化する試みもあったが、その後seek時にバッファをダンプして復元する方式へ変わった
- この変更はバッファを完全に無効化するよりもはるかに高速で、
fseek後に誤った読み込みが出る問題も避けられる SDL_LoadWAVがtest/sample.wavを読む際に数秒かかっていた現象が消え、正しいデータが入るようになった
- オーディオ処理方式も安定性重視で継続的に調整された
- Sound Blaster 16実装から始まり、その後pre-SB16 8ビット monoとSB Pro stereoサポートが追加された
- オーディオミキシングはIRQハンドラで直接回していた方式からメインループを経由し、再びSDLオーディオスレッド側へ移された
- IRQ内で再入問題が起きないよう方向転換し、ロード中は古いDMAバッファ半分をsilence処理してstutterを減らした
- サンプルレート調整、DSP状態polling、DMAメモリ割り当ての根拠、割り込みベクタ復元後のIRET wrapper解除まで整理された
- ビデオもDOS環境の制約に合わせて細部機能が拡張された
- VESAインターフェースでソフトウェアレンダラと動作する初期実装が入った
- その後、8ビットパレットサポート、VBEページフリッピング、状態復元、単一バッファモードvsync、VESA banked framebufferモードが追加された
- VBE 1.2+でLFBがない場合はbank switchingでフレームバッファをコピーし、このモードではページフリッピングを無効化する
- ビデオ初期化と終了時にVBE状態全体を保存・復元して、モード切り替えをきれいに処理する
- 入力と割り込み処理も実用レベルまで磨かれた
- キーボードは拡張スキャンコードとPauseキーまで扱い、イベント保存には単純なring bufferを使う
- マウスはINT 33h function
0x1Bで感度を取得して使う - ジョイスティックはBIOS timing loopのコストを減らすため軸pollingを約60 Hzに制限し、ボタンは応答性のため常に直接pollingする
- ISRコードとデータをロックして、割り込み中のページフォルトを防ぐ
- ビルドとプラットフォーム検出の問題も修正された
- DJGPP向けCI jobが追加され、DOS向けCMakeプラットフォーム検出が入った
SDL_PLATFORM_DOSをSDL_RunApp()除外リストに入れて、DOS専用ランチャーと衝突しないようにした- DJGPPが
__unix__を定義していてもDOSにはGTKやdisplay serverがないため、SDL_Gtk_Quit()をDOSから除外してリンクエラーを防いだ - 一部DJGPPツールチェーンが
i386-pc-msdosdjgpp-gccプレフィックスを使う点まで反映された
テスト状況と残っていた制約
- 自動テストはすべてが完全にスムーズだったわけではないが、パッチで完走可能な水準には到達した
- 標準formatting関数の一部問題のため自動テストが最後まで進まなかったが、これを回避するパッチでテスト自体は完了した
- なお失敗するケースはまだ残っていたが、そのformatting関数をどう直すのが正しいか確信がなく、含めなかった
- ほとんどのデモはおおむね期待できるレベルで動作すると整理されている
- 実際の使用結果も追加された
- DOSBoxとDOS 6.22 on a Vortex86 boardでは、非fullscreenウィンドウがテストプログラムでもユーザーコードでも問題なく動作した
- ただしfullscreenは当時の時点で
draw.exe --fullscreenのような例で黒い空画面が出た - 速度は遅いが実用可能な水準とされている
- 一部テストプログラムの問題も確認された
sprite.exeはDOSBoxで即時終了するwm.exeとdraw.exeはレンダリングされないが、ESCを押すと終了する状態だったtestpaletteではsegfaultが発生する
fullscreen色深度選択の問題と調整
- fullscreen黒画面の直接原因は**
SDL_GetClosestFullscreenDisplayMode()がINDEX8モードに落ちる挙動**だった- アプリケーションがINDEX8レンダリングを処理できないと画面が黒くなる
- 内部実装では、ユーザーがすでにINDEX8 fullscreenモードを設定している場合にのみINDEX8を考慮するよう扱った
- 当初はDOSでのみ**
SDL_HINT_DOS_ALLOW_INDEX8_MODES**の背後にINDEX8モードを隠す対応が入った- アプリケーションが正しく処理できるときだけ明示的にopt-inさせる狙いだった
- しかしmainブランチには最低bit depthではなく最高bit depthを選ぶ変更がすでに入っており、それで解決するか確認する流れになった
- この変更はbpp問題は解決したが、640x480 best fit要求で1024x768が選ばれる新たな問題を生んだ
- その変更は最終的にrevertされ、よりよい修正は別PRで扱う方向に整理された
- 最終的にDOS専用hintは削除され、モード順を論理的に並べる方向へ変わった
Remove SDL_HINT_DOS_ALLOW_INDEX8_MODES and order modes logicallyコミットが追加された- #15442がマージされれば、パレットを設定しない、またはfullscreenモードを直接選ばないアプリでも自動モード選択が正しく動く可能性があると整理された
- 既存SDLの他問題までこのPRで際限なく掘り下げるつもりはない、という線引きも示された
デモレンダリングとカーソル修正
- デモが黒く見える問題はこのPR単独ではなく、**#15442**のマージ状態に左右された
SDL_GetClosestFullscreenDisplayMode()が最も低い色深度を優先する間は、このポートでINDEX8が選ばれ、アプリがパレットを設定しないと黒画面になる- そのPRをローカルにマージすると、テスト対象がすべて動いたことが確認された
- 残る視覚的問題はカーソル透明度だった
- ローカル確認後、カーソルだけ透明にならないことが追加で判明した
- これを直すために
Don't convert cursor if dest is not INDEX8コミットが入った - 修正後はRGBモードで最適化されていない版を使うが、RGBとINDEX8の両方で正しく動くようになった
- XRGB1555とRGB565ではわずかに性能向上の余地が残るが、優先度は低いとされた
レビューとマージ判断
- PRはsquash mergeが適切だと判断された
- GitHubが結果コミットにPR参照を残すため、作業過程も追跡可能と見なされた
- 結果コミットには追加の功績表記を入れようという提案も記されている
Co-authored-by2名とTested-by1名を追加する具体的文言も含まれていた
- レビュアーは、マージ後に一部ビルドスクリプト表現をよりきれいに書き直すと残している
- 最終マージ直前には、他の関連PRも合わせてマージしつつDOS PRも進める方向に整理された
- 「Last call on DOS pull request!」の後、関連PRのマージ状況が確認された
- 続いて46 checks passedの状態でmainへマージされた
- マージ後のリリース範囲も明確に整理された
- この変更は3.6.0 featureと見なされる
-
3.4.xへはcherry-pickしない
- マージ翌日にDOS作業ブランチは削除された
後続課題と残るハードウェア問題
- 一部Nvidia GPUで
4f07(SetDisplayStart)実装が正しく動作しない問題も提起された- VOGONSスレッドへのリンクとともに、報告上は対応となっていても実際には動かないGPUがあると記されている
- テスト範囲はGeForce 9300から3060までさらに広い可能性があるが、保有機材ベースなので完全な範囲ではない
- プロジェクト側のテストでも同じ表示問題が観測された
- GPUベンダー基準でSDLが機能を無効化する方式は望ましくないとされ、ユーザー制御用hintの可能性も言及された
page_flip_availableをユーザーが直接制御する新しいhintの方がよいかもしれない、という方向性が示された- すぐには対応せず、後で追加できるという形で締めくくられた
- マージ後には直接フレームバッファ利用hintにも触れられている
SDL_HINT_DOS_ALLOW_DIRECT_FRAMEBUFFERを有効にすると、上記のSetDisplayStart問題はそれほど重要でなくなる可能性があると記されている
1件のコメント
Hacker Newsのコメント
これで UEFI向けSDL さえあれば、ゲームを OS起動前環境 でも動かせるようになる
セキュリティがより堅牢になったのか、それとも今でもアクセス可能なのかも気になる https://www.zdnet.com/article/minix-intels-hidden-in-chip-operating-system/
ただ、UEFIには サウンドドライバ がないと理解しているし、最近はオーディオコーデックチップですら NDA限定のデータシート しかなかったりして、自前で書くのも厄介
さらにひどいのは、graphics output protocolに vsync情報 がないせいでティアリングなしのblittingができないことで、文字どおりVGA以下
grubみたいなメニューで起動したら レトロゲームの一覧 がずらっと出てくると思うとかなりワクワクする
このスクリーンショットが特に面白いのは、DosBOX 自体が SDLの上で 作られていること
これは DJGPP を使っていて、DPMIでCPUを 32ビットモード に切り替えている
なので、segmented memoryやnear pointer、あらゆる 64KB制限 が飛び交っていた本当のオールドスクール感は味わえない
すばらしい
SDLバインディングをサポートする FreeBASIC の386+向け MS-DOS実行ファイル と一緒に使うとどうなるのか気になる
[1] - https://github.com/freebasic/fbc
もともとDOS由来の OHRRPGCE をまたDOSへ移植しようと何年も考えていた
それに、SDL 1.2時代に存在したほとんどすべての移植先やOSサポートをSDLがかなり積極的に切り捨てていたことを思うと、SDL3がDOSサポートを取り戻した のはかなり驚き
完璧
今朝ちょうど macOSの中のVMware Fusionの中のDebian GNU/Linuxの中のDOSBox-Xの中のTurbo C で開発していたので、まさにぴったりのニュース
何十年も前にturbocで作業していた記憶がうっすらある
厳密に言えば、これはすでに HXDOS で可能だった
DirectDrawをSDLが使える程度には十分うまくエミュレートしていたから
だとすると、どの SDLターゲット でコンパイルするのか気になる
Win32専用フルスクリーンなのか、それとも640x480みたいな VESA解像度 なのか知りたい
SDLのような オープンソースプロジェクト でこうしたサポートが入るかどうかは、たいてい変更がどれだけ侵襲的か、そして貢献者が実際に 保守まで続けるか にかかっている
プロジェクトごとに方針は違うしSDLの方針は知らないが、すでに移植先が多いのだから、自分たちが何を引き受けるのか理解したうえでやっているのだと思う
私のお気に入りの例はopenbsd luna88k https://www.openbsd.org/luna88k.html
実際のユーザーがどれくらいいるのかはまったく分からない。たぶん日本でしか出なかったかなり珍しいマシンだった気がするし、ユーザーがいるとしても大半は日本だろうが、自分の視界には入ってこないので、体感ではユーザーはポーター一人だけ
それでも毎リリースごとに、まるで森の中からひょっこり現れるみたいに、正式公開日から数週間遅れて luna88kのファイルとパッケージ を上げてくれる
実機のluna88kでコンパイルしているせいで時間がかかるのかもしれないし、とにかくそれだけで OpenBSDの公式ハードウェアプラットフォーム として維持されるには十分
自分自身はluna88kが欲しいわけではないけれど、あれを動き続けるようにしているその人には本当に敬意を抱く
SDLがまた Loki時代のルーツ に戻っていく感じがする
いいね
なぜかって? 理由は分からないけど、クールならそれで十分
Internet Archiveを見れば分かるように、キーボードとマウスさえあれば90年代半ばの AAA級の複雑さ くらいまでは事実上どこでも動かせる
Tomb Raider、Command & Conquer、Quakeのようなゲームがそうで、とにかくちゃんと動くプラットフォーム が欲しいならかなり魅力的
そこにSDLまで加わればずっと簡単になる
本当にうれしい
これは自分をとても幸せにしてくれる