139 lines
3.1 KiB
C++
139 lines
3.1 KiB
C++
#include "PboReadbackRing.h"
|
|
|
|
#include <algorithm>
|
|
|
|
PboReadbackRing::~PboReadbackRing()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
bool PboReadbackRing::Initialize(std::size_t depth, std::size_t byteCount)
|
|
{
|
|
Shutdown();
|
|
if (depth == 0 || byteCount == 0)
|
|
return false;
|
|
|
|
mSlots.resize(depth);
|
|
mByteCount = byteCount;
|
|
for (Slot& slot : mSlots)
|
|
{
|
|
glGenBuffers(1, &slot.pbo);
|
|
if (slot.pbo == 0)
|
|
{
|
|
Shutdown();
|
|
return false;
|
|
}
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, slot.pbo);
|
|
glBufferData(GL_PIXEL_PACK_BUFFER, static_cast<GLsizeiptr>(mByteCount), nullptr, GL_STREAM_READ);
|
|
}
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
return true;
|
|
}
|
|
|
|
void PboReadbackRing::Shutdown()
|
|
{
|
|
for (Slot& slot : mSlots)
|
|
{
|
|
if (slot.fence)
|
|
glDeleteSync(slot.fence);
|
|
if (slot.pbo != 0)
|
|
glDeleteBuffers(1, &slot.pbo);
|
|
slot = {};
|
|
}
|
|
mSlots.clear();
|
|
mWriteIndex = 0;
|
|
mReadIndex = 0;
|
|
mByteCount = 0;
|
|
}
|
|
|
|
bool PboReadbackRing::QueueReadback(GLuint framebuffer, unsigned width, unsigned height, uint64_t frameIndex)
|
|
{
|
|
if (mSlots.empty())
|
|
return false;
|
|
|
|
Slot& slot = mSlots[mWriteIndex];
|
|
if (slot.inFlight || slot.acquired)
|
|
{
|
|
++mQueueMisses;
|
|
return false;
|
|
}
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, slot.pbo);
|
|
glBufferData(GL_PIXEL_PACK_BUFFER, static_cast<GLsizeiptr>(mByteCount), nullptr, GL_STREAM_READ);
|
|
glReadPixels(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr);
|
|
slot.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
slot.inFlight = slot.fence != nullptr;
|
|
slot.frameIndex = frameIndex;
|
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
|
|
|
if (!slot.inFlight)
|
|
return false;
|
|
|
|
mWriteIndex = (mWriteIndex + 1) % mSlots.size();
|
|
return true;
|
|
}
|
|
|
|
bool PboReadbackRing::TryAcquireCompleted(CompletedReadback& readback)
|
|
{
|
|
if (mSlots.empty())
|
|
return false;
|
|
|
|
for (std::size_t checked = 0; checked < mSlots.size(); ++checked)
|
|
{
|
|
Slot& slot = mSlots[mReadIndex];
|
|
if (!slot.inFlight || slot.acquired || slot.fence == nullptr)
|
|
{
|
|
mReadIndex = (mReadIndex + 1) % mSlots.size();
|
|
continue;
|
|
}
|
|
|
|
const GLenum waitResult = glClientWaitSync(slot.fence, 0, 0);
|
|
if (waitResult != GL_ALREADY_SIGNALED && waitResult != GL_CONDITION_SATISFIED)
|
|
return false;
|
|
|
|
slot.acquired = true;
|
|
readback.pbo = slot.pbo;
|
|
readback.frameIndex = slot.frameIndex;
|
|
readback.byteCount = mByteCount;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PboReadbackRing::ReleaseCompleted(const CompletedReadback& readback)
|
|
{
|
|
for (std::size_t index = 0; index < mSlots.size(); ++index)
|
|
{
|
|
Slot& slot = mSlots[index];
|
|
if (!slot.acquired || slot.pbo != readback.pbo)
|
|
continue;
|
|
ResetSlot(slot);
|
|
mReadIndex = (index + 1) % mSlots.size();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void PboReadbackRing::DrainCompleted()
|
|
{
|
|
for (std::size_t pass = 0; pass < mSlots.size() * 2; ++pass)
|
|
{
|
|
CompletedReadback readback;
|
|
if (!TryAcquireCompleted(readback))
|
|
break;
|
|
ReleaseCompleted(readback);
|
|
}
|
|
}
|
|
|
|
void PboReadbackRing::ResetSlot(Slot& slot)
|
|
{
|
|
if (slot.fence)
|
|
glDeleteSync(slot.fence);
|
|
slot.fence = nullptr;
|
|
slot.inFlight = false;
|
|
slot.acquired = false;
|
|
slot.frameIndex = 0;
|
|
}
|