#include "SyntheticInputProducer.h" #include "VideoIOFormat.h" #include #include namespace RenderCadenceCompositor { SyntheticInputProducer::SyntheticInputProducer(InputFrameMailbox& mailbox, SyntheticInputProducerConfig config) : mMailbox(mailbox), mConfig(config) { } SyntheticInputProducer::~SyntheticInputProducer() { Stop(); } bool SyntheticInputProducer::Start() { if (mThread.joinable()) return true; if (mConfig.width == 0 || mConfig.height == 0) return false; mStopping.store(false, std::memory_order_release); mThread = std::thread([this]() { ThreadMain(); }); return true; } void SyntheticInputProducer::Stop() { mStopping.store(true, std::memory_order_release); if (mThread.joinable()) mThread.join(); } SyntheticInputProducerMetrics SyntheticInputProducer::Metrics() const { SyntheticInputProducerMetrics metrics; metrics.generatedFrames = mGeneratedFrames.load(std::memory_order_relaxed); metrics.submitMisses = mSubmitMisses.load(std::memory_order_relaxed); return metrics; } void SyntheticInputProducer::ThreadMain() { const unsigned rowBytes = VideoIORowBytes(VideoIOPixelFormat::Bgra8, mConfig.width); std::vector buffer(static_cast(rowBytes) * static_cast(mConfig.height)); const auto frameDuration = std::chrono::duration_cast( std::chrono::duration(mConfig.frameDurationMilliseconds)); uint64_t frameIndex = 0; auto nextFrameTime = std::chrono::steady_clock::now(); while (!mStopping.load(std::memory_order_acquire)) { const auto now = std::chrono::steady_clock::now(); if (now < nextFrameTime) { std::this_thread::sleep_for((std::min)(std::chrono::milliseconds(1), std::chrono::duration_cast(nextFrameTime - now))); continue; } GenerateFrame(frameIndex, buffer); if (!mMailbox.SubmitFrame(buffer.data(), rowBytes, frameIndex)) mSubmitMisses.fetch_add(1, std::memory_order_relaxed); mGeneratedFrames.fetch_add(1, std::memory_order_relaxed); ++frameIndex; nextFrameTime += frameDuration; if (std::chrono::steady_clock::now() - nextFrameTime > frameDuration * 4) nextFrameTime = std::chrono::steady_clock::now() + frameDuration; } } void SyntheticInputProducer::GenerateFrame(uint64_t frameIndex, std::vector& buffer) const { const float t = static_cast(frameIndex) / 60.0f; const unsigned boxWidth = (std::max)(1u, mConfig.width / 5u); const unsigned boxHeight = (std::max)(1u, mConfig.height / 6u); const unsigned maxX = mConfig.width > boxWidth ? mConfig.width - boxWidth : 0u; const unsigned maxY = mConfig.height > boxHeight ? mConfig.height - boxHeight : 0u; const unsigned boxX = static_cast((0.5f + 0.5f * std::sin(t * 1.4f)) * static_cast(maxX)); const unsigned boxY = static_cast((0.5f + 0.5f * std::sin(t * 0.9f + 1.2f)) * static_cast(maxY)); const unsigned rowBytes = VideoIORowBytes(VideoIOPixelFormat::Bgra8, mConfig.width); for (unsigned y = 0; y < mConfig.height; ++y) { for (unsigned x = 0; x < mConfig.width; ++x) { const std::size_t offset = static_cast(y) * rowBytes + static_cast(x) * 4; const unsigned checker = ((x / 80u) + (y / 80u) + static_cast(frameIndex / 15u)) & 1u; unsigned char red = checker ? 42 : 16; unsigned char green = checker ? 70 : 28; unsigned char blue = checker ? 110 : 55; if (x >= boxX && x < boxX + boxWidth && y >= boxY && y < boxY + boxHeight) { red = 245; green = static_cast(160 + 60 * (0.5f + 0.5f * std::sin(t * 2.1f))); blue = 35; } buffer[offset + 0] = blue; buffer[offset + 1] = green; buffer[offset + 2] = red; buffer[offset + 3] = 255; } } } }