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.h"
|
||||
"${APP_DIR}/gl/OpenGLCompositeRuntimeControls.cpp"
|
||||
"${APP_DIR}/gl/RenderCommandQueue.cpp"
|
||||
"${APP_DIR}/gl/RenderCommandQueue.h"
|
||||
"${APP_DIR}/gl/RenderEngine.cpp"
|
||||
"${APP_DIR}/gl/RenderEngine.h"
|
||||
"${APP_DIR}/gl/RenderFrameState.h"
|
||||
@@ -368,6 +370,22 @@ endif()
|
||||
|
||||
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
|
||||
"${APP_DIR}/runtime/support/RuntimeJson.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 "ShaderBuildQueue.h"
|
||||
|
||||
#include <gl/gl.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -88,48 +86,16 @@ bool RenderEngine::ApplyPreparedShaderBuild(
|
||||
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()
|
||||
{
|
||||
mShaderPrograms.ResetTemporalHistoryState();
|
||||
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryOnly);
|
||||
ProcessRenderResetCommandsOnRenderThread();
|
||||
}
|
||||
|
||||
void RenderEngine::ResetShaderFeedbackState()
|
||||
{
|
||||
mShaderPrograms.ResetShaderFeedbackState();
|
||||
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::ShaderFeedbackOnly);
|
||||
ProcessRenderResetCommandsOnRenderThread();
|
||||
}
|
||||
|
||||
void RenderEngine::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope)
|
||||
@@ -137,11 +103,12 @@ void RenderEngine::ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderRe
|
||||
switch (resetScope)
|
||||
{
|
||||
case RuntimeCoordinatorRenderResetScope::TemporalHistoryOnly:
|
||||
ResetTemporalHistoryState();
|
||||
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryOnly);
|
||||
ProcessRenderResetCommandsOnRenderThread();
|
||||
break;
|
||||
case RuntimeCoordinatorRenderResetScope::TemporalHistoryAndFeedback:
|
||||
ResetTemporalHistoryState();
|
||||
ResetShaderFeedbackState();
|
||||
mRenderCommandQueue.RequestRenderReset(RenderCommandResetScope::TemporalHistoryAndFeedback);
|
||||
ProcessRenderResetCommandsOnRenderThread();
|
||||
break;
|
||||
case RuntimeCoordinatorRenderResetScope::None:
|
||||
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()
|
||||
{
|
||||
mRuntimeLiveState.Clear();
|
||||
@@ -195,7 +199,10 @@ bool RenderEngine::TryPresentPreview(bool force, unsigned previewFps, unsigned o
|
||||
if (!TryEnterCriticalSection(&mMutex))
|
||||
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);
|
||||
return presented;
|
||||
}
|
||||
@@ -252,6 +259,7 @@ bool RenderEngine::RenderOutputFrame(const RenderPipelineFrameContext& context,
|
||||
|
||||
bool RenderEngine::RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame)
|
||||
{
|
||||
ProcessRenderResetCommandsOnRenderThread();
|
||||
return mRenderPipeline.RenderFrame(context, outputFrame);
|
||||
}
|
||||
|
||||
@@ -333,7 +341,10 @@ bool RenderEngine::CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height
|
||||
{
|
||||
EnterCriticalSection(&mMutex);
|
||||
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);
|
||||
LeaveCriticalSection(&mMutex);
|
||||
return captured;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "OpenGLRenderPipeline.h"
|
||||
#include "OpenGLRenderer.h"
|
||||
#include "OpenGLShaderPrograms.h"
|
||||
#include "RenderCommandQueue.h"
|
||||
#include "RenderFrameState.h"
|
||||
#include "RenderFrameStateResolver.h"
|
||||
#include "HealthTelemetry.h"
|
||||
@@ -18,8 +19,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ShaderBuildQueue;
|
||||
|
||||
class RenderEngine
|
||||
{
|
||||
public:
|
||||
@@ -50,13 +49,6 @@ public:
|
||||
uint64_t generation = 0;
|
||||
};
|
||||
|
||||
struct PreparedShaderBuildApplyResult
|
||||
{
|
||||
bool hadReadyBuild = false;
|
||||
bool applied = false;
|
||||
std::string errorMessage;
|
||||
};
|
||||
|
||||
RenderEngine(
|
||||
RuntimeSnapshotProvider& runtimeSnapshotProvider,
|
||||
HealthTelemetry& healthTelemetry,
|
||||
@@ -79,7 +71,6 @@ public:
|
||||
unsigned outputPackTextureWidth,
|
||||
std::string& error);
|
||||
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(
|
||||
const PreparedShaderBuild& preparedBuild,
|
||||
unsigned inputFrameWidth,
|
||||
@@ -87,13 +78,7 @@ public:
|
||||
bool preserveFeedbackState,
|
||||
int errorMessageSize,
|
||||
char* errorMessage);
|
||||
PreparedShaderBuildApplyResult TryApplyReadyShaderBuild(
|
||||
ShaderBuildQueue& shaderBuildQueue,
|
||||
unsigned inputFrameWidth,
|
||||
unsigned inputFrameHeight,
|
||||
bool preserveFeedbackState);
|
||||
|
||||
const std::vector<RuntimeRenderState>& CommittedLayerStates() const;
|
||||
void ResetTemporalHistoryState();
|
||||
void ResetShaderFeedbackState();
|
||||
void ApplyRuntimeCoordinatorRenderReset(RuntimeCoordinatorRenderResetScope resetScope);
|
||||
@@ -110,6 +95,10 @@ public:
|
||||
std::vector<OscOverlayCommitRequest>* commitRequests,
|
||||
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(
|
||||
bool hasInputSource,
|
||||
const std::vector<RuntimeRenderState>& layerStates,
|
||||
@@ -118,9 +107,10 @@ public:
|
||||
unsigned captureTextureWidth,
|
||||
VideoIOPixelFormat inputPixelFormat,
|
||||
unsigned historyCap);
|
||||
bool CaptureOutputFrameRgbaTopDown(unsigned width, unsigned height, std::vector<unsigned char>& topDownPixels);
|
||||
|
||||
private:
|
||||
void ResetTemporalHistoryStateOnRenderThread();
|
||||
void ResetShaderFeedbackStateOnRenderThread();
|
||||
void ApplyRenderResetOnRenderThread(RenderCommandResetScope resetScope);
|
||||
void ProcessRenderResetCommandsOnRenderThread();
|
||||
bool PresentPreviewOnRenderThread(unsigned outputFrameWidth, unsigned outputFrameHeight);
|
||||
bool UploadInputFrameOnRenderThread(const VideoIOFrame& inputFrame, const VideoIOState& videoState);
|
||||
bool RenderOutputFrameOnRenderThread(const RenderPipelineFrameContext& context, VideoIOOutputFrame& outputFrame);
|
||||
@@ -136,6 +126,7 @@ private:
|
||||
HGLRC mHglrc;
|
||||
|
||||
std::chrono::steady_clock::time_point mLastPreviewPresentTime;
|
||||
RenderCommandQueue mRenderCommandQueue;
|
||||
RenderFrameStateResolver mFrameStateResolver;
|
||||
RuntimeLiveState mRuntimeLiveState;
|
||||
};
|
||||
|
||||
@@ -7,12 +7,12 @@ Phase 1 named the subsystems. Phase 2 added the typed event substrate. Phase 3 m
|
||||
## Status
|
||||
|
||||
- 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 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.
|
||||
- `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.
|
||||
@@ -129,6 +129,13 @@ Non-responsibilities:
|
||||
|
||||
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:
|
||||
|
||||
- `UploadInputFrame`
|
||||
@@ -224,9 +231,9 @@ Introduce a small queue/mailbox for render commands.
|
||||
|
||||
Start with low-risk commands:
|
||||
|
||||
- preview present request
|
||||
- screenshot request
|
||||
- render-local reset requests
|
||||
- [x] preview present request
|
||||
- [x] screenshot request
|
||||
- [x] render-local reset requests
|
||||
|
||||
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