mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
[VideoOut] Scope the AMD integrated-GPU penalty to Windows (#339)
* [VideoOut] Prefer real integrated GPUs over software rasterizers (#325) Penalize only AMD integrated GPUs (the #97 vkCreateGraphicsPipelines crash) instead of all integrated devices, so Intel/Apple/Qualcomm iGPUs outrank Cpu-type software rasterizers (Mesa lavapipe). Hoist ScorePhysicalDevice to the outer class and add unit tests for the ordering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [VideoOut] Scope AMD iGPU penalty to Windows via a penalty helper Extract ComputeDevicePenalty (the value subtracted from a device's base score) and gate the #97 AMD-integrated penalty on Windows only. Mesa RADV on Linux (e.g. the Steam Deck's AMD APU) is a different, working driver and should keep its full integrated score. Add a Steam Deck test case and drop inline comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [VideoOut] Move device-scoring helpers out of the const block Relocate ScorePhysicalDevice and ComputeDevicePenalty below the leading const cluster instead of splitting it, and trim the vendor-ID reference comment to adapters an x86-64 host can realistically enumerate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -271,6 +271,10 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private static bool _presenterCloseRequested;
|
private static bool _presenterCloseRequested;
|
||||||
private const string DebugUtilsExtensionName = "VK_EXT_debug_utils";
|
private const string DebugUtilsExtensionName = "VK_EXT_debug_utils";
|
||||||
private const uint NvidiaVendorId = 0x10DE;
|
private const uint NvidiaVendorId = 0x10DE;
|
||||||
|
private const uint AmdVendorId = 0x1002;
|
||||||
|
// Other GPU PCI vendor IDs, for reference when adding future rules:
|
||||||
|
// Intel 0x8086, Apple 0x106B, Qualcomm 0x5143 (Windows-on-ARM), Microsoft software 0x1414.
|
||||||
|
private const int LastResortPenalty = 1000;
|
||||||
private const string PortabilityEnumerationExtensionName = "VK_KHR_portability_enumeration";
|
private const string PortabilityEnumerationExtensionName = "VK_KHR_portability_enumeration";
|
||||||
private const string PortabilitySubsetExtensionName = "VK_KHR_portability_subset";
|
private const string PortabilitySubsetExtensionName = "VK_KHR_portability_subset";
|
||||||
private const int GlfwPlatformHint = 0x00050003;
|
private const int GlfwPlatformHint = 0x00050003;
|
||||||
@@ -279,6 +283,50 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private const int GlfwPlatformWayland = 0x00060003;
|
private const int GlfwPlatformWayland = 0x00060003;
|
||||||
private const int GlfwPlatformX11 = 0x00060004;
|
private const int GlfwPlatformX11 = 0x00060004;
|
||||||
private const int GlfwPlatformNull = 0x00060005;
|
private const int GlfwPlatformNull = 0x00060005;
|
||||||
|
|
||||||
|
internal 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.IntegratedGpu => 50,
|
||||||
|
PhysicalDeviceType.Cpu => 20,
|
||||||
|
_ => 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (properties.VendorID == NvidiaVendorId)
|
||||||
|
{
|
||||||
|
score += 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
score -= ComputeDevicePenalty(properties, OperatingSystem.IsWindows());
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static int ComputeDevicePenalty(PhysicalDeviceProperties properties, bool isWindows)
|
||||||
|
{
|
||||||
|
var penalty = 0;
|
||||||
|
|
||||||
|
if (isWindows &&
|
||||||
|
properties.DeviceType == PhysicalDeviceType.IntegratedGpu &&
|
||||||
|
properties.VendorID == AmdVendorId)
|
||||||
|
{
|
||||||
|
penalty += LastResortPenalty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return penalty;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool _splashHidden;
|
private static bool _splashHidden;
|
||||||
private static long _enqueuedGuestWorkSequence;
|
private static long _enqueuedGuestWorkSequence;
|
||||||
// Largest contiguous completed sequence, retained for compact diagnostics.
|
// Largest contiguous completed sequence, retained for compact diagnostics.
|
||||||
@@ -3200,33 +3248,6 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
_window.Title = VideoOutExports.GetWindowTitle();
|
_window.Title = VideoOutExports.GetWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
PhysicalDeviceType.IntegratedGpu => -100,
|
|
||||||
_ => 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (properties.VendorID == NvidiaVendorId)
|
|
||||||
{
|
|
||||||
score += 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadComputeDeviceLimits()
|
private void LoadComputeDeviceLimits()
|
||||||
{
|
{
|
||||||
_vk.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
|
_vk.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
using SharpEmu.Libs.VideoOut;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace SharpEmu.Libs.Tests.VideoOut;
|
||||||
|
|
||||||
|
public sealed class VulkanPhysicalDeviceScoringTests
|
||||||
|
{
|
||||||
|
private const uint NvidiaVendorId = 0x10DE;
|
||||||
|
private const uint AmdVendorId = 0x1002;
|
||||||
|
private const uint IntelVendorId = 0x8086;
|
||||||
|
private const uint QualcommVendorId = 0x5143;
|
||||||
|
|
||||||
|
private static PhysicalDeviceProperties Device(PhysicalDeviceType type, uint vendorId = 0)
|
||||||
|
=> new() { DeviceType = type, VendorID = vendorId };
|
||||||
|
|
||||||
|
private static int Score(PhysicalDeviceType type, uint vendorId = 0)
|
||||||
|
=> VulkanVideoPresenter.ScorePhysicalDevice(Device(type, vendorId), name: string.Empty, deviceOverride: null);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RealIntegratedGpuOutranksSoftwareRasterizer()
|
||||||
|
{
|
||||||
|
Assert.True(Score(PhysicalDeviceType.IntegratedGpu) > Score(PhysicalDeviceType.Cpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(IntelVendorId)]
|
||||||
|
[InlineData(QualcommVendorId)]
|
||||||
|
[InlineData(0u)]
|
||||||
|
public void NonAmdIntegratedGpuBeatsSoftware(uint vendorId)
|
||||||
|
{
|
||||||
|
Assert.True(Score(PhysicalDeviceType.IntegratedGpu, vendorId) > Score(PhysicalDeviceType.Cpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DiscreteGpuOutranksIntegratedGpu()
|
||||||
|
{
|
||||||
|
Assert.True(Score(PhysicalDeviceType.DiscreteGpu) > Score(PhysicalDeviceType.IntegratedGpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NvidiaDiscreteGpuScoresHighest()
|
||||||
|
{
|
||||||
|
var nvidia = Score(PhysicalDeviceType.DiscreteGpu, NvidiaVendorId);
|
||||||
|
Assert.True(nvidia > Score(PhysicalDeviceType.DiscreteGpu));
|
||||||
|
Assert.True(nvidia > Score(PhysicalDeviceType.IntegratedGpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DeviceOverridePinsMatchingAdapterAndExcludesOthers()
|
||||||
|
{
|
||||||
|
var properties = Device(PhysicalDeviceType.Cpu);
|
||||||
|
Assert.Equal(1000, VulkanVideoPresenter.ScorePhysicalDevice(properties, "Radeon Graphics", "radeon"));
|
||||||
|
Assert.Equal(-1000, VulkanVideoPresenter.ScorePhysicalDevice(properties, "Radeon Graphics", "nvidia"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AmdIntegratedGpuIsLastResortOnWindows()
|
||||||
|
{
|
||||||
|
var penalty = VulkanVideoPresenter.ComputeDevicePenalty(
|
||||||
|
Device(PhysicalDeviceType.IntegratedGpu, AmdVendorId), isWindows: true);
|
||||||
|
Assert.True(penalty > 0);
|
||||||
|
Assert.True(50 - penalty < 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AmdApuIsNotPenalizedOffWindows()
|
||||||
|
{
|
||||||
|
var penalty = VulkanVideoPresenter.ComputeDevicePenalty(
|
||||||
|
Device(PhysicalDeviceType.IntegratedGpu, AmdVendorId), isWindows: false);
|
||||||
|
Assert.Equal(0, penalty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public void AmdDiscreteGpuIsNeverPenalized(bool isWindows)
|
||||||
|
{
|
||||||
|
Assert.Equal(0, VulkanVideoPresenter.ComputeDevicePenalty(
|
||||||
|
Device(PhysicalDeviceType.DiscreteGpu, AmdVendorId), isWindows));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(IntelVendorId)]
|
||||||
|
[InlineData(QualcommVendorId)]
|
||||||
|
public void NonAmdIntegratedGpuIsNeverPenalized(uint vendorId)
|
||||||
|
{
|
||||||
|
Assert.Equal(0, VulkanVideoPresenter.ComputeDevicePenalty(
|
||||||
|
Device(PhysicalDeviceType.IntegratedGpu, vendorId), isWindows: true));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user