restructure
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
#include "Bgra8ReadbackPipeline.h"
|
||||
|
||||
#include "../frames/SystemFrameTypes.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
Bgra8ReadbackPipeline::~Bgra8ReadbackPipeline()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool Bgra8ReadbackPipeline::Initialize(unsigned width, unsigned height, std::size_t pboDepth)
|
||||
{
|
||||
Shutdown();
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mRowBytes = VideoIORowBytes(VideoIOPixelFormat::Bgra8, width);
|
||||
if (mWidth == 0 || mHeight == 0 || mRowBytes == 0)
|
||||
return false;
|
||||
|
||||
if (!CreateRenderTarget())
|
||||
{
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::size_t byteCount = static_cast<std::size_t>(mRowBytes) * static_cast<std::size_t>(mHeight);
|
||||
if (!mPboRing.Initialize(pboDepth, byteCount))
|
||||
{
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bgra8ReadbackPipeline::Shutdown()
|
||||
{
|
||||
mPboRing.Shutdown();
|
||||
DestroyRenderTarget();
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mRowBytes = 0;
|
||||
}
|
||||
|
||||
bool Bgra8ReadbackPipeline::RenderAndQueue(uint64_t frameIndex, const RenderCallback& renderFrame)
|
||||
{
|
||||
if (mFramebuffer == 0 || !renderFrame)
|
||||
return false;
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
|
||||
renderFrame(frameIndex);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return mPboRing.QueueReadback(mFramebuffer, mWidth, mHeight, frameIndex);
|
||||
}
|
||||
|
||||
void Bgra8ReadbackPipeline::ConsumeCompleted(
|
||||
const AcquireFrameCallback& acquireFrame,
|
||||
const PublishFrameCallback& publishFrame,
|
||||
const CounterCallback& onAcquireMiss,
|
||||
const CounterCallback& onCompleted)
|
||||
{
|
||||
if (!acquireFrame || !publishFrame)
|
||||
return;
|
||||
|
||||
PboReadbackRing::CompletedReadback readback;
|
||||
while (mPboRing.TryAcquireCompleted(readback))
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, readback.pbo);
|
||||
void* mapped = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||
if (!mapped)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
mPboRing.ReleaseCompleted(readback);
|
||||
continue;
|
||||
}
|
||||
|
||||
SystemFrame frame;
|
||||
if (acquireFrame(frame))
|
||||
{
|
||||
const std::size_t byteCount = static_cast<std::size_t>(frame.rowBytes) * static_cast<std::size_t>(frame.height);
|
||||
if (frame.bytes != nullptr && byteCount <= readback.byteCount)
|
||||
{
|
||||
std::memcpy(frame.bytes, mapped, byteCount);
|
||||
frame.frameIndex = readback.frameIndex;
|
||||
frame.pixelFormat = VideoIOPixelFormat::Bgra8;
|
||||
publishFrame(frame);
|
||||
if (onCompleted)
|
||||
onCompleted();
|
||||
}
|
||||
}
|
||||
else if (onAcquireMiss)
|
||||
{
|
||||
onAcquireMiss();
|
||||
}
|
||||
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
mPboRing.ReleaseCompleted(readback);
|
||||
}
|
||||
}
|
||||
|
||||
bool Bgra8ReadbackPipeline::CreateRenderTarget()
|
||||
{
|
||||
glGenFramebuffers(1, &mFramebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
|
||||
|
||||
glGenTextures(1, &mTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, mTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
static_cast<GLsizei>(mWidth),
|
||||
static_cast<GLsizei>(mHeight),
|
||||
0,
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
nullptr);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
|
||||
|
||||
const bool complete = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
return complete;
|
||||
}
|
||||
|
||||
void Bgra8ReadbackPipeline::DestroyRenderTarget()
|
||||
{
|
||||
if (mFramebuffer != 0)
|
||||
glDeleteFramebuffers(1, &mFramebuffer);
|
||||
if (mTexture != 0)
|
||||
glDeleteTextures(1, &mTexture);
|
||||
mFramebuffer = 0;
|
||||
mTexture = 0;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "PboReadbackRing.h"
|
||||
#include "VideoIOFormat.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
struct SystemFrame;
|
||||
|
||||
class Bgra8ReadbackPipeline
|
||||
{
|
||||
public:
|
||||
using RenderCallback = std::function<void(uint64_t frameIndex)>;
|
||||
using AcquireFrameCallback = std::function<bool(SystemFrame& frame)>;
|
||||
using PublishFrameCallback = std::function<bool(const SystemFrame& frame)>;
|
||||
using CounterCallback = std::function<void()>;
|
||||
|
||||
Bgra8ReadbackPipeline() = default;
|
||||
Bgra8ReadbackPipeline(const Bgra8ReadbackPipeline&) = delete;
|
||||
Bgra8ReadbackPipeline& operator=(const Bgra8ReadbackPipeline&) = delete;
|
||||
~Bgra8ReadbackPipeline();
|
||||
|
||||
bool Initialize(unsigned width, unsigned height, std::size_t pboDepth);
|
||||
void Shutdown();
|
||||
|
||||
bool RenderAndQueue(uint64_t frameIndex, const RenderCallback& renderFrame);
|
||||
void ConsumeCompleted(
|
||||
const AcquireFrameCallback& acquireFrame,
|
||||
const PublishFrameCallback& publishFrame,
|
||||
const CounterCallback& onAcquireMiss = {},
|
||||
const CounterCallback& onCompleted = {});
|
||||
|
||||
GLuint Framebuffer() const { return mFramebuffer; }
|
||||
unsigned Width() const { return mWidth; }
|
||||
unsigned Height() const { return mHeight; }
|
||||
unsigned RowBytes() const { return mRowBytes; }
|
||||
VideoIOPixelFormat PixelFormat() const { return VideoIOPixelFormat::Bgra8; }
|
||||
uint64_t PboQueueMisses() const { return mPboRing.QueueMisses(); }
|
||||
|
||||
private:
|
||||
bool CreateRenderTarget();
|
||||
void DestroyRenderTarget();
|
||||
|
||||
unsigned mWidth = 0;
|
||||
unsigned mHeight = 0;
|
||||
unsigned mRowBytes = 0;
|
||||
GLuint mFramebuffer = 0;
|
||||
GLuint mTexture = 0;
|
||||
PboReadbackRing mPboRing;
|
||||
};
|
||||
138
apps/RenderCadenceCompositor/render/readback/PboReadbackRing.cpp
Normal file
138
apps/RenderCadenceCompositor/render/readback/PboReadbackRing.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "GLExtensions.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class PboReadbackRing
|
||||
{
|
||||
public:
|
||||
struct CompletedReadback
|
||||
{
|
||||
GLuint pbo = 0;
|
||||
uint64_t frameIndex = 0;
|
||||
std::size_t byteCount = 0;
|
||||
};
|
||||
|
||||
PboReadbackRing() = default;
|
||||
PboReadbackRing(const PboReadbackRing&) = delete;
|
||||
PboReadbackRing& operator=(const PboReadbackRing&) = delete;
|
||||
~PboReadbackRing();
|
||||
|
||||
bool Initialize(std::size_t depth, std::size_t byteCount);
|
||||
void Shutdown();
|
||||
|
||||
bool QueueReadback(GLuint framebuffer, unsigned width, unsigned height, uint64_t frameIndex);
|
||||
bool TryAcquireCompleted(CompletedReadback& readback);
|
||||
void ReleaseCompleted(const CompletedReadback& readback);
|
||||
void DrainCompleted();
|
||||
|
||||
std::size_t Depth() const { return mSlots.size(); }
|
||||
uint64_t QueueMisses() const { return mQueueMisses; }
|
||||
|
||||
private:
|
||||
struct Slot
|
||||
{
|
||||
GLuint pbo = 0;
|
||||
GLsync fence = nullptr;
|
||||
bool inFlight = false;
|
||||
bool acquired = false;
|
||||
uint64_t frameIndex = 0;
|
||||
};
|
||||
|
||||
void ResetSlot(Slot& slot);
|
||||
|
||||
std::vector<Slot> mSlots;
|
||||
std::size_t mWriteIndex = 0;
|
||||
std::size_t mReadIndex = 0;
|
||||
std::size_t mByteCount = 0;
|
||||
uint64_t mQueueMisses = 0;
|
||||
};
|
||||
Reference in New Issue
Block a user