Compartir a través de


Uso compartido de superficies entre API gráficas de Windows

En este tema se proporciona información general técnica sobre la interoperabilidad mediante el uso compartido de superficies entre las API de gráficos de Windows, como Direct3D 11, Direct2D, DirectWrite, Direct3D 10 y Direct3D 9Ex. Si ya tiene conocimientos prácticos de estas API, este documento puede ayudarle a usar varias API para representar en la misma superficie en una aplicación diseñada para los sistemas operativos Windows 7 o Windows Vista. En este tema también se proporcionan instrucciones de procedimientos recomendados y punteros a recursos adicionales.

Nota:

Para la interoperabilidad de Direct2D y DirectWrite en el entorno de ejecución de DirectX 11.1, puede usar dispositivos y contextos de dispositivo Direct2D para representar directamente en dispositivos Direct3D 11.

 

En este tema se incluyen las siguientes secciones.

Introducción

En este documento, la interoperabilidad de la API de gráficos de Windows hace referencia al uso compartido de la misma superficie de representación por diferentes API. Este tipo de interoperabilidad permite a las aplicaciones crear pantallas atractivas aprovechando varias API de gráficos de Windows y facilitar la migración a nuevas tecnologías manteniendo la compatibilidad con las API existentes.

En Windows 7 (y Windows Vista SP2 con Windows 7 Interop Pack, Vista 7IP), las API de representación de gráficos son Direct3D 11, Direct2D, Direct3D 10.1, Direct3D 10.0, Direct3D 9Ex, Direct3D 9c y las API de Direct3D anteriores, así como GDI y GDI+. Windows Imaging Component (WIC) y DirectWrite son tecnologías relacionadas para el procesamiento de imágenes y Direct2D realiza la representación de texto. DirectX Video Acceleration API (DXVA), basado en Direct3D 9c y Direct3D 9Ex, se usa para el procesamiento de video.

A medida que las API de gráficos de Windows evolucionan hacia la base de Direct3D, Microsoft está invirtiendo más esfuerzo en garantizar la interoperabilidad entre las API. Las API de Direct3D recién desarrolladas y las API de nivel superior basadas en las API de Direct3D también proporcionan compatibilidad cuando sea necesario para la compatibilidad de puente con las API anteriores. Para ilustrarlo, las aplicaciones de Direct2D pueden usar Direct3D 10.1 compartiendo un dispositivo Direct3D 10.1. Además, las API de Direct3D 11, Direct2D y Direct3D 10.1 pueden aprovechar todas las ventajas de directX Graphics Infrastructure (DXGI) 1.1, lo que permite superficies compartidas sincronizadas que admiten completamente la interoperabilidad entre estas API. Las API basadas en DXGI 1.1 interoperan con GDI y con GDI+ por asociación, obteniendo el contexto del dispositivo GDI de una superficie DXGI 1.1.

El tiempo de ejecución de Direct3D 9Ex admite el uso compartido de superficies no asincrónicas. Las aplicaciones de video basadas en DXVA pueden usar el asistente de interoperabilidad de Direct3D 9Ex y DXGI para la interoperabilidad DXVA basada en Direct3D 9Ex con Direct3D 11 para el sombreador de proceso o pueden interoperar con Direct2D para controles 2D o representación de texto. WIC y DirectWrite también interoperan con GDI, Direct2D y por asociación, con otras API de Direct3D.

Los entornos de ejecución de Direct3D 10.0, Direct3D 9c y anteriores no admiten superficies compartidas. Las copias de memoria del sistema seguirán utilizándose para la interoperabilidad con las API basadas en GDI o DXGI.

Tenga en cuenta que los escenarios de interoperabilidad de este documento hacen referencia a varias API de gráficos que se representan en una superficie de representación compartida, en lugar de en la misma ventana de aplicación. La sincronización de API independientes destinadas a diferentes superficies que se compone en la misma ventana está fuera del ámbito de este documento.

Información general sobre interoperabilidad de API

La interoperabilidad de uso compartido de Surface de las API de gráficos de Windows se puede describir en términos de escenarios de API a API y la funcionalidad de interoperabilidad correspondiente. A partir de Windows 7 y a partir de Windows Vista SP2 con 7IP, las nuevas API y los entornos de ejecución asociados incluyen Direct2D y tecnologías relacionadas: Direct3D 11 y DXGI 1.1. El rendimiento de GDI también se ha mejorado en Windows 7. Direct3D 10.1 se introdujo en Windows Vista SP1. En el diagrama siguiente se muestra la compatibilidad con la interoperabilidad entre las API.

diagrama de compatibilidad con la interoperabilidad entre las API de gráficos de Windows

En este diagrama, las flechas muestran escenarios de interoperabilidad en los que las API conectadas pueden acceder a la misma superficie. Las flechas azules indican mecanismos de interoperabilidad introducidos en Windows Vista. Las flechas verdes indican compatibilidad de interoperabilidad con nuevas API o mejoras que ayudan a las API anteriores a interoperar con las API más recientes. Por ejemplo, las flechas verdes representan el uso compartido de dispositivos, la compatibilidad con superficies compartidas sincronizadas, el asistente de sincronización de Direct3D 9Ex/DXGI y la obtención de un contexto de dispositivo GDI de una superficie compatible.

Escenarios de interoperabilidad

A partir de Windows 7 y Windows Vista 7IP, las ofertas estándar de las API de gráficos de Windows admiten varias API que se representan en la misma superficie DXGI 1.1.

Direct3D 11, Direct3D 10.1, Direct2D: interoperabilidad entre sí

Las API de Direct3D 11, Direct3D 10.1 y Direct2D (y sus API relacionadas, como DirectWrite y WIC) pueden interoperar entre sí mediante el uso compartido de dispositivos Direct3D 10.1 o superficies compartidas sincronizadas.

