overlay: remove dependency on d3dcompiler (#738)

## Link to GitHub Issue or related Pull Request, if one exists
Fixes #737, regressed by #707

## Description of change
ImGui backend for DX11 pulled in dependency on a specific version of
`d3dcompiler_47.dll` which is not present on stock Win7 OS image.

Change this to a runtime load of the DLL and disable the overlay if we
can't find one.

## Testing
- [x] Win11, DX11 overlay
- [x] Win11, DX9 overlay
- [x] Win7, DX9 overlay
This commit is contained in:
bicarus
2026-06-06 14:06:37 -07:00
committed by GitHub
parent 0457262472
commit 2e2968d1bc
4 changed files with 70 additions and 10 deletions
+3 -3
View File
@@ -236,7 +236,7 @@ add_compile_definitions(
) )
# SPICE_XP: set by build_all.sh for the WinXP-compat toolchains. Gates out # SPICE_XP: set by build_all.sh for the WinXP-compat toolchains. Gates out
# the DX11 overlay backend (WinXP lacks D3DCOMPILER_47.dll). # the DX11 overlay backend
option(SPICE_XP "Build for the WinXP-compat toolchain" OFF) option(SPICE_XP "Build for the WinXP-compat toolchain" OFF)
if(SPICE_XP) if(SPICE_XP)
add_compile_definitions(SPICE_XP=1) add_compile_definitions(SPICE_XP=1)
@@ -734,7 +734,7 @@ set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64") set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1) target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
if(NOT SPICE_XP) if(NOT SPICE_XP)
target_link_libraries(spicetools_spice64 PUBLIC d3d11 dxgi d3dcompiler) target_link_libraries(spicetools_spice64 PUBLIC d3d11 dxgi)
target_compile_definitions(spicetools_spice64 PRIVATE SPICE_D3D11=1) target_compile_definitions(spicetools_spice64 PRIVATE SPICE_D3D11=1)
endif() endif()
@@ -757,7 +757,7 @@ set_target_properties(spicetools_spice64_linux PROPERTIES OUTPUT_NAME "spice64_l
target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE64=1) target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE64=1)
target_compile_definitions(spicetools_spice64_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1) target_compile_definitions(spicetools_spice64_linux PRIVATE NO_SCARD=1 PRIVATE SPICE_LINUX=1)
# spice64_linux is never built under the WinXP toolchain, so dx11 is always on. # spice64_linux is never built under the WinXP toolchain, so dx11 is always on.
target_link_libraries(spicetools_spice64_linux PUBLIC d3d11 dxgi d3dcompiler) target_link_libraries(spicetools_spice64_linux PUBLIC d3d11 dxgi)
target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE_D3D11=1) target_compile_definitions(spicetools_spice64_linux PRIVATE SPICE_D3D11=1)
IF(NOT MSVC) IF(NOT MSVC)
+1 -2
View File
@@ -21,8 +21,7 @@ set(IMGUI_SOURCES
misc/cpp/imgui_stdlib.cpp misc/cpp/imgui_stdlib.cpp
) )
# spice2x: DX11 backend is only built for non-XP toolchains. WinXP lacks # spice2x: DX11 backend is only built for non-XP toolchains
# D3DCOMPILER_47.dll, and including this TU would pull in D3DCompile imports.
if(NOT SPICE_XP) if(NOT SPICE_XP)
list(APPEND IMGUI_HEADERS backends/imgui_impl_dx11.h) list(APPEND IMGUI_HEADERS backends/imgui_impl_dx11.h)
list(APPEND IMGUI_SOURCES backends/imgui_impl_dx11.cpp) list(APPEND IMGUI_SOURCES backends/imgui_impl_dx11.cpp)
+37 -5
View File
@@ -50,9 +50,35 @@
#include <stdio.h> #include <stdio.h>
#include <d3d11.h> #include <d3d11.h>
#include <d3dcompiler.h> #include <d3dcompiler.h>
#ifdef _MSC_VER // #ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. // #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif // #endif
// spice2x: resolve D3DCompile at runtime rather than static-linking d3dcompiler,
// which would hardwire an import on d3dcompiler_47.dll (absent on stock Win7).
static pD3DCompile ImGui_ImplDX11_GetD3DCompile()
{
static pD3DCompile fn = []() -> pD3DCompile {
static const char *dlls[] = {
"d3dcompiler_47.dll",
"d3dcompiler_46.dll",
"d3dcompiler_43.dll" };
for (const char *dll : dlls) {
HMODULE mod = GetModuleHandleA(dll);
if (!mod) {
mod = LoadLibraryA(dll);
}
if (mod) {
if (auto p = (pD3DCompile) GetProcAddress(mod, "D3DCompile")) {
return p;
}
}
}
return nullptr;
}();
return fn;
}
// Clang/GCC warnings with -Weverything // Clang/GCC warnings with -Weverything
#if defined(__clang__) #if defined(__clang__)
@@ -442,6 +468,12 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details. // See https://github.com/ocornut/imgui/pull/638 for sources and details.
// spice2x: dynamically resolved; see ImGui_ImplDX11_GetD3DCompile.
pD3DCompile D3DCompile_fn = ImGui_ImplDX11_GetD3DCompile();
if (!D3DCompile_fn) {
return false;
}
// Create the vertex shader // Create the vertex shader
{ {
static const char* vertexShader = static const char* vertexShader =
@@ -473,7 +505,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
}"; }";
ID3DBlob* vertexShaderBlob; ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vertexShaderBlob, nullptr))) if (FAILED(D3DCompile_fn(vertexShader, strlen(vertexShader), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vertexShaderBlob, nullptr)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), nullptr, &bd->pVertexShader) != S_OK) if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), nullptr, &bd->pVertexShader) != S_OK)
{ {
@@ -530,7 +562,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
}"; }";
ID3DBlob* pixelShaderBlob; ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &pixelShaderBlob, nullptr))) if (FAILED(D3DCompile_fn(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &pixelShaderBlob, nullptr)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), nullptr, &bd->pPixelShader) != S_OK) if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), nullptr, &bd->pPixelShader) != S_OK)
{ {
@@ -213,6 +213,27 @@ void poll_thread() {
} }
} }
// the overlay's imgui dx11 backend needs D3DCompile (d3dcompiler_XX.dll) to
// build its shaders. _43 ships with the DX June 2010 redist on stock Win7;
// _46/_47 come with newer Windows.
bool d3dcompiler_available() {
static const wchar_t *names[] = {
L"d3dcompiler_47.dll",
L"d3dcompiler_46.dll",
L"d3dcompiler_43.dll",
};
for (auto name : names) {
HMODULE mod = GetModuleHandleW(name);
if (!mod) {
mod = LoadLibraryW(name);
}
if (mod && GetProcAddress(mod, "D3DCompile")) {
return true;
}
}
return false;
}
} // namespace } // namespace
void graphics_d3d11_init() { void graphics_d3d11_init() {
@@ -223,6 +244,14 @@ void graphics_d3d11_init() {
return; return;
} }
// no d3dcompiler -> overlay can't build shaders; skip dx11 overlay
if (!d3dcompiler_available()) {
log_warning(
"graphics::d3d11",
"d3dcompiler not found; dx11 overlay disabled");
return;
}
std::lock_guard<std::mutex> lock(g_init_mutex); std::lock_guard<std::mutex> lock(g_init_mutex);
if (g_poll_thread.joinable()) { if (g_poll_thread.joinable()) {
return; // already initialized return; // already initialized