为什么为设备和交换链使用相同的IDXGIFactory ?

Why use the same IDXGIFactory for Device and Swap Chain

本文关键字:IDXGIFactory 交换 为什么      更新时间:2023-10-16

IDXGIFactory接口的引用告诉我,为了创建交换链,我可以使用用于创建Direct3D设备的相同工厂:

因为您可以在不创建交换链的情况下创建Direct3D设备,因此您可能需要检索用于创建设备的工厂以创建交换链。

它还给出了以下代码示例:

IDXGIDevice * pDXGIDevice;
hr = g_pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice);
IDXGIAdapter * pDXGIAdapter;
hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);
IDXGIFactory * pIDXGIFactory;
pDXGIAdapter->GetParent(__uuidof(IDXGIFactory), (void **)&pIDXGIFactory);

这篇文章非常简短,当我试图完全理解它时,出现了以下两个问题,而第一个问题是主要问题(关于这篇文章的标题):

为什么我必须使用用于创建Direct3D设备的相同工厂来创建交换链?工厂实例是否维护一个重要的内部状态,或者只是为了避免创建另一个消耗资源的工厂实例?

同样,在代码示例中,我与以下代码行作斗争:

hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);

对我来说,IDXGIAdapter是IDXGIDevice的父节点是不符合逻辑的。否则,我会期望IDXGIAdapter有一个类似CreateDevice的方法,它会使适配器成为设备的父设备。但事实并非如此。为什么适配器是设备的父设备?

问题的根源可以追溯到DXGI 1.0 (Direct3D 10)和DXGI 1.1 (Direct3D 11)。如果你试图在同一过程中使用由CreateDXGIFactoryCreateDXGIFactory1创建的IDXGIFactory,事情会很快变成梨形。

另一个问题是,当你使用Direct3D 10的"默认设备"时,会创建一个隐式工厂。X或11。X设备创建。上面的"魔法代码序列"可以让你隐式地创建工厂。

文档中的第二步应该是dxgiDevice->GetAdapter而不是dxgiDevice->GetParent。我要把它归档。

如果使用Microsoft::WRL::ComPtr:

,这更容易做到
    // First, retrieve the underlying DXGI Device from the D3D Device
    ComPtr<IDXGIDevice1> dxgiDevice;
    DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
    // Identify the physical adapter (GPU or card) this device is running on.
    ComPtr<IDXGIAdapter> dxgiAdapter;
    DX::ThrowIfFailed(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf()));
    // And obtain the factory object that created it.
    ComPtr<IDXGIFactory1> dxgiFactory;
    DX::ThrowIfFailed(dxgiAdapter->GetParent(__uuidof(IDXGIFactory1), &dxgiFactory));

Direct3D 12

请注意,这整个区域已被Direct3D 12清理,你不能使用QI从ID3D12Device获得IDXGIDevice

引用

DirectX Graphics Infrastructure (DXGI):最佳实践
Direct3D 11 Create Device的解剖

扩展Chuck Walbourn关于如何从D3D12设备获得DXGI工厂的评论,这里是一个可能的实现:

#include <d3d12.h>
#include <dxgi1_6.h>
#include <wrl/client.h>
#include <concepts>
#include <expected>
using namespace Microsoft::WRL;
template <typename TFactory> requires std::derived_from<TFactory, IDXGIFactory>
std::expected<ComPtr<TFactory>, HRESULT> GetDXGIFactoryFromDevice(ID3D12Device* device)
{
    ComPtr<TFactory> foundFactory;
    HRESULT hr = E_FAIL;
    // Get the adapter LUID (locally unique identifier) associated with the D3D device
    const LUID adapterLuid = device->GetAdapterLuid();
    // Create DXGI factory for adapter enumeration
    ComPtr<IDXGIFactory4> enumFactory;
    if (FAILED(hr = CreateDXGIFactory1(IID_PPV_ARGS(&enumFactory)))) return std::unexpected(hr);
    // Enumerate DXGI adapters to find adapter with the matching LUID
    for (UINT adapterIndex = 0u; ; ++adapterIndex)
    {
        ComPtr<IDXGIAdapter1> adapter1;
        if (FAILED(hr = enumFactory->EnumAdapters1(adapterIndex, &adapter1)))
        {
            if (hr == DXGI_ERROR_NOT_FOUND) break; // No more adapters to enumerate
            return std::unexpected(hr); // Error
        }
        DXGI_ADAPTER_DESC1 desc;
        if (FAILED(hr = adapter1->GetDesc1(&desc))) return std::unexpected(hr);
        if (desc.AdapterLuid.LowPart == adapterLuid.LowPart &&
            desc.AdapterLuid.HighPart == adapterLuid.HighPart)
        {
            // Found the matching adapter.
            // Get the DXGI interface from the adapter
            if (FAILED(hr = adapter1->GetParent(IID_PPV_ARGS(&foundFactory)))) return std::unexpected(hr);
            break;
        }
    }
    return foundFactory;
}

允许查询DXGI工厂的不同接口版本,并提供基本的错误处理。

当然也可以改成只从设备中获取DXGI适配器