Uso compartido de dispositivos direct3D 10.1 con Direct2D

El uso compartido de dispositivos entre Direct2D y Direct3D 10.1 permite a una aplicación usar ambas API para representar sin problemas y de forma eficaz en la misma superficie DXGI 1.1, con el mismo objeto de dispositivo Direct3D subyacente. Direct2D proporciona la capacidad de llamar a las API de Direct2D mediante un dispositivo Direct3D 10.1 existente, aprovechando el hecho de que Direct2D se basa en los entornos de ejecución de Direct3D 10.1 y DXGI 1.1. Los fragmentos de código siguientes muestran cómo Direct2D obtiene el destino de representación del dispositivo Direct3D 10.1 desde una superficie DXGI 1.1 asociada al dispositivo. El destino de representación del dispositivo Direct3D 10.1 puede ejecutar llamadas de dibujo de Direct2D entre las API BeginDraw y EndDraw.

// Direct3D 10.1 Device and Swapchain creation
HRESULT hr = D3D10CreateDeviceandSwapChain1(
                pAdapter,
                DriverType,
                Software,
                D3D10_CREATE_DEVICE_BGRA_SUPPORT,
                featureLevel,
                D3D10_1_SDK_VERSION,
                pSwapChainDesc,
                &pSwapChain,
                &pDevice
                );

hr = pSwapChain->GetBuffer(
        0,
        __uuidof(IDXGISurface),
        (void **)&pDXGIBackBuffer
        ));

// Direct3D 10.1 API rendering calls
...

hr = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &m_spD2DFactory
        ));

pD2DFactory->CreateDxgiSurfaceRenderTarget(
        pDXGIBackBuffer,
        &renderTargetProperties,
        &pD2DBackBufferRenderTarget
        ));
...

pD2DBackBufferRenderTarget->BeginDraw();
//Direct2D API rendering calls
...

pD2DBackBufferRenderTarget->EndDraw();

pSwapChain->Present(0, 0);

Observaciones

  • El dispositivo Direct3D 10.1 asociado debe admitir el formato BGRA. Ese dispositivo se creó llamando a D3D10CreateDevice1 con el parámetro D3D10_CREATE_DEVICE_BGRA_SUPPORT. El formato BGRA se admite a partir del nivel de característica 9.1 de Direct3D 10.
  • La aplicación no debe crear varios ID2D1RenderTargets que se asocien al mismo dispositivo Direct3D10.1.
  • Para obtener un rendimiento óptimo, mantenga al menos un recurso alrededor de todo momento, como texturas o superficies asociadas al dispositivo.

El uso compartido de dispositivos es adecuado para el uso en proceso y de un solo subproceso de un dispositivo de representación compartido por las API de representación de Direct3D 10.1 y Direct2D. Las superficies compartidas sincronizadas permiten el uso multiproceso, en proceso y fuera del proceso de varios dispositivos de representación usados por las API de Direct3D 10.1, Direct2D y Direct3D 11.

Otro método de interoperabilidad de Direct3D 10.1 y Direct2D es mediante ID3D1RenderTarget::CreateSharedBitmap, que crea un objeto ID2D1Bitmap a partir de IDXGISurface. Puede escribir una escena de Direct3D10.1 en el mapa de bits y representarla con Direct2D. Para obtener más información, consulte ID2D1RenderTarget::CreateSharedBitmap (Método).

Rasterización de software de Direct2D

No se admite el uso compartido de dispositivos con Direct3D 10.1 al usar el representador de software direct2D, por ejemplo, especificando D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING en D2D1_RENDER_TARGET_USAGE al crear un destino de representación de Direct2D.

Direct2D puede usar el rasterizador de software WARP10 para compartir el dispositivo con Direct3D 10 o Direct3D 11, pero el rendimiento disminuye significativamente.

Superficies compartidas sincronizadas de DXGI 1.1

Las API de Direct3D 11, Direct3D 10.1 y Direct2D usan DXGI 1.1, que proporciona la funcionalidad de sincronizar la lectura y escritura en la misma superficie de memoria de video (DXGISurface1) por dos o más dispositivos Direct3D. Los dispositivos de representación que usan superficies compartidas sincronizadas pueden ser dispositivos Direct3D 10.1 o Direct3D 11, cada uno que se ejecuta en el mismo proceso o entre procesos.

Las aplicaciones pueden usar superficies compartidas sincronizadas para interoperar entre cualquier dispositivo basado en DXGI 1.1, como Direct3D 11 y Direct3D 10.1, o entre Direct3D 11 y Direct2D, obteniendo el dispositivo Direct3D 10.1 del objeto de destino de representación de Direct2D.

En Direct3D 10.1 y las API posteriores, para usar DXGI 1.1 asegúrese de que el dispositivo Direct3D se crea mediante un objeto de adaptador DXGI 1.1, que se enumera desde el objeto de fábrica DXGI 1.1. Llame a CreateDXGIFactory1 para crear el objeto IDXGIFactory1 y EnumAdapters1 para enumerar el objeto IDXGIAdapter1. El objeto IDXGIAdapter1 debe pasarse como parte de la llamada a D3D10CreateDevice o D3D10CreateDeviceAndSwapChain. Para obtener más información sobre las API de DXGI 1.1, consulte la Guía de programación para DXGI.

API existentes

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Al crear el recurso compartido sincronizado, establezca D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX en D3D10_RESOURCE_MISC_FLAG.

