代码之家  ›  专栏  ›  技术社区  ›  Eugen

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

  •  4
  • Eugen  · 技术社区  · 7 年前

    我开发了一个应用程序,它在窗口中显示类似视频的内容。我使用这里描述的技术 Introducing Direct2D 1.1 . 在我的例子中,唯一的区别是最终我使用

    ID2D1DeviceContext::CreateBitmap
    

    然后我使用

    ID2D1Bitmap::CopyFromMemory
    

    将原始RGB数据复制到其中,然后调用

    ID2D1DeviceContext::DrawBitmap
    

    绘制位图。我使用高质量立方体插值模式D2D1\u interpolation\u mode\u high\u quality\u cubic进行缩放以获得最佳图片,但在某些情况下(RDP、Citrix、虚拟机等),速度非常慢,CPU消耗非常高。这是因为在这些情况下使用了非硬件视频适配器。因此,对于非硬件适配器,我正在尝试关闭插值并使用更快的方法。问题是我无法准确地检查系统是否有真正的硬件适配器。

    调用D3D11CreateDevice时,我将其与D3D\U DRIVER\U TYPE\U硬件一起使用,但在虚拟机上,它通常返回“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会话,IDXGIAdapter在实际机器的情况下仍会返回供应商,但它不使用GPU(我可以通过Process Hacker 2和AMD System Monitor(在ATI Radeon的情况下)看到),因此我使用三次插值时仍会有很高的CPU消耗。如果使用ATI Radeon与Windows 7进行RDP会话,则比通过物理控制台进行的会话大10%。

    还是我弄错了,RDP使用了GPU资源,这就是为什么它通过IDXGIAdapter::GetDesc返回一个真正的硬件适配器?

    DirectDraw公司

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

    IDXGIAdapter1::GetDesc1和DXGI\u ADAPTER\u标志

    我还尝试使用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
                 }
             }
         }
     }
    

    Information about the DXGI_ADAPTER_FLAG_SOFTWARE flag

     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没有使用。至少在使用ATI Radeon的Windows 7上,在RDP会话的情况下,我可以看到更大的CPU使用率。所以看起来DXGI\u ADAPTER\u FLAG\u软件仅适用于Microsoft Basic Render Driver。所以这个问题没有解决。

    问题是

    是否有正确的方法检查当前Windows会话是否使用了真正的硬件视频卡(GPU)?或者可以检查ID2D1DeviceContext::DrawBitmap的特定插值模式是否具有硬件实现,并且是否在当前会话中使用GPU?

    UPD公司

    本主题不是关于检测RDP或Citrix会话。这与检测应用程序是否在虚拟机中无关。我已经有了所有的验证,并在这些情况下使用线性插值。本主题是关于检测当前Windows会话是否使用真实的GPU来显示桌面。我正在寻找一种更复杂的解决方案,使用DirectX和DXGI的功能来做出决策。

    2 回复  |  直到 7 年前
        1
  •  5
  •   Chuck Walbourn    7 年前

    如果要检测Microsoft Basic渲染器,最好的选择是使用其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.
                }
            }
        }
    }
    

    看见 MSDN Anatomy of Direct3D 11 Create Device

    对于测试/调试,您可能会发现,您不想显式阻止这些场景,但确实想向用户提供某种警告或通知反馈,说明他们正在使用软件而不是硬件渲染。

    从Win32 classic桌面应用程序进行远程桌面检测最好直接通过 GetSystemMetrics( SM_REMOTESESSION ) .

    看见 MSDN

        2
  •  2
  •   iench    3 年前

    在我努力回答一个三年前的问题时。

    我必须通过登记处。首先要在注册表中找到适配器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驱动程序名称。。。由你决定。