Improvement
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m52s
CI / Windows Release Package (push) Successful in 3m0s

This commit is contained in:
Aiden
2026-05-12 00:00:23 +10:00
parent a434a88108
commit 9e3412712c
22 changed files with 1409 additions and 34 deletions

View File

@@ -25,7 +25,8 @@ OpenGLRenderPipeline::OpenGLRenderPipeline(
mRenderEffect(renderEffect),
mOutputReady(outputReady),
mPaint(paint),
mOutputReadbackMode(ReadOutputReadbackModeFromEnvironment())
mOutputReadbackMode(ReadOutputReadbackModeFromEnvironment()),
mAsyncReadbackDepth(ReadAsyncReadbackDepthFromEnvironment())
{
}
@@ -47,7 +48,9 @@ bool OpenGLRenderPipeline::RenderFrame(const RenderPipelineFrameContext& context
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputFramebuffer());
if (mOutputReady)
mOutputReady();
if (state.outputPixelFormat == VideoIOPixelFormat::V210 || state.outputPixelFormat == VideoIOPixelFormat::Yuva10)
if (state.outputPixelFormat == VideoIOPixelFormat::Bgra8)
PackOutputForBgra8(state);
else if (state.outputPixelFormat == VideoIOPixelFormat::V210 || state.outputPixelFormat == VideoIOPixelFormat::Yuva10)
PackOutputFor10Bit(state);
glFlush();
@@ -76,6 +79,24 @@ bool OpenGLRenderPipeline::RenderFrame(const RenderPipelineFrameContext& context
return true;
}
void OpenGLRenderPipeline::PackOutputForBgra8(const VideoIOState& state)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputFramebuffer());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mRenderer.OutputPackFramebuffer());
glBlitFramebuffer(
0,
0,
state.outputFrameSize.width,
state.outputFrameSize.height,
0,
0,
state.outputFrameSize.width,
state.outputFrameSize.height,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputPackFramebuffer());
}
void OpenGLRenderPipeline::PackOutputFor10Bit(const VideoIOState& state)
{
glBindFramebuffer(GL_FRAMEBUFFER, mRenderer.OutputPackFramebuffer());
@@ -109,11 +130,17 @@ bool OpenGLRenderPipeline::EnsureAsyncReadbackBuffers(std::size_t requiredBytes)
if (requiredBytes == 0)
return false;
if (mAsyncReadbackBytes == requiredBytes && mAsyncReadbackSlots[0].pixelPackBuffer != 0)
if (mAsyncReadbackBytes == requiredBytes &&
mAsyncReadbackSlots.size() == mAsyncReadbackDepth &&
!mAsyncReadbackSlots.empty() &&
mAsyncReadbackSlots[0].pixelPackBuffer != 0)
{
return true;
}
ResetAsyncReadbackState();
mAsyncReadbackBytes = requiredBytes;
mAsyncReadbackSlots.resize(mAsyncReadbackDepth);
for (AsyncReadbackSlot& slot : mAsyncReadbackSlots)
{
glGenBuffers(1, &slot.pixelPackBuffer);
@@ -134,7 +161,7 @@ void OpenGLRenderPipeline::ResetAsyncReadbackState()
for (AsyncReadbackSlot& slot : mAsyncReadbackSlots)
slot.sizeBytes = 0;
if (mAsyncReadbackSlots[0].pixelPackBuffer != 0)
if (!mAsyncReadbackSlots.empty() && mAsyncReadbackSlots[0].pixelPackBuffer != 0)
{
for (AsyncReadbackSlot& slot : mAsyncReadbackSlots)
{
@@ -149,6 +176,7 @@ void OpenGLRenderPipeline::ResetAsyncReadbackState()
mAsyncReadbackWriteIndex = 0;
mAsyncReadbackReadIndex = 0;
mAsyncReadbackBytes = 0;
mAsyncReadbackSlots.clear();
}
void OpenGLRenderPipeline::FlushAsyncReadbackPipeline()
@@ -170,12 +198,14 @@ void OpenGLRenderPipeline::FlushAsyncReadbackPipeline()
bool OpenGLRenderPipeline::QueueAsyncReadback(const VideoIOState& state, OutputReadbackTiming& timing)
{
const auto queueStartTime = std::chrono::steady_clock::now();
const bool usePackedOutput = state.outputPixelFormat == VideoIOPixelFormat::V210 || state.outputPixelFormat == VideoIOPixelFormat::Yuva10;
const bool useTenBitPackedOutput = state.outputPixelFormat == VideoIOPixelFormat::V210 ||
state.outputPixelFormat == VideoIOPixelFormat::Yuva10;
const bool usePackFramebuffer = state.outputPixelFormat == VideoIOPixelFormat::Bgra8 || useTenBitPackedOutput;
const std::size_t requiredBytes = static_cast<std::size_t>(state.outputFrameRowBytes) * state.outputFrameSize.height;
const GLenum format = usePackedOutput ? GL_RGBA : GL_BGRA;
const GLenum type = usePackedOutput ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_8_8_8_8_REV;
const GLuint framebuffer = usePackedOutput ? mRenderer.OutputPackFramebuffer() : mRenderer.OutputFramebuffer();
const GLsizei readWidth = static_cast<GLsizei>(usePackedOutput ? state.outputPackTextureWidth : state.outputFrameSize.width);
const GLenum format = useTenBitPackedOutput ? GL_RGBA : GL_BGRA;
const GLenum type = useTenBitPackedOutput ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_8_8_8_8_REV;
const GLuint framebuffer = usePackFramebuffer ? mRenderer.OutputPackFramebuffer() : mRenderer.OutputFramebuffer();
const GLsizei readWidth = static_cast<GLsizei>(useTenBitPackedOutput ? state.outputPackTextureWidth : state.outputFrameSize.width);
const GLsizei readHeight = static_cast<GLsizei>(state.outputFrameSize.height);
const auto finishTiming = [&timing, queueStartTime]() {
@@ -204,6 +234,12 @@ bool OpenGLRenderPipeline::QueueAsyncReadback(const VideoIOState& state, OutputR
}
}
if (mAsyncReadbackSlots.empty())
{
finishTiming();
return false;
}
AsyncReadbackSlot& slot = mAsyncReadbackSlots[mAsyncReadbackWriteIndex];
if (slot.inFlight)
{
@@ -321,13 +357,17 @@ void OpenGLRenderPipeline::ReadOutputFrameSynchronously(const VideoIOState& stat
{
const auto readStartTime = std::chrono::steady_clock::now();
const bool usePackedOutput = state.outputPixelFormat == VideoIOPixelFormat::V210 || state.outputPixelFormat == VideoIOPixelFormat::Yuva10;
const bool usePackFramebuffer = state.outputPixelFormat == VideoIOPixelFormat::Bgra8 || usePackedOutput;
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
if (usePackedOutput)
if (usePackFramebuffer)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, mRenderer.OutputPackFramebuffer());
glReadPixels(0, 0, state.outputPackTextureWidth, state.outputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, destinationBytes);
if (usePackedOutput)
glReadPixels(0, 0, state.outputPackTextureWidth, state.outputFrameSize.height, GL_RGBA, GL_UNSIGNED_BYTE, destinationBytes);
else
glReadPixels(0, 0, state.outputFrameSize.width, state.outputFrameSize.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, destinationBytes);
}
else
{
@@ -366,15 +406,19 @@ OpenGLRenderPipeline::OutputReadbackTiming OpenGLRenderPipeline::ReadOutputFrame
return timing;
}
if (TryConsumeAsyncReadback(outputFrame, 500000, timing))
if (TryConsumeAsyncReadback(outputFrame, 0, timing))
{
(void)QueueAsyncReadback(state, timing);
return timing;
}
const bool queued = QueueAsyncReadback(state, timing);
if (queued && TryConsumeAsyncReadback(outputFrame, 0, timing))
return timing;
if (TryCopyCachedOutputFrame(outputFrame, timing))
{
(void)QueueAsyncReadback(state, timing);
return timing;
}
@@ -386,8 +430,8 @@ OpenGLRenderPipeline::OutputReadbackTiming OpenGLRenderPipeline::ReadOutputFrame
CacheOutputFrame(outputFrame);
}
FlushAsyncReadbackPipeline();
(void)QueueAsyncReadback(state, timing);
if (!queued)
(void)QueueAsyncReadback(state, timing);
return timing;
}
@@ -409,3 +453,27 @@ OpenGLRenderPipeline::OutputReadbackMode OpenGLRenderPipeline::ReadOutputReadbac
return OutputReadbackMode::AsyncPbo;
}
std::size_t OpenGLRenderPipeline::ReadAsyncReadbackDepthFromEnvironment()
{
char* depthValue = nullptr;
std::size_t depthValueSize = 0;
if (_dupenv_s(&depthValue, &depthValueSize, "VST_OUTPUT_READBACK_DEPTH") != 0 || depthValue == nullptr)
return 6;
const std::string value(depthValue);
std::free(depthValue);
try
{
const unsigned long requestedDepth = std::stoul(value);
if (requestedDepth < 3)
return 3;
if (requestedDepth > 12)
return 12;
return static_cast<std::size_t>(requestedDepth);
}
catch (...)
{
return 6;
}
}