discord-bot/CompatBot/Database/Providers/AmdDriverVersionProvider.cs

170 lines
6.9 KiB
C#

using System.Net.Http;
using System.Xml.Linq;
using CompatApiClient.Compression;
namespace CompatBot.Database.Providers;
internal static class AmdDriverVersionProvider
{
private static readonly Dictionary<string, List<string>> VulkanToDriver = new();
private static readonly Dictionary<string, string> OpenglToDriver = new();
private static readonly Dictionary<string, string> InternalToDriver = new();
private static readonly SemaphoreSlim SyncObj = new(1, 1);
public static async Task RefreshAsync()
{
if (await SyncObj.WaitAsync(0).ConfigureAwait(false))
try
{
using var httpClient = HttpClientFactory.Create(new CompressionMessageHandler());
await using var response = await httpClient.GetStreamAsync("https://raw.githubusercontent.com/GPUOpen-Drivers/amd-vulkan-versions/master/amdversions.xml").ConfigureAwait(false);
var xml = await XDocument.LoadAsync(response, LoadOptions.None, Config.Cts.Token).ConfigureAwait(false);
if (xml.Root is null)
{
Config.Log.Warn("Failed to update AMD version mapping");
return;
}
foreach (var driver in xml.Root.Elements("driver"))
{
var winVer = (string?)driver.Element("windows-version");
var vkVer = (string?)driver.Element("vulkan-version");
var internVer = (string?)driver.Element("internal-version");
var driverVer = (string?)driver.Attribute("version");
if (vkVer is null)
continue;
if (!VulkanToDriver.TryGetValue(vkVer, out var verList))
VulkanToDriver[vkVer] = verList = new();
if (string.IsNullOrEmpty(driverVer))
continue;
verList.Insert(0, driverVer);
if (!string.IsNullOrEmpty(winVer))
OpenglToDriver[winVer] = driverVer;
if (!string.IsNullOrEmpty(internVer))
InternalToDriver[internVer] = driverVer;
}
foreach (var key in VulkanToDriver.Keys.ToList())
VulkanToDriver[key] = VulkanToDriver[key].Distinct().ToList();
}
catch (Exception e)
{
Config.Log.Warn(e, "Failed to update AMD version mapping");
}
finally
{
SyncObj.Release();
}
}
public static async ValueTask<string> GetFromOpenglAsync(string openglVersion, bool autoRefresh = true)
{
if (OpenglToDriver.TryGetValue(openglVersion, out var result))
return result;
if (!Version.TryParse(openglVersion, out var glVersion))
return openglVersion;
if (glVersion is { Major: >= 22, Minor: < 13, Build: <10, Revision: > 220600 })
return $"{glVersion.Major}.{glVersion.Minor}.{glVersion.Build}";
var glVersions = new List<(Version glVer, string driverVer)>(OpenglToDriver.Count);
foreach (var key in OpenglToDriver.Keys)
{
if (Version.TryParse(key, out var ver))
glVersions.Add((ver, OpenglToDriver[key]));
}
if (glVersions.Count == 0)
return openglVersion;
glVersions.Sort((l, r) => l.glVer < r.glVer ? -1 : l.glVer > r.glVer ? 1 : 0);
if (glVersion < glVersions[0].glVer)
return $"older than {glVersions[0].driverVer} ({openglVersion})";
var newest = glVersions.Last();
if (glVersion > newest.glVer)
{
if (autoRefresh)
{
await RefreshAsync().ConfigureAwait(false);
return await GetFromOpenglAsync(openglVersion, false).ConfigureAwait(false);
}
return $"newer than {newest.driverVer} ({openglVersion})";
}
var approximate = glVersions.FirstOrDefault(v => v.glVer.Minor == glVersion.Minor && v.glVer.Build == glVersion.Build);
if (!string.IsNullOrEmpty(approximate.driverVer))
return $"{approximate.driverVer} rev {glVersion.Revision}";
if (string.IsNullOrEmpty(approximate.driverVer))
for (var i = 0; i < glVersions.Count - 1; i++)
if (glVersion > glVersions[i].glVer && glVersion < glVersions[i + 1].glVer)
{
approximate = glVersions[i];
break;
}
if (!string.IsNullOrEmpty(approximate.driverVer))
return $"probably {approximate.driverVer}";
return openglVersion;
}
public static async ValueTask<string> GetFromVulkanAsync(string vulkanVersion, bool autoRefresh = true)
{
if (!VulkanToDriver.TryGetValue(vulkanVersion, out var result))
await RefreshAsync().ConfigureAwait(false);
if (result?.Count > 0 || (VulkanToDriver.TryGetValue(vulkanVersion, out result) && result.Count > 0))
{
if (result.Count == 1)
return result[0];
return $"{result.First()} - {result.Last()}";
}
if (Version.TryParse(vulkanVersion, out var vkVer))
{
var vkVersions = new List<(Version vkVer, List<string> driverVers)>(VulkanToDriver.Count);
foreach (var key in VulkanToDriver.Keys)
{
if (Version.TryParse(key, out var ver))
vkVersions.Add((ver, VulkanToDriver[key]));
}
if (vkVersions.Count == 0)
return vulkanVersion;
vkVersions.Sort((l, r) => l.vkVer < r.vkVer ? -1 : l.vkVer > r.vkVer ? 1 : 0);
if (vkVer < vkVersions[0].vkVer)
return $"older than {vkVersions[0].driverVers.First()} ({vulkanVersion})";
var (version, driverVers) = vkVersions.Last();
if (vkVer > version)
{
if (!autoRefresh)
return $"newer than {driverVers.Last()} ({vulkanVersion})";
await RefreshAsync().ConfigureAwait(false);
return await GetFromVulkanAsync(vulkanVersion, false).ConfigureAwait(false);
}
for (var i = 1; i < vkVersions.Count; i++)
{
if (vkVer >= vkVersions[i].vkVer)
continue;
var lowerVer = vkVersions[i - 1].vkVer;
var mapKey = VulkanToDriver.Keys.FirstOrDefault(k => Version.Parse(k) == lowerVer);
if (mapKey is null)
continue;
if (!VulkanToDriver.TryGetValue(mapKey, out var oldestDriverList))
continue;
return $"unknown version between {oldestDriverList.First()} and {vkVersions[i].driverVers.Last()} ({vulkanVersion})";
}
}
return vulkanVersion;
}
}