Json telemetry
This commit is contained in:
@@ -308,6 +308,8 @@ set(RENDER_CADENCE_APP_SOURCES
|
|||||||
"${RENDER_CADENCE_APP_DIR}/frames/SystemFrameExchange.cpp"
|
"${RENDER_CADENCE_APP_DIR}/frames/SystemFrameExchange.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/frames/SystemFrameExchange.h"
|
"${RENDER_CADENCE_APP_DIR}/frames/SystemFrameExchange.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/frames/SystemFrameTypes.h"
|
"${RENDER_CADENCE_APP_DIR}/frames/SystemFrameTypes.h"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json/JsonWriter.cpp"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json/JsonWriter.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/logging/Logger.cpp"
|
"${RENDER_CADENCE_APP_DIR}/logging/Logger.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/logging/Logger.h"
|
"${RENDER_CADENCE_APP_DIR}/logging/Logger.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/platform/HiddenGlWindow.cpp"
|
"${RENDER_CADENCE_APP_DIR}/platform/HiddenGlWindow.cpp"
|
||||||
@@ -330,6 +332,7 @@ set(RENDER_CADENCE_APP_SOURCES
|
|||||||
"${RENDER_CADENCE_APP_DIR}/runtime/RuntimeShaderBridge.h"
|
"${RENDER_CADENCE_APP_DIR}/runtime/RuntimeShaderBridge.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/runtime/RuntimeSlangShaderCompiler.cpp"
|
"${RENDER_CADENCE_APP_DIR}/runtime/RuntimeSlangShaderCompiler.cpp"
|
||||||
"${RENDER_CADENCE_APP_DIR}/runtime/RuntimeSlangShaderCompiler.h"
|
"${RENDER_CADENCE_APP_DIR}/runtime/RuntimeSlangShaderCompiler.h"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/telemetry/CadenceTelemetryJson.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/telemetry/CadenceTelemetry.h"
|
"${RENDER_CADENCE_APP_DIR}/telemetry/CadenceTelemetry.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/telemetry/TelemetryPrinter.h"
|
"${RENDER_CADENCE_APP_DIR}/telemetry/TelemetryPrinter.h"
|
||||||
"${RENDER_CADENCE_APP_DIR}/video/DeckLinkOutput.cpp"
|
"${RENDER_CADENCE_APP_DIR}/video/DeckLinkOutput.cpp"
|
||||||
@@ -351,6 +354,7 @@ target_include_directories(RenderCadenceCompositor PRIVATE
|
|||||||
"${RENDER_CADENCE_APP_DIR}"
|
"${RENDER_CADENCE_APP_DIR}"
|
||||||
"${RENDER_CADENCE_APP_DIR}/app"
|
"${RENDER_CADENCE_APP_DIR}/app"
|
||||||
"${RENDER_CADENCE_APP_DIR}/frames"
|
"${RENDER_CADENCE_APP_DIR}/frames"
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json"
|
||||||
"${RENDER_CADENCE_APP_DIR}/logging"
|
"${RENDER_CADENCE_APP_DIR}/logging"
|
||||||
"${RENDER_CADENCE_APP_DIR}/platform"
|
"${RENDER_CADENCE_APP_DIR}/platform"
|
||||||
"${RENDER_CADENCE_APP_DIR}/render"
|
"${RENDER_CADENCE_APP_DIR}/render"
|
||||||
@@ -775,10 +779,12 @@ endif()
|
|||||||
add_test(NAME RenderCadenceCompositorClockTests COMMAND RenderCadenceCompositorClockTests)
|
add_test(NAME RenderCadenceCompositorClockTests COMMAND RenderCadenceCompositorClockTests)
|
||||||
|
|
||||||
add_executable(RenderCadenceCompositorTelemetryTests
|
add_executable(RenderCadenceCompositorTelemetryTests
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json/JsonWriter.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RenderCadenceCompositorTelemetryTests.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RenderCadenceCompositorTelemetryTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(RenderCadenceCompositorTelemetryTests PRIVATE
|
target_include_directories(RenderCadenceCompositorTelemetryTests PRIVATE
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json"
|
||||||
"${RENDER_CADENCE_APP_DIR}/telemetry"
|
"${RENDER_CADENCE_APP_DIR}/telemetry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -821,6 +827,21 @@ endif()
|
|||||||
|
|
||||||
add_test(NAME RenderCadenceCompositorLoggerTests COMMAND RenderCadenceCompositorLoggerTests)
|
add_test(NAME RenderCadenceCompositorLoggerTests COMMAND RenderCadenceCompositorLoggerTests)
|
||||||
|
|
||||||
|
add_executable(RenderCadenceCompositorJsonWriterTests
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json/JsonWriter.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/RenderCadenceCompositorJsonWriterTests.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(RenderCadenceCompositorJsonWriterTests PRIVATE
|
||||||
|
"${RENDER_CADENCE_APP_DIR}/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(RenderCadenceCompositorJsonWriterTests PRIVATE /W3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME RenderCadenceCompositorJsonWriterTests COMMAND RenderCadenceCompositorJsonWriterTests)
|
||||||
|
|
||||||
add_executable(SystemOutputFramePoolTests
|
add_executable(SystemOutputFramePoolTests
|
||||||
"${APP_DIR}/videoio/SystemOutputFramePool.cpp"
|
"${APP_DIR}/videoio/SystemOutputFramePool.cpp"
|
||||||
"${APP_DIR}/videoio/VideoIOFormat.cpp"
|
"${APP_DIR}/videoio/VideoIOFormat.cpp"
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ Included now:
|
|||||||
- render-thread-only GL commit once the artifact is ready
|
- render-thread-only GL commit once the artifact is ready
|
||||||
- manifest-driven stateless single-pass shader packages
|
- manifest-driven stateless single-pass shader packages
|
||||||
- default float, vec2, color, boolean, enum, and trigger parameters
|
- default float, vec2, color, boolean, enum, and trigger parameters
|
||||||
|
- small JSON writer for future HTTP/WebSocket payloads
|
||||||
|
- JSON serialization for cadence telemetry snapshots
|
||||||
- background logging with `log`, `warning`, and `error` levels
|
- background logging with `log`, `warning`, and `error` levels
|
||||||
- compact telemetry
|
- compact telemetry
|
||||||
- non-GL frame-exchange tests
|
- non-GL frame-exchange tests
|
||||||
@@ -189,6 +191,7 @@ This app keeps the same core behavior but splits it into modules that can grow:
|
|||||||
- `frames/`: system-memory handoff
|
- `frames/`: system-memory handoff
|
||||||
- `platform/`: COM/Win32/hidden GL context support
|
- `platform/`: COM/Win32/hidden GL context support
|
||||||
- `render/`: cadence, simple rendering, PBO readback
|
- `render/`: cadence, simple rendering, PBO readback
|
||||||
|
- `json/`: compact JSON serialization helpers
|
||||||
- `video/`: DeckLink output wrapper and scheduling thread
|
- `video/`: DeckLink output wrapper and scheduling thread
|
||||||
- `telemetry/`: cadence telemetry
|
- `telemetry/`: cadence telemetry
|
||||||
- `app/`: startup/shutdown orchestration
|
- `app/`: startup/shutdown orchestration
|
||||||
|
|||||||
235
apps/RenderCadenceCompositor/json/JsonWriter.cpp
Normal file
235
apps/RenderCadenceCompositor/json/JsonWriter.cpp
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#include "JsonWriter.h"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr int kMaxDepth = 32;
|
||||||
|
|
||||||
|
void AppendHexEscape(std::ostringstream& stream, unsigned char value)
|
||||||
|
{
|
||||||
|
stream << "\\u"
|
||||||
|
<< std::hex << std::uppercase << std::setw(4) << std::setfill('0')
|
||||||
|
<< static_cast<int>(value)
|
||||||
|
<< std::dec << std::nouppercase << std::setfill(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::BeginObject()
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << "{";
|
||||||
|
PushScope(ScopeKind::Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::EndObject()
|
||||||
|
{
|
||||||
|
PopScope(ScopeKind::Object);
|
||||||
|
mStream << "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::BeginArray()
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << "[";
|
||||||
|
PushScope(ScopeKind::Array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::EndArray()
|
||||||
|
{
|
||||||
|
PopScope(ScopeKind::Array);
|
||||||
|
mStream << "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::Key(const std::string& name)
|
||||||
|
{
|
||||||
|
BeginKey();
|
||||||
|
mStream << "\"" << EscapeString(name) << "\":";
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::String(const std::string& value)
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << "\"" << EscapeString(value) << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::Bool(bool value)
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << (value ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::Null()
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::Int(int64_t value)
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::UInt(uint64_t value)
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::Double(double value)
|
||||||
|
{
|
||||||
|
BeginValue();
|
||||||
|
mStream << std::setprecision(15) << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::KeyString(const std::string& name, const std::string& value)
|
||||||
|
{
|
||||||
|
Key(name);
|
||||||
|
String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::KeyBool(const std::string& name, bool value)
|
||||||
|
{
|
||||||
|
Key(name);
|
||||||
|
Bool(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::KeyNull(const std::string& name)
|
||||||
|
{
|
||||||
|
Key(name);
|
||||||
|
Null();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::KeyInt(const std::string& name, int64_t value)
|
||||||
|
{
|
||||||
|
Key(name);
|
||||||
|
Int(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::KeyUInt(const std::string& name, uint64_t value)
|
||||||
|
{
|
||||||
|
Key(name);
|
||||||
|
UInt(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::KeyDouble(const std::string& name, double value)
|
||||||
|
{
|
||||||
|
Key(name);
|
||||||
|
Double(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JsonWriter::StringValue() const
|
||||||
|
{
|
||||||
|
if (mScopeDepth != 0)
|
||||||
|
throw std::logic_error("JSON document has unclosed scopes.");
|
||||||
|
return mStream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::Reset()
|
||||||
|
{
|
||||||
|
mStream.str(std::string());
|
||||||
|
mStream.clear();
|
||||||
|
mScopeDepth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string JsonWriter::EscapeString(const std::string& value)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
for (unsigned char character : value)
|
||||||
|
{
|
||||||
|
switch (character)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
stream << "\\\"";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
stream << "\\\\";
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
stream << "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
stream << "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
stream << "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
stream << "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
stream << "\\t";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (character < 0x20)
|
||||||
|
AppendHexEscape(stream, character);
|
||||||
|
else
|
||||||
|
stream << character;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::BeginValue()
|
||||||
|
{
|
||||||
|
if (mScopeDepth == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Scope& scope = mScopes[mScopeDepth - 1];
|
||||||
|
if (scope.kind == ScopeKind::Object)
|
||||||
|
{
|
||||||
|
if (!scope.expectingValue)
|
||||||
|
throw std::logic_error("JSON object value must follow a key.");
|
||||||
|
scope.expectingValue = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scope.first)
|
||||||
|
mStream << ",";
|
||||||
|
scope.first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::BeginKey()
|
||||||
|
{
|
||||||
|
if (mScopeDepth == 0)
|
||||||
|
throw std::logic_error("JSON key cannot be written outside an object.");
|
||||||
|
|
||||||
|
Scope& scope = mScopes[mScopeDepth - 1];
|
||||||
|
if (scope.kind != ScopeKind::Object)
|
||||||
|
throw std::logic_error("JSON key cannot be written inside an array.");
|
||||||
|
if (scope.expectingValue)
|
||||||
|
throw std::logic_error("JSON object key cannot be written before its previous value.");
|
||||||
|
|
||||||
|
if (!scope.first)
|
||||||
|
mStream << ",";
|
||||||
|
scope.first = false;
|
||||||
|
scope.expectingValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::PushScope(ScopeKind kind)
|
||||||
|
{
|
||||||
|
if (mScopeDepth >= kMaxDepth)
|
||||||
|
throw std::logic_error("JSON nesting is too deep.");
|
||||||
|
|
||||||
|
mScopes[mScopeDepth++] = Scope{ kind, true, false };
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonWriter::PopScope(ScopeKind kind)
|
||||||
|
{
|
||||||
|
if (mScopeDepth == 0)
|
||||||
|
throw std::logic_error("JSON scope underflow.");
|
||||||
|
|
||||||
|
Scope& scope = mScopes[mScopeDepth - 1];
|
||||||
|
if (scope.kind != kind)
|
||||||
|
throw std::logic_error("JSON scope kind mismatch.");
|
||||||
|
if (scope.expectingValue)
|
||||||
|
throw std::logic_error("JSON object key is missing a value.");
|
||||||
|
|
||||||
|
--mScopeDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
apps/RenderCadenceCompositor/json/JsonWriter.h
Normal file
60
apps/RenderCadenceCompositor/json/JsonWriter.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
class JsonWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void BeginObject();
|
||||||
|
void EndObject();
|
||||||
|
void BeginArray();
|
||||||
|
void EndArray();
|
||||||
|
|
||||||
|
void Key(const std::string& name);
|
||||||
|
void String(const std::string& value);
|
||||||
|
void Bool(bool value);
|
||||||
|
void Null();
|
||||||
|
void Int(int64_t value);
|
||||||
|
void UInt(uint64_t value);
|
||||||
|
void Double(double value);
|
||||||
|
|
||||||
|
void KeyString(const std::string& name, const std::string& value);
|
||||||
|
void KeyBool(const std::string& name, bool value);
|
||||||
|
void KeyNull(const std::string& name);
|
||||||
|
void KeyInt(const std::string& name, int64_t value);
|
||||||
|
void KeyUInt(const std::string& name, uint64_t value);
|
||||||
|
void KeyDouble(const std::string& name, double value);
|
||||||
|
|
||||||
|
std::string StringValue() const;
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
static std::string EscapeString(const std::string& value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class ScopeKind
|
||||||
|
{
|
||||||
|
Object,
|
||||||
|
Array
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Scope
|
||||||
|
{
|
||||||
|
ScopeKind kind = ScopeKind::Object;
|
||||||
|
bool first = true;
|
||||||
|
bool expectingValue = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void BeginValue();
|
||||||
|
void BeginKey();
|
||||||
|
void PushScope(ScopeKind kind);
|
||||||
|
void PopScope(ScopeKind kind);
|
||||||
|
|
||||||
|
std::ostringstream mStream;
|
||||||
|
Scope mScopes[32];
|
||||||
|
int mScopeDepth = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CadenceTelemetry.h"
|
||||||
|
#include "../json/JsonWriter.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace RenderCadenceCompositor
|
||||||
|
{
|
||||||
|
inline void WriteCadenceTelemetryJson(JsonWriter& writer, const CadenceTelemetrySnapshot& snapshot)
|
||||||
|
{
|
||||||
|
writer.BeginObject();
|
||||||
|
writer.KeyDouble("sampleSeconds", snapshot.sampleSeconds);
|
||||||
|
writer.KeyDouble("renderFps", snapshot.renderFps);
|
||||||
|
writer.KeyDouble("scheduleFps", snapshot.scheduleFps);
|
||||||
|
writer.KeyUInt("free", static_cast<uint64_t>(snapshot.freeFrames));
|
||||||
|
writer.KeyUInt("completed", static_cast<uint64_t>(snapshot.completedFrames));
|
||||||
|
writer.KeyUInt("scheduled", static_cast<uint64_t>(snapshot.scheduledFrames));
|
||||||
|
writer.KeyUInt("renderedTotal", snapshot.renderedTotal);
|
||||||
|
writer.KeyUInt("scheduledTotal", snapshot.scheduledTotal);
|
||||||
|
writer.KeyUInt("completedPollMisses", snapshot.completedPollMisses);
|
||||||
|
writer.KeyUInt("scheduleFailures", snapshot.scheduleFailures);
|
||||||
|
writer.KeyUInt("completions", snapshot.completions);
|
||||||
|
writer.KeyUInt("late", snapshot.displayedLate);
|
||||||
|
writer.KeyUInt("dropped", snapshot.dropped);
|
||||||
|
writer.KeyUInt("shaderCommitted", snapshot.shaderBuildsCommitted);
|
||||||
|
writer.KeyUInt("shaderFailures", snapshot.shaderBuildFailures);
|
||||||
|
writer.KeyBool("deckLinkBufferedAvailable", snapshot.deckLinkBufferedAvailable);
|
||||||
|
writer.Key("deckLinkBuffered");
|
||||||
|
if (snapshot.deckLinkBufferedAvailable)
|
||||||
|
writer.UInt(snapshot.deckLinkBuffered);
|
||||||
|
else
|
||||||
|
writer.Null();
|
||||||
|
writer.KeyDouble("scheduleCallMs", snapshot.deckLinkScheduleCallMilliseconds);
|
||||||
|
writer.EndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string CadenceTelemetryToJson(const CadenceTelemetrySnapshot& snapshot)
|
||||||
|
{
|
||||||
|
JsonWriter writer;
|
||||||
|
WriteCadenceTelemetryJson(writer, snapshot);
|
||||||
|
return writer.StringValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
123
tests/RenderCadenceCompositorJsonWriterTests.cpp
Normal file
123
tests/RenderCadenceCompositorJsonWriterTests.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include "JsonWriter.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int gFailures = 0;
|
||||||
|
|
||||||
|
void Expect(bool condition, const std::string& message)
|
||||||
|
{
|
||||||
|
if (condition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
++gFailures;
|
||||||
|
std::cerr << "FAILED: " << message << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExpectEquals(const std::string& actual, const std::string& expected, const std::string& message)
|
||||||
|
{
|
||||||
|
if (actual == expected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
++gFailures;
|
||||||
|
std::cerr << "FAILED: " << message << "\n"
|
||||||
|
<< "expected: " << expected << "\n"
|
||||||
|
<< "actual: " << actual << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEscapesStrings()
|
||||||
|
{
|
||||||
|
using RenderCadenceCompositor::JsonWriter;
|
||||||
|
|
||||||
|
ExpectEquals(
|
||||||
|
JsonWriter::EscapeString("quote\" slash\\ newline\n tab\t"),
|
||||||
|
"quote\\\" slash\\\\ newline\\n tab\\t",
|
||||||
|
"string escape handles common escaped characters");
|
||||||
|
|
||||||
|
std::string control;
|
||||||
|
control.push_back(static_cast<char>(0x01));
|
||||||
|
ExpectEquals(JsonWriter::EscapeString(control), "\\u0001", "string escape handles control characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestObjectSerialization()
|
||||||
|
{
|
||||||
|
using RenderCadenceCompositor::JsonWriter;
|
||||||
|
|
||||||
|
JsonWriter writer;
|
||||||
|
writer.BeginObject();
|
||||||
|
writer.KeyString("name", "cadence");
|
||||||
|
writer.KeyDouble("renderFps", 59.94);
|
||||||
|
writer.KeyBool("healthy", true);
|
||||||
|
writer.KeyNull("error");
|
||||||
|
writer.EndObject();
|
||||||
|
|
||||||
|
ExpectEquals(
|
||||||
|
writer.StringValue(),
|
||||||
|
"{\"name\":\"cadence\",\"renderFps\":59.94,\"healthy\":true,\"error\":null}",
|
||||||
|
"object serialization is compact and ordered");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestNestedArrays()
|
||||||
|
{
|
||||||
|
using RenderCadenceCompositor::JsonWriter;
|
||||||
|
|
||||||
|
JsonWriter writer;
|
||||||
|
writer.BeginObject();
|
||||||
|
writer.Key("levels");
|
||||||
|
writer.BeginArray();
|
||||||
|
writer.String("log");
|
||||||
|
writer.String("warning");
|
||||||
|
writer.String("error");
|
||||||
|
writer.EndArray();
|
||||||
|
writer.Key("counts");
|
||||||
|
writer.BeginObject();
|
||||||
|
writer.KeyUInt("queued", 3);
|
||||||
|
writer.KeyInt("delta", -1);
|
||||||
|
writer.EndObject();
|
||||||
|
writer.EndObject();
|
||||||
|
|
||||||
|
ExpectEquals(
|
||||||
|
writer.StringValue(),
|
||||||
|
"{\"levels\":[\"log\",\"warning\",\"error\"],\"counts\":{\"queued\":3,\"delta\":-1}}",
|
||||||
|
"nested arrays and objects serialize correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMisuseThrows()
|
||||||
|
{
|
||||||
|
using RenderCadenceCompositor::JsonWriter;
|
||||||
|
|
||||||
|
JsonWriter writer;
|
||||||
|
bool threw = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
writer.BeginObject();
|
||||||
|
writer.Key("missing");
|
||||||
|
writer.EndObject();
|
||||||
|
}
|
||||||
|
catch (const std::logic_error&)
|
||||||
|
{
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
Expect(threw, "ending an object with a key missing a value throws");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
TestEscapesStrings();
|
||||||
|
TestObjectSerialization();
|
||||||
|
TestNestedArrays();
|
||||||
|
TestMisuseThrows();
|
||||||
|
|
||||||
|
if (gFailures != 0)
|
||||||
|
{
|
||||||
|
std::cerr << gFailures << " RenderCadenceCompositorJsonWriter test failure(s).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "RenderCadenceCompositorJsonWriter tests passed.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "CadenceTelemetry.h"
|
#include "CadenceTelemetry.h"
|
||||||
|
#include "CadenceTelemetryJson.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -128,12 +129,60 @@ void TestTelemetryComputesRatesFromDeltas()
|
|||||||
Expect(snapshot.renderFps > 0.0, "render fps is computed from completed frame delta");
|
Expect(snapshot.renderFps > 0.0, "render fps is computed from completed frame delta");
|
||||||
Expect(snapshot.scheduleFps > 0.0, "schedule fps is computed from scheduled frame delta");
|
Expect(snapshot.scheduleFps > 0.0, "schedule fps is computed from scheduled frame delta");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestTelemetrySerializesToJson()
|
||||||
|
{
|
||||||
|
RenderCadenceCompositor::CadenceTelemetrySnapshot snapshot;
|
||||||
|
snapshot.sampleSeconds = 1.0;
|
||||||
|
snapshot.renderFps = 59.94;
|
||||||
|
snapshot.scheduleFps = 60.0;
|
||||||
|
snapshot.freeFrames = 7;
|
||||||
|
snapshot.completedFrames = 1;
|
||||||
|
snapshot.scheduledFrames = 4;
|
||||||
|
snapshot.renderedTotal = 120;
|
||||||
|
snapshot.scheduledTotal = 118;
|
||||||
|
snapshot.completedPollMisses = 3;
|
||||||
|
snapshot.scheduleFailures = 0;
|
||||||
|
snapshot.completions = 117;
|
||||||
|
snapshot.displayedLate = 1;
|
||||||
|
snapshot.dropped = 2;
|
||||||
|
snapshot.shaderBuildsCommitted = 1;
|
||||||
|
snapshot.shaderBuildFailures = 0;
|
||||||
|
snapshot.deckLinkBufferedAvailable = true;
|
||||||
|
snapshot.deckLinkBuffered = 4;
|
||||||
|
snapshot.deckLinkScheduleCallMilliseconds = 1.25;
|
||||||
|
|
||||||
|
const std::string json = RenderCadenceCompositor::CadenceTelemetryToJson(snapshot);
|
||||||
|
const std::string expected =
|
||||||
|
"{\"sampleSeconds\":1,\"renderFps\":59.94,\"scheduleFps\":60,"
|
||||||
|
"\"free\":7,\"completed\":1,\"scheduled\":4,"
|
||||||
|
"\"renderedTotal\":120,\"scheduledTotal\":118,"
|
||||||
|
"\"completedPollMisses\":3,\"scheduleFailures\":0,"
|
||||||
|
"\"completions\":117,\"late\":1,\"dropped\":2,"
|
||||||
|
"\"shaderCommitted\":1,\"shaderFailures\":0,"
|
||||||
|
"\"deckLinkBufferedAvailable\":true,\"deckLinkBuffered\":4,"
|
||||||
|
"\"scheduleCallMs\":1.25}";
|
||||||
|
Expect(json == expected, "telemetry snapshot serializes to stable JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestUnavailableDeckLinkBufferSerializesAsNull()
|
||||||
|
{
|
||||||
|
RenderCadenceCompositor::CadenceTelemetrySnapshot snapshot;
|
||||||
|
snapshot.deckLinkBufferedAvailable = false;
|
||||||
|
|
||||||
|
const std::string json = RenderCadenceCompositor::CadenceTelemetryToJson(snapshot);
|
||||||
|
Expect(
|
||||||
|
json.find("\"deckLinkBufferedAvailable\":false,\"deckLinkBuffered\":null") != std::string::npos,
|
||||||
|
"unavailable DeckLink buffer depth serializes as null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
TestTelemetrySamplesCompletedPollMissesAndShaderCounts();
|
TestTelemetrySamplesCompletedPollMissesAndShaderCounts();
|
||||||
TestTelemetryComputesRatesFromDeltas();
|
TestTelemetryComputesRatesFromDeltas();
|
||||||
|
TestTelemetrySerializesToJson();
|
||||||
|
TestUnavailableDeckLinkBufferSerializesAsNull();
|
||||||
|
|
||||||
if (gFailures != 0)
|
if (gFailures != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user