[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
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.
Software processing

View File

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

View File

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

View File

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

View File

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

View File

@@ -54,6 +54,7 @@ CConfigWindow::CConfigWindow() : m_scale(1)
m_defaultRefresh = m_settings.getDefaultRefresh();
m_noGPU = m_settings.getNoGPU();
m_exclusive = m_settings.getExclusiveMonitor();
m_forceFullDirectCopy = m_settings.getForceFullDirectCopy();
}
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_defRefreshLabel, *m_defRefresh, *m_defRefreshHz, *m_modeSave, *m_modeRevert,
*m_prefGroup, *m_prefNoGPU, *m_prefExclusive,
*m_prefForceFullDirectCopy,
}))
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_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_prefForceFullDirectCopy.reset(new CCheckbox(
L"Force full direct frame copies", 0, m_hwnd));
if (m_noGPU)
m_prefNoGPU->setChecked(*m_noGPU);
@@ -194,6 +198,9 @@ LRESULT CConfigWindow::onCreate()
if (m_exclusive)
m_prefExclusive->setChecked(*m_exclusive);
if (m_forceFullDirectCopy)
m_prefForceFullDirectCopy->setChecked(*m_forceFullDirectCopy);
LONG width, height;
getMinimumSize(width, height);
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_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_prefExclusive, 236, 84, 176, 20);
pos.pinTopLeft(*m_prefForceFullDirectCopy, 236, 104, 188, 20);
return 0;
}
@@ -344,6 +352,22 @@ LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
m_settings.setExclusiveMonitor(*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)
{
bool updated = false;

View File

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

View File

@@ -265,3 +265,28 @@ LSTATUS CRegistrySettings::setExclusiveMonitor(bool exclusive)
DWORD dwValue = exclusive;
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();
LSTATUS setExclusiveMonitor(bool exclusive);
std::optional<bool> getForceFullDirectCopy();
LSTATUS setForceFullDirectCopy(bool forceFullCopy);
};