V2 working
This commit is contained in:
245
apps/RenderCadenceCompositor/frames/SystemFrameExchange.cpp
Normal file
245
apps/RenderCadenceCompositor/frames/SystemFrameExchange.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "SystemFrameExchange.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
SystemFrameExchangeConfig NormalizeConfig(SystemFrameExchangeConfig config)
|
||||
{
|
||||
if (config.rowBytes == 0)
|
||||
config.rowBytes = VideoIORowBytes(config.pixelFormat, config.width);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
SystemFrameExchange::SystemFrameExchange(const SystemFrameExchangeConfig& config)
|
||||
{
|
||||
Configure(config);
|
||||
}
|
||||
|
||||
void SystemFrameExchange::Configure(const SystemFrameExchangeConfig& config)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mConfig = NormalizeConfig(config);
|
||||
mCompletedIndices.clear();
|
||||
mSlots.clear();
|
||||
mSlots.resize(mConfig.capacity);
|
||||
|
||||
const std::size_t byteCount = FrameByteCount();
|
||||
for (Slot& slot : mSlots)
|
||||
{
|
||||
slot.bytes.resize(byteCount);
|
||||
slot.state = SystemFrameSlotState::Free;
|
||||
slot.frameIndex = 0;
|
||||
++slot.generation;
|
||||
}
|
||||
|
||||
mCounters = SystemFrameExchangeMetrics();
|
||||
mCondition.notify_all();
|
||||
}
|
||||
|
||||
SystemFrameExchangeConfig SystemFrameExchange::Config() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::AcquireForRender(SystemFrame& frame)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (!AcquireFreeLocked(frame))
|
||||
{
|
||||
if (!DropOldestCompletedLocked() || !AcquireFreeLocked(frame))
|
||||
{
|
||||
frame = SystemFrame();
|
||||
++mCounters.acquireMisses;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
++mCounters.acquiredFrames;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::PublishCompleted(const SystemFrame& frame)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
if (!IsValidLocked(frame))
|
||||
return false;
|
||||
|
||||
Slot& slot = mSlots[frame.index];
|
||||
if (slot.state != SystemFrameSlotState::Rendering)
|
||||
return false;
|
||||
|
||||
slot.state = SystemFrameSlotState::Completed;
|
||||
slot.frameIndex = frame.frameIndex;
|
||||
mCompletedIndices.push_back(frame.index);
|
||||
++mCounters.completedFrames;
|
||||
mCondition.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::ConsumeCompletedForSchedule(SystemFrame& frame)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
while (!mCompletedIndices.empty())
|
||||
{
|
||||
const std::size_t index = mCompletedIndices.front();
|
||||
mCompletedIndices.pop_front();
|
||||
if (index >= mSlots.size() || mSlots[index].state != SystemFrameSlotState::Completed)
|
||||
continue;
|
||||
|
||||
mSlots[index].state = SystemFrameSlotState::Scheduled;
|
||||
FillFrameLocked(index, frame);
|
||||
++mCounters.scheduledFrames;
|
||||
return true;
|
||||
}
|
||||
|
||||
frame = SystemFrame();
|
||||
++mCounters.completedPollMisses;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::ReleaseScheduledByBytes(void* bytes)
|
||||
{
|
||||
if (bytes == nullptr)
|
||||
return false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
for (std::size_t index = 0; index < mSlots.size(); ++index)
|
||||
{
|
||||
Slot& slot = mSlots[index];
|
||||
if (slot.bytes.empty() || slot.bytes.data() != bytes)
|
||||
continue;
|
||||
if (slot.state != SystemFrameSlotState::Scheduled)
|
||||
return false;
|
||||
|
||||
slot.state = SystemFrameSlotState::Free;
|
||||
slot.frameIndex = 0;
|
||||
++slot.generation;
|
||||
mCondition.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::WaitForCompletedDepth(std::size_t targetDepth, std::chrono::milliseconds timeout)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
return mCondition.wait_for(lock, timeout, [&]() {
|
||||
return CompletedCountLocked() >= targetDepth;
|
||||
});
|
||||
}
|
||||
|
||||
void SystemFrameExchange::Clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mCompletedIndices.clear();
|
||||
for (Slot& slot : mSlots)
|
||||
{
|
||||
slot.state = SystemFrameSlotState::Free;
|
||||
slot.frameIndex = 0;
|
||||
++slot.generation;
|
||||
}
|
||||
mCondition.notify_all();
|
||||
}
|
||||
|
||||
SystemFrameExchangeMetrics SystemFrameExchange::Metrics() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
SystemFrameExchangeMetrics metrics = mCounters;
|
||||
metrics.capacity = mSlots.size();
|
||||
metrics.completedDepth = mCompletedIndices.size();
|
||||
|
||||
for (const Slot& slot : mSlots)
|
||||
{
|
||||
switch (slot.state)
|
||||
{
|
||||
case SystemFrameSlotState::Free:
|
||||
++metrics.freeCount;
|
||||
break;
|
||||
case SystemFrameSlotState::Rendering:
|
||||
++metrics.renderingCount;
|
||||
break;
|
||||
case SystemFrameSlotState::Completed:
|
||||
++metrics.completedCount;
|
||||
break;
|
||||
case SystemFrameSlotState::Scheduled:
|
||||
++metrics.scheduledCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::AcquireFreeLocked(SystemFrame& frame)
|
||||
{
|
||||
for (std::size_t index = 0; index < mSlots.size(); ++index)
|
||||
{
|
||||
Slot& slot = mSlots[index];
|
||||
if (slot.state != SystemFrameSlotState::Free)
|
||||
continue;
|
||||
|
||||
slot.state = SystemFrameSlotState::Rendering;
|
||||
++slot.generation;
|
||||
FillFrameLocked(index, frame);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::DropOldestCompletedLocked()
|
||||
{
|
||||
while (!mCompletedIndices.empty())
|
||||
{
|
||||
const std::size_t index = mCompletedIndices.front();
|
||||
mCompletedIndices.pop_front();
|
||||
if (index >= mSlots.size() || mSlots[index].state != SystemFrameSlotState::Completed)
|
||||
continue;
|
||||
|
||||
Slot& slot = mSlots[index];
|
||||
slot.state = SystemFrameSlotState::Free;
|
||||
slot.frameIndex = 0;
|
||||
++slot.generation;
|
||||
++mCounters.completedDrops;
|
||||
mCondition.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SystemFrameExchange::IsValidLocked(const SystemFrame& frame) const
|
||||
{
|
||||
return frame.index < mSlots.size() && mSlots[frame.index].generation == frame.generation;
|
||||
}
|
||||
|
||||
void SystemFrameExchange::FillFrameLocked(std::size_t index, SystemFrame& frame)
|
||||
{
|
||||
Slot& slot = mSlots[index];
|
||||
frame.bytes = slot.bytes.empty() ? nullptr : slot.bytes.data();
|
||||
frame.rowBytes = static_cast<long>(mConfig.rowBytes);
|
||||
frame.width = mConfig.width;
|
||||
frame.height = mConfig.height;
|
||||
frame.pixelFormat = mConfig.pixelFormat;
|
||||
frame.index = index;
|
||||
frame.generation = slot.generation;
|
||||
frame.frameIndex = slot.frameIndex;
|
||||
}
|
||||
|
||||
std::size_t SystemFrameExchange::CompletedCountLocked() const
|
||||
{
|
||||
std::size_t count = 0;
|
||||
for (const Slot& slot : mSlots)
|
||||
{
|
||||
if (slot.state == SystemFrameSlotState::Completed)
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
std::size_t SystemFrameExchange::FrameByteCount() const
|
||||
{
|
||||
return static_cast<std::size_t>(mConfig.rowBytes) * static_cast<std::size_t>(mConfig.height);
|
||||
}
|
||||
51
apps/RenderCadenceCompositor/frames/SystemFrameExchange.h
Normal file
51
apps/RenderCadenceCompositor/frames/SystemFrameExchange.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "SystemFrameTypes.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
class SystemFrameExchange
|
||||
{
|
||||
public:
|
||||
SystemFrameExchange() = default;
|
||||
explicit SystemFrameExchange(const SystemFrameExchangeConfig& config);
|
||||
|
||||
void Configure(const SystemFrameExchangeConfig& config);
|
||||
SystemFrameExchangeConfig Config() const;
|
||||
|
||||
bool AcquireForRender(SystemFrame& frame);
|
||||
bool PublishCompleted(const SystemFrame& frame);
|
||||
bool ConsumeCompletedForSchedule(SystemFrame& frame);
|
||||
bool ReleaseScheduledByBytes(void* bytes);
|
||||
bool WaitForCompletedDepth(std::size_t targetDepth, std::chrono::milliseconds timeout);
|
||||
void Clear();
|
||||
|
||||
SystemFrameExchangeMetrics Metrics() const;
|
||||
|
||||
private:
|
||||
struct Slot
|
||||
{
|
||||
std::vector<unsigned char> bytes;
|
||||
SystemFrameSlotState state = SystemFrameSlotState::Free;
|
||||
uint64_t generation = 1;
|
||||
uint64_t frameIndex = 0;
|
||||
};
|
||||
|
||||
bool AcquireFreeLocked(SystemFrame& frame);
|
||||
bool DropOldestCompletedLocked();
|
||||
bool IsValidLocked(const SystemFrame& frame) const;
|
||||
void FillFrameLocked(std::size_t index, SystemFrame& frame);
|
||||
std::size_t CompletedCountLocked() const;
|
||||
std::size_t FrameByteCount() const;
|
||||
|
||||
mutable std::mutex mMutex;
|
||||
std::condition_variable mCondition;
|
||||
SystemFrameExchangeConfig mConfig;
|
||||
std::vector<Slot> mSlots;
|
||||
std::deque<std::size_t> mCompletedIndices;
|
||||
SystemFrameExchangeMetrics mCounters;
|
||||
};
|
||||
51
apps/RenderCadenceCompositor/frames/SystemFrameTypes.h
Normal file
51
apps/RenderCadenceCompositor/frames/SystemFrameTypes.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "VideoIOFormat.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
enum class SystemFrameSlotState
|
||||
{
|
||||
Free,
|
||||
Rendering,
|
||||
Completed,
|
||||
Scheduled
|
||||
};
|
||||
|
||||
struct SystemFrameExchangeConfig
|
||||
{
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8;
|
||||
unsigned rowBytes = 0;
|
||||
std::size_t capacity = 0;
|
||||
};
|
||||
|
||||
struct SystemFrame
|
||||
{
|
||||
void* bytes = nullptr;
|
||||
long rowBytes = 0;
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8;
|
||||
std::size_t index = 0;
|
||||
uint64_t generation = 0;
|
||||
uint64_t frameIndex = 0;
|
||||
};
|
||||
|
||||
struct SystemFrameExchangeMetrics
|
||||
{
|
||||
std::size_t capacity = 0;
|
||||
std::size_t freeCount = 0;
|
||||
std::size_t renderingCount = 0;
|
||||
std::size_t completedCount = 0;
|
||||
std::size_t scheduledCount = 0;
|
||||
std::size_t completedDepth = 0;
|
||||
uint64_t acquiredFrames = 0;
|
||||
uint64_t completedFrames = 0;
|
||||
uint64_t scheduledFrames = 0;
|
||||
uint64_t completedDrops = 0;
|
||||
uint64_t acquireMisses = 0;
|
||||
uint64_t completedPollMisses = 0;
|
||||
};
|
||||
Reference in New Issue
Block a user