mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-04 06:12:04 +00:00
[client/host/idd/lgmp] add additional timing metrics
This commit is contained in:
@@ -33,13 +33,16 @@ using namespace Microsoft::WRL;
|
||||
class CFrameBufferResource
|
||||
{
|
||||
private:
|
||||
bool m_valid = false;
|
||||
unsigned m_frameIndex = 0;
|
||||
uint8_t * m_base = nullptr;
|
||||
size_t m_size = 0;
|
||||
size_t m_frameSize = 0;
|
||||
bool m_valid = false;
|
||||
unsigned m_frameIndex = 0;
|
||||
uint8_t * m_base = nullptr;
|
||||
size_t m_size = 0;
|
||||
size_t m_frameSize = 0;
|
||||
uint64_t m_captureTime = 0;
|
||||
uint64_t m_postProcessTime = 0;
|
||||
uint64_t m_copyStart = 0;
|
||||
ComPtr<ID3D12Resource> m_res;
|
||||
void * m_map = nullptr;
|
||||
void * m_map = nullptr;
|
||||
|
||||
public:
|
||||
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
|
||||
@@ -52,5 +55,16 @@ class CFrameBufferResource
|
||||
size_t GetFrameSize() { return m_frameSize; }
|
||||
void * GetMap() { return m_map; }
|
||||
|
||||
void SetTiming(uint64_t captureTime, uint64_t postProcessTime,
|
||||
uint64_t copyStart)
|
||||
{
|
||||
m_captureTime = captureTime;
|
||||
m_postProcessTime = postProcessTime;
|
||||
m_copyStart = copyStart;
|
||||
}
|
||||
uint64_t GetCaptureTime () const { return m_captureTime; }
|
||||
uint64_t GetPostProcessTime() const { return m_postProcessTime; }
|
||||
uint64_t GetCopyStart () const { return m_copyStart; }
|
||||
|
||||
ComPtr<ID3D12Resource> Get() { return m_res; }
|
||||
};
|
||||
|
||||
@@ -212,7 +212,8 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
return;
|
||||
}
|
||||
|
||||
if (InterlockedCompareExchange(&m_initInProgress, 1, 0) != 0)
|
||||
LONG initExpected = 0;
|
||||
if (!m_initInProgress.compare_exchange_strong(initExpected, 1))
|
||||
{
|
||||
DEBUG_TRACE("Adapter initialization skipped: initialization already in progress");
|
||||
return;
|
||||
@@ -227,7 +228,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
{
|
||||
DEBUG_WARN("IVSHMEM not available yet, scheduling init retry");
|
||||
ScheduleInitRetry();
|
||||
InterlockedExchange(&m_initInProgress, 0);
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
}
|
||||
m_ivshmemOpened = true;
|
||||
@@ -370,7 +371,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DEBUG_ERROR_HR(status, "IddCxAdapterInitAsync Failed");
|
||||
InterlockedExchange(&m_initInProgress, 0);
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -378,7 +379,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
if (!m_adapter)
|
||||
{
|
||||
DEBUG_ERROR("IddCxAdapterInitAsync succeeded without returning an adapter object");
|
||||
InterlockedExchange(&m_initInProgress, 0);
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -401,7 +402,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
|
||||
// Adapter is up; no need to keep retrying.
|
||||
StopInitRetry();
|
||||
InterlockedExchange(&m_initInProgress, 0);
|
||||
m_initInProgress.store(0);
|
||||
DEBUG_INFO("Adapter initialization request complete; returning to IddCx");
|
||||
}
|
||||
|
||||
@@ -501,7 +502,7 @@ void CIndirectDeviceContext::ReplugMonitor()
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
// Either no monitor yet, or one is already pending; build it now and
|
||||
// cancel any queued rebuild so we do not create two.
|
||||
InterlockedExchange(&m_finishInitQueued, 0);
|
||||
m_finishInitQueued.store(0);
|
||||
FinishInit(0);
|
||||
return;
|
||||
}
|
||||
@@ -537,7 +538,7 @@ void CIndirectDeviceContext::ReplugMonitor()
|
||||
// If there was no swap chain there will be no unassign callback to queue the
|
||||
// rebuild. Otherwise OnSwapChainReleased does so after teardown has drained.
|
||||
if (rebuild)
|
||||
InterlockedExchange(&m_finishInitQueued, 1);
|
||||
m_finishInitQueued.store(1);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnMonitorDestroyed(IDDCX_MONITOR monitor)
|
||||
@@ -571,7 +572,7 @@ void CIndirectDeviceContext::OnSwapChainReleased()
|
||||
ReleaseSRWLockExclusive(&m_stateLock);
|
||||
|
||||
if (rebuild)
|
||||
InterlockedExchange(&m_finishInitQueued, 1);
|
||||
m_finishInitQueued.store(1);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::OnSwapChainReady()
|
||||
@@ -613,7 +614,7 @@ void CIndirectDeviceContext::OnSwapChainReady()
|
||||
g_pipe.SetDeviceContext(this);
|
||||
|
||||
if (replug)
|
||||
InterlockedExchange(&m_replugQueued, 1);
|
||||
m_replugQueued.store(1);
|
||||
else if (doSetMode)
|
||||
g_pipe.SetDisplayMode(mode.width, mode.height, mode.refresh);
|
||||
}
|
||||
@@ -1100,7 +1101,7 @@ bool CIndirectDeviceContext::SetupLGMP(size_t alignSize)
|
||||
|
||||
void CIndirectDeviceContext::DeInitLGMP()
|
||||
{
|
||||
InterlockedExchange(&m_publishedFrameIndex, -1);
|
||||
m_publishedFrameIndex.store(-1);
|
||||
|
||||
// The retry timer callback dereferences this context, so make sure it is
|
||||
// stopped and drained before we tear anything down. Wait for any in-flight
|
||||
@@ -1135,13 +1136,13 @@ void CIndirectDeviceContext::DeInitLGMP()
|
||||
void CIndirectDeviceContext::LGMPTimer()
|
||||
{
|
||||
// Rebuild the monitor queued by ReplugMonitor, off the IddCx callback thread.
|
||||
if (InterlockedExchange(&m_finishInitQueued, 0))
|
||||
if (m_finishInitQueued.exchange(0))
|
||||
{
|
||||
FinishInit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (InterlockedExchange(&m_replugQueued, 0))
|
||||
if (m_replugQueued.exchange(0))
|
||||
{
|
||||
ReplugMonitor();
|
||||
return;
|
||||
@@ -1188,8 +1189,7 @@ void CIndirectDeviceContext::LGMPTimer()
|
||||
|
||||
if (lgmpHostQueueNewSubs(m_frameQueue) && m_monitor)
|
||||
{
|
||||
const LONG frameIndex =
|
||||
InterlockedCompareExchange(&m_publishedFrameIndex, 0, 0);
|
||||
const LONG frameIndex = m_publishedFrameIndex.load();
|
||||
if (frameIndex >= 0)
|
||||
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[frameIndex]);
|
||||
}
|
||||
@@ -1299,6 +1299,11 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
// fi->offset is initialized at startup
|
||||
fi->flags = flags;
|
||||
fi->sdrWhiteLevel = dstFormat.sdrWhiteLevel;
|
||||
fi->captureTime = 0;
|
||||
fi->postProcessTime = 0;
|
||||
fi->copyTime = 0;
|
||||
fi->timingSerial = 0;
|
||||
InterlockedExchange((volatile LONG *)&fi->timingValid, 0);
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
fi->type = dstFormat.format;
|
||||
|
||||
@@ -1351,7 +1356,7 @@ bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex)
|
||||
/* Make resends select this submitted frame before posting it. This prevents
|
||||
* a new subscriber racing publication from receiving the previous frame
|
||||
* after the new one. */
|
||||
InterlockedExchange(&m_publishedFrameIndex, (LONG)frameIndex);
|
||||
m_publishedFrameIndex.store(static_cast<LONG>(frameIndex));
|
||||
|
||||
const LGMP_STATUS status =
|
||||
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[frameIndex]);
|
||||
@@ -1364,6 +1369,20 @@ bool CIndirectDeviceContext::PublishFrameBuffer(unsigned frameIndex)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::SetFrameTiming(unsigned frameIndex,
|
||||
uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_LEN)
|
||||
return;
|
||||
|
||||
KVMFRFrame * frame = m_frame[frameIndex];
|
||||
frame->captureTime = captureTime;
|
||||
frame->postProcessTime = postProcessTime;
|
||||
frame->copyTime = copyTime;
|
||||
frame->timingSerial = frame->frameSerial;
|
||||
InterlockedExchange((volatile LONG *)&frame->timingValid, 1);
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const
|
||||
{
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <IddCx.h>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include "CIVSHMEM.h"
|
||||
@@ -78,9 +79,9 @@ private:
|
||||
|
||||
// Retry state for InitAdapter. At boot the IVSHMEM device may not have
|
||||
// enumerated yet; if so we re-attempt from a timer instead of giving up.
|
||||
WDFTIMER m_initTimer = nullptr;
|
||||
bool m_ivshmemOpened = false;
|
||||
volatile LONG m_initInProgress = 0;
|
||||
WDFTIMER m_initTimer = nullptr;
|
||||
bool m_ivshmemOpened = false;
|
||||
std::atomic<LONG> m_initInProgress = 0;
|
||||
|
||||
CIVSHMEM m_ivshmem;
|
||||
|
||||
@@ -99,16 +100,16 @@ private:
|
||||
bool m_cursorVisible = false;
|
||||
int m_cursorX = 0, m_cursorY = 0;
|
||||
|
||||
size_t m_alignSize = 0;
|
||||
size_t m_frameMemoryOffset = 0;
|
||||
size_t m_maxFrameSize = 0;
|
||||
int m_frameIndex = 0;
|
||||
volatile LONG m_publishedFrameIndex = -1;
|
||||
uint32_t m_formatVer = 0;
|
||||
uint32_t m_frameSerial = 0;
|
||||
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {};
|
||||
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
|
||||
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
|
||||
size_t m_alignSize = 0;
|
||||
size_t m_frameMemoryOffset = 0;
|
||||
size_t m_maxFrameSize = 0;
|
||||
int m_frameIndex = 0;
|
||||
std::atomic<LONG> m_publishedFrameIndex = -1;
|
||||
uint32_t m_formatVer = 0;
|
||||
uint32_t m_frameSerial = 0;
|
||||
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {};
|
||||
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
|
||||
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
|
||||
|
||||
unsigned m_width = 0;
|
||||
unsigned m_height = 0;
|
||||
@@ -164,8 +165,8 @@ private:
|
||||
|
||||
// Set by ReplugMonitor after a departure to rebuild the monitor from the LGMP
|
||||
// timer, off the IddCx callback thread.
|
||||
volatile LONG m_finishInitQueued = 0;
|
||||
volatile LONG m_replugQueued = 0;
|
||||
std::atomic<LONG> m_finishInitQueued = 0;
|
||||
std::atomic<LONG> m_replugQueued = 0;
|
||||
|
||||
public:
|
||||
CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) :
|
||||
@@ -216,6 +217,8 @@ public:
|
||||
bool FrameBufferAvailable() const;
|
||||
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, const RECT * dirtyRects, unsigned nbDirtyRects);
|
||||
bool PublishFrameBuffer(unsigned frameIndex);
|
||||
void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
|
||||
uint64_t postProcessTime, uint64_t copyTime);
|
||||
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const;
|
||||
void FinalizeFrameBuffer(unsigned frameIndex) const;
|
||||
|
||||
|
||||
@@ -28,6 +28,22 @@
|
||||
static const uint32_t HDR_PQ_MIN_LUMINANCE = 50;
|
||||
static const uint32_t HDR_PQ_MAX_LUMINANCE = 10000;
|
||||
|
||||
static uint64_t Nanotime()
|
||||
{
|
||||
static const uint64_t frequency = []()
|
||||
{
|
||||
LARGE_INTEGER value;
|
||||
QueryPerformanceFrequency(&value);
|
||||
return (uint64_t)value.QuadPart;
|
||||
}();
|
||||
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceCounter(&counter);
|
||||
const uint64_t ticks = (uint64_t)counter.QuadPart;
|
||||
return ticks / frequency * 1000000000ULL +
|
||||
ticks % frequency * 1000000000ULL / frequency;
|
||||
}
|
||||
|
||||
CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContext,
|
||||
UINT64 assignmentGeneration, IDDCX_MONITOR monitor,
|
||||
CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain,
|
||||
@@ -192,6 +208,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
|
||||
// path HDR is not available, so default to SDR.
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
|
||||
UINT sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||
const uint64_t captureStart = Nanotime();
|
||||
|
||||
#ifdef HAS_IDDCX_110
|
||||
if (m_devContext->HasIddCx110DDIs())
|
||||
@@ -252,7 +269,8 @@ bool CSwapChainProcessor::SwapChainThreadCore()
|
||||
if (frameNumber != lastFrameNumber)
|
||||
{
|
||||
lastFrameNumber = frameNumber;
|
||||
if (!SwapChainNewFrame(surface, dirtyRectCount, colorSpace, sdrWhiteLevel))
|
||||
if (!SwapChainNewFrame(surface, dirtyRectCount, colorSpace,
|
||||
sdrWhiteLevel, Nanotime() - captureStart))
|
||||
DEBUG_WARN("Failed to submit frame");
|
||||
}
|
||||
|
||||
@@ -286,16 +304,21 @@ void CSwapChainProcessor::CompletionFunction(
|
||||
// fail gracefully
|
||||
if (!result)
|
||||
{
|
||||
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||
fbRes->GetCaptureTime(), fbRes->GetPostProcessTime(),
|
||||
Nanotime() - fbRes->GetCopyStart());
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
return;
|
||||
}
|
||||
|
||||
if (sc->m_dx12Device->IsIndirectCopy())
|
||||
sc->m_devContext->WriteFrameBuffer(
|
||||
fbRes->GetFrameIndex(),
|
||||
fbRes->GetMap(), 0, fbRes->GetFrameSize(), true);
|
||||
else
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
fbRes->GetFrameIndex(), fbRes->GetMap(), 0, fbRes->GetFrameSize(), false);
|
||||
|
||||
const uint64_t copyTime = Nanotime() - fbRes->GetCopyStart();
|
||||
sc->m_devContext->SetFrameTiming(fbRes->GetFrameIndex(),
|
||||
fbRes->GetCaptureTime(), fbRes->GetPostProcessTime(), copyTime);
|
||||
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
|
||||
}
|
||||
|
||||
|
||||
@@ -470,8 +493,9 @@ bool CSwapChainProcessor::GetContentHDRMetadata(D12FrameFormat& format) const
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel)
|
||||
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
||||
unsigned dirtyRectCount, DXGI_COLOR_SPACE_TYPE colorSpace,
|
||||
UINT sdrWhiteLevel, uint64_t captureTime)
|
||||
{
|
||||
// Preserve the fast drop path: never hold an IddCx frame while waiting for
|
||||
// a slow or disconnected client. We have not read its rectangles, so force
|
||||
@@ -482,6 +506,8 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t postProcessStart = Nanotime();
|
||||
|
||||
ComPtr<ID3D11Texture2D> texture;
|
||||
HRESULT hr = acquiredBuffer.As(&texture);
|
||||
if (FAILED(hr))
|
||||
@@ -708,6 +734,9 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t copyStart = Nanotime();
|
||||
fbRes->SetTiming(captureTime, copyStart - postProcessStart, copyStart);
|
||||
|
||||
copyQueue->SetCompletionCallback(&CompletionFunction, this, fbRes);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
|
||||
|
||||
@@ -102,7 +102,7 @@ private:
|
||||
#endif
|
||||
bool GetContentHDRMetadata(D12FrameFormat& format) const;
|
||||
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel);
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel, uint64_t captureTime);
|
||||
|
||||
public:
|
||||
CSwapChainProcessor(CIndirectMonitorContext * monitorContext, UINT64 assignmentGeneration,
|
||||
|
||||
Reference in New Issue
Block a user