#include "PboReadbackRing.h" #include 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(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(mByteCount), nullptr, GL_STREAM_READ); glReadPixels(0, 0, static_cast(width), static_cast(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; }