typedef enum D3D10_RESOURCE_MISC_FLAG {
    D3D10_RESOURCE_MISC_GENERATE_MIPS      = 0x1L,
    D3D10_RESOURCE_MISC_SHARED             = 0x2L,
    D3D10_RESOURCE_MISC_TEXTURECUBE        = 0x4L,
    D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX  = 0x10L,
    D3D10_RESOURCE_MISC_GDI_COMPATIBLE     = 0x20L,
}   D3D10_RESOURCE_MISC_FLAG;

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Permite sincronizar el recurso creado mediante las API IDXGIKeyedMutex::AcquireSync y ReleaseSync. Las siguientes API de Direct3D 10.1 que toman un parámetro D3D10_RESOURCE_MISC_FLAG se han ampliado para admitir la nueva marca.

  • ID3D10Device1::CreateTexture1D
  • ID3D10Device1::CreateTexture2D
  • ID3D10Device1::CreateTexture3D
  • ID3D10Device1::CreateBuffer

Si se llama a cualquiera de las funciones enumeradas con el conjunto de marcas de D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX, se puede consultar la interfaz devuelta para obtener una interfaz IDXGIKeyedMutex, que implementa las API AcquireSync y ReleaseSync para sincronizar el acceso a la superficie. El dispositivo que crea la superficie y cualquier otro dispositivo que abra la superficie (mediante OpenSharedResource) es necesario llamar a IDXGIKeyedMutex::AcquireSync antes de cualquier comando de representación en la superficie y IDXGIKeyedMutex::ReleaseSync cuando se realiza la representación.
Los dispositivos WARP y REF no admiten recursos compartidos. Si intenta crear un recurso con esta marca en un dispositivo WARP o REF, el método create devolverá un código de error E_OUTOFMEMORY.
INTERFAZ IDXGIKEYEDMUTEX
Una nueva interfaz en DXGI 1.1, IDXGIKeyedMutex, representa una exclusión mutua con clave, que permite el acceso exclusivo a un recurso compartido que usan varios dispositivos. Para obtener documentación de referencia sobre esta interfaz y sus dos métodos, AcquireSync y ReleaseSync, consulte IDXGIKeyedMutex.

Ejemplo: uso compartido de superficie sincronizado entre dos dispositivos Direct3D 10.1

En el ejemplo siguiente se muestra cómo compartir una superficie entre dos dispositivos Direct3D 10.1. El dispositivo Direct3D10.1 crea la superficie compartida sincronizada.

// Create Sync Shared Surface using Direct3D10.1 Device 1.
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
// must match swapchain format in order to CopySubresourceRegion.
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
// creates 2D texture as a Synchronized Shared Surface.
desc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
ID3D10Texture2D* g_pShared = NULL;
g_pd3dDevice1->CreateTexture2D( &desc, NULL, &g_pShared );

// QI IDXGIResource interface to synchronized shared surface.
IDXGIResource* pDXGIResource = NULL;
g_pShared->QueryInterface(__uuidof(IDXGIResource), (LPVOID*) &pDXGIResource);

// obtain handle to IDXGIResource object.
pDXGIResource->GetSharedHandle(&g_hsharedHandle);
pDXGIResource->Release();
if ( !g_hsharedHandle )
    return E_FAIL;

// QI IDXGIKeyedMutex interface of synchronized shared surface's resource handle.
hr = g_pShared->QueryInterface( __uuidof(IDXGIKeyedMutex),
    (LPVOID*)&g_pDXGIKeyedMutex_dev1 );
If ( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev1 == NULL ) )
    return E_FAIL;

El mismo dispositivo Direct3D10.1 puede obtener la superficie compartida sincronizada para la representación llamando a AcquireSync y liberando, a continuación, la superficie para la representación del otro dispositivo llamando a ReleaseSync. Al no compartir la superficie compartida sincronizada con ningún otro dispositivo Direct3D, el creador puede obtener y liberar la superficie compartida sincronizada (para iniciar y finalizar la representación) mediante la adquisición y liberación con el mismo valor de clave.

// Obtain handle to Sync Shared Surface created by Direct3D10.1 Device 1.
hr = g_pd3dDevice2->OpenSharedResource( g_hsharedHandle,__uuidof(ID3D10Texture2D),
                                        (LPVOID*) &g_pdev2Shared);
if (FAILED (hr))
    return hr;
hr = g_pdev2Shared->QueryInterface( __uuidof(IDXGIKeyedMutex),
                                    (LPVOID*) &g_pDXGIKeyedMutex_dev2);
if( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev2 == NULL ) )
    return E_FAIL;

// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2.
UINT acqKey = 1;
UINT relKey = 0;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev2->AcquireSync(acqKey, timeOut);
if ( result == WAIT_OBJECT_0 )
    // Rendering calls using Device 2.
else
    // Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev2->ReleaseSync(relKey));
if (result == WAIT_OBJECT_0)
    return S_OK;

El segundo dispositivo Direct3D10.1 puede obtener la superficie compartida sincronizada para la representación llamando a AcquireSync y liberando, a continuación, la superficie para la representación del primer dispositivo llamando a ReleaseSync. Tenga en cuenta que el dispositivo 2 puede adquirir la superficie compartida sincronizada con el mismo valor de clave que el especificado en la llamada ReleaseSync por el dispositivo 1.

// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1.
UINT acqKey = 0;
UINT relKey = 1;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev1->AcquireSync(acqKey, timeOut);
if (result == WAIT_OBJECT_0)
    // Rendering calls using Device 1.
else
    // Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(relKey));
if ( result == WAIT_OBJECT_0 )
    return S_OK;

Los dispositivos adicionales que comparten la misma superficie pueden tomar turnos de adquisición y liberación de la superficie mediante claves adicionales, como se muestra en las siguientes llamadas.

// Within Device 1's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1
result = g_pDXGIKeyedMutex_dev1->AcquireSync(0, timeOut);
// Rendering calls using Device 1
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(1);
...
////////////////////////////////////////////////////////////////////////////
// Within Device 2's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2
result = g_pDXGIKeyedMutex_dev2->AcquireSync(1, timeOut);
// Rendering calls using Device 2
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(2);

