Skip to content

Commit

Permalink
VSync無効時にフレームレートを制限する機能を追加
Browse files Browse the repository at this point in the history
  • Loading branch information
m4saka committed Feb 14, 2024
1 parent 33ec928 commit da52430
Show file tree
Hide file tree
Showing 16 changed files with 302 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ set(SIV3D_INTERNAL_SOURCES
../Siv3D/src/Siv3D/FormatInt/SivFormatInt.cpp
../Siv3D/src/Siv3D/Formatter/SivFormatter.cpp
../Siv3D/src/Siv3D/FormatUtility/SivFormatUtility.cpp
../Siv3D/src/Siv3D/FrameRateLimit/SivFrameRateLimit.cpp
../Siv3D/src/Siv3D/FrameRateLimit/FrameRateLimitFactory.cpp
../Siv3D/src/Siv3D/Gamepad/GamepadFactory.cpp
../Siv3D/src/Siv3D/Gamepad/SivGamepad.cpp
../Siv3D/src/Siv3D/GamepadInfo/SivGamepadInfo.cpp
Expand Down
11 changes: 11 additions & 0 deletions Siv3D/include/Siv3D/Graphics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

# pragma once
# include "Common.hpp"
# include "Optional.hpp"

namespace s3d
{
Expand All @@ -27,5 +28,15 @@ namespace s3d
/// @return VSync が有効である場合 true, 無効である場合 false
[[nodiscard]]
bool IsVSyncEnabled();

/// @brief VSync 無効時の目標フレームレートを設定します。
/// @param fps 目標フレームレート(FPS)。フレームレート制限を無効にする場合は none
/// @remark デフォルトでは none です。
void SetTargetFrameRate(const Optional<double>& fps);

/// @brief VSync 無効時の目標フレームレートを取得します。
/// @return 目標フレームレート(FPS)。フレームレート制限が無効の場合は none
[[nodiscard]]
Optional<double> GetTargetFrameRate();
}
}
2 changes: 2 additions & 0 deletions Siv3D/src/Siv3D-Platform/Linux/Siv3D/System/CSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
# include <Siv3D/Effect/IEffect.hpp>
# include <Siv3D/Script/IScript.hpp>
# include <Siv3D/Addon/IAddon.hpp>
# include <Siv3D/FrameRateLimit/IFrameRateLimit.hpp>
# include <Siv3D/System/SystemLog.hpp>
# include <Siv3D/System/SystemMisc.hpp>
# include "CSystem.hpp"
Expand Down Expand Up @@ -131,6 +132,7 @@ namespace s3d
SIV3D_ENGINE(Renderer)->present();
SIV3D_ENGINE(ScreenCapture)->update();
SIV3D_ENGINE(Addon)->postPresent();
SIV3D_ENGINE(FrameRateLimit)->update();

//
// previous frame
Expand Down
2 changes: 2 additions & 0 deletions Siv3D/src/Siv3D-Platform/Web/Siv3D/System/CSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
# include <Siv3D/Asset/IAsset.hpp>
# include <Siv3D/Effect/IEffect.hpp>
# include <Siv3D/Addon/IAddon.hpp>
# include <Siv3D/FrameRateLimit/IFrameRateLimit.hpp>
# include <Siv3D/Script/IScript.hpp>
# include <Siv3D/System/SystemLog.hpp>
# include <Siv3D/System/SystemMisc.hpp>
Expand Down Expand Up @@ -151,6 +152,7 @@ namespace s3d
SIV3D_ENGINE(Renderer)->present();
SIV3D_ENGINE(ScreenCapture)->update();
SIV3D_ENGINE(Addon)->postPresent();
SIV3D_ENGINE(FrameRateLimit)->update();

detail::siv3dRequestAnimationFrame();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
# include <Siv3D/Asset/IAsset.hpp>
# include <Siv3D/Effect/IEffect.hpp>
# include <Siv3D/Addon/IAddon.hpp>
# include <Siv3D/FrameRateLimit/IFrameRateLimit.hpp>
# include <Siv3D/System/SystemLog.hpp>
# include <Siv3D/System/SystemMisc.hpp>
# include <Siv3D/Windows/Windows.hpp>
Expand Down Expand Up @@ -177,6 +178,7 @@ namespace s3d
SIV3D_ENGINE(Renderer)->present();
SIV3D_ENGINE(ScreenCapture)->update();
SIV3D_ENGINE(Addon)->postPresent();
SIV3D_ENGINE(FrameRateLimit)->update();

//
// previous frame
Expand Down
2 changes: 2 additions & 0 deletions Siv3D/src/Siv3D-Platform/macOS/Siv3D/System/CSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
# include <Siv3D/Effect/IEffect.hpp>
# include <Siv3D/Script/IScript.hpp>
# include <Siv3D/Addon/IAddon.hpp>
# include <Siv3D/FrameRateLimit/IFrameRateLimit.hpp>
# include <Siv3D/System/SystemLog.hpp>
# include <Siv3D/System/SystemMisc.hpp>
# include "CSystem.hpp"
Expand Down Expand Up @@ -131,6 +132,7 @@ namespace s3d
SIV3D_ENGINE(Renderer)->present();
SIV3D_ENGINE(ScreenCapture)->update();
SIV3D_ENGINE(Addon)->postPresent();
SIV3D_ENGINE(FrameRateLimit)->update();

//
// previous frame
Expand Down
1 change: 1 addition & 0 deletions Siv3D/src/Siv3D/Common/Siv3DEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
# include <Siv3D/TrailRenderer/ITrailRenderer.hpp>
# include <Siv3D/Script/IScript.hpp>
# include <Siv3D/Addon/IAddon.hpp>
# include <Siv3D/FrameRateLimit/IFrameRateLimit.hpp>

namespace s3d
{
Expand Down
4 changes: 3 additions & 1 deletion Siv3D/src/Siv3D/Common/Siv3DEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ namespace s3d
class ISiv3DTrailRenderer;
class ISiv3DScript;
class ISiv3DAddon;
class ISiv3DFrameRateLimit;

class Siv3DEngine
{
Expand Down Expand Up @@ -118,7 +119,8 @@ namespace s3d
Siv3DComponent<ISiv3DEffect>,
Siv3DComponent<ISiv3DTrailRenderer>,
Siv3DComponent<ISiv3DScript>,
Siv3DComponent<ISiv3DAddon>> m_components;
Siv3DComponent<ISiv3DAddon>,
Siv3DComponent<ISiv3DFrameRateLimit>> m_components;

public:

Expand Down
86 changes: 86 additions & 0 deletions Siv3D/src/Siv3D/FrameRateLimit/CFrameRateLimit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//-----------------------------------------------
//
// This file is part of the Siv3D Engine.
//
// Copyright (c) 2008-2023 Ryo Suzuki
// Copyright (c) 2016-2023 OpenSiv3D Project
//
// Licensed under the MIT License.
//
//-----------------------------------------------

# pragma once
# include "IFrameRateLimit.hpp"

namespace s3d
{
class CFrameRateLimit final : public ISiv3DFrameRateLimit
{
public:

using clock_type = std::chrono::steady_clock;

/// @brief フレームレート制限を実行します。
void update() override;

/// @brief 目標フレームレートを設定します。
/// @param fps 目標フレームレート(FPS)。フレームレート制限を無効にする場合は none
void setTargetFrameRate(const Optional<double>& fps) override;

/// @brief 目標フレームレートを取得します。
/// @return 目標フレームレート(FPS)。フレームレート制限が無効の場合は none
SIV3D_NODISCARD_CXX20
Optional<double> getTargetFrameRate() const override;

private:

class Limiter
{
public:

/// @brief コンストラクタ
/// @param fps 目標フレームレート(FPS)
/// @throw Error fps が無効な値の場合
SIV3D_NODISCARD_CXX20
explicit Limiter(double fps);

/// @brief sleepを実行し、フレームを次に進めます。
void doSleep();

/// @brief 目標フレームレートを設定します。
/// @param targetFrameRate 目標フレームレート(FPS)
/// @throw Error fps が無効な値の場合
void setTargetFrameRate(double fps);

/// @brief 目標フレームレートを取得します。
/// @return 目標フレームレート(FPS)
SIV3D_NODISCARD_CXX20
double getTargetFrameRate() const noexcept { return m_fps; }

private:

/// @brief 1秒間
static constexpr clock_type::duration OneSecond = std::chrono::seconds{ 1 };

/// @brief 目標フレームレート(FPS)
double m_fps;

/// @brief 1フレームあたりの時間
clock_type::duration m_oneFrameDuration;

/// @brief 目標sleep時刻
clock_type::time_point m_sleepUntil;

/// @brief フレームレートから1フレームあたりの時間の長さを計算します。
/// @param fps フレームレート(FPS)
/// @return 1フレームあたりの時間の長さ
/// @throw Error fps が無効な値の場合
SIV3D_NODISCARD_CXX20
static clock_type::duration FPSToOneFrameDuration(double fps);
};

/// @brief フレームレート制限
/// @remark フレームレート制限が無効の場合は nullptr
std::unique_ptr<Limiter> m_limiter;
};
}
20 changes: 20 additions & 0 deletions Siv3D/src/Siv3D/FrameRateLimit/FrameRateLimitFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//-----------------------------------------------
//
// This file is part of the Siv3D Engine.
//
// Copyright (c) 2008-2023 Ryo Suzuki
// Copyright (c) 2016-2023 OpenSiv3D Project
//
// Licensed under the MIT License.
//
//-----------------------------------------------

# include "CFrameRateLimit.hpp"

namespace s3d
{
ISiv3DFrameRateLimit* ISiv3DFrameRateLimit::Create()
{
return new CFrameRateLimit;
}
}
32 changes: 32 additions & 0 deletions Siv3D/src/Siv3D/FrameRateLimit/IFrameRateLimit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//-----------------------------------------------
//
// This file is part of the Siv3D Engine.
//
// Copyright (c) 2008-2023 Ryo Suzuki
// Copyright (c) 2016-2023 OpenSiv3D Project
//
// Licensed under the MIT License.
//
//-----------------------------------------------

# pragma once
# include <Siv3D/Common.hpp>
# include <Siv3D/Optional.hpp>

namespace s3d
{
class SIV3D_NOVTABLE ISiv3DFrameRateLimit
{
public:

static ISiv3DFrameRateLimit* Create();

virtual ~ISiv3DFrameRateLimit() = default;

virtual void update() = 0;

virtual void setTargetFrameRate(const Optional<double>& fps) = 0;

virtual Optional<double> getTargetFrameRate() const = 0;
};
}
107 changes: 107 additions & 0 deletions Siv3D/src/Siv3D/FrameRateLimit/SivFrameRateLimit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//-----------------------------------------------
//
// This file is part of the Siv3D Engine.
//
// Copyright (c) 2008-2023 Ryo Suzuki
// Copyright (c) 2016-2023 OpenSiv3D Project
//
// Licensed under the MIT License.
//
//-----------------------------------------------

# include "CFrameRateLimit.hpp"
# include "Siv3D/Graphics.hpp"
# include "Siv3D/Error.hpp"
# include "Siv3D/Duration.hpp"

namespace s3d
{
void CFrameRateLimit::update()
{
if (Graphics::IsVSyncEnabled())
{
// VSync が有効の場合はフレームレート制限を無視
return;
}

if (not m_limiter)
{
// フレームレート制限が無効の場合は何もしない
return;
}

m_limiter->doSleep();
}

void CFrameRateLimit::setTargetFrameRate(const Optional<double>& fps)
{
if (not fps)
{
// フレームレート制限を無効化
m_limiter.reset();
return;
}

if (m_limiter)
{
// 既にフレームレート制限が有効の場合は目標フレームレートを設定
m_limiter->setTargetFrameRate(*fps);
}
else
{
// フレームレート制限を有効化
m_limiter = std::make_unique<Limiter>(*fps);
}
}

Optional<double> CFrameRateLimit::getTargetFrameRate() const
{
if (m_limiter)
{
return m_limiter->getTargetFrameRate();
}
else
{
return none;
}
}

CFrameRateLimit::Limiter::Limiter(double fps)
: m_fps(fps)
, m_oneFrameDuration(FPSToOneFrameDuration(fps))
, m_sleepUntil(clock_type::now())
{
}

void CFrameRateLimit::Limiter::doSleep()
{
const auto now = clock_type::now();
const auto sleepUntil = Max(m_sleepUntil + m_oneFrameDuration, now);

if (now < sleepUntil)
{
std::this_thread::sleep_until(sleepUntil);
}

m_sleepUntil = sleepUntil;
}

void CFrameRateLimit::Limiter::setTargetFrameRate(double fps)
{
m_fps = fps;
m_oneFrameDuration = FPSToOneFrameDuration(fps);

// setTargetFrameRate が毎フレーム呼び出された場合に sleep 時間に誤差が生じないよう,
// m_sleepUntil はここでは更新しない
}

CFrameRateLimit::clock_type::duration CFrameRateLimit::Limiter::FPSToOneFrameDuration(double fps)
{
if (fps <= 0.0)
{
throw Error{ U"FrameRateLimit::Limiter::FPSToOneFrameDuration(): Invalid fps" };
}

return DurationCast<clock_type::duration>(OneSecond / fps);
}
}
11 changes: 11 additions & 0 deletions Siv3D/src/Siv3D/Graphics/SivGraphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

# include <Siv3D/Graphics.hpp>
# include <Siv3D/Renderer/IRenderer.hpp>
# include <Siv3D/FrameRateLimit/IFrameRateLimit.hpp>
# include <Siv3D/Common/Siv3DEngine.hpp>

namespace s3d
Expand All @@ -26,5 +27,15 @@ namespace s3d
{
return SIV3D_ENGINE(Renderer)->isVSyncEnabled();
}

void SetTargetFrameRate(const Optional<double>& fps)
{
SIV3D_ENGINE(FrameRateLimit)->setTargetFrameRate(fps);
}

Optional<double> GetTargetFrameRate()
{
return SIV3D_ENGINE(FrameRateLimit)->getTargetFrameRate();
}
}
}
Loading

0 comments on commit da52430

Please sign in to comment.