input testing
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "InputFrameTexture.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
InputFrameTexture::~InputFrameTexture()
|
||||
{
|
||||
ShutdownGl();
|
||||
@@ -14,28 +16,24 @@ GLuint InputFrameTexture::PollAndUpload(InputFrameMailbox* mailbox)
|
||||
if (!mailbox->TryAcquireLatest(frame))
|
||||
{
|
||||
++mUploadMisses;
|
||||
mLastUploadMilliseconds = 0.0;
|
||||
return mTexture;
|
||||
}
|
||||
|
||||
if (frame.bytes != nullptr && frame.pixelFormat == VideoIOPixelFormat::Bgra8 && EnsureTexture(frame))
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, mTexture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.rowBytes > 0 ? static_cast<GLint>(frame.rowBytes / 4) : 0);
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
static_cast<GLsizei>(frame.width),
|
||||
static_cast<GLsizei>(frame.height),
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
frame.bytes);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
mLastFrameFormatSupported = true;
|
||||
const auto uploadStart = std::chrono::steady_clock::now();
|
||||
UploadBgra8FrameFlippedVertically(frame);
|
||||
const auto uploadEnd = std::chrono::steady_clock::now();
|
||||
mLastUploadMilliseconds = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(uploadEnd - uploadStart).count();
|
||||
++mUploadedFrames;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLastFrameFormatSupported = frame.pixelFormat == VideoIOPixelFormat::Bgra8;
|
||||
mLastUploadMilliseconds = 0.0;
|
||||
}
|
||||
|
||||
mailbox->Release(frame);
|
||||
return mTexture;
|
||||
@@ -81,3 +79,30 @@ bool InputFrameTexture::EnsureTexture(const InputFrame& frame)
|
||||
mHeight = frame.height;
|
||||
return mTexture != 0;
|
||||
}
|
||||
|
||||
void InputFrameTexture::UploadBgra8FrameFlippedVertically(const InputFrame& frame)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, mTexture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.rowBytes > 0 ? static_cast<GLint>(frame.rowBytes / 4) : 0);
|
||||
|
||||
const unsigned char* sourceBytes = static_cast<const unsigned char*>(frame.bytes);
|
||||
for (unsigned destinationY = 0; destinationY < frame.height; ++destinationY)
|
||||
{
|
||||
const unsigned sourceY = frame.height - 1u - destinationY;
|
||||
const unsigned char* sourceRow = sourceBytes + static_cast<std::size_t>(sourceY) * static_cast<std::size_t>(frame.rowBytes);
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
static_cast<GLint>(destinationY),
|
||||
static_cast<GLsizei>(frame.width),
|
||||
1,
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
sourceRow);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
@@ -17,14 +17,19 @@ public:
|
||||
GLuint Texture() const { return mTexture; }
|
||||
uint64_t UploadedFrames() const { return mUploadedFrames; }
|
||||
uint64_t UploadMisses() const { return mUploadMisses; }
|
||||
double LastUploadMilliseconds() const { return mLastUploadMilliseconds; }
|
||||
bool LastFrameFormatSupported() const { return mLastFrameFormatSupported; }
|
||||
void ShutdownGl();
|
||||
|
||||
private:
|
||||
bool EnsureTexture(const InputFrame& frame);
|
||||
void UploadBgra8FrameFlippedVertically(const InputFrame& frame);
|
||||
|
||||
GLuint mTexture = 0;
|
||||
unsigned mWidth = 0;
|
||||
unsigned mHeight = 0;
|
||||
uint64_t mUploadedFrames = 0;
|
||||
uint64_t mUploadMisses = 0;
|
||||
double mLastUploadMilliseconds = 0.0;
|
||||
bool mLastFrameFormatSupported = true;
|
||||
};
|
||||
|
||||
@@ -85,6 +85,12 @@ RenderThread::Metrics RenderThread::GetMetrics() const
|
||||
metrics.skippedFrames = mSkippedFrames.load(std::memory_order_relaxed);
|
||||
metrics.shaderBuildsCommitted = mShaderBuildsCommitted.load(std::memory_order_relaxed);
|
||||
metrics.shaderBuildFailures = mShaderBuildFailures.load(std::memory_order_relaxed);
|
||||
metrics.inputFramesReceived = mInputFramesReceived.load(std::memory_order_relaxed);
|
||||
metrics.inputFramesDropped = mInputFramesDropped.load(std::memory_order_relaxed);
|
||||
metrics.inputLatestAgeMilliseconds = mInputLatestAgeMilliseconds.load(std::memory_order_relaxed);
|
||||
metrics.inputUploadMilliseconds = mInputUploadMilliseconds.load(std::memory_order_relaxed);
|
||||
metrics.inputFormatSupported = mInputFormatSupported.load(std::memory_order_relaxed);
|
||||
metrics.inputSignalPresent = mInputSignalPresent.load(std::memory_order_relaxed);
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@@ -156,6 +162,7 @@ void RenderThread::ThreadMain()
|
||||
|
||||
TryCommitReadyRuntimeShader(runtimeRenderScene);
|
||||
const GLuint videoInputTexture = inputTexture.PollAndUpload(mInputMailbox);
|
||||
PublishInputMetrics(inputTexture);
|
||||
if (!readback.RenderAndQueue(frameIndex, [this, &renderer, &runtimeRenderScene, videoInputTexture](uint64_t index) {
|
||||
if (runtimeRenderScene.HasLayers())
|
||||
runtimeRenderScene.RenderFrame(index, mConfig.width, mConfig.height, videoInputTexture);
|
||||
@@ -226,6 +233,28 @@ void RenderThread::CountAcquireMiss()
|
||||
mAcquireMisses.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void RenderThread::PublishInputMetrics(const InputFrameTexture& inputTexture)
|
||||
{
|
||||
if (mInputMailbox != nullptr)
|
||||
{
|
||||
const InputFrameMailboxMetrics mailboxMetrics = mInputMailbox->Metrics();
|
||||
mInputFramesReceived.store(mailboxMetrics.submittedFrames, std::memory_order_relaxed);
|
||||
mInputFramesDropped.store(mailboxMetrics.droppedReadyFrames + mailboxMetrics.submitMisses, std::memory_order_relaxed);
|
||||
mInputLatestAgeMilliseconds.store(mailboxMetrics.latestFrameAgeMilliseconds, std::memory_order_relaxed);
|
||||
mInputSignalPresent.store(mailboxMetrics.hasSubmittedFrame, std::memory_order_relaxed);
|
||||
}
|
||||
else
|
||||
{
|
||||
mInputFramesReceived.store(0, std::memory_order_relaxed);
|
||||
mInputFramesDropped.store(0, std::memory_order_relaxed);
|
||||
mInputLatestAgeMilliseconds.store(0.0, std::memory_order_relaxed);
|
||||
mInputSignalPresent.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
mInputUploadMilliseconds.store(inputTexture.LastUploadMilliseconds(), std::memory_order_relaxed);
|
||||
mInputFormatSupported.store(inputTexture.LastFrameFormatSupported(), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void RenderThread::SubmitRuntimeShaderArtifact(const RuntimeShaderArtifact& artifact)
|
||||
{
|
||||
if (artifact.fragmentShaderSource.empty())
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
class SystemFrameExchange;
|
||||
class InputFrameMailbox;
|
||||
class InputFrameTexture;
|
||||
|
||||
class RenderThread
|
||||
{
|
||||
@@ -37,6 +38,12 @@ public:
|
||||
uint64_t skippedFrames = 0;
|
||||
uint64_t shaderBuildsCommitted = 0;
|
||||
uint64_t shaderBuildFailures = 0;
|
||||
uint64_t inputFramesReceived = 0;
|
||||
uint64_t inputFramesDropped = 0;
|
||||
double inputLatestAgeMilliseconds = 0.0;
|
||||
double inputUploadMilliseconds = 0.0;
|
||||
bool inputFormatSupported = true;
|
||||
bool inputSignalPresent = false;
|
||||
};
|
||||
|
||||
RenderThread(SystemFrameExchange& frameExchange, Config config);
|
||||
@@ -60,6 +67,7 @@ private:
|
||||
void CountRendered();
|
||||
void CountCompleted();
|
||||
void CountAcquireMiss();
|
||||
void PublishInputMetrics(const InputFrameTexture& inputTexture);
|
||||
void TryCommitReadyRuntimeShader(RuntimeRenderScene& runtimeRenderScene);
|
||||
bool TryTakePendingRuntimeShaderArtifact(RuntimeShaderArtifact& artifact);
|
||||
bool TryTakePendingRenderLayers(std::vector<RenderCadenceCompositor::RuntimeRenderLayerModel>& layers);
|
||||
@@ -84,6 +92,12 @@ private:
|
||||
std::atomic<uint64_t> mSkippedFrames{ 0 };
|
||||
std::atomic<uint64_t> mShaderBuildsCommitted{ 0 };
|
||||
std::atomic<uint64_t> mShaderBuildFailures{ 0 };
|
||||
std::atomic<uint64_t> mInputFramesReceived{ 0 };
|
||||
std::atomic<uint64_t> mInputFramesDropped{ 0 };
|
||||
std::atomic<double> mInputLatestAgeMilliseconds{ 0.0 };
|
||||
std::atomic<double> mInputUploadMilliseconds{ 0.0 };
|
||||
std::atomic<bool> mInputFormatSupported{ true };
|
||||
std::atomic<bool> mInputSignalPresent{ false };
|
||||
|
||||
std::mutex mShaderArtifactMutex;
|
||||
bool mHasPendingShaderArtifact = false;
|
||||
|
||||
@@ -159,6 +159,9 @@ void RuntimeRenderScene::RenderFrame(uint64_t frameIndex, unsigned width, unsign
|
||||
return;
|
||||
}
|
||||
|
||||
// Shader source contract:
|
||||
// - gVideoInput is the raw/latest input texture for every layer in the stack.
|
||||
// - gLayerInput starts as gVideoInput for the first layer, then becomes the previous layer output.
|
||||
GLuint layerInputTexture = videoInputTexture;
|
||||
std::size_t nextTargetIndex = 0;
|
||||
for (std::size_t layerIndex = 0; layerIndex < readyLayers.size(); ++layerIndex)
|
||||
@@ -324,7 +327,7 @@ GLuint RuntimeRenderScene::RenderLayer(
|
||||
if (!pass.renderer || !pass.renderer->HasProgram())
|
||||
continue;
|
||||
|
||||
GLuint sourceTexture = layerInputTexture;
|
||||
GLuint sourceTexture = videoInputTexture;
|
||||
if (!pass.inputNames.empty())
|
||||
{
|
||||
const std::string& inputName = pass.inputNames.front();
|
||||
@@ -334,6 +337,9 @@ GLuint RuntimeRenderScene::RenderLayer(
|
||||
}
|
||||
else if (inputName != "layerInput")
|
||||
{
|
||||
// Named intermediate pass inputs currently use the gVideoInput binding slot as the
|
||||
// selected pass source. Layer stack shaders should use gLayerInput for previous-layer
|
||||
// sampling and gVideoInput for the original input frame.
|
||||
for (std::size_t index = 0; index < 2; ++index)
|
||||
{
|
||||
if (namedOutputNames[index] == inputName)
|
||||
|
||||
Reference in New Issue
Block a user