////////////////////////////////////////////////////////////////////////////
// Within Device 3's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 3
result = g_pDXGIKeyedMutex_dev1->AcquireSync(2, timeOut);
// Rendering calls using Device 3
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(0);
...

Tenga en cuenta que una aplicación real siempre puede representarse en una superficie intermedia que se copia en la superficie compartida para evitar que un dispositivo esté esperando en otro dispositivo que comparta la superficie.

Usar superficies compartidas sincronizadas con Direct2D y Direct3D 11

De forma similar, para compartir entre las API de Direct3D 11 y Direct3D 10.1, se puede crear una superficie compartida sincronizada desde cualquier dispositivo de API y compartirlo con los otros dispositivos de API dentro o fuera del proceso.

Las aplicaciones que usan Direct2D pueden compartir un dispositivo Direct3D 10.1 y usar una superficie compartida sincronizada para interoperar con Direct3D 11 u otros dispositivos Direct3D 10.1, tanto si pertenecen al mismo proceso como a procesos diferentes. Sin embargo, para las aplicaciones de un solo proceso y de un solo subproceso, el uso compartido de dispositivos es el método de interoperabilidad más eficaz y de alto rendimiento entre Direct2D y Direct3D 10 o Direct3D 11.

Rasterizador de software

Las superficies compartidas sincronizadas no se admiten cuando las aplicaciones usan los rasterizadores de software Direct3D o Direct2D, incluido el rasterizador de referencia y WARP, en lugar de usar la aceleración de hardware gráfico.

Interoperabilidad entre las API basadas en Direct3D 9Ex y DXGI

Las API de Direct3D 9Ex incluían la noción de uso compartido de superficie para permitir que otras API lean desde la superficie compartida. Para compartir la lectura y escritura en una superficie compartida de Direct3D 9Ex, debe agregar sincronización manual a la propia aplicación.

Direct3D 9Ex Shared Surfaces Plus Manual Synchronization Helper

La tarea más fundamental de la interoperabilidad de Direct3D 9Ex y Direct3D 10 o 11 es pasar una sola superficie del primer dispositivo (dispositivo A) al segundo (dispositivo B), de modo que cuando el dispositivo B adquiere un identificador en la superficie, se garantiza que la representación del dispositivo A se haya completado. Por lo tanto, el dispositivo B puede usar esta superficie sin preocuparse. Esto es muy similar al problema clásico productor-consumidor y esta discusión modela el problema de esa manera. El primer dispositivo que usa la superficie y luego renuncia a él es el productor (dispositivo A) y el dispositivo que está esperando inicialmente es el consumidor (dispositivo B). Cualquier aplicación real es más sofisticada que esta, y encadene varios bloques de creación productor-consumidor para crear la funcionalidad deseada.

Los bloques de creación productor-consumidor se implementan en el asistente mediante una cola de superficies. Las superficies son puestas en cola por el productor y puestas en cola por el consumidor. El asistente presenta tres interfaces COM: ISurfaceQueue, ISurfaceProducer e ISurfaceConsumer.

Información general de alto nivel del asistente

El objeto ISurfaceQueue es el bloque de creación para usar las superficies compartidas. Se crea con un dispositivo Direct3D inicializado y una descripción para crear un número fijo de superficies compartidas. El objeto queue administra la creación de recursos y la apertura del código. El número y el tipo de superficies son fijos; una vez creadas las superficies, la aplicación no puede agregarlas ni quitarlas.

Cada instancia del objeto ISurfaceQueue proporciona una clase de calle unidireccional que se puede usar para enviar superficies desde el dispositivo de producción al dispositivo de consumo. Se pueden usar varias calles unidireccionales para habilitar escenarios de uso compartido de superficies entre dispositivos de aplicaciones específicas.

Duración del objeto/creación
Hay dos maneras de crear el objeto queue: a través de CreateSurfaceQueue o mediante el método Clone de ISurfaceQueue. Dado que las interfaces son objetos COM, se aplica la administración estándar de la duración com.
Modelo de productor/consumidor
Enqueue (): el productor llama a esta función para indicar que se realiza con la superficie, que ahora puede estar disponible para otro dispositivo. Al volver de esta función, el dispositivo productor ya no tiene derechos para la superficie y no es seguro seguir usándolo.
Dequeue (): el dispositivo que consume llama a esta función para obtener una superficie compartida. La API garantiza que todas las superficies quitadas de la cola estén listas para usarse.
Metadata
La API admite la asociación de metadatos con las superficies compartidas.
Enqueue() tiene la opción de especificar metadatos adicionales que se pasarán al dispositivo de consumo. Los metadatos deben ser menores que un máximo conocido en el momento de la creación.
Dequeue() puede pasar opcionalmente un búfer y un puntero al tamaño del búfer. La cola rellena el búfer con los metadatos de la llamada en cola correspondiente.
Clonación
Cada objeto ISurfaceQueue resuelve una sincronización unidireccional. Se supone que la gran mayoría de las aplicaciones que usan esta API usarán un sistema cerrado. El sistema cerrado más sencillo con dos dispositivos que envían superficies hacia atrás y hacia adelante requiere dos colas. El objeto ISurfaceQueue tiene un método Clone() para que sea posible crear varias colas que forman parte de la misma canalización más grande.
Clone crea un nuevo objeto ISurfaceQueue a partir de uno existente y comparte todos los recursos abiertos entre ellos. El objeto resultante tiene exactamente las mismas superficies que la cola de origen. Las colas clonadas pueden tener diferentes tamaños de metadatos entre sí.
y trabajo
ISurfaceQueue asume la responsabilidad de crear y administrar sus superficies. No es válido poner en cola superficies arbitrarias. Además, una superficie solo debe tener un "propietario" activo. Debe estar en una cola específica o usarse en un dispositivo específico. No es válido tenerlo en varias colas o para que los dispositivos sigan usando la superficie después de ponerla en cola.

