如何检查是否使用真正的硬件视频适配器

4

我正在开发一款应用程序,它在窗口中显示类似于视频的东西。我使用的技术在这里描述:介绍Direct2D 1.1。在我的情况下,唯一的区别是最终我使用位图创建。

ID2D1DeviceContext::CreateBitmap

然后我使用

ID2D1Bitmap::CopyFromMemory

将原始的RGB数据复制到其中,然后我调用。
ID2D1DeviceContext::DrawBitmap

为了绘制位图,我使用高质量的立方插值模式D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC进行缩放,以获得最佳图片。但在某些情况下(如RDP、Citrix、虚拟机等),它非常慢且CPU消耗很高。这是因为在这些情况下,使用的是非硬件视频适配器。因此,对于非硬件适配器,我正在尝试关闭插值并使用更快的方法。问题是我无法确切地检查系统是否有真正的硬件适配器。
当我调用D3D11CreateDevice时,我将其与D3D_DRIVER_TYPE_HARDWARE一起使用,但在虚拟机上,它通常返回“Microsoft Basic Render Driver”,这是一个软件驱动程序,不使用GPU(它会消耗CPU)。因此,目前我检查供应商ID。如果供应商是AMD(ATI)、NVIDIA或Intel,则使用立方插值。否则,我使用不会大量消耗CPU的最快方法。
Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
if (SUCCEEDED(m_pD3dDevice->QueryInterface(...)))
{
    Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
    if (SUCCEEDED(dxgiDevice->GetAdapter(&adapter)))
    {
        DXGI_ADAPTER_DESC desc;
        if (SUCCEEDED(adapter->GetDesc(&desc)))
        {
            // NVIDIA
            if (desc.VendorId == 0x10DE ||
                // AMD
                desc.VendorId == 0x1002 || // 0x1022 ?
                // Intel
                desc.VendorId == 0x8086) // 0x163C, 0x8087 ?
            {
                bSupported = true;
            }
        }
     }
 }

即使在虚拟机中,它也适用于物理(控制台)Windows会话。但是对于RDP会话,在真实的机器上仍然返回供应商,但它不使用GPU(我可以通过Process Hacker 2和AMD System Monitor看到它(在ATI Radeon的情况下)),因此我仍然具有立方插值的高CPU消耗。在RDP会话到具有ATI Radeon Windows 7的情况下,与通过物理控制台相比大10%。

或者我错了,某种方式RDP使用GPU资源,并且这就是返回IDXGIAdapter :: GetDesc的真实硬件适配器的原因?

DirectDraw

另外我查看了DirectX诊断工具。看起来“DirectDraw加速”信息字段完全返回我所需要的信息。在物理(控制台)会话的情况下,它说“启用”。在RDP和虚拟机(没有硬件视频加速)会话的情况下,它说“不可用”。我查看了源代码,理论上我可以使用验证算法。但是实际上这是针对我在应用程序中不使用的DirectDraw。我想使用直接链接到ID3D11Device、IDXGIDevice、IDXGIAdapter等内容的东西。

IDXGIAdapter1 :: GetDesc1和DXGI_ADAPTER_FLAG

我还尝试使用IDXGIAdapter1 :: GetDesc1并检查标志。

Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
if (SUCCEEDED(m_pD3dDevice->QueryInterface(...)))
{
    Microsoft::WRL::ComPtr<IDXGIAdapter> adapter;
    if (SUCCEEDED(dxgiDevice->GetAdapter(&adapter)))
    {
         Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter1;
         if (SUCCEEDED(adapter->QueryInterface(__uuidof(IDXGIAdapter1), reinterpret_cast<void**>(adapter1.GetAddressOf()))))
         {
             DXGI_ADAPTER_DESC1 desc;
             if (SUCCEEDED(adapter1->GetDesc1(&desc)))
             {
                    // desc.Flags
                    // DXGI_ADAPTER_FLAG_NONE         = 0,
                    // DXGI_ADAPTER_FLAG_REMOTE       = 1,
                    // DXGI_ADAPTER_FLAG_SOFTWARE     = 2,
                    // DXGI_ADAPTER_FLAG_FORCE_DWORD  = 0xffffffff
             }
         }
     }
 }

有关DXGI_ADAPTER_FLAG_SOFTWARE标志的信息

 Virtual Machine RDP Win Serv 2012 (Microsoft Basic Render Driver) -> (0x02) DXGI_ADAPTER_FLAG_SOFTWARE
 Physical Win 10 (Intel Video) -> (0x00) DXGI_ADAPTER_FLAG_NONE
 Physical Win 7 (ATI Radeon) - > (0x00) DXGI_ADAPTER_FLAG_NONE
 RDP Win 10 (Intel Video) -> (0x00) DXGI_ADAPTER_FLAG_NONE
 RDP Win 7 (ATI Radeon) -> (0x00) DXGI_ADAPTER_FLAG_NONE

在硬件适配器的真实机器上进行RDP会话时,标志位为0,但我可以通过Process Hacker 2看到GPU没有被使用。至少在Windows 7上,使用ATI Radeon时,在RDP会话中可以看到更大的CPU使用率。因此,看起来DXGI_ADAPTER_FLAG_SOFTWARE仅适用于Microsoft基本渲染驱动程序。所以问题还没有解决。
问题是:
有没有正确的方法来检查当前Windows会话是否使用真正的硬件视频卡(GPU)?或者可能可以检查ID2D1DeviceContext::DrawBitmap的特定插值模式是否具有硬件实现并在当前会话中使用GPU?
更新:
该主题不是关于检测RDP或Citrix会话的。也不是关于检测应用程序是否在虚拟机内部。我已经进行了所有验证,并对这些情况使用了线性插值。这个主题是关于检测当前Windows会话是否使用真正的GPU来显示桌面。我正在寻找一个更复杂的解决方案,利用DirectX和DXGI的功能来做出决策。
2个回答

