Tests
This commit is contained in:
@@ -7,6 +7,11 @@
|
|||||||
#include "RuntimeStore.h"
|
#include "RuntimeStore.h"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr auto kCompatibilityPollFallbackInterval = std::chrono::milliseconds(250);
|
||||||
|
}
|
||||||
|
|
||||||
ControlServices::ControlServices(RuntimeEventDispatcher& runtimeEventDispatcher) :
|
ControlServices::ControlServices(RuntimeEventDispatcher& runtimeEventDispatcher) :
|
||||||
mControlServer(std::make_unique<ControlServer>()),
|
mControlServer(std::make_unique<ControlServer>()),
|
||||||
mOscServer(std::make_unique<OscServer>()),
|
mOscServer(std::make_unique<OscServer>()),
|
||||||
@@ -130,6 +135,7 @@ bool ControlServices::QueueOscCommit(const std::string& routeKey, const std::str
|
|||||||
std::lock_guard<std::mutex> lock(mPendingOscCommitMutex);
|
std::lock_guard<std::mutex> lock(mPendingOscCommitMutex);
|
||||||
mPendingOscCommits[routeKey] = std::move(commit);
|
mPendingOscCommits[routeKey] = std::move(commit);
|
||||||
}
|
}
|
||||||
|
WakePolling();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +190,7 @@ void ControlServices::StopPolling()
|
|||||||
if (!mPollRunning.exchange(false))
|
if (!mPollRunning.exchange(false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
WakePolling();
|
||||||
if (mPollThread.joinable())
|
if (mPollThread.joinable())
|
||||||
mPollThread.join();
|
mPollThread.join();
|
||||||
}
|
}
|
||||||
@@ -224,11 +231,23 @@ void ControlServices::PollLoop(RuntimeCoordinator& runtimeCoordinator)
|
|||||||
if (pollResult.runtimeStateBroadcastRequired || pollResult.shaderBuildRequested || pollResult.compileStatusChanged)
|
if (pollResult.runtimeStateBroadcastRequired || pollResult.shaderBuildRequested || pollResult.compileStatusChanged)
|
||||||
QueueRuntimeCoordinatorResult(pollResult, pollResult.compileStatusChanged && !pollResult.compileStatusSucceeded && !pollResult.compileStatusMessage.empty());
|
QueueRuntimeCoordinatorResult(pollResult, pollResult.compileStatusChanged && !pollResult.compileStatusSucceeded && !pollResult.compileStatusMessage.empty());
|
||||||
|
|
||||||
for (int i = 0; i < 25 && mPollRunning; ++i)
|
std::unique_lock<std::mutex> wakeLock(mPollWakeMutex);
|
||||||
Sleep(10);
|
mPollWakeCondition.wait_for(wakeLock, kCompatibilityPollFallbackInterval, [this]() {
|
||||||
|
return !mPollRunning.load() || mPollWakeRequested;
|
||||||
|
});
|
||||||
|
mPollWakeRequested = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlServices::WakePolling()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mPollWakeMutex);
|
||||||
|
mPollWakeRequested = true;
|
||||||
|
}
|
||||||
|
mPollWakeCondition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
void ControlServices::QueueRuntimeCoordinatorResult(const RuntimeCoordinatorResult& result, bool failed)
|
void ControlServices::QueueRuntimeCoordinatorResult(const RuntimeCoordinatorResult& result, bool failed)
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorServiceResult serviceResult;
|
RuntimeCoordinatorServiceResult serviceResult;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "ShaderTypes.h"
|
#include "ShaderTypes.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <chrono>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -76,6 +78,7 @@ private:
|
|||||||
void StartPolling(RuntimeCoordinator& runtimeCoordinator);
|
void StartPolling(RuntimeCoordinator& runtimeCoordinator);
|
||||||
void StopPolling();
|
void StopPolling();
|
||||||
void PollLoop(RuntimeCoordinator& runtimeCoordinator);
|
void PollLoop(RuntimeCoordinator& runtimeCoordinator);
|
||||||
|
void WakePolling();
|
||||||
void QueueRuntimeCoordinatorResult(const RuntimeCoordinatorResult& result, bool failed = false);
|
void QueueRuntimeCoordinatorResult(const RuntimeCoordinatorResult& result, bool failed = false);
|
||||||
void PublishRuntimeStateBroadcastRequested(const std::string& reason);
|
void PublishRuntimeStateBroadcastRequested(const std::string& reason);
|
||||||
void PublishOscValueReceived(const PendingOscUpdate& update, const std::string& routeKey);
|
void PublishOscValueReceived(const PendingOscUpdate& update, const std::string& routeKey);
|
||||||
@@ -86,6 +89,9 @@ private:
|
|||||||
RuntimeEventDispatcher& mRuntimeEventDispatcher;
|
RuntimeEventDispatcher& mRuntimeEventDispatcher;
|
||||||
std::thread mPollThread;
|
std::thread mPollThread;
|
||||||
std::atomic<bool> mPollRunning;
|
std::atomic<bool> mPollRunning;
|
||||||
|
std::mutex mPollWakeMutex;
|
||||||
|
std::condition_variable mPollWakeCondition;
|
||||||
|
bool mPollWakeRequested = false;
|
||||||
std::mutex mRuntimeCoordinatorResultMutex;
|
std::mutex mRuntimeCoordinatorResultMutex;
|
||||||
std::vector<RuntimeCoordinatorServiceResult> mRuntimeCoordinatorResults;
|
std::vector<RuntimeCoordinatorServiceResult> mRuntimeCoordinatorResults;
|
||||||
std::mutex mPendingOscMutex;
|
std::mutex mPendingOscMutex;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
|||||||
InitializeCriticalSection(&pMutex);
|
InitializeCriticalSection(&pMutex);
|
||||||
mRuntimeStore = std::make_unique<RuntimeStore>();
|
mRuntimeStore = std::make_unique<RuntimeStore>();
|
||||||
mRuntimeEventDispatcher = std::make_unique<RuntimeEventDispatcher>();
|
mRuntimeEventDispatcher = std::make_unique<RuntimeEventDispatcher>();
|
||||||
mRuntimeSnapshotProvider = std::make_unique<RuntimeSnapshotProvider>(mRuntimeStore->GetRenderSnapshotBuilder());
|
mRuntimeSnapshotProvider = std::make_unique<RuntimeSnapshotProvider>(mRuntimeStore->GetRenderSnapshotBuilder(), *mRuntimeEventDispatcher);
|
||||||
mRuntimeCoordinator = std::make_unique<RuntimeCoordinator>(*mRuntimeStore, *mRuntimeEventDispatcher);
|
mRuntimeCoordinator = std::make_unique<RuntimeCoordinator>(*mRuntimeStore, *mRuntimeEventDispatcher);
|
||||||
mRenderEngine = std::make_unique<RenderEngine>(
|
mRenderEngine = std::make_unique<RenderEngine>(
|
||||||
*mRuntimeSnapshotProvider,
|
*mRuntimeSnapshotProvider,
|
||||||
@@ -49,7 +49,7 @@ OpenGLComposite::OpenGLComposite(HWND hWnd, HDC hDC, HGLRC hRC) :
|
|||||||
[this]() { renderEffect(); },
|
[this]() { renderEffect(); },
|
||||||
[this]() { ProcessScreenshotRequest(); },
|
[this]() { ProcessScreenshotRequest(); },
|
||||||
[this]() { paintGL(false); });
|
[this]() { paintGL(false); });
|
||||||
mVideoBackend = std::make_unique<VideoBackend>(*mRenderEngine, mRuntimeStore->GetHealthTelemetry());
|
mVideoBackend = std::make_unique<VideoBackend>(*mRenderEngine, mRuntimeStore->GetHealthTelemetry(), *mRuntimeEventDispatcher);
|
||||||
mShaderBuildQueue = std::make_unique<ShaderBuildQueue>(*mRuntimeSnapshotProvider, *mRuntimeEventDispatcher);
|
mShaderBuildQueue = std::make_unique<ShaderBuildQueue>(*mRuntimeSnapshotProvider, *mRuntimeEventDispatcher);
|
||||||
mRuntimeServices = std::make_unique<RuntimeServices>(*mRuntimeEventDispatcher);
|
mRuntimeServices = std::make_unique<RuntimeServices>(*mRuntimeEventDispatcher);
|
||||||
mRuntimeUpdateController = std::make_unique<RuntimeUpdateController>(
|
mRuntimeUpdateController = std::make_unique<RuntimeUpdateController>(
|
||||||
@@ -300,7 +300,7 @@ bool OpenGLComposite::RequestScreenshot(std::string& error)
|
|||||||
|
|
||||||
void OpenGLComposite::renderEffect()
|
void OpenGLComposite::renderEffect()
|
||||||
{
|
{
|
||||||
if (mRuntimeUpdateController)
|
if (mRuntimeUpdateController && ProcessRuntimeServiceResults())
|
||||||
mRuntimeUpdateController->ProcessRuntimeWork();
|
mRuntimeUpdateController->ProcessRuntimeWork();
|
||||||
std::vector<RuntimeServices::AppliedOscUpdate> appliedOscUpdates;
|
std::vector<RuntimeServices::AppliedOscUpdate> appliedOscUpdates;
|
||||||
std::vector<RuntimeServices::CompletedOscCommit> completedOscCommits;
|
std::vector<RuntimeServices::CompletedOscCommit> completedOscCommits;
|
||||||
@@ -369,6 +369,25 @@ void OpenGLComposite::renderEffect()
|
|||||||
historyCap);
|
historyCap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OpenGLComposite::ProcessRuntimeServiceResults()
|
||||||
|
{
|
||||||
|
if (!mRuntimeServices || !mRuntimeUpdateController)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool shaderBuildRequested = false;
|
||||||
|
std::vector<RuntimeCoordinatorServiceResult> serviceResults;
|
||||||
|
mRuntimeServices->ConsumeRuntimeCoordinatorResults(serviceResults);
|
||||||
|
for (const RuntimeCoordinatorServiceResult& serviceResult : serviceResults)
|
||||||
|
{
|
||||||
|
shaderBuildRequested = shaderBuildRequested || serviceResult.result.shaderBuildRequested;
|
||||||
|
mRuntimeUpdateController->ApplyRuntimeCoordinatorResult(serviceResult.result);
|
||||||
|
if (serviceResult.failed)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !shaderBuildRequested;
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLComposite::ProcessScreenshotRequest()
|
void OpenGLComposite::ProcessScreenshotRequest()
|
||||||
{
|
{
|
||||||
if (!mScreenshotRequested.exchange(false))
|
if (!mScreenshotRequested.exchange(false))
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ private:
|
|||||||
|
|
||||||
bool InitOpenGLState();
|
bool InitOpenGLState();
|
||||||
void renderEffect();
|
void renderEffect();
|
||||||
|
bool ProcessRuntimeServiceResults();
|
||||||
void ProcessScreenshotRequest();
|
void ProcessScreenshotRequest();
|
||||||
std::filesystem::path BuildScreenshotPath() const;
|
std::filesystem::path BuildScreenshotPath() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "VideoBackend.h"
|
#include "VideoBackend.h"
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -107,20 +106,6 @@ bool RuntimeUpdateController::ApplyRuntimeCoordinatorResult(const RuntimeCoordin
|
|||||||
|
|
||||||
bool RuntimeUpdateController::ProcessRuntimeWork()
|
bool RuntimeUpdateController::ProcessRuntimeWork()
|
||||||
{
|
{
|
||||||
bool shaderBuildRequested = false;
|
|
||||||
std::vector<RuntimeCoordinatorServiceResult> serviceResults;
|
|
||||||
mRuntimeServices.ConsumeRuntimeCoordinatorResults(serviceResults);
|
|
||||||
for (const RuntimeCoordinatorServiceResult& serviceResult : serviceResults)
|
|
||||||
{
|
|
||||||
shaderBuildRequested = shaderBuildRequested || serviceResult.result.shaderBuildRequested;
|
|
||||||
ApplyRuntimeCoordinatorResult(serviceResult.result);
|
|
||||||
if (serviceResult.failed)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shaderBuildRequested)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
DispatchRuntimeEvents();
|
DispatchRuntimeEvents();
|
||||||
|
|
||||||
return ConsumeReadyShaderBuild(0, true, true);
|
return ConsumeReadyShaderBuild(0, true, true);
|
||||||
@@ -324,5 +309,33 @@ RuntimeEventDispatchResult RuntimeUpdateController::DispatchRuntimeEvents(std::s
|
|||||||
queueMetrics.capacity,
|
queueMetrics.capacity,
|
||||||
static_cast<uint64_t>(queueMetrics.droppedCount),
|
static_cast<uint64_t>(queueMetrics.droppedCount),
|
||||||
queueMetrics.oldestEventAgeMilliseconds);
|
queueMetrics.oldestEventAgeMilliseconds);
|
||||||
|
PublishRuntimeEventHealthObservations(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeUpdateController::PublishRuntimeEventHealthObservations(const RuntimeEventDispatchResult& result)
|
||||||
|
{
|
||||||
|
const RuntimeEventQueueMetrics queueMetrics = mRuntimeEventDispatcher.GetQueueMetrics();
|
||||||
|
if (queueMetrics.depth != mLastReportedRuntimeEventQueueDepth ||
|
||||||
|
queueMetrics.droppedCount != mLastReportedRuntimeEventDroppedCount)
|
||||||
|
{
|
||||||
|
QueueDepthChangedEvent queueDepth;
|
||||||
|
queueDepth.queueName = "runtime-events";
|
||||||
|
queueDepth.depth = queueMetrics.depth;
|
||||||
|
queueDepth.capacity = queueMetrics.capacity;
|
||||||
|
queueDepth.droppedCount = queueMetrics.droppedCount;
|
||||||
|
mRuntimeEventDispatcher.PublishPayload(queueDepth, "HealthTelemetry");
|
||||||
|
mLastReportedRuntimeEventQueueDepth = queueMetrics.depth;
|
||||||
|
mLastReportedRuntimeEventDroppedCount = queueMetrics.droppedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.handlerInvocations == 0 && result.handlerFailures == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TimingSampleRecordedEvent timing;
|
||||||
|
timing.subsystem = "RuntimeEventDispatcher";
|
||||||
|
timing.metric = "dispatchDuration";
|
||||||
|
timing.value = result.dispatchDurationMilliseconds;
|
||||||
|
timing.unit = "ms";
|
||||||
|
mRuntimeEventDispatcher.PublishPayload(timing, "HealthTelemetry");
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ private:
|
|||||||
const std::string& message);
|
const std::string& message);
|
||||||
bool ShouldSuppressCoordinatorFollowUp(const RuntimeEvent& event, std::size_t& pendingSuppressions);
|
bool ShouldSuppressCoordinatorFollowUp(const RuntimeEvent& event, std::size_t& pendingSuppressions);
|
||||||
RuntimeEventDispatchResult DispatchRuntimeEvents(std::size_t maxEvents = 0);
|
RuntimeEventDispatchResult DispatchRuntimeEvents(std::size_t maxEvents = 0);
|
||||||
|
void PublishRuntimeEventHealthObservations(const RuntimeEventDispatchResult& result);
|
||||||
|
|
||||||
RuntimeStore& mRuntimeStore;
|
RuntimeStore& mRuntimeStore;
|
||||||
RuntimeCoordinator& mRuntimeCoordinator;
|
RuntimeCoordinator& mRuntimeCoordinator;
|
||||||
@@ -61,4 +62,6 @@ private:
|
|||||||
std::size_t mPendingCoordinatorShaderBuildEvents = 0;
|
std::size_t mPendingCoordinatorShaderBuildEvents = 0;
|
||||||
std::size_t mPendingCoordinatorCompileStatusEvents = 0;
|
std::size_t mPendingCoordinatorCompileStatusEvents = 0;
|
||||||
std::size_t mPendingCoordinatorRenderResetEvents = 0;
|
std::size_t mPendingCoordinatorRenderResetEvents = 0;
|
||||||
|
std::size_t mLastReportedRuntimeEventQueueDepth = static_cast<std::size_t>(-1);
|
||||||
|
std::size_t mLastReportedRuntimeEventDroppedCount = static_cast<std::size_t>(-1);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::AddLayer(const std::string& shaderI
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidateShaderExists(shaderId, error))
|
if (!ValidateShaderExists(shaderId, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("AddLayer", result);
|
PublishCoordinatorResult("AddLayer", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.CreateStoredLayer(shaderId, error), error, true, true);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.CreateStoredLayer(shaderId, error), error, true, true, true);
|
||||||
PublishCoordinatorResult("AddLayer", result);
|
PublishCoordinatorResult("AddLayer", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -50,12 +50,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::RemoveLayer(const std::string& laye
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidateLayerExists(layerId, error))
|
if (!ValidateLayerExists(layerId, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("RemoveLayer", result);
|
PublishCoordinatorResult("RemoveLayer", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.DeleteStoredLayer(layerId, error), error, true, true);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.DeleteStoredLayer(layerId, error), error, true, true, true);
|
||||||
PublishCoordinatorResult("RemoveLayer", result);
|
PublishCoordinatorResult("RemoveLayer", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ RuntimeCoordinatorResult RuntimeCoordinator::MoveLayer(const std::string& layerI
|
|||||||
bool shouldMove = false;
|
bool shouldMove = false;
|
||||||
if (!ResolveLayerMove(layerId, direction, shouldMove, error))
|
if (!ResolveLayerMove(layerId, direction, shouldMove, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("MoveLayer", result);
|
PublishCoordinatorResult("MoveLayer", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ RuntimeCoordinatorResult RuntimeCoordinator::MoveLayer(const std::string& layerI
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.MoveStoredLayer(layerId, direction, error), error, true, true);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.MoveStoredLayer(layerId, direction, error), error, true, true, true);
|
||||||
PublishCoordinatorResult("MoveLayer", result);
|
PublishCoordinatorResult("MoveLayer", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ RuntimeCoordinatorResult RuntimeCoordinator::MoveLayerToIndex(const std::string&
|
|||||||
bool shouldMove = false;
|
bool shouldMove = false;
|
||||||
if (!ResolveLayerMoveToIndex(layerId, targetIndex, shouldMove, error))
|
if (!ResolveLayerMoveToIndex(layerId, targetIndex, shouldMove, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("MoveLayerToIndex", result);
|
PublishCoordinatorResult("MoveLayerToIndex", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ RuntimeCoordinatorResult RuntimeCoordinator::MoveLayerToIndex(const std::string&
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.MoveStoredLayerToIndex(layerId, targetIndex, error), error, true, true);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.MoveStoredLayerToIndex(layerId, targetIndex, error), error, true, true, true);
|
||||||
PublishCoordinatorResult("MoveLayerToIndex", result);
|
PublishCoordinatorResult("MoveLayerToIndex", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -110,12 +110,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::SetLayerBypass(const std::string& l
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidateLayerExists(layerId, error))
|
if (!ValidateLayerExists(layerId, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("SetLayerBypass", result);
|
PublishCoordinatorResult("SetLayerBypass", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredLayerBypassState(layerId, bypassed, error), error, true, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredLayerBypassState(layerId, bypassed, error), error, true, false, true);
|
||||||
PublishCoordinatorResult("SetLayerBypass", result);
|
PublishCoordinatorResult("SetLayerBypass", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -126,12 +126,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::SetLayerShader(const std::string& l
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidateLayerExists(layerId, error) || !ValidateShaderExists(shaderId, error))
|
if (!ValidateLayerExists(layerId, error) || !ValidateShaderExists(shaderId, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("SetLayerShader", result);
|
PublishCoordinatorResult("SetLayerShader", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredLayerShaderSelection(layerId, shaderId, error), error, true, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredLayerShaderSelection(layerId, shaderId, error), error, true, false, true);
|
||||||
PublishCoordinatorResult("SetLayerShader", result);
|
PublishCoordinatorResult("SetLayerShader", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -143,12 +143,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameter(const std::str
|
|||||||
ResolvedParameterMutation mutation;
|
ResolvedParameterMutation mutation;
|
||||||
if (!BuildParameterMutationById(layerId, parameterId, newValue, true, mutation, error))
|
if (!BuildParameterMutationById(layerId, parameterId, newValue, true, mutation, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("UpdateLayerParameter", result);
|
PublishCoordinatorResult("UpdateLayerParameter", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false, mutation.persistState);
|
||||||
PublishCoordinatorResult("UpdateLayerParameter", result);
|
PublishCoordinatorResult("UpdateLayerParameter", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -160,12 +160,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::UpdateLayerParameterByControlKey(co
|
|||||||
ResolvedParameterMutation mutation;
|
ResolvedParameterMutation mutation;
|
||||||
if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, true, mutation, error))
|
if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, true, mutation, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("UpdateLayerParameterByControlKey", result);
|
PublishCoordinatorResult("UpdateLayerParameterByControlKey", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false, mutation.persistState);
|
||||||
PublishCoordinatorResult("UpdateLayerParameterByControlKey", result);
|
PublishCoordinatorResult("UpdateLayerParameterByControlKey", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -177,12 +177,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::CommitOscParameterByControlKey(cons
|
|||||||
ResolvedParameterMutation mutation;
|
ResolvedParameterMutation mutation;
|
||||||
if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, false, mutation, error))
|
if (!BuildParameterMutationByControlKey(layerKey, parameterKey, newValue, false, mutation, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("CommitOscParameterByControlKey", result);
|
PublishCoordinatorResult("CommitOscParameterByControlKey", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SetStoredParameterValue(mutation.layerId, mutation.parameterId, mutation.value, mutation.persistState, error), error, false, false, mutation.persistState);
|
||||||
PublishCoordinatorResult("CommitOscParameterByControlKey", result);
|
PublishCoordinatorResult("CommitOscParameterByControlKey", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -193,12 +193,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::ResetLayerParameters(const std::str
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidateLayerExists(layerId, error))
|
if (!ValidateLayerExists(layerId, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("ResetLayerParameters", result);
|
PublishCoordinatorResult("ResetLayerParameters", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.ResetStoredLayerParameterValues(layerId, error), error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.ResetStoredLayerParameterValues(layerId, error), error, false, false, true);
|
||||||
if (!result.accepted)
|
if (!result.accepted)
|
||||||
{
|
{
|
||||||
PublishCoordinatorResult("ResetLayerParameters", result);
|
PublishCoordinatorResult("ResetLayerParameters", result);
|
||||||
@@ -217,12 +217,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::SaveStackPreset(const std::string&
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidatePresetName(presetName, error))
|
if (!ValidatePresetName(presetName, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("SaveStackPreset", result);
|
PublishCoordinatorResult("SaveStackPreset", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SaveStackPresetSnapshot(presetName, error), error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.SaveStackPresetSnapshot(presetName, error), error, false, false, true);
|
||||||
PublishCoordinatorResult("SaveStackPreset", result);
|
PublishCoordinatorResult("SaveStackPreset", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -233,12 +233,12 @@ RuntimeCoordinatorResult RuntimeCoordinator::LoadStackPreset(const std::string&
|
|||||||
std::string error;
|
std::string error;
|
||||||
if (!ValidatePresetName(presetName, error))
|
if (!ValidatePresetName(presetName, error))
|
||||||
{
|
{
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(false, error, false, false, false);
|
||||||
PublishCoordinatorResult("LoadStackPreset", result);
|
PublishCoordinatorResult("LoadStackPreset", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.LoadStackPresetSnapshot(presetName, error), error, true, false);
|
RuntimeCoordinatorResult result = ApplyStoreMutation(mRuntimeStore.LoadStackPresetSnapshot(presetName, error), error, true, false, true);
|
||||||
PublishCoordinatorResult("LoadStackPreset", result);
|
PublishCoordinatorResult("LoadStackPreset", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -446,7 +446,7 @@ bool RuntimeCoordinator::ValidatePresetName(const std::string& presetName, std::
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult RuntimeCoordinator::ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState)
|
RuntimeCoordinatorResult RuntimeCoordinator::ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState, bool persistenceRequested)
|
||||||
{
|
{
|
||||||
if (!succeeded)
|
if (!succeeded)
|
||||||
{
|
{
|
||||||
@@ -457,9 +457,15 @@ RuntimeCoordinatorResult RuntimeCoordinator::ApplyStoreMutation(bool succeeded,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (reloadRequired)
|
if (reloadRequired)
|
||||||
return BuildQueuedReloadResult(preserveFeedbackState);
|
{
|
||||||
|
RuntimeCoordinatorResult result = BuildQueuedReloadResult(preserveFeedbackState);
|
||||||
|
result.persistenceRequested = persistenceRequested;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return BuildAcceptedNoReloadResult();
|
RuntimeCoordinatorResult result = BuildAcceptedNoReloadResult();
|
||||||
|
result.persistenceRequested = persistenceRequested;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeCoordinatorResult RuntimeCoordinator::BuildQueuedReloadResult(bool preserveFeedbackState)
|
RuntimeCoordinatorResult RuntimeCoordinator::BuildQueuedReloadResult(bool preserveFeedbackState)
|
||||||
@@ -497,6 +503,7 @@ void RuntimeCoordinator::PublishCoordinatorResult(const std::string& action, con
|
|||||||
mutation.runtimeStateChanged = result.accepted && result.runtimeStateBroadcastRequired;
|
mutation.runtimeStateChanged = result.accepted && result.runtimeStateBroadcastRequired;
|
||||||
mutation.runtimeStateBroadcastRequired = result.runtimeStateBroadcastRequired;
|
mutation.runtimeStateBroadcastRequired = result.runtimeStateBroadcastRequired;
|
||||||
mutation.shaderBuildRequested = result.shaderBuildRequested;
|
mutation.shaderBuildRequested = result.shaderBuildRequested;
|
||||||
|
mutation.persistenceRequested = result.persistenceRequested;
|
||||||
mutation.clearTransientOscState = result.clearTransientOscState;
|
mutation.clearTransientOscState = result.clearTransientOscState;
|
||||||
mutation.renderResetScope = ToRuntimeEventRenderResetScope(result.renderResetScope);
|
mutation.renderResetScope = ToRuntimeEventRenderResetScope(result.renderResetScope);
|
||||||
mutation.errorMessage = result.errorMessage;
|
mutation.errorMessage = result.errorMessage;
|
||||||
@@ -521,9 +528,18 @@ void RuntimeCoordinator::PublishCoordinatorFollowUpEvents(const std::string& act
|
|||||||
RuntimeStateChangedEvent stateChanged;
|
RuntimeStateChangedEvent stateChanged;
|
||||||
stateChanged.reason = action;
|
stateChanged.reason = action;
|
||||||
stateChanged.renderVisible = result.renderResetScope != RuntimeCoordinatorRenderResetScope::None;
|
stateChanged.renderVisible = result.renderResetScope != RuntimeCoordinatorRenderResetScope::None;
|
||||||
|
stateChanged.persistenceRequested = result.persistenceRequested;
|
||||||
mRuntimeEventDispatcher.PublishPayload(stateChanged, "RuntimeCoordinator");
|
mRuntimeEventDispatcher.PublishPayload(stateChanged, "RuntimeCoordinator");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.persistenceRequested)
|
||||||
|
{
|
||||||
|
RuntimePersistenceRequestedEvent persistenceRequested;
|
||||||
|
persistenceRequested.reason = action;
|
||||||
|
persistenceRequested.debounceAllowed = true;
|
||||||
|
mRuntimeEventDispatcher.PublishPayload(persistenceRequested, "RuntimeCoordinator");
|
||||||
|
}
|
||||||
|
|
||||||
if (result.shaderBuildRequested)
|
if (result.shaderBuildRequested)
|
||||||
{
|
{
|
||||||
RuntimeReloadRequestedEvent reloadRequested;
|
RuntimeReloadRequestedEvent reloadRequested;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ struct RuntimeCoordinatorResult
|
|||||||
bool accepted = false;
|
bool accepted = false;
|
||||||
bool runtimeStateBroadcastRequired = false;
|
bool runtimeStateBroadcastRequired = false;
|
||||||
bool shaderBuildRequested = false;
|
bool shaderBuildRequested = false;
|
||||||
|
bool persistenceRequested = false;
|
||||||
bool clearTransientOscState = false;
|
bool clearTransientOscState = false;
|
||||||
bool compileStatusChanged = false;
|
bool compileStatusChanged = false;
|
||||||
bool compileStatusSucceeded = false;
|
bool compileStatusSucceeded = false;
|
||||||
@@ -89,7 +90,7 @@ private:
|
|||||||
bool ResolveLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const;
|
bool ResolveLayerMove(const std::string& layerId, int direction, bool& shouldMove, std::string& error) const;
|
||||||
bool ResolveLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const;
|
bool ResolveLayerMoveToIndex(const std::string& layerId, std::size_t targetIndex, bool& shouldMove, std::string& error) const;
|
||||||
bool ValidatePresetName(const std::string& presetName, std::string& error) const;
|
bool ValidatePresetName(const std::string& presetName, std::string& error) const;
|
||||||
RuntimeCoordinatorResult ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState);
|
RuntimeCoordinatorResult ApplyStoreMutation(bool succeeded, const std::string& errorMessage, bool reloadRequired, bool preserveFeedbackState, bool persistenceRequested);
|
||||||
RuntimeCoordinatorResult BuildQueuedReloadResult(bool preserveFeedbackState);
|
RuntimeCoordinatorResult BuildQueuedReloadResult(bool preserveFeedbackState);
|
||||||
RuntimeCoordinatorResult BuildAcceptedNoReloadResult() const;
|
RuntimeCoordinatorResult BuildAcceptedNoReloadResult() const;
|
||||||
void PublishCoordinatorResult(const std::string& action, const RuntimeCoordinatorResult& result) const;
|
void PublishCoordinatorResult(const std::string& action, const RuntimeCoordinatorResult& result) const;
|
||||||
|
|||||||
@@ -406,8 +406,12 @@ constexpr RuntimeEventType RuntimeEventPayloadType(const OutputFrameScheduledEve
|
|||||||
return RuntimeEventType::OutputFrameScheduled;
|
return RuntimeEventType::OutputFrameScheduled;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr RuntimeEventType RuntimeEventPayloadType(const OutputFrameCompletedEvent&)
|
inline RuntimeEventType RuntimeEventPayloadType(const OutputFrameCompletedEvent& event)
|
||||||
{
|
{
|
||||||
|
if (event.result == "DisplayedLate")
|
||||||
|
return RuntimeEventType::OutputLateFrameDetected;
|
||||||
|
if (event.result == "Dropped")
|
||||||
|
return RuntimeEventType::OutputDroppedFrameDetected;
|
||||||
return RuntimeEventType::OutputFrameCompleted;
|
return RuntimeEventType::OutputFrameCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
#include "RuntimeSnapshotProvider.h"
|
#include "RuntimeSnapshotProvider.h"
|
||||||
|
|
||||||
|
#include "RuntimeEventDispatcher.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
RuntimeSnapshotProvider::RuntimeSnapshotProvider(RenderSnapshotBuilder& renderSnapshotBuilder) :
|
RuntimeSnapshotProvider::RuntimeSnapshotProvider(RenderSnapshotBuilder& renderSnapshotBuilder, RuntimeEventDispatcher& runtimeEventDispatcher) :
|
||||||
mRenderSnapshotBuilder(renderSnapshotBuilder)
|
mRenderSnapshotBuilder(renderSnapshotBuilder),
|
||||||
|
mRuntimeEventDispatcher(runtimeEventDispatcher)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +47,17 @@ void RuntimeSnapshotProvider::AdvanceFrame()
|
|||||||
|
|
||||||
RuntimeRenderStateSnapshot RuntimeSnapshotProvider::PublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const
|
RuntimeRenderStateSnapshot RuntimeSnapshotProvider::PublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight) const
|
||||||
{
|
{
|
||||||
|
PublishRenderSnapshotPublishRequested(outputWidth, outputHeight, "publish-render-state-snapshot");
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
const RuntimeSnapshotVersions versionsBefore = GetVersions();
|
const RuntimeSnapshotVersions versionsBefore = GetVersions();
|
||||||
RuntimeRenderStateSnapshot publishedSnapshot;
|
RuntimeRenderStateSnapshot publishedSnapshot;
|
||||||
if (TryGetPublishedRenderStateSnapshot(outputWidth, outputHeight, versionsBefore, publishedSnapshot))
|
if (TryGetPublishedRenderStateSnapshot(outputWidth, outputHeight, versionsBefore, publishedSnapshot))
|
||||||
|
{
|
||||||
|
PublishRenderSnapshotPublished(publishedSnapshot);
|
||||||
return publishedSnapshot;
|
return publishedSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeRenderStateSnapshot snapshot;
|
RuntimeRenderStateSnapshot snapshot;
|
||||||
snapshot.outputWidth = outputWidth;
|
snapshot.outputWidth = outputWidth;
|
||||||
@@ -60,6 +70,7 @@ RuntimeRenderStateSnapshot RuntimeSnapshotProvider::PublishRenderStateSnapshot(u
|
|||||||
{
|
{
|
||||||
snapshot.versions = versionsAfter;
|
snapshot.versions = versionsAfter;
|
||||||
StorePublishedRenderStateSnapshot(snapshot);
|
StorePublishedRenderStateSnapshot(snapshot);
|
||||||
|
PublishRenderSnapshotPublished(snapshot);
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,9 +78,14 @@ RuntimeRenderStateSnapshot RuntimeSnapshotProvider::PublishRenderStateSnapshot(u
|
|||||||
|
|
||||||
bool RuntimeSnapshotProvider::TryPublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const
|
bool RuntimeSnapshotProvider::TryPublishRenderStateSnapshot(unsigned outputWidth, unsigned outputHeight, RuntimeRenderStateSnapshot& snapshot) const
|
||||||
{
|
{
|
||||||
|
PublishRenderSnapshotPublishRequested(outputWidth, outputHeight, "try-publish-render-state-snapshot");
|
||||||
|
|
||||||
const RuntimeSnapshotVersions versionsBefore = GetVersions();
|
const RuntimeSnapshotVersions versionsBefore = GetVersions();
|
||||||
if (TryGetPublishedRenderStateSnapshot(outputWidth, outputHeight, versionsBefore, snapshot))
|
if (TryGetPublishedRenderStateSnapshot(outputWidth, outputHeight, versionsBefore, snapshot))
|
||||||
|
{
|
||||||
|
PublishRenderSnapshotPublished(snapshot);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<RuntimeRenderState> states;
|
std::vector<RuntimeRenderState> states;
|
||||||
if (!mRenderSnapshotBuilder.TryBuildLayerRenderStates(outputWidth, outputHeight, states))
|
if (!mRenderSnapshotBuilder.TryBuildLayerRenderStates(outputWidth, outputHeight, states))
|
||||||
@@ -87,6 +103,7 @@ bool RuntimeSnapshotProvider::TryPublishRenderStateSnapshot(unsigned outputWidth
|
|||||||
snapshot.versions = versionsAfter;
|
snapshot.versions = versionsAfter;
|
||||||
snapshot.states = std::move(states);
|
snapshot.states = std::move(states);
|
||||||
StorePublishedRenderStateSnapshot(snapshot);
|
StorePublishedRenderStateSnapshot(snapshot);
|
||||||
|
PublishRenderSnapshotPublished(snapshot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +119,7 @@ bool RuntimeSnapshotProvider::TryRefreshPublishedSnapshotParameters(RuntimeRende
|
|||||||
|
|
||||||
snapshot.versions = versions;
|
snapshot.versions = versions;
|
||||||
StorePublishedRenderStateSnapshot(snapshot);
|
StorePublishedRenderStateSnapshot(snapshot);
|
||||||
|
PublishRenderSnapshotPublished(snapshot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,3 +157,40 @@ bool RuntimeSnapshotProvider::SnapshotMatches(const RuntimeRenderStateSnapshot&
|
|||||||
snapshot.versions.renderStateVersion == versions.renderStateVersion &&
|
snapshot.versions.renderStateVersion == versions.renderStateVersion &&
|
||||||
snapshot.versions.parameterStateVersion == versions.parameterStateVersion;
|
snapshot.versions.parameterStateVersion == versions.parameterStateVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RuntimeSnapshotProvider::PublishRenderSnapshotPublishRequested(unsigned outputWidth, unsigned outputHeight, const std::string& reason) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RenderSnapshotPublishRequestedEvent event;
|
||||||
|
event.outputWidth = outputWidth;
|
||||||
|
event.outputHeight = outputHeight;
|
||||||
|
event.reason = reason;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "RuntimeSnapshotProvider"))
|
||||||
|
OutputDebugStringA("RenderSnapshotPublishRequested event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("RenderSnapshotPublishRequested event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RuntimeSnapshotProvider::PublishRenderSnapshotPublished(const RuntimeRenderStateSnapshot& snapshot) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RenderSnapshotPublishedEvent event;
|
||||||
|
event.snapshotVersion = snapshot.versions.renderStateVersion;
|
||||||
|
event.structureVersion = snapshot.versions.renderStateVersion;
|
||||||
|
event.parameterVersion = snapshot.versions.parameterStateVersion;
|
||||||
|
event.outputWidth = snapshot.outputWidth;
|
||||||
|
event.outputHeight = snapshot.outputHeight;
|
||||||
|
event.layerCount = snapshot.states.size();
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "RuntimeSnapshotProvider"))
|
||||||
|
OutputDebugStringA("RenderSnapshotPublished event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("RenderSnapshotPublished event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class RuntimeEventDispatcher;
|
||||||
|
|
||||||
struct RuntimeRenderStateSnapshot
|
struct RuntimeRenderStateSnapshot
|
||||||
{
|
{
|
||||||
RuntimeSnapshotVersions versions;
|
RuntimeSnapshotVersions versions;
|
||||||
@@ -17,7 +19,7 @@ struct RuntimeRenderStateSnapshot
|
|||||||
class RuntimeSnapshotProvider
|
class RuntimeSnapshotProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RuntimeSnapshotProvider(RenderSnapshotBuilder& renderSnapshotBuilder);
|
RuntimeSnapshotProvider(RenderSnapshotBuilder& renderSnapshotBuilder, RuntimeEventDispatcher& runtimeEventDispatcher);
|
||||||
|
|
||||||
bool BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error) const;
|
bool BuildLayerPassFragmentShaderSources(const std::string& layerId, std::vector<ShaderPassBuildSource>& passSources, std::string& error) const;
|
||||||
unsigned GetMaxTemporalHistoryFrames() const;
|
unsigned GetMaxTemporalHistoryFrames() const;
|
||||||
@@ -34,8 +36,11 @@ private:
|
|||||||
void StorePublishedRenderStateSnapshot(const RuntimeRenderStateSnapshot& snapshot) const;
|
void StorePublishedRenderStateSnapshot(const RuntimeRenderStateSnapshot& snapshot) const;
|
||||||
static bool SnapshotMatches(const RuntimeRenderStateSnapshot& snapshot, unsigned outputWidth, unsigned outputHeight,
|
static bool SnapshotMatches(const RuntimeRenderStateSnapshot& snapshot, unsigned outputWidth, unsigned outputHeight,
|
||||||
const RuntimeSnapshotVersions& versions);
|
const RuntimeSnapshotVersions& versions);
|
||||||
|
void PublishRenderSnapshotPublishRequested(unsigned outputWidth, unsigned outputHeight, const std::string& reason) const;
|
||||||
|
void PublishRenderSnapshotPublished(const RuntimeRenderStateSnapshot& snapshot) const;
|
||||||
|
|
||||||
RenderSnapshotBuilder& mRenderSnapshotBuilder;
|
RenderSnapshotBuilder& mRenderSnapshotBuilder;
|
||||||
|
RuntimeEventDispatcher& mRuntimeEventDispatcher;
|
||||||
mutable std::mutex mPublishedSnapshotMutex;
|
mutable std::mutex mPublishedSnapshotMutex;
|
||||||
mutable bool mHasPublishedRenderStateSnapshot = false;
|
mutable bool mHasPublishedRenderStateSnapshot = false;
|
||||||
mutable RuntimeRenderStateSnapshot mPublishedRenderStateSnapshot;
|
mutable RuntimeRenderStateSnapshot mPublishedRenderStateSnapshot;
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
#include "OpenGLVideoIOBridge.h"
|
#include "OpenGLVideoIOBridge.h"
|
||||||
#include "HealthTelemetry.h"
|
#include "HealthTelemetry.h"
|
||||||
#include "RenderEngine.h"
|
#include "RenderEngine.h"
|
||||||
|
#include "RuntimeEventDispatcher.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry) :
|
VideoBackend::VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry, RuntimeEventDispatcher& runtimeEventDispatcher) :
|
||||||
mHealthTelemetry(healthTelemetry),
|
mHealthTelemetry(healthTelemetry),
|
||||||
|
mRuntimeEventDispatcher(runtimeEventDispatcher),
|
||||||
mVideoIODevice(std::make_unique<DeckLinkSession>()),
|
mVideoIODevice(std::make_unique<DeckLinkSession>()),
|
||||||
mBridge(std::make_unique<OpenGLVideoIOBridge>(renderEngine))
|
mBridge(std::make_unique<OpenGLVideoIOBridge>(renderEngine))
|
||||||
{
|
{
|
||||||
@@ -54,12 +57,16 @@ bool VideoBackend::ConfigureOutput(const VideoFormat& outputVideoMode, bool exte
|
|||||||
|
|
||||||
bool VideoBackend::Start()
|
bool VideoBackend::Start()
|
||||||
{
|
{
|
||||||
return mVideoIODevice->Start();
|
const bool started = mVideoIODevice->Start();
|
||||||
|
PublishBackendStateChanged(started ? "started" : "start-failed", started ? "Video backend started." : StatusMessage());
|
||||||
|
return started;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoBackend::Stop()
|
bool VideoBackend::Stop()
|
||||||
{
|
{
|
||||||
return mVideoIODevice->Stop();
|
const bool stopped = mVideoIODevice->Stop();
|
||||||
|
PublishBackendStateChanged(stopped ? "stopped" : "stop-failed", stopped ? "Video backend stopped." : StatusMessage());
|
||||||
|
return stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VideoIOState& VideoBackend::State() const
|
const VideoIOState& VideoBackend::State() const
|
||||||
@@ -191,6 +198,7 @@ void VideoBackend::PublishStatus(bool externalKeyingConfigured, const std::strin
|
|||||||
externalKeyingConfigured,
|
externalKeyingConfigured,
|
||||||
ExternalKeyingActive(),
|
ExternalKeyingActive(),
|
||||||
StatusMessage());
|
StatusMessage());
|
||||||
|
PublishBackendStateChanged("status", StatusMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBackend::ReportNoInputDeviceSignalStatus()
|
void VideoBackend::ReportNoInputDeviceSignalStatus()
|
||||||
@@ -200,12 +208,15 @@ void VideoBackend::ReportNoInputDeviceSignalStatus()
|
|||||||
InputFrameWidth(),
|
InputFrameWidth(),
|
||||||
InputFrameHeight(),
|
InputFrameHeight(),
|
||||||
InputDisplayModeName());
|
InputDisplayModeName());
|
||||||
|
PublishBackendStateChanged("no-input-device", "No input device is available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
|
void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
|
||||||
{
|
{
|
||||||
const VideoIOState& state = mVideoIODevice->State();
|
const VideoIOState& state = mVideoIODevice->State();
|
||||||
mHealthTelemetry.TryReportSignalStatus(!frame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
|
mHealthTelemetry.TryReportSignalStatus(!frame.hasNoInputSource, state.inputFrameSize.width, state.inputFrameSize.height, state.inputDisplayModeName);
|
||||||
|
PublishInputSignalChanged(frame, state);
|
||||||
|
PublishInputFrameArrived(frame);
|
||||||
|
|
||||||
if (mBridge)
|
if (mBridge)
|
||||||
mBridge->UploadInputFrame(frame, state);
|
mBridge->UploadInputFrame(frame, state);
|
||||||
@@ -214,6 +225,7 @@ void VideoBackend::HandleInputFrame(const VideoIOFrame& frame)
|
|||||||
void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completion)
|
void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completion)
|
||||||
{
|
{
|
||||||
RecordFramePacing(completion.result);
|
RecordFramePacing(completion.result);
|
||||||
|
PublishOutputFrameCompleted(completion);
|
||||||
|
|
||||||
VideoIOOutputFrame outputFrame;
|
VideoIOOutputFrame outputFrame;
|
||||||
if (!BeginOutputFrame(outputFrame))
|
if (!BeginOutputFrame(outputFrame))
|
||||||
@@ -228,7 +240,8 @@ void VideoBackend::HandleOutputFrameCompletion(const VideoIOCompletion& completi
|
|||||||
|
|
||||||
// Schedule the next frame after render work is complete so device-side
|
// Schedule the next frame after render work is complete so device-side
|
||||||
// bookkeeping stays with the backend seam and the bridge stays render-only.
|
// bookkeeping stays with the backend seam and the bridge stays render-only.
|
||||||
ScheduleOutputFrame(outputFrame);
|
if (ScheduleOutputFrame(outputFrame))
|
||||||
|
PublishOutputFrameScheduled(outputFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
|
void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
|
||||||
@@ -260,4 +273,152 @@ void VideoBackend::RecordFramePacing(VideoIOCompletionResult completionResult)
|
|||||||
mLateFrameCount,
|
mLateFrameCount,
|
||||||
mDroppedFrameCount,
|
mDroppedFrameCount,
|
||||||
mFlushedFrameCount);
|
mFlushedFrameCount);
|
||||||
|
PublishTimingSample("VideoBackend", "completionInterval", mCompletionIntervalMilliseconds, "ms");
|
||||||
|
PublishTimingSample("VideoBackend", "smoothedCompletionInterval", mSmoothedCompletionIntervalMilliseconds, "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoBackend::PublishBackendStateChanged(const std::string& state, const std::string& message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BackendStateChangedEvent event;
|
||||||
|
event.backendName = "decklink";
|
||||||
|
event.state = state;
|
||||||
|
event.message = message;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||||
|
OutputDebugStringA("BackendStateChanged event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("BackendStateChanged event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoBackend::PublishInputSignalChanged(const VideoIOFrame& frame, const VideoIOState& state)
|
||||||
|
{
|
||||||
|
const bool hasSignal = !frame.hasNoInputSource;
|
||||||
|
const unsigned width = state.inputFrameSize.width;
|
||||||
|
const unsigned height = state.inputFrameSize.height;
|
||||||
|
if (mHasLastInputSignal &&
|
||||||
|
mLastInputSignal == hasSignal &&
|
||||||
|
mLastInputSignalWidth == width &&
|
||||||
|
mLastInputSignalHeight == height &&
|
||||||
|
mLastInputSignalModeName == state.inputDisplayModeName)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mHasLastInputSignal = true;
|
||||||
|
mLastInputSignal = hasSignal;
|
||||||
|
mLastInputSignalWidth = width;
|
||||||
|
mLastInputSignalHeight = height;
|
||||||
|
mLastInputSignalModeName = state.inputDisplayModeName;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InputSignalChangedEvent event;
|
||||||
|
event.hasSignal = hasSignal;
|
||||||
|
event.width = width;
|
||||||
|
event.height = height;
|
||||||
|
event.modeName = state.inputDisplayModeName;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||||
|
OutputDebugStringA("InputSignalChanged event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("InputSignalChanged event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoBackend::PublishInputFrameArrived(const VideoIOFrame& frame)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InputFrameArrivedEvent event;
|
||||||
|
event.frameIndex = ++mInputFrameIndex;
|
||||||
|
event.width = frame.width;
|
||||||
|
event.height = frame.height;
|
||||||
|
event.rowBytes = frame.rowBytes;
|
||||||
|
event.pixelFormat = PixelFormatName(frame.pixelFormat);
|
||||||
|
event.hasNoInputSource = frame.hasNoInputSource;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||||
|
OutputDebugStringA("InputFrameArrived event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("InputFrameArrived event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoBackend::PublishOutputFrameScheduled(const VideoIOOutputFrame& frame)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputFrameScheduledEvent event;
|
||||||
|
event.frameIndex = ++mOutputFrameScheduleIndex;
|
||||||
|
(void)frame;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||||
|
OutputDebugStringA("OutputFrameScheduled event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("OutputFrameScheduled event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoBackend::PublishOutputFrameCompleted(const VideoIOCompletion& completion)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputFrameCompletedEvent event;
|
||||||
|
event.frameIndex = ++mOutputFrameCompletionIndex;
|
||||||
|
event.result = CompletionResultName(completion.result);
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "VideoBackend"))
|
||||||
|
OutputDebugStringA("OutputFrameCompleted event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("OutputFrameCompleted event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoBackend::PublishTimingSample(const std::string& subsystem, const std::string& metric, double value, const std::string& unit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TimingSampleRecordedEvent event;
|
||||||
|
event.subsystem = subsystem;
|
||||||
|
event.metric = metric;
|
||||||
|
event.value = value;
|
||||||
|
event.unit = unit;
|
||||||
|
if (!mRuntimeEventDispatcher.PublishPayload(event, "HealthTelemetry"))
|
||||||
|
OutputDebugStringA("TimingSampleRecorded event publish failed.\n");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
OutputDebugStringA("TimingSampleRecorded event publish threw.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VideoBackend::CompletionResultName(VideoIOCompletionResult result)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case VideoIOCompletionResult::Completed:
|
||||||
|
return "Completed";
|
||||||
|
case VideoIOCompletionResult::DisplayedLate:
|
||||||
|
return "DisplayedLate";
|
||||||
|
case VideoIOCompletionResult::Dropped:
|
||||||
|
return "Dropped";
|
||||||
|
case VideoIOCompletionResult::Flushed:
|
||||||
|
return "Flushed";
|
||||||
|
case VideoIOCompletionResult::Unknown:
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VideoBackend::PixelFormatName(VideoIOPixelFormat pixelFormat)
|
||||||
|
{
|
||||||
|
return std::string(VideoIOPixelFormatName(pixelFormat));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,13 @@
|
|||||||
class HealthTelemetry;
|
class HealthTelemetry;
|
||||||
class OpenGLVideoIOBridge;
|
class OpenGLVideoIOBridge;
|
||||||
class RenderEngine;
|
class RenderEngine;
|
||||||
|
class RuntimeEventDispatcher;
|
||||||
class VideoIODevice;
|
class VideoIODevice;
|
||||||
|
|
||||||
class VideoBackend
|
class VideoBackend
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry);
|
VideoBackend(RenderEngine& renderEngine, HealthTelemetry& healthTelemetry, RuntimeEventDispatcher& runtimeEventDispatcher);
|
||||||
~VideoBackend();
|
~VideoBackend();
|
||||||
|
|
||||||
void ReleaseResources();
|
void ReleaseResources();
|
||||||
@@ -57,10 +58,27 @@ private:
|
|||||||
void HandleInputFrame(const VideoIOFrame& frame);
|
void HandleInputFrame(const VideoIOFrame& frame);
|
||||||
void HandleOutputFrameCompletion(const VideoIOCompletion& completion);
|
void HandleOutputFrameCompletion(const VideoIOCompletion& completion);
|
||||||
void RecordFramePacing(VideoIOCompletionResult completionResult);
|
void RecordFramePacing(VideoIOCompletionResult completionResult);
|
||||||
|
void PublishBackendStateChanged(const std::string& state, const std::string& message);
|
||||||
|
void PublishInputSignalChanged(const VideoIOFrame& frame, const VideoIOState& state);
|
||||||
|
void PublishInputFrameArrived(const VideoIOFrame& frame);
|
||||||
|
void PublishOutputFrameScheduled(const VideoIOOutputFrame& frame);
|
||||||
|
void PublishOutputFrameCompleted(const VideoIOCompletion& completion);
|
||||||
|
void PublishTimingSample(const std::string& subsystem, const std::string& metric, double value, const std::string& unit);
|
||||||
|
static std::string CompletionResultName(VideoIOCompletionResult result);
|
||||||
|
static std::string PixelFormatName(VideoIOPixelFormat pixelFormat);
|
||||||
|
|
||||||
HealthTelemetry& mHealthTelemetry;
|
HealthTelemetry& mHealthTelemetry;
|
||||||
|
RuntimeEventDispatcher& mRuntimeEventDispatcher;
|
||||||
std::unique_ptr<VideoIODevice> mVideoIODevice;
|
std::unique_ptr<VideoIODevice> mVideoIODevice;
|
||||||
std::unique_ptr<OpenGLVideoIOBridge> mBridge;
|
std::unique_ptr<OpenGLVideoIOBridge> mBridge;
|
||||||
|
uint64_t mInputFrameIndex = 0;
|
||||||
|
uint64_t mOutputFrameScheduleIndex = 0;
|
||||||
|
uint64_t mOutputFrameCompletionIndex = 0;
|
||||||
|
bool mHasLastInputSignal = false;
|
||||||
|
bool mLastInputSignal = false;
|
||||||
|
unsigned mLastInputSignalWidth = 0;
|
||||||
|
unsigned mLastInputSignalHeight = 0;
|
||||||
|
std::string mLastInputSignalModeName;
|
||||||
std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
|
std::chrono::steady_clock::time_point mLastPlayoutCompletionTime;
|
||||||
double mCompletionIntervalMilliseconds = 0.0;
|
double mCompletionIntervalMilliseconds = 0.0;
|
||||||
double mSmoothedCompletionIntervalMilliseconds = 0.0;
|
double mSmoothedCompletionIntervalMilliseconds = 0.0;
|
||||||
|
|||||||
@@ -50,8 +50,10 @@ void TestRuntimeEventPayloadTypes()
|
|||||||
acceptedMutation.action = "SetLayerShader";
|
acceptedMutation.action = "SetLayerShader";
|
||||||
acceptedMutation.accepted = true;
|
acceptedMutation.accepted = true;
|
||||||
acceptedMutation.shaderBuildRequested = true;
|
acceptedMutation.shaderBuildRequested = true;
|
||||||
|
acceptedMutation.persistenceRequested = true;
|
||||||
Expect(RuntimeEventPayloadType(acceptedMutation) == RuntimeEventType::RuntimeMutationAccepted, "accepted mutation payload maps to accepted event type");
|
Expect(RuntimeEventPayloadType(acceptedMutation) == RuntimeEventType::RuntimeMutationAccepted, "accepted mutation payload maps to accepted event type");
|
||||||
Expect(acceptedMutation.shaderBuildRequested, "mutation payload carries shader build follow-up");
|
Expect(acceptedMutation.shaderBuildRequested, "mutation payload carries shader build follow-up");
|
||||||
|
Expect(acceptedMutation.persistenceRequested, "mutation payload carries persistence follow-up");
|
||||||
|
|
||||||
RuntimeMutationEvent rejectedMutation;
|
RuntimeMutationEvent rejectedMutation;
|
||||||
rejectedMutation.accepted = false;
|
rejectedMutation.accepted = false;
|
||||||
@@ -59,6 +61,12 @@ void TestRuntimeEventPayloadTypes()
|
|||||||
Expect(RuntimeEventPayloadType(rejectedMutation) == RuntimeEventType::RuntimeMutationRejected, "rejected mutation payload maps to rejected event type");
|
Expect(RuntimeEventPayloadType(rejectedMutation) == RuntimeEventType::RuntimeMutationRejected, "rejected mutation payload maps to rejected event type");
|
||||||
Expect(rejectedMutation.errorMessage == "Unknown layer.", "mutation payload carries rejection error");
|
Expect(rejectedMutation.errorMessage == "Unknown layer.", "mutation payload carries rejection error");
|
||||||
|
|
||||||
|
RuntimePersistenceRequestedEvent persistence;
|
||||||
|
persistence.reason = "UpdateLayerParameter";
|
||||||
|
persistence.debounceAllowed = true;
|
||||||
|
Expect(RuntimeEventPayloadType(persistence) == RuntimeEventType::RuntimePersistenceRequested, "runtime persistence payload maps to persistence event type");
|
||||||
|
Expect(persistence.debounceAllowed, "runtime persistence payload carries debounce policy");
|
||||||
|
|
||||||
ShaderBuildEvent preparedBuild;
|
ShaderBuildEvent preparedBuild;
|
||||||
preparedBuild.phase = RuntimeEventShaderBuildPhase::Prepared;
|
preparedBuild.phase = RuntimeEventShaderBuildPhase::Prepared;
|
||||||
preparedBuild.inputWidth = 1920;
|
preparedBuild.inputWidth = 1920;
|
||||||
@@ -72,6 +80,40 @@ void TestRuntimeEventPayloadTypes()
|
|||||||
Expect(RuntimeEventPayloadType(appliedReset) == RuntimeEventType::RenderResetApplied, "render reset payload maps applied state");
|
Expect(RuntimeEventPayloadType(appliedReset) == RuntimeEventType::RenderResetApplied, "render reset payload maps applied state");
|
||||||
Expect(appliedReset.scope == RuntimeEventRenderResetScope::TemporalHistoryAndFeedback, "render reset payload carries reset scope");
|
Expect(appliedReset.scope == RuntimeEventRenderResetScope::TemporalHistoryAndFeedback, "render reset payload carries reset scope");
|
||||||
|
|
||||||
|
RenderSnapshotPublishRequestedEvent snapshotRequest;
|
||||||
|
snapshotRequest.outputWidth = 1920;
|
||||||
|
snapshotRequest.outputHeight = 1080;
|
||||||
|
snapshotRequest.reason = "test";
|
||||||
|
Expect(RuntimeEventPayloadType(snapshotRequest) == RuntimeEventType::RenderSnapshotPublishRequested, "render snapshot request payload maps to request event");
|
||||||
|
|
||||||
|
RenderSnapshotPublishedEvent snapshotPublished;
|
||||||
|
snapshotPublished.snapshotVersion = 3;
|
||||||
|
snapshotPublished.parameterVersion = 4;
|
||||||
|
snapshotPublished.layerCount = 2;
|
||||||
|
Expect(RuntimeEventPayloadType(snapshotPublished) == RuntimeEventType::RenderSnapshotPublished, "render snapshot published payload maps to published event");
|
||||||
|
Expect(snapshotPublished.layerCount == 2, "render snapshot published payload carries layer count");
|
||||||
|
|
||||||
|
OutputFrameCompletedEvent completedFrame;
|
||||||
|
completedFrame.result = "Completed";
|
||||||
|
Expect(RuntimeEventPayloadType(completedFrame) == RuntimeEventType::OutputFrameCompleted, "completed output frame payload maps to completed event");
|
||||||
|
completedFrame.result = "DisplayedLate";
|
||||||
|
Expect(RuntimeEventPayloadType(completedFrame) == RuntimeEventType::OutputLateFrameDetected, "late output frame payload maps to late event");
|
||||||
|
completedFrame.result = "Dropped";
|
||||||
|
Expect(RuntimeEventPayloadType(completedFrame) == RuntimeEventType::OutputDroppedFrameDetected, "dropped output frame payload maps to dropped event");
|
||||||
|
|
||||||
|
TimingSampleRecordedEvent timingSample;
|
||||||
|
timingSample.subsystem = "RuntimeEventDispatcher";
|
||||||
|
timingSample.metric = "dispatchDuration";
|
||||||
|
timingSample.value = 0.5;
|
||||||
|
timingSample.unit = "ms";
|
||||||
|
Expect(RuntimeEventPayloadType(timingSample) == RuntimeEventType::TimingSampleRecorded, "timing sample payload maps to timing event");
|
||||||
|
|
||||||
|
QueueDepthChangedEvent queueDepth;
|
||||||
|
queueDepth.queueName = "runtime-events";
|
||||||
|
queueDepth.depth = 1;
|
||||||
|
queueDepth.capacity = 16;
|
||||||
|
Expect(RuntimeEventPayloadType(queueDepth) == RuntimeEventType::QueueDepthChanged, "queue depth payload maps to queue depth event");
|
||||||
|
|
||||||
SubsystemWarningEvent warning;
|
SubsystemWarningEvent warning;
|
||||||
warning.subsystem = "VideoBackend";
|
warning.subsystem = "VideoBackend";
|
||||||
warning.warningKey = "late-frame";
|
warning.warningKey = "late-frame";
|
||||||
@@ -310,6 +352,132 @@ void TestRuntimeEventTestHarness()
|
|||||||
const auto* seenPayload = seenOsc ? std::get_if<OscValueReceivedEvent>(&seenOsc->payload) : nullptr;
|
const auto* seenPayload = seenOsc ? std::get_if<OscValueReceivedEvent>(&seenOsc->payload) : nullptr;
|
||||||
Expect(seenPayload && seenPayload->valueJson == "0.8", "test harness keeps latest coalesced payload");
|
Expect(seenPayload && seenPayload->valueJson == "0.8", "test harness keeps latest coalesced payload");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestAcceptedMutationFollowUps()
|
||||||
|
{
|
||||||
|
RuntimeEventTestHarness harness;
|
||||||
|
|
||||||
|
RuntimeMutationEvent mutation;
|
||||||
|
mutation.action = "SetLayerShader";
|
||||||
|
mutation.accepted = true;
|
||||||
|
mutation.runtimeStateChanged = true;
|
||||||
|
mutation.runtimeStateBroadcastRequired = true;
|
||||||
|
mutation.shaderBuildRequested = true;
|
||||||
|
mutation.persistenceRequested = true;
|
||||||
|
|
||||||
|
RuntimeStateChangedEvent stateChanged;
|
||||||
|
stateChanged.reason = mutation.action;
|
||||||
|
stateChanged.persistenceRequested = true;
|
||||||
|
|
||||||
|
RuntimePersistenceRequestedEvent persistence;
|
||||||
|
persistence.reason = mutation.action;
|
||||||
|
persistence.debounceAllowed = true;
|
||||||
|
|
||||||
|
RuntimeReloadRequestedEvent reload;
|
||||||
|
reload.reason = mutation.action;
|
||||||
|
reload.preserveFeedbackState = false;
|
||||||
|
|
||||||
|
ShaderBuildEvent build;
|
||||||
|
build.phase = RuntimeEventShaderBuildPhase::Requested;
|
||||||
|
build.succeeded = true;
|
||||||
|
build.message = "Shader rebuild queued.";
|
||||||
|
|
||||||
|
Expect(harness.Publish(mutation, "RuntimeCoordinator"), "accepted mutation event publishes");
|
||||||
|
Expect(harness.Publish(stateChanged, "RuntimeCoordinator"), "state changed follow-up publishes");
|
||||||
|
Expect(harness.Publish(persistence, "RuntimeCoordinator"), "persistence follow-up publishes");
|
||||||
|
Expect(harness.Publish(reload, "RuntimeCoordinator"), "reload follow-up publishes");
|
||||||
|
Expect(harness.Publish(build, "RuntimeCoordinator"), "shader build follow-up publishes");
|
||||||
|
|
||||||
|
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||||
|
Expect(result.dispatchedEvents == 5, "accepted mutation dispatches every expected follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimeMutationAccepted) == 1, "accepted mutation fact is observed");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimeStateChanged) == 1, "accepted mutation publishes state changed follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimePersistenceRequested) == 1, "accepted mutation publishes persistence follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimeReloadRequested) == 1, "accepted mutation publishes reload follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::ShaderBuildRequested) == 1, "accepted mutation publishes shader build follow-up");
|
||||||
|
|
||||||
|
const RuntimeEvent* persistenceEvent = harness.LastSeen(RuntimeEventType::RuntimePersistenceRequested);
|
||||||
|
const auto* persistencePayload = persistenceEvent ? std::get_if<RuntimePersistenceRequestedEvent>(&persistenceEvent->payload) : nullptr;
|
||||||
|
Expect(persistencePayload && persistencePayload->reason == "SetLayerShader", "persistence follow-up preserves mutation action reason");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRejectedMutationHasNoDownstreamFollowUps()
|
||||||
|
{
|
||||||
|
RuntimeEventTestHarness harness;
|
||||||
|
|
||||||
|
RuntimeMutationEvent mutation;
|
||||||
|
mutation.action = "SetLayerShader";
|
||||||
|
mutation.accepted = false;
|
||||||
|
mutation.errorMessage = "Unknown shader id: missing";
|
||||||
|
|
||||||
|
Expect(harness.Publish(mutation, "RuntimeCoordinator"), "rejected mutation event publishes");
|
||||||
|
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||||
|
Expect(result.dispatchedEvents == 1, "rejected mutation dispatches only the rejection fact");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimeMutationRejected) == 1, "rejected mutation fact is observed");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimeStateChanged) == 0, "rejected mutation has no state follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimePersistenceRequested) == 0, "rejected mutation has no persistence follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::RuntimeReloadRequested) == 0, "rejected mutation has no reload follow-up");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::ShaderBuildRequested) == 0, "rejected mutation has no shader build follow-up");
|
||||||
|
|
||||||
|
const RuntimeEvent* rejectedEvent = harness.LastSeen(RuntimeEventType::RuntimeMutationRejected);
|
||||||
|
const auto* rejectedPayload = rejectedEvent ? std::get_if<RuntimeMutationEvent>(&rejectedEvent->payload) : nullptr;
|
||||||
|
Expect(rejectedPayload && rejectedPayload->errorMessage == "Unknown shader id: missing", "rejected mutation preserves error message");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestShaderBuildGenerationEventMatching()
|
||||||
|
{
|
||||||
|
RuntimeEventTestHarness harness;
|
||||||
|
std::size_t handledBuilds = 0;
|
||||||
|
uint64_t handledGeneration = 0;
|
||||||
|
|
||||||
|
harness.Dispatcher().Subscribe(RuntimeEventType::ShaderBuildPrepared, [&](const RuntimeEvent& event) {
|
||||||
|
const auto* payload = std::get_if<ShaderBuildEvent>(&event.payload);
|
||||||
|
if (!payload || payload->generation != 7)
|
||||||
|
return;
|
||||||
|
|
||||||
|
++handledBuilds;
|
||||||
|
handledGeneration = payload->generation;
|
||||||
|
});
|
||||||
|
|
||||||
|
ShaderBuildEvent stale;
|
||||||
|
stale.phase = RuntimeEventShaderBuildPhase::Prepared;
|
||||||
|
stale.generation = 6;
|
||||||
|
stale.succeeded = true;
|
||||||
|
|
||||||
|
ShaderBuildEvent current = stale;
|
||||||
|
current.generation = 7;
|
||||||
|
|
||||||
|
Expect(harness.Publish(stale, "ShaderBuildQueue"), "stale shader build event publishes");
|
||||||
|
Expect(harness.Publish(current, "ShaderBuildQueue"), "current shader build event publishes");
|
||||||
|
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||||
|
Expect(result.dispatchedEvents == 2, "shader build readiness events dispatch in order");
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::ShaderBuildPrepared) == 2, "both shader build readiness facts are observable");
|
||||||
|
Expect(handledBuilds == 1, "generation-aware handler applies only the expected build once");
|
||||||
|
Expect(handledGeneration == 7, "generation-aware handler records the expected generation");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestHandlerFailureCanBecomeTelemetryEvent()
|
||||||
|
{
|
||||||
|
RuntimeEventTestHarness harness;
|
||||||
|
harness.Dispatcher().Subscribe(RuntimeEventType::RuntimeStateBroadcastRequested, [](const RuntimeEvent&) {
|
||||||
|
throw std::runtime_error("handler failed");
|
||||||
|
});
|
||||||
|
|
||||||
|
RuntimeStateBroadcastRequestedEvent broadcast;
|
||||||
|
broadcast.reason = "test";
|
||||||
|
Expect(harness.Publish(broadcast, "test"), "broadcast event publishes before failing handler");
|
||||||
|
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||||
|
Expect(result.handlerFailures == 1, "dispatcher reports handler failure for telemetry");
|
||||||
|
|
||||||
|
TimingSampleRecordedEvent timing;
|
||||||
|
timing.subsystem = "RuntimeEventDispatcher";
|
||||||
|
timing.metric = "handlerFailures";
|
||||||
|
timing.value = static_cast<double>(result.handlerFailures);
|
||||||
|
timing.unit = "count";
|
||||||
|
Expect(harness.Publish(timing, "HealthTelemetry"), "handler failure timing sample publishes");
|
||||||
|
harness.DispatchPending();
|
||||||
|
Expect(harness.SeenCount(RuntimeEventType::TimingSampleRecorded) == 1, "handler failure can be observed as telemetry event");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -322,6 +490,10 @@ int main()
|
|||||||
TestRuntimeEventCoalescingQueue();
|
TestRuntimeEventCoalescingQueue();
|
||||||
TestRuntimeEventCoalescingCustomKey();
|
TestRuntimeEventCoalescingCustomKey();
|
||||||
TestRuntimeEventTestHarness();
|
TestRuntimeEventTestHarness();
|
||||||
|
TestAcceptedMutationFollowUps();
|
||||||
|
TestRejectedMutationHasNoDownstreamFollowUps();
|
||||||
|
TestShaderBuildGenerationEventMatching();
|
||||||
|
TestHandlerFailureCanBecomeTelemetryEvent();
|
||||||
|
|
||||||
if (gFailures != 0)
|
if (gFailures != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user