Merge synthetic v210 feed benchmark

This commit is contained in:
Johanness
2026-05-19 14:58:48 +03:00
6 changed files with 337 additions and 1 deletions
+11 -1
View File
@@ -686,6 +686,13 @@ std::optional<FeedKind> parseFeedKind(std::string_view text)
return FeedKind::SmpteBars; return FeedKind::SmpteBars;
} }
if (text == "v210" ||
text == "synthetic-v210" ||
text == "v210test")
{
return FeedKind::SyntheticV210;
}
if (text == "mxlsdk") if (text == "mxlsdk")
{ {
return FeedKind::MxlSdk; return FeedKind::MxlSdk;
@@ -724,6 +731,9 @@ std::string_view feedKindName(FeedKind kind)
case FeedKind::SmpteBars: case FeedKind::SmpteBars:
return "smpte"; return "smpte";
case FeedKind::SyntheticV210:
return "v210";
case FeedKind::MxlSdk: case FeedKind::MxlSdk:
return "mxlsdk"; return "mxlsdk";
@@ -754,7 +764,7 @@ void printUsage(const char* executableName)
<< " --flow <idx> <uuid> Set MXL flow UUID for feed slot\n" << " --flow <idx> <uuid> Set MXL flow UUID for feed slot\n"
<< " --version Print version\n" << " --version Print version\n"
<< " --help Print this help\n" << " --help Print this help\n"
<< "Feed kinds: nosignal, none, fake, mxl, smpte, mxlsdk\n" << "Feed kinds: nosignal, none, fake, mxl, smpte, v210, mxlsdk\n"
<< "Default: 2x2 grid, all slots nosignal" << "Default: 2x2 grid, all slots nosignal"
<< std::endl; << std::endl;
} }
+1
View File
@@ -19,6 +19,7 @@ enum class FeedKind
NoSignal, NoSignal,
MxlPlaceholder, MxlPlaceholder,
SmpteBars, SmpteBars,
SyntheticV210,
MxlSdk MxlSdk
}; };
+1
View File
@@ -32,6 +32,7 @@ add_executable(mxl_multiviewer
MxlInstanceCache.cpp MxlInstanceCache.cpp
MxlSdkFeed.cpp MxlSdkFeed.cpp
SmpteBarsFeed.cpp SmpteBarsFeed.cpp
SyntheticV210Feed.cpp
TileLayout.cpp TileLayout.cpp
VulkanContext.cpp VulkanContext.cpp
VulkanUtils.cpp VulkanUtils.cpp
+13
View File
@@ -4,6 +4,7 @@
#include "MxlFeed.hpp" #include "MxlFeed.hpp"
#include "MxlSdkFeed.hpp" #include "MxlSdkFeed.hpp"
#include "SmpteBarsFeed.hpp" #include "SmpteBarsFeed.hpp"
#include "SyntheticV210Feed.hpp"
#include <iostream> #include <iostream>
@@ -74,6 +75,18 @@ std::unique_ptr<IVideoFeed> createFeed(
frameHeight frameHeight
); );
case FeedKind::SyntheticV210:
if (verbose)
{
std::cerr << "Feed " << (feedIndex + 1)
<< ": synthetic v210" << std::endl;
}
return std::make_unique<SyntheticV210Feed>(
feedIndex,
frameWidth,
frameHeight
);
case FeedKind::NoSignal: case FeedKind::NoSignal:
default: default:
if (verbose) if (verbose)
+256
View File
@@ -0,0 +1,256 @@
#include "SyntheticV210Feed.hpp"
#include "PixelFormat.hpp"
#include <algorithm>
#include <array>
#include <cstring>
#include <sstream>
namespace
{
constexpr uint32_t SyntheticWidth = 1920;
constexpr uint32_t SyntheticHeight = 1080;
constexpr uint32_t SyntheticPatternCount = 4;
uint32_t clamp10(int32_t value)
{
return static_cast<uint32_t>(
value < 0 ? 0 : (value > 1023 ? 1023 : value));
}
uint32_t packWord(uint32_t a, uint32_t b, uint32_t c)
{
return (a & 0x3FFu) |
((b & 0x3FFu) << 10) |
((c & 0x3FFu) << 20);
}
void storeWord(uint8_t* dst, uint32_t word)
{
dst[0] = static_cast<uint8_t>(word & 0xFFu);
dst[1] = static_cast<uint8_t>((word >> 8) & 0xFFu);
dst[2] = static_cast<uint8_t>((word >> 16) & 0xFFu);
dst[3] = static_cast<uint8_t>((word >> 24) & 0xFFu);
}
double syntheticRateForFeed(uint32_t feedIndex)
{
static constexpr std::array<double, 9> Rates =
{
25.0, 25.0, 25.0,
50.0, 50.0, 60.0,
25.0, 50.0, 60.0
};
return Rates[feedIndex % Rates.size()];
}
}
SyntheticV210Feed::SyntheticV210Feed(
uint32_t feedIndex,
uint32_t frameWidth,
uint32_t frameHeight)
: mFeedIndex(feedIndex)
{
mSourceWidth = SyntheticWidth;
mSourceHeight = SyntheticHeight;
mSourceStride = v210LineStride(mSourceWidth);
mFrameRate = syntheticRateForFeed(feedIndex);
mFrame.width = frameWidth;
mFrame.height = frameHeight;
mFrame.pixels.resize(
static_cast<size_t>(frameWidth) * frameHeight,
packRgba8({24, 24, 24}));
generatePayloads();
}
const VideoFrame& SyntheticV210Feed::getFrame(uint32_t frameCounter)
{
const uint64_t index = syntheticIndex(frameCounter);
if (index != mGrainIndex)
{
mGrainIndex = index;
++mFrameVersion;
}
return mFrame;
}
FeedRuntimeStatus SyntheticV210Feed::status() const
{
return FeedRuntimeStatus::Live;
}
std::string SyntheticV210Feed::displayName() const
{
return "SYNTH V210";
}
std::string SyntheticV210Feed::sourceInfo() const
{
std::ostringstream out;
out << "synthetic v210 "
<< mSourceWidth << "x" << mSourceHeight
<< "@" << static_cast<uint32_t>(mFrameRate);
return out.str();
}
uint64_t SyntheticV210Feed::frameVersion() const
{
return mFrameVersion;
}
bool SyntheticV210Feed::hasGrainIndex() const
{
return true;
}
uint64_t SyntheticV210Feed::grainIndex() const
{
return mGrainIndex;
}
bool SyntheticV210Feed::hasFrameRate() const
{
return true;
}
double SyntheticV210Feed::frameRate() const
{
return mFrameRate;
}
float SyntheticV210Feed::srcAspectRatio() const
{
return static_cast<float>(mSourceWidth) /
static_cast<float>(mSourceHeight);
}
bool SyntheticV210Feed::hasV210() const
{
return !mPayloads.empty();
}
const uint8_t* SyntheticV210Feed::v210Data() const
{
if (mPayloads.empty())
{
return nullptr;
}
return mPayloads[mGrainIndex % mPayloads.size()].data();
}
bool SyntheticV210Feed::supportsDirectV210Read() const
{
return true;
}
bool SyntheticV210Feed::readV210FrameInto(
uint32_t frameCounter,
void* destination,
size_t destinationSize)
{
if (destination == nullptr || mPayloads.empty())
{
return false;
}
const uint64_t index = syntheticIndex(frameCounter);
if (index == mGrainIndex)
{
return false;
}
const std::vector<uint8_t>& payload =
mPayloads[index % mPayloads.size()];
if (destinationSize < payload.size())
{
return false;
}
std::memcpy(destination, payload.data(), payload.size());
mGrainIndex = index;
++mFrameVersion;
return true;
}
uint32_t SyntheticV210Feed::v210Width() const
{
return mSourceWidth;
}
uint32_t SyntheticV210Feed::v210Height() const
{
return mSourceHeight;
}
uint32_t SyntheticV210Feed::v210Stride() const
{
return mSourceStride;
}
uint64_t SyntheticV210Feed::syntheticIndex(
uint32_t frameCounter) const
{
return static_cast<uint64_t>(
(static_cast<double>(frameCounter) * mFrameRate) / 60.0);
}
void SyntheticV210Feed::generatePayloads()
{
const size_t payloadSize =
static_cast<size_t>(mSourceStride) * mSourceHeight;
mPayloads.resize(SyntheticPatternCount);
for (uint32_t pattern = 0;
pattern < SyntheticPatternCount;
++pattern)
{
std::vector<uint8_t>& payload = mPayloads[pattern];
payload.assign(payloadSize, 0);
for (uint32_t y = 0; y < mSourceHeight; ++y)
{
encodeLine(
y,
pattern,
payload.data() +
static_cast<size_t>(y) * mSourceStride);
}
}
}
void SyntheticV210Feed::encodeLine(
uint32_t y,
uint32_t patternIndex,
uint8_t* line) const
{
const uint32_t groups = (mSourceWidth + 5) / 6;
const uint32_t bandHeight = std::max(1u, mSourceHeight / 8u);
const uint32_t yBand = (y / bandHeight + patternIndex) % 8u;
for (uint32_t group = 0; group < groups; ++group)
{
const uint32_t x = group * 6u;
const uint32_t xBand =
((x * 8u) / mSourceWidth + mFeedIndex + patternIndex) %
8u;
const uint32_t luma =
clamp10(96 + static_cast<int32_t>(xBand) * 96);
const uint32_t altLuma =
clamp10(96 + static_cast<int32_t>(yBand) * 96);
const uint32_t cb =
clamp10(512 + (static_cast<int32_t>(xBand) - 3) * 56);
const uint32_t cr =
clamp10(512 + (static_cast<int32_t>(yBand) - 3) * 56);
uint8_t* dst = line + group * 16u;
storeWord(dst + 0, packWord(cb, luma, cr));
storeWord(dst + 4, packWord(altLuma, cb, luma));
storeWord(dst + 8, packWord(cr, altLuma, cb));
storeWord(dst + 12, packWord(luma, cr, altLuma));
}
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "IVideoFeed.hpp"
#include <cstddef>
#include <cstdint>
#include <vector>
class SyntheticV210Feed : public IVideoFeed
{
public:
SyntheticV210Feed(
uint32_t feedIndex,
uint32_t frameWidth,
uint32_t frameHeight);
const VideoFrame& getFrame(uint32_t frameCounter) override;
FeedRuntimeStatus status() const override;
std::string displayName() const override;
std::string sourceInfo() const override;
uint64_t frameVersion() const override;
bool hasGrainIndex() const override;
uint64_t grainIndex() const override;
bool hasFrameRate() const override;
double frameRate() const override;
float srcAspectRatio() const override;
bool hasV210() const override;
const uint8_t* v210Data() const override;
bool supportsDirectV210Read() const override;
bool readV210FrameInto(
uint32_t frameCounter,
void* destination,
size_t destinationSize) override;
uint32_t v210Width() const override;
uint32_t v210Height() const override;
uint32_t v210Stride() const override;
private:
uint64_t syntheticIndex(uint32_t frameCounter) const;
void generatePayloads();
void encodeLine(
uint32_t y,
uint32_t patternIndex,
uint8_t* line) const;
uint32_t mFeedIndex = 0;
uint32_t mSourceWidth = 1920;
uint32_t mSourceHeight = 1080;
uint32_t mSourceStride = 0;
double mFrameRate = 60.0;
uint64_t mFrameVersion = 0;
uint64_t mGrainIndex = 0;
std::vector<std::vector<uint8_t>> mPayloads;
VideoFrame mFrame;
};