#include "Bgra8ReadbackPipeline.h" #include "../frames/SystemFrameTypes.h" #include 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(mRowBytes) * static_cast(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(frame.rowBytes) * static_cast(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(mWidth), static_cast(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; }