246 lines
5.7 KiB
C++
246 lines
5.7 KiB
C++
#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);
|
|
}
|