zed/crates/gpui_windows/src/directx_devices.rs
kevin-mai 3f1e4afd77 gpui_windows: add win-legacy-compat feature flag for Windows Server 2016 / pre-1809 back-compat
Introduces a `win-legacy-compat` Cargo feature (forwarded from `gpui_platform`) that
gates all Windows Server 2016 / pre-Windows 10 1809 workarounds behind a compile-time
flag, preserving exact upstream behaviour and performance when the flag is absent.

Changes when `win-legacy-compat` is enabled:
- directx_devices.rs: use IDXGIFactory2 (DXGI 1.2, Win8+) instead of IDXGIFactory6
- direct_write.rs: use IDWriteFactory4 + IDWriteFontSetBuilder (Server 2016) instead of
  Factory5 + in-memory font loader; fall back to temp files for custom font loading
- directx_renderer.rs: runtime is_pre_1809 check gates SRV FirstElement workaround;
  draw_* functions accept scene: &Scene for per-batch buffer re-upload on pre-1809;
  standard alpha blend replaces dual-source SRC1_COLOR for subpixel pipeline;
  MSAA quality 0 replaces D3D11_STANDARD_MULTISAMPLE_PATTERN (D3D11.1 sentinel);
  SM_REMOTESESSION check disables DirectComposition on RDP sessions
- shaders.hlsl: #ifdef WIN_LEGACY_COMPAT collapses per-channel ClearType mask to
  scalar alpha for standard SRC_ALPHA/INV_SRC_ALPHA blending
- build.rs: pass /D WIN_LEGACY_COMPAT to fxc (release); pass D3D_SHADER_MACRO define
  to D3DCompileFromFile (debug); fix /D argument order (must precede input filename);
  add rerun-if-env-changed for CARGO_FEATURE_WIN_LEGACY_COMPAT
- gpui_platform/Cargo.toml: forward win-legacy-compat → gpui_windows/win-legacy-compat
2026-05-27 21:10:07 +08:00

203 lines
6.3 KiB
Rust

use anyhow::{Context, Result};
use itertools::Itertools;
use util::ResultExt;
use windows::Win32::{
Foundation::HMODULE,
Graphics::{
Direct3D::{
D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
},
Direct3D11::{
D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG,
D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS,
D3D11_SDK_VERSION, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
},
Dxgi::{
CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS,
IDXGIAdapter1,
},
},
};
use windows::core::Interface;
#[cfg(not(feature = "win-legacy-compat"))]
use windows::Win32::Graphics::Dxgi::IDXGIFactory6;
#[cfg(feature = "win-legacy-compat")]
use windows::Win32::Graphics::Dxgi::IDXGIFactory2;
#[cfg(not(feature = "win-legacy-compat"))]
pub(crate) type DxgiFactory = IDXGIFactory6;
#[cfg(feature = "win-legacy-compat")]
pub(crate) type DxgiFactory = IDXGIFactory2;
pub(crate) fn try_to_recover_from_device_lost<T>(mut f: impl FnMut() -> Result<T>) -> Result<T> {
(0..5)
.map(|i| {
if i > 0 {
// Add a small delay before retrying
std::thread::sleep(std::time::Duration::from_millis(100 + i * 10));
}
f()
})
.find_or_last(Result::is_ok)
.unwrap()
.context("DirectXRenderer failed to recover from lost device after multiple attempts")
}
#[derive(Clone)]
pub(crate) struct DirectXDevices {
pub(crate) adapter: IDXGIAdapter1,
pub(crate) dxgi_factory: DxgiFactory,
pub(crate) device: ID3D11Device,
pub(crate) device_context: ID3D11DeviceContext,
}
impl DirectXDevices {
pub(crate) fn new() -> Result<Self> {
let debug_layer_available = check_debug_layer_available();
let dxgi_factory =
get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
let (adapter, device, device_context, feature_level) =
get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
match feature_level {
D3D_FEATURE_LEVEL_11_1 => {
log::info!("Created device with Direct3D 11.1 feature level.")
}
D3D_FEATURE_LEVEL_11_0 => {
log::info!("Created device with Direct3D 11.0 feature level.")
}
D3D_FEATURE_LEVEL_10_1 => {
log::info!("Created device with Direct3D 10.1 feature level.")
}
_ => unreachable!(),
}
Ok(Self {
adapter,
dxgi_factory,
device,
device_context,
})
}
}
#[inline]
fn check_debug_layer_available() -> bool {
#[cfg(debug_assertions)]
{
use windows::Win32::Graphics::Dxgi::{DXGIGetDebugInterface1, IDXGIInfoQueue};
unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
.log_err()
.is_some()
}
#[cfg(not(debug_assertions))]
{
false
}
}
#[inline]
fn get_dxgi_factory(debug_layer_available: bool) -> Result<DxgiFactory> {
let factory_flag = if debug_layer_available {
DXGI_CREATE_FACTORY_DEBUG
} else {
#[cfg(debug_assertions)]
log::warn!(
"Failed to get DXGI debug interface. DirectX debugging features will be disabled."
);
DXGI_CREATE_FACTORY_FLAGS::default()
};
unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
}
#[inline]
fn get_adapter(
dxgi_factory: &DxgiFactory,
debug_layer_available: bool,
) -> Result<(
IDXGIAdapter1,
ID3D11Device,
ID3D11DeviceContext,
D3D_FEATURE_LEVEL,
)> {
for adapter_index in 0.. {
let adapter: IDXGIAdapter1 = unsafe { dxgi_factory.EnumAdapters(adapter_index)?.cast()? };
if let Ok(desc) = unsafe { adapter.GetDesc1() } {
let gpu_name = String::from_utf16_lossy(&desc.Description)
.trim_matches(char::from(0))
.to_string();
log::info!("Using GPU: {}", gpu_name);
}
// Check to see whether the adapter supports Direct3D 11 and create
// the device if it does.
let mut context: Option<ID3D11DeviceContext> = None;
let mut feature_level = D3D_FEATURE_LEVEL::default();
if let Some(device) = get_device(
&adapter,
Some(&mut context),
Some(&mut feature_level),
debug_layer_available,
)
.log_err()
{
return Ok((adapter, device, context.unwrap(), feature_level));
}
}
unreachable!()
}
#[inline]
fn get_device(
adapter: &IDXGIAdapter1,
context: Option<*mut Option<ID3D11DeviceContext>>,
feature_level: Option<*mut D3D_FEATURE_LEVEL>,
debug_layer_available: bool,
) -> Result<ID3D11Device> {
let mut device: Option<ID3D11Device> = None;
let device_flags = if debug_layer_available {
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
} else {
D3D11_CREATE_DEVICE_BGRA_SUPPORT
};
unsafe {
D3D11CreateDevice(
adapter,
D3D_DRIVER_TYPE_UNKNOWN,
HMODULE::default(),
device_flags,
// 4x MSAA is required for Direct3D Feature Level 10.1 or better
Some(&[
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
]),
D3D11_SDK_VERSION,
Some(&mut device),
feature_level,
context,
)?;
}
let device = device.unwrap();
let mut data = D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS::default();
unsafe {
device
.CheckFeatureSupport(
D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS,
&mut data as *mut _ as _,
std::mem::size_of::<D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS>() as u32,
)
.context("Checking GPU device feature support")?;
}
if data
.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
.as_bool()
{
Ok(device)
} else {
Err(anyhow::anyhow!(
"Required feature StructuredBuffer is not supported by GPU/driver"
))
}
}