API Details

IsurfaceQueue

La cola es responsable de crear y mantener los recursos compartidos. También proporciona la funcionalidad para encadenar varias colas mediante Clone. La cola tiene métodos que abren el dispositivo de producción y un dispositivo de consumo. Solo se puede abrir una de cada una en cualquier momento.

La cola expone las siguientes API:

API Descripción
CreateSurfaceQueue Crea un objeto ISurfaceQueue (la cola "raíz").
ISurfaceQueue::OpenConsumer Devuelve una interfaz para que el dispositivo de consumo salga de la cola.
ISurfaceQueue::OpenProducer Devuelve una interfaz para que el dispositivo de producción entre en cola.
ISurfaceQueue::Clone Crea un objeto ISurfaceQueue que comparte superficies con el objeto de cola raíz.

 

CreateSurfaceQueue

typedef struct SURFACE_QUEUE_DESC {
  UINT            Width;
  UINT            Height;
  DXGI_FORMAT     Format;
  UINT            NumSurfaces;
  UINT            MetaDataSize;
  DWORD           Flags;
} SURFACE_QUEUE_DESC;

Miembros

Width, Height Las dimensiones de las superficies compartidas. Todas las superficies compartidas deben tener las mismas dimensiones.
Format El formato de las superficies compartidas. Todas las superficies compartidas deben tener el mismo formato. Los formatos válidos dependen de los dispositivos que se usarán, ya que diferentes pares de dispositivos pueden compartir tipos de formato diferentes.
NumSurfaces El número de superficies que forman parte de la cola. Se trata de un número fijo.
MetaDataSize El tamaño máximo del búfer de metadatos.
Flags Marcas para controlar el comportamiento de la cola. Vea la sección Comentarios.

