mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 12:47:58 -10:00
fix(windows): pick d3d12 only for a hardware Direct3D 12 adapter, llvmpipe otherwise
Left to itself, Mesa's d3d12 driver takes the Microsoft Basic Render Driver (WARP) when that is the only D3D12 adapter: on the hosted ARM64 runner the retry came up as "D3D12 (Microsoft Basic Render Driver)" and the app exited with status 87 before its first screenshot, where the same runner renders fine on llvmpipe. WARP is software D3D12 under a GL translation, so it is the slower software path anyway. The launcher now enumerates DXGI adapters (dxgi.dll and d3d12.dll loaded by hand, no link dependency), skips DXGI_ADAPTER_FLAG_SOFTWARE ones and asks D3D12CreateDevice at feature level 11_0. First hit: GALLIUM_DRIVER= d3d12 plus MESA_D3D12_DEFAULT_ADAPTER_NAME pinned to that adapter. No hit: GALLIUM_DRIVER=llvmpipe and LIBGL_ALWAYS_SOFTWARE=true as #1138 had. A GALLIUM_DRIVER already in the environment wins. Both outcomes are logged on stderr and the GL test workflow asserts one of the two lines. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
c69181a112
commit
4e39bcdd20
4 files changed
+76
-19
No files matched your search
@@ -69,10 +69,12 @@ jobs:
|
||||
>diagnostics/automatic-stdout.txt 2>diagnostics/automatic-stderr.txt
|
||||
test -s diagnostics/automatic-fallback.png
|
||||
grep -q 'automatically retrying with bundled Mesa OpenGL' diagnostics/automatic-stderr.txt
|
||||
# Mesa picks d3d12 when the runner exposes a D3D12 adapter and llvmpipe
|
||||
# when it does not (hosted runners have no GPU); either is the bundled
|
||||
# Mesa and not the GDI Generic 1.1 driver the native attempt reported.
|
||||
# The launcher picks d3d12 only for a hardware D3D12 adapter and llvmpipe
|
||||
# otherwise; the hosted runner has only the Basic Render Driver (WARP), so
|
||||
# it shows llvmpipe. Either is the bundled Mesa and not the GDI Generic
|
||||
# 1.1 driver the native attempt reported.
|
||||
grep -E 'renderer=(llvmpipe|D3D12)' diagnostics/automatic-stderr.txt
|
||||
grep -E '\[gl\] (bundled Mesa will render through Direct3D 12|no hardware Direct3D 12 adapter)' diagnostics/automatic-stderr.txt
|
||||
test -f 'automatic fallback data/config.ini'
|
||||
test ! -e 'automatic fallback data/saves/instance-2'
|
||||
export PATH="$PWD/stage/bin/software-opengl:$PATH"
|
||||
|
||||
@@ -432,9 +432,12 @@ graphics startup fails, which on a stock Windows 10/11 install without a vendor
|
||||
OpenGL driver (Windows on ARM, VMs, Remote Desktop, the basic display adapter)
|
||||
means WGL handed us the GDI Generic OpenGL 1.1 driver. The retry preserves game
|
||||
arguments, working directory and the original config/save root; users do not
|
||||
need a separate launcher. Mesa chooses its own driver: `d3d12` (OpenGL on the
|
||||
machine's Direct3D 12 driver, hardware rendering, the same layer as Microsoft's
|
||||
OpenGL Compatibility Pack) when a D3D12 adapter exists, `llvmpipe` (CPU
|
||||
rendering, slower) otherwise. The `[gl] renderer=` log line names which one
|
||||
need a separate launcher. The launcher picks Mesa's driver: `d3d12` (OpenGL on
|
||||
the machine's Direct3D 12 driver, hardware rendering, the same layer as
|
||||
Microsoft's OpenGL Compatibility Pack) when a hardware D3D12 adapter exists,
|
||||
pinned to that adapter; `llvmpipe` (CPU rendering, slower) otherwise, including
|
||||
when the only D3D12 adapter is the Microsoft Basic Render Driver (WARP), which
|
||||
is software too and slower than llvmpipe. A `GALLIUM_DRIVER` already in the
|
||||
environment wins. The `[gl]` log lines name the choice and the renderer that
|
||||
came up. Keep the dependency notices and matching architecture alongside the
|
||||
bundled executable.
|
||||
@@ -85,7 +85,8 @@ Windows packages include a `software-opengl/` app copy and Mesa dependency notic
|
||||
Startup tries the normal driver first. If window/context/renderer initialization
|
||||
fails, it releases the first instance and retries once with the bundled Mesa,
|
||||
preserving command-line arguments and the original data root. Mesa renders on
|
||||
the Direct3D 12 driver when the machine has one (hardware, `d3d12`) and on the
|
||||
CPU otherwise (`llvmpipe`). Only the child's environment selects Mesa; system
|
||||
drivers and settings are unchanged.
|
||||
the machine's hardware Direct3D 12 adapter when there is one (`d3d12`) and on
|
||||
the CPU otherwise (`llvmpipe`; the Basic Render Driver does not count as
|
||||
hardware). Only the child's environment selects Mesa; system drivers and
|
||||
settings are unchanged.
|
||||
Qualify both the normal and fallback launch paths for each Store architecture.
|
||||
@@ -5,6 +5,8 @@
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <d3d12.h>
|
||||
#include "software_gl.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
@@ -13,6 +15,42 @@
|
||||
|
||||
namespace vj {
|
||||
namespace {
|
||||
// The description of the first hardware adapter that can create a Direct3D 12
|
||||
// device at feature level 11_0, or empty when the machine has none. The
|
||||
// "Microsoft Basic Render Driver" (WARP) is a software adapter and is skipped:
|
||||
// GL on WARP is slower than llvmpipe and the app did not survive it on the
|
||||
// hosted ARM64 runner. dxgi.dll / d3d12.dll are loaded by hand so the
|
||||
// executable keeps running on a machine without either (no link dependency).
|
||||
std::wstring hardwareD3D12Adapter() {
|
||||
std::wstring found;
|
||||
HMODULE dxgi = LoadLibraryW(L"dxgi.dll");
|
||||
HMODULE d3d12 = LoadLibraryW(L"d3d12.dll");
|
||||
if (!dxgi || !d3d12) {
|
||||
if (dxgi) FreeLibrary(dxgi);
|
||||
if (d3d12) FreeLibrary(d3d12);
|
||||
return found;
|
||||
}
|
||||
using CreateFactory1 = HRESULT(WINAPI*)(REFIID, void**);
|
||||
using CreateDevice = HRESULT(WINAPI*)(IUnknown*, D3D_FEATURE_LEVEL, REFIID, void**);
|
||||
auto createFactory = reinterpret_cast<CreateFactory1>(GetProcAddress(dxgi, "CreateDXGIFactory1"));
|
||||
auto createDevice = reinterpret_cast<CreateDevice>(GetProcAddress(d3d12, "D3D12CreateDevice"));
|
||||
IDXGIFactory1* factory = nullptr;
|
||||
if (createFactory && createDevice && SUCCEEDED(createFactory(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory)))) {
|
||||
IDXGIAdapter1* adapter = nullptr;
|
||||
for (UINT i = 0; found.empty() && factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; ++i) {
|
||||
DXGI_ADAPTER_DESC1 desc = {};
|
||||
if (SUCCEEDED(adapter->GetDesc1(&desc)) && !(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) &&
|
||||
SUCCEEDED(createDevice(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr)))
|
||||
found = desc.Description;
|
||||
adapter->Release();
|
||||
adapter = nullptr;
|
||||
}
|
||||
factory->Release();
|
||||
}
|
||||
FreeLibrary(d3d12);
|
||||
FreeLibrary(dxgi);
|
||||
return found;
|
||||
}
|
||||
std::wstring wide(const std::string& text) {
|
||||
if (text.empty()) return {};
|
||||
int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.data(), (int)text.size(), nullptr, 0);
|
||||
@@ -57,15 +95,28 @@ bool retrySoftwareOpenGL(int argc, char** argv, const std::string& dataDir, int&
|
||||
FreeEnvironmentStringsW(block);
|
||||
entries.push_back(L"SDL_OPENGL_LIBRARY=" + dll);
|
||||
entries.push_back(L"VJ_SOFTWARE_OPENGL_RETRY=1");
|
||||
// Which Gallium driver Mesa uses is deliberately left to Mesa. The bundled
|
||||
// build carries d3d12 (OpenGL on the Direct3D 12 driver every stock Windows
|
||||
// 10/11 install has, the same layer as Microsoft's OpenGL Compatibility
|
||||
// Pack) and llvmpipe (pure software). Mesa's WGL frontend takes d3d12 when
|
||||
// a D3D12 adapter exists and llvmpipe otherwise, so a machine without a
|
||||
// vendor OpenGL driver still gets hardware rendering; forcing llvmpipe here
|
||||
// made every such machine rasterize the UI and the shader presets on the
|
||||
// CPU. GALLIUM_DRIVER and LIBGL_ALWAYS_SOFTWARE pass through untouched so a
|
||||
// user (or the GL test workflow) can still pin a driver.
|
||||
// Which Gallium driver the bundled Mesa uses. It carries d3d12 (OpenGL on
|
||||
// the Direct3D 12 driver every stock Windows 10/11 install has, the same
|
||||
// layer as Microsoft's OpenGL Compatibility Pack) and llvmpipe (pure
|
||||
// software). With a hardware D3D12 adapter present, d3d12 renders on the
|
||||
// GPU, pinned to that adapter so Mesa cannot drift onto WARP; without one,
|
||||
// llvmpipe: Mesa left alone takes the Microsoft Basic Render Driver, which
|
||||
// is software D3D12 under a GL translation, slower than llvmpipe and not
|
||||
// something the app survived on the hosted ARM64 runner. A GALLIUM_DRIVER
|
||||
// the user already set wins over this probe.
|
||||
if (!GetEnvironmentVariableW(L"GALLIUM_DRIVER", nullptr, 0)) {
|
||||
std::wstring adapter = hardwareD3D12Adapter();
|
||||
if (!adapter.empty()) {
|
||||
entries.push_back(L"GALLIUM_DRIVER=d3d12");
|
||||
if (!GetEnvironmentVariableW(L"MESA_D3D12_DEFAULT_ADAPTER_NAME", nullptr, 0))
|
||||
entries.push_back(L"MESA_D3D12_DEFAULT_ADAPTER_NAME=" + adapter);
|
||||
std::fprintf(stderr, "[gl] bundled Mesa will render through Direct3D 12 on %ls\n", adapter.c_str());
|
||||
} else {
|
||||
entries.push_back(L"GALLIUM_DRIVER=llvmpipe");
|
||||
entries.push_back(L"LIBGL_ALWAYS_SOFTWARE=true");
|
||||
std::fprintf(stderr, "[gl] no hardware Direct3D 12 adapter; bundled Mesa will render in software (llvmpipe)\n");
|
||||
}
|
||||
}
|
||||
std::sort(entries.begin(), entries.end(), [](const auto& a, const auto& b) { return _wcsicmp(a.c_str(), b.c_str()) < 0; });
|
||||
std::vector<wchar_t> environment;
|
||||
for (const auto& entry : entries) environment.insert(environment.end(), entry.c_str(), entry.c_str() + entry.size() + 1);
|
||||
|
||||
Reference in new issue
Block a user