mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 00:31:31 +00:00
[idd] capture: recover final desktop updates
Request one static-desktop re-encode and inspect repeated presentation numbers for its empty dirty-rectangle marker. Prepare every marker in a free candidate without superseding retained damage, so the last desktop update survives a late copy failure.
This commit is contained in:
@@ -412,7 +412,8 @@ void CIndirectDeviceContext::InitAdapter()
|
|||||||
caps.Flags |= IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
|
caps.Flags |= IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
caps.MaxMonitorsSupported = 1;
|
caps.MaxMonitorsSupported = 1;
|
||||||
|
caps.StaticDesktopReencodeFrameCount = 1;
|
||||||
|
|
||||||
caps.EndPointDiagnostics.Size = sizeof(caps.EndPointDiagnostics);
|
caps.EndPointDiagnostics.Size = sizeof(caps.EndPointDiagnostics);
|
||||||
caps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
caps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE;
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex
|
|||||||
// once set or only one thread would ever observe termination.
|
// once set or only one thread would ever observe termination.
|
||||||
m_terminateEvent.Attach(CreateEvent(nullptr, TRUE, FALSE, nullptr));
|
m_terminateEvent.Attach(CreateEvent(nullptr, TRUE, FALSE, nullptr));
|
||||||
m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
m_candidateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||||
|
m_candidateAvailableEvent.Attach(
|
||||||
|
CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||||
m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr,
|
m_publishTimer.Attach(CreateWaitableTimerExW(nullptr, nullptr,
|
||||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS));
|
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS));
|
||||||
if (!m_publishTimer.Get())
|
if (!m_publishTimer.Get())
|
||||||
@@ -102,7 +104,8 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex
|
|||||||
bool CSwapChainProcessor::Start()
|
bool CSwapChainProcessor::Start()
|
||||||
{
|
{
|
||||||
if (!m_terminateEvent.Get() || !m_candidateEvent.Get() ||
|
if (!m_terminateEvent.Get() || !m_candidateEvent.Get() ||
|
||||||
!m_publishTimer.Get() || !m_cursorDataEvent.Get() || !m_shapeBuffer)
|
!m_candidateAvailableEvent.Get() || !m_publishTimer.Get() ||
|
||||||
|
!m_cursorDataEvent.Get() || !m_shapeBuffer)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize swap chain worker resources");
|
DEBUG_ERROR("Failed to initialize swap chain worker resources");
|
||||||
return false;
|
return false;
|
||||||
@@ -516,7 +519,8 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
|||||||
// restart loop while waiting for a valid configuration
|
// restart loop while waiting for a valid configuration
|
||||||
g_pipe.SetGPUStatus(m_dx11Device->IsSoftware());
|
g_pipe.SetGPUStatus(m_dx11Device->IsSoftware());
|
||||||
|
|
||||||
UINT lastFrameNumber = 0;
|
UINT lastFrameNumber = 0;
|
||||||
|
bool hasLastFrameNumber = false;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0)
|
if (WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0)
|
||||||
@@ -591,13 +595,16 @@ void CSwapChainProcessor::SwapChainThreadCore()
|
|||||||
}
|
}
|
||||||
else if (SUCCEEDED(hr))
|
else if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
if (frameNumber != lastFrameNumber)
|
const bool duplicateFrame =
|
||||||
|
hasLastFrameNumber && frameNumber == lastFrameNumber;
|
||||||
|
if (!duplicateFrame)
|
||||||
{
|
{
|
||||||
lastFrameNumber = frameNumber;
|
lastFrameNumber = frameNumber;
|
||||||
if (!SwapChainNewFrame(surface, dirtyRectCount, moveRegionCount,
|
hasLastFrameNumber = true;
|
||||||
colorSpace, sdrWhiteLevel, captureStart))
|
|
||||||
DEBUG_WARN("Failed to submit frame");
|
|
||||||
}
|
}
|
||||||
|
if (!SwapChainNewFrame(surface, dirtyRectCount, moveRegionCount,
|
||||||
|
colorSpace, sdrWhiteLevel, captureStart, duplicateFrame))
|
||||||
|
DEBUG_WARN("Failed to submit frame");
|
||||||
|
|
||||||
// Every acquired frame must be finished before the next acquire, even if
|
// Every acquired frame must be finished before the next acquire, even if
|
||||||
// its presentation number was a duplicate and no work was submitted.
|
// its presentation number was a duplicate and no work was submitted.
|
||||||
@@ -944,7 +951,8 @@ void CSwapChainProcessor::AccumulateFrameDamage(
|
|||||||
ReleaseSRWLockExclusive(&m_damageLock);
|
ReleaseSRWLockExclusive(&m_damageLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSwapChainProcessor::AcquireCandidate(bool exclusiveSample)
|
int CSwapChainProcessor::AcquireCandidate(
|
||||||
|
bool exclusiveSample, bool allowSupersede)
|
||||||
{
|
{
|
||||||
int selected = -1;
|
int selected = -1;
|
||||||
uint64_t oldest = UINT64_MAX;
|
uint64_t oldest = UINT64_MAX;
|
||||||
@@ -978,7 +986,7 @@ int CSwapChainProcessor::AcquireCandidate(bool exclusiveSample)
|
|||||||
// Preserve one completed fallback unless another candidate is already
|
// Preserve one completed fallback unless another candidate is already
|
||||||
// publishing. In that case its peer must remain available for new source
|
// publishing. In that case its peer must remain available for new source
|
||||||
// frames instead of being frozen for the duration of the transport copy.
|
// frames instead of being frozen for the duration of the transport copy.
|
||||||
if (!exclusiveSample && selected < 0 &&
|
if (allowSupersede && !exclusiveSample && selected < 0 &&
|
||||||
readyCount > (publishing ? 0U : 1U))
|
readyCount > (publishing ? 0U : 1U))
|
||||||
for (unsigned i = 0; i < ARRAYSIZE(m_candidates); ++i)
|
for (unsigned i = 0; i < ARRAYSIZE(m_candidates); ++i)
|
||||||
if (m_candidates[i].state == CANDIDATE_READY &&
|
if (m_candidates[i].state == CANDIDATE_READY &&
|
||||||
@@ -1099,6 +1107,7 @@ void CSwapChainProcessor::ResetCandidates()
|
|||||||
void CSwapChainProcessor::SignalCandidateState()
|
void CSwapChainProcessor::SignalCandidateState()
|
||||||
{
|
{
|
||||||
SetEvent(m_candidateEvent.Get());
|
SetEvent(m_candidateEvent.Get());
|
||||||
|
SetEvent(m_candidateAvailableEvent.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSwapChainProcessor::PublishNewestCandidate(
|
bool CSwapChainProcessor::PublishNewestCandidate(
|
||||||
@@ -1389,35 +1398,21 @@ bool CSwapChainProcessor::GetContentHDRMetadata(D12FrameFormat& format) const
|
|||||||
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
||||||
unsigned dirtyRectCount, unsigned moveRegionCount,
|
unsigned dirtyRectCount, unsigned moveRegionCount,
|
||||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
||||||
uint64_t captureStart)
|
uint64_t captureStart, bool duplicateFrame)
|
||||||
{
|
{
|
||||||
const uint64_t postProcessStart = CFrameScheduler::Nanotime();
|
const uint64_t postProcessStart = CFrameScheduler::Nanotime();
|
||||||
const uint64_t captureTime = postProcessStart - captureStart;
|
const uint64_t captureTime = postProcessStart - captureStart;
|
||||||
|
|
||||||
ComPtr<ID3D11Texture2D> texture;
|
RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {0};
|
||||||
HRESULT hr = acquiredBuffer.As(&texture);
|
unsigned resolvedDirtyRectCount = 0;
|
||||||
if (FAILED(hr))
|
bool fullDamage = false;
|
||||||
{
|
bool noImageUpdate = false;
|
||||||
DEBUG_ERROR_HR(hr, "Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
|
HRESULT hr;
|
||||||
SetFullPendingDamage();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CInteropResource * srcRes = m_resPool.Get(texture);
|
|
||||||
if (!srcRes)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to get a CInteropResource from the pool");
|
|
||||||
SetFullPendingDamage();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RECT dirtyRects[LG_MAX_DIRTY_RECTS] = {0};
|
|
||||||
bool noImageUpdate = false;
|
|
||||||
if (moveRegionCount || dirtyRectCount > ARRAYSIZE(dirtyRects))
|
if (moveRegionCount || dirtyRectCount > ARRAYSIZE(dirtyRects))
|
||||||
{
|
{
|
||||||
// Move regions are not represented by the dirty rectangle list. Copy the
|
// Move regions are not represented by the dirty rectangle list. Copy the
|
||||||
// full surface so the alternating destinations remain coherent.
|
// full surface so the alternating destinations remain coherent.
|
||||||
srcRes->SetFullDamage();
|
fullDamage = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1430,7 +1425,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR_HR(hr, "IddCxSwapChainGetDirtyRects Failed");
|
DEBUG_ERROR_HR(hr, "IddCxSwapChainGetDirtyRects Failed");
|
||||||
srcRes->SetFullDamage();
|
fullDamage = true;
|
||||||
}
|
}
|
||||||
else if (dirtyOut.DirtyRectOutCount == 1 &&
|
else if (dirtyOut.DirtyRectOutCount == 1 &&
|
||||||
dirtyRects[0].left == 0 && dirtyRects[0].top == 0 &&
|
dirtyRects[0].left == 0 && dirtyRects[0].top == 0 &&
|
||||||
@@ -1439,12 +1434,39 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
// One empty rectangle is IddCx's static-desktop re-encode marker. It
|
// One empty rectangle is IddCx's static-desktop re-encode marker. It
|
||||||
// does not describe an image update and must not become full damage.
|
// does not describe an image update and must not become full damage.
|
||||||
noImageUpdate = true;
|
noImageUpdate = true;
|
||||||
srcRes->SetDirtyRects(nullptr, 0);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
srcRes->SetDirtyRects(dirtyRects, dirtyOut.DirtyRectOutCount);
|
resolvedDirtyRectCount = dirtyOut.DirtyRectOutCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reencode frames reuse the preceding presentation number. Inspect their
|
||||||
|
// empty dirty rectangle above, but suppress every ordinary duplicate.
|
||||||
|
if (duplicateFrame && !noImageUpdate)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ComPtr<ID3D11Texture2D> texture;
|
||||||
|
hr = acquiredBuffer.As(&texture);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(hr,
|
||||||
|
"Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
|
||||||
|
SetFullPendingDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CInteropResource * srcRes = m_resPool.Get(texture);
|
||||||
|
if (!srcRes)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to get a CInteropResource from the pool");
|
||||||
|
SetFullPendingDamage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullDamage)
|
||||||
|
srcRes->SetFullDamage();
|
||||||
|
else
|
||||||
|
srcRes->SetDirtyRects(dirtyRects, resolvedDirtyRectCount);
|
||||||
|
|
||||||
D3D12_RESOURCE_DESC srcDesc = srcRes->GetRes()->GetDesc();
|
D3D12_RESOURCE_DESC srcDesc = srcRes->GetRes()->GetDesc();
|
||||||
if (!noImageUpdate)
|
if (!noImageUpdate)
|
||||||
{
|
{
|
||||||
@@ -1619,16 +1641,30 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
if (needsReconfigure || postProcessFormatChanged || frameMetadataChanged)
|
if (needsReconfigure || postProcessFormatChanged || frameMetadataChanged)
|
||||||
m_devContext->ForceFrame();
|
m_devContext->ForceFrame();
|
||||||
|
|
||||||
if (noImageUpdate)
|
// Always prepare the requested static-desktop re-encode. An older
|
||||||
|
// publication can still fail after this frame is acquired, so deciding
|
||||||
|
// solely from the current pending-damage state can lose the final update.
|
||||||
|
int selectedCandidate = AcquireCandidate(timingToken != 0, !noImageUpdate);
|
||||||
|
while (selectedCandidate < 0 && noImageUpdate)
|
||||||
{
|
{
|
||||||
AcquireSRWLockShared(&m_damageLock);
|
HANDLE waitHandles[] =
|
||||||
const bool hasPendingDamage = m_hasPendingDamage;
|
{
|
||||||
ReleaseSRWLockShared(&m_damageLock);
|
m_terminateEvent.Get(),
|
||||||
if (!hasPendingDamage)
|
m_candidateAvailableEvent.Get(),
|
||||||
|
};
|
||||||
|
const DWORD waitResult = WaitForMultipleObjects(
|
||||||
|
ARRAYSIZE(waitHandles), waitHandles, FALSE, INFINITE);
|
||||||
|
if (waitResult == WAIT_OBJECT_0)
|
||||||
return true;
|
return true;
|
||||||
}
|
if (waitResult != WAIT_OBJECT_0 + 1)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(GetLastError()),
|
||||||
|
"Failed while waiting for a frame candidate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const int selectedCandidate = AcquireCandidate(timingToken != 0);
|
selectedCandidate = AcquireCandidate(timingToken != 0, false);
|
||||||
|
}
|
||||||
if (selectedCandidate < 0)
|
if (selectedCandidate < 0)
|
||||||
{
|
{
|
||||||
m_devContext->FrameSuperseded();
|
m_devContext->FrameSuperseded();
|
||||||
@@ -1636,6 +1672,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
|||||||
}
|
}
|
||||||
const unsigned candidateIndex =
|
const unsigned candidateIndex =
|
||||||
static_cast<unsigned>(selectedCandidate);
|
static_cast<unsigned>(selectedCandidate);
|
||||||
|
|
||||||
FrameCandidate& candidate = m_candidates[candidateIndex];
|
FrameCandidate& candidate = m_candidates[candidateIndex];
|
||||||
|
|
||||||
CSRWExclusiveLock pipelineLock(&m_pipelineLock);
|
CSRWExclusiveLock pipelineLock(&m_pipelineLock);
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ private:
|
|||||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
|
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
|
||||||
Wrappers::Event m_terminateEvent;
|
Wrappers::Event m_terminateEvent;
|
||||||
Wrappers::Event m_candidateEvent;
|
Wrappers::Event m_candidateEvent;
|
||||||
|
Wrappers::Event m_candidateAvailableEvent;
|
||||||
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_publishTimer;
|
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_publishTimer;
|
||||||
|
|
||||||
Wrappers::Event m_cursorDataEvent;
|
Wrappers::Event m_cursorDataEvent;
|
||||||
@@ -147,7 +148,7 @@ private:
|
|||||||
const CFrameScheduler::Schedule& schedule, bool periodic,
|
const CFrameScheduler::Schedule& schedule, bool periodic,
|
||||||
uint64_t publishStart);
|
uint64_t publishStart);
|
||||||
bool HasReadyCandidate();
|
bool HasReadyCandidate();
|
||||||
int AcquireCandidate(bool exclusiveSample);
|
int AcquireCandidate(bool exclusiveSample, bool allowSupersede);
|
||||||
void ReleaseCandidate(unsigned candidateIndex);
|
void ReleaseCandidate(unsigned candidateIndex);
|
||||||
bool EnsureCandidateResource(
|
bool EnsureCandidateResource(
|
||||||
unsigned candidateIndex, size_t frameSize);
|
unsigned candidateIndex, size_t frameSize);
|
||||||
@@ -171,7 +172,7 @@ private:
|
|||||||
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer,
|
||||||
unsigned dirtyRectCount, unsigned moveRegionCount,
|
unsigned dirtyRectCount, unsigned moveRegionCount,
|
||||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel,
|
||||||
uint64_t captureStart);
|
uint64_t captureStart, bool duplicateFrame);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CSwapChainProcessor(CIndirectMonitorContext * monitorContext, UINT64 assignmentGeneration,
|
CSwapChainProcessor(CIndirectMonitorContext * monitorContext, UINT64 assignmentGeneration,
|
||||||
|
|||||||
Reference in New Issue
Block a user