OpenSiv3D icon indicating copy to clipboard operation
OpenSiv3D copied to clipboard

VSync無効時にフレームレートを制限する機能の提案

Open m4saka opened this issue 1 year ago • 7 comments

追加する機能の内容 | Describe the solution you'd like

過去バージョンに存在していた、下記の関数を別の実装で復活させたいと思っています。

  • Graphics::SetTargetFrameRateHz
  • Graphics::GetTargetFrameRateHz

下記のQiita記事に記載したstd::this_thread::sleep_untilを使う方法で、Windows版・macOS版の2通りで安定して300fpsに固定できることを確認できました。 OpenSiv3Dでフレームレートを60fps以外に固定する方法(FrameRateLimitアドオン) | Qiita

そこで、上記と同じような内容の実装をSiv3D内に組み込んで、VSync無効時のフレームレートを指定できるようにしたいと思っています。

その機能の追加によって解決する問題 | Is your feature request related to a problem? Please describe.

60fps以外のフレームレートで安定動作させられる。 ソフトウェア作成者が適切なウェイトを入れないままVSyncを無効化してしまい、ソフトウェア利用者の環境にて過剰な負荷が発生するのを防げる。

備考 | Additional context

もしよろしければ、実装についてぜひ私の方で挑戦してみたいです。 そこで、まずは実装方針について3点ご相談したいです。

  1. ユーザー側で呼び出すインタフェースは旧バージョンにあったGraphics::SetTargetFrameRateHzと同じにすべきでしょうか?それとも、より適切な名前があれば名前空間や関数名を別のものに変更しても問題ないでしょうか?
  2. フレームレート制限は旧バージョンと同様、VSyncを無効化した場合のみ効くようにした方が良いでしょうか?それとも、VSync有効のままで120Hzモニタや144Hzモニタでも60fpsに固定したいユーザーなどを想定して、VSyncが有効な場合も効くようにした方が良いでしょうか?
  3. プラットフォーム間で同一実装になる見込みです。そこで、時間待ちは旧バージョンで実装していたD3DSwapChain.cpp等ではなく、ISiv3DFrameRateLimitインタフェース、CFrameRateLimitクラスを新たに作成して実装し、System::Update内のAddonのpostPresentの直前か直後のタイミングにSIV3D_ENGINE(FrameRateLimit)->update();を入れて実行する形が良さそうですが、方針について問題なさそうでしょうか?

m4saka avatar Jan 02 '24 17:01 m4saka

ご提案ありがとうございます。#1179 の調査完了後に検討します。

Reputeless avatar Jan 03 '24 12:01 Reputeless

#1179 をマージしたので、こちら進めます。

  1. Hz を外して void Graphics::SetTargetFrameRate(const Optional<double>& fps) / Optional<double> Graphics::GetTargetFrameRate() にしましょう。
  2. 既存のコードへの影響を小さくするため、VSync が無効な場合のみ有効としたいです。
  3. 新しいエンジンインタフェース FrameRateLimit の追加 OK です。その実装方針で良さそうです。

Reputeless avatar Jan 08 '24 05:01 Reputeless

ご確認ありがとうございます。1〜3について承知しました! 実装に進展がありましたらお知らせいたします!

m4saka avatar Jan 09 '24 04:01 m4saka

@Reputeless 下記で仮実装しました。実装内容に問題ないかご確認いただけますと幸いです! https://github.com/m4saka/OpenSiv3D/commit/da52430ef41c7aec1b624512a2f0873ee4e12602

実装にあたって気になっている点は下記2点です。

  1. 目標フレームレート(fps引数)にゼロや負の値を指定した時に例外を投げているのは問題ないか?
  2. 目標フレームレート(fps引数)にNaNや+inf、-infを指定した場合も例外を投げるべきか?

補足事項として、Qiita記事での実装ではMaxDrift(=10ミリ秒)という定数値を導入していましたが、now < sleepUntilの場合のみsleepを実行することで不要になったので、今回の実装には入っていません。

手元では現状Windows版のみで動作確認しているので、macOS版、Linux版、Web版でも正常動作するかどうかは今後確認予定です。 (Xcodeプロジェクトへのソースファイル追加についても現状は未対応です)

m4saka avatar Feb 14 '24 20:02 m4saka

ありがとうございます。確認します、

Reputeless avatar Feb 15 '24 13:02 Reputeless

レビューコメントが記載できるように一旦Draft PRにいたしました。 https://github.com/Siv3D/OpenSiv3D/pull/1205

m4saka avatar Feb 16 '24 00:02 m4saka

目標フレームレート(fps引数)にゼロや負の値を指定した時に例外を投げているのは問題ないか? 目標フレームレート(fps引数)にNaNや+inf、-infを指定した場合も例外を投げるべきか?

  • ほかの関数のほとんどがそうなので、NaN の挙動は未定義(対策なし)で問題ありませんが、例外で知らせても良いです。
  • 「スリープ無し / 目標フレームレート ∞」である状態を、+inf で表現するのはどうでしょうか。
  • あまりにも低すぎるフレームレートは他の機能(入力関連など)に悪影響を及ぼす可能性があるため、目標フレームレートの下限を 1.0 fps にしたいです。
  • 目標フレームレートの有効範囲を関数内部で自動的に 1.0 以上 +inf 以下にクランプすると良さそうな気がします。

Reputeless avatar Feb 22 '24 13:02 Reputeless