Skip to content
Draft
Show file tree
Hide file tree
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
86 changes: 55 additions & 31 deletions crates/warpui/src/rendering/wgpu/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ lazy_static! {
static ref MIN_SUPPORTED_LAVAPIPE_VERSION: Version<'static> = Version::from("24.0.2")
.expect("should not fail to parse version");

/// The minimum supported driver version for Vulkan-backed Intel UHD integrated graphics.
/// The minimum supported Mesa driver version for Vulkan-backed Intel integrated graphics.
///
/// Some issues we've seen: PLAT-744 and PLAT-599.
/// Mesa changelog mentions a fix for flickering on Intel UHD:
/// https://docs.mesa3d.org/relnotes/21.3.6.html#:~:text=Flickering%20Intel%20Uhd%20620%20Graphics
static ref MIN_SUPPORTED_INTEL_UHD_VERSION: Version<'static> = Version::from("21.3.6")
static ref MIN_SUPPORTED_INTEL_MESA_VERSION: Version<'static> = Version::from("21.3.6")
.expect("should not fail to parse version");

/// Nvidia drivers version 535 have problems with Wayland window managers, e.g. PLAT-667 and
Expand Down Expand Up @@ -469,37 +469,47 @@ fn is_gl_to_metal_adapter_on_windows_in_parallels(adapter_info: &wgpu::AdapterIn
&& adapter_info.name.to_lowercase().starts_with("parallels")
}

/// Returns whether or not the provided adapter is an unsupported Intel UHD Mesa driver version for
/// warpui to render properly. Affected adapters include:
/// - `Intel(R) HD Graphics 620` (KBL GT2) — flickering (PLAT-744)
/// - `Intel(R) UHD Graphics (ICL GT1)` — stuck at "Starting zsh..." on Mesa 21.2.6 (GH #14325)
/// - `Intel(R) UHD Graphics (TGL GT1)` — window flashing/flicker on older Mesa (PLAT-599, GH #4533)
/// Returns whether the provided adapter uses the Mesa open-source Intel driver (ANV for Vulkan,
/// Iris/i965 for GL).
fn is_intel_mesa_adapter(adapter_info: &wgpu::AdapterInfo) -> bool {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comment is inaccurate for GL: this helper never matches a Mesa GL adapter.

In wgpu 30 the GL backend builds AdapterInfo in wgpu-hal/src/gles/adapter.rs::make_info, which sets only name, vendor and driver_infodriver is left as the default empty string (that's why the reporter's log shows Driver: Unknown for the GL adapter). Since is_intel_mesa_adapter requires driver.contains("Mesa"), it returns false for Mesa Intel(R) Xe Graphics (TGL GT2) on GL.

That's harmless today because the only caller already gates on Backend::Vulkan, but the comment invites a future caller to reuse this for GL and silently get false. Suggest either narrowing the comment to the Vulkan/ANV case or renaming to something like is_intel_mesa_vulkan_adapter.

// The Mesa Intel Vulkan driver reports itself as "Intel open-source Mesa driver", so requiring
// "Mesa" in the driver name keeps us from misinterpreting non-Mesa (e.g. Windows) Intel driver
// version strings as Mesa versions.
adapter_info.driver.contains("Mesa")
&& (adapter_info.driver.contains("Intel") || adapter_info.name.contains("Intel"))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional robustness suggestion: AdapterInfo::vendor is populated with the PCI vendor ID by both the Vulkan and GL backends (db::intel::VENDOR = 0x8086), so the Intel half of this test could be adapter_info.vendor == 0x8086 instead of substring matching on driver/name. That's immune to unusual/absent driverName strings and to any adapter that merely happens to have "Intel" in its name (e.g. a zink-style adapter reporting Mesa ... as its driver and an Intel renderer string currently matches here).

Not a blocker — the current strings do match the reported hardware, and switching would require setting vendor in the test helper (it's hardcoded to 0).

}

/// Returns whether or not the provided adapter is an Intel integrated GPU running a Mesa Vulkan
/// driver that is too old for warpui to render properly.
///
/// This is keyed off the Mesa version rather than an allowlist of adapter names: every Intel
/// integrated GPU we've seen fail this way (`Intel(R) HD Graphics 620` (KBL GT2), `Intel(R) UHD
/// Graphics (ICL GT1)`, `Intel(R) UHD Graphics (TGL GT1)`, `Intel(R) Xe Graphics (TGL GT2)`) has
/// been on Mesa older than [`MIN_SUPPORTED_INTEL_MESA_VERSION`], and name-based matching kept
/// missing new Intel graphics families and GT tiers on the exact same broken drivers.
///
/// Symptoms range from flickering (PLAT-744, PLAT-599, GH #4533) to a permanently frozen window
/// caused by every `get_current_texture` call returning a validation error (GH #14325, GH #14577).
///
/// Note that this only deprioritizes the adapter: if no other adapter can present to the surface,
/// we still fall back to using it rather than failing to open a window.
///
/// See the Mesa 21.3.6 changelog for the upstream fix:
/// <https://docs.mesa3d.org/relnotes/21.3.6.html#:~:text=Flickering%20Intel%20Uhd%20620%20Graphics>
fn is_older_vulkan_intel_uhd_adapter(adapter_info: &wgpu::AdapterInfo) -> bool {
fn is_older_vulkan_intel_mesa_adapter(adapter_info: &wgpu::AdapterInfo) -> bool {
if adapter_info.backend != wgpu::Backend::Vulkan
|| adapter_info.device_type != wgpu::DeviceType::IntegratedGpu
{
return false;
}

let affected_names = [
"Intel(R) HD Graphics 620",
"Intel(R) UHD Graphics (ICL GT1)",
"Intel(R) UHD Graphics (TGL GT1)",
];

if !affected_names
.iter()
.any(|name| adapter_info.name.contains(name))
{
if !is_intel_mesa_adapter(adapter_info) {
return false;
}

mesa_driver_version_is_below_minimum(
&adapter_info.driver_info,
&MIN_SUPPORTED_INTEL_UHD_VERSION,
&MIN_SUPPORTED_INTEL_MESA_VERSION,
)
}

Expand Down Expand Up @@ -753,70 +763,84 @@ fn adapter_stability_sort_func(
windowing_system: Option<windowing::System>,
downrank_non_nvidia_vulkan_adapters: bool,
) -> AdapterSupport {
let adapter_info = adapter.get_info();
adapter_support(
&adapter.get_info(),
windowing_system,
downrank_non_nvidia_vulkan_adapters,
)
}

/// Computes the [`AdapterSupport`] level for the given adapter info.
///
/// This is split out from [`adapter_stability_sort_func`] so that it can be unit tested without a
/// real GPU adapter.
fn adapter_support(
adapter_info: &wgpu::AdapterInfo,
windowing_system: Option<windowing::System>,
downrank_non_nvidia_vulkan_adapters: bool,
) -> AdapterSupport {
let window_server_is_wayland = matches!(
windowing_system,
Some(windowing::System::Wayland) | Some(windowing::System::X11 { is_x_wayland: true })
);

if downrank_non_nvidia_vulkan_adapters
&& adapter_info.backend == Backend::Vulkan
&& !is_vulkan_nvidia_adapter(&adapter_info)
&& !is_vulkan_nvidia_adapter(adapter_info)
{
log::info!(
"Deprioritizing non-NVIDIA Vulkan adapter (the PRIME performance profile is likely enabled)"
);
return AdapterSupport::Unsupported;
}

if is_v3d_vulkan_adapter(&adapter_info) {
if is_v3d_vulkan_adapter(adapter_info) {
log::warn!("Deprioritizing Vulkan-backed V3D adapter");
return AdapterSupport::Unsupported;
}

if is_intel_uhd_620_adapter_on_windows_with_vulkan_backend(&adapter_info) {
if is_intel_uhd_620_adapter_on_windows_with_vulkan_backend(adapter_info) {
log::warn!("Deprioritizing Vulkan-backed Intel UHD 620 adapter");
return AdapterSupport::SupportedWithIssues;
}

if is_intel_uhd_770_adapter_on_windows(&adapter_info) {
if is_intel_uhd_770_adapter_on_windows(adapter_info) {
log::warn!("Deprioritizing Intel UHD 770 integrated GPU on Windows");
return AdapterSupport::SupportedWithIssues;
}

if is_older_vulkan_intel_uhd_adapter(&adapter_info) {
if is_older_vulkan_intel_mesa_adapter(adapter_info) {
log::warn!(
"Deprioritizing Vulkan-backed Intel UHD adapter due to Mesa < {} (unsupported)",
*MIN_SUPPORTED_INTEL_UHD_VERSION
"Deprioritizing Vulkan-backed Intel adapter due to Mesa < {} (unsupported)",
*MIN_SUPPORTED_INTEL_MESA_VERSION
);
AdapterSupport::SupportedWithIssues
}
// Deprioritize older lavapipe adapters where we have evidence that they are less stable.
else if is_older_lavapipe_adapter(&adapter_info) {
else if is_older_lavapipe_adapter(adapter_info) {
log::warn!(
"Deprioritizing Vulkan-backed llvmpipe adapter due to Mesa < {} (unsupported)",
*MIN_SUPPORTED_LAVAPIPE_VERSION
);
AdapterSupport::Unsupported
// Same with Nvidia drivers, though this is only an issue with a Wayland window server.
} else if window_server_is_wayland && is_older_nvidia_adapter(&adapter_info) {
} else if window_server_is_wayland && is_older_nvidia_adapter(adapter_info) {
log::warn!(
"Deprioritizing Vulkan-backed Nvidia adapter due to version < {} (unsupported).\nSee \
the \"Graphics\" secion of our docs here: \
https://docs.warp.dev/help/known-issues#linux-1",
*MIN_SUPPORTED_NVIDIA_VERSION
);
AdapterSupport::Unsupported
} else if is_newer_nondx12_nvidia_adapter_on_windows(&adapter_info) {
} else if is_newer_nondx12_nvidia_adapter_on_windows(adapter_info) {
log::warn!(
"Deprioritizing non DX12 Nvidia adapter due to version > {} (unsupported). Newer NVIDIA \
drivers can crash if multiple windows are created if the `Vulkan / OpenGL Present Method\
NVIDIA setting is set to `Auto` or `Prefer layered on DXGI Swapchain`.",
*MAX_SUPPORTED_NVIDIA_VERSION_ON_WINDOWS
);
AdapterSupport::SupportedWithIssues
} else if is_gl_to_metal_adapter_on_windows_in_parallels(&adapter_info) {
} else if is_gl_to_metal_adapter_on_windows_in_parallels(adapter_info) {
log::warn!("Deprioritizing integrated OpenGL Windows Parallels adapter.");
AdapterSupport::SupportedWithIssues
} else {
Expand Down
169 changes: 163 additions & 6 deletions crates/warpui/src/rendering/wgpu/resources_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,68 @@ fn test_is_unsupported_llvmpipe_adapter() {
assert!(is_older_lavapipe_adapter(&unsupported_adapter_info));
}

/// Builds an [`wgpu::AdapterInfo`] with the fields our adapter selection logic looks at.
fn adapter_info(
name: &str,
driver: &str,
driver_info: &str,
backend: wgpu::Backend,
device_type: wgpu::DeviceType,
) -> wgpu::AdapterInfo {
wgpu::AdapterInfo {
name: name.to_owned(),
vendor: 0,
device: 0,
device_type,
driver: driver.to_owned(),
driver_info: driver_info.to_owned(),
backend,
device_pci_bus_id: "01:00.0".to_owned(),
subgroup_min_size: wgpu::MINIMUM_SUBGROUP_MIN_SIZE,
subgroup_max_size: wgpu::MAXIMUM_SUBGROUP_MAX_SIZE,
transient_saves_memory: Some(false),
limit_bucket: None,
}
}

/// The adapter reported in https://github.com/warpdotdev/warp/issues/14577: every frame fails with
/// a validation error when rendering through Vulkan on Mesa 21.2.6.
fn intel_xe_tgl_gt2_vulkan_adapter_info(mesa_version: &str) -> wgpu::AdapterInfo {
adapter_info(
"Intel(R) Xe Graphics (TGL GT2)",
"Intel open-source Mesa driver",
&format!("Mesa {mesa_version}"),
wgpu::Backend::Vulkan,
wgpu::DeviceType::IntegratedGpu,
)
}

/// The healthy GL adapter enumerated alongside the Vulkan one in the same report.
fn intel_xe_tgl_gt2_gl_adapter_info(mesa_version: &str) -> wgpu::AdapterInfo {
adapter_info(
"Mesa Intel(R) Xe Graphics (TGL GT2)",
"",
&format!("4.6 (Core Profile) Mesa {mesa_version}"),
wgpu::Backend::Gl,
wgpu::DeviceType::IntegratedGpu,
)
}

/// Ranks adapter infos the same way the final (and dominant) sorting step in [`sort_adapters`]
/// does, so we can assert on selection order without a real GPU.
fn rank_by_support(adapter_infos: Vec<wgpu::AdapterInfo>) -> Vec<wgpu::AdapterInfo> {
let windowing_system = Some(windowing::System::X11 {
is_x_wayland: false,
});
adapter_infos
.into_iter()
.sorted_by_key(|info| adapter_support(info, windowing_system, false))
.collect_vec()
}

#[test]
fn test_is_unsupported_intel_uhd_adapter() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this test no longer exercises anything UHD-specific — consider renaming to match the new rule (e.g. test_is_unsupported_intel_mesa_adapter), since the name is now the only remaining reference to the removed allowlist.

assert!(is_older_vulkan_intel_uhd_adapter(&wgpu::AdapterInfo {
assert!(is_older_vulkan_intel_mesa_adapter(&wgpu::AdapterInfo {
name: String::from("Intel(R) HD Graphics 620 (KBL GT2)"),
vendor: 0,
device: 0,
Expand All @@ -56,7 +115,7 @@ fn test_is_unsupported_intel_uhd_adapter() {
transient_saves_memory: Some(false),
limit_bucket: None,
}));
assert!(!is_older_vulkan_intel_uhd_adapter(&wgpu::AdapterInfo {
assert!(!is_older_vulkan_intel_mesa_adapter(&wgpu::AdapterInfo {
name: String::from("Intel(R) HD Graphics 620 (KBL GT2)"),
vendor: 0,
device: 0,
Expand All @@ -71,7 +130,7 @@ fn test_is_unsupported_intel_uhd_adapter() {
transient_saves_memory: Some(false),
limit_bucket: None,
}));
assert!(!is_older_vulkan_intel_uhd_adapter(&wgpu::AdapterInfo {
assert!(!is_older_vulkan_intel_mesa_adapter(&wgpu::AdapterInfo {
name: String::from("Intel(R) HD Graphics 620 (KBL GT2)"),
vendor: 0,
device: 0,
Expand All @@ -86,7 +145,7 @@ fn test_is_unsupported_intel_uhd_adapter() {
transient_saves_memory: Some(false),
limit_bucket: None,
}));
assert!(is_older_vulkan_intel_uhd_adapter(&wgpu::AdapterInfo {
assert!(is_older_vulkan_intel_mesa_adapter(&wgpu::AdapterInfo {
name: String::from("Intel(R) HD Graphics 620 (KBL GT2)"),
vendor: 0,
device: 0,
Expand All @@ -101,7 +160,7 @@ fn test_is_unsupported_intel_uhd_adapter() {
transient_saves_memory: Some(false),
limit_bucket: None,
}));
assert!(!is_older_vulkan_intel_uhd_adapter(&wgpu::AdapterInfo {
assert!(!is_older_vulkan_intel_mesa_adapter(&wgpu::AdapterInfo {
name: String::from("Intel(R) HD Graphics 620 (KBL GT2)"),
vendor: 0,
device: 0,
Expand All @@ -116,7 +175,7 @@ fn test_is_unsupported_intel_uhd_adapter() {
transient_saves_memory: Some(false),
limit_bucket: None,
}));
assert!(is_older_vulkan_intel_uhd_adapter(&wgpu::AdapterInfo {
assert!(is_older_vulkan_intel_mesa_adapter(&wgpu::AdapterInfo {
name: String::from("Intel(R) HD Graphics 620 (KBL GT2)"),
vendor: 0,
device: 0,
Expand All @@ -132,3 +191,101 @@ fn test_is_unsupported_intel_uhd_adapter() {
limit_bucket: None,
}));
}

/// The Intel graphics family/GT tier in the adapter name must not affect the result: matching is
/// based on the Mesa driver version. See https://github.com/warpdotdev/warp/issues/14577.
#[test]
fn test_older_vulkan_intel_mesa_adapters_are_matched_regardless_of_family() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage gap: nothing here pins the version boundary. The interesting cases for a rule that is now purely version-keyed are Mesa 21.3.5 (deprioritized) and Mesa 21.3.6 (not deprioritized, i.e. the comparison is strictly-less-than) — the existing cases only use 21.2.6 / 23.2.6 / 24.0.2, which would still pass if the threshold silently moved by a patch release.

I verified locally that the current code gets both right, plus distro-suffixed strings (Mesa 21.2.6-0ubuntu0.1~20.04.2 → true, Mesa 21.3.6-0ubuntu0.1~20.04.2 → false, which is the exact form Ubuntu 20.04 can report). Worth adding those four as explicit cases.

for name in [
"Intel(R) Xe Graphics (TGL GT2)",
"Intel(R) UHD Graphics (TGL GT1)",
"Intel(R) UHD Graphics (ICL GT1)",
"Intel(R) HD Graphics 620 (KBL GT2)",
"Intel(R) Graphics (ADL GT2)",
] {
let info = adapter_info(
name,
"Intel open-source Mesa driver",
"Mesa 21.2.6",
wgpu::Backend::Vulkan,
wgpu::DeviceType::IntegratedGpu,
);
assert!(
is_older_vulkan_intel_mesa_adapter(&info),
"expected {name} on Mesa 21.2.6 to be considered unsupported"
);
}
}

#[test]
fn test_non_mesa_and_non_intel_adapters_are_unaffected() {
// Windows Intel drivers aren't Mesa, so their version strings must not be parsed as Mesa
// versions.
assert!(!is_older_vulkan_intel_mesa_adapter(&adapter_info(
"Intel(R) Iris(R) Xe Graphics",
"Intel Corporation",
"Intel driver 31.0.101.5445",
wgpu::Backend::Vulkan,
wgpu::DeviceType::IntegratedGpu,
)));
// Non-Intel Mesa drivers (e.g. RADV) are out of scope for this check.
assert!(!is_older_vulkan_intel_mesa_adapter(&adapter_info(
"AMD Radeon Graphics (RADV RENOIR)",
"radv",
"Mesa 21.2.6",
wgpu::Backend::Vulkan,
wgpu::DeviceType::IntegratedGpu,
)));
// The GL adapter for the same Intel GPU renders fine and must stay fully supported.
assert!(!is_older_vulkan_intel_mesa_adapter(
&intel_xe_tgl_gt2_gl_adapter_info("21.2.6")
));
}

#[test]
fn test_intel_xe_tgl_gt2_prefers_gl_on_older_mesa() {
let vulkan = intel_xe_tgl_gt2_vulkan_adapter_info("21.2.6");
let gl = intel_xe_tgl_gt2_gl_adapter_info("21.2.6");

assert_eq!(
adapter_support(&vulkan, None, false),
AdapterSupport::SupportedWithIssues
);
assert_eq!(adapter_support(&gl, None, false), AdapterSupport::Supported);

// The Vulkan adapter is enumerated first, but the GL adapter should win the ranking.
let ranked = rank_by_support(vec![vulkan, gl]);
assert_eq!(ranked[0].backend, wgpu::Backend::Gl);
assert_eq!(ranked[1].backend, wgpu::Backend::Vulkan);
}

#[test]
fn test_intel_xe_tgl_gt2_is_used_on_newer_mesa() {
let vulkan = intel_xe_tgl_gt2_vulkan_adapter_info("24.0.2");
let gl = intel_xe_tgl_gt2_gl_adapter_info("24.0.2");

assert!(!is_older_vulkan_intel_mesa_adapter(&vulkan));
assert_eq!(
adapter_support(&vulkan, None, false),
AdapterSupport::Supported
);
// With both adapters equally supported, the stability sort is a no-op and the earlier
// backend-priority sort (which prefers Vulkan on Linux) decides.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment overstates what the test proves. rank_by_support only applies the stability sort, so the backend-priority sort is never executed here — with both adapters equal, sorted_by_key is stable and ranked[0] is simply whichever element was pushed into the vec first. The assertion would pass even if backend priority preferred GL (as it effectively can on Windows, where DX12/Vulkan/GL ordering differs).

Suggest either rewording to "the stability sort is a no-op, so input order is preserved" or asserting on adapter_support(&gl, ...) == adapter_support(&vulkan, ...) instead of on ordering.

let ranked = rank_by_support(vec![vulkan, gl]);
assert_eq!(ranked[0].backend, wgpu::Backend::Vulkan);
}

/// Deprioritizing is not filtering: when no GL adapter can present, the old Intel Mesa Vulkan
/// adapter is still the best (and only) candidate, so we must not rank it as `Unsupported`.
#[test]
fn test_intel_xe_tgl_gt2_is_still_used_without_a_gl_fallback() {
let vulkan = intel_xe_tgl_gt2_vulkan_adapter_info("21.2.6");
let support = adapter_support(&vulkan, None, false);

assert_eq!(support, AdapterSupport::SupportedWithIssues);
assert!(support < AdapterSupport::Unsupported);

let ranked = rank_by_support(vec![vulkan]);
assert_eq!(ranked.len(), 1);
assert_eq!(ranked[0].backend, wgpu::Backend::Vulkan);
}