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;