Skip to content

Commit

Permalink
Try to actually acquire a frame on Intel/Linux upon adapter inspection
Browse files Browse the repository at this point in the history
  • Loading branch information
kvark committed Jul 18, 2024
1 parent 9e47628 commit 90c6da2
Showing 1 changed file with 77 additions and 21 deletions.
98 changes: 77 additions & 21 deletions blade-graphics/src/vulkan/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ash::{amd, ext, khr, vk};
use naga::back::spv;
use std::{ffi, fs, mem, sync::Mutex};
use std::{ffi, mem, sync::Mutex};

use crate::NotSupportedError;

Expand All @@ -24,11 +24,68 @@ const REQUIRED_DEVICE_EXTENSIONS: &[&ffi::CStr] = &[
vk::KHR_DYNAMIC_RENDERING_NAME,
];

unsafe fn test_presentation(
instance: &super::Instance,
physical_device: vk::PhysicalDevice,
surface: vk::SurfaceKHR,
) -> bool {
let device_extensions = vec![vk::KHR_SWAPCHAIN_NAME.as_ptr()];
let family_infos = [vk::DeviceQueueCreateInfo::default().queue_priorities(&[1.0])];
let device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&family_infos)
.enabled_extension_names(&device_extensions);
let device = instance
.core
.create_device(physical_device, &device_create_info, None)
.unwrap();

let fence = device
.create_fence(&vk::FenceCreateInfo::default(), None)
.unwrap();

let extension = khr::swapchain::Device::new(&instance.core, &device);
let capabilities = instance
.surface
.as_ref()
.unwrap()
.get_physical_device_surface_capabilities(physical_device, surface)
.unwrap();
let mut is_good = true;

let swapchain_create_info = vk::SwapchainCreateInfoKHR {
surface,
min_image_count: capabilities.min_image_count,
image_format: vk::Format::B8G8R8A8_UNORM,
image_extent: capabilities.min_image_extent,
image_array_layers: 1,
image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
composite_alpha: vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
..Default::default()
};
match extension.create_swapchain(&swapchain_create_info, None) {
Ok(swapchain) => unsafe {
let result = extension.acquire_next_image(swapchain, !0, vk::Semaphore::null(), fence);
if result.is_err() {
log::warn!("Acquire frame failed, skipping the adapter");
is_good = false;
}
extension.destroy_swapchain(swapchain, None);
},
Err(_) => {
log::warn!("Swapchain creation failed, skipping the adapter");
is_good = false;
}
}

device.destroy_fence(fence, None);
device.destroy_device(None);
is_good
}

#[derive(Debug)]
#[allow(unused)]
struct SystemBugs {
/// https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
intel_unable_to_present: bool,
/// https://github.com/kvark/blade/issues/117
intel_fix_descriptor_pool_leak: bool,
}
Expand All @@ -46,18 +103,15 @@ struct AdapterCapabilities {
bugs: SystemBugs,
}

// See https://github.com/canonical/nvidia-prime/blob/587c5012be9dddcc17ab4d958f10a24fa3342b4d/prime-select#L56
fn is_nvidia_prime_forced() -> bool {
match fs::read_to_string("/etc/prime-discrete") {
Ok(contents) => contents == "on\n",
Err(_) => false,
}
struct SystemInfo {
driver_api_version: u32,
adapter_count: usize,
}

unsafe fn inspect_adapter(
phd: vk::PhysicalDevice,
instance: &super::Instance,
driver_api_version: u32,
info: &SystemInfo,
surface: Option<vk::SurfaceKHR>,
) -> Option<AdapterCapabilities> {
let supported_extension_properties = instance
Expand Down Expand Up @@ -101,19 +155,14 @@ unsafe fn inspect_adapter(
let api_version = properties2_khr
.properties
.api_version
.min(driver_api_version);
.min(info.driver_api_version);
if api_version < vk::API_VERSION_1_1 {
log::warn!("\tRejected for API version {}", api_version);
return None;
}

let vendor_id = properties2_khr.properties.vendor_id;

let bugs = SystemBugs {
//Note: this is somewhat broad across X11/Wayland and different drivers.
// It could be narrower, but at the end of the day if the user forced Prime
// for GLX it should be safe to assume they want it for Vulkan as well.
intel_unable_to_present: is_nvidia_prime_forced() && vendor_id == db::intel::VENDOR,
intel_fix_descriptor_pool_leak: cfg!(windows) && vendor_id == db::intel::VENDOR,
};

Expand All @@ -124,9 +173,13 @@ unsafe fn inspect_adapter(
log::warn!("Rejected for not presenting to the window surface");
return None;
}
if bugs.intel_unable_to_present {
log::warn!("Rejecting Intel for not presenting when Nvidia is present (on Linux)");
return None;
// Check if we are inspecting an Intel GPU on a multi-GPU Linux system.
// https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
if cfg!(target_os = "linux") && vendor_id == db::intel::VENDOR && info.adapter_count > 2 {
log::info!("Testing presentation capability on Linux/Intel");
if !test_presentation(instance, phd, surface) {
return None;
}
}
}

Expand Down Expand Up @@ -389,11 +442,14 @@ impl super::Context {
.core
.enumerate_physical_devices()
.map_err(|e| NotSupportedError::VulkanError(e))?;
let system_info = SystemInfo {
driver_api_version,
adapter_count: physical_devices.len(),
};
let (physical_device, capabilities) = physical_devices
.into_iter()
.find_map(|phd| {
inspect_adapter(phd, &instance, driver_api_version, vk_surface)
.map(|caps| (phd, caps))
inspect_adapter(phd, &instance, &system_info, vk_surface).map(|caps| (phd, caps))
})
.ok_or_else(|| NotSupportedError::NoSupportedDeviceFound)?;

Expand Down

0 comments on commit 90c6da2

Please sign in to comment.