[client/host/idd/lgmp] add additional timing metrics

This commit is contained in:
Geoffrey McRae
2026-08-03 10:57:32 +10:00
parent 3035fa6282
commit 7533c855c2
18 changed files with 339 additions and 88 deletions

View File

@@ -97,6 +97,12 @@ typedef struct CaptureFrame
uint32_t damageRectsCount;
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
// Producer-local durations. Absolute timestamps must not cross KVMFR as the
// client and producer use different monotonic clock domains.
uint64_t captureTime;
uint64_t postProcessTime;
uint64_t copyTime;
}
CaptureFrame;
@@ -146,8 +152,9 @@ typedef struct CaptureInterface
CaptureFrame * frame,
const size_t maxFrameSize);
CaptureResult (*getFrame )(
unsigned frameBufferIndex,
unsigned frameBufferIndex,
FrameBuffer * frame,
const size_t maxFrameSize);
const size_t maxFrameSize,
CaptureFrame * captureFrame);
}
CaptureInterface;

View File

@@ -299,10 +299,12 @@ static CaptureResult xcb_waitFrame(
}
static CaptureResult xcb_getFrame(
unsigned frameBufferIndex,
unsigned frameBufferIndex,
FrameBuffer * frame,
const size_t maxFrameSize)
const size_t maxFrameSize,
CaptureFrame * captureFrame)
{
(void)captureFrame;
DEBUG_ASSERT(this);
DEBUG_ASSERT(this->initialized);

View File

@@ -463,10 +463,12 @@ static CaptureResult pipewire_waitFrame(
}
static CaptureResult pipewire_getFrame(
unsigned frameBufferIndex,
unsigned frameBufferIndex,
FrameBuffer * frame,
const size_t maxFrameSize)
const size_t maxFrameSize,
CaptureFrame * captureFrame)
{
(void)captureFrame;
if (this->stop || !this->frameData)
return CAPTURE_RESULT_REINIT;

View File

@@ -29,6 +29,7 @@
#include "common/rects.h"
#include "common/vector.h"
#include "common/display.h"
#include "common/time.h"
#include "com_ref.h"
#include "backend.h"
@@ -771,8 +772,11 @@ exit:
return result;
}
static CaptureResult d12_getFrame(unsigned frameBufferIndex,
FrameBuffer * frameBuffer, const size_t maxFrameSize)
static CaptureResult d12_getFrame(
unsigned frameBufferIndex,
FrameBuffer * frameBuffer,
const size_t maxFrameSize,
CaptureFrame * captureFrame)
{
CaptureResult result = CAPTURE_RESULT_ERROR;
comRef_scopePush(3);
@@ -808,6 +812,7 @@ static CaptureResult d12_getFrame(unsigned frameBufferIndex,
if (result != CAPTURE_RESULT_OK)
goto exit;
const uint64_t postProcessStart = nanotime();
ID3D12Resource * next = *src;
D12Effect * effect;
vector_forEach(effect, &this->effects)
@@ -934,6 +939,8 @@ static CaptureResult d12_getFrame(unsigned frameBufferIndex,
ID3D12CommandQueue_Wait(*this->copyQueue,
*this->computeCommand.fence, this->computeCommand.fenceValue);
}
captureFrame->postProcessTime = this->effectsActive ?
nanotime() - postProcessStart : 0;
// execute the copy commands
DEBUG_TRACE("Execute copy commands");

View File

@@ -1504,9 +1504,13 @@ static CaptureResult dxgi_waitFrame(unsigned frameBufferIndex,
return CAPTURE_RESULT_OK;
}
static CaptureResult dxgi_getFrame(unsigned frameBufferIndex,
FrameBuffer * frame, const size_t maxFrameSize)
static CaptureResult dxgi_getFrame(
unsigned frameBufferIndex,
FrameBuffer * frame,
const size_t maxFrameSize,
CaptureFrame * captureFrame)
{
(void)captureFrame;
DEBUG_ASSERT(this);
DEBUG_ASSERT(this->initialized);

View File

@@ -718,9 +718,13 @@ static CaptureResult nvfbc_waitFrame(unsigned frameBufferIndex,
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_getFrame(unsigned frameBufferIndex,
FrameBuffer * frame, const size_t maxFrameSize)
static CaptureResult nvfbc_getFrame(
unsigned frameBufferIndex,
FrameBuffer * frame,
const size_t maxFrameSize,
CaptureFrame * captureFrame)
{
(void)captureFrame;
const unsigned int h = DIFF_MAP_DIM(this->grabHeight, this->diffShift);
const unsigned int w = DIFF_MAP_DIM(this->grabWidth, this->diffShift);
uint8_t * frameData = framebuffer_get_data(frame);

View File

@@ -109,6 +109,7 @@ struct app
bool frameValid;
uint32_t frameSerial;
uint32_t formatVer;
uint64_t captureTime[LGMP_Q_FRAME_LEN];
unsigned int captureFormatVer;
bool hdr;
bool hdrPQ;
@@ -291,6 +292,7 @@ static bool sendFrame(CaptureResult result, bool * restart)
}
KVMFRFrame * fi = app.frame[app.captureIndex];
frame.captureTime = app.captureTime[app.captureIndex];
const uint32_t sdrWhiteLevel = frame.sdrWhiteLevel ?
frame.sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
const bool metadataChanged =
@@ -396,6 +398,11 @@ static bool sendFrame(CaptureResult result, bool * restart)
// fi->offset is initialized at startup
fi->flags = flags;
fi->sdrWhiteLevel = sdrWhiteLevel;
fi->captureTime = frame.captureTime;
fi->postProcessTime = 0;
fi->copyTime = 0;
fi->timingSerial = 0;
__atomic_store_n(&fi->timingValid, 0, __ATOMIC_RELAXED);
if (frame.hdrMetadata)
{
memcpy(fi->hdrDisplayPrimary, frame.hdrDisplayPrimary,
@@ -433,10 +440,20 @@ static bool sendFrame(CaptureResult result, bool * restart)
return true;
}
const uint64_t copyStart = nanotime();
app.iface->getFrame(
app.captureIndex,
app.frameBuffer[app.captureIndex],
app.maxFrameSize);
app.maxFrameSize,
&frame);
const uint64_t copyEnd = nanotime();
fi->postProcessTime = frame.postProcessTime;
frame.copyTime = copyEnd - copyStart > frame.postProcessTime ?
copyEnd - copyStart - frame.postProcessTime : 0;
fi->copyTime = frame.copyTime;
fi->timingSerial = fi->frameSerial;
__atomic_store_n(&fi->timingValid, 1, __ATOMIC_RELEASE);
app.readIndex = app.captureIndex;
if (++app.captureIndex == LGMP_Q_FRAME_LEN)
@@ -1108,13 +1125,15 @@ int app_main(int argc, char * argv[])
nsleep(us * 1000);
}
const uint64_t captureStartTime = microtime();
const uint64_t captureStartTime = nanotime();
const CaptureResult result = app.iface->capture(
app.captureIndex, app.frameBuffer[app.captureIndex]);
app.captureTime[app.captureIndex] = nanotime() - captureStartTime;
if (likely(result == CAPTURE_RESULT_OK))
previousFrameTime = captureStartTime;
previousFrameTime = captureStartTime / 1000;
else if (likely(result == CAPTURE_RESULT_TIMEOUT))
{
if (!app.iface->asyncCapture)