Json telemetry
This commit is contained in:
@@ -44,6 +44,8 @@ Included now:
|
||||
- render-thread-only GL commit once the artifact is ready
|
||||
- manifest-driven stateless single-pass shader packages
|
||||
- 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
|
||||
- compact telemetry
|
||||
- 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
|
||||
- `platform/`: COM/Win32/hidden GL context support
|
||||
- `render/`: cadence, simple rendering, PBO readback
|
||||
- `json/`: compact JSON serialization helpers
|
||||
- `video/`: DeckLink output wrapper and scheduling thread
|
||||
- `telemetry/`: cadence telemetry
|
||||
- `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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user