diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp index 5ce3e30..19bdebd 100644 --- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.cpp @@ -531,7 +531,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) if (!sInteractiveResize && pOpenGLComposite) { wglMakeCurrent(hDC, hRC); - pOpenGLComposite->paintGL(); + pOpenGLComposite->paintGL(true); wglMakeCurrent( NULL, NULL ); RaiseStatusControls(sStatusStrip); } diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp index d46322b..8f7aff3 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp @@ -35,7 +35,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : *mRuntimeHost, [this]() { renderEffect(); }, [this]() { ProcessScreenshotRequest(); }, - [this]() { paintGL(); }); + [this]() { paintGL(false); }); mVideoIOBridge = std::make_unique( *mVideoIO, *mRenderer, @@ -156,8 +156,26 @@ error: return false; } -void OpenGLComposite::paintGL() +void OpenGLComposite::paintGL(bool force) { + if (!force) + { + if (IsIconic(hGLWnd)) + return; + + const unsigned previewFps = mRuntimeHost ? mRuntimeHost->GetPreviewFps() : 30u; + if (previewFps == 0) + return; + + const auto now = std::chrono::steady_clock::now(); + const auto minimumInterval = std::chrono::microseconds(1000000 / (previewFps == 0 ? 1u : previewFps)); + if (mLastPreviewPresentTime != std::chrono::steady_clock::time_point() && + now - mLastPreviewPresentTime < minimumInterval) + { + return; + } + } + if (!TryEnterCriticalSection(&pMutex)) { ValidateRect(hGLWnd, NULL); @@ -165,6 +183,7 @@ void OpenGLComposite::paintGL() } mRenderer->PresentToWindow(hGLDC, mVideoIO->OutputFrameWidth(), mVideoIO->OutputFrameHeight()); + mLastPreviewPresentTime = std::chrono::steady_clock::now(); ValidateRect(hGLWnd, NULL); LeaveCriticalSection(&pMutex); } diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h index 7aa49e4..a97918e 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h @@ -23,6 +23,7 @@ #include #include #include +#include class VideoIODevice; class OpenGLVideoIOBridge; @@ -64,7 +65,7 @@ public: std::string GetOscAddress() const; void resizeGL(WORD width, WORD height); - void paintGL(); + void paintGL(bool force = false); private: void resizeWindow(int width, int height); @@ -92,6 +93,7 @@ private: unsigned mCachedRenderStateHeight = 0; std::atomic mUseCommittedLayerStates; std::atomic mScreenshotRequested; + std::chrono::steady_clock::time_point mLastPreviewPresentTime; bool InitOpenGLState(); void renderEffect(); diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp index 56b850f..7368ef1 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.cpp @@ -1481,6 +1481,11 @@ bool RuntimeHost::LoadConfig(std::string& error) const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast(mConfig.maxTemporalHistoryFrames)); mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast(configuredValue); } + if (const JsonValue* previewFpsValue = configJson.find("previewFps")) + { + const double configuredValue = previewFpsValue->asNumber(static_cast(mConfig.previewFps)); + mConfig.previewFps = configuredValue <= 0.0 ? 0u : static_cast(configuredValue); + } if (const JsonValue* enableExternalKeyingValue = configJson.find("enableExternalKeying")) mConfig.enableExternalKeying = enableExternalKeyingValue->asBoolean(mConfig.enableExternalKeying); if (const JsonValue* videoFormatValue = configJson.find("videoFormat")) @@ -1867,6 +1872,7 @@ JsonValue RuntimeHost::BuildStateValue() const app.set("oscPort", JsonValue(static_cast(mConfig.oscPort))); app.set("autoReload", JsonValue(mAutoReloadEnabled)); app.set("maxTemporalHistoryFrames", JsonValue(static_cast(mConfig.maxTemporalHistoryFrames))); + app.set("previewFps", JsonValue(static_cast(mConfig.previewFps))); app.set("enableExternalKeying", JsonValue(mConfig.enableExternalKeying)); app.set("inputVideoFormat", JsonValue(mConfig.inputVideoFormat)); app.set("inputFrameRate", JsonValue(mConfig.inputFrameRate)); diff --git a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h index 8c075d8..c27f8fb 100644 --- a/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h +++ b/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeHost.h @@ -65,6 +65,7 @@ public: unsigned short GetServerPort() const { return mServerPort; } unsigned short GetOscPort() const { return mConfig.oscPort; } unsigned GetMaxTemporalHistoryFrames() const { return mConfig.maxTemporalHistoryFrames; } + unsigned GetPreviewFps() const { return mConfig.previewFps; } bool ExternalKeyingEnabled() const { return mConfig.enableExternalKeying; } const std::string& GetInputVideoFormat() const { return mConfig.inputVideoFormat; } const std::string& GetInputFrameRate() const { return mConfig.inputFrameRate; } @@ -81,6 +82,7 @@ private: unsigned short oscPort = 9000; bool autoReload = true; unsigned maxTemporalHistoryFrames = 4; + unsigned previewFps = 30; bool enableExternalKeying = false; std::string inputVideoFormat = "1080p"; std::string inputFrameRate = "59.94"; diff --git a/config/runtime-host.json b/config/runtime-host.json index 5e3a55e..18babc0 100644 --- a/config/runtime-host.json +++ b/config/runtime-host.json @@ -8,5 +8,6 @@ "outputFrameRate": "59.94", "autoReload": true, "maxTemporalHistoryFrames": 12, + "previewFps": 30, "enableExternalKeying": true }