Uso di DXCore per enumerare gli adapter

DXCore è un'API di enumerazione di adattatori per i dispositivi DirectX, quindi alcune delle sue strutture si sovrappongono a quelle di DXGI.

DXCore consente l'esposizione di nuovi tipi di dispositivi alla modalità utente, ad esempio MCDM (Microsoft Compute Driver Model), per l'uso con Direct3D 12, DirectML e Windows Machine Learning. DXCore, a differenza di DXGI, non fornisce informazioni sulla tecnologia o sulle proprietà correlate allo schermo

Nelle prossime sezioni verranno esaminate le funzionalità principali di DXCore con alcuni esempi di codice (scritti in C++/WinRT). Gli esempi di codice illustrati di seguito sono estratti dal listato di codice sorgente completo che è possibile trovare nell'argomento Applicazione DXCore minima.

Creare una factory di adattatori

Per iniziare l'enumerazione dell'adattatore DXCore, creare un oggetto factory dell'adattatore, rappresentato dall'interfaccia IDXCoreAdapterFactory . Per creare una factory, includere il dxcore.h file di intestazione e chiamare la funzione libera DXCoreCreateAdapterFactory .

#include <dxcore.h>
...
winrt::com_ptr<IDXCoreAdapterFactory> adapterFactory;
winrt::check_hresult(::DXCoreCreateAdapterFactory(adapterFactory.put()));

Recuperare un elenco di adattatori

A differenza di DXGI, una fabbrica di adattatori DXCore appena creata non crea automaticamente uno snapshot dello stato della scheda del sistema. DXCore crea invece lo snapshot quando si recupera in modo esplicito un oggetto elenco di adattatori, rappresentato dall'interfaccia IDXCoreAdapterList .

winrt::com_ptr<IDXCoreAdapterList> d3D12CoreComputeAdapters;
GUID attributes[]{ DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE };
winrt::check_hresult(
    adapterFactory->CreateAdapterList(_countof(attributes),
        attributes,
        d3D12CoreComputeAdapters.put()));

Selezionare un adattatore appropriato dall'elenco

Questa sezione illustra come, dato un oggetto elenco adattatori, è possibile trovare la prima scheda hardware nell'elenco.

Il metodo IDXCoreAdapterList::GetAdapterCount indica il numero di elementi nell'elenco e IDXCoreAdapterList::GetAdapter recupera un adattatore specifico per indice.

È quindi possibile eseguire una query sulle proprietà dell'adattatore seguendo questa procedura.

  • Prima di tutto, per confermare che sia valido recuperare il valore di una determinata proprietà per questo adattatore in questa versione del sistema operativo, chiamare IDXCoreAdapter::IsPropertySupported. Passare un valore dell'enumerazione DXCoreAdapterProperty per identificare la proprietà su cui si vuole eseguire una query.
  • Facoltativamente, verificare le dimensioni del valore della proprietà con una chiamata a IDXCoreAdapter::GetPropertySize. Per una proprietà come DXCoreAdapterProperty::IsHardware, che è un valore booleano semplice, questo passaggio non è necessario.
  • Infine, recuperare il valore della proprietà chiamando IDXCoreAdapter::GetProperty.
winrt::com_ptr<IDXCoreAdapter> preferredAdapter;

const uint32_t count{ d3D12CoreComputeAdapters->GetAdapterCount() };

for (uint32_t i = 0; i < count; ++i)
{
    winrt::com_ptr<IDXCoreAdapter> candidateAdapter;
    winrt::check_hresult(
        d3D12CoreComputeAdapters->GetAdapter(i, candidateAdapter.put()));

    bool isHardware{ false };
    winrt::check_hresult(candidateAdapter->GetProperty(
        DXCoreAdapterProperty::IsHardware,
        &isHardware));

    if (isHardware)
    {
        // Choose the first hardware adapter, and stop looping.
        preferredAdapter = candidateAdapter;
        break;
    }

    // Otherwise, ensure that (as long as there are *any* adapters) we'll
    // at least choose one.
    if (!preferredAdapter)
    {
        preferredAdapter = candidateAdapter;
    }
}

Selezionare l'adattatore preferito dall'elenco ordinando gli adattatori.

È possibile ordinare un elenco di adattatori DXCore chiamando il metodo IDXCoreAdapterList::Sort .

L'enumerazione DXCoreAdapterPreference definisce i valori che rappresentano i criteri di ordinamento. Passa un array di tali valori a Ordina e quindi leggi il primo adattatore nella lista ordinata risultante.

Per determinare se un tipo di ordinamento verrà compreso in Sort, chiamare prima IDXCoreAdapterList::IsAdapterPreferenceSupported.

