diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f0ec17..330ce2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ set(APP_SOURCES "${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp" "${APP_DIR}/gl/OpenGLRenderPass.cpp" "${APP_DIR}/gl/OpenGLRenderPass.h" + "${APP_DIR}/gl/OpenGLRenderPipeline.cpp" + "${APP_DIR}/gl/OpenGLRenderPipeline.h" "${APP_DIR}/gl/OpenGLRenderer.cpp" "${APP_DIR}/gl/OpenGLRenderer.h" "${APP_DIR}/gl/OpenGLVideoIOBridge.cpp" diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj index 2b557f6..c0a8fef 100644 --- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj +++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj @@ -179,6 +179,7 @@ + @@ -204,6 +205,7 @@ + diff --git a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters index e42ac2c..31018c1 100644 --- a/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters +++ b/apps/LoopThroughWithOpenGLCompositing/LoopThroughWithOpenGLCompositing.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + Source Files @@ -86,6 +89,9 @@ Header Files + + Header Files + Header Files diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp index 1432e4e..b1b4540 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.cpp @@ -4,6 +4,7 @@ #include "GLExtensions.h" #include "GlRenderConstants.h" #include "OpenGLRenderPass.h" +#include "OpenGLRenderPipeline.h" #include "OpenGLShaderPrograms.h" #include "OpenGLVideoIOBridge.h" #include "PngScreenshotWriter.h" @@ -29,16 +30,20 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) : { InitializeCriticalSection(&pMutex); mRuntimeHost = std::make_unique(); - mVideoIOBridge = std::make_unique( - *mVideoIO, + mRenderPipeline = std::make_unique( *mRenderer, *mRuntimeHost, - pMutex, - hGLDC, - hGLRC, [this]() { renderEffect(); }, [this]() { ProcessScreenshotRequest(); }, [this]() { paintGL(); }); + mVideoIOBridge = std::make_unique( + *mVideoIO, + *mRenderer, + *mRenderPipeline, + *mRuntimeHost, + pMutex, + hGLDC, + hGLRC); mRenderPass = std::make_unique(*mRenderer); mShaderPrograms = std::make_unique(*mRenderer, *mRuntimeHost); mShaderBuildQueue = std::make_unique(*mRuntimeHost); diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h index b6d10b7..7375b3d 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLComposite.h @@ -27,6 +27,7 @@ class VideoIODevice; class OpenGLVideoIOBridge; class OpenGLRenderPass; +class OpenGLRenderPipeline; class OpenGLShaderPrograms; class RuntimeServices; class ShaderBuildQueue; @@ -81,6 +82,7 @@ private: std::unique_ptr mRuntimeHost; std::unique_ptr mVideoIOBridge; std::unique_ptr mRenderPass; + std::unique_ptr mRenderPipeline; std::unique_ptr mShaderPrograms; std::unique_ptr mShaderBuildQueue; std::unique_ptr mRuntimeServices; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPipeline.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPipeline.cpp new file mode 100644 index 0000000..cb4ab49 --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPipeline.cpp @@ -0,0 +1,92 @@ +#include "OpenGLRenderPipeline.h" + +#include "OpenGLRenderer.h" +#include "RuntimeHost.h" +#include "VideoIOFormat.h" + +#include +#include + +OpenGLRenderPipeline::OpenGLRenderPipeline( + OpenGLRenderer& renderer, + RuntimeHost& runtimeHost, + RenderEffectCallback renderEffect, + OutputReadyCallback outputReady, + PaintCallback paint) : + mRenderer(renderer), + mRuntimeHost(runtimeHost), + mRenderEffect(renderEffect), + mOutputReady(outputReady), + mPaint(paint) +{ +} + +bool OpenGLRenderPipeline::RenderFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame) +{ + const VideoIOState& state = context.videoState; + + const auto renderStartTime = std::chrono::steady_clock::now(); + glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.CompositeFramebuffer()); + mRenderEffect(); + glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.CompositeFramebuffer()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer.OutputFramebuffer()); + glBlitFramebuffer(0, 0, state.inputFrameSize.width, state.inputFrameSize.height, 0, 0, state.outputFrameSize.width, state.outputFrameSize.height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputFramebuffer()); + if (mOutputReady) + mOutputReady(); + if (state.outputPixelFormat == VideoIOPixelFormat::V210) + PackOutputForV210(state); + glFlush(); + + const auto renderEndTime = std::chrono::steady_clock::now(); + const double renderMilliseconds = std::chrono::duration_cast>(renderEndTime - renderStartTime).count(); + mRuntimeHost.TrySetPerformanceStats(state.frameBudgetMilliseconds, renderMilliseconds); + mRuntimeHost.TryAdvanceFrame(); + + ReadOutputFrame(state, outputFrame); + if (mPaint) + mPaint(); + + return true; +} + +void OpenGLRenderPipeline::PackOutputForV210(const VideoIOState& state) +{ + glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputPackFramebuffer()); + glViewport(0, 0, state.outputPackTextureWidth, state.outputFrameSize.height); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mRenderer.OutputTexture()); + glBindVertexArray(mRenderer.FullscreenVertexArray()); + glUseProgram(mRenderer.OutputPackProgram()); + + const GLint outputResolutionLocation = glGetUniformLocation(mRenderer.OutputPackProgram(), "uOutputVideoResolution"); + const GLint activeWordsLocation = glGetUniformLocation(mRenderer.OutputPackProgram(), "uActiveV210Words"); + if (outputResolutionLocation >= 0) + glUniform2f(outputResolutionLocation, static_cast(state.outputFrameSize.width), static_cast(state.outputFrameSize.height)); + if (activeWordsLocation >= 0) + glUniform1f(activeWordsLocation, static_cast(ActiveV210WordsForWidth(state.outputFrameSize.width))); + + glDrawArrays(GL_TRIANGLES, 0, 3); + glUseProgram(0); + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void OpenGLRenderPipeline::ReadOutputFrame(const VideoIOState& state, VideoIOOutputFrame& outputFrame) +{ + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + if (state.outputPixelFormat == VideoIOPixelFormat::V210) + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputPackFramebuffer()); + glReadPixels(0, 0, state.outputPackTextureWidth, state.outputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, outputFrame.bytes); + } + else + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer()); + glReadPixels(0, 0, state.outputFrameSize.width, state.outputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, outputFrame.bytes); + } +} diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPipeline.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPipeline.h new file mode 100644 index 0000000..e4d718e --- /dev/null +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLRenderPipeline.h @@ -0,0 +1,41 @@ +#pragma once + +#include "VideoIOTypes.h" + +#include + +class OpenGLRenderer; +class RuntimeHost; + +struct RenderPipelineFrameContext +{ + VideoIOState videoState; + VideoIOCompletion completion; +}; + +class OpenGLRenderPipeline +{ +public: + using RenderEffectCallback = std::function; + using OutputReadyCallback = std::function; + using PaintCallback = std::function; + + OpenGLRenderPipeline( + OpenGLRenderer& renderer, + RuntimeHost& runtimeHost, + RenderEffectCallback renderEffect, + OutputReadyCallback outputReady, + PaintCallback paint); + + bool RenderFrame(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame); + +private: + void PackOutputForV210(const VideoIOState& state); + void ReadOutputFrame(const VideoIOState& state, VideoIOOutputFrame& outputFrame); + + OpenGLRenderer& mRenderer; + RuntimeHost& mRuntimeHost; + RenderEffectCallback mRenderEffect; + OutputReadyCallback mOutputReady; + PaintCallback mPaint; +}; diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.cpp b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.cpp index 0911611..658ecd8 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.cpp +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.cpp @@ -9,22 +9,18 @@ OpenGLVideoIOBridge::OpenGLVideoIOBridge( VideoIODevice& videoIO, OpenGLRenderer& renderer, + OpenGLRenderPipeline& renderPipeline, RuntimeHost& runtimeHost, CRITICAL_SECTION& mutex, HDC hdc, - HGLRC hglrc, - RenderEffectCallback renderEffect, - OutputReadyCallback outputReady, - PaintCallback paint) : + HGLRC hglrc) : mVideoIO(videoIO), mRenderer(renderer), + mRenderPipeline(renderPipeline), mRuntimeHost(runtimeHost), mMutex(mutex), mHdc(hdc), - mHglrc(hglrc), - mRenderEffect(renderEffect), - mOutputReady(outputReady), - mPaint(paint) + mHglrc(hglrc) { } @@ -113,58 +109,7 @@ void OpenGLVideoIOBridge::PlayoutFrameCompleted(const VideoIOCompletion& complet // make GL context current in this thread wglMakeCurrent(mHdc, mHglrc); - // Draw the effect output to the off-screen framebuffer. - const auto renderStartTime = std::chrono::steady_clock::now(); - glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.CompositeFramebuffer()); - mRenderEffect(); - glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.CompositeFramebuffer()); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer.OutputFramebuffer()); - glBlitFramebuffer(0, 0, state.inputFrameSize.width, state.inputFrameSize.height, 0, 0, state.outputFrameSize.width, state.outputFrameSize.height, GL_COLOR_BUFFER_BIT, GL_LINEAR); - glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputFramebuffer()); - if (mOutputReady) - mOutputReady(); - if (state.outputPixelFormat == VideoIOPixelFormat::V210) - { - glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputPackFramebuffer()); - glViewport(0, 0, state.outputPackTextureWidth, state.outputFrameSize.height); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, mRenderer.OutputTexture()); - glBindVertexArray(mRenderer.FullscreenVertexArray()); - glUseProgram(mRenderer.OutputPackProgram()); - const GLint outputResolutionLocation = glGetUniformLocation(mRenderer.OutputPackProgram(), "uOutputVideoResolution"); - const GLint activeWordsLocation = glGetUniformLocation(mRenderer.OutputPackProgram(), "uActiveV210Words"); - if (outputResolutionLocation >= 0) - glUniform2f(outputResolutionLocation, static_cast(state.outputFrameSize.width), static_cast(state.outputFrameSize.height)); - if (activeWordsLocation >= 0) - glUniform1f(activeWordsLocation, static_cast(ActiveV210WordsForWidth(state.outputFrameSize.width))); - glDrawArrays(GL_TRIANGLES, 0, 3); - glUseProgram(0); - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); - } - glFlush(); - const auto renderEndTime = std::chrono::steady_clock::now(); - const double frameBudgetMilliseconds = state.frameBudgetMilliseconds; - const double renderMilliseconds = std::chrono::duration_cast>(renderEndTime - renderStartTime).count(); - mRuntimeHost.TrySetPerformanceStats(frameBudgetMilliseconds, renderMilliseconds); - mRuntimeHost.TryAdvanceFrame(); - - glPixelStorei(GL_PACK_ALIGNMENT, 4); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - if (state.outputPixelFormat == VideoIOPixelFormat::V210) - { - glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputPackFramebuffer()); - glReadPixels(0, 0, state.outputPackTextureWidth, state.outputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, outputFrame.bytes); - } - else - { - glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer()); - glReadPixels(0, 0, state.outputFrameSize.width, state.outputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, outputFrame.bytes); - } - mPaint(); + mRenderPipeline.RenderFrame(frameContext, outputFrame); mVideoIO.EndOutputFrame(outputFrame); diff --git a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.h b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.h index a317b6b..7cca4f8 100644 --- a/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.h +++ b/apps/LoopThroughWithOpenGLCompositing/gl/OpenGLVideoIOBridge.h @@ -1,39 +1,25 @@ #pragma once -#include "VideoIOTypes.h" +#include "OpenGLRenderPipeline.h" #include #include -#include #include -class OpenGLRenderer; class RuntimeHost; -struct RenderPipelineFrameContext -{ - VideoIOState videoState; - VideoIOCompletion completion; -}; - class OpenGLVideoIOBridge { public: - using RenderEffectCallback = std::function; - using OutputReadyCallback = std::function; - using PaintCallback = std::function; - OpenGLVideoIOBridge( VideoIODevice& videoIO, OpenGLRenderer& renderer, + OpenGLRenderPipeline& renderPipeline, RuntimeHost& runtimeHost, CRITICAL_SECTION& mutex, HDC hdc, - HGLRC hglrc, - RenderEffectCallback renderEffect, - OutputReadyCallback outputReady, - PaintCallback paint); + HGLRC hglrc); void VideoFrameArrived(const VideoIOFrame& inputFrame); void PlayoutFrameCompleted(const VideoIOCompletion& completion); @@ -43,13 +29,11 @@ private: VideoIODevice& mVideoIO; OpenGLRenderer& mRenderer; + OpenGLRenderPipeline& mRenderPipeline; RuntimeHost& mRuntimeHost; CRITICAL_SECTION& mMutex; HDC mHdc; HGLRC mHglrc; - RenderEffectCallback mRenderEffect; - OutputReadyCallback mOutputReady; - PaintCallback mPaint; std::chrono::steady_clock::time_point mLastPlayoutCompletionTime; double mCompletionIntervalMilliseconds = 0.0; double mSmoothedCompletionIntervalMilliseconds = 0.0;