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

How to check if a true hardware video adapter is used

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

创建了一个位图
ID2D1DeviceContext::CreateBitmap

那我用

ID2D1Bitmap::CopyFromMemory

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

ID2D1DeviceContext::DrawBitmap

绘制位图。我使用高质量三次插值模式 D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC 进行缩放以获得最佳图片,但在某些情况下(RDP、Citrix、虚拟机等)它非常慢并且 CPU 消耗非常高。发生这种情况是因为在这些情况下使用了非硬件视频适配器。因此,对于非硬件适配器,我试图关闭插值并使用更快的方法。问题是我无法准确检查系统是否有真正的硬件适配器。

当我调用 D3D11CreateDevice 时,我将它与 D3D_DRIVER_TYPE_HARDWARE 一起使用,但在虚拟机上它通常 returns "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 仍然 returns 供应商在真机的情况下但它不使用 GPU(我可以通过 Process Hacker 2 和 AMD System Monitor(在 ATI Radeon 的情况下)看到它)所以我仍然有三次插值的高 CPU 消耗。如果使用 ATI Radeon 与 Windows 7 进行 RDP 会话,则它比通过物理控制台大 10%。

或者我错了,RDP 以某种方式使用 GPU 资源,这就是为什么它 returns 通过 IDXGIAdapter::GetDesc 成为真正的硬件适配器的原因?

DirectDraw

我还查看了 DirectX 诊断工具。 "DirectDraw Acceleration" 信息字段 returns 看起来正是我需要的。在物理(控制台)会话的情况下,它显示 "Enabled"。在 RDP 和虚拟机(没有硬件视频加速)会话的情况下,它显示 "Not Available"。我查看了来源,理论上我可以使用验证算法。但它实际上适用于我在我的应用程序中不使用的 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
             }
         }
     }
 }

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 会话,Flags == 0 但正如我通过 Process Hacker 2 看到的那样,GPU 未被使用。至少在使用 ATI Radeon 的 Windows 7 上,我可以看到在 RDP 会话的情况下更大的 CPU 使用率。所以看起来 DXGI_ADAPTER_FLAG_SOFTWARE 仅适用于 Microsoft Basic Render Driver。所以问题没有解决

问题

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

UPD

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

如果要检测 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.
            }
        }
    }
}

MSDN and Anatomy of Direct3D 11 Create Device

You will probably find for testing/debugging that you don't want to explicitly block these scenarios, but you do want to provide some kind of warning or notice feedback to the user that they are using software rather than hardware rendering.

Win32 经典桌面应用程序的远程桌面检测最好直接通过 GetSystemMetrics( SM_REMOTESESSION ) 完成。

MSDN

努力回答一个 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 个驱动程序名称...由您决定。