winrt::com_ptr<IDXCoreAdapter> TryFindHardwareHighPerformanceGraphicsAdapter()
{
    // You begin DXCore adapter enumeration by creating an adapter factory.
    winrt::com_ptr<IDXCoreAdapterFactory> adapterFactory;
    winrt::check_hresult(::DXCoreCreateAdapterFactory(adapterFactory.put()));

    // From the factory, retrieve a list of all the Direct3D 12 Graphics adapters.
    winrt::com_ptr<IDXCoreAdapterList> d3D12GraphicsAdapters;
    GUID attributes[]{ DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS };
    winrt::check_hresult(
        adapterFactory->CreateAdapterList(_countof(attributes),
            attributes,
            d3D12GraphicsAdapters.put()));

    DXCoreAdapterPreference sortPreferences[]{
        DXCoreAdapterPreference::Hardware, DXCoreAdapterPreference::HighPerformance };

    // Ask the OS to sort for the highest performance hardware adapter.
    winrt::check_hresult(d3D12GraphicsAdapters->Sort(_countof(sortPreferences), sortPreferences));

    winrt::com_ptr<IDXCoreAdapter> preferredAdapter;

    if (d3D12GraphicsAdapters->GetAdapterCount() > 0)
    {
        winrt::check_hresult(d3D12GraphicsAdapters->GetAdapter(0, preferredAdapter.put()));
    }

    return preferredAdapter;
}

Eseguire query e impostare lo stato dell'adattatore (proprietà)

È possibile recuperare e impostare lo stato di un elemento di stato specificato di un adattatore chiamando i metodi IDXCoreAdapter::QueryState e IDXCoreAdapter::SetState.

void SetDesiredMemoryReservation(winrt::com_ptr<IDXCoreAdapter> const& adapter, uint64_t reservation)
{
    DXCoreAdapterMemoryBudgetNodeSegmentGroup nodeSegmentGroup{};
    nodeSegmentGroup.nodeIndex = 0;
    nodeSegmentGroup.segmentGroup = DXCoreSegmentGroup::Local;

    DXCoreAdapterMemoryBudget memoryBudget{};
    winrt::check_hresult(adapter->QueryState(
        DXCoreAdapterState::AdapterMemoryBudget,
        &nodeSegmentGroup,
        &memoryBudget));

    // Clamp the reservation to what's available.
    reservation = std::min<uint64_t>(reservation, memoryBudget.availableForReservation);

    winrt::check_hresult(adapter->SetState(
        DXCoreAdapterState::AdapterMemoryBudget,
        &nodeSegmentGroup,
        &reservation));
}

In pratica, prima di chiamare QueryState e SetState, è necessario chiamare IsQueryStateSupported per verificare che l'esecuzione di query sul tipo di stato sia disponibile per questa scheda e il sistema operativo.

Aggiornamento dell'elenco di adattatori

Se un elenco di adattatori diventa obsoleto a causa della modifica delle condizioni di sistema, verrà contrassegnato come tale. È possibile determinare la freschezza di un elenco di adattatori interrogando il relativo metodo IDXCoreAdapterList::IsStale.

In modo più comodo, tuttavia, è possibile iscriversi alle notifiche per condizioni come l'obsolescenza. A tale scopo, passare DXCoreNotificationType::AdapterListStale a IDXCoreAdapterFactory::RegisterEventNotification e archiviare in modo sicuro il cookie restituito per usarlo in un secondo momento.

uint32_t m_eventCookie = 0;
...
winrt::check_hresult(factory->RegisterEventNotification(
    m_adapters.get(),
    DXCoreNotificationType::AdapterListStale,
    OnAdapterListStale,
    this,
    &m_eventCookie));
...
static void WINAPI OnAdapterListStale(
    DXCoreNotificationType notificationType,
    IUnknown* staleObject,
    void* context)
{
    ...
}

È quindi possibile generare un nuovo oggetto corrente dell'elenco di adattatori dall'oggetto factory già presente. La gestione di queste condizioni è fondamentale per la possibilità di rispondere senza problemi a eventi come l'arrivo e la rimozione delle schede (che si tratti di una GPU o di una scheda di calcolo specializzata) e di spostare in modo appropriato i carichi di lavoro in risposta.

Prima di eliminare definitivamente l'oggetto elenco adattatori, è necessario utilizzare il valore del cookie per annullare la registrazione dell'oggetto dalle notifiche chiamando IDXCoreAdapterFactory::UnregisterEventNotification. Se non si annulla la registrazione, viene generata un'eccezione irreversibile quando viene rilevata la situazione.

HRESULT hr = factory->UnregisterEventNotification(m_eventCookie);

Visualizzare le informazioni

Nota

DXCore non fornisce informazioni di visualizzazione. Se necessario, devi usare la classe DisplayMonitor di Windows Runtime per recuperare queste informazioni. Il LUID di un adattatore fornisce un identificatore comune che è possibile usare per eseguire il mapping di un adattatore DXCore alle informazioni DisplayMonitor.DisplayAdapterId. Per ottenere il LUID di un adattatore, passare DXCoreAdapterProperty::InstanceLuid al metodo IDXCoreAdapter::GetProperty.

