Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions src/platform/windows/display_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
// standard includes
#include <cmath>
#include <set>
#include <thread>

// platform includes
Expand Down Expand Up @@ -442,7 +443,7 @@
int display_base_t::init(const ::video::config_t &config, const std::string &display_name) {
std::once_flag windows_cpp_once_flag;

std::call_once(windows_cpp_once_flag, []() {

Check warning on line 446 in src/platform/windows/display_base.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This lambda has 49 lines, which is greater than the 20 lines authorized. Split it into several lambdas or functions, or make it a named function.

See more on https://sonarcloud.io/project/issues?id=LizardByte_Sunshine&issues=AZ1OwDiHUUzNE27-P7nr&open=AZ1OwDiHUUzNE27-P7nr&pullRequest=4938
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);

typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value);
Expand All @@ -458,10 +459,51 @@
}

{
// We aren't calling MH_Uninitialize(), but that's okay because this hook lasts for the life of the process
MH_Initialize();
MH_CreateHookApi(L"win32u.dll", "NtGdiDdDDIGetCachedHybridQueryValue", (void *) NtGdiDdDDIGetCachedHybridQueryValueHook, nullptr);
MH_EnableHook(MH_ALL_HOOKS);
// The hybrid GPU workaround hooks NtGdiDdDDIGetCachedHybridQueryValue to prevent
// DXGI output reparenting that breaks DDA on multi-GPU systems. On single-GPU
// systems, this hook is unnecessary and can cause crashes on some Windows builds
// (e.g., Windows 11 24H2 build 29558+) where dxgi.dll doesn't handle the spoofed
// GPU preference state correctly, resulting in an access violation.
bool needs_hybrid_workaround = false;
{
IDXGIFactory1 *probe_factory = nullptr;
if (SUCCEEDED(CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &probe_factory))) {
// Count unique physical GPUs by VendorId+DeviceId.
// DXGI can present the same physical GPU as multiple logical adapters
// (e.g., cross-adapter copies), so we deduplicate.
std::set<std::pair<UINT, UINT>> unique_gpus;
IDXGIAdapter1 *probe_adapter = nullptr;
for (int i = 0; probe_factory->EnumAdapters1(i, &probe_adapter) != DXGI_ERROR_NOT_FOUND; ++i) {
DXGI_ADAPTER_DESC1 desc;
probe_adapter->GetDesc1(&desc);
if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) {
unique_gpus.emplace(desc.VendorId, desc.DeviceId);
}
probe_adapter->Release();
}
probe_factory->Release();
needs_hybrid_workaround = unique_gpus.size() > 1;
if (!needs_hybrid_workaround) {
BOOST_LOG(info) << "Single GPU detected, skipping hybrid GPU output reparenting workaround"sv;
}
}
}

if (needs_hybrid_workaround) {
// We aren't calling MH_Uninitialize(), but that's okay because this hook lasts for the life of the process
auto mh_status = MH_Initialize();
if (mh_status == MH_OK || mh_status == MH_ERROR_ALREADY_INITIALIZED) {
mh_status = MH_CreateHookApi(L"win32u.dll", "NtGdiDdDDIGetCachedHybridQueryValue", (void *) NtGdiDdDDIGetCachedHybridQueryValueHook, nullptr);
if (mh_status == MH_OK) {
MH_EnableHook(MH_ALL_HOOKS);
BOOST_LOG(info) << "Installed hybrid GPU output reparenting workaround"sv;
} else {
BOOST_LOG(warning) << "Failed to hook NtGdiDdDDIGetCachedHybridQueryValue (MH status: "sv << mh_status << "), skipping hybrid GPU workaround"sv;
}
} else {
BOOST_LOG(warning) << "Failed to initialize MinHook (MH status: "sv << mh_status << "), skipping hybrid GPU workaround"sv;
}
}
}
});

Expand Down