HRESULT CreateSurfaceQueue(
  [in]   SURFACE_QUEUE_DESC *pDesc,
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

Parámetros

pDesc [in] Descripción de la cola de superficie compartida que se va a crear.

pDevice [in] Dispositivo que se debe usar para crear las superficies compartidas. Se trata de un parámetro explícito debido a una característica de Windows Vista. Para las superficies compartidas entre Direct3D 9 y Direct3D 10, las superficies deben crearse con Direct3D 9.

ppQueue [out] Al devolver, contiene un puntero al objeto ISurfaceQueue.

Valores devueltos

Si pDevice no es capaz de compartir recursos, esta función devuelve DXGI_ERROR_INVALID_CALL. Esta función crea los recursos. Si se produce un error, devuelve un error. Si se realiza correctamente, devuelve S_OK.

Observaciones

La creación del objeto queue también crea todas las superficies. Se supone que todas las superficies son destinos de representación 2D y se crearán con las marcas D3D10_BIND_RENDER_TARGET y D3D10_BIND_SHADER_RESOURCE establecidas (o las marcas equivalentes para los distintos entornos de ejecución).

El desarrollador puede especificar una marca que indique si varios subprocesos tendrán acceso a la cola. Si no se establecen marcas (Flags == 0), varias subprocesos usarán la cola. El desarrollador puede especificar el acceso de un solo subproceso, que desactiva el código de sincronización y proporciona una mejora del rendimiento para esos casos. Cada cola clonada tiene su propia marca, por lo que es posible que diferentes colas del sistema tengan controles de sincronización diferentes.

Abrir un productor

HRESULT OpenProducer(
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceProducer **ppProducer
);

Parámetros

pDevice [in]

Dispositivo productor que pone en cola las superficies.

ppProducer [out] Devuelve un objeto a la interfaz del productor.

Valores devueltos

Si el dispositivo no es capaz de compartir superficies, devuelve DXGI_ERROR_INVALID_CALL.

Abrir un consumidor

HRESULT OpenConsumer(
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceConsumer **ppConsumer
);

Parámetros
pDevice [in]
Dispositivo de consumidor que pone en cola las superficies de la cola de superficies. ppConsumer [out] Devuelve un objeto a la interfaz de consumidor.

Valores devueltos

Si el dispositivo no es capaz de compartir superficies, devuelve DXGI_ERROR_INVALID_CALL.

Observaciones

Esta función abre todas las superficies de la cola para el dispositivo de entrada y las almacena en caché. Las llamadas posteriores a Dequeue simplemente irán a la memoria caché y no tendrán que volver a abrir las superficies cada vez.

Clonación de un IDXGIXSurfaceQueue

typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
  UINT         MetaDataSize;
  DWORD        Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;

Members MetaDataSize y Flags tienen el mismo comportamiento que para CreateSurfaceQueue.

HRESULT Clone(
  [in]   SHARED_SURFACE_QUEUE_CLONE_DESC *pDesc,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

Parámetros

pDesc [in] Estructura que proporciona una descripción del objeto Clone que se va a crear. Este parámetro debería inicializarse.
ppQueue [out] Devuelve el objeto inicializado.

Observaciones

Puede clonar desde cualquier objeto de cola existente, incluso si no es la raíz.

IDXGIXSurfaceConsumer

HRESULT Dequeue(
  [in]      REFIID    id,
  [out]     void      **ppSurface,
  [in,out]  void      *pBuffer,
  [in,out]  UINT      *pBufferSize,
  [in]      DWORD     dwTimeout
);

Parámetros
id [in]
REFIID de una superficie 2D del dispositivo que consume.

  • Para un IDirect3DDevice9, el REFIID debe ser __uuidof(IDirect3DTexture9).
  • Para un ID3D10Device, el REFIID debe ser __uuidof(ID3D10Texture2D).
  • Para un ID3D11Device, el REFIID debe ser __uuidof(ID3D11Texture2D).

ppSurface [out] Devuelve un puntero a la superficie.
pBuffer [in, out] Un parámetro opcional y, si no es NULL, en devolución, contiene los metadatos que se pasaron en la llamada de puesta en cola correspondiente.
pBufferSize [in, out] Tamaño de pBuffer, en bytes. Devuelve el número de bytes devueltos en pBuffer. Si la llamada de puesta en cola no proporcionó metadatos, pBuffer se establece en 0.
dwTimeout [in] Especifica un valor de tiempo de espera. Consulte la sección de Comentarios para obtener más detalles.

Valores devueltos

Esta función puede devolver WAIT_TIMEOUT si se especifica un valor de tiempo de espera y la función no devuelve antes del valor de tiempo de espera. Vea la sección Comentarios. Si no hay superficies disponibles, la función devuelve con ppSurface establecido en NULL, pBufferSize establecido en 0 y el valor devuelto es 0x80070120 (WIN32_TO_HRESULT(WAIT_TIMEOUT)).

Observaciones

Esta API puede bloquearse si la cola está vacía. El parámetro dwTimeout funciona de forma idéntica a las API de sincronización de Windows, como WaitForSingleObject. Para el comportamiento de no bloqueo, use un tiempo de espera de 0.

ISurfaceProducer

Esta interfaz proporciona dos métodos que permiten a la aplicación poner en cola las superficies. Después de poner en cola una superficie, el puntero de superficie ya no es válido y no es seguro de usar. La única acción que debe realizar la aplicación con el puntero es liberarla.

Method Descripción
ISurfaceProducer::Enqueue Pone en cola una superficie para el objeto queue. Una vez completada esta llamada, el productor se realiza con la superficie y la superficie está lista para otro dispositivo.
ISurfaceProducer::Flush Se usa si las aplicaciones deben tener un comportamiento de no bloqueo. Consulte Comentarios para obtener más detalles.

 

Poner en cola

HRESULT Enqueue(
  [in]  IUnknown *pSurface,
  [in]  void *pBuffer,
  [in]  UINT BufferSize,
  [in]  DWORD Flags
);

Parámetros
pSurface [in]
Superficie del dispositivo de producción que debe ponerse en cola. Esta superficie debe ser una superficie fuera de la cola de la misma red de la cola. pBuffer [in] Parámetro opcional, que se usa para pasar metadatos. Debe apuntar a los datos que se pasarán a la llamada de retirada de la cola.
BufferSize [in] Tamaño de pBuffer, en bytes.
Flags [in] Parámetro opcional que controla el comportamiento de esta función. La única marca es SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. Consulte los Comentarios para Flush. Si no se pasa ninguna marca (Flags == 0), se usa el comportamiento de bloqueo predeterminado.

Valores devueltos

Esta función puede devolver DXGI_ERROR_WAS_STILL_DRAWING si se usa una marca SURFACE_QUEUE_FLAG_DO_NOT_WAIT.

Observaciones

  • Esta función coloca la superficie en la cola. Si la aplicación no especifica SURFACE_QUEUE_FLAG_DO_NOT_WAIT, esta función está bloqueando y realizará una sincronización de GPU-CPU para asegurarse de que se complete toda la representación en la superficie en cola. Si esta función se realiza correctamente, una superficie estará disponible para la puesta en cola. Si desea un comportamiento que no sea de bloqueo, use la marca DO_NOT_WAIT. Consulte Flush() para obtener más información.
  • Según las reglas de recuento de referencias COM, la superficie devuelta por Dequeue será AddRef() para que la aplicación no necesite hacerlo. Después de llamar a Enqueue, la aplicación debe liberar la superficie porque ya no la usa.

Vaciar

HRESULT Flush(
  [in]  DWORD Flags,
  [out] UINT *nSurfaces
);

Parámetros
Flags [in]
La única marca es SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. Vea la sección Comentarios. nSurfaces [out] Devuelve el número de superficies que todavía están pendientes y no vacías.

Valores devueltos

Esta función puede devolver DXGI_ERROR_WAS_STILL_DRAWING si se usa la marca SURFACE_QUEUE_FLAG_DO_NOT_WAIT. Esta función devuelve S_OK si alguna superficie se ha vaciado correctamente. Esta función devuelve DXGI_ERROR_WAS_STILL_DRAWING solo si no se vaciaron superficies. Juntos, el valor devuelto y nSurfaces indican a la aplicación qué trabajo se ha realizado y si queda algún trabajo pendiente.

Observaciones

El vaciado solo es significativo si la llamada anterior para poner en cola usó la marca DO_NOT_WAIT; de lo contrario, será una operación no operativa. Si la llamada a poner en cola usó la marca DO_NOT_WAIT, la puesta en cola devuelve inmediatamente y no se garantiza la sincronización de GPU-CPU. La superficie todavía se considera en cola, el dispositivo de producción no puede seguir usándolo, pero no está disponible para sacarlo de la cola. Para intentar confirmar la superficie para la puesta en cola, se debe llamar a Flush. Flush intenta confirmar todas las superficies que están actualmente en cola. Si no se pasa ninguna marca a Flush, se bloqueará y borrará toda la cola, preparando todas las superficies en ella para la retirada de la cola. Si se usa la marca DO_NOT_WAIT, la cola comprobará las superficies para ver si alguna de ellas está lista; este paso no es de bloqueo. Las superficies que han finalizado la sincronización de CPU con GPU estarán listas para el dispositivo de consumidor. Las superficies que todavía están pendientes no se verán afectadas. La función devuelve el número de superficies que todavía deben vaciarse.

Nota:

Flush no interrumpirá la semántica de la cola. La API garantiza que las superficies puestas en cola primero se confirmarán antes de que las superficies en cola más adelante, independientemente de cuándo se produzca la sincronización de la CPU con la GPU.

 

Asistente de interoperabilidad de Direct3D 9Ex y DXGI: modo de uso

Esperamos que la mayoría de los casos de uso impliquen dos dispositivos que comparten una serie de superficies. Dado que esto también es el escenario más sencillo, en este documento se detalla cómo usar las API para lograr este objetivo, se describe una variación sin bloqueo y se termina con una breve sección sobre cómo inicializar para tres dispositivos.

Dos dispositivos

La aplicación de ejemplo que usa este asistente puede usar Direct3D 9Ex y Direct3D 11 juntos. La aplicación puede procesar contenido con ambos dispositivos y presentar contenido mediante Direct3D 9. El procesamiento podría significar representar contenido, descodificar video, ejecutar sombreadores de proceso, etc. Para cada fotograma, la aplicación primero procesará con Direct3D 11, luego procesará con Direct3D 9 y, por último, se presentará con Direct3D 9. Además, el procesamiento con Direct3D 11 generará algunos metadatos que tendrá que consumir Direct3D 9 presentes. En esta sección se describe el uso del asistente en tres partes que corresponden a esta secuencia: Inicialización, Bucle principal y Limpieza.

Inicialización
La inicialización implica los pasos siguientes:

  1. Inicializar ambos dispositivos.
  2. Cree la cola raíz: m_11to9Queue.
  3. Clonar desde la cola raíz: m_9to11Queue.
  4. Llamar a OpenProducer/OpenConsumer en ambas colas.

Los nombres de cola usan los números 9 y 11 para indicar qué API es el productor y cuál es el consumidor: m_produceraconsumerQueue. En consecuencia, m_11to9Queue indica una cola para la que el dispositivo Direct3D 11 genera superficies que consume el dispositivo Direct3D 9. Del mismo modo, m_9to11Queue indica una cola para la que Direct3D 9 genera superficies que consume Direct3D 11.
La cola raíz está llena inicialmente y todas las colas clonadas están inicialmente vacías. Esto no debe ser un problema para la aplicación, excepto para el primer ciclo de Enqueues y Dequeues y la disponibilidad de los metadatos. Si un dequeue solicita metadatos pero no se estableció ninguno (ya sea porque no hay nada allí inicialmente o la cola no estableció nada), dequeue verá que no se recibió ningún metadato.

  1. Inicializar ambos dispositivos.

    m_pD3D9Device = InitializeD3D9ExDevice();
    m_pD3D11Device = InitializeD3D11Device();
    
  2. Crear la cola raíz.
    Este paso también crea las superficies. Las restricciones de tamaño y formato son idénticas a la creación de cualquier recurso compartido. El tamaño del búfer de metadatos se fija en tiempo de creación y, en este caso, solo pasaremos un UINT.
    La cola debe crearse con un número fijo de superficies. El rendimiento variará en función del escenario. Tener varias superficies aumenta las posibilidades de que los dispositivos estén ocupados. Por ejemplo, si solo hay una superficie, no habrá paralelización entre los dos dispositivos. Por otro lado, aumentar el número de superficies aumenta la superficie de memoria, lo que puede degradar el rendimiento. En este ejemplo se usan dos superficies.

    SURFACE_QUEUE_DESC Desc;
    Desc.Width        = 640;
    Desc.Height       = 480;
    Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
    Desc.NumSurfaces  = 2;
    Desc.MetaDataSize = sizeof(UINT);
    Desc.Flags        = 0;
    
    CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
    
  3. Clonar la cola raíz.
    Cada cola clonada debe usar las mismas superficies, pero puede tener diferentes tamaños de búfer de metadatos y marcas diferentes. En este caso, no hay metadatos de Direct3D 9 a Direct3D 11.

    SURFACE_QUEUE_CLONE_DESC Desc;
    Desc.MetaDataSize = 0;
    Desc.Flags        = 0;
    
    m_11to9Queue->Clone(&Desc, &m_9to11Queue);
    
  4. Abrir los dispositivos productor y consumidor.
    La aplicación debe realizar este paso antes de llamar a Enqueue y Dequeue. Al abrir un productor y consumidor, se devuelven interfaces que contienen las API de puesta en cola/retirada de cola.

    // Open for m_p9to11Queue.
    m_p9to11Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
    m_p9to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);
    
    // Open for m_p11to9Queue.
    m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
    m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);
    

