261 lines
6.6 KiB
C++
261 lines
6.6 KiB
C++
#include "SystemOutputFramePool.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace
|
|
{
|
|
SystemOutputFramePoolConfig NormalizeConfig(SystemOutputFramePoolConfig config)
|
|
{
|
|
if (config.rowBytes == 0)
|
|
config.rowBytes = VideoIORowBytes(config.pixelFormat, config.width);
|
|
return config;
|
|
}
|
|
}
|
|
|
|
SystemOutputFramePool::SystemOutputFramePool(const SystemOutputFramePoolConfig& config)
|
|
{
|
|
Configure(config);
|
|
}
|
|
|
|
void SystemOutputFramePool::Configure(const SystemOutputFramePoolConfig& config)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
mConfig = NormalizeConfig(config);
|
|
mReadySlots.clear();
|
|
mSlots.clear();
|
|
mSlots.resize(mConfig.capacity);
|
|
|
|
const std::size_t byteCount = FrameByteCount();
|
|
for (StoredSlot& slot : mSlots)
|
|
{
|
|
slot.bytes.resize(byteCount);
|
|
slot.state = OutputFrameSlotState::Free;
|
|
++slot.generation;
|
|
}
|
|
|
|
mAcquireMissCount = 0;
|
|
mReadyUnderrunCount = 0;
|
|
}
|
|
|
|
SystemOutputFramePoolConfig SystemOutputFramePool::Config() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
return mConfig;
|
|
}
|
|
|
|
bool SystemOutputFramePool::AcquireFreeSlot(OutputFrameSlot& slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
for (std::size_t index = 0; index < mSlots.size(); ++index)
|
|
{
|
|
if (mSlots[index].state != OutputFrameSlotState::Free)
|
|
continue;
|
|
|
|
mSlots[index].state = OutputFrameSlotState::Rendering;
|
|
++mSlots[index].generation;
|
|
FillOutputSlotLocked(index, slot);
|
|
return true;
|
|
}
|
|
|
|
slot = OutputFrameSlot();
|
|
++mAcquireMissCount;
|
|
return false;
|
|
}
|
|
|
|
bool SystemOutputFramePool::AcquireRenderingSlot(OutputFrameSlot& slot)
|
|
{
|
|
return AcquireFreeSlot(slot);
|
|
}
|
|
|
|
bool SystemOutputFramePool::PublishReadySlot(const OutputFrameSlot& slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (!TransitionSlotLocked(slot, OutputFrameSlotState::Rendering, OutputFrameSlotState::Completed))
|
|
return false;
|
|
|
|
mReadySlots.push_back(slot.index);
|
|
return true;
|
|
}
|
|
|
|
bool SystemOutputFramePool::PublishCompletedSlot(const OutputFrameSlot& slot)
|
|
{
|
|
return PublishReadySlot(slot);
|
|
}
|
|
|
|
bool SystemOutputFramePool::ConsumeReadySlot(OutputFrameSlot& slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
while (!mReadySlots.empty())
|
|
{
|
|
const std::size_t index = mReadySlots.front();
|
|
mReadySlots.pop_front();
|
|
if (index >= mSlots.size() || mSlots[index].state != OutputFrameSlotState::Completed)
|
|
continue;
|
|
|
|
FillOutputSlotLocked(index, slot);
|
|
return true;
|
|
}
|
|
|
|
slot = OutputFrameSlot();
|
|
++mReadyUnderrunCount;
|
|
return false;
|
|
}
|
|
|
|
bool SystemOutputFramePool::ConsumeCompletedSlot(OutputFrameSlot& slot)
|
|
{
|
|
return ConsumeReadySlot(slot);
|
|
}
|
|
|
|
bool SystemOutputFramePool::MarkScheduled(const OutputFrameSlot& slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (!IsValidSlotLocked(slot))
|
|
return false;
|
|
if (mSlots[slot.index].state != OutputFrameSlotState::Completed)
|
|
return false;
|
|
|
|
RemoveReadyIndexLocked(slot.index);
|
|
mSlots[slot.index].state = OutputFrameSlotState::Scheduled;
|
|
return true;
|
|
}
|
|
|
|
bool SystemOutputFramePool::MarkScheduledByBuffer(void* bytes)
|
|
{
|
|
if (bytes == nullptr)
|
|
return false;
|
|
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
for (std::size_t index = 0; index < mSlots.size(); ++index)
|
|
{
|
|
if (mSlots[index].bytes.empty() || mSlots[index].bytes.data() != bytes)
|
|
continue;
|
|
if (mSlots[index].state != OutputFrameSlotState::Completed)
|
|
return false;
|
|
|
|
RemoveReadyIndexLocked(index);
|
|
mSlots[index].state = OutputFrameSlotState::Scheduled;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SystemOutputFramePool::ReleaseSlot(const OutputFrameSlot& slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (!IsValidSlotLocked(slot) || mSlots[slot.index].state == OutputFrameSlotState::Free)
|
|
return false;
|
|
|
|
return ReleaseSlotByIndexLocked(slot.index);
|
|
}
|
|
|
|
bool SystemOutputFramePool::ReleaseScheduledSlot(const OutputFrameSlot& slot)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
return TransitionSlotLocked(slot, OutputFrameSlotState::Scheduled, OutputFrameSlotState::Free);
|
|
}
|
|
|
|
bool SystemOutputFramePool::ReleaseSlotByBuffer(void* bytes)
|
|
{
|
|
if (bytes == nullptr)
|
|
return false;
|
|
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
for (std::size_t index = 0; index < mSlots.size(); ++index)
|
|
{
|
|
if (!mSlots[index].bytes.empty() && mSlots[index].bytes.data() == bytes)
|
|
return ReleaseSlotByIndexLocked(index);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SystemOutputFramePool::Clear()
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
mReadySlots.clear();
|
|
for (StoredSlot& slot : mSlots)
|
|
{
|
|
slot.state = OutputFrameSlotState::Free;
|
|
++slot.generation;
|
|
}
|
|
}
|
|
|
|
SystemOutputFramePoolMetrics SystemOutputFramePool::GetMetrics() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
SystemOutputFramePoolMetrics metrics;
|
|
metrics.capacity = mSlots.size();
|
|
metrics.readyCount = mReadySlots.size();
|
|
metrics.acquireMissCount = mAcquireMissCount;
|
|
metrics.readyUnderrunCount = mReadyUnderrunCount;
|
|
|
|
for (const StoredSlot& slot : mSlots)
|
|
{
|
|
switch (slot.state)
|
|
{
|
|
case OutputFrameSlotState::Free:
|
|
++metrics.freeCount;
|
|
break;
|
|
case OutputFrameSlotState::Rendering:
|
|
++metrics.renderingCount;
|
|
++metrics.acquiredCount;
|
|
break;
|
|
case OutputFrameSlotState::Completed:
|
|
++metrics.completedCount;
|
|
break;
|
|
case OutputFrameSlotState::Scheduled:
|
|
++metrics.scheduledCount;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return metrics;
|
|
}
|
|
|
|
bool SystemOutputFramePool::IsValidSlotLocked(const OutputFrameSlot& slot) const
|
|
{
|
|
return slot.index < mSlots.size() && mSlots[slot.index].generation == slot.generation;
|
|
}
|
|
|
|
bool SystemOutputFramePool::TransitionSlotLocked(const OutputFrameSlot& slot, OutputFrameSlotState expectedState, OutputFrameSlotState nextState)
|
|
{
|
|
if (!IsValidSlotLocked(slot) || mSlots[slot.index].state != expectedState)
|
|
return false;
|
|
|
|
mSlots[slot.index].state = nextState;
|
|
return true;
|
|
}
|
|
|
|
void SystemOutputFramePool::FillOutputSlotLocked(std::size_t index, OutputFrameSlot& slot)
|
|
{
|
|
StoredSlot& storedSlot = mSlots[index];
|
|
slot.index = index;
|
|
slot.generation = storedSlot.generation;
|
|
slot.frame.bytes = storedSlot.bytes.empty() ? nullptr : storedSlot.bytes.data();
|
|
slot.frame.rowBytes = static_cast<long>(mConfig.rowBytes);
|
|
slot.frame.width = mConfig.width;
|
|
slot.frame.height = mConfig.height;
|
|
slot.frame.pixelFormat = mConfig.pixelFormat;
|
|
slot.frame.nativeFrame = nullptr;
|
|
slot.frame.nativeBuffer = slot.frame.bytes;
|
|
}
|
|
|
|
void SystemOutputFramePool::RemoveReadyIndexLocked(std::size_t index)
|
|
{
|
|
mReadySlots.erase(std::remove(mReadySlots.begin(), mReadySlots.end(), index), mReadySlots.end());
|
|
}
|
|
|
|
bool SystemOutputFramePool::ReleaseSlotByIndexLocked(std::size_t index)
|
|
{
|
|
if (index >= mSlots.size() || mSlots[index].state == OutputFrameSlotState::Free)
|
|
return false;
|
|
|
|
RemoveReadyIndexLocked(index);
|
|
mSlots[index].state = OutputFrameSlotState::Free;
|
|
return true;
|
|
}
|
|
|
|
std::size_t SystemOutputFramePool::FrameByteCount() const
|
|
{
|
|
return static_cast<std::size_t>(mConfig.rowBytes) * static_cast<std::size_t>(mConfig.height);
|
|
}
|