2025/2/2(日)

ゲーム開発ライブラリの設計の整理を進めた。まず、描画周りについてこれまでrendererクラスとrender_targetクラスに分けていた。そしてrender_targetクラスにはサーフェス・スワップチェーン・イメージビューなどの管理とプレゼンテーションの機能を持たせ、rendererクラスにパイプライン・レンダーパス・コマンドバッファの管理とプレゼンテーションの呼び出しを任せていた。

しかしrendererがサーフェス・スワップチェーンだけでなくただのイメージなどを対象にレンダリングすることを考えると、必ずしもプレゼンテーションは必要ない。それが必要かどうかはrender_targetの正体がウィンドウかただのイメージかに依存する。とすると、rendererはそこに携わるべきではない。

ということで、render_targetにコマンドバッファの管理やプレゼンテーションを一任することにして、rendererrender_targetからもらったコマンドバッファにコマンドを詰め込むことだけに注力する設計にした。

  • render_targetが管理するオブジェクト
    • サーフェス
    • スワップチェーン
    • イメージビュー
    • コマンドバッファ
    • 同期用のセマフォ・フェンス
  • rendererが管理するオブジェクト
    • レンダーパス
    • シェーダモジュール
    • パイプライン・パイプラインレイアウト
    • フレームバッファ

設計をこのように変更した結果、rendererが先に始末されることでフレームバッファがコマンドバッファに先んじて始末されてバリデーションエラーが飛ぶ問題が起きた。コマンドバッファの管理はrender_target側が握っているので、rendererが始末される段ではまだコマンドバッファは生きているしフレームバッファを記録したコマンドが実行されている可能性もあるのだ。これについてはrendererのデストラクタで同期待ちを行うことで解決した。

別の問題として、画像を表すimageクラスの処遇をどうしようか少し悩んでいる。imageクラスのオブジェクトがrendererやrender_targetより先に死ぬとまだ使われている可能性がある。

正直グラフィックよりもオーディオ部の実装に早く取り掛かりたいのだが、libsoundioで上手くやる見込みがまだ立っていない。とりあえず練習用に初期化周りのプログラムだけ書いて感触を思い出しておいた。コールバックでいちいち波形データを書き込むのがだるい。複数の音を鳴らそうとすればミキサーを自前実装しなければならず、各音声の再生停止を自前で制御することになる。フィルタ類をかけようとするのも同様である。どういう設計が良いんだ。

とりあえず

  • 音声データのアセット管理的な仕組み
  • 再生停止、ミキサ
  • 簡単なフィルタ(音量調整、パン)

みたいな順で作戦を考えていきたい。

ところでlibsoundioをナイーブにFetchContentで取り込んでWindowsでビルドするとエラーが出る。これについて今まで以下のようなコードで対処していた。(去年の9/20, 10/12の日報)

if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /experimental:c11atomics")
set(CMAKE_C_STANDARD 11)
endif()
set(BUILD_EXAMPLE_PROGRAMS OFF CACHE BOOL "")
set(BUILD_TESTS OFF CACHE BOOL "")
set(BUILD_DYNAMIC_LIBS OFF CACHE BOOL "")

が、どうも改めてこれをやってみるとやっぱりエラーが出ていて、BUILD_TESTSなどが反映されていない気配がある。調べて見ると以下のページを見つけた。

https://stackoverflow.com/questions/62101576/using-fetchcontent-declare-together-with-cmake-args

https://qiita.com/mrk_21/items/186439952a6665184444

あまり詳しい原理は理解していないが、CACHE BOOLではなくCACHE INTERNALに変えることで反映されるのを確認した。つまりこうだ。

if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /experimental:c11atomics")
set(CMAKE_C_STANDARD 11)
endif()
set(BUILD_EXAMPLE_PROGRAMS OFF CACHE INTERNAL "")
set(BUILD_TESTS OFF CACHE INTERNAL "")
set(BUILD_DYNAMIC_LIBS OFF CACHE INTERNAL "")

これで安定的にビルドが通るようになった。詳しい原理は追って確認する。

Categories: