From c17b9980fe57440a637e980e1b383293373cd485 Mon Sep 17 00:00:00 2001 From: Xu Date: Sun, 29 Dec 2024 20:54:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=9C=80=E5=B0=8F=E5=B8=A7=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DesktopDuplicationFrameSource.cpp | 16 ++-- .../DesktopDuplicationFrameSource.h | 4 +- .../DwmSharedSurfaceFrameSource.cpp | 8 +- src/Magpie.Core/DwmSharedSurfaceFrameSource.h | 4 +- src/Magpie.Core/FrameSourceBase.cpp | 22 +++-- src/Magpie.Core/FrameSourceBase.h | 28 +++---- src/Magpie.Core/GDIFrameSource.cpp | 10 +-- src/Magpie.Core/GDIFrameSource.h | 4 +- .../GraphicsCaptureFrameSource.cpp | 10 +-- src/Magpie.Core/GraphicsCaptureFrameSource.h | 4 +- src/Magpie.Core/Renderer.cpp | 45 +++++----- src/Magpie.Core/StepTimer.cpp | 83 +++++++++++++------ src/Magpie.Core/StepTimer.h | 15 +++- src/Magpie/Profile.h | 2 + 14 files changed, 153 insertions(+), 102 deletions(-) diff --git a/src/Magpie.Core/DesktopDuplicationFrameSource.cpp b/src/Magpie.Core/DesktopDuplicationFrameSource.cpp index 9a7f00ce..b6eda26a 100644 --- a/src/Magpie.Core/DesktopDuplicationFrameSource.cpp +++ b/src/Magpie.Core/DesktopDuplicationFrameSource.cpp @@ -125,7 +125,7 @@ bool DesktopDuplicationFrameSource::_Initialize() noexcept { return true; } -FrameSourceBase::UpdateState DesktopDuplicationFrameSource::_Update() noexcept { +FrameSourceState DesktopDuplicationFrameSource::_Update() noexcept { ID3D11DeviceContext4* d3dDC = _deviceResources->GetD3DDC(); if (_isFrameAcquired) { @@ -139,12 +139,12 @@ FrameSourceBase::UpdateState DesktopDuplicationFrameSource::_Update() noexcept { // 等待 1ms HRESULT hr = _outputDup->AcquireNextFrame(1, &info, dxgiRes.put()); if (hr == DXGI_ERROR_WAIT_TIMEOUT) { - return UpdateState::Waiting; + return FrameSourceState::Waiting; } if (FAILED(hr)) { Logger::Get().ComError("AcquireNextFrame 失败", hr); - return UpdateState::Error; + return FrameSourceState::Error; } _isFrameAcquired = true; @@ -165,7 +165,7 @@ FrameSourceBase::UpdateState DesktopDuplicationFrameSource::_Update() noexcept { bufSize, (DXGI_OUTDUPL_MOVE_RECT*)_dupMetaData.data(), &bufSize); if (FAILED(hr)) { Logger::Get().ComError("GetFrameMoveRects 失败", hr); - return UpdateState::Error; + return FrameSourceState::Error; } uint32_t nRect = bufSize / sizeof(DXGI_OUTDUPL_MOVE_RECT); @@ -186,7 +186,7 @@ FrameSourceBase::UpdateState DesktopDuplicationFrameSource::_Update() noexcept { bufSize, (RECT*)_dupMetaData.data(), &bufSize); if (FAILED(hr)) { Logger::Get().ComError("GetFrameDirtyRects 失败", hr); - return UpdateState::Error; + return FrameSourceState::Error; } nRect = bufSize / sizeof(RECT); @@ -201,19 +201,19 @@ FrameSourceBase::UpdateState DesktopDuplicationFrameSource::_Update() noexcept { } if (noUpdate) { - return UpdateState::Waiting; + return FrameSourceState::Waiting; } winrt::com_ptr frameTexture = dxgiRes.try_as(); if (!frameTexture) { Logger::Get().Error("从 IDXGIResource 检索 ID3D11Resource 失败"); - return UpdateState::Error; + return FrameSourceState::Error; } d3dDC->CopySubresourceRegion( _output.get(), 0, 0, 0, 0, frameTexture.get(), 0, &_frameInMonitor); - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } } diff --git a/src/Magpie.Core/DesktopDuplicationFrameSource.h b/src/Magpie.Core/DesktopDuplicationFrameSource.h index 9937d7dd..a3e9302f 100644 --- a/src/Magpie.Core/DesktopDuplicationFrameSource.h +++ b/src/Magpie.Core/DesktopDuplicationFrameSource.h @@ -12,7 +12,7 @@ class DesktopDuplicationFrameSource final : public FrameSourceBase { } FrameSourceWaitType WaitType() const noexcept override { - return WaitForFrame; + return FrameSourceWaitType::WaitForFrame; } const char* Name() const noexcept override { @@ -30,7 +30,7 @@ class DesktopDuplicationFrameSource final : public FrameSourceBase { bool _Initialize() noexcept override; - UpdateState _Update() noexcept override; + FrameSourceState _Update() noexcept override; private: winrt::com_ptr _outputDup; diff --git a/src/Magpie.Core/DwmSharedSurfaceFrameSource.cpp b/src/Magpie.Core/DwmSharedSurfaceFrameSource.cpp index eef264ad..9c0fe8f8 100644 --- a/src/Magpie.Core/DwmSharedSurfaceFrameSource.cpp +++ b/src/Magpie.Core/DwmSharedSurfaceFrameSource.cpp @@ -87,14 +87,14 @@ bool DwmSharedSurfaceFrameSource::_Initialize() noexcept { return true; } -FrameSourceBase::UpdateState DwmSharedSurfaceFrameSource::_Update() noexcept { +FrameSourceState DwmSharedSurfaceFrameSource::_Update() noexcept { HANDLE sharedTextureHandle = NULL; if (!dwmGetDxSharedSurface(ScalingWindow::Get().HwndSrc(), &sharedTextureHandle, nullptr, nullptr, nullptr, nullptr) || !sharedTextureHandle ) { Logger::Get().Win32Error("DwmGetDxSharedSurface 失败"); - return UpdateState::Error; + return FrameSourceState::Error; } winrt::com_ptr sharedTexture; @@ -102,13 +102,13 @@ FrameSourceBase::UpdateState DwmSharedSurfaceFrameSource::_Update() noexcept { ->OpenSharedResource(sharedTextureHandle, IID_PPV_ARGS(&sharedTexture)); if (FAILED(hr)) { Logger::Get().ComError("OpenSharedResource 失败", hr); - return UpdateState::Error; + return FrameSourceState::Error; } _deviceResources->GetD3DDC()->CopySubresourceRegion( _output.get(), 0, 0, 0, 0, sharedTexture.get(), 0, &_frameInWnd); - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } } diff --git a/src/Magpie.Core/DwmSharedSurfaceFrameSource.h b/src/Magpie.Core/DwmSharedSurfaceFrameSource.h index ee5d20c2..d942efc2 100644 --- a/src/Magpie.Core/DwmSharedSurfaceFrameSource.h +++ b/src/Magpie.Core/DwmSharedSurfaceFrameSource.h @@ -12,7 +12,7 @@ class DwmSharedSurfaceFrameSource final : public FrameSourceBase { } FrameSourceWaitType WaitType() const noexcept override { - return NoWait; + return FrameSourceWaitType::NoWait; } const char* Name() const noexcept override { @@ -22,7 +22,7 @@ class DwmSharedSurfaceFrameSource final : public FrameSourceBase { protected: bool _Initialize() noexcept override; - UpdateState _Update() noexcept override; + FrameSourceState _Update() noexcept override; bool _HasRoundCornerInWin11() noexcept override { return false; diff --git a/src/Magpie.Core/FrameSourceBase.cpp b/src/Magpie.Core/FrameSourceBase.cpp index f3110ff0..ffa6935e 100644 --- a/src/Magpie.Core/FrameSourceBase.cpp +++ b/src/Magpie.Core/FrameSourceBase.cpp @@ -110,12 +110,16 @@ bool FrameSourceBase::Initialize(DeviceResources& deviceResources, BackendDescri return true; } -FrameSourceBase::UpdateState FrameSourceBase::Update() noexcept { - const UpdateState state = _Update(); +FrameSourceState FrameSourceBase::Update(bool forceNewFrame) noexcept { + const FrameSourceState state = _Update(); + + if (forceNewFrame) { + return FrameSourceState::NewFrame; + } const ScalingOptions& options = ScalingWindow::Get().Options(); const auto duplicateFrameDetectionMode = options.duplicateFrameDetectionMode; - if (state != UpdateState::NewFrame || options.Is3DGameMode() || + if (state != FrameSourceState::NewFrame || options.Is3DGameMode() || duplicateFrameDetectionMode == DuplicateFrameDetectionMode::Never) { return state; } @@ -131,16 +135,16 @@ FrameSourceBase::UpdateState FrameSourceBase::Update() noexcept { _prevFrameSrv = nullptr; } - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } if (duplicateFrameDetectionMode == DuplicateFrameDetectionMode::Always) { // 总是检查重复帧 if (_IsDuplicateFrame()) { - return UpdateState::Waiting; + return FrameSourceState::Waiting; } else { d3dDC->CopyResource(_prevFrame.get(), _output.get()); - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } } @@ -166,12 +170,12 @@ FrameSourceBase::UpdateState FrameSourceBase::Update() noexcept { _isCheckingForDuplicateFrame = true; _framesLeft = INITIAL_CHECK_COUNT; _nextSkipCount = INITIAL_SKIP_COUNT; - return UpdateState::Waiting; + return FrameSourceState::Waiting; } else { if (_isCheckingForDuplicateFrame || isStatisticsEnabled) { d3dDC->CopyResource(_prevFrame.get(), _output.get()); } - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } } else { if (--_framesLeft == 0) { @@ -201,7 +205,7 @@ FrameSourceBase::UpdateState FrameSourceBase::Update() noexcept { _statistics.store(statistics, std::memory_order_relaxed); } - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } } diff --git a/src/Magpie.Core/FrameSourceBase.h b/src/Magpie.Core/FrameSourceBase.h index 7c59a789..efe9160f 100644 --- a/src/Magpie.Core/FrameSourceBase.h +++ b/src/Magpie.Core/FrameSourceBase.h @@ -5,6 +5,18 @@ namespace Magpie { class DeviceResources; class BackendDescriptorStore; +enum class FrameSourceWaitType { + NoWait, + WaitForMessage, + WaitForFrame +}; + +enum class FrameSourceState { + NewFrame, + Waiting, + Error +}; + class FrameSourceBase { public: FrameSourceBase() noexcept; @@ -17,13 +29,7 @@ class FrameSourceBase { bool Initialize(DeviceResources& deviceResources, BackendDescriptorStore& descriptorStore) noexcept; - enum class UpdateState { - NewFrame, - Waiting, - Error - }; - - UpdateState Update() noexcept; + FrameSourceState Update(bool forceNewFrame) noexcept; ID3D11Texture2D* GetOutput() noexcept { return _output.get(); @@ -39,12 +45,6 @@ class FrameSourceBase { virtual bool IsScreenCapture() const noexcept = 0; - enum FrameSourceWaitType { - NoWait, - WaitForMessage, - WaitForFrame - }; - virtual FrameSourceWaitType WaitType() const noexcept = 0; virtual void OnCursorVisibilityChanged(bool /*isVisible*/, bool /*onDestory*/) noexcept {}; @@ -52,7 +52,7 @@ class FrameSourceBase { protected: virtual bool _Initialize() noexcept = 0; - virtual UpdateState _Update() noexcept = 0; + virtual FrameSourceState _Update() noexcept = 0; virtual bool _HasRoundCornerInWin11() noexcept = 0; diff --git a/src/Magpie.Core/GDIFrameSource.cpp b/src/Magpie.Core/GDIFrameSource.cpp index 8e6d54df..f22e7ab2 100644 --- a/src/Magpie.Core/GDIFrameSource.cpp +++ b/src/Magpie.Core/GDIFrameSource.cpp @@ -63,12 +63,12 @@ bool GDIFrameSource::_Initialize() noexcept { return true; } -FrameSourceBase::UpdateState GDIFrameSource::_Update() noexcept { +FrameSourceState GDIFrameSource::_Update() noexcept { HDC hdcDest; HRESULT hr = _dxgiSurface->GetDC(TRUE, &hdcDest); if (FAILED(hr)) { Logger::Get().ComError("从 Texture2D 获取 IDXGISurface1 失败", hr); - return UpdateState::Error; + return FrameSourceState::Error; } auto se = wil::scope_exit([&]() { @@ -79,17 +79,17 @@ FrameSourceBase::UpdateState GDIFrameSource::_Update() noexcept { wil::unique_hdc_window hdcSrc(wil::window_dc(GetDCEx(hwndSrc, NULL, DCX_WINDOW), hwndSrc)); if (!hdcSrc) { Logger::Get().Win32Error("GetDC 失败"); - return UpdateState::Error; + return FrameSourceState::Error; } if (!BitBlt(hdcDest, 0, 0, _frameRect.right - _frameRect.left, _frameRect.bottom - _frameRect.top, hdcSrc.get(), _frameRect.left, _frameRect.top, SRCCOPY) ) { Logger::Get().Win32Error("BitBlt 失败"); - return UpdateState::Error; + return FrameSourceState::Error; } - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } } diff --git a/src/Magpie.Core/GDIFrameSource.h b/src/Magpie.Core/GDIFrameSource.h index a284692f..211ca249 100644 --- a/src/Magpie.Core/GDIFrameSource.h +++ b/src/Magpie.Core/GDIFrameSource.h @@ -12,7 +12,7 @@ class GDIFrameSource final : public FrameSourceBase { } FrameSourceWaitType WaitType() const noexcept override { - return NoWait; + return FrameSourceWaitType::NoWait; } const char* Name() const noexcept override { @@ -22,7 +22,7 @@ class GDIFrameSource final : public FrameSourceBase { protected: bool _Initialize() noexcept override; - UpdateState _Update() noexcept override; + FrameSourceState _Update() noexcept override; bool _HasRoundCornerInWin11() noexcept override { return false; diff --git a/src/Magpie.Core/GraphicsCaptureFrameSource.cpp b/src/Magpie.Core/GraphicsCaptureFrameSource.cpp index 7614c7ff..019ec76a 100644 --- a/src/Magpie.Core/GraphicsCaptureFrameSource.cpp +++ b/src/Magpie.Core/GraphicsCaptureFrameSource.cpp @@ -91,15 +91,15 @@ bool GraphicsCaptureFrameSource::_Initialize() noexcept { return true; } -FrameSourceBase::UpdateState GraphicsCaptureFrameSource::_Update() noexcept { +FrameSourceState GraphicsCaptureFrameSource::_Update() noexcept { if (!_captureSession) { - return UpdateState::Waiting; + return FrameSourceState::Waiting; } winrt::Direct3D11CaptureFrame frame = _captureFramePool.TryGetNextFrame(); if (!frame) { // 因为已通过 FrameArrived 注册回调,所以每当有新帧时会有新消息到达 - return UpdateState::Waiting; + return FrameSourceState::Waiting; } // 从帧获取 IDXGISurface @@ -113,12 +113,12 @@ FrameSourceBase::UpdateState GraphicsCaptureFrameSource::_Update() noexcept { HRESULT hr = dxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&withFrame)); if (FAILED(hr)) { Logger::Get().ComError("从获取 IDirect3DSurface 获取 ID3D11Texture2D 失败", hr); - return UpdateState::Error; + return FrameSourceState::Error; } _deviceResources->GetD3DDC()->CopySubresourceRegion(_output.get(), 0, 0, 0, 0, withFrame.get(), 0, &_frameBox); - return UpdateState::NewFrame; + return FrameSourceState::NewFrame; } void GraphicsCaptureFrameSource::OnCursorVisibilityChanged(bool isVisible, bool onDestory) noexcept { diff --git a/src/Magpie.Core/GraphicsCaptureFrameSource.h b/src/Magpie.Core/GraphicsCaptureFrameSource.h index c8d5ea15..3d45181f 100644 --- a/src/Magpie.Core/GraphicsCaptureFrameSource.h +++ b/src/Magpie.Core/GraphicsCaptureFrameSource.h @@ -17,7 +17,7 @@ class GraphicsCaptureFrameSource final : public FrameSourceBase { } FrameSourceWaitType WaitType() const noexcept override { - return WaitForMessage; + return FrameSourceWaitType::WaitForMessage; } const char* Name() const noexcept override { @@ -37,7 +37,7 @@ class GraphicsCaptureFrameSource final : public FrameSourceBase { bool _Initialize() noexcept override; - UpdateState _Update() noexcept override; + FrameSourceState _Update() noexcept override; private: bool _StartCapture() noexcept; diff --git a/src/Magpie.Core/Renderer.cpp b/src/Magpie.Core/Renderer.cpp index fce25667..0b43a52f 100644 --- a/src/Magpie.Core/Renderer.cpp +++ b/src/Magpie.Core/Renderer.cpp @@ -666,11 +666,20 @@ void Renderer::_BackendThreadProc() noexcept { return; } - bool waitingForStepTimer = true; bool exiting = false; + bool waitMsgForNewFrame = false; MSG msg; while (true) { + StepTimerStatus stepTimerStatus = StepTimerStatus::WaitForNewFrame; + + if (exiting) { + WaitMessage(); + } else { + stepTimerStatus = _stepTimer.WaitForNextFrame(waitMsgForNewFrame); + waitMsgForNewFrame = false; + } + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { // 不能在前端线程释放 @@ -682,38 +691,29 @@ void Renderer::_BackendThreadProc() noexcept { } if (exiting) { - // 准备退出,不再捕获 - WaitMessage(); continue; } - - if (waitingForStepTimer) { - if (!_stepTimer.WaitForNextFrame()) { - _stepTimer.UpdateFPS(false); - continue; - } - waitingForStepTimer = false; + + if (stepTimerStatus == StepTimerStatus::WaitForFPSLimiter) { + _stepTimer.UpdateFPS(false); + continue; } - const FrameSourceBase::UpdateState state = _frameSource->Update(); - _stepTimer.UpdateFPS(state == FrameSourceBase::UpdateState::NewFrame); + FrameSourceState state = _frameSource->Update(stepTimerStatus == StepTimerStatus::ForceNewFrame); + _stepTimer.UpdateFPS(state == FrameSourceState::NewFrame); switch (state) { - case FrameSourceBase::UpdateState::NewFrame: + case FrameSourceState::NewFrame: { _BackendRender(outputTexture); - waitingForStepTimer = true; break; } - case FrameSourceBase::UpdateState::Waiting: + case FrameSourceState::Waiting: { - if (_frameSource->WaitType() == FrameSourceBase::WaitForMessage) { - // 等待新消息 - WaitMessage(); - } + waitMsgForNewFrame = _frameSource->WaitType() == FrameSourceWaitType::WaitForMessage; break; } - case FrameSourceBase::UpdateState::Error: + case FrameSourceState::Error: { // 捕获出错,退出缩放 ScalingWindow::Get().Dispatcher().TryEnqueue([]() { @@ -726,7 +726,6 @@ void Renderer::_BackendThreadProc() noexcept { } default: { - waitingForStepTimer = true; break; } } @@ -765,7 +764,7 @@ ID3D11Texture2D* Renderer::_InitBackend() noexcept { { std::optional frameRateLimit; - if (_frameSource->WaitType() == FrameSourceBase::NoWait) { + if (_frameSource->WaitType() == FrameSourceWaitType::NoWait) { // 某些捕获方式不会限制捕获帧率,因此将捕获帧率限制为屏幕刷新率 const HWND hwndSrc = ScalingWindow::Get().HwndSrc(); if (HMONITOR hMon = MonitorFromWindow(hwndSrc, MONITOR_DEFAULTTONEAREST)) { @@ -789,7 +788,7 @@ ID3D11Texture2D* Renderer::_InitBackend() noexcept { } } - _stepTimer.Initialize(frameRateLimit); + _stepTimer.Initialize(10.0f, frameRateLimit); } ID3D11Texture2D* outputTexture = _BuildEffects(); diff --git a/src/Magpie.Core/StepTimer.cpp b/src/Magpie.Core/StepTimer.cpp index 643bf50c..2eda310b 100644 --- a/src/Magpie.Core/StepTimer.cpp +++ b/src/Magpie.Core/StepTimer.cpp @@ -5,46 +5,64 @@ using namespace std::chrono; namespace Magpie { -void StepTimer::Initialize(std::optional maxFrameRate) noexcept { +void StepTimer::Initialize(float minFrameRate, std::optional maxFrameRate) noexcept { + if (minFrameRate > 1e-6) { + _maxInterval = duration_cast(duration(1 / minFrameRate)); + } else { + _maxInterval = nanoseconds(std::numeric_limits::max()); + } + if (maxFrameRate) { _minInterval = duration_cast(duration(1 / *maxFrameRate)); - _hTimer.reset(CreateWaitableTimerEx(nullptr, nullptr, - CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); } + + _hTimer.reset(CreateWaitableTimerEx(nullptr, nullptr, + CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); } -bool StepTimer::WaitForNextFrame() noexcept { - if (!_minInterval) { - return true; +StepTimerStatus StepTimer::WaitForNextFrame(bool waitMsgForNewFrame) noexcept { + const time_point now = steady_clock::now(); + + if (_lastFrameTime == time_point{}) { + _lastFrameTime = now; + return StepTimerStatus::WaitForNewFrame; } - const time_point now = steady_clock::now(); const nanoseconds delta = now - _lastFrameTime; - if (delta >= *_minInterval) { - _lastFrameTime = now - delta % *_minInterval; - return true; + + if (delta >= _maxInterval) { + _lastFrameTime = now - delta % _maxInterval; + return StepTimerStatus::ForceNewFrame; } - const nanoseconds rest = *_minInterval - delta; - if (rest > 1ms) { - // Sleep 精度太低,我们使用 WaitableTimer 睡眠。负值表示相对时间 - LARGE_INTEGER liDueTime{ - .QuadPart = (rest - 1ms).count() / -100 - }; - SetWaitableTimerEx(_hTimer.get(), &liDueTime, 0, NULL, NULL, 0, 0); + if (_minInterval) { + if (delta < *_minInterval) { + _WaitForMsgAndTimer(*_minInterval - delta); + return StepTimerStatus::WaitForFPSLimiter; + } - // 新消息到达则中止等待 - HANDLE hTimer = _hTimer.get(); - MsgWaitForMultipleObjectsEx(1, &hTimer, INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE); - } else { - // 剩余时间在 1ms 以内则“忙等待” - Sleep(0); + if (!_isWaitingForNewFrame) { + _isWaitingForNewFrame = true; + _lastFrameTime = now - delta % *_minInterval; + } } - return false; + if (waitMsgForNewFrame) { + if (_maxInterval == nanoseconds(std::numeric_limits::max())) { + WaitMessage(); + } else { + _WaitForMsgAndTimer(_maxInterval - delta); + } + } + + return StepTimerStatus::WaitForNewFrame; } void StepTimer::UpdateFPS(bool newFrame) noexcept { + if (newFrame) { + _isWaitingForNewFrame = false; + } + if (_lastSecondTime == time_point{}) { // 第一帧 if (!newFrame) { @@ -73,4 +91,21 @@ void StepTimer::UpdateFPS(bool newFrame) noexcept { } } +void StepTimer::_WaitForMsgAndTimer(std::chrono::nanoseconds time) noexcept { + if (time > 1ms) { + // Sleep 精度太低,我们使用 WaitableTimer 睡眠。负值表示相对时间 + LARGE_INTEGER liDueTime{ + .QuadPart = (time - 1ms).count() / -100 + }; + SetWaitableTimerEx(_hTimer.get(), &liDueTime, 0, NULL, NULL, 0, 0); + + // 新消息到达则中止等待 + HANDLE hTimer = _hTimer.get(); + MsgWaitForMultipleObjectsEx(1, &hTimer, INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE); + } else { + // 剩余时间在 1ms 以内则“忙等待” + Sleep(0); + } +} + } diff --git a/src/Magpie.Core/StepTimer.h b/src/Magpie.Core/StepTimer.h index f0867676..3bcc30c3 100644 --- a/src/Magpie.Core/StepTimer.h +++ b/src/Magpie.Core/StepTimer.h @@ -3,6 +3,12 @@ namespace Magpie { +enum class StepTimerStatus { + WaitForNewFrame, + WaitForFPSLimiter, + ForceNewFrame +}; + class StepTimer { public: StepTimer() = default; @@ -10,9 +16,9 @@ class StepTimer { StepTimer(const StepTimer&) = delete; StepTimer(StepTimer&&) = delete; - void Initialize(std::optional maxFrameRate) noexcept; + void Initialize(float minFrameRate, std::optional maxFrameRate) noexcept; - bool WaitForNextFrame() noexcept; + StepTimerStatus WaitForNextFrame(bool waitMsgForNewFrame) noexcept; void UpdateFPS(bool newFrame) noexcept; @@ -26,7 +32,10 @@ class StepTimer { } private: + void _WaitForMsgAndTimer(std::chrono::nanoseconds time) noexcept; + std::optional _minInterval; + std::chrono::nanoseconds _maxInterval{}; wil::unique_event_nothrow _hTimer; std::chrono::time_point _lastFrameTime; @@ -35,6 +44,8 @@ class StepTimer { uint32_t _frameCount = 0; std::atomic _framesPerSecond = 0; uint32_t _framesThisSecond = 0; + + bool _isWaitingForNewFrame = false; }; } diff --git a/src/Magpie/Profile.h b/src/Magpie/Profile.h index 9145768e..1d597554 100644 --- a/src/Magpie/Profile.h +++ b/src/Magpie/Profile.h @@ -64,6 +64,8 @@ struct Profile { // 10~1000 float maxFrameRate = 60.0f; + // 0~1000 + float minFrameRate = 0.0f; std::wstring launchParameters;