XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;

namespace XFESystem.DriverOracle;

internal sealed record CudaDevice(int Ordinal, int Handle, string Name, string PciBusId, string Uuid, ulong TotalMemoryBytes, int Major, int Minor, PciDevice? WindowsPciIdentity);
internal sealed record CudaCall(string Api, int ReturnCode, double Milliseconds, bool Cleanup);

internal sealed unsafe class CudaDriver : IDisposable
{
    private readonly nint library;
    private readonly StreamWriter trace;
    public string LibraryPath { get; }
    public List<CudaCall> Calls { get; } = [];
    public bool LibraryReleased { get; private set; }
    public int Version { get; private set; }

    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int InitD(uint flags);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int IntOutD(out int value);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int DeviceD(out int device, int ordinal);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int DeviceTextD(byte* text, int length, int device);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int UuidD(byte* uuid, int device);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int TotalMemD(out nuint bytes, int device);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int CapabilityD(out int major, out int minor, int device);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int ContextCreateD(out nint context, uint flags, int device);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int PointerD(nint value);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int PointerOutD(out nint value);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int NoArgsD();
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int LinkCreateD(uint count, int* options, nint* values, out nint state);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int LinkAddD(nint state, int inputType, byte* data, nuint size, [MarshalAs(UnmanagedType.LPStr)] string name, uint count, int* options, nint* values);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int LinkCompleteD(nint state, out nint cubin, out nuint size);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int ModuleLoadD(out nint module, byte* image);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int FunctionD(out nint function, nint module, [MarshalAs(UnmanagedType.LPStr)] string name);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int FunctionAttributeD(out int value, int attribute, nint function);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int AllocateD(out ulong address, nuint bytes);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int FreeD(ulong address);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int UploadD(ulong target, void* source, nuint bytes);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int DownloadD(void* target, ulong source, nuint bytes);
    [UnmanagedFunctionPointer(CallingConvention.Winapi)] internal delegate int LaunchD(nint function, uint gx, uint gy, uint gz, uint bx, uint by, uint bz, uint sharedBytes, nint stream, void** arguments, void** extra);
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
    private static extern uint GetModuleFileNameW(nint module, StringBuilder path, int size);

    public CudaDriver(StreamWriter trace)
    {
        this.trace = trace;
        LibraryPath = Path.Combine(Environment.SystemDirectory, "nvcuda.dll");
        library = NativeLibrary.Load(LibraryPath);
        var actual = new StringBuilder(32768);
        uint count = GetModuleFileNameW(library, actual, actual.Capacity);
        if (count == 0 || count >= actual.Capacity || !Path.GetFullPath(actual.ToString()).Equals(Path.GetFullPath(LibraryPath), StringComparison.OrdinalIgnoreCase))
        {
            NativeLibrary.Free(library);
            throw new InvalidOperationException("The loaded CUDA driver did not resolve to the pinned System32 path.");
        }
    }

    internal T Api<T>(string name) where T : Delegate => Marshal.GetDelegateForFunctionPointer<T>(NativeLibrary.GetExport(library, name));
    internal void Check(string name, Func<int> call)
    {
        var timer = Stopwatch.StartNew(); int code = call();
        Record(name, code, timer.Elapsed.TotalMilliseconds, false);
        if (code != 0) throw new InvalidOperationException(name + " returned CUDA error " + code + ".");
    }
    // Pointer-bearing calls cannot be captured by a lambda; record immediately
    // after their return and before interpreting any output.
    internal void Check(string name, int code)
    {
        Record(name, code, 0, false);
        if (code != 0) throw new InvalidOperationException(name + " returned CUDA error " + code + ".");
    }
    internal bool Cleanup(string name, Func<int> call)
    {
        try { var timer = Stopwatch.StartNew(); int code = call(); Record(name, code, timer.Elapsed.TotalMilliseconds, true); return code == 0; }
        catch (Exception error) { trace.WriteLine(JsonSerializer.Serialize(new { Api = name, Cleanup = true, ManagedException = error.ToString() })); return false; }
    }
    private void Record(string name, int code, double elapsed, bool cleanup)
    {
        var entry = new CudaCall(name, code, elapsed, cleanup); Calls.Add(entry); trace.WriteLine(JsonSerializer.Serialize(entry));
    }

    public CudaDevice[] Query()
    {
        int version = 0; Check("cuDriverGetVersion", () => Api<IntOutD>("cuDriverGetVersion")(out version)); Version = version;
        Check("cuInit", () => Api<InitD>("cuInit")(0));
        int count = 0; Check("cuDeviceGetCount", () => Api<IntOutD>("cuDeviceGetCount")(out count));
        if (count is < 0 or > 32) throw new InvalidOperationException("CUDA device enumeration exceeds its bound.");
        var pciDevices = WindowsPci.Exact5090(); var devices = new List<CudaDevice>();
        byte* name = stackalloc byte[256]; byte* bus = stackalloc byte[32]; byte* uuid = stackalloc byte[16];
        for (int ordinal = 0; ordinal < count; ordinal++)
        {
            int device = 0; Check("cuDeviceGet", Api<DeviceD>("cuDeviceGet")(out device, ordinal));
            new Span<byte>(name, 256).Clear(); new Span<byte>(bus, 32).Clear();
            Check("cuDeviceGetName", Api<DeviceTextD>("cuDeviceGetName")(name, 256, device));
            Check("cuDeviceGetPCIBusId", Api<DeviceTextD>("cuDeviceGetPCIBusId")(bus, 32, device));
            Check("cuDeviceGetUuid", Api<UuidD>("cuDeviceGetUuid")(uuid, device));
            Check("cuDeviceTotalMem_v2", Api<TotalMemD>("cuDeviceTotalMem_v2")(out nuint memory, device));
            Check("cuDeviceComputeCapability", Api<CapabilityD>("cuDeviceComputeCapability")(out int major, out int minor, device));
            string deviceName = Marshal.PtrToStringUTF8((nint)name) ?? "", busId = Marshal.PtrToStringUTF8((nint)bus) ?? "";
            var matched = pciDevices.Where(pci => busId.Equals($"0000:{pci.Bus:x2}:{pci.Device:x2}.{pci.Function:x}", StringComparison.OrdinalIgnoreCase)).ToArray();
            if (matched.Length > 1) throw new InvalidOperationException("The CUDA bus has ambiguous Windows PCI identities.");
            devices.Add(new(ordinal, device, deviceName, busId, Convert.ToHexStringLower(new ReadOnlySpan<byte>(uuid, 16)), memory, major, minor, matched.SingleOrDefault()));
        }
        return devices.ToArray();
    }

    public void Dispose()
    {
        if (!LibraryReleased) { NativeLibrary.Free(library); LibraryReleased = true; }
    }
}