Bucle principal
El uso de la cola se modela después del problema clásico del productor o consumidor. Piense en esto desde una perspectiva por dispositivo. Cada dispositivo debe realizar estos pasos: quitar la cola para obtener una superficie de su cola de consumo, procesar en la superficie y, a continuación, poner en cola su cola de producción. Para el dispositivo Direct3D 11, el uso de Direct3D 9 es casi idéntico.

// Direct3D 9 Device.
IDirect3DTexture9* pTexture9 = NULL;
REFIID             surfaceID9 = _uuidof(IDirect3DTexture9);
UINT               metaData;
UINT               metaDataSize;
while (!done)
{
    // Dequeue surface.
    m_pD3D9Consumer->Dequeue(surfaceID9, (void**)&pSurface9,
                             &metaData, &metaDataSize, INFINITE);

    // Process the surface.
    ProcessD3D9(pSurface9);

    // Present the surface using the meta data.
    PresentD3D9(pSurface9, metaData, metaDataSize);

    // Enqueue surface.
    m_pD3D9Producer->Enqueue(pSurface9, NULL, 0, 0);
}

Limpieza
Este paso es muy sencillo. Además de los pasos normales para limpiar las API de Direct3D, la aplicación debe liberar las interfaces COM de retorno.

m_pD3D9Producer->Release();
m_pD3D9Consumer->Release();
m_pD3D11Producer->Release();
m_pD3D11Consumer->Release();
m_p9to11Queue->Release();
m_p11to9Queue->Release();

