Uso de DXCore para enumerar adaptadores
DXCore es una API de enumeración de adaptadores para dispositivos DirectX, por lo que algunas de sus funciones se superponen con las de DXGI.
DXCore permite la exposición de nuevos tipos de dispositivos al modo de usuario, como MCDM (modelo de controlador de proceso de Microsoft), para su uso con Direct3D 12, DirectML y Windows Machine Learning. DXCore, a diferencia de DXGI, no proporciona ninguna información sobre la tecnología o las propiedades relacionadas con la visualización
En las secciones siguientes, echaremos un vistazo a las características principales de DXCore con algunos ejemplos de código (escritos en C++/WinRT). Los ejemplos de código que se muestran a continuación se extraen de la lista de códigos fuente completa que puede encontrar en el tema Aplicación DXCore mínima.
Creación de un generador de adaptadores
Para comenzar la enumeración del adaptador DXCore, cree un objeto de generador de adaptadores, que se representa mediante la interfaz IDXCoreAdapterFactory. Para crear un generador, incluya el archivo de encabezado dxcore.h
y llame a la función gratuita DXCoreCreateAdapterFactory.
#include <dxcore.h>
...
winrt::com_ptr<IDXCoreAdapterFactory> adapterFactory;
winrt::check_hresult(::DXCoreCreateAdapterFactory(adapterFactory.put()));
Recuperación de una lista de adaptadores
A diferencia de DXGI, un generador de adaptadores DXCore recién creado no crea automáticamente una instantánea del estado del adaptador del sistema. En su lugar, DXCore crea esa instantánea cuando se recupera explícitamente un objeto de la lista de adaptadores, que se representa mediante la interfaz IDXCoreAdapterList.
winrt::com_ptr<IDXCoreAdapterList> d3D12CoreComputeAdapters;
GUID attributes[]{ DXCORE_ADAPTER_ATTRIBUTE_D3D12_CORE_COMPUTE };
winrt::check_hresult(
adapterFactory->CreateAdapterList(_countof(attributes),
attributes,
d3D12CoreComputeAdapters.put()));
Selección de un adaptador adecuado de la lista
En esta sección se muestra cómo, dado un objeto de lista de adaptadores, se podría encontrar el primer adaptador de hardware en la lista.
El método IDXCoreAdapterList::GetAdapterCount indica el número de elementos de la lista e IDXCoreAdapterList::GetAdapter recupera un adaptador específico por índice.
A continuación, puede consultar las propiedades de ese adaptador siguiendo estos pasos.
- En primer lugar, para confirmar que es válido recuperar el valor de una propiedad determinada para este adaptador en esta versión del sistema operativo, llame a IDXCoreAdapter::IsPropertySupported. Pase un valor de la enumeración DXCoreAdapterProperty para identificar qué propiedad está consultando.
- Opcionalmente, confirme el tamaño del valor de propiedad con una llamada a IDXCoreAdapter::GetPropertySize. Para una propiedad como DXCoreAdapterProperty::IsHardware, que es un booleano simple, este paso no es necesario.
- Y, por último, recupere el valor de la propiedad llamando a 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;
}
}
Selección del adaptador preferido mediante la ordenación de una lista de adaptadores.
Puede ordenar una lista de adaptadores DXCore llamando al método IDXCoreAdapterList::Sort.
La enumeración DXCoreAdapterPreference define valores que representan criterios de ordenación. Pase una matriz de esos valores a Ordenar y, a continuación, lea el primer adaptador de la lista ordenada resultante.
Para determinar si un tipo de ordenación va a entenderse por Ordenar, primero llame a 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;
}
Consulta y establecimiento del estado del adaptador (propiedades)
Puede recuperar y establecer el estado de un elemento de estado especificado de un adaptador llamando a los métodos 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));
}
En la práctica, antes de llamar a QueryState y SetState, debe llamar a IsQueryStateSupported para confirmar que la consulta del tipo de estado está disponible para este adaptador y sistema operativo (SO).
Actualización de la lista de adaptadores
Si una lista de adaptadores está obsoleta debido a las condiciones cambiantes del sistema, se marcará como tal. Puede determinar la actualización de una lista de adaptadores sondeando su método IDXCoreAdapterList::IsStale.
Sin embargo, para mayor comodidad, puede suscribirse a notificaciones para condiciones como obsolescencia. Para ello, pase DXCoreNotificationType::AdapterListStale a IDXCoreAdapterFactory::RegisterEventNotification y almacene de forma segura la cookie devuelta para su uso más adelante.
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)
{
...
}
A continuación, puede generar un objeto de lista de adaptadores nuevo a partir del objeto de fábrica que ya tiene. Controlar estas condiciones es fundamental para la capacidad de responder sin problemas a eventos como la llegada y eliminación del adaptador (ya sea una GPU o un adaptador de proceso especializado) y para desplazar correctamente las cargas de trabajo en respuesta.
Antes de destruir el objeto de lista de adaptadores, debe usar el valor de cookie para anular el registro de ese objeto de las notificaciones llamando a IDXCoreAdapterFactory::UnregisterEventNotification. Si no anula el registro, se genera una excepción grave cuando se detecta la situación.
HRESULT hr = factory->UnregisterEventNotification(m_eventCookie);
Información de visualización
Nota:
DXCore no proporciona ninguna información de visualización. Cuando sea necesario, debe usar la clase DisplayMonitor de Windows Runtime para recuperar esta información. El LUID de un adaptador proporciona un identificador común que se puede usar para asignar un adaptador DXCore a la información de DisplayMonitor.DisplayAdapterId. Para obtener el LUID de un adaptador, pase DXCoreAdapterProperty::InstanceLuid al método IDXCoreAdapter::GetProperty.
Enumeración de adaptadores para cargas de trabajo multimedia
Aunque el método IDXCoreAdapterFactory1::CreateAdapterListByWorkload permite proporcionar filtros específicos para cargas de trabajo, tiempo de ejecución y tipo de hardware, se recomienda limitar el filtrado a lo que es esencial. La razón principal de esto es ayudar a garantizar la compatibilidad en una amplia gama de configuraciones de usuario final. Puede confiar en que DXCore proporcionará a su aplicación el adaptador más adecuado en la primera entrada de la lista de adaptadores; y puede ignorar el resto, si es necesario. En este ejemplo, crearemos una lista de adaptadores para una carga de trabajo multimedia (es decir, procesamiento de vídeo), donde DXCore priorizará los aceleradores de medios.
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
}
Enumeración de NPU para cargas de trabajo de ML
En algunos casos, es adecuado restringir aún más la lista de adaptadores para IDXCoreAdapterFactory1::CreateAdapterListByWorkload, como en el caso en el que solo desee un procesamiento eficaz de cargas de trabajo de aprendizaje automático en una NPU. En este ejemplo se proporcionará una lista de adaptadores que consta de NPU capaces de procesar cargas de trabajo de ML a través de metacomandos de DirectX, omitiendo GPU y aceleradores de proceso.
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
}
Enumeración de adaptadores con o sin controladores en modo de usuario de DirectX
DXCore admite la enumeración de dispositivos MCDM/WDDM que no proporcionan un controlador en modo de usuario de Direct 3D (sino que se basan en interfaces privadas u otras bibliotecas para la interacción). Esos dispositivos no se pueden enumerar a través de IDXCoreAdapterFactory1::CreateAdapterListByWorkload porque ese método está diseñado para admitir casos de uso que implican la pila de DirectX. Sin embargo, esta clase estrecha de adaptadores se enumerará a través del método IDXCoreAdapterFactory::CreateAdapterList mediante los nuevos atributos de tipo hardware. En este ejemplo se enumeran todos los adaptadores del sistema, independientemente de la compatibilidad con el entorno de ejecución de Direct 3D, y se agrega a un mapa con el GUID del atributo de tipo de hardware como clave.
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});
}
}
}