[idd] capture: add direct full-copy workaround

This commit is contained in:
Geoffrey McRae
2026-08-17 04:42:30 +10:00
parent 4e15f3b68b
commit 96c3056cc9
9 changed files with 88 additions and 8 deletions

View File

@@ -63,6 +63,14 @@ Disable no GPU warning
processing. It does not enable GPU acceleration or change the active processing. It does not enable GPU acceleration or change the active
adapter. adapter.
Force full direct frame copies
Works around GPU drivers that corrupt partial D3D12 copies into the shared
transport heap. When enabled, the GPU copies every direct frame in full;
readback/indirect transfers continue using partial damage copies. This can
substantially increase GPU and shared-memory bandwidth, so leave it
disabled unless you encounter corruption. Changing it requests a driver
reload.
Unlike mode-list edits, preference checkboxes are saved when clicked. Unlike mode-list edits, preference checkboxes are saved when clicked.
Software processing Software processing

View File

@@ -82,6 +82,7 @@ quadraphonic
Quadro Quadro
queueing queueing
radeon radeon
readback
realtime realtime
renderer renderer
repo repo

View File

@@ -19,6 +19,8 @@
*/ */
#include "capture/CHardwareFrameProcessor.h" #include "capture/CHardwareFrameProcessor.h"
#include "config/CSettings.h"
#include "capture/CFrameProcessorUtil.h" #include "capture/CFrameProcessorUtil.h"
#include "transport/IFrameTransport.h" #include "transport/IFrameTransport.h"
#include "CSRWLock.h" #include "CSRWLock.h"
@@ -76,11 +78,15 @@ CHardwareFrameProcessor::CHardwareFrameProcessor(
CSRWLock * pipelineLock, HANDLE terminateEvent, bool useCadence) : CSRWLock * pipelineLock, HANDLE terminateEvent, bool useCadence) :
CFrameProcessor(transport, std::move(dx12), postProcessors, CFrameProcessor(transport, std::move(dx12), postProcessors,
pipelineLock, terminateEvent), pipelineLock, terminateEvent),
m_useCadence(useCadence) m_useCadence(useCadence),
m_forceFullDirectCopy(CSettings::ShouldForceFullDirectCopy())
{ {
m_candidateAvailableEvent.Attach( m_candidateAvailableEvent.Attach(
CreateEvent(nullptr, FALSE, FALSE, nullptr)); CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_copySubmitEvent.Attach(CreateEvent(nullptr, TRUE, TRUE, nullptr)); m_copySubmitEvent.Attach(CreateEvent(nullptr, TRUE, TRUE, nullptr));
if (m_forceFullDirectCopy)
DEBUG_INFO("Forcing full copies to direct transport memory");
} }
bool CHardwareFrameProcessor::IsValid() const bool CHardwareFrameProcessor::IsValid() const
@@ -668,12 +674,17 @@ bool CHardwareFrameProcessor::Publish(
RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {}; RECT copyDirtyRects[LG_MAX_DIRTY_RECTS * 2] = {};
unsigned nbCopyDirtyRects = 0; unsigned nbCopyDirtyRects = 0;
const bool fullCopy = CFrameProcessorUtil::BuildCopyDamage( // Some GPUs corrupt fragmented writes to the external transport heap.
postProcessor, prepared.targets[i].fullCopy, // An unmapped target is a direct D3D12 transport resource; readback
previousDirtyRects, nbPreviousDirtyRects, // targets remain mapped and continue using normal damage copies.
candidate.dirtyRects, candidate.nbDirtyRects, const bool fullCopy =
candidate.dstFormat.width, candidate.dstFormat.height, (m_forceFullDirectCopy && !fbRes->GetMap()) ||
copyDirtyRects, &nbCopyDirtyRects); CFrameProcessorUtil::BuildCopyDamage(
postProcessor, prepared.targets[i].fullCopy,
previousDirtyRects, nbPreviousDirtyRects,
candidate.dirtyRects, candidate.nbDirtyRects,
candidate.dstFormat.width, candidate.dstFormat.height,
copyDirtyRects, &nbCopyDirtyRects);
fbRes->SetTiming( fbRes->SetTiming(
candidate.captureTime, candidate.postProcessStart, publishStart); candidate.captureTime, candidate.postProcessStart, publishStart);
fbRes->SetCandidateIndex(candidateIndex); fbRes->SetCandidateIndex(candidateIndex);

View File

@@ -73,6 +73,7 @@ private:
CSRWLock m_copySubmitLock; CSRWLock m_copySubmitLock;
bool m_publishPending = false; bool m_publishPending = false;
bool m_useCadence = true; bool m_useCadence = true;
bool m_forceFullDirectCopy = false;
Wrappers::Event m_candidateAvailableEvent; Wrappers::Event m_candidateAvailableEvent;
Wrappers::Event m_copySubmitEvent; Wrappers::Event m_copySubmitEvent;

View File

@@ -96,6 +96,11 @@ class CSettings
return ReadBoolValue(L"LogStatistics"); return ReadBoolValue(L"LogStatistics");
} }
static bool ShouldForceFullDirectCopy()
{
return ReadBoolValue(L"ForceFullDirectCopy");
}
private: private:
static const wchar_t * RegistryKey() static const wchar_t * RegistryKey()
{ {

View File

@@ -54,6 +54,7 @@ CConfigWindow::CConfigWindow() : m_scale(1)
m_defaultRefresh = m_settings.getDefaultRefresh(); m_defaultRefresh = m_settings.getDefaultRefresh();
m_noGPU = m_settings.getNoGPU(); m_noGPU = m_settings.getNoGPU();
m_exclusive = m_settings.getExclusiveMonitor(); m_exclusive = m_settings.getExclusiveMonitor();
m_forceFullDirectCopy = m_settings.getForceFullDirectCopy();
} }
if (!CreateWindowEx(0, MAKEINTATOM(s_atom), L"Looking Glass IDD Configuration", if (!CreateWindowEx(0, MAKEINTATOM(s_atom), L"Looking Glass IDD Configuration",
@@ -94,6 +95,7 @@ void CConfigWindow::updateFont()
*m_modeWidth, *m_modeHeight, *m_modeRefresh, *m_modeUpdate, *m_modeDelete, *m_modeReset, *m_modeWidth, *m_modeHeight, *m_modeRefresh, *m_modeUpdate, *m_modeDelete, *m_modeReset,
*m_defRefreshLabel, *m_defRefresh, *m_defRefreshHz, *m_modeSave, *m_modeRevert, *m_defRefreshLabel, *m_defRefresh, *m_defRefreshHz, *m_modeSave, *m_modeRevert,
*m_prefGroup, *m_prefNoGPU, *m_prefExclusive, *m_prefGroup, *m_prefNoGPU, *m_prefExclusive,
*m_prefForceFullDirectCopy,
})) }))
SendMessage(child, WM_SETFONT, (WPARAM)m_font.Get(), 1); SendMessage(child, WM_SETFONT, (WPARAM)m_font.Get(), 1);
} }
@@ -187,6 +189,8 @@ LRESULT CConfigWindow::onCreate()
m_prefGroup.reset(new CGroupBox(L"Preferences", 0, m_hwnd)); m_prefGroup.reset(new CGroupBox(L"Preferences", 0, m_hwnd));
m_prefNoGPU.reset(new CCheckbox(L"Disable no GPU warning", 0, m_hwnd)); m_prefNoGPU.reset(new CCheckbox(L"Disable no GPU warning", 0, m_hwnd));
m_prefExclusive.reset(new CCheckbox(L"Make LG the only monitor", 0, m_hwnd)); m_prefExclusive.reset(new CCheckbox(L"Make LG the only monitor", 0, m_hwnd));
m_prefForceFullDirectCopy.reset(new CCheckbox(
L"Force full direct frame copies", 0, m_hwnd));
if (m_noGPU) if (m_noGPU)
m_prefNoGPU->setChecked(*m_noGPU); m_prefNoGPU->setChecked(*m_noGPU);
@@ -194,6 +198,9 @@ LRESULT CConfigWindow::onCreate()
if (m_exclusive) if (m_exclusive)
m_prefExclusive->setChecked(*m_exclusive); m_prefExclusive->setChecked(*m_exclusive);
if (m_forceFullDirectCopy)
m_prefForceFullDirectCopy->setChecked(*m_forceFullDirectCopy);
LONG width, height; LONG width, height;
getMinimumSize(width, height); getMinimumSize(width, height);
SetWindowPos(m_hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER); SetWindowPos(m_hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER);
@@ -233,9 +240,10 @@ LRESULT CConfigWindow::onResize(DWORD width, DWORD height)
pos.pinBottomLeft(*m_modeSave, 20, 20, 132, 24); pos.pinBottomLeft(*m_modeSave, 20, 20, 132, 24);
pos.pinBottomLeft(*m_modeRevert, 154, 20, 50, 24); pos.pinBottomLeft(*m_modeRevert, 154, 20, 50, 24);
pos.pinTopLeft(*m_prefGroup, 224, 40, 200, 72); pos.pinTopLeft(*m_prefGroup, 224, 40, 200, 92);
pos.pinTopLeft(*m_prefNoGPU, 236, 64, 176, 20); pos.pinTopLeft(*m_prefNoGPU, 236, 64, 176, 20);
pos.pinTopLeft(*m_prefExclusive, 236, 84, 176, 20); pos.pinTopLeft(*m_prefExclusive, 236, 84, 176, 20);
pos.pinTopLeft(*m_prefForceFullDirectCopy, 236, 104, 188, 20);
return 0; return 0;
} }
@@ -344,6 +352,22 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
m_settings.setExclusiveMonitor(*m_exclusive); m_settings.setExclusiveMonitor(*m_exclusive);
m_prefExclusive->setChecked(*m_exclusive); m_prefExclusive->setChecked(*m_exclusive);
} }
else if (m_prefForceFullDirectCopy &&
hwnd == *m_prefForceFullDirectCopy && code == BN_CLICKED &&
m_forceFullDirectCopy)
{
const bool value = !*m_forceFullDirectCopy;
const LSTATUS result = m_settings.setForceFullDirectCopy(value);
if (result != ERROR_SUCCESS)
{
DEBUG_ERROR_HR(result, "Failed to save ForceFullDirectCopy");
return 0;
}
*m_forceFullDirectCopy = value;
m_prefForceFullDirectCopy->setChecked(value);
sendSettingChange();
}
else if (m_modeSave && hwnd == *m_modeSave && code == BN_CLICKED) else if (m_modeSave && hwnd == *m_modeSave && code == BN_CLICKED)
{ {
bool updated = false; bool updated = false;

View File

@@ -64,6 +64,7 @@ class CConfigWindow : public CWindow
std::unique_ptr<CGroupBox> m_prefGroup; std::unique_ptr<CGroupBox> m_prefGroup;
std::unique_ptr<CCheckbox> m_prefNoGPU; std::unique_ptr<CCheckbox> m_prefNoGPU;
std::unique_ptr<CCheckbox> m_prefExclusive; std::unique_ptr<CCheckbox> m_prefExclusive;
std::unique_ptr<CCheckbox> m_prefForceFullDirectCopy;
std::function<void()> m_onDestroy; std::function<void()> m_onDestroy;
std::function<void()> m_onSettingChange; std::function<void()> m_onSettingChange;
@@ -75,6 +76,7 @@ class CConfigWindow : public CWindow
std::optional<DWORD> m_defaultRefresh; std::optional<DWORD> m_defaultRefresh;
std::optional<bool> m_noGPU; std::optional<bool> m_noGPU;
std::optional<bool> m_exclusive; std::optional<bool> m_exclusive;
std::optional<bool> m_forceFullDirectCopy;
void getMinimumSize(LONG &width, LONG &height); void getMinimumSize(LONG &width, LONG &height);
void updateFont(); void updateFont();

View File

@@ -265,3 +265,28 @@ LSTATUS CRegistrySettings::setExclusiveMonitor(bool exclusive)
DWORD dwValue = exclusive; DWORD dwValue = exclusive;
return RegSetValueEx(hKey, L"ExclusiveMonitor", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD)); return RegSetValueEx(hKey, L"ExclusiveMonitor", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));
} }
std::optional<bool> CRegistrySettings::getForceFullDirectCopy()
{
DWORD result, cbData = sizeof result;
LSTATUS status = RegGetValue(hKey, nullptr, L"ForceFullDirectCopy",
RRF_RT_REG_DWORD, nullptr, &result, &cbData);
switch (status)
{
case ERROR_SUCCESS:
return !!result;
case ERROR_FILE_NOT_FOUND:
return false;
default:
DEBUG_ERROR_HR(status, "RegGetValue(ForceFullDirectCopy)");
return {};
}
}
LSTATUS CRegistrySettings::setForceFullDirectCopy(bool forceFullCopy)
{
DWORD dwValue = forceFullCopy;
return RegSetValueEx(hKey, L"ForceFullDirectCopy", 0, REG_DWORD,
(LPBYTE)&dwValue, sizeof(DWORD));
}

View File

@@ -56,4 +56,7 @@ public:
std::optional<bool> getExclusiveMonitor(); std::optional<bool> getExclusiveMonitor();
LSTATUS setExclusiveMonitor(bool exclusive); LSTATUS setExclusiveMonitor(bool exclusive);
std::optional<bool> getForceFullDirectCopy();
LSTATUS setForceFullDirectCopy(bool forceFullCopy);
}; };