Enumerare gli adattatori per i carichi di lavoro multimediali

Anche se il metodo IDXCoreAdapterFactory1::CreateAdapterListByWorkload consente di fornire filtri specifici per carichi di lavoro, runtime e tipo hardware, è consigliabile limitare il filtro a ciò che è essenziale. Il motivo principale è garantire la compatibilità in un'ampia gamma di configurazioni degli utenti finali. Puoi fare affidamento su DXCore per fornire alla tua app l'adattatore più adatto nella prima voce dell'elenco di adattatori; è possibile ignorare il resto, qualora fosse necessario. In questo esempio verrà creato un elenco di adattatori per un carico di lavoro multimediale (ovvero elaborazione video) in cui gli acceleratori multimediali saranno classificati in ordine di priorità da DXCore.

void EnumerateAcceleratorsForMedia(ComPtr<IDXCoreAdapterList> &accelsForMedia)
{
    ComPtr<IDXCoreAdapterFactory1> adapterFactory;
    winrt::check_hresult(DXCoreCreateAdapterFactory(&adapterFactory));
    
    winrt::check_hresult(adapterFactory->CreateAdapterListByWorkload(
        DXCoreWorkload::Media,
        DXCoreRuntimeFilterFlags::None,
        DXCoreHardwareTypeFilterFlags::None,
        &accelsForMedia));
 
    // Ordering for returned adapter list:
    // - Media only
    // - Media and ML
    // - Media with Compute shaders
    // - Media with full GPU
}

Enumerare le NPU per i carichi di lavoro di Machine Learning

In alcuni casi è opportuno restringere ulteriormente l'elenco di adattatori per IDXCoreAdapterFactory1::CreateAdapterListByWorkload, ad esempio nel caso in cui si voglia solo un'elaborazione efficiente dei carichi di lavoro di Machine Learning in una NPU. Questo esempio fornirà un elenco di adattatori costituito da unità di elaborazione neurale (NPUs) in grado di elaborare carichi di lavoro di apprendimento automatico tramite meta-comandi DirectX, omettendo le GPU e gli acceleratori di calcolo.

void EnumerateNPUsForML(ComPtr<IDXCoreAdapterList> &npus)
{
    ComPtr<IDXCoreAdapterFactory1> adapterFactory;
    winrt::check_hresult(DXCoreCreateAdapterFactory(&adapterFactory));
    
    winrt::check_hresult(adapterFactory->CreateAdapterListByWorkload(
        DXCoreWorkload::MachineLearning,
        DXCoreRuntimeFilterFlags::None,
        DXCoreHardwareTypeFilterFlags::NPU,
        &npus));
 
    // Ordering for returned adapter list:
    // - NPUs with metacommands
    // - NPUs with metacommands and compute shaders
}

Enumerare gli adattatori con o senza i driver DirectX in modalità utente

DXCore supporta l'enumerazione di dispositivi MCDM/WDDM che non forniscono un driver in modalità utente Direct 3D (ma si basano invece su interfacce private o altre librerie per l'interazione). Questi dispositivi non sono enumerabili tramite IDXCoreAdapterFactory1::CreateAdapterListByWorkload perché questo metodo è progettato per supportare i casi d'uso che coinvolgono lo stack DirectX. Tuttavia, questa classe stretta di adattatori sarà enumerabile tramite il metodo IDXCoreAdapterFactory::CreateAdapterList usando i nuovi attributi di tipo hardware. Questo esempio enumera ogni adattatore nel sistema indipendentemente dal supporto del runtime Direct 3D e lo aggiunge a una mappa con il GUID dell'attributo di tipo hardware come chiave.

void EnumerateAllAdapters(std::map<GUID, ComPtr<IDXCoreAdapterList>> &adapterListByType)
{
    ComPtr<IDXCoreAdapterFactory> adapterFactory;
    winrt::check_hresult(DXCoreCreateAdapterFactory(&adapterFactory));
    
    const GUID attributes[] = {
        DXCORE_HARDWARE_TYPE_ATTRIBUTE_GPU,
        DXCORE_HARDWARE_TYPE_ATTRIBUTE_COMPUTE_ACCELERATOR,
        DXCORE_HARDWARE_TYPE_ATTRIBUTE_NPU,
        DXCORE_HARDWARE_TYPE_ATTRIBUTE_MEDIA_ACCELERATOR,
    };
 
    for(uint32_t i = 0; i < ARRAYSIZE(attributes); ++i)
    {
        ComPtr<IDXCoreAdapterList> adapterList = nullptr;
        winrt::check_hresult(adapterFactory->CreateAdapterList(1, attributes[i], &adapterList));
 
        if(adapterList->GetAdapterCount() > 0)
        {
            adapterListByType.insert({attributes[i], adapterList});
        }
    }
}

Vedi anche