mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 04:08:40 +08:00
[VideoOut] Prefer NVIDIA/discrete GPUs over integrated (#97)
SelectPhysicalDevice took the first device exposing a graphics+present queue. On hybrid-graphics laptops that is the integrated GPU, so the discrete card went unused - and AMD's integrated driver access-violates inside vkCreateGraphicsPipelines while compiling some translated guest shaders, killing the process. The CLR surfaced that native AV as "Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code", which made it look like a CPU/threading fault. Score the candidates instead: NVIDIA parts win, other discrete GPUs come next, and an integrated GPU is only chosen when nothing else can present. SHARPEMU_VK_DEVICE=<substring> pins a specific adapter, and the selected device is logged.
This commit is contained in:
@@ -178,6 +178,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private static uint _windowHeight;
|
private static uint _windowHeight;
|
||||||
private static bool _closed;
|
private static bool _closed;
|
||||||
private const string DebugUtilsExtensionName = "VK_EXT_debug_utils";
|
private const string DebugUtilsExtensionName = "VK_EXT_debug_utils";
|
||||||
|
private const uint NvidiaVendorId = 0x10DE;
|
||||||
private static bool _splashHidden;
|
private static bool _splashHidden;
|
||||||
private static long _enqueuedGuestWorkSequence;
|
private static long _enqueuedGuestWorkSequence;
|
||||||
private static long _completedGuestWorkSequence;
|
private static long _completedGuestWorkSequence;
|
||||||
@@ -1502,6 +1503,13 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
Check(_vk.EnumeratePhysicalDevices(_instance, &deviceCount, devicePointer), "vkEnumeratePhysicalDevices");
|
Check(_vk.EnumeratePhysicalDevices(_instance, &deviceCount, devicePointer), "vkEnumeratePhysicalDevices");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hybrid laptops enumerate the iGPU first, and AMD's integrated driver
|
||||||
|
// segfaults compiling some translated shaders, so rank rather than take
|
||||||
|
// the first hit. SHARPEMU_VK_DEVICE=<substring> pins an adapter by name.
|
||||||
|
var deviceOverride = Environment.GetEnvironmentVariable("SHARPEMU_VK_DEVICE");
|
||||||
|
var bestScore = int.MinValue;
|
||||||
|
var found = false;
|
||||||
|
|
||||||
foreach (var device in devices)
|
foreach (var device in devices)
|
||||||
{
|
{
|
||||||
uint queueCount = 0;
|
uint queueCount = 0;
|
||||||
@@ -1521,13 +1529,60 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_physicalDevice = device;
|
_vk.GetPhysicalDeviceProperties(device, out var properties);
|
||||||
_queueFamilyIndex = index;
|
var name = SilkMarshal.PtrToString((nint)properties.DeviceName) ?? string.Empty;
|
||||||
return;
|
var score = ScorePhysicalDevice(properties, name, deviceOverride);
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][INFO] Vulkan candidate: {name} ({properties.DeviceType}) score={score}");
|
||||||
|
|
||||||
|
if (score > bestScore)
|
||||||
|
{
|
||||||
|
bestScore = score;
|
||||||
|
_physicalDevice = device;
|
||||||
|
_queueFamilyIndex = index;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("No Vulkan graphics/present queue was found.");
|
if (!found)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No Vulkan graphics/present queue was found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_vk.GetPhysicalDeviceProperties(_physicalDevice, out var selected);
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][INFO] Vulkan device: {SilkMarshal.PtrToString((nint)selected.DeviceName)} ({selected.DeviceType})");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ScorePhysicalDevice(
|
||||||
|
PhysicalDeviceProperties properties,
|
||||||
|
string name,
|
||||||
|
string? deviceOverride)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(deviceOverride))
|
||||||
|
{
|
||||||
|
return name.Contains(deviceOverride, StringComparison.OrdinalIgnoreCase) ? 1000 : -1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
var score = properties.DeviceType switch
|
||||||
|
{
|
||||||
|
PhysicalDeviceType.DiscreteGpu => 300,
|
||||||
|
PhysicalDeviceType.VirtualGpu => 100,
|
||||||
|
PhysicalDeviceType.Cpu => 50,
|
||||||
|
// Last resort: only picked when nothing else can present.
|
||||||
|
PhysicalDeviceType.IntegratedGpu => -100,
|
||||||
|
_ => 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (properties.VendorID == NvidiaVendorId)
|
||||||
|
{
|
||||||
|
score += 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDevice()
|
private void CreateDevice()
|
||||||
|
|||||||
Reference in New Issue
Block a user