5
如果你想检测 Microsoft Basic Renderer,最好的方法是使用它的 VID/PID 组合。
ComPtr<IDXGIDevice> dxgiDevice;
if (SUCCEEDED(device.As(&dxgiDevice)))
{
    ComPtr<IDXGIAdapter> adapter;
    if (SUCCEEDED(dxgiDevice->GetAdapter(&adapter)))
    {
        DXGI_ADAPTER_DESC desc;
        if (SUCCEEDED(adapter->GetDesc(&desc)))
        {
            if ( (desc.VendorId == 0x1414) && (desc.DeviceId == 0x8c) )
            {
                // WARNING: Microsoft Basic Render Driver is active.
                // Performance of this application may be unsatisfactory.
                // Please ensure that your video card is Direct3D10/11 capable
                // and has the appropriate driver installed.
            }
        }
    }
}

请查看 Microsoft DocsDirect3D 11 Create Device解剖

在测试/调试时,您可能不希望明确地阻止这些情况,但您确实希望向用户提供某种警告或通知反馈,以表明他们正在使用软件而不是硬件渲染。

从Win32经典桌面应用程序中检测远程桌面最好直接使用 GetSystemMetrics( SM_REMOTESESSION )

请参见 Microsoft Docs


谢谢您的回答,但不幸的是我不需要检测Microsoft Basic Renderer。当我对真实机器进行RDP会话时,IDXGIAdapter返回(在描述中)安装在机器上的真实视频适配器。根据像Process Hacker 2这样的工具,GPU未被使用,而CPU用于渲染。我查看了IDXGIAdapter1 :: GetDesc1和DXGI_ADAPTER_FLAG,并且在RDP会话的情况下它没有DXGI_ADAPTER_FLAG_SOFTWARE标志。 - Eugen
你在原帖中的逻辑有缺陷,因为你试图“锁定”三个供应商之一。我的答案是,你应该反转逻辑,如果这是你关心的问题,则排除MBR。RDP是一个独立的问题,你当然可以直接检测到。你知道自己是否正在使用RemoteFX vGPU吗? - Chuck Walbourn
我们在原帖中的逻辑存在缺陷,因为您试图“锁定”到三个供应商之一。您能确定虚拟机不会模拟实际具有软件实现的真实硬件视频卡吗?目前我只能测试VMWare。它在设备中有VMWare SVGA 3D,但IDXGIDevice返回“Microsoft Basic Renderer”。那其他虚拟化软件呢? - Eugen
最好的做法可能是先回顾并解释一下你试图解决的问题,而不是提出一个解决方案。如果你担心你的应用程序表现不佳,那么检测RDP和/或MBR场景,并弹出一个警告框,让用户知道它不会表现良好。你不能假设NVIDIA、AMD和/或Intel永远不会推出软件渲染器。事实上,很多早期的Intel“GPU”几乎什么都不是,只是软件。 - Chuck Walbourn
但我已经解释了问题。它在主贴中:「我使用高品质的立方插值模式... 但在某些情况下(RDP、Citrix、虚拟机等),它非常缓慢并且具有非常高的CPU消耗。」因此,目前我只限于可进行立方插值的卡片为NVIDIA、AMD和INTEL。另外,是的,它已经检查是否在RDP会话内,并且不使用立方插值。我只是在寻找更复杂的解决方案,因为软件可以在Citrix中运行,或者如您所注意到的可能存在旧的Intel“GPU”等情况。 - Eugen
显示剩余3条评论

2

我将回答一个三年前的问题,因为我自己也曾为此而苦苦挣扎。

我需要查阅注册表。第一步是在注册表中找到适配器LUID,以获取适配器GUID。

private string GetAdapterGuid(long luid)
{
    var directXRegistryKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DirectX");
    if (directXRegistryKey == null)
        return "";
    var subKeyNames = directXRegistryKey.GetSubKeyNames();
    foreach (var subKeyName in subKeyNames)
    {
        var subKey = directXRegistryKey.OpenSubKey(subKeyName);
        if (subKey.GetValueKind("AdapterLuid") != RegistryValueKind.QWord)
            continue;
        var luidValue = (long)subKey.GetValue("AdapterLuid");
        if (luidValue == luid)
            return subKeyName;
    }
    return "";
}

一旦您获得了该Guid,您可以像这样在HKLM中搜索图形卡的详细信息。如果是虚拟的,则服务名称将是“INDIRECTKMD”:

private bool IsVirtualAdapter(string adapterGuid)
{
    var videoRegistryKey = Registry.LocalMachine.OpenSubKey($@"SYSTEM\CurrentControlSet\Control\Video\{adapterGuid}\Video");
    if (videoRegistryKey == null)
        return false;
    if (videoRegistryKey.GetValueKind("Service") != RegistryValueKind.String)
        return false;
    var serviceName = (string)videoRegistryKey.GetValue("Service");
    return serviceName.ToUpper() == "INDIRECTKMD";
}

检查服务名称比解析DeviceDesc值要容易。

我的用例涉及准备好Guid,所以我将函数拆分了,你可以将其合并为一个函数。

它仅通过此检测RDP/MSTSC,其他虚拟适配器可能需要其他服务名称。或者您可以尝试仅检测Nvidia/AMD/Intel驱动程序名称...由您决定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接