为什么 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_pD3DDevice
和 g_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
。这是我的困惑。
- 为什么我们不能从
IDXGIAdapter
QueryInterface
得到 IDXGIFactory
?
- 它们不是同一个 DLL 的接口,即
dxgi.dll
吗?
- 它们是属于同一个组件对象的接口吗?
根据 MSD 文档,IDXGIAdapter
和 IDXGIFactory
派生自相同的基础-class 接口 IDXGIObject
。
我们能否安全地假设它们是同一 COM 对象的接口,因此,可以在对象接口 到 [=18= 中 navigate ]?
如果不是,我们如何从文档中知道两个接口是否属于同一个对象?文档好像没说清楚。
为了巩固我的论点,在调试模式下,我们可以清楚地看到 IDXGIAdapter
和 IDXGIFactory
来自同一个 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::QueryInterface
和 IDXGIObject::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。其他所有内容实际上仅用于内部实施目的,因此可能会发生变化或其他未记录的行为。
我了解 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_pD3DDevice
和 g_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
。这是我的困惑。
- 为什么我们不能从
IDXGIAdapter
QueryInterface
得到IDXGIFactory
? - 它们不是同一个 DLL 的接口,即
dxgi.dll
吗? - 它们是属于同一个组件对象的接口吗?
根据 MSD 文档,IDXGIAdapter
和 IDXGIFactory
派生自相同的基础-class 接口 IDXGIObject
。
我们能否安全地假设它们是同一 COM 对象的接口,因此,可以在对象接口 到 [=18= 中 navigate ]?
如果不是,我们如何从文档中知道两个接口是否属于同一个对象?文档好像没说清楚。
为了巩固我的论点,在调试模式下,我们可以清楚地看到 IDXGIAdapter
和 IDXGIFactory
来自同一个 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::QueryInterface
和 IDXGIObject::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。其他所有内容实际上仅用于内部实施目的,因此可能会发生变化或其他未记录的行为。