restructure

This commit is contained in:
Aiden
2026-05-12 15:47:59 +10:00
parent 6a33bd02ab
commit 02b221f481
21 changed files with 40 additions and 33 deletions

View File

@@ -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;
}

View File

@@ -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;
};

View 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;
}

View File

@@ -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;
};