Uso sin bloqueo

El ejemplo anterior tiene sentido para un caso de uso multiproceso en el que cada dispositivo tiene su propio subproceso. En el ejemplo se usan las versiones de bloqueo de las API: INFINITE para el tiempo de espera y sin ninguna marca para poner en cola. Si desea usar el asistente de forma no bloqueada, debe realizar solo unos pocos cambios. En esta sección se muestra el uso sin bloqueo con ambos dispositivos en un subproceso.

Inicialización
La inicialización es idéntica, excepto para las marcas. Dado que la aplicación es de un solo subproceso, use esa marca para la creación. Esto desactiva parte del código de sincronización, lo que podría mejorar el rendimiento.

SURFACE_QUEUE_DESC Desc;
Desc.Width        = 640;
Desc.Height       = 480;
Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces  = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags        = SURFACE_QUEUE_FLAG_SINGLE_THREADED;

CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags        = SURFACE_QUEUE_FLAG_SINGLE_THREADED;

m_11to9Queue->Clone(&Desc, &m_9to11Queue);

Abrir los dispositivos de productor y consumidor es el mismo que en el ejemplo de bloqueo.
Uso de la cola
Hay muchas maneras de usar la cola de forma no bloqueada con varias características de rendimiento. El ejemplo siguiente es sencillo, pero tiene un rendimiento deficiente debido a un giro y sondeo excesivos. A pesar de estos problemas, en el ejemplo se muestra cómo usar el asistente. El enfoque consiste en sentarse constantemente en un bucle y extraer de la cola, procesar, poner en cola y vaciar. Si se produce un error en alguno de los pasos porque el recurso no está disponible, la aplicación simplemente intenta de nuevo el siguiente bucle.

// Direct3D 11 Device.
ID3D11Texture2D* pSurface11 = NULL;
REFIID           surfaceID11 = __uuidof(ID3D11Texture2D);
UINT             metaData;
while (!done)
{
    //
    // D3D11 Portion.
    //

    // Dequeue surface.
    hr = m_pD3D11Consumer->Dequeue(surfaceID11,
                                   (void**)&pSurface11,
                                   NULL, 0, 0);
    // Only continue if we got a surface.
    if (SUCCEEDED(hr))
    {
        // Process the surface and return some meta data.
        ProcessD3D11(pSurface11, &metaData);

        // Enqueue surface.
        m_pD3D11Producer->Enqueue(pSurface11, &metaData,
                                  sizeof(UINT),
                                  SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
    }
    // Flush the queue to check if any surfaces completed.
    m_pD3D11Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);

    //
    // Do the same with the Direct3D 9 Device.
    //

    // Dequeue surface.
    hr = m_pD3D9Consumer->Dequeue(surfaceID9,
                                  (void**)&pSurface9,
                                  &metaData,
                                  &metaDataSize, 0);
    // Only continue if we got a surface.
    if (SUCCEEDED(hr)))
    {
        // Process the surface.
        ProcessD3D9(pSurface9);

        // Present the surface using the meta data.
        PresentD3D9(pSurface9, metaData, metaDataSize);

        // Enqueue surface.
        m_pD3D9Producer->Enqueue(pSurface9, NULL, 0,
                                 SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
    }
    // Flush the queue to check if any surfaces completed.
    m_pD3D9Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
}

Una solución más compleja podría comprobar el valor devuelto de la cola y del vaciado para determinar si es necesario vaciar.

Tres dispositivos

Extender los ejemplos anteriores para cubrir varios dispositivos es sencillo. El siguiente código realiza la inicialización. Una vez creados los objetos Producer/Consumer, el código para usarlos es el mismo. Este ejemplo tiene tres dispositivos y, por tanto, tres colas. Las superficies fluyen de Direct3D 9 a Direct3D 10 a Direct3D 11.

SURFACE_QUEUE_DESC Desc;
Desc.Width        = 640;
Desc.Height       = 480;
Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces  = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags        = 0;

SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags        = 0;

CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
m_11to9Queue->Clone(&Desc, &m_9to10Queue);
m_11to9Queue->Clone(&Desc, &m_10to11Queue);

Como se mencionó anteriormente, la clonación funciona del mismo modo, independientemente de la cola que se clone. Por ejemplo, la segunda llamada a Clone podría haberse desactivado del objeto m_9to10Queue.

// Open for m_p9to10Queue.
m_p9to10Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
m_p9to10Queue->OpenConsumer(m_pD3D10Device, &m_pD3D10Consumer);

// Open for m_p10to11Queue.
m_p10to11Queue->OpenProducer(m_pD3D10Device, &m_pD3D10Producer);
m_p10to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);

// Open for m_p11to9Queue.
m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);

Conclusión

Puede crear soluciones que usen la interoperabilidad para emplear la eficacia de varias API de DirectX. La interoperabilidad de la API de gráficos de Windows ahora ofrece un entorno de ejecución de administración de superficie común DXGI 1.1. Este entorno de ejecución permite la compatibilidad con el uso compartido de superficie sincronizado en las API recién desarrolladas, como Direct3D 11, Direct3D 10.1 y Direct2D. Las mejoras de interoperabilidad entre las nuevas API y las API existentes ayudan a la migración de aplicaciones y la compatibilidad con versiones anteriores. Las API de consumidor de Direct3D 9Ex y DXGI 1.1 pueden interoperar, como se muestra con el mecanismo de sincronización disponible en aplicaciones de ejemplo de Win32 anteriores que se pueden encontrar en el repositorio de ejemplos de la Galería de muestras de código de MSDN de Microsoft archivados.