Phase 4 step 2a
This commit is contained in:
@@ -64,6 +64,8 @@ set(APP_SOURCES
|
|||||||
"${APP_DIR}/gl/OpenGLComposite.cpp"
|
"${APP_DIR}/gl/OpenGLComposite.cpp"
|
||||||
"${APP_DIR}/gl/OpenGLComposite.h"
|
"${APP_DIR}/gl/OpenGLComposite.h"
|
||||||
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
|
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
|
||||||
|
"${APP_DIR}/gl/RenderCommandQueue.cpp"
|
||||||
|
"${APP_DIR}/gl/RenderCommandQueue.h"
|
||||||
"${APP_DIR}/gl/RenderEngine.cpp"
|
"${APP_DIR}/gl/RenderEngine.cpp"
|
||||||
"${APP_DIR}/gl/RenderEngine.h"
|
"${APP_DIR}/gl/RenderEngine.h"
|
||||||
"${APP_DIR}/gl/RenderFrameState.h"
|
"${APP_DIR}/gl/RenderFrameState.h"
|
||||||
@@ -368,6 +370,22 @@ endif()
|
|||||||
|
|
||||||
add_test(NAME Std140BufferTests COMMAND Std140BufferTests)
|
add_test(NAME Std140BufferTests COMMAND Std140BufferTests)
|
||||||
|
|
||||||
|
add_executable(RenderCommandQueueTests
|
||||||
|
"${APP_DIR}/gl/RenderCommandQueue.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RenderCommandQueueTests.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(RenderCommandQueueTests PRIVATE
|
||||||
|
"${APP_DIR}"
|
||||||
|
"${APP_DIR}/gl"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(RenderCommandQueueTests PRIVATE /W3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME RenderCommandQueueTests COMMAND RenderCommandQueueTests)
|
||||||
|
|
||||||
add_executable(ShaderPackageRegistryTests
|
add_executable(ShaderPackageRegistryTests
|
||||||
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
"${APP_DIR}/runtime/support/RuntimeJson.cpp"
|
||||||
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
"${APP_DIR}/shader/ShaderPackageRegistry.cpp"
|
||||||
|
|||||||
116
apps/LoopThroughWithOpenGLCompositing/gl/RenderCommandQueue.cpp
Normal file
116
apps/LoopThroughWithOpenGLCompositing/gl/RenderCommandQueue.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "RenderCommandQueue.h"
|
||||||
|
|
||||||
|
void RenderCommandQueue::RequestPreviewPresent(const RenderPreviewPresentRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (mHasPreviewPresentRequest)
|
||||||
|
++mCoalescedCount;
|
||||||
|
else
|
||||||
|
++mEnqueuedCount;
|
||||||
|
|
||||||
|
mPreviewPresentRequest = request;
|
||||||
|
mHasPreviewPresentRequest = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderCommandQueue::TryTakePreviewPresent(RenderPreviewPresentRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (!mHasPreviewPresentRequest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
request = mPreviewPresentRequest;
|
||||||
|
mPreviewPresentRequest = {};
|
||||||
|
mHasPreviewPresentRequest = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderCommandQueue::RequestScreenshotCapture(const RenderScreenshotCaptureRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (mHasScreenshotCaptureRequest)
|
||||||
|
++mCoalescedCount;
|
||||||
|
else
|
||||||
|
++mEnqueuedCount;
|
||||||
|
|
||||||
|
mScreenshotCaptureRequest = request;
|
||||||
|
mHasScreenshotCaptureRequest = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderCommandQueue::TryTakeScreenshotCapture(RenderScreenshotCaptureRequest& request)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (!mHasScreenshotCaptureRequest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
request = mScreenshotCaptureRequest;
|
||||||
|
mScreenshotCaptureRequest = {};
|
||||||
|
mHasScreenshotCaptureRequest = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderCommandQueue::RequestRenderReset(RenderCommandResetScope scope)
|
||||||
|
{
|
||||||
|
if (scope == RenderCommandResetScope::None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (mRenderResetScope != RenderCommandResetScope::None)
|
||||||
|
++mCoalescedCount;
|
||||||
|
else
|
||||||
|
++mEnqueuedCount;
|
||||||
|
|
||||||
|
mRenderResetScope = MergeResetScopes(mRenderResetScope, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderCommandQueue::TryTakeRenderReset(RenderCommandResetScope& scope)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (mRenderResetScope == RenderCommandResetScope::None)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
scope = mRenderResetScope;
|
||||||
|
mRenderResetScope = RenderCommandResetScope::None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderCommandQueueMetrics RenderCommandQueue::GetMetrics() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
RenderCommandQueueMetrics metrics;
|
||||||
|
metrics.depth =
|
||||||
|
(mHasPreviewPresentRequest ? 1u : 0u) +
|
||||||
|
(mHasScreenshotCaptureRequest ? 1u : 0u) +
|
||||||
|
(mRenderResetScope != RenderCommandResetScope::None ? 1u : 0u);
|
||||||
|
metrics.enqueuedCount = mEnqueuedCount;
|
||||||
|
metrics.coalescedCount = mCoalescedCount;
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderCommandResetScope RenderCommandQueue::MergeResetScopes(RenderCommandResetScope current, RenderCommandResetScope requested)
|
||||||
|
{
|
||||||
|
if (current == RenderCommandResetScope::TemporalHistoryAndFeedback ||
|
||||||
|
requested == RenderCommandResetScope::TemporalHistoryAndFeedback)
|
||||||
|
{
|
||||||
|
return RenderCommandResetScope::TemporalHistoryAndFeedback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((current == RenderCommandResetScope::TemporalHistoryOnly && requested == RenderCommandResetScope::ShaderFeedbackOnly) ||
|
||||||
|
(current == RenderCommandResetScope::ShaderFeedbackOnly && requested == RenderCommandResetScope::TemporalHistoryOnly))
|
||||||
|
{
|
||||||
|
return RenderCommandResetScope::TemporalHistoryAndFeedback;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current == RenderCommandResetScope::TemporalHistoryOnly ||
|
||||||
|
requested == RenderCommandResetScope::TemporalHistoryOnly)
|
||||||
|
{
|
||||||
|
return RenderCommandResetScope::TemporalHistoryOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current == RenderCommandResetScope::ShaderFeedbackOnly ||
|
||||||
|
requested == RenderCommandResetScope::ShaderFeedbackOnly)
|
||||||
|
{
|
||||||
|
return RenderCommandResetScope::ShaderFeedbackOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RenderCommandResetScope::None;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
enum class RenderCommandResetScope
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
ShaderFeedbackOnly,
|
||||||
|
TemporalHistoryOnly,
|
||||||
|
TemporalHistoryAndFeedback
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderPreviewPresentRequest
|
||||||
|
{
|
||||||
|
unsigned outputFrameWidth = 0;
|
||||||
|
unsigned outputFrameHeight = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderScreenshotCaptureRequest
|
||||||
|
{
|
||||||
|
unsigned width = 0;
|
||||||
|
unsigned height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderCommandQueueMetrics
|
||||||
|
{
|
||||||
|
std::size_t depth = 0;
|
||||||
|
uint64_t enqueuedCount = 0;
|
||||||
|
uint64_t coalescedCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenderCommandQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void RequestPreviewPresent(const RenderPreviewPresentRequest& request);
|
||||||
|
bool TryTakePreviewPresent(RenderPreviewPresentRequest& request);
|
||||||
|
|
||||||
|
void RequestScreenshotCapture(const RenderScreenshotCaptureRequest& request);
|
||||||
|
bool TryTakeScreenshotCapture(RenderScreenshotCaptureRequest& request);
|
||||||
|
|
||||||
|
void RequestRenderReset(RenderCommandResetScope scope);
|
||||||
|
bool TryTakeRenderReset(RenderCommandResetScope& scope);
|
||||||
|
|
||||||
|
RenderCommandQueueMetrics GetMetrics() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static RenderCommandResetScope MergeResetScopes(RenderCommandResetScope current, RenderCommandResetScope requested);
|
||||||
|
|
||||||
|
mutable std::mutex mMutex;
|
||||||
|
bool mHasPreviewPresentRequest = false;
|
||||||
|
RenderPreviewPresentRequest mPreviewPresentRequest;
|
||||||
|
bool mHasScreenshotCaptureRequest = false;
|
||||||
|
RenderScreenshotCaptureRequest mScreenshotCaptureRequest;
|
||||||
|
RenderCommandResetScope mRenderResetScope = RenderCommandResetScope::None;
|
||||||
|
uint64_t mEnqueuedCount = 0;
|
||||||
|
uint64_t mCoalescedCount = 0;
|
||||||
|
};
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "RenderEngine.h"
|
#include "RenderEngine.h"
|
||||||
|
|
||||||
#include "ShaderBuildQueue.h"
|
|
||||||
|
|
||||||
#include <gl/gl.h>
|
#include <gl/gl.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -88,48 +86,16 @@ bool RenderEngine::ApplyPreparedShaderBuild(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderEngine::PreparedShaderBuildApplyResult RenderEngine::TryApplyReadyShaderBuild(
|
|
||||||
ShaderBuildQueue& shaderBuildQueue,
|
|
||||||
unsigned inputFrameWidth,
|
|
||||||
unsigned inputFrameHeight,
|
|
||||||
bool preserveFeedbackState)
|
|
||||||
{
|
|
||||||
PreparedShaderBuildApplyResult result;
|
|
||||||
PreparedShaderBuild readyBuild;
|
|
||||||
if (!shaderBuildQueue.TryConsumeReadyBuild(readyBuild))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
result.hadReadyBuild = true;
|
|
||||||
char compilerErrorMessage[1024] = {};
|
|
||||||
if (!ApplyPreparedShaderBuild(
|
|
||||||
readyBuild,
|
|
||||||
inputFrameWidth,
|
|
||||||
inputFrameHeight,
|
|
||||||
preserveFeedbackState,
|
|
||||||
sizeof(compilerErrorMessage),
|
|
||||||
compilerErrorMessage))
|
|
||||||
{
|
|
||||||
result.errorMessage = compilerErrorMessage;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.applied = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<RuntimeRenderState>& RenderEngine::CommittedLayerStates() const
|
|
||||||
{
|
|
||||||
return mShaderPrograms.CommittedLayerStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderEngine::ResetTemporalHistoryState()
|
void RenderEngine::ResetTemporalHistoryState()
|
||||||
{
|
{
|
||||||
mShaderPrograms.ResetTemporalHistoryState();
|
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryOnly);
|
||||||
|
ProcessRenderResetCommandsOnRenderThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderEngine::ResetShaderFeedbackState()
|
void RenderEngine::ResetShaderFeedbackState()
|
||||||
{
|
{
|
||||||
mShaderPrograms.ResetShaderFeedbackState();
|
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::ShaderFeedbackOnly);
|
||||||
|
ProcessRenderResetCommandsOnRenderThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderEngine::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope)
|
void RenderEngine::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope)
|
||||||
@@ -137,11 +103,12 @@ void RenderEngine::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderRe
|
|||||||
switch (resetScope)
|
switch (resetScope)
|
||||||
{
|
{
|
||||||
case RuntimeCoordinatorRenderResetScope::TemporalHistoryOnly:
|
case RuntimeCoordinatorRenderResetScope::TemporalHistoryOnly:
|
||||||
ResetTemporalHistoryState();
|
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryOnly);
|
||||||
|
ProcessRenderResetCommandsOnRenderThread();
|
||||||
break;
|
break;
|
||||||
case RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback:
|
case RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback:
|
||||||
ResetTemporalHistoryState();
|
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryAndFeedback);
|
||||||
ResetShaderFeedbackState();
|
ProcessRenderResetCommandsOnRenderThread();
|
||||||
break;
|
break;
|
||||||
case RuntimeCoordinatorRenderResetScope::None:
|
case RuntimeCoordinatorRenderResetScope::None:
|
||||||
default:
|
default:
|
||||||
@@ -149,6 +116,43 @@ void RenderEngine::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderEngine::ResetTemporalHistoryStateOnRenderThread()
|
||||||
|
{
|
||||||
|
mShaderPrograms.ResetTemporalHistoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderEngine::ResetShaderFeedbackStateOnRenderThread()
|
||||||
|
{
|
||||||
|
mShaderPrograms.ResetShaderFeedbackState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderEngine::ApplyRenderResetOnRenderThread(RenderCommandResetScope resetScope)
|
||||||
|
{
|
||||||
|
switch (resetScope)
|
||||||
|
{
|
||||||
|
case RenderCommandResetScope::ShaderFeedbackOnly:
|
||||||
|
ResetShaderFeedbackStateOnRenderThread();
|
||||||
|
break;
|
||||||
|
case RenderCommandResetScope::TemporalHistoryOnly:
|
||||||
|
ResetTemporalHistoryStateOnRenderThread();
|
||||||
|
break;
|
||||||
|
case RenderCommandResetScope::TemporalHistoryAndFeedback:
|
||||||
|
ResetTemporalHistoryStateOnRenderThread();
|
||||||
|
ResetShaderFeedbackStateOnRenderThread();
|
||||||
|
break;
|
||||||
|
case RenderCommandResetScope::None:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderEngine::ProcessRenderResetCommandsOnRenderThread()
|
||||||
|
{
|
||||||
|
RenderCommandResetScope resetScope = RenderCommandResetScope::None;
|
||||||
|
while (mRenderCommandQueue.TryTakeRenderReset(resetScope))
|
||||||
|
ApplyRenderResetOnRenderThread(resetScope);
|
||||||
|
}
|
||||||
|
|
||||||
void RenderEngine::ClearOscOverlayState()
|
void RenderEngine::ClearOscOverlayState()
|
||||||
{
|
{
|
||||||
mRuntimeLiveState.Clear();
|
mRuntimeLiveState.Clear();
|
||||||
@@ -195,7 +199,10 @@ bool RenderEngine::TryPresentPreview(bool force, unsigned previewFps, unsigned o
|
|||||||
if (!TryEnterCriticalSection(&mMutex))
|
if (!TryEnterCriticalSection(&mMutex))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const bool presented = PresentPreviewOnRenderThread(outputFrameWidth, outputFrameHeight);
|
mRenderCommandQueue.RequestPreviewPresent({ outputFrameWidth, outputFrameHeight });
|
||||||
|
RenderPreviewPresentRequest request;
|
||||||
|
const bool presented = mRenderCommandQueue.TryTakePreviewPresent(request) &&
|
||||||
|
PresentPreviewOnRenderThread(request.outputFrameWidth, request.outputFrameHeight);
|
||||||
LeaveCriticalSection(&mMutex);
|
LeaveCriticalSection(&mMutex);
|
||||||
return presented;
|
return presented;
|
||||||
}
|
}
|
||||||
@@ -252,6 +259,7 @@ bool RenderEngine::RenderOutputFrame(const RenderPipelineFrameContext& context,
|
|||||||
|
|
||||||
bool RenderEngine::RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame)
|
bool RenderEngine::RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame)
|
||||||
{
|
{
|
||||||
|
ProcessRenderResetCommandsOnRenderThread();
|
||||||
return mRenderPipeline.RenderFrame(context, outputFrame);
|
return mRenderPipeline.RenderFrame(context, outputFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +341,10 @@ bool RenderEngine::CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height
|
|||||||
{
|
{
|
||||||
EnterCriticalSection(&mMutex);
|
EnterCriticalSection(&mMutex);
|
||||||
wglMakeCurrent(mHdc, mHglrc);
|
wglMakeCurrent(mHdc, mHglrc);
|
||||||
const bool captured = CaptureOutputFrameRgbaTopDownOnRenderThread(width, height, topDownPixels);
|
mRenderCommandQueue.RequestScreenshotCapture({ width, height });
|
||||||
|
RenderScreenshotCaptureRequest request;
|
||||||
|
const bool captured = mRenderCommandQueue.TryTakeScreenshotCapture(request) &&
|
||||||
|
CaptureOutputFrameRgbaTopDownOnRenderThread(request.width, request.height, topDownPixels);
|
||||||
wglMakeCurrent(NULL, NULL);
|
wglMakeCurrent(NULL, NULL);
|
||||||
LeaveCriticalSection(&mMutex);
|
LeaveCriticalSection(&mMutex);
|
||||||
return captured;
|
return captured;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "OpenGLRenderPipeline.h"
|
#include "OpenGLRenderPipeline.h"
|
||||||
#include "OpenGLRenderer.h"
|
#include "OpenGLRenderer.h"
|
||||||
#include "OpenGLShaderPrograms.h"
|
#include "OpenGLShaderPrograms.h"
|
||||||
|
#include "RenderCommandQueue.h"
|
||||||
#include "RenderFrameState.h"
|
#include "RenderFrameState.h"
|
||||||
#include "RenderFrameStateResolver.h"
|
#include "RenderFrameStateResolver.h"
|
||||||
#include "HealthTelemetry.h"
|
#include "HealthTelemetry.h"
|
||||||
@@ -18,8 +19,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class ShaderBuildQueue;
|
|
||||||
|
|
||||||
class RenderEngine
|
class RenderEngine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -50,13 +49,6 @@ public:
|
|||||||
uint64_t generation = 0;
|
uint64_t generation = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PreparedShaderBuildApplyResult
|
|
||||||
{
|
|
||||||
bool hadReadyBuild = false;
|
|
||||||
bool applied = false;
|
|
||||||
std::string errorMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
RenderEngine(
|
RenderEngine(
|
||||||
RuntimeSnapshotProvider& runtimeSnapshotProvider,
|
RuntimeSnapshotProvider& runtimeSnapshotProvider,
|
||||||
HealthTelemetry& healthTelemetry,
|
HealthTelemetry& healthTelemetry,
|
||||||
@@ -79,7 +71,6 @@ public:
|
|||||||
unsigned outputPackTextureWidth,
|
unsigned outputPackTextureWidth,
|
||||||
std::string& error);
|
std::string& error);
|
||||||
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
bool CompileLayerPrograms(unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
||||||
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
|
||||||
bool ApplyPreparedShaderBuild(
|
bool ApplyPreparedShaderBuild(
|
||||||
const PreparedShaderBuild& preparedBuild,
|
const PreparedShaderBuild& preparedBuild,
|
||||||
unsigned inputFrameWidth,
|
unsigned inputFrameWidth,
|
||||||
@@ -87,13 +78,7 @@ public:
|
|||||||
bool preserveFeedbackState,
|
bool preserveFeedbackState,
|
||||||
int errorMessageSize,
|
int errorMessageSize,
|
||||||
char* errorMessage);
|
char* errorMessage);
|
||||||
PreparedShaderBuildApplyResult TryApplyReadyShaderBuild(
|
|
||||||
ShaderBuildQueue& shaderBuildQueue,
|
|
||||||
unsigned inputFrameWidth,
|
|
||||||
unsigned inputFrameHeight,
|
|
||||||
bool preserveFeedbackState);
|
|
||||||
|
|
||||||
const std::vector<RuntimeRenderState>& CommittedLayerStates() const;
|
|
||||||
void ResetTemporalHistoryState();
|
void ResetTemporalHistoryState();
|
||||||
void ResetShaderFeedbackState();
|
void ResetShaderFeedbackState();
|
||||||
void ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope);
|
void ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope);
|
||||||
@@ -110,6 +95,10 @@ public:
|
|||||||
std::vector<OscOverlayCommitRequest>* commitRequests,
|
std::vector<OscOverlayCommitRequest>* commitRequests,
|
||||||
RenderFrameState& frameState);
|
RenderFrameState& frameState);
|
||||||
void RenderPreparedFrame(const RenderFrameState& frameState);
|
void RenderPreparedFrame(const RenderFrameState& frameState);
|
||||||
|
bool CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height, std::vector<unsigned char>& topDownPixels);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool CommitPreparedLayerPrograms(const PreparedShaderBuild& preparedBuild, unsigned inputFrameWidth, unsigned inputFrameHeight, int errorMessageSize, char* errorMessage);
|
||||||
void RenderLayerStack(
|
void RenderLayerStack(
|
||||||
bool hasInputSource,
|
bool hasInputSource,
|
||||||
const std::vector<RuntimeRenderState>& layerStates,
|
const std::vector<RuntimeRenderState>& layerStates,
|
||||||
@@ -118,9 +107,10 @@ public:
|
|||||||
unsigned captureTextureWidth,
|
unsigned captureTextureWidth,
|
||||||
VideoIOPixelFormat inputPixelFormat,
|
VideoIOPixelFormat inputPixelFormat,
|
||||||
unsigned historyCap);
|
unsigned historyCap);
|
||||||
bool CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height, std::vector<unsigned char>& topDownPixels);
|
void ResetTemporalHistoryStateOnRenderThread();
|
||||||
|
void ResetShaderFeedbackStateOnRenderThread();
|
||||||
private:
|
void ApplyRenderResetOnRenderThread(RenderCommandResetScope resetScope);
|
||||||
|
void ProcessRenderResetCommandsOnRenderThread();
|
||||||
bool PresentPreviewOnRenderThread(unsigned outputFrameWidth, unsigned outputFrameHeight);
|
bool PresentPreviewOnRenderThread(unsigned outputFrameWidth, unsigned outputFrameHeight);
|
||||||
bool UploadInputFrameOnRenderThread(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
|
bool UploadInputFrameOnRenderThread(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
|
||||||
bool RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
|
bool RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
|
||||||
@@ -136,6 +126,7 @@ private:
|
|||||||
HGLRC mHglrc;
|
HGLRC mHglrc;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point mLastPreviewPresentTime;
|
std::chrono::steady_clock::time_point mLastPreviewPresentTime;
|
||||||
|
RenderCommandQueue mRenderCommandQueue;
|
||||||
RenderFrameStateResolver mFrameStateResolver;
|
RenderFrameStateResolver mFrameStateResolver;
|
||||||
RuntimeLiveState mRuntimeLiveState;
|
RuntimeLiveState mRuntimeLiveState;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ Phase 1 named the subsystems. Phase 2 added the typed event substrate. Phase 3 m
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
- Phase 4 design package: proposed.
|
- Phase 4 design package: proposed.
|
||||||
- Phase 4 implementation: Step 1 started. The existing synchronous `RenderEngine` entrypoints now delegate their GL bodies to named `...OnRenderThread(...)` helpers, but no command queue or dedicated render thread exists yet.
|
- Phase 4 implementation: Step 2 started. The existing synchronous `RenderEngine` entrypoints delegate their GL bodies to named `...OnRenderThread(...)` helpers, and preview/screenshot/render-reset requests now pass through a small `RenderCommandQueue` compatibility mailbox. No dedicated render thread exists yet.
|
||||||
- Current alignment: the repo has a named frame-state contract and cleaner render-state preparation, but GL work is still entered through multiple paths protected by one shared `CRITICAL_SECTION`.
|
- Current alignment: the repo has a named frame-state contract and cleaner render-state preparation, but GL work is still entered through multiple paths protected by one shared `CRITICAL_SECTION`.
|
||||||
|
|
||||||
Current GL ownership footholds:
|
Current GL ownership footholds:
|
||||||
|
|
||||||
- `RenderEngine` owns GL resources, the current context-binding compatibility shims, and named render-thread helper methods.
|
- `RenderEngine` owns GL resources, the current context-binding compatibility shims, a small render command mailbox, and named render-thread helper methods.
|
||||||
- `RenderFrameInput` / `RenderFrameState` provide the frame-state contract that a render thread can consume.
|
- `RenderFrameInput` / `RenderFrameState` provide the frame-state contract that a render thread can consume.
|
||||||
- `RenderFrameStateResolver` prepares the render-facing layer state before drawing.
|
- `RenderFrameStateResolver` prepares the render-facing layer state before drawing.
|
||||||
- `OpenGLVideoIOBridge` still calls `RenderEngine::TryUploadInputFrame(...)` from the input path and `RenderEngine::RenderOutputFrame(...)` from the output path.
|
- `OpenGLVideoIOBridge` still calls `RenderEngine::TryUploadInputFrame(...)` from the input path and `RenderEngine::RenderOutputFrame(...)` from the output path.
|
||||||
@@ -129,6 +129,13 @@ Non-responsibilities:
|
|||||||
|
|
||||||
Small bounded queue or command mailbox for render-thread work.
|
Small bounded queue or command mailbox for render-thread work.
|
||||||
|
|
||||||
|
Current implementation:
|
||||||
|
|
||||||
|
- `RenderCommandQueue` exists as a pure C++ mailbox helper.
|
||||||
|
- Preview present and screenshot capture requests use latest-value coalescing.
|
||||||
|
- Render-local reset requests coalesce to the strongest pending reset scope.
|
||||||
|
- `RenderEngine` drains these commands synchronously as compatibility shims until a dedicated render thread is introduced.
|
||||||
|
|
||||||
Possible commands:
|
Possible commands:
|
||||||
|
|
||||||
- `UploadInputFrame`
|
- `UploadInputFrame`
|
||||||
@@ -224,9 +231,9 @@ Introduce a small queue/mailbox for render commands.
|
|||||||
|
|
||||||
Start with low-risk commands:
|
Start with low-risk commands:
|
||||||
|
|
||||||
- preview present request
|
- [x] preview present request
|
||||||
- screenshot request
|
- [x] screenshot request
|
||||||
- render-local reset requests
|
- [x] render-local reset requests
|
||||||
|
|
||||||
Then move input upload and output render requests once the queue and wakeup behavior are proven.
|
Then move input upload and output render requests once the queue and wakeup behavior are proven.
|
||||||
|
|
||||||
|
|||||||
89
tests/RenderCommandQueueTests.cpp
Normal file
89
tests/RenderCommandQueueTests.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "RenderCommandQueue.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int gFailures = 0;
|
||||||
|
|
||||||
|
void Expect(bool condition, const char* message)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::cerr << "FAIL: " << message << "\n";
|
||||||
|
++gFailures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestPreviewRequestUsesLatestValue()
|
||||||
|
{
|
||||||
|
RenderCommandQueue queue;
|
||||||
|
queue.RequestPreviewPresent({ 1920, 1080 });
|
||||||
|
queue.RequestPreviewPresent({ 1280, 720 });
|
||||||
|
|
||||||
|
const RenderCommandQueueMetrics metrics = queue.GetMetrics();
|
||||||
|
Expect(metrics.depth == 1, "preview requests coalesce to one pending command");
|
||||||
|
Expect(metrics.enqueuedCount == 1, "first preview request is counted as enqueued");
|
||||||
|
Expect(metrics.coalescedCount == 1, "second preview request is counted as coalesced");
|
||||||
|
|
||||||
|
RenderPreviewPresentRequest request;
|
||||||
|
Expect(queue.TryTakePreviewPresent(request), "preview request can be consumed");
|
||||||
|
Expect(request.outputFrameWidth == 1280 && request.outputFrameHeight == 720, "latest preview request wins");
|
||||||
|
Expect(!queue.TryTakePreviewPresent(request), "preview request is removed after consume");
|
||||||
|
Expect(queue.GetMetrics().depth == 0, "preview consume empties queue depth");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestScreenshotRequestUsesLatestValue()
|
||||||
|
{
|
||||||
|
RenderCommandQueue queue;
|
||||||
|
queue.RequestScreenshotCapture({ 640, 360 });
|
||||||
|
queue.RequestScreenshotCapture({ 3840, 2160 });
|
||||||
|
|
||||||
|
RenderScreenshotCaptureRequest request;
|
||||||
|
Expect(queue.TryTakeScreenshotCapture(request), "screenshot request can be consumed");
|
||||||
|
Expect(request.width == 3840 && request.height == 2160, "latest screenshot request wins");
|
||||||
|
Expect(!queue.TryTakeScreenshotCapture(request), "screenshot request is removed after consume");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRenderResetScopesCoalesceToStrongestRequest()
|
||||||
|
{
|
||||||
|
RenderCommandQueue queue;
|
||||||
|
queue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryOnly);
|
||||||
|
queue.RequestRenderReset(RenderCommandResetScope::ShaderFeedbackOnly);
|
||||||
|
|
||||||
|
RenderCommandResetScope scope = RenderCommandResetScope::None;
|
||||||
|
Expect(queue.TryTakeRenderReset(scope), "render reset request can be consumed");
|
||||||
|
Expect(scope == RenderCommandResetScope::TemporalHistoryAndFeedback, "temporal and feedback reset requests merge");
|
||||||
|
Expect(!queue.TryTakeRenderReset(scope), "render reset request is removed after consume");
|
||||||
|
|
||||||
|
queue.RequestRenderReset(RenderCommandResetScope::None);
|
||||||
|
Expect(queue.GetMetrics().depth == 0, "none reset request is ignored");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestIndependentCommandKindsShareDepth()
|
||||||
|
{
|
||||||
|
RenderCommandQueue queue;
|
||||||
|
queue.RequestPreviewPresent({ 1, 2 });
|
||||||
|
queue.RequestScreenshotCapture({ 3, 4 });
|
||||||
|
queue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryOnly);
|
||||||
|
|
||||||
|
Expect(queue.GetMetrics().depth == 3, "independent command kinds each contribute to depth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TestPreviewRequestUsesLatestValue();
|
||||||
|
TestScreenshotRequestUsesLatestValue();
|
||||||
|
TestRenderResetScopesCoalesceToStrongestRequest();
|
||||||
|
TestIndependentCommandKindsShareDepth();
|
||||||
|
|
||||||
|
if (gFailures != 0)
|
||||||
|
{
|
||||||
|
std::cerr << gFailures << " RenderCommandQueue test failure(s).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "RenderCommandQueue tests passed.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user