为什么 IDXGIAdapter 无法转换为 IDXGIFactory?

Why IDXGIAdapter cannot cast to IDXGIFactory?

我了解 DirectX 不遵循 COM 标准。但是,它们看起来非常相似,因此我很困惑。

为简单起见,我使用 'COM' 非常 这个词很宽松,我省略了所有 HRESULT 处理,所以请耐心等待。

正确代码:

考虑以下代码。

ComPtr<IDXGIDevice3>            g_pDXGIDevice = nullptr;
ComPtr<ID3D11Device>            g_pD3DDevice = nullptr;
ComPtr<ID3D11DeviceContext>     g_pD3DDeviceContext = nullptr;

D3D11CreateDevice(..., &g_pD3DDevice, ..., &g_pD3DDeviceContext); 

一个'COM'对象被实例化。由于它不是 COM 对象,因此组件是通过 D3D11 函数 D3D11CreateDevice() 而不是通用 COM 函数 coCreateInstance().

实例化的

成功后,函数 returns 2 个 COM 指针:g_pD3DDeviceg_pD3DDeviceContext。这使客户端能够与 COM 服务器交互和使用方法,而无需承担实现细节的负担。

g_pD3DDevice.As(&g_pDXGIDevice);

接下来,我们QueryInterface通过ComPtr的As方法从设备接口中获取DXGI设备class。

return 成功,因为 DXGI 设备接口和设备接口由同一个 COM 对象提供。

接下来,我们创建交换链:

ComPtr<IDXGIAdapter> m_adapter = nullptr;
ComPtr<IDXGIFactory> m_factory = nullptr;

g_pDXGIDevice->GetAdapter(&m_adapter);
m_adapter->GetParent(IID_PPV_ARGS(&m_factory));
m_factory->CreateSwapChain(...);

这来自 MSD 官方文档并且有效。

错误代码:

现在,在创建交换链之前,让我们执行以下操作。

m_adapter.As(&m_factory);

这个returns E_NOINTERFACE。这是我的困惑。

根据 MSD 文档,IDXGIAdapterIDXGIFactory 派生自相同的基础-class 接口 IDXGIObject

我们能否安全地假设它们是同一 COM 对象的接口,因此,可以在对象接口 到 [=18= 中 navigate ]?

如果不是,我们如何从文档中知道两个接口是否属于同一个对象?文档好像没说清楚。

为了巩固我的论点,在调试模式下,我们可以清楚地看到 IDXGIAdapterIDXGIFactory 来自同一个 DLL(见下面的截图)。

Screenshot of m_adapter and m_factory as items to watch in debug mode

那么为什么 m_adapter.As(&m_factory); return 出错了?

备注

IDXGIFactory::CreateSwapChain 自 Direct3d 11.1 以来已弃用(参见 MS documentation)。

通读 Microsoft Docs: Programming DirectX with COM.

是个不错的起点

许多 情况下,DirectX COM 组件允许您如您所说:如果 C++ 接口 class 继承自另一个接口,您应该能够QueryInterface 具有相同对象实例的上链或下链。 IDXGIAdapter 派生自 IDXGIObject,因此您可以从适配器实例 QI 到对象接口。 IDXGIFactory 也派生自 IDXGIObject,因此您也可以 QI 到对象接口。然而,仅仅因为它们都实现相同的基本接口, 是否意味着您从适配器实例转到工厂实例。即,它们不相同 'object'。这反映了 C++ 的继承规则:您可以自由地将 class 的实例转换为其公开继承的父级,但 不能 自由转换回层次结构。

在 DXGI 中有些情况下您可以通过 IUnknown::QueryInterfaceIDXGIObject::GetParent“返回”。例如,以下代码是从 Direct3D 11 设备“返回”DXGI 工厂的良好、一致的方法:

ComPtr<IDXGIFactory1> dxgiFactory;
{
    ComPtr<IDXGIDevice> dxgiDevice;
    if (SUCCEEDED(device.As(&dxgiDevice)))
    {
        ComPtr<IDXGIAdapter> adapter;
        if (SUCCEEDED(dxgiDevice->GetAdapter(&adapter)))
        {
            hr = adapter->GetParent(IID_PPV_ARGS(&dxgiFactory));
            if ( SUCCEEDED(hr) )
            {
                ...
            }
        }
    }
}

Anatomy of Direct3D 11 Create Device

虽然这在上面的简单情况下有效,但一般的类型转换通常会出现问题 and/or 错误。例如,对于 Direct3D 11,如果您使用 DXGICreateFactory 方法而不是 DXGICreateFactory1,或者如果您在同一过程中混合使用这两种方法,则会出现问题。

因此,对于 DirectX 12 这些 DXGI back-track 转换(例如上面的代码)明确地不支持 .您应该直接创建 DXGI 工厂,然后从枚举的适配器创建 Direct3D 12 设备,而不是尝试采用其他方式。

参见 Anatomy of Direct3D 12 Create Device

实际上,除了枚举之外,您唯一需要使用这些 DXGI 接口的情况是“表面共享”场景。参见 Microsoft Docs: DX11 and DX12。其他所有内容实际上仅用于内部实施目的,因此可能会发生变化或其他未记录的行为。